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

View file

@ -1,13 +1,14 @@
from django.shortcuts import render
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 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.models import *
from admin.decorators import superuser_only
from appsettings.models import AppSettings
from instances.models import Instance
import sass
import os
@ -19,7 +20,7 @@ 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 = AppSettings.objects.get(key="SHOW_PROFILE_EDIT_PASSWORD").value
@ -29,7 +30,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', '')
@ -39,11 +40,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 +70,7 @@ def profile(request):
return HttpResponseRedirect(request.get_full_path())
return render(request, 'profile.html', locals())
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())
@superuser_only
def account(request, user_id):
"""
:param request:
@ -157,9 +78,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)

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,24 +10,23 @@ from computes.models import Compute
from computes.forms import ComputeAddTcpForm, ComputeAddSshForm, ComputeEditHostForm, ComputeAddTlsForm, ComputeAddSocketForm
from vrtManager.hostdetails import wvmHostDetails
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):
"""
: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,
compute_data.append({
'id': compute.id,
'name': compute.name,
'hostname': compute.hostname,
'status': connection_manager.host_is_up(compute.type, compute.hostname),
@ -133,6 +132,7 @@ def computes(request):
return render(request, 'computes.html', locals())
@superuser_only
def overview(request, compute_id):
"""
:param request:
@ -140,18 +140,17 @@ 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)
status = 'true' if connection_manager.host_is_up(compute.type, compute.hostname) is True else 'false'
try:
conn = wvmHostDetails(compute.hostname,
conn = wvmHostDetails(
compute.hostname,
compute.login,
compute.password,
compute.type)
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()
@ -173,10 +172,12 @@ def compute_graph(request, compute_id):
"""
compute = get_object_or_404(Compute, pk=compute_id)
try:
conn = wvmHostDetails(compute.hostname,
conn = wvmHostDetails(
compute.hostname,
compute.login,
compute.password,
compute.type)
compute.type,
)
current_time = timezone.now().strftime("%H:%M:%S")
cpu_usage = conn.get_cpu_usage()
mem_usage = conn.get_memory_usage()
@ -186,9 +187,11 @@ def compute_graph(request, compute_id):
mem_usage = {'usage': 0}
current_time = 0
data = json.dumps({'cpudata': cpu_usage['usage'],
data = json.dumps({
'cpudata': cpu_usage['usage'],
'memdata': mem_usage,
'timeline': current_time})
'timeline': current_time,
})
response = HttpResponse()
response['Content-Type'] = "text/javascript"
response.write(data)
@ -207,10 +210,12 @@ 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,
conn = wvmConnect(
compute.hostname,
compute.login,
compute.password,
compute.type)
compute.type,
)
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()
try:
compute = get_object_or_404(Compute, pk=compute_id)
conn = wvmConnect(compute.hostname,
conn = wvmConnect(
compute.hostname,
compute.login,
compute.password,
compute.type)
compute.type,
)
data['machines'] = conn.get_machine_types(arch)
except libvirtError:
pass
@ -261,10 +268,12 @@ 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,
conn = wvmConnect(
compute.hostname,
compute.login,
compute.password,
compute.type)
compute.type,
)
data['videos'] = conn.get_video_models(arch, machine)
except libvirtError:
pass
@ -283,10 +292,12 @@ 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,
conn = wvmConnect(
compute.hostname,
compute.login,
compute.password,
compute.type)
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,4 +1,6 @@
Django==2.2.12
django-bootstrap4
django-fa==1.0.0
django-login-required-middleware==0.5.0
gunicorn==20.0.4
libsass

View file

@ -12,17 +12,17 @@ from instances.models import Instance
from vrtManager.create import wvmCreate
from vrtManager import util
from logs.views import addlogmsg
from admin.decorators import superuser_only
from webvirtcloud.settings import QEMU_CONSOLE_LISTEN_ADDRESSES
@superuser_only
def create_instance_select_type(request, compute_id):
"""
:param request:
:param compute_id:
:return:
"""
if not request.user.is_superuser:
return HttpResponseRedirect(reverse('index'))
conn = None
error_messages = list()
@ -34,10 +34,7 @@ def create_instance_select_type(request, compute_id):
appsettings = AppSettings.objects.all()
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)
@ -69,6 +66,7 @@ def create_instance_select_type(request, compute_id):
return render(request, 'create_instance_w1.html', locals())
@superuser_only
def create_instance(request, compute_id, arch, machine):
"""
:param request:
@ -77,8 +75,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()
@ -92,10 +88,7 @@ def create_instance(request, compute_id, arch, machine):
appsettings = AppSettings.objects.all()
try:
conn = wvmCreate(compute.hostname,
compute.login,
compute.password,
compute.type)
conn = wvmCreate(compute.hostname, compute.login, compute.password, compute.type)
default_firmware = appsettings.get(key="INSTANCE_FIRMWARE_DEFAULT_TYPE").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)
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:
@ -184,8 +174,14 @@ 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'], default_disk_format,
meta_prealloc, default_disk_owner_uid, default_disk_owner_gid)
path = conn.create_volume(
data['storage'],
data['name'],
data['hdd_size'],
default_disk_format,
meta_prealloc,
default_disk_owner_uid,
default_disk_owner_gid)
volume = dict()
volume['device'] = 'disk'
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_messages.append(error_msg)
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['path'] = clone_path
volume['type'] = conn.get_volume_type(clone_path)
@ -258,7 +260,8 @@ 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' "
messages.warning(
request, "Changing machine type from '%s' to 'q35' "
"which is required for UEFI secure boot." % machine)
machine = 'q35'
firmware["secure"] = 'yes'
@ -266,14 +269,23 @@ def create_instance(request, compute_id, arch, machine):
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,
volumes=volume_list,
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'],
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

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

View file

@ -1,8 +1,9 @@
import json
from django.shortcuts import render
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse
from appsettings.models import AppSettings
from admin.decorators import superuser_only
from instances.models import Instance
from logs.models import Logs
@ -18,27 +19,7 @@ def addlogmsg(user, instance, message):
add_log_msg.save()
def showlogs(request, page=1):
"""
: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())
@superuser_only
def vm_logs(request, vname):
"""
:param request:
@ -46,9 +27,6 @@ def vm_logs(request, 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')
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.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 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 vrtManager.network import network_size, wvmNetwork, wvmNetworks
@superuser_only
def networks(request, compute_id):
"""
:param request:
:param 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,
conn = wvmNetworks(
compute.hostname,
compute.login,
compute.password,
compute.type)
compute.type,
)
networks = conn.get_networks_info()
dhcp4 = netmask4 = gateway4 = ''
dhcp6 = prefix6 = gateway6 = ''
@ -51,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'],
conn.create_network(
data['name'],
data['forward'],
ipv4, gateway4, netmask4, dhcp4,
ipv6, gateway6, prefix6, dhcp6,
data['bridge_name'], data['openvswitch'], data['fixed'])
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():
@ -67,6 +79,7 @@ def networks(request, compute_id):
return render(request, 'networks.html', locals())
@superuser_only
def network(request, compute_id, pool):
"""
:param request:
@ -75,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,
conn = wvmNetwork(
compute.hostname,
compute.login,
compute.password,
compute.type,
pool)
pool,
)
networks = conn.get_networks()
state = conn.is_active()
device = conn.get_bridge_device()
@ -186,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())
@ -200,7 +211,9 @@ def network(request, compute_id, pool):
try:
conn.set_qos(qos_dir, average, peak, burst)
if conn.is_active():
messages.success(request, _(f"{qos_dir.capitalize()} QoS is set. Network XML is changed.") +
messages.success(
request,
_(f"{qos_dir.capitalize()} QoS is set. Network XML is changed.") +
_("Stop and start network to activate new config"))
else:
messages.success(request, _("{} QoS is set").format(qos_dir.capitalize()))
@ -212,7 +225,9 @@ def network(request, compute_id, pool):
conn.unset_qos(qos_dir)
if conn.is_active():
messages.success(request, _(f"{qos_dir.capitalize()} QoS is deleted. Network XML is changed. ") +
messages.success(
request,
_(f"{qos_dir.capitalize()} QoS is deleted. Network XML is changed. ") +
_("Stop and start network to activate new config."))
else:
messages.success(request, _(f"{qos_dir.capitalize()} QoS is deleted"))

View file

@ -1,15 +1,18 @@
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.utils.translation import ugettext_lazy as _
from libvirt import libvirtError
from admin.decorators import superuser_only
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 vrtManager import util
from vrtManager.instance import wvmInstance, wvmInstances
from vrtManager.nwfilters import wvmNWFilter, wvmNWFilters
@superuser_only
def nwfilters(request, compute_id):
"""
:param request:
@ -17,18 +20,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:
@ -102,9 +99,11 @@ 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,
return render(request, 'nwfilters.html', {
'error_messages': error_messages,
'nwfilters': nwfilters_all,
'compute': compute})
'compute': compute
})
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)
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

@ -9,8 +9,8 @@
<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>
<h5 class="modal-title">{% trans "Create New Secret" %}</h5>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
</div>
<div class="modal-body">
<form method="post" action="" role="form" aria-label="Create Secret form">{% csrf_token %}
@ -36,16 +36,17 @@
<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>
{% for key, usage_type in secret_usage_types.items %}
<option value="{{ usage_type }}">{{ usage_type }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<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\. ]+"/>
<input type="text" name="data" class="form-control" value="" required
pattern="[a-z0-9\. ]+" />
</div>
</div>
</div>

View file

@ -47,7 +47,8 @@
<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" %}
<i class="fa fa-exclamation-triangle"></i> <strong>{% trans "Warning" %}:</strong>
{% trans "Hypervisor doesn't have any Secrets" %}
</div>
</div>
{% else %}
@ -57,33 +58,27 @@
<thead>
<tr class="active">
<th scope="col">{% trans "UUID" %}</th>
<th scope="col">{% trans "Type" %}</th>
<th scope="col">{% trans "Usage" %}</th>
<th scope="col" data-sortable="false" style="width:90px;">{% trans "Action" %}</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>{% ifequal secret.usageType 0 %}
{% trans "volume" %}
{% endifequal %}
{% ifequal secret.usageType 1 %}
{% trans "iscsi" %}
{% endifequal %}
{% ifequal secret.usageType 2 %}
{% trans "ceph" %}
{% endifequal %}
</td>
<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" %}">
<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?" %}')">
<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>
@ -95,21 +90,27 @@
<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>
<button type="button" class="close" data-dismiss="modal"
aria-hidden="true">&times;</button>
</div>
<div class="modal-body">
<form method="post" role="form" aria-label="Edit secret form">{% csrf_token %}
<form method="post" role="form" aria-label="Edit secret form">
{% csrf_token %}
<div class="form-group row">
<label class="col-sm-3 col-form-label">{% trans "Value" %}</label>
<input type="hidden" name="uuid" value="{{ secret.uuid }}">
<div class="col-sm-6">
<input type="text" name="value" class="form-control" value="{{ secret.value }}" maxlength="45" required pattern="[a-zA-Z0-9]$+">
<input type="text" name="value" class="form-control"
value="{{ secret.value }}" maxlength="45" required
pattern="[a-zA-Z0-9]$+">
</div>
</div>
</div> <!-- /.modal-content -->
<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="set_value">{% trans "Set" %}</button>
<button type="button" class="btn btn-secondary"
data-dismiss="modal">{% trans "Close" %}</button>
<button type="submit" class="btn btn-primary"
name="set_value">{% trans "Set" %}</button>
</div>
</form>
</div> <!-- /.tab-content -->
@ -126,5 +127,5 @@
</div>
{% endblock %}
{% block script %}
<script src="{% static "js/sortable.min.js" %}"></script>
<script src="{% static 'js/sortable.min.js' %}"></script>
{% 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.shortcuts import get_object_or_404, render
from django.urls import reverse
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 secrets.forms import AddSecret
from vrtManager.secrets import wvmSecrets
@superuser_only
def secrets(request, compute_id):
"""
:param request:
@ -14,36 +24,44 @@ 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)
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:
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:
secrt = conn.get_secret(uuid)
try:
secret_value = conn.get_secret_value(uuid)
secrt_value = conn.get_secret_value(uuid)
except libvirtError as lib_err:
secret_value = None
secrets_all.append({'usage': secrt.usageID(),
secrets_all.append({
'usage': secrt.usageID(),
'uuid': secrt.UUIDString(),
'usageType': secrt.usageType(),
'value': secret_value
'usageType': secret_usage_types[secrt.usageType()],
'value': secrt_value
})
if request.method == 'POST':
if 'create' in request.POST:
form = AddSecret(request.POST)
if form.is_valid():
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())
else:
for msg_err in form.errors.values():
@ -55,7 +73,10 @@ def secrets(request, compute_id):
if 'set_value' in request.POST:
uuid = request.POST.get('uuid', '')
value = request.POST.get('value', '')
try:
conn.set_secret_value(uuid, value)
except Exception as err:
error_messages.append(err)
return HttpResponseRedirect(request.get_full_path())
except libvirtError as err:
error_messages.append(err)

View file

@ -155,3 +155,17 @@ p {
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
from django.shortcuts import render, get_object_or_404
from django.contrib import messages
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.urls import reverse
from django.contrib import messages
from libvirt import libvirtError
from admin.decorators import superuser_only
from computes.models import Compute
from appsettings.models import AppSettings
from storages.forms import AddStgPool, AddImage, CloneImage
from vrtManager.storage import wvmStorage, wvmStorages
@superuser_only
def storages(request, compute_id):
"""
:param request:
:param 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()
@ -48,12 +46,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'])
@ -68,6 +64,7 @@ def storages(request, compute_id):
return render(request, 'storages.html', locals())
@superuser_only
def storage(request, compute_id, pool):
"""
:param request:
@ -75,10 +72,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+')
@ -91,11 +84,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()
@ -226,11 +215,7 @@ 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 "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 tags_active %}
{% load font_awesome %}
{% load common_tags %}
<!-- Fixed navbar -->
<nav class="navbar navbar-expand-md navbar-dark bg-primary mb-3" aria-label="Main top navbar">
<div class="container">
@ -10,26 +11,27 @@
<div id="navbar" class="collapse navbar-collapse">
<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>
</li>
{% if request.user.is_superuser %}
<li class="nav-item {% class_active request "^/compute" %}
{% class_active request "^/create" %}">
<li class="nav-item {% class_active request '^/computes' %}">
<a class="nav-link" href="{% url 'computes' %}"><i class="fa fa-fw fa-server"></i> {% trans "Computes" %}</a>
</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 %}
</ul>
<ul class="navbar-nav navbar-right">
<ul class="navbar-nav navbar-right mt-2 mt-md-0">
{% if request.user.is_superuser %}
<li class="nav-item">
<a class="nav-link" href="{% url 'appsettings' %}"><i class="fa fa-wrench"></i></a>
<li class="nav-item dropdown {% app_active request 'admin' %}">
<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>
{% endif %}
<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>
</div>
</li>
</ul>
</div><!--/.nav-collapse -->
</div>
</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 = ['*']
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'bootstrap4',
'fa',
'accounts',
'admin',
'appsettings',
'computes',
'console',
'create',
@ -35,7 +37,6 @@ INSTALLED_APPS = [
'nwfilters',
'storages',
'secrets',
'appsettings',
'logs',
]
@ -57,7 +58,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': [
@ -66,13 +69,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
@ -85,12 +90,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'
@ -154,3 +159,4 @@ LIBVIRT_KEEPALIVE_COUNT = 5
ALLOW_EMPTY_PASSWORD = True
NEW_USER_DEFAULT_INSTANCES = []

View file

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