1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2024-10-31 19:44:16 +00:00

Add all settings to appsettings page & Convert settings choices

This commit is contained in:
catborise 2020-05-29 00:43:26 +03:00 committed by catborise
parent 62f8ece0ef
commit 7538ddab4f
27 changed files with 454 additions and 447 deletions

View file

@ -9,8 +9,7 @@ class UserAddForm(forms.Form):
name = forms.CharField(label="Name",
error_messages={'required': _('No User name has been entered')},
max_length=20)
password = forms.CharField(required=not settings.ALLOW_EMPTY_PASSWORD,
error_messages={'required': _('No password has been entered')},)
password = forms.CharField(required=not settings.ALLOW_EMPTY_PASSWORD, error_messages={'required': _('No password has been entered')},)
def clean_name(self):
name = self.cleaned_data['name']

View file

@ -0,0 +1,24 @@
# Generated by Django 2.2.12 on 2020-06-04 09:30
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0002_permissionset'),
]
operations = [
migrations.AlterField(
model_name='userattributes',
name='max_cpus',
field=models.IntegerField(default=2, help_text='-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)]),
),
migrations.AlterField(
model_name='userattributes',
name='max_instances',
field=models.IntegerField(default=2, help_text='-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)]),
),
]

View file

@ -1,4 +1,3 @@
from django.conf import settings
from django.contrib.auth.models import User
from django.core.validators import MinValueValidator
from django.db import models

View file

@ -68,8 +68,8 @@
<div class="modal-content">
<form method="post" role="form" aria-label="Edit user form">{% csrf_token %}
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h5 class="modal-title">{% trans "Edit user info" %}</h5>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
</div>
<div class="modal-body">
<div class="form-group row">
@ -133,11 +133,11 @@
{% trans "Delete" %}
</button>
{% if user.is_active %}
<button type="submit" class="float-left btn btn-warning" name="block">
<button type="submit" class="btn btn-warning mr-auto" name="block">
{% trans "Block" %}
</button>
{% else %}
<button type="submit" class="float-left btn btn-success" name="unblock">
<button type="submit" class="btn btn-success mr-auto" name="unblock">
{% trans "Unblock" %}
</button>
{% endif %}

View file

@ -1,4 +1,4 @@
from django.urls import path, re_path
from django.urls import path
from django.contrib.auth import views as auth_views
from . import views

View file

@ -4,10 +4,10 @@ from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from accounts.forms import UserAddForm
from accounts.models import *
from admin.decorators import superuser_only
from appsettings.models import AppSettings
from instances.models import Instance
import sass
@ -22,7 +22,6 @@ def profile(request):
error_messages = []
# user = User.objects.get(id=request.user.id)
publickeys = UserSSHKey.objects.filter(user_id=request.user.id)
themes_list = os.listdir(settings.SCSS_DIR +"/wvc-theme")
if request.method == 'POST':
@ -69,20 +68,6 @@ def profile(request):
delkeypublic = UserSSHKey.objects.get(id=keyid)
delkeypublic.delete()
return HttpResponseRedirect(request.get_full_path())
if 'change_theme' in request.POST:
theme = request.POST.get('theme_select', '')
scss_var = f"@import '{settings.SCSS_DIR}/wvc-theme/{theme}/variables';"
scss_bootswatch = f"@import '{settings.SCSS_DIR}/wvc-theme/{theme}/bootswatch';"
scss_boot = f"@import '{settings.SCSS_DIR}/bootstrap-overrides.scss';"
with open(settings.SCSS_DIR + "/wvc-main.scss", "w") as main:
main.write(scss_var + "\n" + scss_boot + "\n" + scss_bootswatch)
css_compressed = sass.compile(string=scss_var + "\n"+ scss_boot + "\n" + scss_bootswatch, output_style='compressed')
with open("static/" + "css/wvc-main.min.css", "w") as css:
css.write(css_compressed)
return HttpResponseRedirect(request.get_full_path())
return render(request, 'profile.html', locals())
@ -120,7 +105,7 @@ def account(request, user_id):
if 'add' in request.POST:
inst_id = request.POST.get('inst_id', '')
if settings.ALLOW_INSTANCE_MULTIPLE_OWNER:
if AppSettings.objects.get(key="ALLOW_INSTANCE_MULTIPLE_OWNER").value == 'True':
check_inst = UserInstance.objects.filter(instance_id=int(inst_id), user_id=int(user_id))
else:
check_inst = UserInstance.objects.filter(instance_id=int(inst_id))

View file

@ -1,4 +1,4 @@
# Generated by Django 2.2.12 on 2020-05-23 15:53
# Generated by Django 2.2.12 on 2020-05-27 16:03
from django.db import migrations, models
@ -16,8 +16,9 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=25)),
('key', models.CharField(max_length=50, unique=True)),
('key', models.CharField(db_index=True, max_length=50, unique=True)),
('value', models.CharField(max_length=25)),
('choices', models.CharField(max_length=50)),
('description', models.CharField(max_length=100, null=True)),
],
),

View file

@ -1,80 +0,0 @@
# Generated by Django 2.2.12 on 2020-05-23 12:05
from django.db import migrations
def add_default_settings(apps, schema_editor):
setting = apps.get_model("appsettings", "AppSettings")
db_alias = schema_editor.connection.alias
setting.objects.using(db_alias).bulk_create([
setting(1, "Console Type", "QEMU_CONSOLE_DEFAULT_TYPE", "vnc", "Default console type"),
setting(2, "Libvirt K.alive Intrval", "LIBVIRT_KEEPALIVE_INTERVAL", "5", "libvirt keepalive interval"),
setting(3, "Libvirt K.alive Count", "LIBVIRT_KEEPALIVE_COUNT", "5", "libvirt keepalive count"),
setting(4, "Multiple Owner for VM ", "ALLOW_INSTANCE_MULTIPLE_OWNER", "True"),
setting(5, "VM Clone Name Prefix", "CLONE_INSTANCE_DEFAULT_PREFIX", "instance"),
setting(6, "VM Clone Auto Name", "CLONE_INSTANCE_AUTO_NAME", "False"),
setting(7, "VM Clone Auto Migrate", "CLONE_INSTANCE_AUTO_MIGRATE", "False"),
setting(8, "Logs per Page", "LOGS_PER_PAGE", "100"),
setting(9, "Quota Debug", "QUOTA_DEBUG", "True"),
setting(10, "Empty Password", "ALLOW_EMPTY_PASSWORD", "True"),
setting(11, "Account View Style", "VIEW_ACCOUNTS_STYLE", "grid", "available: default (grid), list"),
setting(12, "Instance List View", "VIEW_INSTANCES_LIST_STYLE", "grouped", "available instance list style: default (grouped), nongrouped"),
setting(13, "Show Bottom Bar", "VIEW_INSTANCE_DETAIL_BOTTOM_BAR", "True", "available options: True, False"),
setting(14, "Disk Format", "INSTANCE_VOLUME_DEFAULT_FORMAT", "qcow2", "available volume format: raw, qcow2, qcow"),
setting(15, "Disk Bus", "INSTANCE_VOLUME_DEFAULT_BUS", "virtio", "available bus types: virtio, scsi, ide, usb, sata"),
setting(16, "Disk SCSI Controller", "INSTANCE_VOLUME_DEFAULT_SCSI_CONTROLLER", "virtio-scsi", "SCSI Types: virtio-scsi, lsilogic"),
setting(17, "Disk Cache", "INSTANCE_VOLUME_DEFAULT_CACHE", "directsync", " Volume cache: default, directsync, none, unsafe, writeback, writethrough"),
setting(18, "Disk IO Type", "INSTANCE_VOLUME_DEFAULT_IO", "default", "Volume io mode: default, native, threads"),
setting(19, "Disk Detect Zeroes", "INSTANCE_VOLUME_DEFAULT_DETECT_ZEROES", "default", "Volume detect zeroes mode: default, on, off, unmap"),
setting(20, "Disk Discard", "INSTANCE_VOLUME_DEFAULT_DISCARD", "default", "Volume discard mode: default, unmap, ignore"),
setting(21, "Disk Owner UID", "INSTANCE_VOLUME_DEFAULT_OWNER_UID", "0", "up to os, 0=root, 107=qemu or libvirt-bin(for ubuntu)"),
setting(22, "Disk Owner GID", "INSTANCE_VOLUME_DEFAULT_OWNER_GID", "0", "up to os, 0=root, 107=qemu or libvirt-bin(for ubuntu)"),
setting(23, "CPU Mode", "INSTANCE_CPU_DEFAULT_MODE", "host-model", "Cpu modes: no-model, host-model, host-passthrough, custom"),
setting(24, "Machine Type", "INSTANCE_MACHINE_DEFAULT_TYPE", "q35", "Chipset/Machine: pc or q35 for x86_64"),
setting(25, "Firmware Type", "INSTANCE_FIRMWARE_DEFAULT_TYPE", "BIOS", "Firmware: BIOS or UEFI for x86_64"),
setting(26, "Architecture Type", "INSTANCE_ARCH_DEFAULT_TYPE", "x86_64", "Architecture: x86_64, i686, etc"),
setting(27, "Theme", "BOOTSTRAP_THEME", "", "Bootstrap CSS & Bootswatch"),
setting(28, "Sass Path", "SASS_DIR", "", "Bootstrap SASS & Bootswatch SASS "),
])
def del_default_settings(apps, schema_editor):
setting = apps.get_model("appsettings", "AppSettings")
db_alias = schema_editor.connection.alias
setting.objects.using(db_alias).filter(id=1, key="QEMU_CONSOLE_DEFAULT_TYPE").delete()
setting.objects.using(db_alias).filter(id=2, key="LIBVIRT_KEEPALIVE_INTERVAL").delete()
setting.objects.using(db_alias).filter(id=3, key="LIBVIRT_KEEPALIVE_COUNT").delete()
setting.objects.using(db_alias).filter(id=4, key="ALLOW_INSTANCE_MULTIPLE_OWNER").delete()
setting.objects.using(db_alias).filter(id=5, key="CLONE_INSTANCE_DEFAULT_PREFIX").delete()
setting.objects.using(db_alias).filter(id=6, key="CLONE_INSTANCE_AUTO_NAME").delete()
setting.objects.using(db_alias).filter(id=7, key="CLONE_INSTANCE_AUTO_MIGRATE").delete()
setting.objects.using(db_alias).filter(id=8, key="LOGS_PER_PAGE").delete()
setting.objects.using(db_alias).filter(id=9, key="QUOTA_DEBUG").delete()
setting.objects.using(db_alias).filter(id=10, key="ALLOW_EMPTY_PASSWORD").delete()
setting.objects.using(db_alias).filter(id=11, key="VIEW_ACCOUNTS_STYLE").delete()
setting.objects.using(db_alias).filter(id=12, key="VIEW_INSTANCES_LIST_STYLE").delete()
setting.objects.using(db_alias).filter(id=13, key="VIEW_INSTANCE_DETAIL_BOTTOM_BAR").delete()
setting.objects.using(db_alias).filter(id=14, key="INSTANCE_VOLUME_DEFAULT_FORMAT").delete()
setting.objects.using(db_alias).filter(id=15, key="INSTANCE_VOLUME_DEFAULT_BUS").delete()
setting.objects.using(db_alias).filter(id=16, key="INSTANCE_VOLUME_DEFAULT_SCSI_CONTROLLER").delete()
setting.objects.using(db_alias).filter(id=17, key="INSTANCE_VOLUME_DEFAULT_CACHE").delete()
setting.objects.using(db_alias).filter(id=18, key="INSTANCE_VOLUME_DEFAULT_IO").delete()
setting.objects.using(db_alias).filter(id=19, key="INSTANCE_VOLUME_DEFAULT_DETECT_ZEROES").delete()
setting.objects.using(db_alias).filter(id=20, key="INSTANCE_VOLUME_DEFAULT_DISCARD").delete()
setting.objects.using(db_alias).filter(id=21, key="INSTANCE_VOLUME_DEFAULT_OWNER_UID").delete()
setting.objects.using(db_alias).filter(id=22, key="INSTANCE_VOLUME_DEFAULT_OWNER_GID").delete()
setting.objects.using(db_alias).filter(id=23, key="INSTANCE_CPU_DEFAULT_MODE").delete()
setting.objects.using(db_alias).filter(id=24, key="INSTANCE_MACHINE_DEFAULT_TYPE").delete()
setting.objects.using(db_alias).filter(id=25, key="INSTANCE_FIRMWARE_DEFAULT_TYPE").delete()
setting.objects.using(db_alias).filter(id=26, key="INSTANCE_ARCH_DEFAULT_TYPE").delete()
setting.objects.using(db_alias).filter(id=27, key="BOOTSTRAP_THEME").delete()
setting.objects.using(db_alias).filter(id=28, key="SASS_DIR").delete()
class Migration(migrations.Migration):
dependencies = [
('appsettings', '0001_initial'),
]
operations = [
migrations.RunPython(add_default_settings, del_default_settings),
]

View file

@ -0,0 +1,82 @@
# Generated by Django 2.2.12 on 2020-05-23 12:05
from django.db import migrations
def add_default_settings(apps, schema_editor):
setting = apps.get_model("appsettings", "AppSettings")
db_alias = schema_editor.connection.alias
setting.objects.using(db_alias).bulk_create([
setting(1, "Theme", "BOOTSTRAP_THEME", "flaty", "", "Bootstrap CSS & Bootswatch Theme"),
setting(2, "Theme SASS Path", "SASS_DIR", "dev/scss/", "", "Bootstrap SASS & Bootswatch SASS Directory"),
setting(3, "All Instances View Style", "VIEW_INSTANCES_LIST_STYLE", "grouped", "grouped,nongrouped", "All instances list style"),
setting(4, "Account View Style", "VIEW_ACCOUNTS_STYLE", "grid", "grid,list", "User accounts view style"),
setting(5, "Logs per Page", "LOGS_PER_PAGE", "100", "", "Pagination for logs"),
setting(6, "Multiple Owner for VM ", "ALLOW_INSTANCE_MULTIPLE_OWNER", "True", "True,False", "Allow to have multiple owner for instance"),
setting(7, "Quota Debug", "QUOTA_DEBUG", "True", "True,False", "Debug for user quotas"),
setting(8, "Empty Password", "ALLOW_EMPTY_PASSWORD", "True", "True,False", "Allow empty password"),
setting(9, "Disk Format", "INSTANCE_VOLUME_DEFAULT_FORMAT", "qcow2", "raw,qcow,qcow2", "Instance disk format"),
setting(10, "Disk Bus", "INSTANCE_VOLUME_DEFAULT_BUS", "virtio", "virtio,scsi,ide,usb,sata", "Instance disk bus type"),
setting(11, "Disk SCSI Controller", "INSTANCE_VOLUME_DEFAULT_SCSI_CONTROLLER", "virtio-scsi", "virtio-scsi, lsilogic, virtio-blk", "SCSI controller type"),
setting(12, "Disk Cache", "INSTANCE_VOLUME_DEFAULT_CACHE", "directsync", "default,directsync,none,unsafe,writeback,writethrough", "Disk volume cache type"),
setting(13, "Disk IO Type", "INSTANCE_VOLUME_DEFAULT_IO", "default", "default,native,threads", "Volume io modes"),
setting(14, "Disk Detect Zeroes", "INSTANCE_VOLUME_DEFAULT_DETECT_ZEROES", "default", "default,on,off,unmap", "Volume detect zeroes mode"),
setting(15, "Disk Discard", "INSTANCE_VOLUME_DEFAULT_DISCARD", "default", "default,unmap,ignore", "Volume discard mode"),
setting(16, "Disk Owner UID", "INSTANCE_VOLUME_DEFAULT_OWNER_UID", "0", "", "Owner UID: up to os, 0=root, 107=qemu or libvirt-bin(for ubuntu)"),
setting(17, "Disk Owner GID", "INSTANCE_VOLUME_DEFAULT_OWNER_GID", "0", "", "Owner GID: up to os, 0=root, 107=qemu or libvirt-bin(for ubuntu)"),
setting(18, "VM CPU Mode", "INSTANCE_CPU_DEFAULT_MODE", "host-model", "no-model,host-model,host-passthrough,custom", "Cpu modes"),
setting(19, "VM Machine Type", "INSTANCE_MACHINE_DEFAULT_TYPE", "q35", "q35,x86_64", "Chipset/Machine type"),
setting(20, "VM Firmware Type", "INSTANCE_FIRMWARE_DEFAULT_TYPE", "BIOS", "BIOS,UEFI", "Firmware type for x86_64"),
setting(21, "VM Architecture Type", "INSTANCE_ARCH_DEFAULT_TYPE", "x86_64", "x86_64,i686", "Architecture type: x86_64, i686, etc"),
setting(22, "VM Console Type", "QEMU_CONSOLE_DEFAULT_TYPE", "vnc", "vnc,spice", "Default console type"),
setting(23, "VM Clone Name Prefix", "CLONE_INSTANCE_DEFAULT_PREFIX", "instance", "True,False", "Prefix for cloned instance name"),
setting(24, "VM Clone Auto Name", "CLONE_INSTANCE_AUTO_NAME", "False", "True,False", "Generated name for cloned instance"),
setting(25, "VM Clone Auto Migrate", "CLONE_INSTANCE_AUTO_MIGRATE", "False", "True,False", "Auto migrate instance after clone"),
setting(26, "VM Bottom Bar", "VIEW_INSTANCE_DETAIL_BOTTOM_BAR", "True", "True,False", "Bottom navbar for instance details"),
setting(27, "Show Access Root Pass", "SHOW_ACCESS_ROOT_PASSWORD", "False", "True,False", "Show access root password"),
setting(28, "Show Access SSH Keys", "SHOW_ACCESS_SSH_KEYS", "False", "True,False", "Show access ssh keys"),
setting(29, "Show Profile Edit Pass", "SHOW_PROFILE_EDIT_PASSWORD", "False", "True,False", "Show profile edit password"),
])
def del_default_settings(apps, schema_editor):
setting = apps.get_model("appsettings", "AppSettings")
db_alias = schema_editor.connection.alias
setting.objects.using(db_alias).filter(key="QEMU_CONSOLE_DEFAULT_TYPE").delete()
setting.objects.using(db_alias).filter(key="ALLOW_INSTANCE_MULTIPLE_OWNER").delete()
setting.objects.using(db_alias).filter(key="CLONE_INSTANCE_DEFAULT_PREFIX").delete()
setting.objects.using(db_alias).filter(key="CLONE_INSTANCE_AUTO_NAME").delete()
setting.objects.using(db_alias).filter(key="CLONE_INSTANCE_AUTO_MIGRATE").delete()
setting.objects.using(db_alias).filter(key="LOGS_PER_PAGE").delete()
setting.objects.using(db_alias).filter(key="QUOTA_DEBUG").delete()
setting.objects.using(db_alias).filter(key="ALLOW_EMPTY_PASSWORD").delete()
setting.objects.using(db_alias).filter(key="VIEW_ACCOUNTS_STYLE").delete()
setting.objects.using(db_alias).filter(key="VIEW_INSTANCES_LIST_STYLE").delete()
setting.objects.using(db_alias).filter(key="VIEW_INSTANCE_DETAIL_BOTTOM_BAR").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_VOLUME_DEFAULT_FORMAT").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_VOLUME_DEFAULT_BUS").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_VOLUME_DEFAULT_SCSI_CONTROLLER").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_VOLUME_DEFAULT_CACHE").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_VOLUME_DEFAULT_IO").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_VOLUME_DEFAULT_DETECT_ZEROES").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_VOLUME_DEFAULT_DISCARD").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_VOLUME_DEFAULT_OWNER_UID").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_VOLUME_DEFAULT_OWNER_GID").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_CPU_DEFAULT_MODE").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_MACHINE_DEFAULT_TYPE").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_FIRMWARE_DEFAULT_TYPE").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_ARCH_DEFAULT_TYPE").delete()
setting.objects.using(db_alias).filter(key="BOOTSTRAP_THEME").delete()
setting.objects.using(db_alias).filter(key="SASS_DIR").delete()
setting.objects.using(db_alias).filter(key="SHOW_ACCESS_ROOT_PASSWORD").delete()
setting.objects.using(db_alias).filter(key="SHOW_ACCESS_SSH_KEYS").delete()
setting.objects.using(db_alias).filter(key="SHOW_PROFILE_EDIT_PASSWORD").delete()
class Migration(migrations.Migration):
dependencies = [
('appsettings', '0001_initial'),
]
operations = [
migrations.RunPython(add_default_settings, del_default_settings),
]

View file

@ -2,7 +2,12 @@ from django.db import models
class AppSettings(models.Model):
def choices_as_list(self):
return self.choices.split(',')
name = models.CharField(max_length=25, null=False)
key = models.CharField(max_length=50, unique=True)
key = models.CharField(db_index=True, max_length=50, unique=True)
value = models.CharField(max_length=25)
choices = models.CharField(max_length=50)
description = models.CharField(max_length=100, null=True)

View file

@ -57,17 +57,25 @@
</div>
</form>
{% endif %}
<form method="post" action="" role="form" aria-label="Edit bottom bar settings form">{% csrf_token %}
<h3 class="page-header">{% trans "Other Settings" %}</h3>
{% for setting in appsettings %}
<form method="post" action="" role="form" aria-label="{{setting.name}} form">{% csrf_token %}
<div class="form-group row">
<label class="col-sm-3 col-form-label">{{ show_inst_bottom_bar.name }}</label>
<label class="col-sm-3 col-form-label">{{ setting.name }}</label>
<div class="col-sm-6">
<select class="form-control" name="{{ show_inst_bottom_bar.key }}" onchange="this.form.submit()">
<option {% if show_inst_bottom_bar.value == 'True' %} selected {% endif %} value=True>{% trans "True" %}</option>
<option {% if show_inst_bottom_bar.value == 'False' %} selected {% endif %} value=False>{% trans "False" %}</option>
{% if setting.choices %}
<select class="form-control" name="{{ setting.key }}" onchange="this.form.submit()" title="{{ setting.description }}">
{% for choice in setting.choices_as_list %}
<option {% if setting.value == choice %} selected {% endif %} value={{ choice }}>{% trans choice %}</option>
{% endfor %}
</select>
{% else %}
<input class="form-control" name="{{ setting.key }}" value="{{ setting.value }}" title="{{ setting.description }}" onchange="this.form.submit()"/>
{% endif%}
</div>
</div>
</form>
{% endfor %}
</div>
</div>
{% endblock %}

View file

@ -7,7 +7,6 @@ from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.conf import settings
from appsettings.models import AppSettings
from logs.views import addlogmsg
@ -22,14 +21,33 @@ def appsettings(request):
"""
error_messages = []
show_inst_bottom_bar = AppSettings.objects.get(key="VIEW_INSTANCE_DETAIL_BOTTOM_BAR")
bootstrap_theme = AppSettings.objects.get(key="BOOTSTRAP_THEME")
sass_dir = AppSettings.objects.get(key="SASS_DIR")
bootstrap_theme = AppSettings.objects.get(key="BOOTSTRAP_THEME")
try:
themes_list = os.listdir(sass_dir.value + "/wvc-theme")
except FileNotFoundError as err:
error_messages.append(err)
addlogmsg(request.user.username, "", err)
# Bootstrap settings related with filesystems, because of that they are excluded from other settings
appsettings = AppSettings.objects.exclude(description__startswith="Bootstrap").order_by("name")
if request.method == 'POST':
if 'SASS_DIR' in request.POST:
try:
sass_dir.value = request.POST.get("SASS_DIR", "")
sass_dir.save()
msg = _(f"SASS directory path is changed. Now: {sass_dir.value}")
messages.success(request, msg)
except Exception as err:
msg = err
error_messages.append(msg)
addlogmsg(request.user.username, "", msg)
return HttpResponseRedirect(request.get_full_path())
if 'BOOTSTRAP_THEME' in request.POST:
theme = request.POST.get("BOOTSTRAP_THEME", "")
scss_var = f"@import '{sass_dir.value}/wvc-theme/{theme}/variables';"
@ -56,26 +74,13 @@ def appsettings(request):
addlogmsg(request.user.username, "", msg)
return HttpResponseRedirect(request.get_full_path())
if 'SASS_DIR' in request.POST:
for setting in appsettings:
if setting.key in request.POST:
try:
sass_dir.value = request.POST.get("SASS_DIR", "")
sass_dir.save()
setting.value = request.POST.get(setting.key, "")
setting.save()
msg = _(f"SASS directory path is changed. Now: {sass_dir.value}")
messages.success(request, msg)
except Exception as err:
msg = err
error_messages.append(msg)
addlogmsg(request.user.username, "", msg)
return HttpResponseRedirect(request.get_full_path())
if 'VIEW_INSTANCE_DETAIL_BOTTOM_BAR' in request.POST:
try:
show_inst_bottom_bar.value = request.POST.get("VIEW_INSTANCE_DETAIL_BOTTOM_BAR", "")
show_inst_bottom_bar.save()
msg = _(f"Show bottom bar setting is changed. Now: {show_inst_bottom_bar.value}")
msg = _(f"{setting.name} is changed. Now: {setting.value}")
messages.success(request, msg)
except Exception as err:
msg = err

View file

@ -115,6 +115,7 @@
</div>
</div>
</div>
</div>
{% endblock %}
{% block script %}
<script src="{% static "js/Chart.bundle.min.js" %}"></script>

View file

@ -3,8 +3,7 @@ from django.shortcuts import render
from libvirt import libvirtError
from instances.models import Instance
from vrtManager.instance import wvmInstance
from webvirtcloud.settings import WS_PUBLIC_PORT
from webvirtcloud.settings import WS_PUBLIC_HOST
from webvirtcloud.settings import WS_PUBLIC_PORT, WS_PUBLIC_HOST
def console(request):

View file

@ -83,7 +83,7 @@
<div class="clearfix"></div>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="addFromXML">
<div class="well">
<div>
<form method="post" role="form" aria-label="Create instance with XML form">{% csrf_token %}
<div class="col-sm-12" id="xmlheight">
<input type="hidden" name="dom_xml"/>

View file

@ -199,7 +199,7 @@
<div class="col-sm-6">
<select id="cache_mode" name="cache_mode" class="custom-select">
{% for mode, name in cache_modes %}
<option value="{{ mode }}" {% ifequal mode default_cache %}selected {% endifequal %}>
<option value="{{ mode }}" {% if mode == default_cache %}selected {% endif %}>
{% trans name %}</option>
{% endfor %}
</select>
@ -303,7 +303,7 @@
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="addCustom">
<div class="well">
<div>
<form method="post" role="form" aria-label="Create custom instance form">{% csrf_token %}
<div class="form-group row">
<label class="col-sm-3 col-form-label">{% trans "Name" %}</label>
@ -423,7 +423,7 @@
<div class="col-sm-7">
<select id="cache_mode" name="cache_mode" class="form-control">
{% for mode, name in cache_modes %}
<option value="{{ mode }}" {% ifequal mode default_cache %}selected {% endifequal %}>
<option value="{{ mode }}" {% if mode == default_cache %}selected {% endif %}>
{% trans name %}</option>
{% endfor %}
</select>
@ -516,7 +516,7 @@
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="addFromTemp">
<div class="well">
<div>
<form method="post" role="form" aria-label="Create instance from template form">{% csrf_token %}
<div class="form-group row">
<label class="col-sm-3 col-form-label">{% trans "Name" %}</label>
@ -636,7 +636,7 @@
<div class="col-sm-7">
<select id="cache_mode" name="cache_mode" class="custom-select">
{% for mode, name in cache_modes %}
<option value="{{ mode }}" {% ifequal mode default_cache %}selected{% endifequal %}>
<option value="{{ mode }}" {% if mode == default_cache %}selected{% endif %}>
{% trans name %}</option>
{% endfor %}
</select>
@ -788,8 +788,7 @@
'{% endfor %}' +
'</select>' +
' -> ' + value + ' ' +
'<a class="btn-link float-right" onclick="javascript:$(\'#image-control\').multiselect(\'deselect\', \'' + value + '\', true)"><i class="fa fa-remove"></i></a>' +
'</li>';
'<a class="btn-link float-right" onclick="javascript:$(\'#image-control\').multiselect(\'deselect\', \'' + value + '\', true)"><i class="fa fa-remove"></i></a></li>';
selected_list_html += li;
counter++;
});
@ -797,7 +796,6 @@
$('#disk_list_div').addClass('d-none');
}
$('#img-list').html(selected_list_html);
}
});
@ -822,7 +820,7 @@
$.each(input_value.split(','), function (index, value) {
let li = '<li>eth' + counter +
' -> ' + value + ' ' +
'<a class="btn-link float-right" onclick="$(\'#network-control\').multiselect(\'deselect\', \'' + value + '\', true)"><i class="fa fa-remove"></i></a></a></li>';
'<a class="btn-link float-right" onclick="$(\'#network-control\').multiselect(\'deselect\', \'' + value + '\', true)"><i class="fa fa-remove"></i></a></li>';
selected_list_html += li;
counter++;
});

View file

@ -8,23 +8,12 @@ from admin.decorators import superuser_only
from computes.models import Compute
from create.models import Flavor
from create.forms import FlavorAddForm, NewVMForm
from appsettings.models import AppSettings
from instances.models import Instance
from vrtManager.create import wvmCreate
from vrtManager import util
from logs.views import addlogmsg
from webvirtcloud.settings import QEMU_CONSOLE_LISTEN_ADDRESSES
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_CACHE
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_BUS
from webvirtcloud.settings import INSTANCE_CPU_DEFAULT_MODE
from webvirtcloud.settings import INSTANCE_MACHINE_DEFAULT_TYPE
from webvirtcloud.settings import QEMU_CONSOLE_DEFAULT_TYPE
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_IO
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_DETECT_ZEROES
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_DISCARD
from webvirtcloud.settings import INSTANCE_ARCH_DEFAULT_TYPE
from webvirtcloud.settings import INSTANCE_FIRMWARE_DEFAULT_TYPE
@superuser_only
def create_instance_select_type(request, compute_id):
@ -40,6 +29,7 @@ def create_instance_select_type(request, compute_id):
hypervisors = list()
meta_prealloc = False
compute = get_object_or_404(Compute, pk=compute_id)
appsettings = AppSettings.objects.all()
try:
conn = wvmCreate(compute.hostname, compute.login, compute.password, compute.type)
@ -48,8 +38,8 @@ def create_instance_select_type(request, compute_id):
# Supported hypervisors by webvirtcloud: i686, x86_64(for now)
supported_arch = ["x86_64", "i686", "aarch64", "armv7l", "ppc64", "ppc64le", "s390x"]
hypervisors = [hpv for hpv in all_hypervisors.keys() if hpv in supported_arch]
default_machine = INSTANCE_MACHINE_DEFAULT_TYPE
default_arch = INSTANCE_ARCH_DEFAULT_TYPE
default_machine = appsettings.get(key="INSTANCE_MACHINE_DEFAULT_TYPE").value
default_arch = appsettings.get(key="INSTANCE_ARCH_DEFAULT_TYPE").value
if request.method == 'POST':
if 'create_xml' in request.POST:
@ -93,28 +83,33 @@ def create_instance(request, compute_id, arch, machine):
meta_prealloc = False
compute = get_object_or_404(Compute, pk=compute_id)
flavors = Flavor.objects.filter().order_by('id')
appsettings = AppSettings.objects.all()
try:
conn = wvmCreate(compute.hostname, compute.login, compute.password, compute.type)
default_firmware = INSTANCE_FIRMWARE_DEFAULT_TYPE
default_cpu_mode = INSTANCE_CPU_DEFAULT_MODE
default_firmware = appsettings.get(key="INSTANCE_FIRMWARE_DEFAULT_TYPE").value
default_cpu_mode = appsettings.get(key="INSTANCE_CPU_DEFAULT_MODE").value
instances = conn.get_instances()
videos = conn.get_video_models(arch, machine)
cache_modes = sorted(conn.get_cache_modes().items())
default_cache = INSTANCE_VOLUME_DEFAULT_CACHE.lower()
default_io = INSTANCE_VOLUME_DEFAULT_IO.lower()
default_zeroes = INSTANCE_VOLUME_DEFAULT_DETECT_ZEROES.lower()
default_discard = INSTANCE_VOLUME_DEFAULT_DISCARD.lower()
default_cache = appsettings.get(key="INSTANCE_VOLUME_DEFAULT_CACHE").value
default_io = appsettings.get(key="INSTANCE_VOLUME_DEFAULT_IO").value
default_zeroes = appsettings.get(key="INSTANCE_VOLUME_DEFAULT_DETECT_ZEROES").value
default_discard = appsettings.get(key="INSTANCE_VOLUME_DEFAULT_DISCARD").value
default_disk_format = appsettings.get(key="INSTANCE_VOLUME_DEFAULT_FORMAT").value
default_disk_owner_uid = int(appsettings.get(key="INSTANCE_VOLUME_DEFAULT_OWNER_UID").value)
default_disk_owner_gid = int(appsettings.get(key="INSTANCE_VOLUME_DEFAULT_OWNER_GID").value)
default_scsi_disk_model = appsettings.get(key="INSTANCE_VOLUME_DEFAULT_SCSI_CONTROLLER").value
listener_addr = QEMU_CONSOLE_LISTEN_ADDRESSES
mac_auto = util.randomMAC()
disk_devices = conn.get_disk_device_types(arch, machine)
disk_buses = conn.get_disk_bus_types(arch, machine)
default_bus = INSTANCE_VOLUME_DEFAULT_BUS
default_bus = appsettings.get(key="INSTANCE_VOLUME_DEFAULT_BUS").value
networks = sorted(conn.get_networks())
nwfilters = conn.get_nwfilters()
storages = sorted(conn.get_storages(only_actives=True))
default_graphics = QEMU_CONSOLE_DEFAULT_TYPE
default_graphics = appsettings.get(key="QEMU_CONSOLE_DEFAULT_TYPE").value
dom_caps = conn.get_dom_capabilities(arch, machine)
caps = conn.get_capabilities(arch)
@ -177,18 +172,29 @@ def create_instance(request, compute_id, arch, machine):
error_messages.append(error_msg)
else:
try:
path = conn.create_volume(data['storage'],
path = conn.create_volume(
data['storage'],
data['name'],
data['hdd_size'],
metadata=meta_prealloc)
default_disk_format,
meta_prealloc,
default_disk_owner_uid,
default_disk_owner_gid)
volume = dict()
volume['device'] = 'disk'
volume['path'] = path
volume['type'] = conn.get_volume_type(path)
volume['device'] = 'disk'
if data['virtio']:
volume['bus'] = INSTANCE_VOLUME_DEFAULT_BUS
volume['cache_mode'] = data['cache_mode']
volume['bus'] = default_bus
if volume['bus'] == 'scsi':
volume['scsi_model'] = default_scsi_disk_model
volume['discard_mode'] = default_discard
volume['detect_zeroes_mode'] = default_zeroes
volume['io_mode'] = default_io
volume_list.append(volume)
is_disk_created = True
except libvirtError as lib_err:
error_messages.append(lib_err)
elif data['template']:
@ -198,16 +204,25 @@ 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'],
clone_path = conn.clone_from_template(
data['name'],
templ_path,
data['storage'],
metadata=meta_prealloc)
meta_prealloc,
default_disk_owner_uid,
default_disk_owner_gid)
volume = dict()
volume['path'] = clone_path
volume['type'] = conn.get_volume_type(clone_path)
volume['device'] = 'disk'
if data['virtio']:
volume['bus'] = INSTANCE_VOLUME_DEFAULT_BUS
volume['cache_mode'] = data['cache_mode']
volume['bus'] = default_bus
if volume['bus'] == 'scsi':
volume['scsi_model'] = default_scsi_disk_model
volume['discard_mode'] = default_discard
volume['detect_zeroes_mode'] = default_zeroes
volume['io_mode'] = default_io
volume_list.append(volume)
is_disk_created = True
else:
@ -223,6 +238,13 @@ def create_instance(request, compute_id, arch, machine):
volume['type'] = conn.get_volume_type(path)
volume['device'] = request.POST.get('device' + str(idx), '')
volume['bus'] = request.POST.get('bus' + str(idx), '')
if volume['bus'] == 'scsi':
volume['scsi_model'] = default_scsi_disk_model
volume['cache_mode'] = data['cache_mode']
volume['discard_mode'] = default_discard
volume['detect_zeroes_mode'] = default_zeroes
volume['io_mode'] = default_io
volume_list.append(volume)
except libvirtError as lib_err:
error_messages.append(lib_err)
@ -253,11 +275,7 @@ def create_instance(request, compute_id, arch, machine):
arch=arch,
machine=machine,
firmware=firmware,
images=volume_list,
cache_mode=data['cache_mode'],
io_mode=default_io,
discard_mode=default_discard,
detect_zeroes_mode=default_zeroes,
volumes=volume_list,
networks=data['networks'],
virtio=data['virtio'],
listen_addr=data["listener_addr"],
@ -269,7 +287,7 @@ def create_instance(request, compute_id, arch, machine):
qemu_ga=data['qemu_ga'])
create_instance = Instance(compute_id=compute_id, name=data['name'], uuid=uuid)
create_instance.save()
msg = _("Instance is created.")
msg = _("Instance is created")
messages.success(request, msg)
addlogmsg(request.user.username, create_instance.name, msg)
return HttpResponseRedirect(reverse('instance', args=[compute_id, data['name']]))

View file

@ -1,12 +1,9 @@
from django.conf.urls import url
from django.urls import path
from . import views
urlpatterns = [
url(r'^openstack/$',
views.os_index, name='ds_openstack_index'),
url(r'^openstack/(?P<version>[\w\-\.]+)/meta_data.json$',
views.os_metadata_json, name='ds_openstack_metadata'),
url(r'^openstack/(?P<version>[\w\-\.]+)/user_data$',
views.os_userdata, name='ds_openstack_userdata'),
url(r'^vdi/(?P<compute_id>[0-9]+)/(?P<vname>[\w\-\.]+)/$', views.get_vdi_url, name='vdi_url'),
path('openstack/', views.os_index, name='ds_openstack_index'),
path('openstack/<version>/meta_data.json', views.os_metadata_json, name='ds_openstack_metadata'),
path('openstack/<version>/user_data', views.os_userdata, name='ds_openstack_userdata'),
path('vdi/<int:compute_id>/<vname>/', views.get_vdi_url, name='vdi_url'),
]

View file

@ -1,3 +1,3 @@
@import 'dev/scss/wvc-theme/flatly/variables';
@import 'dev/scss/bootstrap-overrides.scss';
@import 'dev/scss/wvc-theme/flatly/bootswatch';
@import 'dev/scss//wvc-theme/flatly/variables';
@import 'dev/scss//bootstrap-overrides.scss';
@import 'dev/scss//wvc-theme/flatly/bootswatch';

View file

@ -121,7 +121,7 @@
<div role="tabpanel">
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist" aria-label="Instance power actions">
{% ifequal status 1 %}
{% if status == 1 %}
<li class="nav-item">
<a class="nav-link text-secondary active" href="#poweroff" aria-controls="poweroff" role="tab" data-toggle="tab">
{% trans "Power Off" %}
@ -144,8 +144,8 @@
</a>
</li>
{% endif %}
{% endifequal %}
{% ifequal status 3 %}
{% endif %}
{% if status == 3 %}
{% if request.user.is_superuser %}
<li class="nav-item">
<a class="nav-link text-secondary" href="#resume" aria-controls="resume" role="tab" data-toggle="tab">
@ -158,18 +158,18 @@
</a>
</li>
{% endif %}
{% endifequal %}
{% ifequal status 5 %}
{% endif %}
{% if status == 5 %}
<li class="nav-item">
<a class="nav-link text-secondary active" href="#boot" aria-controls="boot" role="tab" data-toggle="tab">
{% trans "Power On" %}
</a>
</li>
{% endifequal %}
{% endif %}
</ul>
<!-- Tab panes -->
<div class="tab-content">
{% ifequal status 1 %}
{% if status == 1 %}
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="poweroff">
<p>{% trans "This action sends an ACPI shutdown signal to the instance." %}</p>
<form action="" method="post" role="form" aria-label0="Power off instance form">{% csrf_token %}
@ -200,8 +200,8 @@
</form>
</div>
{% endif %}
{% endifequal %}
{% ifequal status 3 %}
{% endif %}
{% if status == 3 %}
{% if request.user.is_superuser %}
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="resume">
<p>{% trans "This action restore the instance after suspend." %}</p>
@ -226,8 +226,8 @@
</form>
</div>
{% endif %}
{% endifequal %}
{% ifequal status 5 %}
{% endif %}
{% if status == 5 %}
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="boot">
<p>{% trans "Click on Boot button to start this instance." %}</p>
<form action="" method="post" role="form" aria-label="Start instance form">{% csrf_token %}
@ -240,7 +240,7 @@
<div class="clearfix"></div>
</form>
</div>
{% endifequal %}
{% endif %}
</div>
</div>
</div>
@ -253,33 +253,33 @@
{% trans "Console" %}
</a>
</li>
{% if show_access_root_password %}
{% if show_access_root_password == 'True' %}
<li class="nav-item">
<a class="nav-link text-secondary" href="#rootpasswd" aria-controls="rootpasswd" role="tab" data-toggle="tab">
{% trans "Root Password" %}
</a>
</li>
{% endif %}
{% if show_access_ssh_keys %}
{% if show_access_ssh_keys == 'True' %}
<li class="nav-item">
<a class="nav-link text-secondary" href="#sshkeys" aria-controls="sshkeys" role="tab" data-toggle="tab">
{% trans "SSH Keys" %}
</a>
</li>
{% endif %}
{% ifequal status 1 %}
{% if status == 1 %}
<li class="nav-item">
<a class="nav-link text-secondary" href="#vdiconsole" aria-controls="vdiconsole" role="tab" data-toggle="tab">
{% trans "VDI" %}
</a>
</li>
{% endifequal %}
{% endif %}
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="vnconsole">
<p>{% trans "This action opens a new window with a VNC connection to the console of the instance." %}</p>
{% ifequal status 1 %}
{% if status == 1 %}
<!-- Split button -->
<div class="btn-group float-right">
<button type="button" id="consoleBtnGroup" class="btn btn-lg btn-success" onclick="open_console('lite')">{% trans 'Console' %}</button>
@ -293,10 +293,10 @@
</div>
{% else %}
<button class="btn btn-lg btn-success float-right disabled">{% trans "Console" %}</button>
{% endifequal %}
{% endif %}
<div class="clearfix"></div>
</div>
{% if show_access_root_password %}
{% if show_access_root_password == 'True' %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="rootpasswd">
<p>{% trans "You need shut down your instance and enter a new root password." %}</p>
<form class="form-inline" method="post" role="form" aria-label="Add root password to instance form">{% csrf_token %}
@ -305,22 +305,22 @@
<input type="text" class="form-control-lg" name="passwd" placeholder="{% trans "Enter Password" %}" maxlength="24">
</div>
</div>
{% ifequal status 5 %}
{% if status == 5 %}
<input type="submit" class="btn btn-lg btn-success float-right" name="rootpasswd" value="{% trans "Reset Root Password" %}">
{% else %}
<button class="btn btn-lg btn-success float-right disabled">{% trans "Reset Root Password" %}</button>
{% endifequal %}
{% endif %}
</form>
<div class="clearfix"></div>
</div>
{% endif %}
{% if show_access_ssh_keys %}
{% if show_access_ssh_keys == 'True' %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="sshkeys">
<p>{% trans "You need shut down your instance and choose your public key." %}</p>
<form class="form-inline" method="post" role="form" aria-label="Add public key to instance form">{% csrf_token %}
<div class="form-group row">
<div class="col-sm-12">
<select name="sshkeyid" class="form-control keyselect">
<select name="sshkeyid" class="form-control-lg keyselect">
{% if publickeys %}
{% for key in publickeys %}
<option value="{{ key.id }}">{{ key.keyname }}</option>
@ -331,16 +331,16 @@
</select>
</div>
</div>
{% ifequal status 5 %}
{% if status == 5 %}
<input type="submit" class="btn btn-lg btn-success float-right" name="addpublickey" value="{% trans "Add Public Key" %}">
{% else %}
<button class="btn btn-lg btn-success float-right disabled">{% trans "Add Public Key" %}</button>
{% endifequal %}
{% endif %}
</form>
<div class="clearfix"></div>
</div>
{% endif %}
{% ifequal status 1 %}
{% if status == 1 %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="vdiconsole">
<p>{% trans "This action opens a remote viewer with a connection to the console of the instance." %}</p>
<div class="input-group">
@ -351,7 +351,7 @@
</div>
<div class="clearfix"></div>
</div>
{% endifequal %}
{% endif %}
</div>
</div>
</div>
@ -408,11 +408,11 @@
</div>
</div>
{% ifequal status 5 %}
{% if status == 5 %}
<button type="submit" class="btn btn-lg btn-success float-right" name="resizevm_cpu">{% trans "Resize" %}</button>
{% else %}
<button class="btn btn-lg btn-success float-right disabled">{% trans "Resize" %}</button>
{% endifequal %}
{% endif %}
</form>
<div class="clearfix"></div>
{% else %}
@ -493,11 +493,11 @@
</div>
</div>
{% endfor %}
{% ifequal status 5 %}
{% if status == 5 %}
<button type="submit" class="btn btn-lg btn-success float-right" name="resizevm_disk">{% trans "Resize" %}</button>
{% else %}
<button class="btn btn-lg btn-success float-right disabled">{% trans "Resize" %}</button>
{% endifequal %}
{% endif %}
</form>
{% else %}
{% trans "You don't have permission for resizing instance" %}
@ -526,7 +526,7 @@
<!-- Tab panes -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="takesnapshot">
{% ifequal status 5 %}
{% if status == 5 %}
<p>{% trans "This may take more than an hour, depending on how much content is on your droplet and how large the disk is." %}</p>
<form class="form-inline" method="post" role="form" aria-label="Create snapshot form">{% csrf_token %}
<div class="form-group row">
@ -534,16 +534,16 @@
<input type="text" class="form-control form-control-lg" name="name" placeholder="{% trans "Enter Snapshot Name" %}" maxlength="14">
</div>
</div>
{% ifequal status 5 %}
{% if status == 5 %}
<input type="submit" class="btn btn-lg btn-success float-right" name="snapshot" value="{% trans "Take Snapshot" %}">
{% else %}
<button class="btn btn-lg btn-success float-right disabled">{% trans "Take Snapshot" %}</button>
{% endifequal %}
{% endif %}
</form>
<div class="clearfix"></div>
{% else %}
<p>{% trans "To take a snapshot please Power Off the instance." %}</p>
{% endifequal %}
{% endif %}
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="managesnapshot">
{% if snapshots %}
@ -563,7 +563,7 @@
<td style="width:30px;">
<form action="" method="post" role="form" aria-label="Restore snapshot form">{% csrf_token %}
<input type="hidden" name="name" value="{{ snap.name }}">
{% ifequal status 5 %}
{% if status == 5 %}
<button type="submit" class="btn btn-sm btn-secondary" name="revert_snapshot" title="{% trans 'Revert to this Snapshot' %}" onclick="return confirm('Are you sure?')">
<span class="fa fa-download"></span>
</button>
@ -572,7 +572,7 @@
title="{% trans "To restore snapshots you need Power Off the instance." %}">
<span class="fa fa-download"></span>
</button>
{% endifequal %}
{% endif %}
</form>
</td>
<td style="width:30px;">
@ -668,11 +668,11 @@
<div class="form-group row">
<div class="col-sm-12 text-center">
<p>{% trans "Autostart your instance when host server is power on " %}
{% ifequal autostart 0 %}
{% if autostart == 0 %}
<input type="submit" class="btn btn-success" name="set_autostart" value="{% trans "Enable" %}">
{% else %}
<input type="submit" class="btn btn-danger" name="unset_autostart" value="{% trans "Disable" %}">
{% endifequal %}
{% endif %}
</p>
</div>
</div>
@ -681,23 +681,23 @@
<form action="" method="post" role="form" aria-label="Enable/disable instance boot order form">{% csrf_token %}
<div class="form-group row">
<div class="col-sm-12 text-center">
{% ifequal status 5 %}
{% if status == 5 %}
<p>{% trans "Enable Boot Menu for your instance when it starts up " %}
{% ifequal bootmenu 0 %}
{% if bootmenu == 0 %}
<input type="submit" class="btn btn-success" name="set_bootmenu" title="{% trans 'Show boot menu' %}" value="{% trans "Enable" %}">
{% else %}
<input type="submit" class="btn btn-danger" name="unset_bootmenu" title="{% trans 'Hide boot menu' %}" value="{% trans "Disable" %}">
{% endifequal %}
{% endif %}
{% else %}
{% ifequal bootmenu 0 %}
{% if bootmenu == 0 %}
<p>**** {% trans "Please shutdown instance to modify boot menu" %} ****</p>
{% endifequal %}
{% endifequal %}
{% endif %}
{% endif %}
</div>
</div>
</form>
{% ifequal bootmenu 1 %}
{% if bootmenu == 1 %}
<div class="d-flex justify-content-center">
<div class="col-sm-6 bg-light rounded shadow-sm">
{% for idx, val in boot_order.items %}
@ -734,7 +734,7 @@
</div>
</div>
</form>
{% endifequal %}
{% endif %}
<div class="clearfix"></div>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="disks">
@ -866,7 +866,7 @@
<input type="hidden" name="storage" value="{{ disk.storage }}">
<input type="hidden" name="name" value="{{ disk.image }}">
{% include 'edit_instance_volume.html' with id=forloop.counter0 %}
{% ifequal status 5 %}
{% if status == 5 %}
<button type="submit" class="btn btn-sm btn-secondary" name="detach_vol" value="{{ disk.dev }}" title="{% trans "Detach" %}" onclick="return confirm('{% trans "Are you sure to detach volume?" %}')">
<i class="fa fa-eject"></i>
</button>
@ -880,9 +880,8 @@
<button class="btn btn-sm btn-secondary disabled" name="delete_vol" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure? This may lead data corruption!" %}')">
<i class="fa fa-trash"></i>
</button>
{% endifequal %}
{% endif %}
</form>
</td>
</tr>
{% endfor %}
@ -957,10 +956,10 @@
<input class="form-control" type="text" value="{{ network.nic }}" readonly/>
<select class="form-control" name="net-source-{{ forloop.counter0 }}">
{% for c_net in networks_host %}
<option value="net:{{ c_net }}" {% ifequal c_net network.nic %} selected {% endifequal %}>{% trans 'Network' %} {{ c_net }}</option>
<option value="net:{{ c_net }}" {% if c_net == network.nic %} selected {% endif %}>{% trans 'Network' %} {{ c_net }}</option>
{% endfor %}
{% for c_iface in interfaces_host %}
<option value="iface:{{ c_iface }}" {% ifequal c_iface network.nic %} selected {% endifequal %}>{% trans 'Interface' %} {{ c_iface }}</option>
<option value="iface:{{ c_iface }}" {% if c_iface == network.nic %} selected {% endif %}>{% trans 'Interface' %} {{ c_iface }}</option>
{% endfor %}
</select>
</div>
@ -972,7 +971,7 @@
<select class="form-control" name="net-nwfilter-{{ forloop.counter0 }}">
<option value="">{% trans "None" %}</option>
{% for c_filters in nwfilters_host %}
<option value="{{ c_filters }}" {% ifequal c_filters network.filterref %} selected {% endifequal %}>{{ c_filters }}</option>
<option value="{{ c_filters }}" {% if c_filters == network.filterref %} selected {% endif %}>{{ c_filters }}</option>
{% endfor %}
</select>
</div>
@ -983,7 +982,7 @@
<input class="form-control" type="text" value="{{ network.model }}" readonly/>
<select class="form-control" name="net-model-{{ forloop.counter0 }}">
{% for model in net_models_host %}
<option value="{{ model }}" {% ifequal model network.model %} selected {% endifequal %}>{{ model }}</option>
<option value="{{ model }}" {% if model == network.model %} selected {% endif %}>{{ model }}</option>
{% endfor %}
</select>
</div>
@ -992,7 +991,7 @@
</div>
<div class="modal-footer">
<button class="btn btn-secondary" data-dismiss="modal">{% trans 'Close' %}</button>
<button class="btn btn-success" name="change_network" title="{% trans "Apply Network Changes" %}">{% trans "Apply" %}</button>
<button class="btn btn-success" name="change_network" title="{% trans "Apply network changes" %}">{% trans "Apply" %}</button>
</div>
</div>
</div>
@ -1116,7 +1115,7 @@
<div class="form-group row">
<div class="col-sm-6 offset-3">
<div class="custom-control custom-switch">
<input class="custom-control-input" type="checkbox" name="live_migrate" value="true" id="vm_live_migrate" {% ifnotequal status 5 %}checked{% else %}disabled{% endifnotequal %}>
<input class="custom-control-input" type="checkbox" name="live_migrate" value="true" id="vm_live_migrate" {% if status != 5 %}checked{% else %}disabled{% endif %}>
<label class="custom-control-label" for="vm_live_migrate">{% trans "Live migration" %}</label>
</div>
</div>
@ -1140,7 +1139,7 @@
<div class="form-group row">
<div class="col-sm-6 offset-3">
<div class="custom-control custom-switch">
<input class="custom-control-input" type="checkbox" name="offline_migrate" value="true" id="offline_migrate" {% ifequal status 5 %}checked{% else %}disabled{% endifequal %}>
<input class="custom-control-input" type="checkbox" name="offline_migrate" value="true" id="offline_migrate" {% if status == 5 %}checked{% else %}disabled{% endif %}>
<label class="custom-control-label" for="offline_migrate">{% trans "Offline migration" %}</label>
</div>
</div>
@ -1148,7 +1147,7 @@
<div class="form-group row">
<div class="col-sm-6 offset-3">
<div class="custom-control custom-switch">
<input class="custom-control-input" type="checkbox" name="postcopy" value="true" id="postcopy" {% ifnotequal status 1 %}disabled{% endifnotequal %}>
<input class="custom-control-input" type="checkbox" name="postcopy" value="true" id="postcopy" {% if status != 1 %}disabled{% endif %}>
<label class="custom-control-label" for="postcopy">{% trans "Post copy" %}</label>
</div>
</div>
@ -1156,7 +1155,7 @@
<div class="form-group row">
<div class="col-sm-6 offset-3">
<div class="custom-control custom-switch">
<input class="custom-control-input" type="checkbox" name="autoconverge" value="true" id="autoconverge" {% ifnotequal status 1 %}disabled{% endifnotequal %}>
<input class="custom-control-input" type="checkbox" name="autoconverge" value="true" id="autoconverge" {% if status != 1 %}disabled{% endif %}>
<label class="custom-control-label" for="autoconverge" title="{% trans 'Forces CPU convergence during live migration' %}">{% trans "Auto converge" %}</label>
</div>
</div>
@ -1164,7 +1163,7 @@
<div class="form-group row">
<div class="col-sm-6 offset-3">
<div class="custom-control custom-switch">
<input class="custom-control-input" type="checkbox" name="compress" value="true" id="compress" {% ifnotequal status 1 %}disabled{% endifnotequal %}>
<input class="custom-control-input" type="checkbox" name="compress" value="true" id="compress" {% if status != 1 %}disabled{% endif %}>
<label class="custom-control-label" for="compress" title="{% trans 'Compress instance memory for fast migration' %}">{% trans "Compressed" %}</label>
</div>
</div>
@ -1183,7 +1182,7 @@
<div class="col-sm-12" id="xmlheight">
<textarea id="editor">{{ inst_xml }}</textarea>
</div>
{% ifequal status 5 %}
{% if status == 5 %}
<input type="hidden" name="inst_xml">
<button type="submit" class="btn btn-lg btn-success float-right" name="change_xml">
{% trans "Change" %}
@ -1192,7 +1191,7 @@
<button class="btn btn-lg btn-success float-right disabled">
{% trans "Change" %}
</button>
{% endifequal %}
{% endif %}
</form>
<div class="clearfix"></div>
</div>
@ -1241,11 +1240,11 @@
</select>
<div class="input-group-append">
{% ifequal status 5 %}
{% if status == 5 %}
<button type="submit" class="btn btn-success " name="set_console_type">{% trans "Set" %}</button>
{% else %}
<button class="btn btn-success disabled" name="set_console_type">{% trans "Set" %}</button>
{% endifequal %}
{% endif %}
</div>
</div>
</div>
@ -1264,11 +1263,11 @@
{% endfor %}
</select>
<div class="input-group-append">
{% ifequal status 5 %}
{% if status == 5 %}
<button type="submit" class="btn btn-success " name="set_console_listen_address">{% trans "Set" %}</button>
{% else %}
<button class="btn btn-success disabled" name="set_console_listen_address">{% trans "Set" %}</button>
{% endifequal %}
{% endif %}
</div>
</div>
</div>
@ -1302,11 +1301,11 @@
{% if console_passwd %}
<a href="#" name="console_show" class="btn btn-md btn-primary" onclick="show_console()">{% trans "Show" %}</a>
{% endif %}
{% ifequal status 5 %}
{% if status == 5 %}
<button type="submit" class="btn btn-success" name="set_console_passwd">{% trans "Set" %}</button>
{% else %}
<button class="btn btn-success disabled" name="set_console_passwd">{% trans "Set" %}</button>
{% endifequal %}
{% endif %}
</div>
</div>
</div>
@ -1334,11 +1333,11 @@
</select>
<div class="input-group-append">
{% ifequal status 5 %}
{% if status == 5 %}
<button type="submit" class="btn btn-success" name="set_console_keymap">{% trans "Set" %}</button>
{% else %}
<button class="btn btn-success disabled" name="set_console_keymap">{% trans "Set" %}</button>
{% endifequal %}
{% endif %}
</div>
</div>
</div>
@ -1410,14 +1409,14 @@
<div class="col-sm-6">
<div class="input-group">
<input id="disk_name-{{ disk.dev }}" type="text" class="form-control" name="disk-{{ disk.dev }}" value="{{ disk.image }}"/>
{% ifequal disk.format 'qcow2' %}
{% if disk.format == 'qcow2' %}
<div class="input-group-append">
<span class="input-group-text" >{% trans 'Metadata' %}</span>
<div class="input-group-text">
<input type="checkbox" name="meta-{{ disk.dev }}" value="true">
</div>
</div>
{% endifequal %}
{% endif %}
</div>
</div>
</div>
@ -1439,11 +1438,11 @@
<textarea name="clone-description" class="form-control"></textarea>
</div>
</div>
{% ifequal status 5 %}
{% if status == 5 %}
<button type="submit" class="btn btn-lg btn-success float-right" name="clone" onclick="showPleaseWaitDialog();">{% trans "Clone" %}</button>
{% else %}
<button class="btn btn-lg btn-success float-right disabled" name="clone">{% trans "Clone" %}</button>
{% endifequal %}
{% endif %}
</form>
<div class="clearfix"></div>
</div>
@ -1475,11 +1474,11 @@
</div>
<div class="form-group row">
<div class="offset-3 col-sm-6">
{% ifequal status 5 %}
{% if status == 5 %}
<button type="submit" class="btn btn-block btn-success" name="change_options">{% trans "Change" %}</button>
{% else %}
<button class="btn btn-block btn-success disabled" name="change_options">{% trans "Change" %}</button>
{% endifequal %}
{% endif %}
</div>
</div>
</form>
@ -1497,11 +1496,11 @@
{% endfor %}
</select>
<span class="input-group-btn">
{% ifequal status 5 %}
{% if status == 5 %}
<button type="submit" class="btn btn-success" name="set_video_model">{% trans "Set" %}</button>
{% else %}
<button class="btn btn-success disabled" name="set_video_model">{% trans "Set" %}</button>
{% endifequal %}
{% endif %}
</span>
</div>
</div>
@ -1669,10 +1668,9 @@
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="destroy">
<p>{% trans "Delete storage for instance?" %}</p>
{% if request.user.is_superuser or userinstance.is_delete %}
{% ifequal status 3 %}
{% if status == 3 %}
<button class="btn btn-lg btn-success disabled float-right" name="delete">{% trans "Destroy" %}</button>
{% else %}
<form class="form" method="post" role="form" id="delete_form">{% csrf_token %}
<div class="ml-3 form-row">
<div class="custom-control custom-switch">
@ -1690,7 +1688,7 @@
{% endif %}
<button type="submit" class="btn btn-lg btn-success float-right" name="delete">{% trans "Destroy" %}</button>
</form>
{% endifequal %}
{% endif %}
{% else %}
<button class="btn btn-lg btn-success disabled float-right" name="delete">{% trans "Destroy" %}</button>
{% endif %}

View file

@ -13,9 +13,9 @@ from django.shortcuts import render, get_object_or_404
from django.utils.translation import ugettext_lazy as _
from computes.models import Compute
from instances.models import Instance
from appsettings.models import AppSettings
from django.contrib.auth.models import User
from accounts.models import UserInstance, UserSSHKey
from appsettings.models import AppSettings
from vrtManager.hostdetails import wvmHostDetails
from vrtManager.instance import wvmInstance, wvmInstances
from vrtManager.connection import connection_manager
@ -64,7 +64,7 @@ def allinstances(request):
error_messages.append(lib_err)
addlogmsg(request.user.username, request.POST.get("name", "instance"), lib_err)
view_style = settings.VIEW_INSTANCES_LIST_STYLE
view_style = AppSettings.objects.get(key="VIEW_INSTANCES_LIST_STYLE").value
return render(request, 'allinstances.html', locals())
@ -111,10 +111,11 @@ def instance(request, compute_id, vname):
computes_count = computes.count()
users = User.objects.all().order_by('username')
publickeys = UserSSHKey.objects.filter(user_id=request.user.id)
appsettings = AppSettings.objects.all()
keymaps = settings.QEMU_KEYMAPS
console_types = settings.QEMU_CONSOLE_TYPES
console_types = appsettings.get(key="QEMU_CONSOLE_DEFAULT_TYPE").choices_as_list
console_listen_addresses = settings.QEMU_CONSOLE_LISTEN_ADDRESSES
bottom_bar = AppSettings.objects.get(key="VIEW_INSTANCE_DETAIL_BOTTOM_BAR").value
bottom_bar = appsettings.get(key="VIEW_INSTANCE_DETAIL_BOTTOM_BAR").value
try:
userinstance = UserInstance.objects.get(instance__compute_id=compute_id,
instance__name=vname,
@ -144,7 +145,7 @@ def instance(request, compute_id, vname):
return int(float(size_str))
def get_clone_free_names(size=10):
prefix = settings.CLONE_INSTANCE_DEFAULT_PREFIX
prefix = appsettings.get(key="CLONE_INSTANCE_DEFAULT_PREFIX").value
free_names = []
existing_names = [i.name for i in Instance.objects.filter(name__startswith=prefix)]
index = 1
@ -179,21 +180,22 @@ def instance(request, compute_id, vname):
disk_size += int(disk['size']) >> 30
if ua.max_instances > 0 and instance > ua.max_instances:
quota_debug = appsettings.get(key="QUOTA_DEBUG").value
msg = "instance"
if settings.QUOTA_DEBUG:
msg += " (%s > %s)" % (instance, ua.max_instances)
if quota_debug:
msg += f" ({instance} > {ua.max_instances})"
if ua.max_cpus > 0 and cpu > ua.max_cpus:
msg = "cpu"
if settings.QUOTA_DEBUG:
msg += " (%s > %s)" % (cpu, ua.max_cpus)
if quota_debug:
msg += f" ({cpu} > {ua.max_cpus})"
if ua.max_memory > 0 and memory > ua.max_memory:
msg = "memory"
if settings.QUOTA_DEBUG:
msg += " (%s > %s)" % (memory, ua.max_memory)
if quota_debug:
msg += f" ({memory} > {ua.max_memory})"
if ua.max_disk_size > 0 and disk_size > ua.max_disk_size:
msg = "disk"
if settings.QUOTA_DEBUG:
msg += " (%s > %s)" % (disk_size, ua.max_disk_size)
if quota_debug:
msg += f" ({disk_size} > {ua.max_disk_size})"
return msg
def get_new_disk_dev(media, disks, bus):
@ -314,18 +316,21 @@ def instance(request, compute_id, vname):
io_modes = sorted(conn.get_io_modes().items())
discard_modes = sorted(conn.get_discard_modes().items())
detect_zeroes_modes = sorted(conn.get_detect_zeroes_modes().items())
default_io = settings.INSTANCE_VOLUME_DEFAULT_IO
default_discard = settings.INSTANCE_VOLUME_DEFAULT_DISCARD
default_zeroes = settings.INSTANCE_VOLUME_DEFAULT_DETECT_ZEROES
default_cache = settings.INSTANCE_VOLUME_DEFAULT_CACHE
default_format = settings.INSTANCE_VOLUME_DEFAULT_FORMAT
default_owner = settings.INSTANCE_VOLUME_DEFAULT_OWNER
default_bus = appsettings.get(key="INSTANCE_VOLUME_DEFAULT_BUS").value
default_io = appsettings.get(key="INSTANCE_VOLUME_DEFAULT_IO").value
default_discard = appsettings.get(key="INSTANCE_VOLUME_DEFAULT_DISCARD").value
default_zeroes = appsettings.get(key="INSTANCE_VOLUME_DEFAULT_DETECT_ZEROES").value
default_cache = appsettings.get(key="INSTANCE_VOLUME_DEFAULT_CACHE").value
default_format = appsettings.get(key="INSTANCE_VOLUME_DEFAULT_FORMAT").value
default_disk_owner_uid = int(appsettings.get(key="INSTANCE_VOLUME_DEFAULT_OWNER_UID").value)
default_disk_owner_gid = int(appsettings.get(key="INSTANCE_VOLUME_DEFAULT_OWNER_GID").value)
formats = conn.get_image_formats()
show_access_root_password = settings.SHOW_ACCESS_ROOT_PASSWORD
show_access_ssh_keys = settings.SHOW_ACCESS_SSH_KEYS
clone_instance_auto_name = settings.CLONE_INSTANCE_AUTO_NAME
default_bus = settings.INSTANCE_VOLUME_DEFAULT_BUS
show_access_root_password = appsettings.get(key="SHOW_ACCESS_ROOT_PASSWORD").value
show_access_ssh_keys = appsettings.get(key="SHOW_ACCESS_SSH_KEYS").value
clone_instance_auto_name = appsettings.get(key="CLONE_INSTANCE_AUTO_NAME").value
try:
instance = Instance.objects.get(compute_id=compute_id, name=vname)
@ -520,7 +525,7 @@ def instance(request, compute_id, vname):
error_messages.append(msg)
else:
conn.resize_disk(disks_new)
msg = _("Resize")
msg = _("Disk resize")
addlogmsg(request.user.username, instance.name, msg)
messages.success(request, msg)
return HttpResponseRedirect(request.get_full_path() + '#resize')
@ -539,9 +544,9 @@ def instance(request, compute_id, vname):
cache = request.POST.get('cache', default_cache)
target_dev = get_new_disk_dev(media, disks, bus)
source = conn_create.create_volume(storage, name, size, format, meta_prealloc, default_owner)
source = conn_create.create_volume(storage, name, size, format, meta_prealloc, default_disk_owner_uid, default_disk_owner_gid)
conn.attach_disk(target_dev, source, target_bus=bus, driver_type=format, cache_mode=cache)
msg = _('Attach new disk {} ({})'.format(name, format))
msg = _(f"Attach new disk {name} ({format})")
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#disks')
@ -810,8 +815,11 @@ def instance(request, compute_id, vname):
if 'set_console_type' in request.POST:
console_type = request.POST.get('console_type', '')
conn.set_console_type(console_type)
msg = _("Set VNC type")
if console_type in console_types:
conn.set_console_type(console_type)
else:
msg = _("Console type not supported")
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#vncsettings')
@ -943,13 +951,13 @@ def instance(request, compute_id, vname):
if 'add_owner' in request.POST:
user_id = int(request.POST.get('user_id', ''))
if settings.ALLOW_INSTANCE_MULTIPLE_OWNER:
if appsettings.get(key="ALLOW_INSTANCE_MULTIPLE_OWNER").value == 'True':
check_inst = UserInstance.objects.filter(instance=instance, user_id=user_id)
else:
check_inst = UserInstance.objects.filter(instance=instance)
if check_inst:
msg = _("Owner already added")
msg = _("One owner is allowed and owner already added")
error_messages.append(msg)
else:
add_user_inst = UserInstance(instance=instance, user_id=user_id)
@ -975,10 +983,13 @@ def instance(request, compute_id, vname):
quota_msg = check_user_quota(1, vcpu, memory, disk_sum)
check_instance = Instance.objects.filter(name=clone_data['name'])
clone_data['disk_owner_uid'] = default_disk_owner_uid
clone_data['disk_owner_gid'] = default_disk_owner_gid
for post in request.POST:
clone_data[post] = request.POST.get(post, '').strip()
if clone_instance_auto_name and not clone_data['name']:
if clone_instance_auto_name == 'True' and not clone_data['name']:
auto_vname = clone_free_names[0]
clone_data['name'] = auto_vname
clone_data['clone-net-mac-0'] = _get_dhcp_mac_address(auto_vname)
@ -1016,7 +1027,8 @@ def instance(request, compute_id, vname):
msg = _("Clone of '%s'" % instance.name)
addlogmsg(request.user.username, new_instance.name, msg)
if settings.CLONE_INSTANCE_AUTO_MIGRATE:
if appsettings.get(key="CLONE_INSTANCE_AUTO_MIGRATE").value == 'True':
new_compute = Compute.objects.order_by('?').first()
migrate_instance(new_compute, new_instance, xml_del=True, offline=True)
return HttpResponseRedirect(
@ -1326,7 +1338,7 @@ def random_mac_address(request):
def guess_clone_name(request):
dhcp_file = '/srv/webvirtcloud/dhcpd.conf'
prefix = settings.CLONE_INSTANCE_DEFAULT_PREFIX
prefix = appsettings.get(key="CLONE_INSTANCE_DEFAULT_PREFIX").value
if os.path.isfile(dhcp_file):
instance_names = [i.name for i in Instance.objects.filter(name__startswith=prefix)]
with open(dhcp_file, 'r') as f:

View file

@ -6,6 +6,7 @@ from django.shortcuts import render
from django.urls import reverse
from admin.decorators import superuser_only
from appsettings.models import AppSettings
from instances.models import Instance
from logs.models import Logs

View file

@ -4,6 +4,7 @@ from django.http import HttpResponseRedirect, HttpResponse
from django.utils.translation import ugettext_lazy as _
from django.urls import reverse
from computes.models import Compute
from appsettings.models import AppSettings
from storages.forms import AddStgPool, AddImage, CloneImage
from vrtManager.storage import wvmStorage, wvmStorages
from libvirt import libvirtError
@ -143,8 +144,12 @@ def storage(request, compute_id, pool):
if data['meta_prealloc'] and data['format'] == 'qcow2':
meta_prealloc = True
try:
name = conn.create_volume(data['name'], data['size'], data['format'], meta_prealloc)
messages.success(request, _("Image file {} is created successfully".format(name)))
disk_owner = AppSettings.objects.filter(key__startswith="INSTANCE_VOLUME_DEFAULT_OWNER")
disk_owner_uid = int(disk_owner.get(key="INSTANCE_VOLUME_DEFAULT_OWNER_UID").value)
disk_owner_gid = int(disk_owner.get(key="INSTANCE_VOLUME_DEFAULT_OWNER_GID").value)
name = conn.create_volume(data['name'], data['size'], data['format'], meta_prealloc, disk_owner_uid, disk_owner_gid)
messages.success(request, _(f"Image file {name} is created successfully"))
return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err:
error_messages.append(lib_err)

View file

@ -1,9 +1,6 @@
import string
from vrtManager import util
from vrtManager.connection import wvmConnect
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_OWNER as DEFAULT_OWNER
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_FORMAT
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_SCSI_CONTROLLER
def get_rbd_storage_data(stg):
@ -23,7 +20,6 @@ def get_rbd_storage_data(stg):
class wvmCreate(wvmConnect):
image_format = INSTANCE_VOLUME_DEFAULT_FORMAT
def get_storages_images(self):
"""
@ -52,7 +48,7 @@ class wvmCreate(wvmConnect):
"""Get guest capabilities"""
return util.get_xml_path(self.get_cap_xml(), "/capabilities/host/cpu/arch")
def create_volume(self, storage, name, size, image_format=image_format, metadata=False, owner=DEFAULT_OWNER):
def create_volume(self, storage, name, size, image_format, metadata=False, disk_owner_uid=0, disk_owner_gid=0):
size = int(size) * 1073741824
stg = self.get_storage(storage)
storage_type = util.get_xml_path(stg.XMLDesc(0), "/pool/@type")
@ -65,16 +61,16 @@ class wvmCreate(wvmConnect):
else:
alloc = size
metadata = False
xml = """
xml = f"""
<volume>
<name>%s</name>
<capacity>%s</capacity>
<allocation>%s</allocation>
<name>{name}</name>
<capacity>{size}</capacity>
<allocation>{alloc}</allocation>
<target>
<format type='%s'/>
<format type='{image_format}'/>
<permissions>
<owner>%s</owner>
<group>%s</group>
<owner>{disk_owner_uid}</owner>
<group>{disk_owner_gid}</group>
<mode>0644</mode>
<label>virt_image_t</label>
</permissions>
@ -83,7 +79,7 @@ class wvmCreate(wvmConnect):
<lazy_refcounts/>
</features>
</target>
</volume>""" % (name, size, alloc, image_format, owner['uid'], owner['guid'])
</volume>"""
stg.createXML(xml, metadata)
try:
stg.refresh(0)
@ -120,7 +116,7 @@ class wvmCreate(wvmConnect):
vol = self.get_volume_by_path(vol_path)
return vol.storagePoolLookupByVolume()
def clone_from_template(self, clone, template, storage=None, metadata=False, owner=DEFAULT_OWNER):
def clone_from_template(self, clone, template, storage=None, metadata=False, disk_owner_uid=0, disk_owner_gid=0):
vol = self.get_volume_by_path(template)
if not storage:
stg = vol.storagePoolLookupByVolume()
@ -133,16 +129,16 @@ class wvmCreate(wvmConnect):
clone += '.img'
else:
metadata = False
xml = """
xml = f"""
<volume>
<name>%s</name>
<name>{clone}</name>
<capacity>0</capacity>
<allocation>0</allocation>
<target>
<format type='%s'/>
<format type='{format}'/>
<permissions>
<owner>%s</owner>
<group>%s</group>
<owner>{disk_owner_uid}</owner>
<group>{disk_owner_gid}</group>
<mode>0644</mode>
<label>virt_image_t</label>
</permissions>
@ -151,7 +147,7 @@ class wvmCreate(wvmConnect):
<lazy_refcounts/>
</features>
</target>
</volume>""" % (clone, format, owner['uid'], owner['guid'])
</volume>"""
stg.createXMLFrom(xml, vol, metadata)
clone_vol = stg.storageVolLookupByName(clone)
return clone_vol.path()
@ -163,11 +159,9 @@ class wvmCreate(wvmConnect):
vol = self.get_volume_by_path(path)
vol.delete()
def create_instance(self, name, memory, vcpu, vcpu_mode, uuid, arch, machine, firmware, images,
def create_instance(self, name, memory, vcpu, vcpu_mode, uuid, arch, machine, firmware, volumes,
networks, nwfilter, graphics, virtio, listen_addr,
video="vga", console_pass="random", mac=None,
cache_mode=None, io_mode=None, discard_mode=None, detect_zeroes_mode=None,
qemu_ga=True):
video="vga", console_pass="random", mac=None, qemu_ga=True):
"""
Create VM function
"""
@ -176,17 +170,17 @@ class wvmCreate(wvmConnect):
memory = int(memory) * 1024
xml = """
<domain type='%s'>
<name>%s</name>
xml = f"""
<domain type='{dom_caps["domain"]}'>
<name>{name}</name>
<description>None</description>
<uuid>%s</uuid>
<memory unit='KiB'>%s</memory>
<vcpu>%s</vcpu>""" % (dom_caps["domain"], name, uuid, memory, vcpu)
<uuid>{uuid}</uuid>
<memory unit='KiB'>{memory}</memory>
<vcpu>{vcpu}</vcpu>"""
if dom_caps["os_support"] == 'yes':
xml += """<os>
<type arch='%s' machine='%s'>%s</type>""" % (arch, machine, caps["os_type"])
xml += f"""<os>
<type arch='{arch}' machine='{machine}'>{caps["os_type"]}</type>"""
xml += """ <boot dev='hd'/>
<boot dev='cdrom'/>
<bootmenu enable='yes'/>"""
@ -221,8 +215,8 @@ class wvmCreate(wvmConnect):
elif vcpu_mode == "":
pass
else:
xml += """<cpu mode='custom' match='exact' check='none'>
<model fallback='allow'>%s</model>""" % vcpu_mode
xml += f"""<cpu mode='custom' match='exact' check='none'>
<model fallback='allow'>{vcpu_mode}</model>"""
xml += """</cpu>"""
xml += """
@ -239,17 +233,18 @@ class wvmCreate(wvmConnect):
sd_disk_letters = list(string.ascii_lowercase)
add_cd = True
disk_opts = ''
if cache_mode is not None and cache_mode != 'default':
disk_opts += "cache='%s' " % cache_mode
if io_mode is not None and io_mode != 'default':
disk_opts += "io='%s' " % io_mode
if discard_mode is not None and discard_mode != 'default':
disk_opts += "discard='%s' " % discard_mode
if detect_zeroes_mode is not None and detect_zeroes_mode != 'default':
disk_opts += "detect_zeroes='%s' " % detect_zeroes_mode
for volume in volumes:
disk_opts = ''
if volume['cache_mode'] is not None and volume['cache_mode'] != 'default':
disk_opts += f"cache='{volume['cache_mode']}' "
if volume['io_mode'] is not None and volume['io_mode'] != 'default':
disk_opts += f"io='{volume['io_mode']}' "
if volume['discard_mode'] is not None and volume['discard_mode'] != 'default':
disk_opts += f"discard='{volume['discard_mode']}' "
if volume['detect_zeroes_mode'] is not None and volume['detect_zeroes_mode'] != 'default':
disk_opts += f"detect_zeroes='{volume['detect_zeroes_mode']}' "
for volume in images:
stg = self.get_storage_by_vol_path(volume['path'])
stg_type = util.get_xml_path(stg.XMLDesc(0), "/pool/@type")
@ -275,7 +270,7 @@ class wvmCreate(wvmConnect):
else:
xml += """<disk type='file' device='%s'>""" % volume['device']
xml += """ <driver name='qemu' type='%s' %s/>""" % (volume['type'], disk_opts)
xml += """ <source file='%s'/>""" % volume['path']
xml += f""" <source file='%s'/>""" % volume['path']
if volume.get('bus') == 'virtio':
xml += """<target dev='vd%s' bus='%s'/>""" % (vd_disk_letters.pop(0), volume.get('bus'))
@ -304,15 +299,15 @@ class wvmCreate(wvmConnect):
xml += """</disk>"""
if volume.get('bus') == 'scsi':
xml += """<controller type='scsi' model='%s'/>""" % INSTANCE_VOLUME_DEFAULT_SCSI_CONTROLLER
xml += f"""<controller type='scsi' model='{volume.get('scsi_model')}'/>"""
for net in networks.split(','):
xml += """<interface type='network'>"""
if mac:
xml += """<mac address='%s'/>""" % mac
xml += """<source network='%s'/>""" % net
xml += f"""<mac address='{mac}'/>"""
xml += f"""<source network='{net}'/>"""
if nwfilter:
xml += """<filterref filter='%s'/>""" % nwfilter
xml += f"""<filterref filter='{nwfilter}'/>"""
if virtio:
xml += """<model type='virtio'/>"""
xml += """</interface>"""
@ -332,18 +327,18 @@ class wvmCreate(wvmConnect):
xml += """<input type='keyboard'/>"""
xml += """<input type='tablet'/>"""
xml += """
<graphics type='%s' port='-1' autoport='yes' %s listen='%s'/>
<console type='pty'/> """ % (graphics, console_pass, listen_addr)
xml += f"""
<graphics type='{graphics}' port='-1' autoport='yes' {console_pass} listen='{listen_addr}'/>
<console type='pty'/> """
if qemu_ga and virtio:
xml += """ <channel type='unix'>
<target type='virtio' name='org.qemu.guest_agent.0'/>
</channel>"""
xml += """ <video>
<model type='%s'/>
xml += f""" <video>
<model type='{video}'/>
</video>
</devices>
</domain>""" % video
</domain>"""
self._defineXML(xml)

View file

@ -22,8 +22,6 @@ from collections import OrderedDict
from vrtManager import util
from vrtManager.connection import wvmConnect
from vrtManager.storage import wvmStorage, wvmStorages
from webvirtcloud.settings import QEMU_CONSOLE_TYPES
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_OWNER as OWNER
class wvmInstances(wvmConnect):
@ -909,7 +907,7 @@ class wvmInstance(wvmConnect):
current_type = self.get_console_type()
if current_type == console_type:
return True
if console_type == '' or console_type not in QEMU_CONSOLE_TYPES:
if console_type == '':
return False
xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
root = ElementTree.fromstring(xml)
@ -1203,8 +1201,8 @@ class wvmInstance(wvmConnect):
<target>
<format type='{vol_format}'/>
<permissions>
<owner>{OWNER['uid']}</owner>
<group>{OWNER['guid']}</group>
<owner>{clone_data['disk_owner_uid']}</owner>
<group>{clone_data['disk_owner_gid']}</group>
<mode>0644</mode>
<label>virt_image_t</label>
</permissions>

View file

@ -1,6 +1,5 @@
from vrtManager import util
from vrtManager.connection import wvmConnect
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_OWNER as OWNER
class wvmStorages(wvmConnect):
@ -210,7 +209,7 @@ class wvmStorage(wvmConnect):
)
return vol_list
def create_volume(self, name, size, vol_fmt='qcow2', metadata=False, owner=OWNER):
def create_volume(self, name, size, vol_fmt='qcow2', metadata=False, disk_owner_uid=0, disk_owner_gid=0):
size = int(size) * 1073741824
storage_type = self.get_type()
alloc = size
@ -230,8 +229,8 @@ class wvmStorage(wvmConnect):
<target>
<format type='{vol_fmt}'/>
<permissions>
<owner>{owner['uid']}</owner>
<group>{owner['guid']}</group>
<owner>{disk_owner_uid}</owner>
<group>{disk_owner_gid}</group>
<mode>0644</mode>
<label>virt_image_t</label>
</permissions>"""
@ -246,7 +245,7 @@ class wvmStorage(wvmConnect):
self._createXML(xml, metadata)
return name
def clone_volume(self, name, target_file, vol_fmt=None, metadata=False, mode='0644', file_suffix='img', owner=OWNER):
def clone_volume(self, name, target_file, vol_fmt=None, metadata=False, mode='0644', file_suffix='img', disk_owner_uid=0, disk_owner_gid=0):
vol = self.get_volume(name)
if not vol_fmt:
vol_fmt = self.get_volume_type(name)
@ -267,8 +266,8 @@ class wvmStorage(wvmConnect):
<target>
<format type='{vol_fmt}'/>
<permissions>
<owner>{owner['uid']}</owner>
<group>{owner['guid']}</group>
<owner>{disk_owner_uid}</owner>
<group>{disk_owner_gid}</group>
<mode>{mode}</mode>
<label>virt_image_t</label>
</permissions>"""

View file

@ -174,52 +174,10 @@ LIBVIRT_KEEPALIVE_INTERVAL = 5
LIBVIRT_KEEPALIVE_COUNT = 5
ALLOW_INSTANCE_MULTIPLE_OWNER = True
ALLOW_EMPTY_PASSWORD = False
NEW_USER_DEFAULT_INSTANCES = []
CLONE_INSTANCE_DEFAULT_PREFIX = 'instance'
CLONE_INSTANCE_AUTO_NAME = False
CLONE_INSTANCE_AUTO_MIGRATE = False
LOGS_PER_PAGE = 100
QUOTA_DEBUG = True
ALLOW_EMPTY_PASSWORD = True
SHOW_ACCESS_ROOT_PASSWORD = False
SHOW_ACCESS_SSH_KEYS = False
# available list style: default (grouped), nongrouped
VIEW_INSTANCES_LIST_STYLE = 'grouped'
# available volume format: raw, qcow2, qcow
INSTANCE_VOLUME_DEFAULT_FORMAT = 'qcow2'
# available bus types: virtio, scsi, ide, usb, sata
INSTANCE_VOLUME_DEFAULT_BUS = 'virtio'
#SCSI types: virtio-scsi, lsilogic
INSTANCE_VOLUME_DEFAULT_SCSI_CONTROLLER = 'virtio-scsi'
# Volume cache: default, directsync, none, unsafe, writeback, writethrough
INSTANCE_VOLUME_DEFAULT_CACHE = 'directsync'
# Volume io mode: default, native, threads
INSTANCE_VOLUME_DEFAULT_IO = 'default'
# Volume detect zeroes mode: default, on, off, unmap
INSTANCE_VOLUME_DEFAULT_DETECT_ZEROES = 'default'
# Volume discard mode: default, unmap, ignore
INSTANCE_VOLUME_DEFAULT_DISCARD = 'default'
# up to os, 0=root, 107=qemu or libvirt-bin(for ubuntu)
INSTANCE_VOLUME_DEFAULT_OWNER = {'uid': 0, 'guid': 0}
# Cpu modes: host-model, host-passthrough, custom
INSTANCE_CPU_DEFAULT_MODE = 'host-model'
# Chipset/Machine: pc or q35 for x86_64
INSTANCE_MACHINE_DEFAULT_TYPE = 'q35'
# Firmware: BIOS or UEFI for x86_64
INSTANCE_FIRMWARE_DEFAULT_TYPE = 'BIOS'
# Architecture: x86_64, i686, etc
INSTANCE_ARCH_DEFAULT_TYPE = 'x86_64'