1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2025-07-31 12:41:08 +00:00

Added admin application

- Manage users
- Manage groups
- Manage logs
This commit is contained in:
Real-Gecko 2020-05-27 18:24:06 +06:00 committed by catborise
parent 713d565a2d
commit fec59b1dd7
37 changed files with 1106 additions and 517 deletions

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'),
path('profile/<int:user_id>/', views.account, name='account'), path('profile/<int:user_id>/', views.account, name='account'),
] ]

View file

@ -1,13 +1,14 @@
from django.core.validators import ValidationError
from django.shortcuts import render
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse from django.urls import reverse
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.core.validators import ValidationError
from instances.models import Instance
from accounts.models import *
from appsettings.models import AppSettings
from accounts.forms import UserAddForm from accounts.forms import UserAddForm
from accounts.models import *
from admin.decorators import superuser_only
from appsettings.models import AppSettings
from instances.models import Instance
import sass import sass
import os import os
@ -19,7 +20,7 @@ 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 = AppSettings.objects.get(key="SHOW_PROFILE_EDIT_PASSWORD").value show_profile_edit_password = AppSettings.objects.get(key="SHOW_PROFILE_EDIT_PASSWORD").value
@ -29,7 +30,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', '')
@ -39,11 +40,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 +70,7 @@ def profile(request):
return HttpResponseRedirect(request.get_full_path()) return HttpResponseRedirect(request.get_full_path())
return render(request, 'profile.html', locals()) return render(request, 'profile.html', locals())
@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')
appsettings = AppSettings.objects.all()
allow_empty_password = appsettings.get(key="ALLOW_EMPTY_PASSWORD").value
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 appsettings.get(key="VIEW_ACCOUNTS_STYLE").value == "list":
accounts_template_file = 'accounts-list.html'
return render(request, accounts_template_file, locals())
def account(request, user_id): def account(request, user_id):
""" """
:param request: :param request:
@ -157,9 +78,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)

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 bootstrap4 %}
{% 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 bootstrap4 %}
{% 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 offset-1">
<form id="create-update" action="" method="post">
{% csrf_token %}
{% bootstrap_form form layout='horizontal' %}
</form>
<div class="form-group float-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 float-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 float-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 float-right">
{% icon 'plus' %}
</a>
<div class="float-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="float-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 bootstrap4 %}
{% 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 offset-1">
<form id="create-update" action="" method="post">
{% csrf_token %}
{% bootstrap_form user_form layout='horizontal' %}
{% bootstrap_form attributes_form layout='horizontal' %}
</form>
<div class="form-group float-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,79 @@
{% 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:user_create' %}" class="btn btn-success btn-header float-right">
{% icon 'plus' %}
</a>
<div class="float-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 scope="col">{% trans "Username" %}</th>
<th scope="col">{% trans "Status" %}</th>
<th scope="col">{% trans "Staff" %}</th>
<th scope="col">{% trans "Superuser" %}</th>
<th scope="col">{% trans "Clone" %}</th>
<th scope="col">{% trans "" %}</th>
</tr>
</thead>
<tbody class="searchable">
{% for user in users %}
<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 user.userattributes.can_clone_instances %}{% icon 'check' %}{% endif %}</td>
<td>
<div class="float-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'),
]

172
admin/views.py Normal file
View file

@ -0,0 +1,172 @@
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 appsettings.models import AppSettings
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, int(AppSettings.objects.get(key="LOGS_PER_PAGE").value))
page = request.GET.get('page', 1)
logs = paginator.page(page)
return render(request, 'admin/logs.html', {'logs': logs})

View file

@ -10,32 +10,31 @@ from computes.models import Compute
from computes.forms import ComputeAddTcpForm, ComputeAddSshForm, ComputeEditHostForm, ComputeAddTlsForm, ComputeAddSocketForm from computes.forms import ComputeAddTcpForm, ComputeAddSshForm, ComputeEditHostForm, ComputeAddTlsForm, ComputeAddSocketForm
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 admin.decorators import superuser_only
@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 = []
@ -133,6 +132,7 @@ def computes(request):
return render(request, 'computes.html', locals()) return render(request, 'computes.html', locals())
@superuser_only
def overview(request, compute_id): def overview(request, compute_id):
""" """
:param request: :param request:
@ -140,18 +140,17 @@ 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)
status = 'true' if connection_manager.host_is_up(compute.type, compute.hostname) is True else 'false' status = 'true' if connection_manager.host_is_up(compute.type, compute.hostname) is True else 'false'
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()
@ -173,10 +172,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()
@ -186,9 +187,11 @@ def compute_graph(request, compute_id):
mem_usage = {'usage': 0} mem_usage = {'usage': 0}
current_time = 0 current_time = 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)
@ -207,10 +210,12 @@ 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)
@ -239,10 +244,12 @@ 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
@ -261,10 +268,12 @@ 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
@ -283,10 +292,12 @@ 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,4 +1,6 @@
Django==2.2.12 Django==2.2.12
django-bootstrap4
django-fa==1.0.0
django-login-required-middleware==0.5.0 django-login-required-middleware==0.5.0
gunicorn==20.0.4 gunicorn==20.0.4
libsass libsass

View file

@ -12,17 +12,17 @@ from instances.models import Instance
from vrtManager.create import wvmCreate from vrtManager.create import wvmCreate
from vrtManager import util from vrtManager import util
from logs.views import addlogmsg from logs.views import addlogmsg
from admin.decorators import superuser_only
from webvirtcloud.settings import QEMU_CONSOLE_LISTEN_ADDRESSES from webvirtcloud.settings import QEMU_CONSOLE_LISTEN_ADDRESSES
@superuser_only
def create_instance_select_type(request, compute_id): def create_instance_select_type(request, compute_id):
""" """
:param request: :param request:
:param compute_id: :param compute_id:
:return: :return:
""" """
if not request.user.is_superuser:
return HttpResponseRedirect(reverse('index'))
conn = None conn = None
error_messages = list() error_messages = list()
@ -34,10 +34,7 @@ def create_instance_select_type(request, compute_id):
appsettings = AppSettings.objects.all() appsettings = AppSettings.objects.all()
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)
@ -69,6 +66,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())
@superuser_only
def create_instance(request, compute_id, arch, machine): def create_instance(request, compute_id, arch, machine):
""" """
:param request: :param request:
@ -77,8 +75,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()
@ -92,10 +88,7 @@ def create_instance(request, compute_id, arch, machine):
appsettings = AppSettings.objects.all() appsettings = AppSettings.objects.all()
try: try:
conn = wvmCreate(compute.hostname, conn = wvmCreate(compute.hostname, compute.login, compute.password, compute.type)
compute.login,
compute.password,
compute.type)
default_firmware = appsettings.get(key="INSTANCE_FIRMWARE_DEFAULT_TYPE").value default_firmware = appsettings.get(key="INSTANCE_FIRMWARE_DEFAULT_TYPE").value
default_cpu_mode = appsettings.get(key="INSTANCE_CPU_DEFAULT_MODE").value default_cpu_mode = appsettings.get(key="INSTANCE_CPU_DEFAULT_MODE").value
@ -150,10 +143,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:
@ -184,8 +174,14 @@ 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'], default_disk_format, path = conn.create_volume(
meta_prealloc, default_disk_owner_uid, default_disk_owner_gid) data['storage'],
data['name'],
data['hdd_size'],
default_disk_format,
meta_prealloc,
default_disk_owner_uid,
default_disk_owner_gid)
volume = dict() volume = dict()
volume['device'] = 'disk' volume['device'] = 'disk'
volume['path'] = path volume['path'] = path
@ -210,7 +206,13 @@ 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'], meta_prealloc, default_disk_owner_uid, default_disk_owner_gid) clone_path = conn.clone_from_template(
data['name'],
templ_path,
data['storage'],
meta_prealloc,
default_disk_owner_uid,
default_disk_owner_gid)
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)
@ -258,22 +260,32 @@ 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,
volumes=volume_list, volumes=volume_list,
networks=data['networks'], virtio=data['virtio'], networks=data['networks'],
listen_addr=data["listener_addr"], nwfilter=data["nwfilter"], virtio=data['virtio'],
graphics=data["graphics"], video=data["video"], listen_addr=data["listener_addr"],
console_pass=data["console_pass"], mac=data['mac'], 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

@ -5,8 +5,10 @@ from libvirt import libvirtError
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 admin.decorators import superuser_only
@superuser_only
def interfaces(request, compute_id): def interfaces(request, compute_id):
""" """
:param request: :param request:
@ -14,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()
@ -40,10 +36,15 @@ 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['ipv4_type'], data['ipv4_addr'], data['ipv4_gw'], data['name'],
data['ipv6_type'], data['ipv6_addr'], data['ipv6_gw'], data['itype'],
data['stp'], data['delay']) 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()) return HttpResponseRedirect(request.get_full_path())
else: else:
for msg_err in form.errors.values(): for msg_err in form.errors.values():
@ -55,6 +56,7 @@ def interfaces(request, compute_id):
return render(request, 'interfaces.html', locals()) return render(request, 'interfaces.html', locals())
@superuser_only
def interface(request, compute_id, iface): def interface(request, compute_id, iface):
""" """
:param request: :param request:
@ -63,19 +65,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'),
path('<int:page>/', views.showlogs, name='showlogspage'),
path('vm_logs/<vname>/', views.vm_logs, name='vm_logs'), path('vm_logs/<vname>/', views.vm_logs, name='vm_logs'),
] ]

View file

@ -1,8 +1,9 @@
import json import json
from django.shortcuts import render
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 appsettings.models import AppSettings
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
@ -18,27 +19,7 @@ def addlogmsg(user, instance, message):
add_log_msg.save() add_log_msg.save()
def showlogs(request, page=1): @superuser_only
"""
:param request:
:param page:
:return:
"""
if not request.user.is_superuser:
return HttpResponseRedirect(reverse('index'))
page = int(page)
logs_per_page = int(AppSettings.objects.get(key="LOGS_PER_PAGE").value)
limit_from = (page-1) * logs_per_page
limit_to = page * logs_per_page
logs = Logs.objects.all().order_by('-date')[limit_from:limit_to+1]
has_next_page = logs.count() > logs_per_page
# TODO: remove last element from queryset, but do not affect database
return render(request, 'showlogs.html', locals())
def vm_logs(request, vname): def vm_logs(request, vname):
""" """
:param request: :param request:
@ -46,9 +27,6 @@ def vm_logs(request, 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')
logs = [] logs = []

View file

@ -1,32 +1,34 @@
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 import messages from django.contrib import messages
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from libvirt import libvirtError 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
@superuser_only
def networks(request, compute_id): def networks(request, compute_id):
""" """
:param request: :param request:
:param compute_id: :param 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 = ''
@ -51,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():
@ -67,6 +79,7 @@ def networks(request, compute_id):
return render(request, 'networks.html', locals()) return render(request, 'networks.html', locals())
@superuser_only
def network(request, compute_id, pool): def network(request, compute_id, pool):
""" """
:param request: :param request:
@ -75,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()
@ -186,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())
@ -200,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, _(f"{qos_dir.capitalize()} QoS is set. Network XML is changed.") + messages.success(
_("Stop and start network to activate new config")) request,
_(f"{qos_dir.capitalize()} QoS is set. Network XML is changed.") +
_("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:
@ -212,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, _(f"{qos_dir.capitalize()} QoS is deleted. Network XML is changed. ") + messages.success(
_("Stop and start network to activate new config.")) request,
_(f"{qos_dir.capitalize()} QoS is deleted. Network XML is changed. ") +
_("Stop and start network to activate new config."))
else: else:
messages.success(request, _(f"{qos_dir.capitalize()} QoS is deleted")) messages.success(request, _(f"{qos_dir.capitalize()} QoS is deleted"))
return HttpResponseRedirect(request.get_full_path()) return HttpResponseRedirect(request.get_full_path())

View file

@ -1,15 +1,18 @@
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.utils.translation import ugettext_lazy as _
from libvirt import libvirtError from libvirt import libvirtError
from admin.decorators import superuser_only
from computes.models import Compute from computes.models import Compute
from vrtManager import util
from vrtManager.nwfilters import wvmNWFilters, wvmNWFilter
from vrtManager.instance import wvmInstances, wvmInstance
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
@superuser_only
def nwfilters(request, compute_id): def nwfilters(request, compute_id):
""" """
:param request: :param request:
@ -17,18 +20,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:
@ -102,9 +99,11 @@ 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
})
def nwfilter(request, compute_id, nwfltr): def nwfilter(request, compute_id, nwfltr):
@ -119,15 +118,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,60 +1,61 @@
{% load i18n %} {% load i18n %}
{% if request.user.is_superuser %} {% if request.user.is_superuser %}
<a href="#AddSecret" type="button" class="btn btn-success float-right" data-toggle="modal"> <a href="#AddSecret" type="button" class="btn btn-success float-right" data-toggle="modal">
<span class="fa fa-plus" aria-hidden="true"></span> <span class="fa fa-plus" aria-hidden="true"></span>
</a> </a>
<!-- Modal Secret --> <!-- Modal Secret -->
<div class="modal fade" id="AddSecret" tabindex="-1" role="dialog" aria-labelledby="AddSecret" aria-hidden="true"> <div class="modal fade" id="AddSecret" tabindex="-1" role="dialog" aria-labelledby="AddSecret" aria-hidden="true">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> <h5 class="modal-title">{% trans "Create New Secret" %}</h5>
<h5 class="modal-title">{% trans "Create New Secret" %}</h5> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form method="post" action="" role="form" aria-label="Create Secret form">{% csrf_token %} <form method="post" action="" role="form" aria-label="Create Secret form">{% csrf_token %}
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">{% trans "Ephemeral" %}</label> <label class="col-sm-3 col-form-label">{% trans "Ephemeral" %}</label>
<div class="col-sm-6"> <div class="col-sm-6">
<select name="ephemeral" class="form-control"> <select name="ephemeral" class="form-control">
<option value="no">{% trans "no" %}</option> <option value="no">{% trans "no" %}</option>
<option value="yes">{% trans "yes" %}</option> <option value="yes">{% trans "yes" %}</option>
</select> </select>
</div>
</div> </div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">{% trans "Private" %}</label>
<div class="col-sm-6">
<select name="private" class="form-control">
<option value="no">{% trans "no" %}</option>
<option value="yes">{% trans "yes" %}</option>
</select>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">{% trans "Usage" %}</label>
<div class="col-sm-6">
<select name="usage_type" class="form-control">
<option value="ceph">{% trans "ceph" %}</option>
<option value="volume">{% trans "volume" %}</option>
<option value="iscsi">{% trans "iscsi" %}</option>
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 col-form-label">{% trans "Data" %}</label>
<div class="col-sm-6">
<input type="text" name="data" class="form-control" value="" required pattern="[a-z0-9\. ]+"/>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">{% trans "Close" %}</button>
<button type="submit" class="btn btn-primary" name="create">{% trans "Create" %}</button>
</div> </div>
</form> <div class="form-group row">
</div> <!-- /.modal-content --> <label class="col-sm-3 col-form-label">{% trans "Private" %}</label>
</div> <!-- /.modal-dialog --> <div class="col-sm-6">
</div><!-- /.modal --> <select name="private" class="form-control">
<option value="no">{% trans "no" %}</option>
<option value="yes">{% trans "yes" %}</option>
</select>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">{% trans "Usage" %}</label>
<div class="col-sm-6">
<select name="usage_type" class="form-control">
{% for key, usage_type in secret_usage_types.items %}
<option value="{{ usage_type }}">{{ usage_type }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">{% trans "Data" %}</label>
<div class="col-sm-6">
<input type="text" name="data" class="form-control" value="" required
pattern="[a-z0-9\. ]+" />
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" 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 %} {% endif %}

View file

@ -3,128 +3,129 @@
{% load staticfiles %} {% load staticfiles %}
{% block title %}{% trans "Secrets" %} - {{ compute.name }}{% endblock %} {% block title %}{% trans "Secrets" %} - {{ compute.name }}{% endblock %}
{% block style %} {% block style %}
<link rel="stylesheet" href="{% static "css/sortable-theme-bootstrap.css" %}" /> <link rel="stylesheet" href="{% static "css/sortable-theme-bootstrap.css" %}" />
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<!-- Page Heading --> <!-- Page Heading -->
<div class="row"> <div class="row">
<div class="col-lg-12"> <div class="col-lg-12">
{% include 'create_secret_block.html' %} {% include 'create_secret_block.html' %}
<h2 class="page-header">{% trans "Secrets" %}</h2> <h2 class="page-header">{% trans "Secrets" %}</h2>
<nav aria-label="breadcrumb"> <nav aria-label="breadcrumb">
<ol class="breadcrumb bg-light shadow-sm"> <ol class="breadcrumb bg-light shadow-sm">
<li class="breadcrumb-item active"> <li class="breadcrumb-item active">
<a href="{% url 'overview' compute.id %}"><i class="fa fa-dashboard"></i> {% trans "Overview" %}</a> <a href="{% url 'overview' compute.id %}"><i class="fa fa-dashboard"></i> {% trans "Overview" %}</a>
</li> </li>
<li class="breadcrumb-item"> <li class="breadcrumb-item">
<a href="{% url 'instances' compute.id %}"><i class="fa fa-server"></i> {% trans "Instances" %}</a> <a href="{% url 'instances' compute.id %}"><i class="fa fa-server"></i> {% trans "Instances" %}</a>
</li> </li>
<li class="breadcrumb-item"> <li class="breadcrumb-item">
<a href="{% url 'storages' compute.id %}"><i class="fa fa-hdd-o"></i> {% trans "Storages" %}</a> <a href="{% url 'storages' compute.id %}"><i class="fa fa-hdd-o"></i> {% trans "Storages" %}</a>
</li> </li>
<li class="breadcrumb-item"> <li class="breadcrumb-item">
<a href="{% url 'networks' compute.id %}"><i class="fa fa-sitemap"></i> {% trans "Networks" %}</a> <a href="{% url 'networks' compute.id %}"><i class="fa fa-sitemap"></i> {% trans "Networks" %}</a>
</li> </li>
<li class="breadcrumb-item"> <li class="breadcrumb-item">
<a href="{% url 'interfaces' compute.id %}"><i class="fa fa-wifi"></i> {% trans "Interfaces" %}</a> <a href="{% url 'interfaces' compute.id %}"><i class="fa fa-wifi"></i> {% trans "Interfaces" %}</a>
</li> </li>
<li class="breadcrumb-item"> <li class="breadcrumb-item">
<a href="{% url 'nwfilters' compute.id %}"><i class="fa fa-filter"></i> {% trans "NWFilters" %}</a> <a href="{% url 'nwfilters' compute.id %}"><i class="fa fa-filter"></i> {% trans "NWFilters" %}</a>
</li> </li>
<li class="breadcrumb-item"> <li class="breadcrumb-item">
<span class="font-weight-bold"><i class="fa fa-key"></i> {% trans "Secrets" %}</span> <span class="font-weight-bold"><i class="fa fa-key"></i> {% trans "Secrets" %}</span>
</li> </li>
</ol> </ol>
</nav> </nav>
</div>
</div>
<!-- /.row -->
{% include 'errors_block.html' %}
<div class="row">
{% if not secrets_all %}
<div class="col-lg-12">
<div class="alert alert-warning alert-dismissable shadow-sm">
<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 "Hypervisor doesn't have any Secrets" %}
</div> </div>
</div> </div>
<!-- /.row --> {% else %}
<div class="col-lg-12">
<div class="table-responsive">
<table class="table table-hover sortable-theme-bootstrap" data-sortable>
<thead>
<tr class="active">
<th scope="col">{% trans "UUID" %}</th>
<th scope="col">{% trans "Usage Type" %}</th>
<th scope="col">{% trans "Data" %}</th>
<th scope="col" data-sortable="false" style="width:90px;">{% trans "Actions" %}</th>
</tr>
</thead>
<tbody>
{% for secret in secrets_all %}
<tr>
<th scope="row">{{ secret.uuid }}</th>
<td>{{ secret.usageType }}</td>
<td>{{ secret.usage }}</td>
<td>
<form action="" method="post" role="form" aria-label="Delete secret form">{% csrf_token %}
<input type="hidden" name="uuid" value="{{ secret.uuid }}" />
<a data-toggle="modal" href="#editSecret{{ secret.uuid }}"
class="btn btn-sm btn-primary" title="{% trans "Edit" %}">
<span class="fa fa-pencil"></span>
</a>
<button type="submit" class="btn btn-sm btn-danger" name="delete"
title="{% trans "Delete" %}"
onclick="return confirm('{% trans "Are you sure?" %}')">
<span class="fa fa-trash"></span>
</button>
</form>
{% include 'errors_block.html' %} <!-- Modal -->
<div class="modal fade" id="editSecret{{ secret.uuid }}" tabindex="-1" role="dialog"
<div class="row"> aria-labelledby="editSecret" aria-hidden="true">
{% if not secrets_all %} <div class="modal-dialog">
<div class="col-lg-12"> <div class="modal-content">
<div class="alert alert-warning alert-dismissable shadow-sm"> <div class="modal-header">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button> <h5 class="modal-title">{% trans "Set secret value" %}</h5>
<i class="fa fa-exclamation-triangle"></i> <strong>{% trans "Warning" %}:</strong> {% trans "Hypervisor doesn't have any Secrets" %} <button type="button" class="close" data-dismiss="modal"
</div> aria-hidden="true">&times;</button>
</div> </div>
{% else %} <div class="modal-body">
<div class="col-lg-12"> <form method="post" role="form" aria-label="Edit secret form">
<div class="table-responsive"> {% csrf_token %}
<table class="table table-hover sortable-theme-bootstrap" data-sortable> <div class="form-group row">
<thead> <label class="col-sm-3 col-form-label">{% trans "Value" %}</label>
<tr class="active"> <input type="hidden" name="uuid" value="{{ secret.uuid }}">
<th scope="col">{% trans "UUID" %}</th> <div class="col-sm-6">
<th scope="col">{% trans "Type" %}</th> <input type="text" name="value" class="form-control"
<th scope="col">{% trans "Usage" %}</th> value="{{ secret.value }}" maxlength="45" required
<th scope="col" data-sortable="false" style="width:90px;">{% trans "Action" %}</th> pattern="[a-zA-Z0-9]$+">
</tr> </div>
</thead>
<tbody>
{% for secret in secrets_all %}
<tr>
<th scope="row">{{ secret.uuid }}</th>
<td>{% ifequal secret.usageType 0 %}
{% trans "volume" %}
{% endifequal %}
{% ifequal secret.usageType 1 %}
{% trans "iscsi" %}
{% endifequal %}
{% ifequal secret.usageType 2 %}
{% trans "ceph" %}
{% endifequal %}
</td>
<td>{{ secret.usage }}</td>
<td>
<form action="" method="post" role="form" aria-label="Delete secret form">{% csrf_token %}
<input type="hidden" name="uuid" value="{{ secret.uuid }}"/>
<a data-toggle="modal" href="#editSecret{{ secret.uuid }}" class="btn btn-sm btn-primary" title="{% trans "Edit" %}">
<span class="fa fa-pencil"></span>
</a>
<button type="submit" class="btn btn-sm btn-danger" name="delete" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure?" %}')">
<span class="fa fa-trash"></span>
</button>
</form>
<!-- Modal -->
<div class="modal fade" id="editSecret{{ secret.uuid }}" tabindex="-1" role="dialog"
aria-labelledby="editSecret" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{% trans "Set secret value" %}</h5>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
</div> </div>
<div class="modal-body"> </div> <!-- /.modal-content -->
<form method="post" role="form" aria-label="Edit secret form">{% csrf_token %} <div class="modal-footer">
<div class="form-group row"> <button type="button" class="btn btn-secondary"
<label class="col-sm-3 col-form-label">{% trans "Value" %}</label> data-dismiss="modal">{% trans "Close" %}</button>
<input type="hidden" name="uuid" value="{{ secret.uuid }}"> <button type="submit" class="btn btn-primary"
<div class="col-sm-6"> name="set_value">{% trans "Set" %}</button>
<input type="text" name="value" class="form-control" value="{{ secret.value }}" maxlength="45" required pattern="[a-zA-Z0-9]$+"> </div>
</div> </form>
</div> </div> <!-- /.tab-content -->
</div> <!-- /.modal-content --> </div> <!-- /.modal-dialog -->
<div class="modal-footer"> </div> <!-- /.modal -->
<button type="button" class="btn btn-secondary" data-dismiss="modal">{% trans "Close" %}</button> </td>
<button type="submit" class="btn btn-primary" name="set_value">{% trans "Set" %}</button> </tr>
</div> {% endfor %}
</form> </tbody>
</div> <!-- /.tab-content --> </table>
</div> <!-- /.modal-dialog --> </div>
</div> <!-- /.modal -->
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
</div> </div>
{% endif %}
</div>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
<script src="{% static "js/sortable.min.js" %}"></script> <script src="{% static 'js/sortable.min.js' %}"></script>
{% endblock %} {% endblock %}

View file

@ -1,12 +1,22 @@
from django.shortcuts import render, get_object_or_404 from secrets.forms import AddSecret
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse from django.urls import reverse
from libvirt import libvirtError from libvirt import libvirtError
from libvirt import VIR_SECRET_USAGE_TYPE_NONE, \
VIR_SECRET_USAGE_TYPE_CEPH, \
VIR_SECRET_USAGE_TYPE_TLS, \
VIR_SECRET_USAGE_TYPE_VOLUME, \
VIR_SECRET_USAGE_TYPE_ISCSI
from admin.decorators import superuser_only
from computes.models import Compute from computes.models import Compute
from secrets.forms import AddSecret
from vrtManager.secrets import wvmSecrets from vrtManager.secrets import wvmSecrets
@superuser_only
def secrets(request, compute_id): def secrets(request, compute_id):
""" """
:param request: :param request:
@ -14,36 +24,44 @@ 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)
secret_usage_types = {
VIR_SECRET_USAGE_TYPE_NONE: "none",
VIR_SECRET_USAGE_TYPE_VOLUME: "volume",
VIR_SECRET_USAGE_TYPE_CEPH: "ceph",
VIR_SECRET_USAGE_TYPE_ISCSI: "iscsi",
VIR_SECRET_USAGE_TYPE_TLS: "tls",
}
try: try:
conn = wvmSecrets(compute.hostname, conn = wvmSecrets(compute.hostname, compute.login,
compute.login, compute.password, compute.type)
compute.password,
compute.type)
secrets = conn.get_secrets() secrets = conn.get_secrets()
for uuid in secrets: for uuid in secrets:
secrt = conn.get_secret(uuid) secrt = conn.get_secret(uuid)
try: try:
secret_value = conn.get_secret_value(uuid) secrt_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': secret_usage_types[secrt.usageType()],
}) 'value': secrt_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)
if form.is_valid(): if form.is_valid():
data = form.cleaned_data data = form.cleaned_data
conn.create_secret(data['ephemeral'], data['private'], data['usage_type'], data['data']) conn.create_secret(
data['ephemeral'],
data['private'],
data['usage_type'],
data['data'])
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():
@ -55,7 +73,10 @@ def secrets(request, compute_id):
if 'set_value' in request.POST: if 'set_value' in request.POST:
uuid = request.POST.get('uuid', '') uuid = request.POST.get('uuid', '')
value = request.POST.get('value', '') value = request.POST.get('value', '')
conn.set_secret_value(uuid, value) try:
conn.set_secret_value(uuid, value)
except Exception as err:
error_messages.append(err)
return HttpResponseRedirect(request.get_full_path()) return HttpResponseRedirect(request.get_full_path())
except libvirtError as err: except libvirtError as err:
error_messages.append(err) error_messages.append(err)

View file

@ -155,3 +155,17 @@ p {
vertical-align: middle; vertical-align: middle;
} }
.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

@ -1,33 +1,31 @@
import json import json
from django.shortcuts import render, get_object_or_404 from django.contrib import messages
from django.http import HttpResponseRedirect, HttpResponse from django.http import HttpResponseRedirect, HttpResponse
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.urls import reverse from django.urls import reverse
from django.contrib import messages
from libvirt import libvirtError from libvirt import libvirtError
from admin.decorators import superuser_only
from computes.models import Compute from computes.models import Compute
from appsettings.models import AppSettings from appsettings.models import AppSettings
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
@superuser_only
def storages(request, compute_id): def storages(request, compute_id):
""" """
:param request: :param request:
:param compute_id: :param 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()
@ -48,12 +46,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'])
@ -68,6 +64,7 @@ def storages(request, compute_id):
return render(request, 'storages.html', locals()) return render(request, 'storages.html', locals())
@superuser_only
def storage(request, compute_id, pool): def storage(request, compute_id, pool):
""" """
:param request: :param request:
@ -75,10 +72,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+')
@ -91,11 +84,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()
@ -226,11 +215,7 @@ 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 "403" %}{% endblock %}
{% block content %}
<div class="row">
<div class="col-12 text-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-success" href="javascript:history.back()">&larr; {% trans 'Back'%}</a>
</div>
</div>
{% endblock %}

View file

@ -1,5 +1,6 @@
{% load i18n %} {% load i18n %}
{% load tags_active %} {% load font_awesome %}
{% load common_tags %}
<!-- Fixed navbar --> <!-- Fixed navbar -->
<nav class="navbar navbar-expand-md navbar-dark bg-primary mb-3" aria-label="Main top navbar"> <nav class="navbar navbar-expand-md navbar-dark bg-primary mb-3" aria-label="Main top navbar">
<div class="container"> <div class="container">
@ -10,26 +11,27 @@
<div id="navbar" class="collapse navbar-collapse"> <div id="navbar" class="collapse navbar-collapse">
<ul class="navbar-nav mr-auto mt-2 mt-md-0"> <ul class="navbar-nav mr-auto mt-2 mt-md-0">
<li class="nav-item {% class_active request "^/instance" %}"> <li class="nav-item {% class_active request '^/instances' %}">
<a class="nav-link" href="{% url 'allinstances' %}"><i class="fa fa-fw fa-desktop"></i> {% trans "Instances" %}</a> <a class="nav-link" href="{% url 'allinstances' %}"><i class="fa fa-fw fa-desktop"></i> {% trans "Instances" %}</a>
</li> </li>
{% if request.user.is_superuser %} {% if request.user.is_superuser %}
<li class="nav-item {% class_active request "^/compute" %} <li class="nav-item {% class_active request '^/computes' %}">
{% class_active request "^/create" %}"> <a class="nav-link" href="{% url 'computes' %}"><i class="fa fa-fw fa-server"></i> {% trans "Computes" %}</a>
<a class="nav-link" href="{% url 'computes' %}"><i class="fa fa-fw fa-server"></i> {% trans "Computes" %}</a> </li>
</li>
<li class="nav-item {% class_active request "^/account" %}">
<a class="nav-link" href="{% url 'accounts' %}"><i class="fa fa-fw fa-users"></i> {% trans "Users" %}</a>
</li>
<li class="nav-item {% class_active request "^/log" %}">
<a class="nav-link" href="{% url 'showlogs' %}"><i class="fa fa-fw fa-list-alt"></i> {% trans "Logs" %}</a>
</li>
{% endif %} {% endif %}
</ul> </ul>
<ul class="navbar-nav navbar-right"> <ul class="navbar-nav navbar-right mt-2 mt-md-0">
{% if request.user.is_superuser %} {% if request.user.is_superuser %}
<li class="nav-item"> <li class="nav-item dropdown {% app_active request 'admin' %}">
<a class="nav-link" href="{% url 'appsettings' %}"><i class="fa fa-wrench"></i></a> <a class="nav-link" id="administration" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{% icon 'wrench' %}
</a>
<div class="dropdown-menu" aria-labelledby="administration">
<a class="dropdown-item {% view_active request 'admin:user_list' %}" href="{% url 'admin:user_list' %}">{% icon 'user-plus' %} {% trans "Users" %}</a>
<a class="dropdown-item {% view_active request 'admin:group_list' %}" href="{% url 'admin:group_list' %}">{% icon 'users' %} {% trans "Groups" %}</a>
<a class="dropdown-item {% view_active request 'admin:logs' %}" href="{% url 'admin:logs' %}">{% icon 'list-alt' %} {% trans "Logs" %}</a>
<a class="dropdown-item {% view_active request 'appsettings' %}" href="{% url 'appsettings' %}">{% icon 'cogs' %} {% trans "Settings" %}</a>
</div>
</li> </li>
{% endif %} {% endif %}
<li class="nav-item dropdown"> <li class="nav-item dropdown">
@ -42,8 +44,8 @@
<a class="dropdown-item" href="{% url 'logout' %}"><i class="fa fa-fw fa-power-off"></i> {% trans "Log Out" %}</a> <a class="dropdown-item" href="{% url 'logout' %}"><i class="fa fa-fw fa-power-off"></i> {% trans "Log Out" %}</a>
</div> </div>
</li> </li>
</ul> </ul>
</div><!--/.nav-collapse --> </div><!--/.nav-collapse -->
</div> </div>
</nav> </nav>

View file

@ -0,0 +1,26 @@
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 'active'
return ''

View file

@ -14,17 +14,19 @@ 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',
'bootstrap4',
'fa',
'accounts', 'accounts',
'admin',
'appsettings',
'computes', 'computes',
'console', 'console',
'create', 'create',
@ -35,7 +37,6 @@ INSTALLED_APPS = [
'nwfilters', 'nwfilters',
'storages', 'storages',
'secrets', 'secrets',
'appsettings',
'logs', 'logs',
] ]
@ -57,7 +58,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': [
@ -66,13 +69,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
@ -85,12 +90,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'
@ -154,3 +159,4 @@ LIBVIRT_KEEPALIVE_COUNT = 5
ALLOW_EMPTY_PASSWORD = True ALLOW_EMPTY_PASSWORD = True
NEW_USER_DEFAULT_INSTANCES = [] NEW_USER_DEFAULT_INSTANCES = []

View file

@ -7,14 +7,13 @@ from appsettings.views import appsettings
urlpatterns = [ urlpatterns = [
path('', index, name='index'), path('', index, name='index'),
path('admin/', include(('admin.urls', 'admin'), namespace='admin')),
path('instances/', include('instances.urls')), path('appsettings/', appsettings, name='appsettings'),
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('datasource/', include('datasource.urls')),
path('console/', console, name='console'), path('console/', console, name='console'),
path('appsettings/', appsettings, name='appsettings'), path('datasource/', include('datasource.urls')),
path('logs/', include('logs.urls')),
path('instances/', include('instances.urls')),
path('i18n/', include('django.conf.urls.i18n')), path('i18n/', include('django.conf.urls.i18n')),
# path('admin/', include(admin.site.urls)),
] ]