1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2024-12-23 22:55:23 +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
```
### 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
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):
from django.utils import timezone
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):

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 instances.models import Instance
from django.contrib.auth.models import User
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):
user = ForeignKey(User, on_delete=CASCADE)
instance = ForeignKey(Instance, on_delete=CASCADE)
is_change = BooleanField(default=False)
is_delete = BooleanField(default=False)
is_vnc = BooleanField(default=False)
class UserInstance(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
instance = models.ForeignKey(Instance, on_delete=models.CASCADE)
is_change = models.BooleanField(default=False)
is_delete = models.BooleanField(default=False)
is_vnc = models.BooleanField(default=False)
def __unicode__(self):
return self.instance.name
class UserSSHKey(Model):
user = ForeignKey(User, on_delete=DO_NOTHING)
keyname = CharField(max_length=25)
keypublic = CharField(max_length=500)
class UserSSHKey(models.Model):
user = models.ForeignKey(User, on_delete=models.DO_NOTHING)
keyname = models.CharField(max_length=25)
keypublic = models.CharField(max_length=500)
def __unicode__(self):
return self.keyname
class UserAttributes(Model):
user = OneToOneField(User, on_delete=CASCADE)
can_clone_instances = BooleanField(default=True)
max_instances = IntegerField(default=1, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1), ])
max_cpus = IntegerField(default=1, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1)])
max_memory = IntegerField(default=2048, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1)])
max_disk_size = IntegerField(default=20, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1)])
class UserAttributes(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
max_instances = models.IntegerField(default=1,
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
def create_missing_userattributes(user):
@ -51,7 +66,7 @@ class UserAttributes(Model):
instance = Instance.objects.get(name=instance_name)
user_instance = UserInstance(user=user, instance=instance)
user_instance.save()
@staticmethod
def configure_user(user):
UserAttributes.create_missing_userattributes(user)
@ -59,3 +74,14 @@ class UserAttributes(Model):
def __unicode__(self):
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 %}
<form class="form-signin" method="post" role="form">{% csrf_token %}
<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="password" class="form-control" name="password" placeholder="Password">
<input name="next" id="next" type="hidden" value="{% url 'allinstances' %}">
<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="{% trans "Password" %}">
<input type="hidden" name="next" value="{{ next }}">
<button class="btn btn-lg btn-success btn-block" type="submit">{% trans "Sign In" %}</button>
</form>
</div>

View file

@ -41,7 +41,7 @@
</div>
</div>
</form>
{% if show_profile_edit_password %}
{% if perms.accounts.change_password %}
<h3 class="page-header">{% trans "Edit Password" %}</h3>
<form class="form-horizontal" method="post" action="" role="form">{% csrf_token %}
<div class="form-group">

View file

@ -5,6 +5,6 @@ from . import views
urlpatterns = [
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('profile/', views.profile, name='profile'), path('', views.accounts, name='accounts'),
re_path(r'^profile/(?P<user_id>[0-9]+)/$', views.account, name='account'),
path('profile/', views.profile, name='profile'),
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.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):
"""
:param request:
@ -18,9 +18,8 @@ def profile(request):
"""
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)
show_profile_edit_password = settings.SHOW_PROFILE_EDIT_PASSWORD
if request.method == 'POST':
if 'username' in request.POST:
@ -28,7 +27,7 @@ def profile(request):
email = request.POST.get('email', '')
user.first_name = username
user.email = email
user.save()
request.user.save()
return HttpResponseRedirect(request.get_full_path())
if 'oldpasswd' in request.POST:
oldpasswd = request.POST.get('oldpasswd', '')
@ -38,11 +37,11 @@ def profile(request):
error_messages.append("Passwords didn't enter")
if password1 and password2 and password1 != password2:
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!")
if not error_messages:
user.set_password(password1)
user.save()
request.user.set_password(password1)
request.user.save()
return HttpResponseRedirect(request.get_full_path())
if 'keyname' in request.POST:
keyname = request.POST.get('keyname', '')
@ -69,87 +68,7 @@ def profile(request):
return render(request, 'profile.html', locals())
@login_required
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
@superuser_only
def account(request, user_id):
"""
:param request:
@ -157,9 +76,6 @@ def account(request, user_id):
:return:
"""
if not request.user.is_superuser:
return HttpResponseRedirect(reverse('index'))
error_messages = []
user = User.objects.get(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())
if 'add' in request.POST:
inst_id = request.POST.get('inst_id', '')
if settings.ALLOW_INSTANCE_MULTIPLE_OWNER:
check_inst = UserInstance.objects.filter(instance_id=int(inst_id), user_id=int(user_id))
else:
check_inst = UserInstance.objects.filter(instance_id=int(inst_id))
if check_inst:
msg = _("Instance already added")
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.urls import reverse
from django.shortcuts import render, get_object_or_404
from django.contrib.auth.decorators import login_required
from computes.models import Compute
from instances.models import Instance
from accounts.models import UserInstance
@ -11,39 +10,37 @@ from computes.forms import ComputeAddTcpForm, ComputeAddSshForm, ComputeEditHost
from vrtManager.hostdetails import wvmHostDetails
from vrtManager.connection import CONN_SSH, CONN_TCP, CONN_TLS, CONN_SOCKET, connection_manager, wvmConnect
from libvirt import libvirtError
from admin.decorators import superuser_only
@login_required
@superuser_only
def computes(request):
"""
:param request:
:return:
"""
if not request.user.is_superuser:
return HttpResponseRedirect(reverse('index'))
def get_hosts_status(computes):
"""
Function return all hosts all vds on host
"""
compute_data = []
for compute in computes:
compute_data.append({'id': compute.id,
'name': compute.name,
'hostname': compute.hostname,
'status': connection_manager.host_is_up(compute.type, compute.hostname),
'type': compute.type,
'login': compute.login,
'password': compute.password,
'details': compute.details
})
compute_data.append({
'id': compute.id,
'name': compute.name,
'hostname': compute.hostname,
'status': connection_manager.host_is_up(compute.type, compute.hostname),
'type': compute.type,
'login': compute.login,
'password': compute.password,
'details': compute.details
})
return compute_data
error_messages = []
computes = Compute.objects.filter().order_by('name')
computes_info = get_hosts_status(computes)
if request.method == 'POST':
if 'host_del' in request.POST:
compute_id = request.POST.get('host_id', '')
@ -135,7 +132,7 @@ def computes(request):
return render(request, 'computes.html', locals())
@login_required
@superuser_only
def overview(request, compute_id):
"""
:param request:
@ -143,17 +140,16 @@ def overview(request, compute_id):
:return:
"""
if not request.user.is_superuser:
return HttpResponseRedirect(reverse('index'))
error_messages = []
compute = get_object_or_404(Compute, pk=compute_id)
try:
conn = wvmHostDetails(compute.hostname,
compute.login,
compute.password,
compute.type)
conn = wvmHostDetails(
compute.hostname,
compute.login,
compute.password,
compute.type,
)
hostname, host_arch, host_memory, logical_cpu, model_cpu, uri_conn = conn.get_node_info()
hypervisor = conn.get_hypervisors_domain_types()
mem_usage = conn.get_memory_usage()
@ -167,7 +163,6 @@ def overview(request, compute_id):
return render(request, 'overview.html', locals())
@login_required
def compute_graph(request, compute_id):
"""
:param request:
@ -176,10 +171,12 @@ def compute_graph(request, compute_id):
"""
compute = get_object_or_404(Compute, pk=compute_id)
try:
conn = wvmHostDetails(compute.hostname,
compute.login,
compute.password,
compute.type)
conn = wvmHostDetails(
compute.hostname,
compute.login,
compute.password,
compute.type,
)
current_time = timezone.now().strftime("%H:%M:%S")
cpu_usage = conn.get_cpu_usage()
mem_usage = conn.get_memory_usage()
@ -188,24 +185,27 @@ def compute_graph(request, compute_id):
cpu_usage = {'usage': 0}
mem_usage = {'usage': 0}
data = json.dumps({'cpudata': cpu_usage['usage'],
'memdata': mem_usage,
'timeline': current_time})
data = json.dumps({
'cpudata': cpu_usage['usage'],
'memdata': mem_usage,
'timeline': current_time,
})
response = HttpResponse()
response['Content-Type'] = "text/javascript"
response.write(data)
return response
@login_required
def get_compute_disk_buses(request, compute_id, arch, machine, disk):
data = dict()
compute = get_object_or_404(Compute, pk=compute_id)
try:
conn = wvmConnect(compute.hostname,
compute.login,
compute.password,
compute.type)
conn = wvmConnect(
compute.hostname,
compute.login,
compute.password,
compute.type,
)
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))
@login_required
def get_compute_machine_types(request, compute_id, arch):
data = dict()
try:
compute = get_object_or_404(Compute, pk=compute_id)
conn = wvmConnect(compute.hostname,
compute.login,
compute.password,
compute.type)
conn = wvmConnect(
compute.hostname,
compute.login,
compute.password,
compute.type,
)
data['machines'] = conn.get_machine_types(arch)
except libvirtError:
pass
@ -240,15 +241,16 @@ def get_compute_machine_types(request, compute_id, arch):
return HttpResponse(json.dumps(data))
@login_required
def get_compute_video_models(request, compute_id, arch, machine):
data = dict()
try:
compute = get_object_or_404(Compute, pk=compute_id)
conn = wvmConnect(compute.hostname,
compute.login,
compute.password,
compute.type)
conn = wvmConnect(
compute.hostname,
compute.login,
compute.password,
compute.type,
)
data['videos'] = conn.get_video_models(arch, machine)
except libvirtError:
pass
@ -256,15 +258,16 @@ def get_compute_video_models(request, compute_id, arch, machine):
return HttpResponse(json.dumps(data))
@login_required
def get_dom_capabilities(request, compute_id, arch, machine):
data = dict()
try:
compute = get_object_or_404(Compute, pk=compute_id)
conn = wvmConnect(compute.hostname,
compute.login,
compute.password,
compute.type)
conn = wvmConnect(
compute.hostname,
compute.login,
compute.password,
compute.type,
)
data['videos'] = conn.get_disk_device_types(arch, machine)
data['bus'] = conn.get_disk_device_types(arch, machine)
except libvirtError:

View file

@ -1,8 +1,13 @@
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
lxml==4.5.0
libvirt-python==6.1.0
six
pytz
rwlock
lxml==4.5.0
numpy==1.18.4
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
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from instances.models import Instance
from vrtManager.instance import wvmInstance
from webvirtcloud.settings import WS_PUBLIC_PORT
@ -8,7 +7,6 @@ from webvirtcloud.settings import WS_PUBLIC_HOST
from libvirt import libvirtError
@login_required
def console(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.utils.translation import ugettext_lazy as _
from django.urls import reverse
from django.contrib.auth.decorators import login_required
from computes.models import Compute
from create.models import Flavor
from create.forms import FlavorAddForm, NewVMForm
@ -24,14 +23,12 @@ from webvirtcloud.settings import INSTANCE_FIRMWARE_DEFAULT_TYPE
from django.contrib import messages
from logs.views import addlogmsg
from admin.decorators import superuser_only
@login_required
@superuser_only
def create_instance_select_type(request, compute_id):
if not request.user.is_superuser:
return HttpResponseRedirect(reverse('index'))
conn = None
error_messages = list()
storages = list()
@ -41,10 +38,7 @@ def create_instance_select_type(request, compute_id):
compute = get_object_or_404(Compute, pk=compute_id)
try:
conn = wvmCreate(compute.hostname,
compute.login,
compute.password,
compute.type)
conn = wvmCreate(compute.hostname, compute.login, compute.password, compute.type)
instances = conn.get_instances()
all_hypervisors = conn.get_hypervisors_machines()
# 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())
@login_required
@superuser_only
def create_instance(request, compute_id, arch, machine):
"""
:param request:
@ -85,8 +79,6 @@ def create_instance(request, compute_id, arch, machine):
:param machine:
:return:
"""
if not request.user.is_superuser:
return HttpResponseRedirect(reverse('index'))
conn = None
error_messages = list()
@ -99,10 +91,7 @@ def create_instance(request, compute_id, arch, machine):
flavors = Flavor.objects.filter().order_by('id')
try:
conn = wvmCreate(compute.hostname,
compute.login,
compute.password,
compute.type)
conn = wvmCreate(compute.hostname, compute.login, compute.password, compute.type)
default_firmware = INSTANCE_FIRMWARE_DEFAULT_TYPE
default_cpu_mode = INSTANCE_CPU_DEFAULT_MODE
@ -153,10 +142,7 @@ def create_instance(request, compute_id, arch, machine):
form = FlavorAddForm(request.POST)
if form.is_valid():
data = form.cleaned_data
create_flavor = Flavor(label=data['label'],
vcpu=data['vcpu'],
memory=data['memory'],
disk=data['disk'])
create_flavor = Flavor(label=data['label'], vcpu=data['vcpu'], memory=data['memory'], disk=data['disk'])
create_flavor.save()
return HttpResponseRedirect(request.get_full_path())
if 'delete_flavor' in request.POST:
@ -187,7 +173,9 @@ def create_instance(request, compute_id, arch, machine):
error_messages.append(error_msg)
else:
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)
volume = dict()
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_messages.append(error_msg)
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['path'] = 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["type"] = 'pflash'
if 'secboot' in firmware["loader"] and machine != 'q35':
messages.warning(request, "Changing machine type from '%s' to 'q35' "
"which is required for UEFI secure boot." % machine)
messages.warning(
request, "Changing machine type from '%s' to 'q35' "
"which is required for UEFI secure boot." % machine)
machine = 'q35'
firmware["secure"] = 'yes'
if not error_messages:
uuid = util.randomUUID()
try:
conn.create_instance(name=data['name'], memory=data['memory'], vcpu=data['vcpu'],
vcpu_mode=data['vcpu_mode'], uuid=uuid, arch=arch, machine=machine,
conn.create_instance(name=data['name'],
memory=data['memory'],
vcpu=data['vcpu'],
vcpu_mode=data['vcpu_mode'],
uuid=uuid,
arch=arch,
machine=machine,
firmware=firmware,
images=volume_list, cache_mode=data['cache_mode'],
io_mode=default_io, discard_mode=default_discard, detect_zeroes_mode=default_zeroes,
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'],
images=volume_list,
cache_mode=data['cache_mode'],
io_mode=default_io,
discard_mode=default_discard,
detect_zeroes_mode=default_zeroes,
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'])
create_instance = Instance(compute_id=compute_id, name=data['name'], uuid=uuid)
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
@ -11,3 +14,15 @@ class Instance(Model):
def __unicode__(self):
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 content %}
{% 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 -->
<div>
<div>
@ -1690,6 +1659,11 @@
</div>
</div>
</div>
{% if bottom_bar %}
{% include 'bottom_bar.html' %}
{% endif %}
{% endblock %}
{% block 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.shortcuts import render, get_object_or_404
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.decorators import login_required
from computes.models import Compute
from instances.models import Instance
from django.contrib.auth.models import User
@ -29,7 +28,6 @@ from django.contrib import messages
from collections import OrderedDict
@login_required
def index(request):
"""
:param request:
@ -38,7 +36,6 @@ def index(request):
return HttpResponseRedirect(reverse('allinstances'))
@login_required
def allinstances(request):
"""
INSTANCES LIST FOR ALL HOSTS
@ -70,7 +67,6 @@ def allinstances(request):
return render(request, 'allinstances.html', locals())
@login_required
def instances(request, compute_id):
"""
:param request:
@ -99,7 +95,6 @@ def instances(request, compute_id):
return render(request, 'instances.html', locals())
@login_required
def instance(request, compute_id, vname):
"""
:param request:
@ -1081,7 +1076,6 @@ def instance(request, compute_id, vname):
return render(request, 'instance.html', locals())
@login_required
def inst_status(request, compute_id, vname):
"""
:param request:
@ -1276,7 +1270,6 @@ def instances_actions(request):
return HttpResponseRedirect(request.get_full_path())
@login_required
def inst_graph(request, compute_id, vname):
"""
:param request:
@ -1339,7 +1332,6 @@ def _get_dhcp_mac_address(vname):
return mac
@login_required
def guess_mac_address(request, vname):
data = {'vname': vname}
mac = _get_dhcp_mac_address(vname)
@ -1358,14 +1350,12 @@ def _get_random_mac_address():
return mac
@login_required
def random_mac_address(request):
data = dict()
data['mac'] = _get_random_mac_address()
return HttpResponse(json.dumps(data))
@login_required
def guess_clone_name(request):
dhcp_file = '/srv/webvirtcloud/dhcpd.conf'
prefix = settings.CLONE_INSTANCE_DEFAULT_PREFIX
@ -1382,7 +1372,6 @@ def guess_clone_name(request):
return HttpResponse(json.dumps({}))
@login_required
def check_instance(request, vname):
instance = Instance.objects.filter(name=vname)
data = {'vname': vname, 'exists': False}

View file

@ -1,14 +1,14 @@
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 interfaces.forms import AddInterface
from vrtManager.interface import wvmInterface, wvmInterfaces
from libvirt import libvirtError
from admin.decorators import superuser_only
@login_required
@superuser_only
def interfaces(request, compute_id):
"""
:param request:
@ -16,18 +16,12 @@ def interfaces(request, compute_id):
:return:
"""
if not request.user.is_superuser:
return HttpResponseRedirect(reverse('index'))
ifaces_all = []
error_messages = []
compute = get_object_or_404(Compute, pk=compute_id)
try:
conn = wvmInterfaces(compute.hostname,
compute.login,
compute.password,
compute.type)
conn = wvmInterfaces(compute.hostname, compute.login, compute.password, compute.type)
ifaces = conn.get_ifaces()
try:
netdevs = conn.get_net_devices()
@ -42,10 +36,9 @@ def interfaces(request, compute_id):
form = AddInterface(request.POST)
if form.is_valid():
data = form.cleaned_data
conn.create_iface(data['name'], data['itype'], data['start_mode'], data['netdev'],
data['ipv4_type'], data['ipv4_addr'], data['ipv4_gw'],
data['ipv6_type'], data['ipv6_addr'], data['ipv6_gw'],
data['stp'], data['delay'])
conn.create_iface(data['name'], data['itype'], data['start_mode'], data['netdev'], data['ipv4_type'],
data['ipv4_addr'], data['ipv4_gw'], data['ipv6_type'], data['ipv6_addr'],
data['ipv6_gw'], data['stp'], data['delay'])
return HttpResponseRedirect(request.get_full_path())
else:
for msg_err in form.errors.values():
@ -57,7 +50,7 @@ def interfaces(request, compute_id):
return render(request, 'interfaces.html', locals())
@login_required
@superuser_only
def interface(request, compute_id, iface):
"""
:param request:
@ -66,19 +59,12 @@ def interface(request, compute_id, iface):
:return:
"""
if not request.user.is_superuser:
return HttpResponseRedirect(reverse('index'))
ifaces_all = []
error_messages = []
compute = get_object_or_404(Compute, pk=compute_id)
try:
conn = wvmInterface(compute.hostname,
compute.login,
compute.password,
compute.type,
iface)
conn = wvmInterface(compute.hostname, compute.login, compute.password, compute.type, iface)
start_mode = conn.get_start_mode()
state = conn.is_active()
mac = conn.get_mac()

View file

@ -2,7 +2,5 @@ from django.urls import path, re_path
from . import views
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'),
]

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.shortcuts import render
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 logs.models import Logs
from django.conf import settings
import json
def addlogmsg(user, instance, message):
@ -19,37 +21,13 @@ def addlogmsg(user, instance, message):
add_log_msg.save()
@login_required
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
@superuser_only
def vm_logs(request, vname):
"""
:param request:
:param vname:
:return:
"""
if not request.user.is_superuser:
return HttpResponseRedirect(reverse('index'))
vm = Instance.objects.get(name=vname)
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['date'] = l.date.strftime('%x %X')
logs.append(log)
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.utils.translation import ugettext_lazy as _
from django.shortcuts import get_object_or_404, render
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 networks.forms import AddNetPool
from vrtManager.network import wvmNetwork, wvmNetworks
from vrtManager.network import network_size
from libvirt import libvirtError
from django.contrib import messages
from vrtManager.network import network_size, wvmNetwork, wvmNetworks
@login_required
@superuser_only
def networks(request, compute_id):
"""
:param request:
@ -19,17 +19,16 @@ def networks(request, compute_id):
:return:
"""
if not request.user.is_superuser:
return HttpResponseRedirect(reverse('index'))
error_messages = []
compute = get_object_or_404(Compute, pk=compute_id)
try:
conn = wvmNetworks(compute.hostname,
compute.login,
compute.password,
compute.type)
conn = wvmNetworks(
compute.hostname,
compute.login,
compute.password,
compute.type,
)
networks = conn.get_networks_info()
dhcp4 = netmask4 = gateway4 = ''
dhcp6 = prefix6 = gateway6 = ''
@ -54,11 +53,21 @@ def networks(request, compute_id):
if prefix6 != '64':
error_messages.append(_('For libvirt, the IPv6 network prefix must be /64'))
if not error_messages:
conn.create_network(data['name'],
data['forward'],
ipv4, gateway4, netmask4, dhcp4,
ipv6, gateway6, prefix6, dhcp6,
data['bridge_name'], data['openvswitch'], data['fixed'])
conn.create_network(
data['name'],
data['forward'],
ipv4,
gateway4,
netmask4,
dhcp4,
ipv6,
gateway6,
prefix6,
dhcp6,
data['bridge_name'],
data['openvswitch'],
data['fixed'],
)
return HttpResponseRedirect(reverse('network', args=[compute_id, data['name']]))
else:
for msg_err in form.errors.values():
@ -70,7 +79,7 @@ def networks(request, compute_id):
return render(request, 'networks.html', locals())
@login_required
@superuser_only
def network(request, compute_id, pool):
"""
:param request:
@ -79,18 +88,17 @@ def network(request, compute_id, pool):
:return:
"""
if not request.user.is_superuser:
return HttpResponseRedirect(reverse('index'))
error_messages = []
compute = get_object_or_404(Compute, pk=compute_id)
try:
conn = wvmNetwork(compute.hostname,
compute.login,
compute.password,
compute.type,
pool)
conn = wvmNetwork(
compute.hostname,
compute.login,
compute.password,
compute.type,
pool,
)
networks = conn.get_networks()
state = conn.is_active()
device = conn.get_bridge_device()
@ -190,8 +198,7 @@ def network(request, compute_id, pool):
if edit_xml:
conn.edit_network(edit_xml)
if conn.is_active():
messages.success(request, _("Network XML is changed. \\"
"Stop and start network to activate new config."))
messages.success(request, _("Network XML is changed. \\" "Stop and start network to activate new config."))
else:
messages.success(request, _("Network XML is changed."))
return HttpResponseRedirect(request.get_full_path())
@ -204,8 +211,10 @@ def network(request, compute_id, pool):
try:
conn.set_qos(qos_dir, average, peak, burst)
if conn.is_active():
messages.success(request, _("{} Qos is set. Network XML is changed.").format(qos_dir.capitalize()) +
_("Stop and start network to activate new config"))
messages.success(
request,
_("{} Qos is set. Network XML is changed.").format(qos_dir.capitalize()) +
_("Stop and start network to activate new config"))
else:
messages.success(request, _("{} Qos is set").format(qos_dir.capitalize()))
except libvirtError as lib_err:
@ -216,8 +225,10 @@ def network(request, compute_id, pool):
conn.unset_qos(qos_dir)
if conn.is_active():
messages.success(request, _("{} Qos is deleted. Network XML is changed. ").format(qos_dir.capitalize()) +
_("Stop and start network to activate new config."))
messages.success(
request,
_("{} Qos is deleted. Network XML is changed. ").format(qos_dir.capitalize()) +
_("Stop and start network to activate new config."))
else:
messages.success(request, _("{} Qos is deleted").format(qos_dir.capitalize()))
return HttpResponseRedirect(request.get_full_path())

View file

@ -2,19 +2,20 @@
from __future__ import unicode_literals
from django.http import HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.utils.translation import ugettext_lazy as _
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.contrib.auth.decorators import login_required
from computes.models import Compute
from vrtManager import util
from vrtManager.nwfilters import wvmNWFilters, wvmNWFilter
from vrtManager.instance import wvmInstances, wvmInstance
from django.utils.translation import ugettext_lazy as _
from libvirt import libvirtError
from admin.decorators import superuser_only
from computes.models import Compute
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):
"""
:param request:
@ -22,18 +23,12 @@ def nwfilters(request, compute_id):
:return:
"""
if not request.user.is_superuser:
return HttpResponseRedirect(reverse('index'))
error_messages = []
nwfilters_all = []
compute = get_object_or_404(Compute, pk=compute_id)
try:
conn = wvmNWFilters(compute.hostname,
compute.login,
compute.password,
compute.type)
conn = wvmNWFilters(compute.hostname, compute.login, compute.password, compute.type)
if request.method == 'POST':
if 'create_nwfilter' in request.POST:
@ -107,12 +102,13 @@ def nwfilters(request, compute_id):
error_messages.append(err)
addlogmsg(request.user.username, compute.hostname, err)
return render(request, 'nwfilters.html', {'error_messages': error_messages,
'nwfilters': nwfilters_all,
'compute': compute})
return render(request, 'nwfilters.html', {
'error_messages': error_messages,
'nwfilters': nwfilters_all,
'compute': compute
})
@login_required
def nwfilter(request, compute_id, nwfltr):
error_messages = []
@ -120,15 +116,8 @@ def nwfilter(request, compute_id, nwfltr):
compute = get_object_or_404(Compute, pk=compute_id)
try:
nwfilter = wvmNWFilter(compute.hostname,
compute.login,
compute.password,
compute.type,
nwfltr)
conn = wvmNWFilters(compute.hostname,
compute.login,
compute.password,
compute.type)
nwfilter = wvmNWFilter(compute.hostname, compute.login, compute.password, compute.type, nwfltr)
conn = wvmNWFilters(compute.hostname, compute.login, compute.password, compute.type)
for nwf in conn.get_nwfilters():
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 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 admin.decorators import superuser_only
from computes.models import Compute
from vrtManager.secrets import wvmSecrets
@login_required
@superuser_only
def secrets(request, compute_id):
"""
:param request:
@ -16,17 +18,11 @@ def secrets(request, compute_id):
:return:
"""
if not request.user.is_superuser:
return HttpResponseRedirect(reverse('index'))
secrets_all = []
error_messages = []
compute = get_object_or_404(Compute, pk=compute_id)
try:
conn = wvmSecrets(compute.hostname,
compute.login,
compute.password,
compute.type)
conn = wvmSecrets(compute.hostname, compute.login, compute.password, compute.type)
secrets = conn.get_secrets()
for uuid in secrets:
@ -35,11 +31,12 @@ def secrets(request, compute_id):
secret_value = conn.get_secret_value(uuid)
except libvirtError as lib_err:
secret_value = None
secrets_all.append({'usage': secrt.usageID(),
'uuid': secrt.UUIDString(),
'usageType': secrt.usageType(),
'value': secret_value
})
secrets_all.append({
'usage': secrt.usageID(),
'uuid': secrt.UUIDString(),
'usageType': secrt.usageType(),
'value': secret_value
})
if request.method == 'POST':
if 'create' in request.POST:
form = AddSecret(request.POST)

View file

@ -150,4 +150,19 @@ p {
.multipleselect-on {
color:#ffffff;
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.utils.translation import ugettext_lazy as _
from django.urls import reverse
from django.contrib.auth.decorators import login_required
from computes.models import Compute
from storages.forms import AddStgPool, AddImage, CloneImage
from vrtManager.storage import wvmStorage, wvmStorages
from libvirt import libvirtError
from django.contrib import messages
import json
from admin.decorators import superuser_only
@login_required
@superuser_only
def storages(request, compute_id):
"""
:param request:
@ -19,17 +19,11 @@ def storages(request, compute_id):
:return:
"""
if not request.user.is_superuser:
return HttpResponseRedirect(reverse('index'))
error_messages = []
compute = get_object_or_404(Compute, pk=compute_id)
try:
conn = wvmStorages(compute.hostname,
compute.login,
compute.password,
compute.type)
conn = wvmStorages(compute.hostname, compute.login, compute.password, compute.type)
storages = conn.get_storages_info()
secrets = conn.get_secrets()
@ -50,12 +44,10 @@ def storages(request, compute_id):
error_messages.append(msg)
if not error_messages:
if data['stg_type'] == 'rbd':
conn.create_storage_ceph(data['stg_type'], data['name'],
data['ceph_pool'], data['ceph_host'],
conn.create_storage_ceph(data['stg_type'], data['name'], data['ceph_pool'], data['ceph_host'],
data['ceph_user'], data['secret'])
elif data['stg_type'] == 'netfs':
conn.create_storage_netfs(data['stg_type'], data['name'],
data['netfs_host'], data['source'],
conn.create_storage_netfs(data['stg_type'], data['name'], data['netfs_host'], data['source'],
data['source_format'], data['target'])
else:
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())
@login_required
@superuser_only
def storage(request, compute_id, pool):
"""
:param request:
@ -78,10 +70,6 @@ def storage(request, compute_id, pool):
:param pool:
:return:
"""
if not request.user.is_superuser:
return HttpResponseRedirect(reverse('index'))
def handle_uploaded_file(path, f_name):
target = path + '/' + str(f_name)
destination = open(target, 'wb+')
@ -94,11 +82,7 @@ def storage(request, compute_id, pool):
meta_prealloc = False
try:
conn = wvmStorage(compute.hostname,
compute.login,
compute.password,
compute.type,
pool)
conn = wvmStorage(compute.hostname, compute.login, compute.password, compute.type, pool)
storages = conn.get_storages()
state = conn.is_active()
@ -215,16 +199,11 @@ def storage(request, compute_id, pool):
return render(request, 'storage.html', locals())
@login_required
def get_volumes(request, compute_id, pool):
data = {}
compute = get_object_or_404(Compute, pk=compute_id)
try:
conn = wvmStorage(compute.hostname,
compute.login,
compute.password,
compute.type,
pool)
conn = wvmStorage(compute.hostname, compute.login, compute.password, compute.type, pool)
conn.refresh()
data['vols'] = sorted(conn.get_volumes())
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 tags_active %}
<!-- Fixed navbar -->
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" 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="{% url 'index' %}">WebVirtCloud</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li {% class_active request "^/instance" %}>
<a href="{% url 'allinstances' %}"><i class="fa fa-fw fa-desktop"></i> {% trans "Instances" %}</a>
</li>
{% if request.user.is_superuser %}
<li {% class_active request "^/compute" %}{% class_active request "^/create" %}>
<a href="{% url 'computes' %}"><i class="fa fa-fw fa-server"></i> {% trans "Computes" %}</a>
</li>
<li {% class_active request "^/account" %}>
<a href="{% url 'accounts' %}"><i class="fa fa-fw fa-users"></i> {% trans "Users" %}</a>
</li>
<li {% class_active request "^/log" %}>
<a href="{% url 'showlogs' %}"><i class="fa fa-fw fa-list-alt"></i> {% trans "Logs" %}</a>
</li>
{% endif %}
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<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>
<ul class="dropdown-menu" role="menu">
<li>
<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>
{% load font_awesome %}
{% load common_tags %}
<!-- Fixed navbar -->
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" 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="{% url 'index' %}">WebVirtCloud</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li {% class_active request '^/instance' %}>
<a href="{% url 'allinstances' %}"><i class="fa fa-fw fa-desktop"></i> {% trans "Instances" %}</a>
</li>
{% if request.user.is_superuser %}
<li {% class_active request "^/compute" %}>
<a href="{% url 'computes' %}"><i class="fa fa-fw fa-server"></i> {% trans "Computes" %}</a>
</li>
<li class="dropdown {% app_active request 'admin' %}">
<a id="administration" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{% trans "Administration" %} {% icon 'caret-down' %}
</a>
<ul class="dropdown-menu" aria-labelledby="administration">
<li class="{% view_active request 'admin:user_list' %}">
<a href="{% url 'admin:user_list' %}">{% icon 'user-plus' %} {% trans "Users" %}</a>
</li>
<li class="{% view_active request 'admin:group_list' %}">
<a href="{% url 'admin:group_list' %}">{% icon 'users' %} {% trans "Groups" %}</a>
</li>
<li class="{% view_active request 'admin:logs' %}">
<a href="{% url 'admin:logs' %}">{% icon 'list-alt' %} {% trans "Logs" %}</a>
</li>
</ul>
</li>
{% endif %}
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<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>
<ul class="dropdown-menu" role="menu">
<li>
<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 = ['*']
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'bootstrap3',
'fa',
'accounts',
'admin',
'computes',
'console',
'create',
@ -44,6 +45,7 @@ MIDDLEWARE = [
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'login_required.middleware.LoginRequiredMiddleware',
'django.contrib.auth.middleware.RemoteUserMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
@ -55,7 +57,9 @@ ROOT_URLCONF = 'webvirtcloud.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [ os.path.join(BASE_DIR, 'templates'), ],
'DIRS': [
os.path.join(BASE_DIR, 'templates'),
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
@ -64,13 +68,15 @@ TEMPLATES = [
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
'libraries': {
'common_tags': 'webvirtcloud.common_tags',
},
},
},
]
WSGI_APPLICATION = 'webvirtcloud.wsgi.application'
# Database
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
@ -83,12 +89,12 @@ DATABASES = {
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
#'django.contrib.auth.backends.RemoteUserBackend',
#'accounts.backends.MyRemoteUserBackend',
]
LOGIN_URL = '/accounts/login'
LOGOUT_REDIRECT_URL = '/accounts/login'
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
@ -165,10 +171,6 @@ QUOTA_DEBUG = True
ALLOW_EMPTY_PASSWORD = True
SHOW_ACCESS_ROOT_PASSWORD = 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
VIEW_INSTANCES_LIST_STYLE = 'grouped'

View file

@ -2,16 +2,14 @@ from django.urls import include, path
from instances.views import index
from console.views import console
# from django.contrib import admin
urlpatterns = [
path('', index, name='index'),
path('admin/', include(('admin.urls', 'admin'), namespace='admin')),
path('instances/', include('instances.urls')),
path('accounts/', include('accounts.urls')),
path('computes/', include('computes.urls')),
path('logs/', include('logs.urls')),
path('datasource/', include('datasource.urls')),
path('console/', console, name='console'),
# url(r'^admin/', include(admin.site.urls)),
]