mirror of
https://github.com/retspen/webvirtcloud
synced 2024-12-24 15:15:22 +00:00
commit
9718b4c215
54 changed files with 1209 additions and 858 deletions
19
README.md
19
README.md
|
@ -273,6 +273,25 @@ You need to put cloud public key into authorized keys on the compute node. Simpl
|
||||||
sudo -u www-data ssh-copy-id root@compute1
|
sudo -u www-data ssh-copy-id root@compute1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Host SMBIOS information is not available
|
||||||
|
|
||||||
|
If you see warning
|
||||||
|
```
|
||||||
|
Unsupported configuration: Host SMBIOS information is not available
|
||||||
|
```
|
||||||
|
Then you need to install `dmidecode` package on your host using your package manager and restart libvirt daemon.
|
||||||
|
|
||||||
|
Debian/Ubuntu like:
|
||||||
|
```
|
||||||
|
$ sudo apt-get install dmidecode
|
||||||
|
$ sudo service libvirt-bin restart
|
||||||
|
```
|
||||||
|
Arch Linux
|
||||||
|
```
|
||||||
|
$ sudo pacman -S dmidecode
|
||||||
|
$ systemctl restart libvirtd
|
||||||
|
```
|
||||||
|
|
||||||
### Cloud-init
|
### Cloud-init
|
||||||
Currently supports only root ssh authorized keys and hostname. Example configuration of the cloud-init client follows.
|
Currently supports only root ssh authorized keys and hostname. Example configuration of the cloud-init client follows.
|
||||||
```
|
```
|
||||||
|
|
|
@ -6,8 +6,10 @@ from django.db import migrations
|
||||||
def add_useradmin(apps, schema_editor):
|
def add_useradmin(apps, schema_editor):
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from accounts.models import UserAttributes
|
||||||
|
|
||||||
User.objects.create_superuser('admin', None, 'admin', last_login=timezone.now())
|
admin = User.objects.create_superuser('admin', None, 'admin', last_login=timezone.now())
|
||||||
|
UserAttributes(user=admin, max_instances=-1, max_cpus=-1, max_memory=-1, max_disk_size=-1).save()
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
24
accounts/migrations/0003_permissionset.py
Normal file
24
accounts/migrations/0003_permissionset.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# Generated by Django 2.2.12 on 2020-05-27 12:29
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('accounts', '0002_addAdmin'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='PermissionSet',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'permissions': (('change_password', 'Can change password'),),
|
||||||
|
'managed': False,
|
||||||
|
'default_permissions': (),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
25
accounts/migrations/0004_apply_change_password.py
Normal file
25
accounts/migrations/0004_apply_change_password.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def apply_change_password(apps, schema_editor):
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib.auth.models import User, Permission
|
||||||
|
|
||||||
|
if hasattr(settings, 'SHOW_PROFILE_EDIT_PASSWORD'):
|
||||||
|
if settings.SHOW_PROFILE_EDIT_PASSWORD:
|
||||||
|
permission = Permission.objects.get(codename='change_password')
|
||||||
|
users = User.objects.all()
|
||||||
|
user: User
|
||||||
|
for user in users:
|
||||||
|
user.user_permissions.add(permission)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('accounts', '0003_permissionset'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(apply_change_password),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.2.12 on 2020-05-28 04:24
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('accounts', '0004_apply_change_password'),
|
||||||
|
('instances', '0003_migrate_can_clone_instances'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='userattributes',
|
||||||
|
name='can_clone_instances',
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,39 +1,54 @@
|
||||||
from django.db.models import Model, BooleanField, IntegerField, CharField
|
|
||||||
from django.db.models import ForeignKey, OneToOneField
|
|
||||||
from django.db.models import CASCADE, DO_NOTHING
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from instances.models import Instance
|
from django.contrib.auth.models import User
|
||||||
from django.core.validators import MinValueValidator
|
from django.core.validators import MinValueValidator
|
||||||
|
from django.db import models
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from instances.models import Instance
|
||||||
|
|
||||||
|
|
||||||
class UserInstance(Model):
|
class UserInstance(models.Model):
|
||||||
user = ForeignKey(User, on_delete=CASCADE)
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
instance = ForeignKey(Instance, on_delete=CASCADE)
|
instance = models.ForeignKey(Instance, on_delete=models.CASCADE)
|
||||||
is_change = BooleanField(default=False)
|
is_change = models.BooleanField(default=False)
|
||||||
is_delete = BooleanField(default=False)
|
is_delete = models.BooleanField(default=False)
|
||||||
is_vnc = BooleanField(default=False)
|
is_vnc = models.BooleanField(default=False)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.instance.name
|
return self.instance.name
|
||||||
|
|
||||||
|
|
||||||
class UserSSHKey(Model):
|
class UserSSHKey(models.Model):
|
||||||
user = ForeignKey(User, on_delete=DO_NOTHING)
|
user = models.ForeignKey(User, on_delete=models.DO_NOTHING)
|
||||||
keyname = CharField(max_length=25)
|
keyname = models.CharField(max_length=25)
|
||||||
keypublic = CharField(max_length=500)
|
keypublic = models.CharField(max_length=500)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.keyname
|
return self.keyname
|
||||||
|
|
||||||
|
|
||||||
class UserAttributes(Model):
|
class UserAttributes(models.Model):
|
||||||
user = OneToOneField(User, on_delete=CASCADE)
|
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
||||||
can_clone_instances = BooleanField(default=True)
|
max_instances = models.IntegerField(default=1,
|
||||||
max_instances = IntegerField(default=1, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1), ])
|
help_text="-1 for unlimited. Any integer value",
|
||||||
max_cpus = IntegerField(default=1, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1)])
|
validators=[
|
||||||
max_memory = IntegerField(default=2048, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1)])
|
MinValueValidator(-1),
|
||||||
max_disk_size = IntegerField(default=20, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1)])
|
])
|
||||||
|
max_cpus = models.IntegerField(
|
||||||
|
default=1,
|
||||||
|
help_text="-1 for unlimited. Any integer value",
|
||||||
|
validators=[MinValueValidator(-1)],
|
||||||
|
)
|
||||||
|
max_memory = models.IntegerField(
|
||||||
|
default=2048,
|
||||||
|
help_text="-1 for unlimited. Any integer value",
|
||||||
|
validators=[MinValueValidator(-1)],
|
||||||
|
)
|
||||||
|
max_disk_size = models.IntegerField(
|
||||||
|
default=20,
|
||||||
|
help_text="-1 for unlimited. Any integer value",
|
||||||
|
validators=[MinValueValidator(-1)],
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_missing_userattributes(user):
|
def create_missing_userattributes(user):
|
||||||
|
@ -51,7 +66,7 @@ class UserAttributes(Model):
|
||||||
instance = Instance.objects.get(name=instance_name)
|
instance = Instance.objects.get(name=instance_name)
|
||||||
user_instance = UserInstance(user=user, instance=instance)
|
user_instance = UserInstance(user=user, instance=instance)
|
||||||
user_instance.save()
|
user_instance.save()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def configure_user(user):
|
def configure_user(user):
|
||||||
UserAttributes.create_missing_userattributes(user)
|
UserAttributes.create_missing_userattributes(user)
|
||||||
|
@ -59,3 +74,14 @@ class UserAttributes(Model):
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.user.username
|
return self.user.username
|
||||||
|
|
||||||
|
|
||||||
|
class PermissionSet(models.Model):
|
||||||
|
"""
|
||||||
|
Dummy model for holding set of permissions we need to be automatically added by Django
|
||||||
|
"""
|
||||||
|
class Meta:
|
||||||
|
default_permissions = ()
|
||||||
|
permissions = (('change_password', _('Can change password')), )
|
||||||
|
|
||||||
|
managed = False
|
||||||
|
|
|
@ -1,175 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
{% load i18n %}
|
|
||||||
{% load staticfiles %}
|
|
||||||
{% block title %}{% trans "Users" %}{% endblock %}
|
|
||||||
{% block content %}
|
|
||||||
<!-- Page Heading -->
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-lg-12">
|
|
||||||
{% include 'create_user_block.html' %}
|
|
||||||
<div class="pull-right search">
|
|
||||||
<input id="filter" class="form-control" type="text" placeholder="Search">
|
|
||||||
</div>
|
|
||||||
<h1 class="page-header">{% trans "Users" %}</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- /.row -->
|
|
||||||
|
|
||||||
{% include 'errors_block.html' %}
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
{% if not users %}
|
|
||||||
<div class="col-lg-12">
|
|
||||||
<div class="alert alert-warning alert-dismissable">
|
|
||||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
|
||||||
<i class="fa fa-exclamation-triangle"></i> <strong>{% trans "Warning:" %}</strong> {% trans "You don't have any User" %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<div class="col-lg-12">
|
|
||||||
<table class="table table-striped table-hover">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>{% trans "Username" %}</th>
|
|
||||||
<th>{% trans "Status" %}</th>
|
|
||||||
<th>{% trans "Staff" %}</th>
|
|
||||||
<th>{% trans "Superuser" %}</th>
|
|
||||||
<th>{% trans "Clone" %}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody class="searchable">
|
|
||||||
{% for user in users %}
|
|
||||||
<tr class="{% if not user.is_active %}danger{% endif %}">
|
|
||||||
<td>
|
|
||||||
<a href="{% url 'account' user.id %}"><strong>{{ user.username }}</strong></a>
|
|
||||||
<a data-toggle="modal" href="#editUser{{ user.id }}" class="pull-right" title="{% trans "Edit" %}">
|
|
||||||
<span class="glyphicon glyphicon-cog"></span>
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{% if user.is_active %}
|
|
||||||
{% trans "Active" %}
|
|
||||||
{% else %}
|
|
||||||
{% trans "Blocked" %}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>{% if user.is_staff %}<span class="glyphicon glyphicon-ok"></span>{% endif %}</td>
|
|
||||||
<td>{% if user.is_superuser %}<span class="glyphicon glyphicon-ok"></span>{% endif %}</td>
|
|
||||||
<td>{% if user.userattributes.can_clone_instances %}<span class="glyphicon glyphicon-ok"></span>{% endif %}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
{% for user in users %}
|
|
||||||
<!-- Modal Edit -->
|
|
||||||
<div class="modal fade" id="editUser{{ user.id }}" tabindex="-1" role="dialog" aria-labelledby="editUserLabel" aria-hidden="true">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
|
|
||||||
<div class="modal-header">
|
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
|
||||||
<h4 class="modal-title">{% trans "Edit user info" %}</h4>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-sm-4 control-label">{% trans "Name" %}</label>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<input type="hidden" name="user_id" value="{{ user.id }}">
|
|
||||||
<input type="text" name="name" class="form-control" value="{{ user.username }}" disabled>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-sm-4 control-label">{% trans "Password" %}</label>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<input type="password" name="user_pass" class="form-control" value="">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-sm-4 control-label">{% trans "Is staff" %}</label>
|
|
||||||
<div class="col-sm-2">
|
|
||||||
<input type="checkbox" name="user_is_staff" {% if user.is_staff %}checked{% endif %}>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-sm-4 control-label">{% trans "Is superuser" %}</label>
|
|
||||||
<div class="col-sm-2">
|
|
||||||
<input type="checkbox" name="user_is_superuser" {% if user.is_superuser %}checked{% endif %}>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-sm-4 control-label">{% trans "Can clone instances" %}</label>
|
|
||||||
<div class="col-sm-2">
|
|
||||||
<input type="checkbox" name="userattributes_can_clone_instances" {% if user.userattributes.can_clone_instances %}checked{% endif %}>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-sm-4 control-label">{% trans "Max instances" %}</label>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<input type="text" name="userattributes_max_instances" class="form-control" value="{{ user.userattributes.max_instances }}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-sm-4 control-label">{% trans "Max cpus" %}</label>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<input type="text" name="userattributes_max_cpus" class="form-control" value="{{ user.userattributes.max_cpus }}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-sm-4 control-label">{% trans "Max memory (MB)" %}</label>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<input type="text" name="userattributes_max_memory" class="form-control" value="{{ user.userattributes.max_memory }}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-sm-4 control-label">{% trans "Max disk size (GB)" %}</label>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<input type="text" name="userattributes_max_disk_size" class="form-control" value="{{ user.userattributes.max_disk_size }}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="submit" class="pull-left btn btn-danger" name="delete">
|
|
||||||
{% trans "Delete" %}
|
|
||||||
</button>
|
|
||||||
{% if user.is_active %}
|
|
||||||
<button type="submit" class="pull-left btn btn-warning" name="block">
|
|
||||||
{% trans "Block" %}
|
|
||||||
</button>
|
|
||||||
{% else %}
|
|
||||||
<button type="submit" class="pull-left btn btn-success" name="unblock">
|
|
||||||
{% trans "Unblock" %}
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">
|
|
||||||
{% trans "Close" %}
|
|
||||||
</button>
|
|
||||||
<button type="submit" class="btn btn-primary" name="edit">
|
|
||||||
{% trans "Edit" %}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div><!-- /.modal-content -->
|
|
||||||
</div><!-- /.modal-dialog -->
|
|
||||||
</div><!-- /.modal -->
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
{% block script %}
|
|
||||||
<script>
|
|
||||||
function filter_table() {
|
|
||||||
var rex = new RegExp($(this).val(), 'i');
|
|
||||||
$('.searchable tr').hide();
|
|
||||||
$('.searchable tr').filter(function () {
|
|
||||||
return rex.test($(this).text());
|
|
||||||
}).show();
|
|
||||||
}
|
|
||||||
$(document).ready(function () {
|
|
||||||
(function ($) {
|
|
||||||
$('#filter').keyup(filter_table)
|
|
||||||
}(jQuery));
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
|
@ -1,144 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
{% load i18n %}
|
|
||||||
{% block title %}{% trans "Users" %}{% endblock %}
|
|
||||||
{% block content %}
|
|
||||||
<!-- Page Heading -->
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-lg-12">
|
|
||||||
{% include 'create_user_block.html' %}
|
|
||||||
<h1 class="page-header">{% trans "Users" %}</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- /.row -->
|
|
||||||
|
|
||||||
{% include 'errors_block.html' %}
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
{% if not users %}
|
|
||||||
<div class="col-lg-12">
|
|
||||||
<div class="alert alert-warning alert-dismissable">
|
|
||||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
|
||||||
<i class="fa fa-exclamation-triangle"></i> <strong>{% trans "Warning:" %}</strong> {% trans "You don't have any User" %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
{% for user in users %}
|
|
||||||
<div id="{{ user.username }}" class="col-xs-12 col-sm-4">
|
|
||||||
<div class="panel {% if user.is_active %}panel-success{% else %}panel-danger{% endif %} panel-data">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<h3 class="panel-title">
|
|
||||||
<a href="{% url 'account' user.id %}"><strong>{{ user.username }}</strong></a>
|
|
||||||
<a data-toggle="modal" href="#editUser{{ user.id }}" class="pull-right" title="{% trans "Edit" %}">
|
|
||||||
<span class="glyphicon glyphicon-cog"></span>
|
|
||||||
</a>
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<div class="col-xs-4 col-sm-4">
|
|
||||||
<p><strong>{% trans "Status:" %}</strong></p>
|
|
||||||
</div>
|
|
||||||
<div class="col-xs-4 col-sm-6">
|
|
||||||
{% if user.is_active %}
|
|
||||||
<p>{% trans "Active" %}</p>
|
|
||||||
{% else %}
|
|
||||||
<p>{% trans "Blocked" %}</p>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Modal Edit -->
|
|
||||||
<div class="modal fade" id="editUser{{ user.id }}" tabindex="-1" role="dialog" aria-labelledby="editUserLabel" aria-hidden="true">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
|
||||||
<h4 class="modal-title">{% trans "Edit user info" %}</h4>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-sm-4 control-label">{% trans "Name" %}</label>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<input type="hidden" name="user_id" value="{{ user.id }}">
|
|
||||||
<input type="text" name="name" class="form-control" value="{{ user.username }}" disabled>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-sm-4 control-label">{% trans "Password" %}</label>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<input type="password" name="user_pass" class="form-control" value="">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-sm-4 control-label">{% trans "Is staff" %}</label>
|
|
||||||
<div class="col-sm-2">
|
|
||||||
<input type="checkbox" name="user_is_staff" {% if user.is_staff %}checked{% endif %}>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-sm-4 control-label">{% trans "Is superuser" %}</label>
|
|
||||||
<div class="col-sm-2">
|
|
||||||
<input type="checkbox" name="user_is_superuser" {% if user.is_superuser %}checked{% endif %}>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-sm-4 control-label">{% trans "Can clone instances" %}</label>
|
|
||||||
<div class="col-sm-2">
|
|
||||||
<input type="checkbox" name="userattributes_can_clone_instances" {% if user.userattributes.can_clone_instances %}checked{% endif %}>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-sm-4 control-label">{% trans "Max instances" %}</label>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<input type="text" name="userattributes_max_instances" class="form-control" value="{{ user.userattributes.max_instances}}" required="True" >
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-sm-4 control-label">{% trans "Max cpus" %}</label>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<input type="text" name="userattributes_max_cpus" class="form-control" value="{{ user.userattributes.max_cpus }}" required="True">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-sm-4 control-label">{% trans "Max memory (MB)" %}</label>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<input type="text" name="userattributes_max_memory" class="form-control" value="{{ user.userattributes.max_memory}}" required="True">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-sm-4 control-label">{% trans "Max disk size (GB)" %}</label>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<input type="text" name="userattributes_max_disk_size" class="form-control" value="{{ user.userattributes.max_disk_size }}" required="True">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="submit" class="pull-left btn btn-danger" name="delete">
|
|
||||||
{% trans "Delete" %}
|
|
||||||
</button>
|
|
||||||
{% if user.is_active %}
|
|
||||||
<button type="submit" class="pull-left btn btn-warning" name="block">
|
|
||||||
{% trans "Block" %}
|
|
||||||
</button>
|
|
||||||
{% else %}
|
|
||||||
<button type="submit" class="pull-left btn btn-success" name="unblock">
|
|
||||||
{% trans "Unblock" %}
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">
|
|
||||||
{% trans "Close" %}
|
|
||||||
</button>
|
|
||||||
<button type="submit" class="btn btn-primary" name="edit">
|
|
||||||
{% trans "Edit" %}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div><!-- /.modal-content -->
|
|
||||||
</div><!-- /.modal-dialog -->
|
|
||||||
</div><!-- /.modal -->
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
|
@ -1,38 +0,0 @@
|
||||||
{% load i18n %}
|
|
||||||
{% if request.user.is_superuser %}
|
|
||||||
<a href="#AddUser" type="button" class="btn btn-success btn-header pull-right" data-toggle="modal">
|
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<!-- Modal pool -->
|
|
||||||
<div class="modal fade" id="AddUser" tabindex="-1" role="dialog" aria-labelledby="AddUserLabel" aria-hidden="true">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<form class="form-horizontal" method="post" action="" role="form">{% csrf_token %}
|
|
||||||
<div class="modal-header">
|
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
|
||||||
<h4 class="modal-title">{% trans "Add New User" %}</h4>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-sm-4 control-label">{% trans "Name" %}</label>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<input type="text" class="form-control" name="name" placeholder="john" required pattern="[a-z0-9]+">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-sm-4 control-label">{% trans "Password" %}</label>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<input type="password" class="form-control" name="password" placeholder="*******" {% if not allow_empty_password %}required{% endif %}>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
|
|
||||||
<button type="submit" class="btn btn-primary" name="create">{% trans "Create" %}</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div> <!-- /.modal-content -->
|
|
||||||
</div> <!-- /.modal-dialog -->
|
|
||||||
</div> <!-- /.modal -->
|
|
||||||
{% endif %}
|
|
|
@ -15,9 +15,9 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<form class="form-signin" method="post" role="form">{% csrf_token %}
|
<form class="form-signin" method="post" role="form">{% csrf_token %}
|
||||||
<h2 class="form-signin-heading">{% trans "Sign In" %}</h2>
|
<h2 class="form-signin-heading">{% trans "Sign In" %}</h2>
|
||||||
<input type="text" class="form-control" name="username" placeholder="Login" autocapitalize="none" autocorrect="off" autofocus>
|
<input type="text" class="form-control" name="username" placeholder="{% trans "Login" %}" autocapitalize="none" autocorrect="off" autofocus>
|
||||||
<input type="password" class="form-control" name="password" placeholder="Password">
|
<input type="password" class="form-control" name="password" placeholder="{% trans "Password" %}">
|
||||||
<input name="next" id="next" type="hidden" value="{% url 'allinstances' %}">
|
<input type="hidden" name="next" value="{{ next }}">
|
||||||
<button class="btn btn-lg btn-success btn-block" type="submit">{% trans "Sign In" %}</button>
|
<button class="btn btn-lg btn-success btn-block" type="submit">{% trans "Sign In" %}</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{% if show_profile_edit_password %}
|
{% if perms.accounts.change_password %}
|
||||||
<h3 class="page-header">{% trans "Edit Password" %}</h3>
|
<h3 class="page-header">{% trans "Edit Password" %}</h3>
|
||||||
<form class="form-horizontal" method="post" action="" role="form">{% csrf_token %}
|
<form class="form-horizontal" method="post" action="" role="form">{% csrf_token %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
|
@ -5,6 +5,6 @@ from . import views
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('login/', auth_views.LoginView.as_view(template_name='login.html'), name='login'),
|
path('login/', auth_views.LoginView.as_view(template_name='login.html'), name='login'),
|
||||||
path('logout/', auth_views.LogoutView.as_view(template_name='logout.html'), name='logout'),
|
path('logout/', auth_views.LogoutView.as_view(template_name='logout.html'), name='logout'),
|
||||||
path('profile/', views.profile, name='profile'), path('', views.accounts, name='accounts'),
|
path('profile/', views.profile, name='profile'),
|
||||||
re_path(r'^profile/(?P<user_id>[0-9]+)/$', views.account, name='account'),
|
path('profile/<int:user_id>/', views.account, name='account'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
from django.shortcuts import render
|
|
||||||
from django.http import HttpResponseRedirect
|
|
||||||
from django.urls import reverse
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from accounts.models import *
|
|
||||||
from instances.models import Instance
|
|
||||||
from accounts.forms import UserAddForm
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.validators import ValidationError
|
from django.core.validators import ValidationError
|
||||||
|
from django.http import HttpResponseRedirect
|
||||||
|
from django.shortcuts import render
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from accounts.forms import UserAddForm
|
||||||
|
from accounts.models import *
|
||||||
|
from admin.decorators import superuser_only
|
||||||
|
from instances.models import Instance
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def profile(request):
|
def profile(request):
|
||||||
"""
|
"""
|
||||||
:param request:
|
:param request:
|
||||||
|
@ -18,9 +18,8 @@ def profile(request):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
error_messages = []
|
error_messages = []
|
||||||
user = User.objects.get(id=request.user.id)
|
# user = User.objects.get(id=request.user.id)
|
||||||
publickeys = UserSSHKey.objects.filter(user_id=request.user.id)
|
publickeys = UserSSHKey.objects.filter(user_id=request.user.id)
|
||||||
show_profile_edit_password = settings.SHOW_PROFILE_EDIT_PASSWORD
|
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
if 'username' in request.POST:
|
if 'username' in request.POST:
|
||||||
|
@ -28,7 +27,7 @@ def profile(request):
|
||||||
email = request.POST.get('email', '')
|
email = request.POST.get('email', '')
|
||||||
user.first_name = username
|
user.first_name = username
|
||||||
user.email = email
|
user.email = email
|
||||||
user.save()
|
request.user.save()
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
if 'oldpasswd' in request.POST:
|
if 'oldpasswd' in request.POST:
|
||||||
oldpasswd = request.POST.get('oldpasswd', '')
|
oldpasswd = request.POST.get('oldpasswd', '')
|
||||||
|
@ -38,11 +37,11 @@ def profile(request):
|
||||||
error_messages.append("Passwords didn't enter")
|
error_messages.append("Passwords didn't enter")
|
||||||
if password1 and password2 and password1 != password2:
|
if password1 and password2 and password1 != password2:
|
||||||
error_messages.append("Passwords don't match")
|
error_messages.append("Passwords don't match")
|
||||||
if not user.check_password(oldpasswd):
|
if not request.user.check_password(oldpasswd):
|
||||||
error_messages.append("Old password is wrong!")
|
error_messages.append("Old password is wrong!")
|
||||||
if not error_messages:
|
if not error_messages:
|
||||||
user.set_password(password1)
|
request.user.set_password(password1)
|
||||||
user.save()
|
request.user.save()
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
if 'keyname' in request.POST:
|
if 'keyname' in request.POST:
|
||||||
keyname = request.POST.get('keyname', '')
|
keyname = request.POST.get('keyname', '')
|
||||||
|
@ -69,87 +68,7 @@ def profile(request):
|
||||||
return render(request, 'profile.html', locals())
|
return render(request, 'profile.html', locals())
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@superuser_only
|
||||||
def accounts(request):
|
|
||||||
"""
|
|
||||||
:param request:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
if not request.user.is_superuser:
|
|
||||||
return HttpResponseRedirect(reverse('index'))
|
|
||||||
|
|
||||||
error_messages = []
|
|
||||||
users = User.objects.all().order_by('username')
|
|
||||||
allow_empty_password = settings.ALLOW_EMPTY_PASSWORD
|
|
||||||
|
|
||||||
if request.method == 'POST':
|
|
||||||
if 'create' in request.POST:
|
|
||||||
form = UserAddForm(request.POST)
|
|
||||||
if form.is_valid():
|
|
||||||
data = form.cleaned_data
|
|
||||||
else:
|
|
||||||
for msg_err in form.errors.values():
|
|
||||||
error_messages.append(msg_err.as_text())
|
|
||||||
if not error_messages:
|
|
||||||
new_user = User.objects.create_user(data['name'], None, data['password'])
|
|
||||||
new_user.save()
|
|
||||||
UserAttributes.configure_user(new_user)
|
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
|
||||||
if 'edit' in request.POST:
|
|
||||||
CHECKBOX_MAPPING = {'on': True, 'off': False, }
|
|
||||||
|
|
||||||
user_id = request.POST.get('user_id', '')
|
|
||||||
user_pass = request.POST.get('user_pass', '')
|
|
||||||
user_edit = User.objects.get(id=user_id)
|
|
||||||
|
|
||||||
if user_pass != '': user_edit.set_password(user_pass)
|
|
||||||
user_edit.is_staff = CHECKBOX_MAPPING.get(request.POST.get('user_is_staff', 'off'))
|
|
||||||
user_edit.is_superuser = CHECKBOX_MAPPING.get(request.POST.get('user_is_superuser', 'off'))
|
|
||||||
user_edit.save()
|
|
||||||
|
|
||||||
UserAttributes.create_missing_userattributes(user_edit)
|
|
||||||
user_edit.userattributes.can_clone_instances = CHECKBOX_MAPPING.get(request.POST.get('userattributes_can_clone_instances', 'off'))
|
|
||||||
user_edit.userattributes.max_instances = request.POST.get('userattributes_max_instances', 0)
|
|
||||||
user_edit.userattributes.max_cpus = request.POST.get('userattributes_max_cpus', 0)
|
|
||||||
user_edit.userattributes.max_memory = request.POST.get('userattributes_max_memory', 0)
|
|
||||||
user_edit.userattributes.max_disk_size = request.POST.get('userattributes_max_disk_size', 0)
|
|
||||||
|
|
||||||
try:
|
|
||||||
user_edit.userattributes.clean_fields()
|
|
||||||
except ValidationError as exc:
|
|
||||||
error_messages.append(exc)
|
|
||||||
else:
|
|
||||||
user_edit.userattributes.save()
|
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
|
||||||
if 'block' in request.POST:
|
|
||||||
user_id = request.POST.get('user_id', '')
|
|
||||||
user_block = User.objects.get(id=user_id)
|
|
||||||
user_block.is_active = False
|
|
||||||
user_block.save()
|
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
|
||||||
if 'unblock' in request.POST:
|
|
||||||
user_id = request.POST.get('user_id', '')
|
|
||||||
user_unblock = User.objects.get(id=user_id)
|
|
||||||
user_unblock.is_active = True
|
|
||||||
user_unblock.save()
|
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
|
||||||
if 'delete' in request.POST:
|
|
||||||
user_id = request.POST.get('user_id', '')
|
|
||||||
try:
|
|
||||||
del_user_inst = UserInstance.objects.filter(user_id=user_id)
|
|
||||||
del_user_inst.delete()
|
|
||||||
finally:
|
|
||||||
user_delete = User.objects.get(id=user_id)
|
|
||||||
user_delete.delete()
|
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
|
||||||
|
|
||||||
accounts_template_file = 'accounts.html'
|
|
||||||
if settings.VIEW_ACCOUNTS_STYLE == "list":
|
|
||||||
accounts_template_file = 'accounts-list.html'
|
|
||||||
return render(request, accounts_template_file, locals())
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def account(request, user_id):
|
def account(request, user_id):
|
||||||
"""
|
"""
|
||||||
:param request:
|
:param request:
|
||||||
|
@ -157,9 +76,6 @@ def account(request, user_id):
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not request.user.is_superuser:
|
|
||||||
return HttpResponseRedirect(reverse('index'))
|
|
||||||
|
|
||||||
error_messages = []
|
error_messages = []
|
||||||
user = User.objects.get(id=user_id)
|
user = User.objects.get(id=user_id)
|
||||||
user_insts = UserInstance.objects.filter(user_id=user_id)
|
user_insts = UserInstance.objects.filter(user_id=user_id)
|
||||||
|
@ -185,12 +101,12 @@ def account(request, user_id):
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
if 'add' in request.POST:
|
if 'add' in request.POST:
|
||||||
inst_id = request.POST.get('inst_id', '')
|
inst_id = request.POST.get('inst_id', '')
|
||||||
|
|
||||||
if settings.ALLOW_INSTANCE_MULTIPLE_OWNER:
|
if settings.ALLOW_INSTANCE_MULTIPLE_OWNER:
|
||||||
check_inst = UserInstance.objects.filter(instance_id=int(inst_id), user_id=int(user_id))
|
check_inst = UserInstance.objects.filter(instance_id=int(inst_id), user_id=int(user_id))
|
||||||
else:
|
else:
|
||||||
check_inst = UserInstance.objects.filter(instance_id=int(inst_id))
|
check_inst = UserInstance.objects.filter(instance_id=int(inst_id))
|
||||||
|
|
||||||
if check_inst:
|
if check_inst:
|
||||||
msg = _("Instance already added")
|
msg = _("Instance already added")
|
||||||
error_messages.append(msg)
|
error_messages.append(msg)
|
||||||
|
|
5
admin/apps.py
Normal file
5
admin/apps.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class AdminConfig(AppConfig):
|
||||||
|
name = 'admin'
|
10
admin/decorators.py
Normal file
10
admin/decorators.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
from django.core.exceptions import PermissionDenied
|
||||||
|
|
||||||
|
|
||||||
|
def superuser_only(function):
|
||||||
|
def _inner(request, *args, **kwargs):
|
||||||
|
if not request.user.is_superuser:
|
||||||
|
raise PermissionDenied
|
||||||
|
return function(request, *args, **kwargs)
|
||||||
|
|
||||||
|
return _inner
|
94
admin/forms.py
Normal file
94
admin/forms.py
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
from django import forms
|
||||||
|
from django.contrib.auth.models import Group, User
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from accounts.models import UserAttributes
|
||||||
|
|
||||||
|
from .models import Permission
|
||||||
|
|
||||||
|
|
||||||
|
class GroupForm(forms.ModelForm):
|
||||||
|
permissions = forms.ModelMultipleChoiceField(
|
||||||
|
widget=forms.CheckboxSelectMultiple,
|
||||||
|
queryset=Permission.objects.filter(content_type__model='permissionset'),
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
users = forms.ModelMultipleChoiceField(
|
||||||
|
widget=forms.CheckboxSelectMultiple,
|
||||||
|
queryset=User.objects.all(),
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(GroupForm, self).__init__(*args, **kwargs)
|
||||||
|
instance = getattr(self, 'instance', None)
|
||||||
|
if instance and instance.id:
|
||||||
|
self.fields['users'].initial = self.instance.user_set.all()
|
||||||
|
|
||||||
|
def save_m2m(self):
|
||||||
|
self.instance.user_set.set(self.cleaned_data['users'])
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
instance = super(GroupForm, self).save()
|
||||||
|
self.save_m2m()
|
||||||
|
return instance
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Group
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class UserForm(forms.ModelForm):
|
||||||
|
user_permissions = forms.ModelMultipleChoiceField(
|
||||||
|
widget=forms.CheckboxSelectMultiple,
|
||||||
|
queryset=Permission.objects.filter(content_type__model='permissionset'),
|
||||||
|
label=_('Permissions'),
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
groups = forms.ModelMultipleChoiceField(
|
||||||
|
widget=forms.CheckboxSelectMultiple,
|
||||||
|
queryset=Group.objects.all(),
|
||||||
|
label=_('Groups'),
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = [
|
||||||
|
'username',
|
||||||
|
'groups',
|
||||||
|
'first_name',
|
||||||
|
'last_name',
|
||||||
|
'email',
|
||||||
|
'user_permissions',
|
||||||
|
'is_staff',
|
||||||
|
'is_active',
|
||||||
|
'is_superuser',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class UserCreateForm(UserForm):
|
||||||
|
password = forms.CharField(widget=forms.PasswordInput)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = [
|
||||||
|
'username',
|
||||||
|
'password',
|
||||||
|
'groups',
|
||||||
|
'first_name',
|
||||||
|
'last_name',
|
||||||
|
'email',
|
||||||
|
'user_permissions',
|
||||||
|
'is_staff',
|
||||||
|
'is_active',
|
||||||
|
'is_superuser',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class UserAttributesForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = UserAttributes
|
||||||
|
exclude = ['user']
|
30
admin/migrations/0001_initial.py
Normal file
30
admin/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# Generated by Django 2.2.12 on 2020-05-27 07:01
|
||||||
|
|
||||||
|
import django.contrib.auth.models
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('auth', '0011_update_proxy_permissions'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Permission',
|
||||||
|
fields=[
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'proxy': True,
|
||||||
|
'indexes': [],
|
||||||
|
'constraints': [],
|
||||||
|
},
|
||||||
|
bases=('auth.permission',),
|
||||||
|
managers=[
|
||||||
|
('objects', django.contrib.auth.models.PermissionManager()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
0
admin/migrations/__init__.py
Normal file
0
admin/migrations/__init__.py
Normal file
11
admin/models.py
Normal file
11
admin/models.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
from django.contrib.auth.models import Permission as P
|
||||||
|
|
||||||
|
class Permission(P):
|
||||||
|
"""
|
||||||
|
Proxy model to Django Permissions model allows us to override __str__
|
||||||
|
"""
|
||||||
|
def __str__(self):
|
||||||
|
return f'{self.content_type.app_label}: {self.name}'
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
proxy = True
|
19
admin/templates/admin/common/confirm_delete.html
Normal file
19
admin/templates/admin/common/confirm_delete.html
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
{% load font_awesome %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block title %}{%trans "Delete" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
{%trans "Are you sure you want to delete" %} "{{ object }}"?
|
||||||
|
</div>
|
||||||
|
<a class="btn btn-primary" href="javascript:history.back()">{% icon 'times' %} {% trans "Cancel" %}</a>
|
||||||
|
<button type="submit" class="btn btn-danger">
|
||||||
|
{% icon 'check' %} {% trans "Delete" %}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
28
admin/templates/admin/common/form.html
Normal file
28
admin/templates/admin/common/form.html
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
{% load font_awesome %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block title %}{% trans "User" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<h2 class="page-header">{{ title }}</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="thumbnail col-sm-10 col-sm-offset-1">
|
||||||
|
<form id="create-update" action="" method="post" class="form-horizontal">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% bootstrap_form form layout='horizontal' %}
|
||||||
|
</form>
|
||||||
|
<div class="form-group pull-right">
|
||||||
|
<a class="btn btn-primary" href="javascript:history.back()">{% icon 'times' %} {% trans "Cancel" %}</a>
|
||||||
|
<button type="submit" form="create-update" class="btn btn-success">
|
||||||
|
{% icon 'check' %} {% trans "Save" %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock content %}
|
28
admin/templates/admin/common/list.html
Normal file
28
admin/templates/admin/common/list.html
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load font_awesome %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block title %}{{ title }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% if create_url %}
|
||||||
|
<a class="btn btn-success pull-right" href="{% url create_url %}">{% icon 'plus' %} {%trans "Create New" %}</a>
|
||||||
|
{% endif %}
|
||||||
|
<table class="table table-hover table-striped">
|
||||||
|
{% for object in object_list %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ object }}
|
||||||
|
<div class="btn-group pull-right">
|
||||||
|
<a class="btn btn-success" href="{% url update_url object.id %}">{% icon 'edit' %} {%trans "Edit"%}</a>
|
||||||
|
{% if extra_urls %}
|
||||||
|
{% for url in extra_urls %}
|
||||||
|
<a class="btn btn-primary" href="{% url url.0 object.id %}">{{ url.1 }}</a>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
<a class="btn btn-danger" href="{% url delete_url object.id %}">{% icon 'times' %} {%trans "Delete" %}</a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% endblock %}
|
63
admin/templates/admin/group_list.html
Normal file
63
admin/templates/admin/group_list.html
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load static %}
|
||||||
|
{% load font_awesome %}
|
||||||
|
{% block title %}{% trans "Users" %}{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<a href="{% url 'admin:group_create' %}" class="btn btn-success btn-header pull-right">
|
||||||
|
{% icon 'plus' %}
|
||||||
|
</a>
|
||||||
|
<div class="pull-right search">
|
||||||
|
<input id="filter" class="form-control" type="text" placeholder="{% trans "Search" %}">
|
||||||
|
</div>
|
||||||
|
<h1 class="page-header">{% trans "Groups" %}</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% include 'errors_block.html' %}
|
||||||
|
<div class="row">
|
||||||
|
{% if not groups %}
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<div class="alert alert-warning alert-dismissable">
|
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||||
|
{% icon 'exclamation-triangle '%} <strong>{% trans "Warning" %}:</strong> {% trans "You don't have any groups" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<table class="table table-striped table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Group Name" %}</th>
|
||||||
|
<th>{% trans "Actions" %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="searchable">
|
||||||
|
{% for group in groups %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href=""><strong>{{ group.name }}</strong></a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="pull-right btn-group">
|
||||||
|
<a class="btn btn-primary" href="{% url 'admin:group_update' group.id %}" title="{%trans "Edit" %}">
|
||||||
|
{% icon 'pencil' %}
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-danger" href="{% url 'admin:group_delete' group.id %}" title="{%trans "Delete" %}">
|
||||||
|
{% icon 'times' %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endblock content %}
|
||||||
|
|
||||||
|
{% block script %}
|
||||||
|
<script src="{% static "js/filter-table.js" %}"></script>
|
||||||
|
{% endblock script %}
|
29
admin/templates/admin/user_form.html
Normal file
29
admin/templates/admin/user_form.html
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
{% load font_awesome %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block title %}{% trans "User" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<h2 class="page-header">{{ title }}</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="thumbnail col-sm-10 col-sm-offset-1">
|
||||||
|
<form id="create-update" action="" method="post" class="form-horizontal">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% bootstrap_form user_form layout='horizontal' %}
|
||||||
|
{% bootstrap_form attributes_form layout='horizontal' %}
|
||||||
|
</form>
|
||||||
|
<div class="form-group pull-right">
|
||||||
|
<a class="btn btn-primary" href="javascript:history.back()">{% icon 'times' %} {% trans "Cancel" %}</a>
|
||||||
|
<button type="submit" form="create-update" class="btn btn-success">
|
||||||
|
{% icon 'check' %} {% trans "Save" %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock content %}
|
81
admin/templates/admin/user_list.html
Normal file
81
admin/templates/admin/user_list.html
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load static %}
|
||||||
|
{% load common_tags %}
|
||||||
|
{% load font_awesome %}
|
||||||
|
{% block title %}{% trans "Users" %}{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<a href="{% url 'admin:user_create' %}" class="btn btn-success btn-header pull-right">
|
||||||
|
{% icon 'plus' %}
|
||||||
|
</a>
|
||||||
|
<div class="pull-right search">
|
||||||
|
<input id="filter" class="form-control" type="text" placeholder="{% trans "Search" %}">
|
||||||
|
</div>
|
||||||
|
<h1 class="page-header">{% trans "Users" %}</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% include 'errors_block.html' %}
|
||||||
|
<div class="row">
|
||||||
|
{% if not users %}
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<div class="alert alert-warning alert-dismissable">
|
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||||
|
{% icon 'exclamation-triangle '%} <strong>{% trans "Warning" %}:</strong> {% trans "You don't have any users" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<table class="table table-striped table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Username" %}</th>
|
||||||
|
<th>{% trans "Status" %}</th>
|
||||||
|
<th>{% trans "Staff" %}</th>
|
||||||
|
<th>{% trans "Superuser" %}</th>
|
||||||
|
<th>{% trans "Can Clone" %}</th>
|
||||||
|
<th>{% trans "" %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="searchable">
|
||||||
|
{% for user in users %}
|
||||||
|
{% has_perm user 'instances.clone_instances' as can_clone %}
|
||||||
|
<tr class="{% if not user.is_active %}danger{% endif %}">
|
||||||
|
<td>
|
||||||
|
{{ user.username }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if user.is_active %}
|
||||||
|
{% trans "Active" %}
|
||||||
|
{% else %}
|
||||||
|
{% trans "Blocked" %}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>{% if user.is_staff %}{% icon 'check' %}{% endif %}</td>
|
||||||
|
<td>{% if user.is_superuser %}{% icon 'check' %}</span>{% endif %}</td>
|
||||||
|
<td>{% if can_clone %}{% icon 'check' %}{% endif %}</td>
|
||||||
|
<td>
|
||||||
|
<div class="pull-right btn-group">
|
||||||
|
<a class="btn btn-success" title="{%trans "View Profile" %}" href="{% url 'account' user.id %}">{% icon 'eye' %}</a>
|
||||||
|
<a class="btn btn-primary" title="{%trans "Edit" %}" href="{% url 'admin:user_update' user.id %}">{% icon 'pencil' %}</a>
|
||||||
|
{% if user.is_active %}
|
||||||
|
<a class="btn btn-warning" title="{%trans "Block" %}" href="{% url 'admin:user_block' user.id %}">{% icon 'stop' %}</a>
|
||||||
|
{% else %}
|
||||||
|
<a class="btn btn-success" title="{%trans "Unblock" %}" href="{% url 'admin:user_unblock' user.id %}">{% icon 'play' %}</a>
|
||||||
|
{% endif %}
|
||||||
|
<a class="btn btn-danger" title="{%trans "Delete" %}" href="{% url 'admin:user_delete' user.id %}">{% icon 'times' %}</a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endblock content %}
|
||||||
|
|
||||||
|
{% block script %}
|
||||||
|
<script src="{% static "js/filter-table.js" %}"></script>
|
||||||
|
{% endblock script %}
|
18
admin/urls.py
Normal file
18
admin/urls.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from django.urls import path
|
||||||
|
from django.contrib.auth.views import PasswordChangeView, PasswordChangeDoneView
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('groups/', views.group_list, name='group_list'),
|
||||||
|
path('groups/create/', views.group_create, name='group_create'),
|
||||||
|
path('groups/<int:pk>/update/', views.group_update, name='group_update'),
|
||||||
|
path('groups/<int:pk>/delete/', views.group_delete, name='group_delete'),
|
||||||
|
path('users/', views.user_list, name='user_list'),
|
||||||
|
path('users/create/', views.user_create, name='user_create'),
|
||||||
|
path('users/<int:pk>/update/', views.user_update, name='user_update'),
|
||||||
|
path('users/<int:pk>/delete/', views.user_delete, name='user_delete'),
|
||||||
|
path('users/<int:pk>/block/', views.user_block, name='user_block'),
|
||||||
|
path('users/<int:pk>/unblock/', views.user_unblock, name='user_unblock'),
|
||||||
|
path('logs/', views.logs, name='logs'),
|
||||||
|
]
|
171
admin/views.py
Normal file
171
admin/views.py
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib.auth.decorators import user_passes_test
|
||||||
|
from django.contrib.auth.models import Group, User
|
||||||
|
from django.core.paginator import Paginator
|
||||||
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from accounts.models import UserAttributes
|
||||||
|
from logs.models import Logs
|
||||||
|
|
||||||
|
from . import forms
|
||||||
|
from .decorators import superuser_only
|
||||||
|
|
||||||
|
|
||||||
|
@superuser_only
|
||||||
|
def group_list(request):
|
||||||
|
groups = Group.objects.all()
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
'admin/group_list.html',
|
||||||
|
{
|
||||||
|
'groups': groups,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@superuser_only
|
||||||
|
def group_create(request):
|
||||||
|
form = forms.GroupForm(request.POST or None)
|
||||||
|
if form.is_valid():
|
||||||
|
form.save()
|
||||||
|
return redirect('admin:group_list')
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
'admin/common/form.html',
|
||||||
|
{
|
||||||
|
'form': form,
|
||||||
|
'title': _('Create Group'),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@superuser_only
|
||||||
|
def group_update(request, pk):
|
||||||
|
group = get_object_or_404(Group, pk=pk)
|
||||||
|
form = forms.GroupForm(request.POST or None, instance=group)
|
||||||
|
if form.is_valid():
|
||||||
|
form.save()
|
||||||
|
return redirect('admin:group_list')
|
||||||
|
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
'admin/common/form.html',
|
||||||
|
{
|
||||||
|
'form': form,
|
||||||
|
'title': _('Update Group'),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@superuser_only
|
||||||
|
def group_delete(request, pk):
|
||||||
|
group = get_object_or_404(Group, pk=pk)
|
||||||
|
if request.method == 'POST':
|
||||||
|
group.delete()
|
||||||
|
return redirect('admin:group_list')
|
||||||
|
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
'admin/common/confirm_delete.html',
|
||||||
|
{'object': group},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@superuser_only
|
||||||
|
def user_list(request):
|
||||||
|
users = User.objects.all()
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
'admin/user_list.html',
|
||||||
|
{
|
||||||
|
'users': users,
|
||||||
|
'title': _('Users'),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@superuser_only
|
||||||
|
def user_create(request):
|
||||||
|
user_form = forms.UserCreateForm(request.POST or None)
|
||||||
|
attributes_form = forms.UserAttributesForm(request.POST or None)
|
||||||
|
if user_form.is_valid() and attributes_form.is_valid():
|
||||||
|
user = user_form.save()
|
||||||
|
password = user_form.cleaned_data['password']
|
||||||
|
user.set_password(password)
|
||||||
|
user.save()
|
||||||
|
attributes = attributes_form.save(commit=False)
|
||||||
|
attributes.user = user
|
||||||
|
attributes.save()
|
||||||
|
return redirect('admin:user_list')
|
||||||
|
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
'admin/user_form.html',
|
||||||
|
{
|
||||||
|
'user_form': user_form,
|
||||||
|
'attributes_form': attributes_form,
|
||||||
|
'title': _('Create User')
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@superuser_only
|
||||||
|
def user_update(request, pk):
|
||||||
|
user = get_object_or_404(User, pk=pk)
|
||||||
|
attributes = UserAttributes.objects.get(user=user)
|
||||||
|
user_form = forms.UserForm(request.POST or None, instance=user)
|
||||||
|
attributes_form = forms.UserAttributesForm(request.POST or None, instance=attributes)
|
||||||
|
if user_form.is_valid() and attributes_form.is_valid():
|
||||||
|
user_form.save()
|
||||||
|
attributes_form.save()
|
||||||
|
return redirect('admin:user_list')
|
||||||
|
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
'admin/user_form.html',
|
||||||
|
{
|
||||||
|
'user_form': user_form,
|
||||||
|
'attributes_form': attributes_form,
|
||||||
|
'title': _('Update User')
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@superuser_only
|
||||||
|
def user_delete(request, pk):
|
||||||
|
user = get_object_or_404(User, pk=pk)
|
||||||
|
if request.method == 'POST':
|
||||||
|
user.delete()
|
||||||
|
return redirect('admin:user_list')
|
||||||
|
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
'admin/common/confirm_delete.html',
|
||||||
|
{'object': user},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@superuser_only
|
||||||
|
def user_block(request, pk):
|
||||||
|
user: User = get_object_or_404(User, pk=pk)
|
||||||
|
user.is_active = False
|
||||||
|
user.save()
|
||||||
|
return redirect('admin:user_list')
|
||||||
|
|
||||||
|
|
||||||
|
@superuser_only
|
||||||
|
def user_unblock(request, pk):
|
||||||
|
user: User = get_object_or_404(User, pk=pk)
|
||||||
|
user.is_active = True
|
||||||
|
user.save()
|
||||||
|
return redirect('admin:user_list')
|
||||||
|
|
||||||
|
|
||||||
|
@superuser_only
|
||||||
|
def logs(request):
|
||||||
|
l = Logs.objects.order_by('-date')
|
||||||
|
paginator = Paginator(l, settings.LOGS_PER_PAGE)
|
||||||
|
page = request.GET.get('page', 1)
|
||||||
|
logs = paginator.page(page)
|
||||||
|
return render(request, 'admin/logs.html', {'logs': logs})
|
|
@ -3,7 +3,6 @@ from django.utils import timezone
|
||||||
from django.http import HttpResponse, HttpResponseRedirect
|
from django.http import HttpResponse, HttpResponseRedirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.shortcuts import render, get_object_or_404
|
from django.shortcuts import render, get_object_or_404
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from computes.models import Compute
|
from computes.models import Compute
|
||||||
from instances.models import Instance
|
from instances.models import Instance
|
||||||
from accounts.models import UserInstance
|
from accounts.models import UserInstance
|
||||||
|
@ -11,39 +10,37 @@ from computes.forms import ComputeAddTcpForm, ComputeAddSshForm, ComputeEditHost
|
||||||
from vrtManager.hostdetails import wvmHostDetails
|
from vrtManager.hostdetails import wvmHostDetails
|
||||||
from vrtManager.connection import CONN_SSH, CONN_TCP, CONN_TLS, CONN_SOCKET, connection_manager, wvmConnect
|
from vrtManager.connection import CONN_SSH, CONN_TCP, CONN_TLS, CONN_SOCKET, connection_manager, wvmConnect
|
||||||
from libvirt import libvirtError
|
from libvirt import libvirtError
|
||||||
|
from admin.decorators import superuser_only
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@superuser_only
|
||||||
def computes(request):
|
def computes(request):
|
||||||
"""
|
"""
|
||||||
:param request:
|
:param request:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not request.user.is_superuser:
|
|
||||||
return HttpResponseRedirect(reverse('index'))
|
|
||||||
|
|
||||||
def get_hosts_status(computes):
|
def get_hosts_status(computes):
|
||||||
"""
|
"""
|
||||||
Function return all hosts all vds on host
|
Function return all hosts all vds on host
|
||||||
"""
|
"""
|
||||||
compute_data = []
|
compute_data = []
|
||||||
for compute in computes:
|
for compute in computes:
|
||||||
compute_data.append({'id': compute.id,
|
compute_data.append({
|
||||||
'name': compute.name,
|
'id': compute.id,
|
||||||
'hostname': compute.hostname,
|
'name': compute.name,
|
||||||
'status': connection_manager.host_is_up(compute.type, compute.hostname),
|
'hostname': compute.hostname,
|
||||||
'type': compute.type,
|
'status': connection_manager.host_is_up(compute.type, compute.hostname),
|
||||||
'login': compute.login,
|
'type': compute.type,
|
||||||
'password': compute.password,
|
'login': compute.login,
|
||||||
'details': compute.details
|
'password': compute.password,
|
||||||
})
|
'details': compute.details
|
||||||
|
})
|
||||||
return compute_data
|
return compute_data
|
||||||
|
|
||||||
error_messages = []
|
error_messages = []
|
||||||
computes = Compute.objects.filter().order_by('name')
|
computes = Compute.objects.filter().order_by('name')
|
||||||
computes_info = get_hosts_status(computes)
|
computes_info = get_hosts_status(computes)
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
if 'host_del' in request.POST:
|
if 'host_del' in request.POST:
|
||||||
compute_id = request.POST.get('host_id', '')
|
compute_id = request.POST.get('host_id', '')
|
||||||
|
@ -135,7 +132,7 @@ def computes(request):
|
||||||
return render(request, 'computes.html', locals())
|
return render(request, 'computes.html', locals())
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@superuser_only
|
||||||
def overview(request, compute_id):
|
def overview(request, compute_id):
|
||||||
"""
|
"""
|
||||||
:param request:
|
:param request:
|
||||||
|
@ -143,17 +140,16 @@ def overview(request, compute_id):
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not request.user.is_superuser:
|
|
||||||
return HttpResponseRedirect(reverse('index'))
|
|
||||||
|
|
||||||
error_messages = []
|
error_messages = []
|
||||||
compute = get_object_or_404(Compute, pk=compute_id)
|
compute = get_object_or_404(Compute, pk=compute_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
conn = wvmHostDetails(compute.hostname,
|
conn = wvmHostDetails(
|
||||||
compute.login,
|
compute.hostname,
|
||||||
compute.password,
|
compute.login,
|
||||||
compute.type)
|
compute.password,
|
||||||
|
compute.type,
|
||||||
|
)
|
||||||
hostname, host_arch, host_memory, logical_cpu, model_cpu, uri_conn = conn.get_node_info()
|
hostname, host_arch, host_memory, logical_cpu, model_cpu, uri_conn = conn.get_node_info()
|
||||||
hypervisor = conn.get_hypervisors_domain_types()
|
hypervisor = conn.get_hypervisors_domain_types()
|
||||||
mem_usage = conn.get_memory_usage()
|
mem_usage = conn.get_memory_usage()
|
||||||
|
@ -167,7 +163,6 @@ def overview(request, compute_id):
|
||||||
return render(request, 'overview.html', locals())
|
return render(request, 'overview.html', locals())
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def compute_graph(request, compute_id):
|
def compute_graph(request, compute_id):
|
||||||
"""
|
"""
|
||||||
:param request:
|
:param request:
|
||||||
|
@ -176,10 +171,12 @@ def compute_graph(request, compute_id):
|
||||||
"""
|
"""
|
||||||
compute = get_object_or_404(Compute, pk=compute_id)
|
compute = get_object_or_404(Compute, pk=compute_id)
|
||||||
try:
|
try:
|
||||||
conn = wvmHostDetails(compute.hostname,
|
conn = wvmHostDetails(
|
||||||
compute.login,
|
compute.hostname,
|
||||||
compute.password,
|
compute.login,
|
||||||
compute.type)
|
compute.password,
|
||||||
|
compute.type,
|
||||||
|
)
|
||||||
current_time = timezone.now().strftime("%H:%M:%S")
|
current_time = timezone.now().strftime("%H:%M:%S")
|
||||||
cpu_usage = conn.get_cpu_usage()
|
cpu_usage = conn.get_cpu_usage()
|
||||||
mem_usage = conn.get_memory_usage()
|
mem_usage = conn.get_memory_usage()
|
||||||
|
@ -188,24 +185,27 @@ def compute_graph(request, compute_id):
|
||||||
cpu_usage = {'usage': 0}
|
cpu_usage = {'usage': 0}
|
||||||
mem_usage = {'usage': 0}
|
mem_usage = {'usage': 0}
|
||||||
|
|
||||||
data = json.dumps({'cpudata': cpu_usage['usage'],
|
data = json.dumps({
|
||||||
'memdata': mem_usage,
|
'cpudata': cpu_usage['usage'],
|
||||||
'timeline': current_time})
|
'memdata': mem_usage,
|
||||||
|
'timeline': current_time,
|
||||||
|
})
|
||||||
response = HttpResponse()
|
response = HttpResponse()
|
||||||
response['Content-Type'] = "text/javascript"
|
response['Content-Type'] = "text/javascript"
|
||||||
response.write(data)
|
response.write(data)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def get_compute_disk_buses(request, compute_id, arch, machine, disk):
|
def get_compute_disk_buses(request, compute_id, arch, machine, disk):
|
||||||
data = dict()
|
data = dict()
|
||||||
compute = get_object_or_404(Compute, pk=compute_id)
|
compute = get_object_or_404(Compute, pk=compute_id)
|
||||||
try:
|
try:
|
||||||
conn = wvmConnect(compute.hostname,
|
conn = wvmConnect(
|
||||||
compute.login,
|
compute.hostname,
|
||||||
compute.password,
|
compute.login,
|
||||||
compute.type)
|
compute.password,
|
||||||
|
compute.type,
|
||||||
|
)
|
||||||
|
|
||||||
disk_device_types = conn.get_disk_device_types(arch, machine)
|
disk_device_types = conn.get_disk_device_types(arch, machine)
|
||||||
|
|
||||||
|
@ -224,15 +224,16 @@ def get_compute_disk_buses(request, compute_id, arch, machine, disk):
|
||||||
return HttpResponse(json.dumps(data))
|
return HttpResponse(json.dumps(data))
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def get_compute_machine_types(request, compute_id, arch):
|
def get_compute_machine_types(request, compute_id, arch):
|
||||||
data = dict()
|
data = dict()
|
||||||
try:
|
try:
|
||||||
compute = get_object_or_404(Compute, pk=compute_id)
|
compute = get_object_or_404(Compute, pk=compute_id)
|
||||||
conn = wvmConnect(compute.hostname,
|
conn = wvmConnect(
|
||||||
compute.login,
|
compute.hostname,
|
||||||
compute.password,
|
compute.login,
|
||||||
compute.type)
|
compute.password,
|
||||||
|
compute.type,
|
||||||
|
)
|
||||||
data['machines'] = conn.get_machine_types(arch)
|
data['machines'] = conn.get_machine_types(arch)
|
||||||
except libvirtError:
|
except libvirtError:
|
||||||
pass
|
pass
|
||||||
|
@ -240,15 +241,16 @@ def get_compute_machine_types(request, compute_id, arch):
|
||||||
return HttpResponse(json.dumps(data))
|
return HttpResponse(json.dumps(data))
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def get_compute_video_models(request, compute_id, arch, machine):
|
def get_compute_video_models(request, compute_id, arch, machine):
|
||||||
data = dict()
|
data = dict()
|
||||||
try:
|
try:
|
||||||
compute = get_object_or_404(Compute, pk=compute_id)
|
compute = get_object_or_404(Compute, pk=compute_id)
|
||||||
conn = wvmConnect(compute.hostname,
|
conn = wvmConnect(
|
||||||
compute.login,
|
compute.hostname,
|
||||||
compute.password,
|
compute.login,
|
||||||
compute.type)
|
compute.password,
|
||||||
|
compute.type,
|
||||||
|
)
|
||||||
data['videos'] = conn.get_video_models(arch, machine)
|
data['videos'] = conn.get_video_models(arch, machine)
|
||||||
except libvirtError:
|
except libvirtError:
|
||||||
pass
|
pass
|
||||||
|
@ -256,15 +258,16 @@ def get_compute_video_models(request, compute_id, arch, machine):
|
||||||
return HttpResponse(json.dumps(data))
|
return HttpResponse(json.dumps(data))
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def get_dom_capabilities(request, compute_id, arch, machine):
|
def get_dom_capabilities(request, compute_id, arch, machine):
|
||||||
data = dict()
|
data = dict()
|
||||||
try:
|
try:
|
||||||
compute = get_object_or_404(Compute, pk=compute_id)
|
compute = get_object_or_404(Compute, pk=compute_id)
|
||||||
conn = wvmConnect(compute.hostname,
|
conn = wvmConnect(
|
||||||
compute.login,
|
compute.hostname,
|
||||||
compute.password,
|
compute.login,
|
||||||
compute.type)
|
compute.password,
|
||||||
|
compute.type,
|
||||||
|
)
|
||||||
data['videos'] = conn.get_disk_device_types(arch, machine)
|
data['videos'] = conn.get_disk_device_types(arch, machine)
|
||||||
data['bus'] = conn.get_disk_device_types(arch, machine)
|
data['bus'] = conn.get_disk_device_types(arch, machine)
|
||||||
except libvirtError:
|
except libvirtError:
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
Django==2.2.12
|
Django==2.2.12
|
||||||
websockify==0.9.0
|
django-bootstrap3==12.1.0
|
||||||
|
django-fa==1.0.0
|
||||||
|
django-login-required-middleware==0.5.0
|
||||||
gunicorn==20.0.4
|
gunicorn==20.0.4
|
||||||
lxml==4.5.0
|
|
||||||
libvirt-python==6.1.0
|
libvirt-python==6.1.0
|
||||||
six
|
lxml==4.5.0
|
||||||
pytz
|
numpy==1.18.4
|
||||||
rwlock
|
pytz==2020.1
|
||||||
|
rwlock==0.0.7
|
||||||
|
six==1.15.0
|
||||||
|
sqlparse==0.3.1
|
||||||
|
websockify==0.9.0
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import re
|
import re
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from instances.models import Instance
|
from instances.models import Instance
|
||||||
from vrtManager.instance import wvmInstance
|
from vrtManager.instance import wvmInstance
|
||||||
from webvirtcloud.settings import WS_PUBLIC_PORT
|
from webvirtcloud.settings import WS_PUBLIC_PORT
|
||||||
|
@ -8,7 +7,6 @@ from webvirtcloud.settings import WS_PUBLIC_HOST
|
||||||
from libvirt import libvirtError
|
from libvirt import libvirtError
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def console(request):
|
def console(request):
|
||||||
"""
|
"""
|
||||||
:param request:
|
:param request:
|
||||||
|
|
|
@ -2,7 +2,6 @@ from django.shortcuts import render, get_object_or_404
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from computes.models import Compute
|
from computes.models import Compute
|
||||||
from create.models import Flavor
|
from create.models import Flavor
|
||||||
from create.forms import FlavorAddForm, NewVMForm
|
from create.forms import FlavorAddForm, NewVMForm
|
||||||
|
@ -24,14 +23,12 @@ from webvirtcloud.settings import INSTANCE_FIRMWARE_DEFAULT_TYPE
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from logs.views import addlogmsg
|
from logs.views import addlogmsg
|
||||||
|
from admin.decorators import superuser_only
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@superuser_only
|
||||||
def create_instance_select_type(request, compute_id):
|
def create_instance_select_type(request, compute_id):
|
||||||
|
|
||||||
if not request.user.is_superuser:
|
|
||||||
return HttpResponseRedirect(reverse('index'))
|
|
||||||
|
|
||||||
conn = None
|
conn = None
|
||||||
error_messages = list()
|
error_messages = list()
|
||||||
storages = list()
|
storages = list()
|
||||||
|
@ -41,10 +38,7 @@ def create_instance_select_type(request, compute_id):
|
||||||
compute = get_object_or_404(Compute, pk=compute_id)
|
compute = get_object_or_404(Compute, pk=compute_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
conn = wvmCreate(compute.hostname,
|
conn = wvmCreate(compute.hostname, compute.login, compute.password, compute.type)
|
||||||
compute.login,
|
|
||||||
compute.password,
|
|
||||||
compute.type)
|
|
||||||
instances = conn.get_instances()
|
instances = conn.get_instances()
|
||||||
all_hypervisors = conn.get_hypervisors_machines()
|
all_hypervisors = conn.get_hypervisors_machines()
|
||||||
# Supported hypervisors by webvirtcloud: i686, x86_64(for now)
|
# Supported hypervisors by webvirtcloud: i686, x86_64(for now)
|
||||||
|
@ -76,7 +70,7 @@ def create_instance_select_type(request, compute_id):
|
||||||
return render(request, 'create_instance_w1.html', locals())
|
return render(request, 'create_instance_w1.html', locals())
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@superuser_only
|
||||||
def create_instance(request, compute_id, arch, machine):
|
def create_instance(request, compute_id, arch, machine):
|
||||||
"""
|
"""
|
||||||
:param request:
|
:param request:
|
||||||
|
@ -85,8 +79,6 @@ def create_instance(request, compute_id, arch, machine):
|
||||||
:param machine:
|
:param machine:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
if not request.user.is_superuser:
|
|
||||||
return HttpResponseRedirect(reverse('index'))
|
|
||||||
|
|
||||||
conn = None
|
conn = None
|
||||||
error_messages = list()
|
error_messages = list()
|
||||||
|
@ -99,10 +91,7 @@ def create_instance(request, compute_id, arch, machine):
|
||||||
flavors = Flavor.objects.filter().order_by('id')
|
flavors = Flavor.objects.filter().order_by('id')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
conn = wvmCreate(compute.hostname,
|
conn = wvmCreate(compute.hostname, compute.login, compute.password, compute.type)
|
||||||
compute.login,
|
|
||||||
compute.password,
|
|
||||||
compute.type)
|
|
||||||
|
|
||||||
default_firmware = INSTANCE_FIRMWARE_DEFAULT_TYPE
|
default_firmware = INSTANCE_FIRMWARE_DEFAULT_TYPE
|
||||||
default_cpu_mode = INSTANCE_CPU_DEFAULT_MODE
|
default_cpu_mode = INSTANCE_CPU_DEFAULT_MODE
|
||||||
|
@ -153,10 +142,7 @@ def create_instance(request, compute_id, arch, machine):
|
||||||
form = FlavorAddForm(request.POST)
|
form = FlavorAddForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
data = form.cleaned_data
|
data = form.cleaned_data
|
||||||
create_flavor = Flavor(label=data['label'],
|
create_flavor = Flavor(label=data['label'], vcpu=data['vcpu'], memory=data['memory'], disk=data['disk'])
|
||||||
vcpu=data['vcpu'],
|
|
||||||
memory=data['memory'],
|
|
||||||
disk=data['disk'])
|
|
||||||
create_flavor.save()
|
create_flavor.save()
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
if 'delete_flavor' in request.POST:
|
if 'delete_flavor' in request.POST:
|
||||||
|
@ -187,7 +173,9 @@ def create_instance(request, compute_id, arch, machine):
|
||||||
error_messages.append(error_msg)
|
error_messages.append(error_msg)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
path = conn.create_volume(data['storage'], data['name'], data['hdd_size'],
|
path = conn.create_volume(data['storage'],
|
||||||
|
data['name'],
|
||||||
|
data['hdd_size'],
|
||||||
metadata=meta_prealloc)
|
metadata=meta_prealloc)
|
||||||
volume = dict()
|
volume = dict()
|
||||||
volume['path'] = path
|
volume['path'] = path
|
||||||
|
@ -206,7 +194,10 @@ def create_instance(request, compute_id, arch, machine):
|
||||||
error_msg = _("Image has already exist. Please check volumes or change instance name")
|
error_msg = _("Image has already exist. Please check volumes or change instance name")
|
||||||
error_messages.append(error_msg)
|
error_messages.append(error_msg)
|
||||||
else:
|
else:
|
||||||
clone_path = conn.clone_from_template(data['name'], templ_path, data['storage'], metadata=meta_prealloc)
|
clone_path = conn.clone_from_template(data['name'],
|
||||||
|
templ_path,
|
||||||
|
data['storage'],
|
||||||
|
metadata=meta_prealloc)
|
||||||
volume = dict()
|
volume = dict()
|
||||||
volume['path'] = clone_path
|
volume['path'] = clone_path
|
||||||
volume['type'] = conn.get_volume_type(clone_path)
|
volume['type'] = conn.get_volume_type(clone_path)
|
||||||
|
@ -241,23 +232,36 @@ def create_instance(request, compute_id, arch, machine):
|
||||||
firmware["readonly"] = 'yes'
|
firmware["readonly"] = 'yes'
|
||||||
firmware["type"] = 'pflash'
|
firmware["type"] = 'pflash'
|
||||||
if 'secboot' in firmware["loader"] and machine != 'q35':
|
if 'secboot' in firmware["loader"] and machine != 'q35':
|
||||||
messages.warning(request, "Changing machine type from '%s' to 'q35' "
|
messages.warning(
|
||||||
"which is required for UEFI secure boot." % machine)
|
request, "Changing machine type from '%s' to 'q35' "
|
||||||
|
"which is required for UEFI secure boot." % machine)
|
||||||
machine = 'q35'
|
machine = 'q35'
|
||||||
firmware["secure"] = 'yes'
|
firmware["secure"] = 'yes'
|
||||||
|
|
||||||
if not error_messages:
|
if not error_messages:
|
||||||
uuid = util.randomUUID()
|
uuid = util.randomUUID()
|
||||||
try:
|
try:
|
||||||
conn.create_instance(name=data['name'], memory=data['memory'], vcpu=data['vcpu'],
|
conn.create_instance(name=data['name'],
|
||||||
vcpu_mode=data['vcpu_mode'], uuid=uuid, arch=arch, machine=machine,
|
memory=data['memory'],
|
||||||
|
vcpu=data['vcpu'],
|
||||||
|
vcpu_mode=data['vcpu_mode'],
|
||||||
|
uuid=uuid,
|
||||||
|
arch=arch,
|
||||||
|
machine=machine,
|
||||||
firmware=firmware,
|
firmware=firmware,
|
||||||
images=volume_list, cache_mode=data['cache_mode'],
|
images=volume_list,
|
||||||
io_mode=default_io, discard_mode=default_discard, detect_zeroes_mode=default_zeroes,
|
cache_mode=data['cache_mode'],
|
||||||
networks=data['networks'], virtio=data['virtio'],
|
io_mode=default_io,
|
||||||
listen_addr=data["listener_addr"], nwfilter=data["nwfilter"],
|
discard_mode=default_discard,
|
||||||
graphics=data["graphics"], video=data["video"],
|
detect_zeroes_mode=default_zeroes,
|
||||||
console_pass=data["console_pass"], mac=data['mac'],
|
networks=data['networks'],
|
||||||
|
virtio=data['virtio'],
|
||||||
|
listen_addr=data["listener_addr"],
|
||||||
|
nwfilter=data["nwfilter"],
|
||||||
|
graphics=data["graphics"],
|
||||||
|
video=data["video"],
|
||||||
|
console_pass=data["console_pass"],
|
||||||
|
mac=data['mac'],
|
||||||
qemu_ga=data['qemu_ga'])
|
qemu_ga=data['qemu_ga'])
|
||||||
create_instance = Instance(compute_id=compute_id, name=data['name'], uuid=uuid)
|
create_instance = Instance(compute_id=compute_id, name=data['name'], uuid=uuid)
|
||||||
create_instance.save()
|
create_instance.save()
|
||||||
|
|
24
instances/migrations/0002_permissionset.py
Normal file
24
instances/migrations/0002_permissionset.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# Generated by Django 2.2.12 on 2020-05-27 07:01
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('instances', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='PermissionSet',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'permissions': (('clone_instances', 'Can clone instances'),),
|
||||||
|
'managed': False,
|
||||||
|
'default_permissions': (),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
35
instances/migrations/0003_migrate_can_clone_instances.py
Normal file
35
instances/migrations/0003_migrate_can_clone_instances.py
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_can_clone_instances(apps, schema_editor):
|
||||||
|
from django.contrib.auth.models import User, Permission
|
||||||
|
user: User
|
||||||
|
users = User.objects.all()
|
||||||
|
|
||||||
|
permission = Permission.objects.get(codename='clone_instances')
|
||||||
|
|
||||||
|
for user in users:
|
||||||
|
if user.userattributes.can_clone_instances:
|
||||||
|
user.user_permissions.add(permission)
|
||||||
|
|
||||||
|
|
||||||
|
def reverse_can_clone_instances(apps, schema_editor):
|
||||||
|
from django.contrib.auth.models import User, Permission
|
||||||
|
user: User
|
||||||
|
users = User.objects.all()
|
||||||
|
|
||||||
|
permission = Permission.objects.get(codename='clone_instances')
|
||||||
|
|
||||||
|
for user in users:
|
||||||
|
user.user_permissions.remove(permission)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('instances', '0002_permissionset'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(migrate_can_clone_instances, reverse_can_clone_instances),
|
||||||
|
]
|
|
@ -1,4 +1,7 @@
|
||||||
from django.db.models import Model, ForeignKey, CharField, BooleanField, DateField, CASCADE
|
from django.db.models import (CASCADE, BooleanField, CharField, DateField,
|
||||||
|
ForeignKey, Model)
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from computes.models import Compute
|
from computes.models import Compute
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,3 +14,15 @@ class Instance(Model):
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
class PermissionSet(Model):
|
||||||
|
"""
|
||||||
|
Dummy model for holding set of permissions we need to be automatically added by Django
|
||||||
|
"""
|
||||||
|
class Meta:
|
||||||
|
default_permissions = ()
|
||||||
|
permissions = (
|
||||||
|
('clone_instances', _('Can clone instances')),
|
||||||
|
)
|
||||||
|
|
||||||
|
managed = False
|
||||||
|
|
31
instances/templates/bottom_bar.html
Normal file
31
instances/templates/bottom_bar.html
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
{% load i18n %}
|
||||||
|
<nav class="navbar navbar-default navbar navbar-fixed-bottom">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-header">
|
||||||
|
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar_bottom" aria-expanded="false" aria-controls="navbar">
|
||||||
|
<span class="sr-only">{% trans 'Toggle navigation' %}</span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
</button>
|
||||||
|
<a class="navbar-brand" href="#">{% trans 'HOST' %}</a>
|
||||||
|
</div>
|
||||||
|
<div id="navbar_bottom" class="navbar-collapse collapse">
|
||||||
|
<ul class="nav navbar-nav">
|
||||||
|
<li>
|
||||||
|
<a class="navbar-link" href="{% url 'overview' compute.id %}">
|
||||||
|
<i class="fa fa-server"></i>
|
||||||
|
{{ compute.name }} {% if compute.name != compute.hostname %} - {{ compute.hostname }}{% endif %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'instances' compute.id %}"><i class="fa fa-desktop"></i> {% trans "Instances" %}</a>
|
||||||
|
</li>
|
||||||
|
<li class="active">
|
||||||
|
<a href="{% url 'instance' compute.id vname %}"><i class="fa fa-hdd-o"></i> {{ vname }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="bottom-bar-margin"></div>
|
|
@ -4,37 +4,6 @@
|
||||||
{% block title %}{% trans "Instance" %} - {{ vname }}{% endblock %}
|
{% block title %}{% trans "Instance" %} - {{ vname }}{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% include 'pleasewaitdialog.html' %}
|
{% include 'pleasewaitdialog.html' %}
|
||||||
{% if bottom_bar %}
|
|
||||||
<nav class="navbar navbar-default navbar navbar-fixed-bottom">
|
|
||||||
<div class="container">
|
|
||||||
<div class="navbar-header">
|
|
||||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar_bottom" aria-expanded="false" aria-controls="navbar">
|
|
||||||
<span class="sr-only">{% trans 'Toggle navigation' %}</span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
</button>
|
|
||||||
<a class="navbar-brand" href="#">{% trans 'HOST' %}</a>
|
|
||||||
</div>
|
|
||||||
<div id="navbar_bottom" class="navbar-collapse collapse">
|
|
||||||
<ul class="nav navbar-nav">
|
|
||||||
<li>
|
|
||||||
<a class="navbar-link" href="{% url 'overview' compute.id %}">
|
|
||||||
<i class="fa fa-server"></i>
|
|
||||||
{{ compute.name }} {% if compute.name != compute.hostname %} - {{ compute.hostname }}{% endif %}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="{% url 'instances' compute.id %}"><i class="fa fa-desktop"></i> {% trans "Instances" %}</a>
|
|
||||||
</li>
|
|
||||||
<li class="active">
|
|
||||||
<a href="{% url 'instance' compute.id vname %}"><i class="fa fa-hdd-o"></i> {{ vname }}</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
{% endif %}
|
|
||||||
<!-- Page Heading -->
|
<!-- Page Heading -->
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -1690,6 +1659,11 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if bottom_bar %}
|
||||||
|
{% include 'bottom_bar.html' %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block script %}
|
{% block script %}
|
||||||
<script src="{% static "js/ace.js" %}" type="text/javascript" charset="utf-8"></script>
|
<script src="{% static "js/ace.js" %}" type="text/javascript" charset="utf-8"></script>
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
from django import template
|
|
||||||
import re
|
|
||||||
|
|
||||||
register = template.Library()
|
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag
|
|
||||||
def class_active(request, pattern):
|
|
||||||
if re.search(pattern, request.path):
|
|
||||||
return 'class="active"'
|
|
||||||
return ''
|
|
|
@ -11,7 +11,6 @@ from django.http import HttpResponse, HttpResponseRedirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.shortcuts import render, get_object_or_404
|
from django.shortcuts import render, get_object_or_404
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from computes.models import Compute
|
from computes.models import Compute
|
||||||
from instances.models import Instance
|
from instances.models import Instance
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
@ -29,7 +28,6 @@ from django.contrib import messages
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def index(request):
|
def index(request):
|
||||||
"""
|
"""
|
||||||
:param request:
|
:param request:
|
||||||
|
@ -38,7 +36,6 @@ def index(request):
|
||||||
return HttpResponseRedirect(reverse('allinstances'))
|
return HttpResponseRedirect(reverse('allinstances'))
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def allinstances(request):
|
def allinstances(request):
|
||||||
"""
|
"""
|
||||||
INSTANCES LIST FOR ALL HOSTS
|
INSTANCES LIST FOR ALL HOSTS
|
||||||
|
@ -70,7 +67,6 @@ def allinstances(request):
|
||||||
return render(request, 'allinstances.html', locals())
|
return render(request, 'allinstances.html', locals())
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def instances(request, compute_id):
|
def instances(request, compute_id):
|
||||||
"""
|
"""
|
||||||
:param request:
|
:param request:
|
||||||
|
@ -99,7 +95,6 @@ def instances(request, compute_id):
|
||||||
return render(request, 'instances.html', locals())
|
return render(request, 'instances.html', locals())
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def instance(request, compute_id, vname):
|
def instance(request, compute_id, vname):
|
||||||
"""
|
"""
|
||||||
:param request:
|
:param request:
|
||||||
|
@ -1081,7 +1076,6 @@ def instance(request, compute_id, vname):
|
||||||
return render(request, 'instance.html', locals())
|
return render(request, 'instance.html', locals())
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def inst_status(request, compute_id, vname):
|
def inst_status(request, compute_id, vname):
|
||||||
"""
|
"""
|
||||||
:param request:
|
:param request:
|
||||||
|
@ -1276,7 +1270,6 @@ def instances_actions(request):
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def inst_graph(request, compute_id, vname):
|
def inst_graph(request, compute_id, vname):
|
||||||
"""
|
"""
|
||||||
:param request:
|
:param request:
|
||||||
|
@ -1339,7 +1332,6 @@ def _get_dhcp_mac_address(vname):
|
||||||
return mac
|
return mac
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def guess_mac_address(request, vname):
|
def guess_mac_address(request, vname):
|
||||||
data = {'vname': vname}
|
data = {'vname': vname}
|
||||||
mac = _get_dhcp_mac_address(vname)
|
mac = _get_dhcp_mac_address(vname)
|
||||||
|
@ -1358,14 +1350,12 @@ def _get_random_mac_address():
|
||||||
return mac
|
return mac
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def random_mac_address(request):
|
def random_mac_address(request):
|
||||||
data = dict()
|
data = dict()
|
||||||
data['mac'] = _get_random_mac_address()
|
data['mac'] = _get_random_mac_address()
|
||||||
return HttpResponse(json.dumps(data))
|
return HttpResponse(json.dumps(data))
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def guess_clone_name(request):
|
def guess_clone_name(request):
|
||||||
dhcp_file = '/srv/webvirtcloud/dhcpd.conf'
|
dhcp_file = '/srv/webvirtcloud/dhcpd.conf'
|
||||||
prefix = settings.CLONE_INSTANCE_DEFAULT_PREFIX
|
prefix = settings.CLONE_INSTANCE_DEFAULT_PREFIX
|
||||||
|
@ -1382,7 +1372,6 @@ def guess_clone_name(request):
|
||||||
return HttpResponse(json.dumps({}))
|
return HttpResponse(json.dumps({}))
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def check_instance(request, vname):
|
def check_instance(request, vname):
|
||||||
instance = Instance.objects.filter(name=vname)
|
instance = Instance.objects.filter(name=vname)
|
||||||
data = {'vname': vname, 'exists': False}
|
data = {'vname': vname, 'exists': False}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
from django.shortcuts import render, get_object_or_404
|
from django.shortcuts import render, get_object_or_404
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from computes.models import Compute
|
from computes.models import Compute
|
||||||
from interfaces.forms import AddInterface
|
from interfaces.forms import AddInterface
|
||||||
from vrtManager.interface import wvmInterface, wvmInterfaces
|
from vrtManager.interface import wvmInterface, wvmInterfaces
|
||||||
from libvirt import libvirtError
|
from libvirt import libvirtError
|
||||||
|
from admin.decorators import superuser_only
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@superuser_only
|
||||||
def interfaces(request, compute_id):
|
def interfaces(request, compute_id):
|
||||||
"""
|
"""
|
||||||
:param request:
|
:param request:
|
||||||
|
@ -16,18 +16,12 @@ def interfaces(request, compute_id):
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not request.user.is_superuser:
|
|
||||||
return HttpResponseRedirect(reverse('index'))
|
|
||||||
|
|
||||||
ifaces_all = []
|
ifaces_all = []
|
||||||
error_messages = []
|
error_messages = []
|
||||||
compute = get_object_or_404(Compute, pk=compute_id)
|
compute = get_object_or_404(Compute, pk=compute_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
conn = wvmInterfaces(compute.hostname,
|
conn = wvmInterfaces(compute.hostname, compute.login, compute.password, compute.type)
|
||||||
compute.login,
|
|
||||||
compute.password,
|
|
||||||
compute.type)
|
|
||||||
ifaces = conn.get_ifaces()
|
ifaces = conn.get_ifaces()
|
||||||
try:
|
try:
|
||||||
netdevs = conn.get_net_devices()
|
netdevs = conn.get_net_devices()
|
||||||
|
@ -42,10 +36,9 @@ def interfaces(request, compute_id):
|
||||||
form = AddInterface(request.POST)
|
form = AddInterface(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
data = form.cleaned_data
|
data = form.cleaned_data
|
||||||
conn.create_iface(data['name'], data['itype'], data['start_mode'], data['netdev'],
|
conn.create_iface(data['name'], data['itype'], data['start_mode'], data['netdev'], data['ipv4_type'],
|
||||||
data['ipv4_type'], data['ipv4_addr'], data['ipv4_gw'],
|
data['ipv4_addr'], data['ipv4_gw'], data['ipv6_type'], data['ipv6_addr'],
|
||||||
data['ipv6_type'], data['ipv6_addr'], data['ipv6_gw'],
|
data['ipv6_gw'], data['stp'], data['delay'])
|
||||||
data['stp'], data['delay'])
|
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
else:
|
else:
|
||||||
for msg_err in form.errors.values():
|
for msg_err in form.errors.values():
|
||||||
|
@ -57,7 +50,7 @@ def interfaces(request, compute_id):
|
||||||
return render(request, 'interfaces.html', locals())
|
return render(request, 'interfaces.html', locals())
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@superuser_only
|
||||||
def interface(request, compute_id, iface):
|
def interface(request, compute_id, iface):
|
||||||
"""
|
"""
|
||||||
:param request:
|
:param request:
|
||||||
|
@ -66,19 +59,12 @@ def interface(request, compute_id, iface):
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not request.user.is_superuser:
|
|
||||||
return HttpResponseRedirect(reverse('index'))
|
|
||||||
|
|
||||||
ifaces_all = []
|
ifaces_all = []
|
||||||
error_messages = []
|
error_messages = []
|
||||||
compute = get_object_or_404(Compute, pk=compute_id)
|
compute = get_object_or_404(Compute, pk=compute_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
conn = wvmInterface(compute.hostname,
|
conn = wvmInterface(compute.hostname, compute.login, compute.password, compute.type, iface)
|
||||||
compute.login,
|
|
||||||
compute.password,
|
|
||||||
compute.type,
|
|
||||||
iface)
|
|
||||||
start_mode = conn.get_start_mode()
|
start_mode = conn.get_start_mode()
|
||||||
state = conn.is_active()
|
state = conn.is_active()
|
||||||
mac = conn.get_mac()
|
mac = conn.get_mac()
|
||||||
|
|
|
@ -2,7 +2,5 @@ from django.urls import path, re_path
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', views.showlogs, name='showlogs'),
|
|
||||||
re_path(r'^(?P<page>[0-9]+)/$', views.showlogs, name='showlogspage'),
|
|
||||||
re_path(r'^vm_logs/(?P<vname>[\w\-\.]+)/$', views.vm_logs, name='vm_logs'),
|
re_path(r'^vm_logs/(?P<vname>[\w\-\.]+)/$', views.vm_logs, name='vm_logs'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
from django.shortcuts import render
|
import json
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.http import HttpResponse, HttpResponseRedirect
|
from django.http import HttpResponse, HttpResponseRedirect
|
||||||
|
from django.shortcuts import render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
|
from admin.decorators import superuser_only
|
||||||
from instances.models import Instance
|
from instances.models import Instance
|
||||||
from logs.models import Logs
|
from logs.models import Logs
|
||||||
from django.conf import settings
|
|
||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
def addlogmsg(user, instance, message):
|
def addlogmsg(user, instance, message):
|
||||||
|
@ -19,37 +21,13 @@ def addlogmsg(user, instance, message):
|
||||||
add_log_msg.save()
|
add_log_msg.save()
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@superuser_only
|
||||||
def showlogs(request, page=1):
|
|
||||||
"""
|
|
||||||
:param request:
|
|
||||||
:param page:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
|
|
||||||
if not request.user.is_superuser:
|
|
||||||
return HttpResponseRedirect(reverse('index'))
|
|
||||||
|
|
||||||
page = int(page)
|
|
||||||
limit_from = (page-1)*settings.LOGS_PER_PAGE
|
|
||||||
limit_to = page*settings.LOGS_PER_PAGE
|
|
||||||
logs = Logs.objects.all().order_by('-date')[limit_from:limit_to+1]
|
|
||||||
has_next_page = logs.count() > settings.LOGS_PER_PAGE
|
|
||||||
# TODO: remove last element from queryset, but do not affect database
|
|
||||||
|
|
||||||
return render(request, 'showlogs.html', locals())
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def vm_logs(request, vname):
|
def vm_logs(request, vname):
|
||||||
"""
|
"""
|
||||||
:param request:
|
:param request:
|
||||||
:param vname:
|
:param vname:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not request.user.is_superuser:
|
|
||||||
return HttpResponseRedirect(reverse('index'))
|
|
||||||
|
|
||||||
vm = Instance.objects.get(name=vname)
|
vm = Instance.objects.get(name=vname)
|
||||||
logs_ = Logs.objects.filter(instance=vm.name, date__gte=vm.created).order_by('-date')
|
logs_ = Logs.objects.filter(instance=vm.name, date__gte=vm.created).order_by('-date')
|
||||||
|
@ -61,5 +39,5 @@ def vm_logs(request, vname):
|
||||||
log['message'] = l.message
|
log['message'] = l.message
|
||||||
log['date'] = l.date.strftime('%x %X')
|
log['date'] = l.date.strftime('%x %X')
|
||||||
logs.append(log)
|
logs.append(log)
|
||||||
|
|
||||||
return HttpResponse(json.dumps(logs))
|
return HttpResponse(json.dumps(logs))
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
from django.shortcuts import render, get_object_or_404
|
from django.contrib import messages
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.shortcuts import get_object_or_404, render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from libvirt import libvirtError
|
||||||
|
|
||||||
|
from admin.decorators import superuser_only
|
||||||
from computes.models import Compute
|
from computes.models import Compute
|
||||||
from networks.forms import AddNetPool
|
from networks.forms import AddNetPool
|
||||||
from vrtManager.network import wvmNetwork, wvmNetworks
|
from vrtManager.network import network_size, wvmNetwork, wvmNetworks
|
||||||
from vrtManager.network import network_size
|
|
||||||
from libvirt import libvirtError
|
|
||||||
from django.contrib import messages
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@superuser_only
|
||||||
def networks(request, compute_id):
|
def networks(request, compute_id):
|
||||||
"""
|
"""
|
||||||
:param request:
|
:param request:
|
||||||
|
@ -19,17 +19,16 @@ def networks(request, compute_id):
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not request.user.is_superuser:
|
|
||||||
return HttpResponseRedirect(reverse('index'))
|
|
||||||
|
|
||||||
error_messages = []
|
error_messages = []
|
||||||
compute = get_object_or_404(Compute, pk=compute_id)
|
compute = get_object_or_404(Compute, pk=compute_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
conn = wvmNetworks(compute.hostname,
|
conn = wvmNetworks(
|
||||||
compute.login,
|
compute.hostname,
|
||||||
compute.password,
|
compute.login,
|
||||||
compute.type)
|
compute.password,
|
||||||
|
compute.type,
|
||||||
|
)
|
||||||
networks = conn.get_networks_info()
|
networks = conn.get_networks_info()
|
||||||
dhcp4 = netmask4 = gateway4 = ''
|
dhcp4 = netmask4 = gateway4 = ''
|
||||||
dhcp6 = prefix6 = gateway6 = ''
|
dhcp6 = prefix6 = gateway6 = ''
|
||||||
|
@ -54,11 +53,21 @@ def networks(request, compute_id):
|
||||||
if prefix6 != '64':
|
if prefix6 != '64':
|
||||||
error_messages.append(_('For libvirt, the IPv6 network prefix must be /64'))
|
error_messages.append(_('For libvirt, the IPv6 network prefix must be /64'))
|
||||||
if not error_messages:
|
if not error_messages:
|
||||||
conn.create_network(data['name'],
|
conn.create_network(
|
||||||
data['forward'],
|
data['name'],
|
||||||
ipv4, gateway4, netmask4, dhcp4,
|
data['forward'],
|
||||||
ipv6, gateway6, prefix6, dhcp6,
|
ipv4,
|
||||||
data['bridge_name'], data['openvswitch'], data['fixed'])
|
gateway4,
|
||||||
|
netmask4,
|
||||||
|
dhcp4,
|
||||||
|
ipv6,
|
||||||
|
gateway6,
|
||||||
|
prefix6,
|
||||||
|
dhcp6,
|
||||||
|
data['bridge_name'],
|
||||||
|
data['openvswitch'],
|
||||||
|
data['fixed'],
|
||||||
|
)
|
||||||
return HttpResponseRedirect(reverse('network', args=[compute_id, data['name']]))
|
return HttpResponseRedirect(reverse('network', args=[compute_id, data['name']]))
|
||||||
else:
|
else:
|
||||||
for msg_err in form.errors.values():
|
for msg_err in form.errors.values():
|
||||||
|
@ -70,7 +79,7 @@ def networks(request, compute_id):
|
||||||
return render(request, 'networks.html', locals())
|
return render(request, 'networks.html', locals())
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@superuser_only
|
||||||
def network(request, compute_id, pool):
|
def network(request, compute_id, pool):
|
||||||
"""
|
"""
|
||||||
:param request:
|
:param request:
|
||||||
|
@ -79,18 +88,17 @@ def network(request, compute_id, pool):
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not request.user.is_superuser:
|
|
||||||
return HttpResponseRedirect(reverse('index'))
|
|
||||||
|
|
||||||
error_messages = []
|
error_messages = []
|
||||||
compute = get_object_or_404(Compute, pk=compute_id)
|
compute = get_object_or_404(Compute, pk=compute_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
conn = wvmNetwork(compute.hostname,
|
conn = wvmNetwork(
|
||||||
compute.login,
|
compute.hostname,
|
||||||
compute.password,
|
compute.login,
|
||||||
compute.type,
|
compute.password,
|
||||||
pool)
|
compute.type,
|
||||||
|
pool,
|
||||||
|
)
|
||||||
networks = conn.get_networks()
|
networks = conn.get_networks()
|
||||||
state = conn.is_active()
|
state = conn.is_active()
|
||||||
device = conn.get_bridge_device()
|
device = conn.get_bridge_device()
|
||||||
|
@ -190,8 +198,7 @@ def network(request, compute_id, pool):
|
||||||
if edit_xml:
|
if edit_xml:
|
||||||
conn.edit_network(edit_xml)
|
conn.edit_network(edit_xml)
|
||||||
if conn.is_active():
|
if conn.is_active():
|
||||||
messages.success(request, _("Network XML is changed. \\"
|
messages.success(request, _("Network XML is changed. \\" "Stop and start network to activate new config."))
|
||||||
"Stop and start network to activate new config."))
|
|
||||||
else:
|
else:
|
||||||
messages.success(request, _("Network XML is changed."))
|
messages.success(request, _("Network XML is changed."))
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
|
@ -204,8 +211,10 @@ def network(request, compute_id, pool):
|
||||||
try:
|
try:
|
||||||
conn.set_qos(qos_dir, average, peak, burst)
|
conn.set_qos(qos_dir, average, peak, burst)
|
||||||
if conn.is_active():
|
if conn.is_active():
|
||||||
messages.success(request, _("{} Qos is set. Network XML is changed.").format(qos_dir.capitalize()) +
|
messages.success(
|
||||||
_("Stop and start network to activate new config"))
|
request,
|
||||||
|
_("{} Qos is set. Network XML is changed.").format(qos_dir.capitalize()) +
|
||||||
|
_("Stop and start network to activate new config"))
|
||||||
else:
|
else:
|
||||||
messages.success(request, _("{} Qos is set").format(qos_dir.capitalize()))
|
messages.success(request, _("{} Qos is set").format(qos_dir.capitalize()))
|
||||||
except libvirtError as lib_err:
|
except libvirtError as lib_err:
|
||||||
|
@ -216,8 +225,10 @@ def network(request, compute_id, pool):
|
||||||
conn.unset_qos(qos_dir)
|
conn.unset_qos(qos_dir)
|
||||||
|
|
||||||
if conn.is_active():
|
if conn.is_active():
|
||||||
messages.success(request, _("{} Qos is deleted. Network XML is changed. ").format(qos_dir.capitalize()) +
|
messages.success(
|
||||||
_("Stop and start network to activate new config."))
|
request,
|
||||||
|
_("{} Qos is deleted. Network XML is changed. ").format(qos_dir.capitalize()) +
|
||||||
|
_("Stop and start network to activate new config."))
|
||||||
else:
|
else:
|
||||||
messages.success(request, _("{} Qos is deleted").format(qos_dir.capitalize()))
|
messages.success(request, _("{} Qos is deleted").format(qos_dir.capitalize()))
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
|
|
|
@ -2,19 +2,20 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.shortcuts import render, get_object_or_404
|
from django.shortcuts import get_object_or_404, render
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from computes.models import Compute
|
|
||||||
from vrtManager import util
|
|
||||||
from vrtManager.nwfilters import wvmNWFilters, wvmNWFilter
|
|
||||||
from vrtManager.instance import wvmInstances, wvmInstance
|
|
||||||
from libvirt import libvirtError
|
from libvirt import libvirtError
|
||||||
|
|
||||||
|
from admin.decorators import superuser_only
|
||||||
|
from computes.models import Compute
|
||||||
from logs.views import addlogmsg
|
from logs.views import addlogmsg
|
||||||
|
from vrtManager import util
|
||||||
|
from vrtManager.instance import wvmInstance, wvmInstances
|
||||||
|
from vrtManager.nwfilters import wvmNWFilter, wvmNWFilters
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@superuser_only
|
||||||
def nwfilters(request, compute_id):
|
def nwfilters(request, compute_id):
|
||||||
"""
|
"""
|
||||||
:param request:
|
:param request:
|
||||||
|
@ -22,18 +23,12 @@ def nwfilters(request, compute_id):
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not request.user.is_superuser:
|
|
||||||
return HttpResponseRedirect(reverse('index'))
|
|
||||||
|
|
||||||
error_messages = []
|
error_messages = []
|
||||||
nwfilters_all = []
|
nwfilters_all = []
|
||||||
compute = get_object_or_404(Compute, pk=compute_id)
|
compute = get_object_or_404(Compute, pk=compute_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
conn = wvmNWFilters(compute.hostname,
|
conn = wvmNWFilters(compute.hostname, compute.login, compute.password, compute.type)
|
||||||
compute.login,
|
|
||||||
compute.password,
|
|
||||||
compute.type)
|
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
if 'create_nwfilter' in request.POST:
|
if 'create_nwfilter' in request.POST:
|
||||||
|
@ -107,12 +102,13 @@ def nwfilters(request, compute_id):
|
||||||
error_messages.append(err)
|
error_messages.append(err)
|
||||||
addlogmsg(request.user.username, compute.hostname, err)
|
addlogmsg(request.user.username, compute.hostname, err)
|
||||||
|
|
||||||
return render(request, 'nwfilters.html', {'error_messages': error_messages,
|
return render(request, 'nwfilters.html', {
|
||||||
'nwfilters': nwfilters_all,
|
'error_messages': error_messages,
|
||||||
'compute': compute})
|
'nwfilters': nwfilters_all,
|
||||||
|
'compute': compute
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def nwfilter(request, compute_id, nwfltr):
|
def nwfilter(request, compute_id, nwfltr):
|
||||||
|
|
||||||
error_messages = []
|
error_messages = []
|
||||||
|
@ -120,15 +116,8 @@ def nwfilter(request, compute_id, nwfltr):
|
||||||
compute = get_object_or_404(Compute, pk=compute_id)
|
compute = get_object_or_404(Compute, pk=compute_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
nwfilter = wvmNWFilter(compute.hostname,
|
nwfilter = wvmNWFilter(compute.hostname, compute.login, compute.password, compute.type, nwfltr)
|
||||||
compute.login,
|
conn = wvmNWFilters(compute.hostname, compute.login, compute.password, compute.type)
|
||||||
compute.password,
|
|
||||||
compute.type,
|
|
||||||
nwfltr)
|
|
||||||
conn = wvmNWFilters(compute.hostname,
|
|
||||||
compute.login,
|
|
||||||
compute.password,
|
|
||||||
compute.type)
|
|
||||||
|
|
||||||
for nwf in conn.get_nwfilters():
|
for nwf in conn.get_nwfilters():
|
||||||
nwfilters_all.append(conn.get_nwfilter_info(nwf))
|
nwfilters_all.append(conn.get_nwfilter_info(nwf))
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
from django.shortcuts import render, get_object_or_404
|
|
||||||
from django.http import HttpResponseRedirect
|
|
||||||
from django.urls import reverse
|
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from computes.models import Compute
|
|
||||||
from secrets.forms import AddSecret
|
from secrets.forms import AddSecret
|
||||||
from vrtManager.secrets import wvmSecrets
|
|
||||||
|
from django.http import HttpResponseRedirect
|
||||||
|
from django.shortcuts import get_object_or_404, render
|
||||||
|
from django.urls import reverse
|
||||||
from libvirt import libvirtError
|
from libvirt import libvirtError
|
||||||
|
|
||||||
|
from admin.decorators import superuser_only
|
||||||
|
from computes.models import Compute
|
||||||
|
from vrtManager.secrets import wvmSecrets
|
||||||
|
|
||||||
@login_required
|
|
||||||
|
@superuser_only
|
||||||
def secrets(request, compute_id):
|
def secrets(request, compute_id):
|
||||||
"""
|
"""
|
||||||
:param request:
|
:param request:
|
||||||
|
@ -16,17 +18,11 @@ def secrets(request, compute_id):
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not request.user.is_superuser:
|
|
||||||
return HttpResponseRedirect(reverse('index'))
|
|
||||||
|
|
||||||
secrets_all = []
|
secrets_all = []
|
||||||
error_messages = []
|
error_messages = []
|
||||||
compute = get_object_or_404(Compute, pk=compute_id)
|
compute = get_object_or_404(Compute, pk=compute_id)
|
||||||
try:
|
try:
|
||||||
conn = wvmSecrets(compute.hostname,
|
conn = wvmSecrets(compute.hostname, compute.login, compute.password, compute.type)
|
||||||
compute.login,
|
|
||||||
compute.password,
|
|
||||||
compute.type)
|
|
||||||
secrets = conn.get_secrets()
|
secrets = conn.get_secrets()
|
||||||
|
|
||||||
for uuid in secrets:
|
for uuid in secrets:
|
||||||
|
@ -35,11 +31,12 @@ def secrets(request, compute_id):
|
||||||
secret_value = conn.get_secret_value(uuid)
|
secret_value = conn.get_secret_value(uuid)
|
||||||
except libvirtError as lib_err:
|
except libvirtError as lib_err:
|
||||||
secret_value = None
|
secret_value = None
|
||||||
secrets_all.append({'usage': secrt.usageID(),
|
secrets_all.append({
|
||||||
'uuid': secrt.UUIDString(),
|
'usage': secrt.usageID(),
|
||||||
'usageType': secrt.usageType(),
|
'uuid': secrt.UUIDString(),
|
||||||
'value': secret_value
|
'usageType': secrt.usageType(),
|
||||||
})
|
'value': secret_value
|
||||||
|
})
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
if 'create' in request.POST:
|
if 'create' in request.POST:
|
||||||
form = AddSecret(request.POST)
|
form = AddSecret(request.POST)
|
||||||
|
|
|
@ -150,4 +150,19 @@ p {
|
||||||
.multipleselect-on {
|
.multipleselect-on {
|
||||||
color:#ffffff;
|
color:#ffffff;
|
||||||
background-color:#000099;
|
background-color:#000099;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-bar-margin {
|
||||||
|
margin-top: 65px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* make dropdowns show on hover */
|
||||||
|
@media only screen and (min-width: 768px) {
|
||||||
|
.dropdown:hover .dropdown-menu {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
}
|
}
|
12
static/js/filter-table.js
Normal file
12
static/js/filter-table.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
function filter_table() {
|
||||||
|
var rex = new RegExp($(this).val(), 'i');
|
||||||
|
$('.searchable tr').hide();
|
||||||
|
$('.searchable tr').filter(function () {
|
||||||
|
return rex.test($(this).text());
|
||||||
|
}).show();
|
||||||
|
}
|
||||||
|
$(document).ready(function () {
|
||||||
|
(function ($) {
|
||||||
|
$('#filter').keyup(filter_table)
|
||||||
|
}(jQuery));
|
||||||
|
});
|
|
@ -2,16 +2,16 @@ from django.shortcuts import render, get_object_or_404
|
||||||
from django.http import HttpResponseRedirect, HttpResponse
|
from django.http import HttpResponseRedirect, HttpResponse
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from computes.models import Compute
|
from computes.models import Compute
|
||||||
from storages.forms import AddStgPool, AddImage, CloneImage
|
from storages.forms import AddStgPool, AddImage, CloneImage
|
||||||
from vrtManager.storage import wvmStorage, wvmStorages
|
from vrtManager.storage import wvmStorage, wvmStorages
|
||||||
from libvirt import libvirtError
|
from libvirt import libvirtError
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
import json
|
import json
|
||||||
|
from admin.decorators import superuser_only
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@superuser_only
|
||||||
def storages(request, compute_id):
|
def storages(request, compute_id):
|
||||||
"""
|
"""
|
||||||
:param request:
|
:param request:
|
||||||
|
@ -19,17 +19,11 @@ def storages(request, compute_id):
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not request.user.is_superuser:
|
|
||||||
return HttpResponseRedirect(reverse('index'))
|
|
||||||
|
|
||||||
error_messages = []
|
error_messages = []
|
||||||
compute = get_object_or_404(Compute, pk=compute_id)
|
compute = get_object_or_404(Compute, pk=compute_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
conn = wvmStorages(compute.hostname,
|
conn = wvmStorages(compute.hostname, compute.login, compute.password, compute.type)
|
||||||
compute.login,
|
|
||||||
compute.password,
|
|
||||||
compute.type)
|
|
||||||
storages = conn.get_storages_info()
|
storages = conn.get_storages_info()
|
||||||
secrets = conn.get_secrets()
|
secrets = conn.get_secrets()
|
||||||
|
|
||||||
|
@ -50,12 +44,10 @@ def storages(request, compute_id):
|
||||||
error_messages.append(msg)
|
error_messages.append(msg)
|
||||||
if not error_messages:
|
if not error_messages:
|
||||||
if data['stg_type'] == 'rbd':
|
if data['stg_type'] == 'rbd':
|
||||||
conn.create_storage_ceph(data['stg_type'], data['name'],
|
conn.create_storage_ceph(data['stg_type'], data['name'], data['ceph_pool'], data['ceph_host'],
|
||||||
data['ceph_pool'], data['ceph_host'],
|
|
||||||
data['ceph_user'], data['secret'])
|
data['ceph_user'], data['secret'])
|
||||||
elif data['stg_type'] == 'netfs':
|
elif data['stg_type'] == 'netfs':
|
||||||
conn.create_storage_netfs(data['stg_type'], data['name'],
|
conn.create_storage_netfs(data['stg_type'], data['name'], data['netfs_host'], data['source'],
|
||||||
data['netfs_host'], data['source'],
|
|
||||||
data['source_format'], data['target'])
|
data['source_format'], data['target'])
|
||||||
else:
|
else:
|
||||||
conn.create_storage(data['stg_type'], data['name'], data['source'], data['target'])
|
conn.create_storage(data['stg_type'], data['name'], data['source'], data['target'])
|
||||||
|
@ -70,7 +62,7 @@ def storages(request, compute_id):
|
||||||
return render(request, 'storages.html', locals())
|
return render(request, 'storages.html', locals())
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@superuser_only
|
||||||
def storage(request, compute_id, pool):
|
def storage(request, compute_id, pool):
|
||||||
"""
|
"""
|
||||||
:param request:
|
:param request:
|
||||||
|
@ -78,10 +70,6 @@ def storage(request, compute_id, pool):
|
||||||
:param pool:
|
:param pool:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not request.user.is_superuser:
|
|
||||||
return HttpResponseRedirect(reverse('index'))
|
|
||||||
|
|
||||||
def handle_uploaded_file(path, f_name):
|
def handle_uploaded_file(path, f_name):
|
||||||
target = path + '/' + str(f_name)
|
target = path + '/' + str(f_name)
|
||||||
destination = open(target, 'wb+')
|
destination = open(target, 'wb+')
|
||||||
|
@ -94,11 +82,7 @@ def storage(request, compute_id, pool):
|
||||||
meta_prealloc = False
|
meta_prealloc = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
conn = wvmStorage(compute.hostname,
|
conn = wvmStorage(compute.hostname, compute.login, compute.password, compute.type, pool)
|
||||||
compute.login,
|
|
||||||
compute.password,
|
|
||||||
compute.type,
|
|
||||||
pool)
|
|
||||||
|
|
||||||
storages = conn.get_storages()
|
storages = conn.get_storages()
|
||||||
state = conn.is_active()
|
state = conn.is_active()
|
||||||
|
@ -215,16 +199,11 @@ def storage(request, compute_id, pool):
|
||||||
return render(request, 'storage.html', locals())
|
return render(request, 'storage.html', locals())
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def get_volumes(request, compute_id, pool):
|
def get_volumes(request, compute_id, pool):
|
||||||
data = {}
|
data = {}
|
||||||
compute = get_object_or_404(Compute, pk=compute_id)
|
compute = get_object_or_404(Compute, pk=compute_id)
|
||||||
try:
|
try:
|
||||||
conn = wvmStorage(compute.hostname,
|
conn = wvmStorage(compute.hostname, compute.login, compute.password, compute.type, pool)
|
||||||
compute.login,
|
|
||||||
compute.password,
|
|
||||||
compute.type,
|
|
||||||
pool)
|
|
||||||
conn.refresh()
|
conn.refresh()
|
||||||
data['vols'] = sorted(conn.get_volumes())
|
data['vols'] = sorted(conn.get_volumes())
|
||||||
except libvirtError:
|
except libvirtError:
|
||||||
|
|
15
templates/403.html
Normal file
15
templates/403.html
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{% extends "base_auth.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block title %}{% trans "404" %}{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12" style="text-align: center;">
|
||||||
|
<h1>{% trans 'Oops!'%}</h1>
|
||||||
|
|
||||||
|
<p class="lead">{% trans "403 Forbidden" %}</p>
|
||||||
|
|
||||||
|
<p>{% trans "You do not have permission to access this page." %}</p>
|
||||||
|
<a class="btn btn-medium btn-success" href="javascript:history.back()">← {% trans 'Back'%}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -1,48 +1,59 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load tags_active %}
|
{% load font_awesome %}
|
||||||
<!-- Fixed navbar -->
|
{% load common_tags %}
|
||||||
<nav class="navbar navbar-default navbar-fixed-top">
|
<!-- Fixed navbar -->
|
||||||
<div class="container">
|
<nav class="navbar navbar-default navbar-fixed-top">
|
||||||
<div class="navbar-header">
|
<div class="container">
|
||||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
<div class="navbar-header">
|
||||||
<span class="sr-only">{% trans 'Toggle navigation' %}</span>
|
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||||
<span class="icon-bar"></span>
|
<span class="sr-only">{% trans 'Toggle navigation' %}</span>
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
</button>
|
<span class="icon-bar"></span>
|
||||||
<a class="navbar-brand" href="{% url 'index' %}">WebVirtCloud</a>
|
</button>
|
||||||
</div>
|
<a class="navbar-brand" href="{% url 'index' %}">WebVirtCloud</a>
|
||||||
<div id="navbar" class="navbar-collapse collapse">
|
</div>
|
||||||
<ul class="nav navbar-nav">
|
<div id="navbar" class="navbar-collapse collapse">
|
||||||
<li {% class_active request "^/instance" %}>
|
<ul class="nav navbar-nav">
|
||||||
<a href="{% url 'allinstances' %}"><i class="fa fa-fw fa-desktop"></i> {% trans "Instances" %}</a>
|
<li {% class_active request '^/instance' %}>
|
||||||
</li>
|
<a href="{% url 'allinstances' %}"><i class="fa fa-fw fa-desktop"></i> {% trans "Instances" %}</a>
|
||||||
{% if request.user.is_superuser %}
|
</li>
|
||||||
<li {% class_active request "^/compute" %}{% class_active request "^/create" %}>
|
{% if request.user.is_superuser %}
|
||||||
<a href="{% url 'computes' %}"><i class="fa fa-fw fa-server"></i> {% trans "Computes" %}</a>
|
<li {% class_active request "^/compute" %}>
|
||||||
</li>
|
<a href="{% url 'computes' %}"><i class="fa fa-fw fa-server"></i> {% trans "Computes" %}</a>
|
||||||
<li {% class_active request "^/account" %}>
|
</li>
|
||||||
<a href="{% url 'accounts' %}"><i class="fa fa-fw fa-users"></i> {% trans "Users" %}</a>
|
<li class="dropdown {% app_active request 'admin' %}">
|
||||||
</li>
|
<a id="administration" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
<li {% class_active request "^/log" %}>
|
{% trans "Administration" %} {% icon 'caret-down' %}
|
||||||
<a href="{% url 'showlogs' %}"><i class="fa fa-fw fa-list-alt"></i> {% trans "Logs" %}</a>
|
</a>
|
||||||
</li>
|
<ul class="dropdown-menu" aria-labelledby="administration">
|
||||||
{% endif %}
|
<li class="{% view_active request 'admin:user_list' %}">
|
||||||
</ul>
|
<a href="{% url 'admin:user_list' %}">{% icon 'user-plus' %} {% trans "Users" %}</a>
|
||||||
<ul class="nav navbar-nav navbar-right">
|
</li>
|
||||||
<li class="dropdown">
|
<li class="{% view_active request 'admin:group_list' %}">
|
||||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><i class="fa fa-fw fa-user"></i> {{ request.user.username }} <span class="caret"></span></a>
|
<a href="{% url 'admin:group_list' %}">{% icon 'users' %} {% trans "Groups" %}</a>
|
||||||
<ul class="dropdown-menu" role="menu">
|
</li>
|
||||||
<li>
|
<li class="{% view_active request 'admin:logs' %}">
|
||||||
<a href="{% url 'profile' %}"><i class="fa fa-fw fa-pencil-square-o"></i> {% trans "Profile" %}</a>
|
<a href="{% url 'admin:logs' %}">{% icon 'list-alt' %} {% trans "Logs" %}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="divider"></li>
|
</ul>
|
||||||
<li>
|
</li>
|
||||||
<a href="{% url 'logout' %}"><i class="fa fa-fw fa-power-off"></i> {% trans "Log Out" %}</a>
|
{% endif %}
|
||||||
</li>
|
</ul>
|
||||||
</ul>
|
<ul class="nav navbar-nav navbar-right">
|
||||||
</li>
|
<li class="dropdown">
|
||||||
</ul>
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><i class="fa fa-fw fa-user"></i> {{ request.user.username }} <span class="caret"></span></a>
|
||||||
</div><!--/.nav-collapse -->
|
<ul class="dropdown-menu" role="menu">
|
||||||
</div>
|
<li>
|
||||||
</nav>
|
<a href="{% url 'profile' %}"><i class="fa fa-fw fa-pencil-square-o"></i> {% trans "Profile" %}</a>
|
||||||
|
</li>
|
||||||
|
<li class="divider"></li>
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'logout' %}"><i class="fa fa-fw fa-power-off"></i> {% trans "Log Out" %}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div><!--/.nav-collapse -->
|
||||||
|
</div>
|
||||||
|
</nav>
|
33
webvirtcloud/common_tags.py
Normal file
33
webvirtcloud/common_tags.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
from django import template
|
||||||
|
import re
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_tag
|
||||||
|
def app_active(request, app_name):
|
||||||
|
if request.resolver_match.app_name == app_name:
|
||||||
|
return 'active'
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_tag
|
||||||
|
def view_active(request, view_name):
|
||||||
|
if request.resolver_match.view_name == view_name:
|
||||||
|
return 'active'
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_tag
|
||||||
|
def class_active(request, pattern):
|
||||||
|
if re.search(pattern, request.path):
|
||||||
|
# Not sure why 'class="active"' returns class=""active""
|
||||||
|
return 'class=active'
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_tag
|
||||||
|
def has_perm(user, permission_codename):
|
||||||
|
if user.has_perm(permission_codename):
|
||||||
|
return True
|
||||||
|
return False
|
|
@ -14,17 +14,18 @@ DEBUG = True
|
||||||
|
|
||||||
ALLOWED_HOSTS = ['*']
|
ALLOWED_HOSTS = ['*']
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
'django.contrib.admin',
|
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
|
'bootstrap3',
|
||||||
|
'fa',
|
||||||
'accounts',
|
'accounts',
|
||||||
|
'admin',
|
||||||
'computes',
|
'computes',
|
||||||
'console',
|
'console',
|
||||||
'create',
|
'create',
|
||||||
|
@ -44,6 +45,7 @@ MIDDLEWARE = [
|
||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
'login_required.middleware.LoginRequiredMiddleware',
|
||||||
'django.contrib.auth.middleware.RemoteUserMiddleware',
|
'django.contrib.auth.middleware.RemoteUserMiddleware',
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
|
@ -55,7 +57,9 @@ ROOT_URLCONF = 'webvirtcloud.urls'
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
'DIRS': [ os.path.join(BASE_DIR, 'templates'), ],
|
'DIRS': [
|
||||||
|
os.path.join(BASE_DIR, 'templates'),
|
||||||
|
],
|
||||||
'APP_DIRS': True,
|
'APP_DIRS': True,
|
||||||
'OPTIONS': {
|
'OPTIONS': {
|
||||||
'context_processors': [
|
'context_processors': [
|
||||||
|
@ -64,13 +68,15 @@ TEMPLATES = [
|
||||||
'django.contrib.auth.context_processors.auth',
|
'django.contrib.auth.context_processors.auth',
|
||||||
'django.contrib.messages.context_processors.messages',
|
'django.contrib.messages.context_processors.messages',
|
||||||
],
|
],
|
||||||
|
'libraries': {
|
||||||
|
'common_tags': 'webvirtcloud.common_tags',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
WSGI_APPLICATION = 'webvirtcloud.wsgi.application'
|
WSGI_APPLICATION = 'webvirtcloud.wsgi.application'
|
||||||
|
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
|
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
|
||||||
|
|
||||||
|
@ -83,12 +89,12 @@ DATABASES = {
|
||||||
|
|
||||||
AUTHENTICATION_BACKENDS = [
|
AUTHENTICATION_BACKENDS = [
|
||||||
'django.contrib.auth.backends.ModelBackend',
|
'django.contrib.auth.backends.ModelBackend',
|
||||||
#'django.contrib.auth.backends.RemoteUserBackend',
|
|
||||||
#'accounts.backends.MyRemoteUserBackend',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
LOGIN_URL = '/accounts/login'
|
LOGIN_URL = '/accounts/login'
|
||||||
|
|
||||||
|
LOGOUT_REDIRECT_URL = '/accounts/login'
|
||||||
|
|
||||||
LANGUAGE_CODE = 'en-us'
|
LANGUAGE_CODE = 'en-us'
|
||||||
|
|
||||||
TIME_ZONE = 'UTC'
|
TIME_ZONE = 'UTC'
|
||||||
|
@ -165,10 +171,6 @@ QUOTA_DEBUG = True
|
||||||
ALLOW_EMPTY_PASSWORD = True
|
ALLOW_EMPTY_PASSWORD = True
|
||||||
SHOW_ACCESS_ROOT_PASSWORD = False
|
SHOW_ACCESS_ROOT_PASSWORD = False
|
||||||
SHOW_ACCESS_SSH_KEYS = False
|
SHOW_ACCESS_SSH_KEYS = False
|
||||||
SHOW_PROFILE_EDIT_PASSWORD = False
|
|
||||||
|
|
||||||
# available: default (grid), list
|
|
||||||
VIEW_ACCOUNTS_STYLE = 'grid'
|
|
||||||
|
|
||||||
# available list style: default (grouped), nongrouped
|
# available list style: default (grouped), nongrouped
|
||||||
VIEW_INSTANCES_LIST_STYLE = 'grouped'
|
VIEW_INSTANCES_LIST_STYLE = 'grouped'
|
||||||
|
|
|
@ -2,16 +2,14 @@ from django.urls import include, path
|
||||||
|
|
||||||
from instances.views import index
|
from instances.views import index
|
||||||
from console.views import console
|
from console.views import console
|
||||||
# from django.contrib import admin
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', index, name='index'),
|
path('', index, name='index'),
|
||||||
|
path('admin/', include(('admin.urls', 'admin'), namespace='admin')),
|
||||||
path('instances/', include('instances.urls')),
|
path('instances/', include('instances.urls')),
|
||||||
path('accounts/', include('accounts.urls')),
|
path('accounts/', include('accounts.urls')),
|
||||||
path('computes/', include('computes.urls')),
|
path('computes/', include('computes.urls')),
|
||||||
path('logs/', include('logs.urls')),
|
path('logs/', include('logs.urls')),
|
||||||
path('datasource/', include('datasource.urls')),
|
path('datasource/', include('datasource.urls')),
|
||||||
path('console/', console, name='console'),
|
path('console/', console, name='console'),
|
||||||
# url(r'^admin/', include(admin.site.urls)),
|
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in a new issue