1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2024-12-24 15:15:22 +00:00

Merge pull request #314 from Real-Gecko/master

Some fixes and changes
This commit is contained in:
Anatoliy Guskov 2020-05-28 11:37:26 +03:00 committed by GitHub
commit 9718b4c215
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
54 changed files with 1209 additions and 858 deletions

View file

@ -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.
``` ```

View file

@ -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):

View 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': (),
},
),
]

View 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),
]

View file

@ -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',
),
]

View file

@ -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

View file

@ -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">&times;</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">&times;</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 %}

View file

@ -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">&times;</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">&times;</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 %}

View file

@ -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">&times;</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 %}

View file

@ -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>

View file

@ -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">

View file

@ -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'),
] ]

View file

@ -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
View file

@ -0,0 +1,5 @@
from django.apps import AppConfig
class AdminConfig(AppConfig):
name = 'admin'

10
admin/decorators.py Normal file
View 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
View 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']

View 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()),
],
),
]

View file

11
admin/models.py Normal file
View 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

View 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 %}

View 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 %}

View 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 %}

View 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">&times;</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 %}

View 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 %}

View 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">&times;</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
View 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
View 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})

View file

@ -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:

View file

@ -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

View file

@ -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:

View file

@ -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()

View 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': (),
},
),
]

View 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),
]

View file

@ -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

View 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>

View file

@ -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>

View file

@ -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 ''

View file

@ -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}

View file

@ -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()

View file

@ -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'),
] ]

View file

@ -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))

View file

@ -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())

View file

@ -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))

View file

@ -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)

View file

@ -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
View 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));
});

View file

@ -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
View 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()">&larr; {% trans 'Back'%}</a>
</div>
</div>
{% endblock %}

View file

@ -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>

View 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

View file

@ -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'

View file

@ -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)),
] ]