mirror of
https://github.com/retspen/webvirtcloud
synced 2024-12-25 15:45:23 +00:00
1618 lines
66 KiB
Python
1618 lines
66 KiB
Python
import crypt
|
|
import json
|
|
import os
|
|
import re
|
|
import socket
|
|
import time
|
|
import subprocess
|
|
from bisect import insort
|
|
|
|
from accounts.models import UserInstance, UserSSHKey
|
|
from admin.decorators import superuser_only
|
|
from appsettings.models import AppSettings
|
|
from appsettings.settings import app_settings
|
|
from computes.models import Compute
|
|
from django.conf import settings
|
|
from django.contrib import messages
|
|
from django.contrib.auth.decorators import permission_required
|
|
from django.contrib.auth.models import User
|
|
from django.http import Http404, HttpResponse, JsonResponse
|
|
from django.shortcuts import get_object_or_404, redirect, render
|
|
from django.urls import reverse
|
|
from django.utils.translation import gettext_lazy as _
|
|
from instances.models import Instance
|
|
from libvirt import VIR_DOMAIN_UNDEFINE_KEEP_NVRAM, VIR_DOMAIN_UNDEFINE_NVRAM, libvirtError
|
|
from logs.views import addlogmsg
|
|
from vrtManager import util
|
|
from vrtManager.create import wvmCreate
|
|
from vrtManager.instance import wvmInstances
|
|
from vrtManager.interface import wvmInterface
|
|
from vrtManager.storage import wvmStorage
|
|
from vrtManager.util import randomPasswd
|
|
|
|
from . import utils
|
|
from .forms import ConsoleForm, FlavorForm, NewVMForm
|
|
from .models import Flavor
|
|
|
|
|
|
def index(request):
|
|
instances = None
|
|
|
|
computes = (
|
|
Compute.objects.all()
|
|
.order_by("name")
|
|
.prefetch_related("instance_set")
|
|
.prefetch_related("instance_set__userinstance_set")
|
|
)
|
|
for compute in computes:
|
|
utils.refr(compute)
|
|
|
|
if request.user.is_superuser or request.user.has_perm("instances.view_instances"):
|
|
instances = Instance.objects.all().prefetch_related("userinstance_set")
|
|
else:
|
|
instances = Instance.objects.filter(userinstance__user=request.user).prefetch_related("userinstance_set")
|
|
|
|
return render(request, "allinstances.html", {"computes": computes, "instances": instances})
|
|
|
|
|
|
def instance(request, pk):
|
|
instance: Instance = get_instance(request.user, pk)
|
|
compute: Compute = instance.compute
|
|
computes = Compute.objects.all().order_by("name")
|
|
computes_count = computes.count()
|
|
users = User.objects.all().order_by("username")
|
|
publickeys = UserSSHKey.objects.filter(user_id=request.user.id)
|
|
keymaps = settings.QEMU_KEYMAPS
|
|
console_types = AppSettings.objects.get(key="QEMU_CONSOLE_DEFAULT_TYPE").choices_as_list()
|
|
console_form = ConsoleForm(
|
|
initial={
|
|
"type": instance.console_type,
|
|
"listen_on": instance.console_listener_address,
|
|
"password": instance.console_passwd,
|
|
"keymap": instance.console_keymap,
|
|
}
|
|
)
|
|
console_listener_addresses = settings.QEMU_CONSOLE_LISTENER_ADDRESSES
|
|
bottom_bar = app_settings.VIEW_INSTANCE_DETAIL_BOTTOM_BAR
|
|
allow_admin_or_not_template = request.user.is_superuser or request.user.is_staff or not instance.is_template
|
|
try:
|
|
userinstance = UserInstance.objects.get(
|
|
instance__compute_id=compute.id, instance__name=instance.name, user__id=request.user.id
|
|
)
|
|
except UserInstance.DoesNotExist:
|
|
userinstance = None
|
|
|
|
memory_range = [256, 512, 768, 1024, 2048, 3072, 4096, 6144, 8192, 16384]
|
|
if instance.memory not in memory_range:
|
|
insort(memory_range, instance.memory)
|
|
if instance.cur_memory not in memory_range:
|
|
insort(memory_range, instance.cur_memory)
|
|
clone_free_names = utils.get_clone_free_names()
|
|
user_quota_msg = utils.check_user_quota(request.user, 0, 0, 0, 0)
|
|
|
|
default_bus = app_settings.INSTANCE_VOLUME_DEFAULT_BUS
|
|
default_io = app_settings.INSTANCE_VOLUME_DEFAULT_IO
|
|
default_discard = app_settings.INSTANCE_VOLUME_DEFAULT_DISCARD
|
|
default_zeroes = app_settings.INSTANCE_VOLUME_DEFAULT_DETECT_ZEROES
|
|
default_cache = app_settings.INSTANCE_VOLUME_DEFAULT_CACHE
|
|
default_format = app_settings.INSTANCE_VOLUME_DEFAULT_FORMAT
|
|
# default_disk_owner_uid = int(app_settings.INSTANCE_VOLUME_DEFAULT_OWNER_UID)
|
|
# default_disk_owner_gid = int(app_settings.INSTANCE_VOLUME_DEFAULT_OWNER_GID)
|
|
|
|
# clone_instance_auto_name = app_settings.CLONE_INSTANCE_AUTO_NAME
|
|
|
|
# try:
|
|
# instance = Instance.objects.get(compute=compute, name=vname)
|
|
# if instance.uuid != uuid:
|
|
# instance.uuid = uuid
|
|
# instance.save()
|
|
# msg = _(f"Fixing UUID {uuid}")
|
|
# addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
# except Instance.DoesNotExist:
|
|
# instance = Instance(compute=compute, name=vname, uuid=uuid)
|
|
# instance.save()
|
|
# msg = _("Instance does not exist: Creating new instance")
|
|
# addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
|
|
# userinstances = UserInstance.objects.filter(instance=instance).order_by('user__username')
|
|
userinstances = instance.userinstance_set.order_by("user__username")
|
|
allow_admin_or_not_template = request.user.is_superuser or request.user.is_staff or not instance.is_template
|
|
|
|
# Host resources
|
|
vcpu_host = len(instance.vcpu_range)
|
|
memory_host = instance.proxy.get_max_memory()
|
|
bus_host = instance.proxy.get_disk_bus_types(instance.arch, instance.machine)
|
|
networks_host = sorted(instance.proxy.get_networks())
|
|
interfaces_host = sorted(instance.proxy.get_ifaces())
|
|
nwfilters_host = instance.proxy.get_nwfilters()
|
|
storages_host = sorted(instance.proxy.get_storages(True))
|
|
net_models_host = instance.proxy.get_network_models()
|
|
|
|
if app_settings.VM_DRBD_STATUS == 'True':
|
|
instance.drbd = drbd_status(request, pk)
|
|
instance.save()
|
|
|
|
return render(request, "instance.html", locals())
|
|
|
|
|
|
def status(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
return JsonResponse({"status": instance.proxy.get_status()})
|
|
|
|
def drbd_status(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
result = "None DRBD"
|
|
|
|
if instance.compute.type == 2:
|
|
conn = instance.compute.login + "@" + instance.compute.hostname
|
|
remoteDrbdStatus = subprocess.run(["ssh", conn, "sudo", "/usr/sbin/drbdadm", "status", "&&", "exit"], stdout=subprocess.PIPE, text=True)
|
|
|
|
if remoteDrbdStatus.stdout:
|
|
try:
|
|
instanceFindDrbd = re.compile(instance.name + '[_]*[A-Z]* role:(.+?)\n disk:(.+?)\n', re.IGNORECASE)
|
|
instanceDrbd = instanceFindDrbd.findall(remoteDrbdStatus.stdout)
|
|
|
|
primaryCount = 0
|
|
secondaryCount = 0
|
|
statusDisk = "OK"
|
|
|
|
for disk in instanceDrbd:
|
|
if disk[0] == "Primary":
|
|
primaryCount = primaryCount + 1
|
|
elif disk[0] == "Secondary":
|
|
secondaryCount = secondaryCount + 1
|
|
if disk[1] != "UpToDate":
|
|
statusDisk = "NOK"
|
|
|
|
if primaryCount > 0 and secondaryCount > 0:
|
|
statusRole = "NOK"
|
|
else:
|
|
if primaryCount > secondaryCount:
|
|
statusRole = "Primary"
|
|
else:
|
|
statusRole = "Secondary"
|
|
|
|
result = statusRole + "/" + statusDisk
|
|
|
|
except:
|
|
print("Error to get drbd role and status")
|
|
|
|
return result
|
|
|
|
def stats(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
json_blk = []
|
|
json_net = []
|
|
|
|
# TODO: stats are inaccurate
|
|
cpu_usage = instance.proxy.cpu_usage()
|
|
mem_usage = instance.proxy.mem_usage()
|
|
blk_usage = instance.proxy.disk_usage()
|
|
net_usage = instance.proxy.net_usage()
|
|
|
|
current_time = time.strftime("%H:%M:%S")
|
|
for blk in blk_usage:
|
|
json_blk.append({"dev": blk["dev"], "data": [int(blk["rd"]) / 1048576, int(blk["wr"]) / 1048576]})
|
|
|
|
for net in net_usage:
|
|
json_net.append({"dev": net["dev"], "data": [int(net["rx"]) / 1048576, int(net["tx"]) / 1048576]})
|
|
|
|
return JsonResponse(
|
|
{
|
|
"cpudata": int(cpu_usage["cpu"]),
|
|
"memdata": mem_usage,
|
|
"blkdata": json_blk,
|
|
"netdata": json_net,
|
|
"timeline": current_time,
|
|
}
|
|
)
|
|
|
|
def osinfo(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
results = instance.proxy.osinfo()
|
|
|
|
return JsonResponse(results)
|
|
|
|
def guess_mac_address(request, vname):
|
|
data = {"vname": vname}
|
|
mac = utils.get_dhcp_mac_address(vname)
|
|
if not mac:
|
|
mac = utils.get_random_mac_address()
|
|
data["mac"] = mac
|
|
return HttpResponse(json.dumps(data))
|
|
|
|
|
|
def random_mac_address(request):
|
|
data = dict()
|
|
data["mac"] = utils.get_random_mac_address()
|
|
return HttpResponse(json.dumps(data))
|
|
|
|
|
|
def guess_clone_name(request):
|
|
dhcp_file = "/srv/webvirtcloud/dhcpd.conf"
|
|
prefix = app_settings.CLONE_INSTANCE_DEFAULT_PREFIX
|
|
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:
|
|
for line in f:
|
|
line = line.strip()
|
|
if f"host {prefix}" in line:
|
|
fqdn = line.split(" ")[1]
|
|
hostname = fqdn.split(".")[0]
|
|
if hostname.startswith(prefix) and hostname not in instance_names:
|
|
return HttpResponse(json.dumps({"name": hostname}))
|
|
return HttpResponse(json.dumps({}))
|
|
|
|
|
|
def check_instance(request, vname):
|
|
instance = Instance.objects.filter(name=vname)
|
|
data = {"vname": vname, "exists": False}
|
|
if instance:
|
|
data["exists"] = True
|
|
return JsonResponse(data)
|
|
|
|
|
|
def sshkeys(request, pk):
|
|
"""
|
|
:param request:
|
|
:param vname:
|
|
:return:
|
|
"""
|
|
instance = get_instance(request.user, pk)
|
|
instance_keys = []
|
|
userinstances = UserInstance.objects.filter(instance=instance)
|
|
|
|
for ui in userinstances:
|
|
keys = UserSSHKey.objects.filter(user=ui.user)
|
|
for k in keys:
|
|
instance_keys.append(k.keypublic)
|
|
if request.GET.get("plain", ""):
|
|
response = "\n".join(instance_keys)
|
|
response += "\n"
|
|
else:
|
|
response = json.dumps(instance_keys)
|
|
return HttpResponse(response)
|
|
|
|
|
|
def get_instance(user, pk):
|
|
"""
|
|
Check that instance is available for user, if not raise 404
|
|
"""
|
|
instance = get_object_or_404(Instance, pk=pk)
|
|
user_instances = user.userinstance_set.all().values_list("instance", flat=True)
|
|
|
|
if user.is_superuser or user.has_perm("instances.view_instances") or instance.id in user_instances:
|
|
return instance
|
|
else:
|
|
raise Http404()
|
|
|
|
|
|
def poweron(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
if instance.is_template:
|
|
messages.warning(request, _("Templates cannot be started."))
|
|
else:
|
|
instance.proxy.start()
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, _("Power On"))
|
|
|
|
return redirect(request.META.get("HTTP_REFERER"))
|
|
|
|
|
|
def powercycle(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
instance.proxy.force_shutdown()
|
|
instance.proxy.start()
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, _("Power Cycle"))
|
|
return redirect(request.META.get("HTTP_REFERER"))
|
|
|
|
|
|
def poweroff(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
instance.proxy.shutdown()
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, _("Power Off"))
|
|
|
|
return redirect(request.META.get("HTTP_REFERER"))
|
|
|
|
|
|
@superuser_only
|
|
def suspend(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
instance.proxy.suspend()
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, _("Suspend"))
|
|
return redirect(request.META.get("HTTP_REFERER"))
|
|
|
|
|
|
@superuser_only
|
|
def resume(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
instance.proxy.resume()
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, _("Resume"))
|
|
return redirect(request.META.get("HTTP_REFERER"))
|
|
|
|
|
|
def force_off(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
instance.proxy.force_shutdown()
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, _("Force Off"))
|
|
return redirect(request.META.get("HTTP_REFERER"))
|
|
|
|
|
|
def destroy(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
try:
|
|
userinstance = instance.userinstance_set.get(user=request.user)
|
|
except Exception:
|
|
userinstance = UserInstance(is_delete=request.user.is_superuser)
|
|
|
|
if request.method in ["POST", "DELETE"] and userinstance.is_delete:
|
|
if instance.proxy.get_status() == 1:
|
|
instance.proxy.force_shutdown()
|
|
|
|
if request.POST.get("delete_disk", ""):
|
|
snapshots = sorted(instance.proxy.get_snapshot(), reverse=True, key=lambda k: k["date"])
|
|
for snapshot in snapshots:
|
|
instance.proxy.snapshot_delete(snapshot["name"])
|
|
instance.proxy.delete_all_disks()
|
|
|
|
if request.POST.get("delete_nvram", ""):
|
|
instance.proxy.delete(VIR_DOMAIN_UNDEFINE_NVRAM)
|
|
else:
|
|
instance.proxy.delete(VIR_DOMAIN_UNDEFINE_KEEP_NVRAM)
|
|
|
|
instance.delete()
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, _("Destroy"))
|
|
return redirect(reverse("instances:index"))
|
|
|
|
return render(
|
|
request,
|
|
"instances/destroy_instance_form.html",
|
|
{
|
|
"instance": instance,
|
|
"userinstance": userinstance,
|
|
},
|
|
)
|
|
|
|
|
|
@superuser_only
|
|
def migrate(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
|
|
compute_id = request.POST.get("compute_id", "")
|
|
live = request.POST.get("live_migrate", False)
|
|
unsafe = request.POST.get("unsafe_migrate", False)
|
|
xml_del = request.POST.get("xml_delete", False)
|
|
offline = request.POST.get("offline_migrate", False)
|
|
autoconverge = request.POST.get("autoconverge", False)
|
|
compress = request.POST.get("compress", False)
|
|
postcopy = request.POST.get("postcopy", False)
|
|
|
|
current_host = instance.compute.hostname
|
|
target_host = Compute.objects.get(id=compute_id)
|
|
|
|
try:
|
|
utils.migrate_instance(target_host, instance, request.user, live, unsafe, xml_del, offline, autoconverge, compress, postcopy)
|
|
except libvirtError as err:
|
|
messages.error(request, err)
|
|
|
|
migration_method = "live" if live is True else "offline"
|
|
msg = _("Instance is migrated(%(method)s) to %(hostname)s") % {"hostname": target_host.hostname, "method": migration_method}
|
|
addlogmsg(request.user.username, current_host, instance.name, msg)
|
|
|
|
return redirect(request.META.get("HTTP_REFERER"))
|
|
|
|
|
|
def set_root_pass(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
|
|
if request.method == "POST":
|
|
passwd = request.POST.get("passwd", None)
|
|
if passwd:
|
|
passwd_hash = crypt.crypt(passwd, "$6$kgPoiREy")
|
|
data = {"action": "password", "passwd": passwd_hash, "vname": instance.name}
|
|
|
|
if instance.proxy.get_status() == 5:
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
s.connect((instance.compute.hostname, 16510))
|
|
s.send(bytes(json.dumps(data).encode()))
|
|
d = s.recv(1024).strip()
|
|
result = json.loads(d)
|
|
s.close()
|
|
if result["return"] == "success":
|
|
msg = _("Reset root password")
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
messages.success(request, msg)
|
|
else:
|
|
messages.error(request, result["message"])
|
|
else:
|
|
msg = _("Please shutdown down your instance and then try again")
|
|
messages.error(request, msg)
|
|
return redirect(reverse("instances:instance", args=[instance.id]) + "#access")
|
|
|
|
|
|
def add_public_key(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
if request.method == "POST":
|
|
sshkeyid = request.POST.get("sshkeyid", "")
|
|
publickey = UserSSHKey.objects.get(id=sshkeyid)
|
|
data = {"action": "publickey", "key": publickey.keypublic, "vname": instance.name}
|
|
|
|
if instance.proxy.get_status() == 5:
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
s.connect((instance.compute.hostname, 16510))
|
|
s.send(json.dumps(data).encode())
|
|
result = json.loads(s.recv(1024))
|
|
s.close()
|
|
if result["return"] == "error":
|
|
msg = result["message"]
|
|
else:
|
|
msg = _("Installed new SSH public key %(keyname)s") % {"keyname": publickey.keyname}
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
|
|
if result["return"] == "success":
|
|
messages.success(request, msg)
|
|
else:
|
|
messages.error(request, msg)
|
|
else:
|
|
msg = _("Please shutdown down your instance and then try again")
|
|
messages.error(request, msg)
|
|
return redirect(reverse("instances:instance", args=[instance.id]) + "#access")
|
|
|
|
|
|
def resizevm_cpu(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
try:
|
|
userinstance = instance.userinstance_set.get(user=request.user)
|
|
except Exception:
|
|
userinstance = UserInstance(is_change=False)
|
|
vcpu = instance.proxy.get_vcpu()
|
|
if request.method == "POST":
|
|
if request.user.is_superuser or request.user.is_staff or userinstance.is_change:
|
|
new_vcpu = request.POST.get("vcpu", "")
|
|
new_cur_vcpu = request.POST.get("cur_vcpu", "")
|
|
|
|
quota_msg = utils.check_user_quota(request.user, 0, int(new_vcpu) - vcpu, 0, 0)
|
|
if not request.user.is_superuser and quota_msg:
|
|
msg = _("User %(quota_msg)s quota reached, cannot resize CPU of '%(instance_name)s'!") % {
|
|
"quota_msg": quota_msg,
|
|
"instance_name": instance.name,
|
|
}
|
|
messages.error(request, msg)
|
|
else:
|
|
cur_vcpu = new_cur_vcpu
|
|
vcpu = new_vcpu
|
|
instance.proxy.resize_cpu(cur_vcpu, vcpu)
|
|
msg = _("CPU is resized: %(old)s to %(new)s") % {"old": cur_vcpu, "new": vcpu}
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
messages.success(request, msg)
|
|
return redirect(reverse("instances:instance", args=[instance.id]) + "#resize")
|
|
|
|
|
|
def resize_memory(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
try:
|
|
userinstance = instance.userinstance_set.get(user=request.user)
|
|
except Exception:
|
|
userinstance = UserInstance(is_change=False)
|
|
|
|
memory = instance.proxy.get_memory()
|
|
cur_memory = instance.proxy.get_cur_memory()
|
|
|
|
if request.method == "POST":
|
|
if request.user.is_superuser or request.user.is_staff or userinstance.is_change:
|
|
new_memory = request.POST.get("memory", "")
|
|
new_memory_custom = request.POST.get("memory_custom", "")
|
|
if new_memory_custom:
|
|
new_memory = new_memory_custom
|
|
new_cur_memory = request.POST.get("cur_memory", "")
|
|
new_cur_memory_custom = request.POST.get("cur_memory_custom", "")
|
|
if new_cur_memory_custom:
|
|
new_cur_memory = new_cur_memory_custom
|
|
quota_msg = utils.check_user_quota(request.user, 0, 0, int(new_memory) - memory, 0)
|
|
if not request.user.is_superuser and quota_msg:
|
|
msg = _("User %(quota_msg)s quota reached, cannot resize memory of '%(instance_name)s'!") % {
|
|
"quota_msg": quota_msg,
|
|
"instance_name": instance.name,
|
|
}
|
|
messages.error(request, msg)
|
|
else:
|
|
instance.proxy.resize_mem(new_cur_memory, new_memory)
|
|
msg = _("Memory is resized: current/max: %(old_cur)s/%(old_max)s to %(new_cur)s/%(new_max)s") % {
|
|
"old_cur": cur_memory,
|
|
"old_max": memory,
|
|
"new_cur": new_cur_memory,
|
|
"new_max": new_memory,
|
|
}
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
messages.success(request, msg)
|
|
|
|
return redirect(reverse("instances:instance", args=[instance.id]) + "#resize")
|
|
|
|
|
|
def resize_disk(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
|
|
try:
|
|
userinstance = instance.userinstance_set.get(user=request.user)
|
|
except Exception:
|
|
userinstance = UserInstance(is_change=False)
|
|
|
|
disks = instance.proxy.get_disk_devices()
|
|
|
|
if request.method == "POST":
|
|
if request.user.is_superuser or request.user.is_staff or userinstance.is_change:
|
|
disks_new = list()
|
|
for disk in disks:
|
|
input_disk_size = int(request.POST.get("disk_size_" + disk["dev"], "0")) * 1073741824
|
|
if input_disk_size > disk["size"] + (64 << 20):
|
|
disk["size_new"] = input_disk_size
|
|
disks_new.append(disk)
|
|
disk_sum = sum([disk["size"] >> 30 for disk in disks_new])
|
|
disk_new_sum = sum([disk["size_new"] >> 30 for disk in disks_new])
|
|
quota_msg = utils.check_user_quota(request.user, 0, 0, 0, disk_new_sum - disk_sum)
|
|
if not request.user.is_superuser and quota_msg:
|
|
msg = _("User %(quota_msg)s quota reached, cannot resize disks of '%(instance_name)s'!") % {
|
|
"quota_msg": quota_msg,
|
|
"instance_name": instance.name,
|
|
}
|
|
messages.error(request, msg)
|
|
else:
|
|
instance.proxy.resize_disk(disks_new)
|
|
msg = _("Disk is resized: %(dev)s") % {"dev": disk["dev"]}
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
messages.success(request, msg)
|
|
|
|
return redirect(reverse("instances:instance", args=[instance.id]) + "#resize")
|
|
|
|
|
|
def add_new_vol(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
allow_admin_or_not_template = request.user.is_superuser or request.user.is_staff or not instance.is_template
|
|
|
|
if allow_admin_or_not_template:
|
|
media = instance.proxy.get_media_devices()
|
|
disks = instance.proxy.get_disk_devices()
|
|
conn_create = wvmCreate(
|
|
instance.compute.hostname,
|
|
instance.compute.login,
|
|
instance.compute.password,
|
|
instance.compute.type,
|
|
)
|
|
storage = request.POST.get("storage", "")
|
|
name = request.POST.get("name", "")
|
|
format = request.POST.get("format", app_settings.INSTANCE_VOLUME_DEFAULT_FORMAT)
|
|
size = request.POST.get("size", 0)
|
|
meta_prealloc = True if request.POST.get("meta_prealloc", False) else False
|
|
bus = request.POST.get("bus", app_settings.INSTANCE_VOLUME_DEFAULT_BUS)
|
|
cache = request.POST.get("cache", app_settings.INSTANCE_VOLUME_DEFAULT_CACHE)
|
|
target_dev = utils.get_new_disk_dev(media, disks, bus)
|
|
|
|
source = conn_create.create_volume(
|
|
storage,
|
|
name,
|
|
size,
|
|
format,
|
|
meta_prealloc,
|
|
int(app_settings.INSTANCE_VOLUME_DEFAULT_OWNER_UID),
|
|
int(app_settings.INSTANCE_VOLUME_DEFAULT_OWNER_GID),
|
|
)
|
|
|
|
conn_pool = wvmStorage(
|
|
instance.compute.hostname,
|
|
instance.compute.login,
|
|
instance.compute.password,
|
|
instance.compute.type,
|
|
storage,
|
|
)
|
|
|
|
pool_type = conn_pool.get_type()
|
|
disk_type = conn_pool.get_volume_type(os.path.basename(source))
|
|
|
|
if pool_type == 'rbd':
|
|
source_info = conn_pool.get_rbd_source()
|
|
else: # add more disk types to handle different pool and disk types
|
|
source_info = None
|
|
|
|
instance.proxy.attach_disk(target_dev, source, source_info=source_info, pool_type=pool_type, disk_type=disk_type, target_bus=bus, format_type=format, cache_mode=cache)
|
|
msg = _("Attach new disk: %(name)s (%(format)s)") % {"name": name, "format": format}
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
return redirect(request.META.get("HTTP_REFERER") + "#disks")
|
|
|
|
|
|
def add_existing_vol(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
allow_admin_or_not_template = request.user.is_superuser or request.user.is_staff or not instance.is_template
|
|
if allow_admin_or_not_template:
|
|
storage = request.POST.get("selected_storage", "")
|
|
name = request.POST.get("vols", "")
|
|
bus = request.POST.get("bus", app_settings.INSTANCE_VOLUME_DEFAULT_BUS)
|
|
cache = request.POST.get("cache", app_settings.INSTANCE_VOLUME_DEFAULT_CACHE)
|
|
|
|
media = instance.proxy.get_media_devices()
|
|
disks = instance.proxy.get_disk_devices()
|
|
|
|
conn_create = wvmStorage(
|
|
instance.compute.hostname,
|
|
instance.compute.login,
|
|
instance.compute.password,
|
|
instance.compute.type,
|
|
storage,
|
|
)
|
|
|
|
format_type = conn_create.get_volume_format_type(name)
|
|
disk_type = conn_create.get_volume_type(name)
|
|
pool_type = conn_create.get_type()
|
|
if pool_type == 'rbd':
|
|
source_info = conn_create.get_rbd_source()
|
|
path = conn_create.get_source_name()
|
|
else:
|
|
source_info = None
|
|
path = conn_create.get_target_path()
|
|
|
|
target_dev = utils.get_new_disk_dev(media, disks, bus)
|
|
source = f"{path}/{name}"
|
|
|
|
instance.proxy.attach_disk(target_dev, source, source_info=source_info, pool_type=pool_type, disk_type=disk_type, target_bus=bus, format_type=format_type, cache_mode=cache)
|
|
msg = _("Attach Existing disk: %(target_dev)s") % {"target_dev": target_dev}
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
return redirect(request.META.get("HTTP_REFERER") + "#disks")
|
|
|
|
|
|
def edit_volume(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
allow_admin_or_not_template = request.user.is_superuser or request.user.is_staff or not instance.is_template
|
|
if "edit_volume" in request.POST and allow_admin_or_not_template:
|
|
target_dev = request.POST.get("dev", "")
|
|
|
|
new_path = request.POST.get("vol_path", "")
|
|
shareable = bool(request.POST.get("vol_shareable", False))
|
|
readonly = bool(request.POST.get("vol_readonly", False))
|
|
disk_type = request.POST.get("vol_type", "")
|
|
bus = request.POST.get("vol_bus_old", "")
|
|
new_bus = request.POST.get("vol_bus", bus)
|
|
serial = request.POST.get("vol_serial", "")
|
|
format = request.POST.get("vol_format", "")
|
|
cache = request.POST.get("vol_cache", app_settings.INSTANCE_VOLUME_DEFAULT_CACHE)
|
|
io = request.POST.get("vol_io_mode", app_settings.INSTANCE_VOLUME_DEFAULT_IO)
|
|
discard = request.POST.get("vol_discard_mode", app_settings.INSTANCE_VOLUME_DEFAULT_DISCARD)
|
|
zeroes = request.POST.get("vol_detect_zeroes", app_settings.INSTANCE_VOLUME_DEFAULT_DETECT_ZEROES)
|
|
new_target_dev = utils.get_new_disk_dev(instance.media, instance.disks, new_bus)
|
|
|
|
if new_bus != bus:
|
|
instance.proxy.detach_disk(target_dev)
|
|
instance.proxy.attach_disk(
|
|
new_target_dev,
|
|
new_path,
|
|
target_bus=new_bus,
|
|
driver_type=format,
|
|
cache_mode=cache,
|
|
readonly=readonly,
|
|
shareable=shareable,
|
|
serial=serial,
|
|
io_mode=io,
|
|
discard_mode=discard,
|
|
detect_zeroes_mode=zeroes,
|
|
)
|
|
else:
|
|
instance.proxy.edit_disk(
|
|
target_dev,
|
|
new_path,
|
|
readonly,
|
|
shareable,
|
|
new_bus,
|
|
serial,
|
|
format,
|
|
cache,
|
|
io,
|
|
discard,
|
|
zeroes,
|
|
)
|
|
|
|
if not instance.proxy.get_status() == 5:
|
|
messages.success(
|
|
request,
|
|
_("Volume changes are applied. " + "But it will be activated after shutdown"),
|
|
)
|
|
else:
|
|
messages.success(request, _("Volume is changed successfully."))
|
|
msg = _("Edit disk: %(target_dev)s") % {"target_dev": target_dev}
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
|
|
return redirect(request.META.get("HTTP_REFERER") + "#disks")
|
|
|
|
|
|
def delete_vol(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
allow_admin_or_not_template = request.user.is_superuser or request.user.is_staff or not instance.is_template
|
|
if allow_admin_or_not_template:
|
|
storage = request.POST.get("storage", "")
|
|
conn_delete = wvmStorage(
|
|
instance.compute.hostname,
|
|
instance.compute.login,
|
|
instance.compute.password,
|
|
instance.compute.type,
|
|
storage,
|
|
)
|
|
dev = request.POST.get("dev", "")
|
|
path = request.POST.get("path", "")
|
|
name = request.POST.get("name", "")
|
|
|
|
msg = _("Delete disk: %(dev)s") % {"dev": dev}
|
|
instance.proxy.detach_disk(dev)
|
|
conn_delete.del_volume(name)
|
|
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
return redirect(request.META.get("HTTP_REFERER") + "#disks")
|
|
|
|
|
|
def detach_vol(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
allow_admin_or_not_template = request.user.is_superuser or request.user.is_staff or not instance.is_template
|
|
|
|
if allow_admin_or_not_template:
|
|
dev = request.POST.get("dev", "")
|
|
path = request.POST.get("path", "")
|
|
instance.proxy.detach_disk(dev)
|
|
msg = _("Detach disk: %(dev)s") % {"dev": dev}
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
|
|
return redirect(request.META.get("HTTP_REFERER") + "#disks")
|
|
|
|
|
|
def add_cdrom(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
allow_admin_or_not_template = request.user.is_superuser or request.user.is_staff or not instance.is_template
|
|
if allow_admin_or_not_template:
|
|
bus = request.POST.get("bus", "ide" if instance.machine == "pc" else "sata")
|
|
target = utils.get_new_disk_dev(instance.media, instance.disks, bus)
|
|
instance.proxy.attach_disk(target, "", disk_device="cdrom", cache_mode="none", target_bus=bus, readonly=True)
|
|
msg = _("Add CD-ROM: %(target)s") % {"target": target}
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
|
|
return redirect(request.META.get("HTTP_REFERER") + "#disks")
|
|
|
|
|
|
def detach_cdrom(request, pk, dev):
|
|
instance = get_instance(request.user, pk)
|
|
allow_admin_or_not_template = request.user.is_superuser or request.user.is_staff or not instance.is_template
|
|
|
|
if allow_admin_or_not_template:
|
|
# dev = request.POST.get('detach_cdrom', '')
|
|
instance.proxy.detach_disk(dev)
|
|
msg = _("Detach CD-ROM: %(dev)s") % {"dev": dev}
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
|
|
return redirect(request.META.get("HTTP_REFERER") + "#disks")
|
|
|
|
|
|
def unmount_iso(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
allow_admin_or_not_template = request.user.is_superuser or request.user.is_staff or not instance.is_template
|
|
if allow_admin_or_not_template:
|
|
image = request.POST.get("path", "")
|
|
dev = request.POST.get("umount_iso", "")
|
|
instance.proxy.umount_iso(dev, image)
|
|
msg = _("Mount media: %(dev)s") % {"dev": dev}
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
|
|
return redirect(request.META.get("HTTP_REFERER") + "#disks")
|
|
|
|
|
|
def mount_iso(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
allow_admin_or_not_template = request.user.is_superuser or request.user.is_staff or not instance.is_template
|
|
if allow_admin_or_not_template:
|
|
image = request.POST.get("media", "")
|
|
dev = request.POST.get("mount_iso", "")
|
|
instance.proxy.mount_iso(dev, image)
|
|
msg = _("Unmount media: %(dev)s") % {"dev": dev}
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
|
|
return redirect(request.META.get("HTTP_REFERER") + "#disks")
|
|
|
|
|
|
def snapshot(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
allow_admin_or_not_template = request.user.is_superuser or request.user.is_staff or not instance.is_template
|
|
|
|
if allow_admin_or_not_template and request.user.has_perm("instances.snapshot_instances"):
|
|
name = request.POST.get("name", "")
|
|
instance.proxy.create_snapshot(name)
|
|
msg = _("Create snapshot: %(snap)s") % {"snap": name}
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
return redirect(request.META.get("HTTP_REFERER") + "#managesnapshot")
|
|
|
|
|
|
def delete_snapshot(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
allow_admin_or_not_template = request.user.is_superuser or request.user.is_staff or not instance.is_template
|
|
if allow_admin_or_not_template and request.user.has_perm("instances.snapshot_instances"):
|
|
snap_name = request.POST.get("name", "")
|
|
instance.proxy.snapshot_delete(snap_name)
|
|
msg = _("Delete snapshot: %(snap)s") % {"snap": snap_name}
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
return redirect(request.META.get("HTTP_REFERER") + "#managesnapshot")
|
|
|
|
|
|
def revert_snapshot(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
allow_admin_or_not_template = request.user.is_superuser or request.user.is_staff or not instance.is_template
|
|
if allow_admin_or_not_template and request.user.has_perm("instances.snapshot_instances"):
|
|
snap_name = request.POST.get("name", "")
|
|
instance.proxy.snapshot_revert(snap_name)
|
|
msg = _("Successful revert snapshot: ")
|
|
msg += snap_name
|
|
messages.success(request, msg)
|
|
msg = _("Revert snapshot: %(snap)s") % {"snap": snap_name}
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
return redirect(request.META.get("HTTP_REFERER") + "#managesnapshot")
|
|
|
|
|
|
@superuser_only
|
|
def set_vcpu(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
id = request.POST.get("id", "")
|
|
enabled = request.POST.get("set_vcpu", "")
|
|
if enabled == "True":
|
|
instance.proxy.set_vcpu(id, 1)
|
|
else:
|
|
instance.proxy.set_vcpu(id, 0)
|
|
msg = _("VCPU %(id)s is enabled=%(enabled)s") % {"id": id, "enabled": enabled}
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
return redirect(request.META.get("HTTP_REFERER") + "#resize")
|
|
|
|
|
|
@superuser_only
|
|
def set_vcpu_hotplug(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
status = True if request.POST.get("vcpu_hotplug", "False") == 'True' else False
|
|
msg = _("VCPU Hot-plug is enabled=%(status)s") % {"status": status}
|
|
instance.proxy.set_vcpu_hotplug(status)
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
return redirect(request.META.get("HTTP_REFERER") + "#resize")
|
|
|
|
|
|
@superuser_only
|
|
def set_autostart(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
instance.proxy.set_autostart(1)
|
|
msg = _("Set autostart")
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
return redirect(request.META.get("HTTP_REFERER") + "#boot_opt")
|
|
|
|
|
|
@superuser_only
|
|
def unset_autostart(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
instance.proxy.set_autostart(0)
|
|
msg = _("Unset autostart")
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
return redirect(request.META.get("HTTP_REFERER") + "#boot_opt")
|
|
|
|
|
|
@superuser_only
|
|
def set_bootmenu(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
instance.proxy.set_bootmenu(1)
|
|
msg = _("Enable boot menu")
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
return redirect(request.META.get("HTTP_REFERER") + "#boot_opt")
|
|
|
|
|
|
@superuser_only
|
|
def unset_bootmenu(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
instance.proxy.set_bootmenu(0)
|
|
msg = _("Disable boot menu")
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
return redirect(request.META.get("HTTP_REFERER") + "#boot_opt")
|
|
|
|
|
|
@superuser_only
|
|
def set_bootorder(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
bootorder = request.POST.get("bootorder", "")
|
|
if bootorder:
|
|
order_list = {}
|
|
for idx, val in enumerate(bootorder.split(",")):
|
|
dev_type, dev = val.split(":", 1)
|
|
order_list[idx] = {"type": dev_type, "dev": dev}
|
|
instance.proxy.set_bootorder(order_list)
|
|
msg = _("Set boot order")
|
|
|
|
if not instance.proxy.get_status() == 5:
|
|
messages.success(
|
|
request,
|
|
_("Boot menu changes applied. " + "But it will be activated after shutdown"),
|
|
)
|
|
else:
|
|
messages.success(request, _("Boot order changed successfully."))
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
return redirect(request.META.get("HTTP_REFERER") + "#boot_opt")
|
|
|
|
|
|
@superuser_only
|
|
def change_xml(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
new_xml = request.POST.get("inst_xml", "")
|
|
if new_xml:
|
|
instance.proxy._defineXML(new_xml)
|
|
msg = _("Change instance XML")
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
return redirect(request.META.get("HTTP_REFERER") + "#xmledit")
|
|
|
|
|
|
@superuser_only
|
|
def set_guest_agent(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
status = request.POST.get("guest_agent")
|
|
if status == "True":
|
|
instance.proxy.add_guest_agent()
|
|
if status == "False":
|
|
instance.proxy.remove_guest_agent()
|
|
|
|
msg = _("Set Guest Agent: %(status)s") % {"status": status}
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
return redirect(request.META.get("HTTP_REFERER") + "#options")
|
|
|
|
|
|
@superuser_only
|
|
def set_video_model(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
video_model = request.POST.get("video_model", "vga")
|
|
instance.proxy.set_video_model(video_model)
|
|
msg = _("Set Video Model: %(model)s") % {"model": video_model}
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
return redirect(request.META.get("HTTP_REFERER") + "#options")
|
|
|
|
|
|
@superuser_only
|
|
def change_network(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
|
|
msg = _("Change network")
|
|
network_data = {}
|
|
|
|
for post in request.POST:
|
|
if post.startswith("net-source-"):
|
|
(source, source_type) = utils.get_network_tuple(request.POST.get(post))
|
|
network_data[post] = source
|
|
network_data[post + "-type"] = source_type
|
|
|
|
if source_type == 'iface':
|
|
iface = wvmInterface(
|
|
instance.compute.hostname,
|
|
instance.compute.login,
|
|
instance.compute.password,
|
|
instance.compute.type,
|
|
source,
|
|
)
|
|
network_data[post + "-type"] = iface.get_type()
|
|
elif post.startswith("net-"):
|
|
network_data[post] = request.POST.get(post, "")
|
|
|
|
instance.proxy.change_network(network_data)
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
msg = _("Network Device Config is changed. Please shutdown instance to activate.")
|
|
if instance.proxy.get_status() != 5:
|
|
messages.success(request, msg)
|
|
return redirect(request.META.get("HTTP_REFERER") + "#network")
|
|
|
|
|
|
@superuser_only
|
|
def add_network(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
|
|
mac = request.POST.get("add-net-mac")
|
|
nwfilter = request.POST.get("add-net-nwfilter")
|
|
(source, source_type) = utils.get_network_tuple(request.POST.get("add-net-network"))
|
|
|
|
if source_type == 'iface':
|
|
iface = wvmInterface(
|
|
instance.compute.hostname,
|
|
instance.compute.login,
|
|
instance.compute.password,
|
|
instance.compute.type,
|
|
source,
|
|
)
|
|
source_type = iface.get_type()
|
|
|
|
instance.proxy.add_network(mac, source, source_type, nwfilter=nwfilter)
|
|
msg = _("Add network: %(mac)s") % {"mac": mac}
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
return redirect(request.META.get("HTTP_REFERER") + "#network")
|
|
|
|
|
|
@superuser_only
|
|
def delete_network(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
mac_address = request.POST.get("delete_network", "")
|
|
|
|
instance.proxy.delete_network(mac_address)
|
|
msg = _("Delete Network: %(mac)s") % {"mac": mac_address}
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
return redirect(request.META.get("HTTP_REFERER") + "#network")
|
|
|
|
|
|
@superuser_only
|
|
def set_link_state(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
|
|
mac_address = request.POST.get("mac", "")
|
|
state = request.POST.get("set_link_state")
|
|
state = "down" if state == "up" else "up"
|
|
instance.proxy.set_link_state(mac_address, state)
|
|
msg = _("Set Link State: %(state)s") % {"state": state}
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
return redirect(request.META.get("HTTP_REFERER") + "#network")
|
|
|
|
|
|
@superuser_only
|
|
def set_qos(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
|
|
qos_dir = request.POST.get("qos_direction", "")
|
|
average = request.POST.get("qos_average") or 0
|
|
peak = request.POST.get("qos_peak") or 0
|
|
burst = request.POST.get("qos_burst") or 0
|
|
keys = request.POST.keys()
|
|
mac_key = [key for key in keys if "mac" in key]
|
|
if mac_key:
|
|
mac = request.POST.get(mac_key[0])
|
|
|
|
instance.proxy.set_qos(mac, qos_dir, average, peak, burst)
|
|
if instance.proxy.get_status() == 5:
|
|
messages.success(request, _("%(qos_dir)s QoS is set") % {"qos_dir": qos_dir.capitalize()})
|
|
else:
|
|
messages.success(
|
|
request,
|
|
_(
|
|
"%(qos_dir)s QoS is set. Network XML is changed. \
|
|
Stop and start network to activate new config."
|
|
)
|
|
% {"qos_dir": qos_dir.capitalize()},
|
|
)
|
|
|
|
return redirect(request.META.get("HTTP_REFERER") + "#network")
|
|
|
|
|
|
@superuser_only
|
|
def unset_qos(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
qos_dir = request.POST.get("qos_direction", "")
|
|
mac = request.POST.get("net-mac")
|
|
instance.proxy.unset_qos(mac, qos_dir)
|
|
|
|
if instance.proxy.get_status() == 5:
|
|
messages.success(request, _("%(qos_dir)s QoS is deleted") % {"qos_dir": qos_dir.capitalize()})
|
|
else:
|
|
messages.success(
|
|
request,
|
|
_(
|
|
"%(qos_dir)s QoS is deleted. Network XML is changed. \
|
|
Stop and start network to activate new config."
|
|
)
|
|
% {"qos_dir": qos_dir.capitalize()},
|
|
)
|
|
return redirect(request.META.get("HTTP_REFERER") + "#network")
|
|
|
|
|
|
@superuser_only
|
|
def add_owner(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
user_id = request.POST.get("user_id")
|
|
|
|
check_inst = 0
|
|
|
|
if app_settings.ALLOW_INSTANCE_MULTIPLE_OWNER == "False":
|
|
check_inst = UserInstance.objects.filter(instance=instance).count()
|
|
|
|
if check_inst > 0:
|
|
messages.error(request, _("Only one owner is allowed and the one already added"))
|
|
else:
|
|
add_user_inst = UserInstance(instance=instance, user_id=user_id)
|
|
add_user_inst.save()
|
|
user = User.objects.get(id=user_id)
|
|
msg = _("Add owner: %(user)s") % {"user": user}
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
return redirect(request.META.get("HTTP_REFERER") + "#users")
|
|
|
|
|
|
@superuser_only
|
|
def del_owner(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
userinstance_id = int(request.POST.get("userinstance", ""))
|
|
userinstance = UserInstance.objects.get(pk=userinstance_id)
|
|
userinstance.delete()
|
|
msg = _("Delete owner: %(userinstance_id)s ") % {"userinstance_id": userinstance_id}
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
return redirect(request.META.get("HTTP_REFERER") + "#users")
|
|
|
|
|
|
@permission_required("instances.clone_instances", raise_exception=True)
|
|
def clone(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
|
|
clone_data = dict()
|
|
clone_data["name"] = request.POST.get("name", "")
|
|
|
|
disk_sum = sum([disk["size"] >> 30 for disk in instance.disks])
|
|
quota_msg = utils.check_user_quota(request.user, 1, instance.vcpu, instance.memory, disk_sum)
|
|
check_instance = Instance.objects.filter(name=clone_data["name"])
|
|
|
|
clone_data["disk_owner_uid"] = int(app_settings.INSTANCE_VOLUME_DEFAULT_OWNER_UID)
|
|
clone_data["disk_owner_gid"] = int(app_settings.INSTANCE_VOLUME_DEFAULT_OWNER_GID)
|
|
|
|
for post in request.POST:
|
|
clone_data[post] = request.POST.get(post, "").strip()
|
|
|
|
if app_settings.CLONE_INSTANCE_AUTO_NAME == "True" and not clone_data["name"]:
|
|
auto_vname = utils.get_clone_free_names()[0]
|
|
clone_data["name"] = auto_vname
|
|
clone_data["clone-net-mac-0"] = utils.get_dhcp_mac_address(auto_vname)
|
|
for disk in instance.disks:
|
|
disk_dev = f"disk-{disk['dev']}"
|
|
disk_name = utils.get_clone_disk_name(disk, instance.name, auto_vname)
|
|
clone_data[disk_dev] = disk_name
|
|
|
|
if not request.user.is_superuser and quota_msg:
|
|
msg = _("User '%(quota_msg)s' quota reached, cannot create '%(clone_name)s'!") % {
|
|
"quota_msg": quota_msg,
|
|
"clone_name": clone_data["name"],
|
|
}
|
|
messages.error(request, msg)
|
|
elif check_instance:
|
|
msg = _("Instance '%(clone_name)s' already exists!") % {"clone_name": clone_data["name"]}
|
|
messages.error(request, msg)
|
|
elif not re.match(r"^[a-zA-Z0-9-]+$", clone_data["name"]):
|
|
msg = _("Instance name '%(clone_name)s' contains invalid characters!") % {"clone_name": clone_data["name"]}
|
|
messages.error(request, msg)
|
|
elif not re.match(r"^([0-9A-F]{2})(:?[0-9A-F]{2}){5}$", clone_data["clone-net-mac-0"], re.IGNORECASE):
|
|
msg = _("Instance MAC '%(clone_mac)s' invalid format!") % {"clone_mac": clone_data["clone-net-mac-0"]}
|
|
messages.error(request, msg)
|
|
else:
|
|
new_instance = Instance(compute=instance.compute, name=clone_data["name"])
|
|
try:
|
|
new_uuid = instance.proxy.clone_instance(clone_data)
|
|
new_instance.uuid = new_uuid
|
|
new_instance.save()
|
|
user_instance = UserInstance(instance_id=new_instance.id, user_id=request.user.id, is_delete=True)
|
|
user_instance.save()
|
|
msg = _("Create a clone of '%(instance_name)s'") % {"instance_name": instance.name}
|
|
messages.success(request, msg)
|
|
addlogmsg(request.user.username, instance.compute.name, new_instance.name, msg)
|
|
|
|
if app_settings.CLONE_INSTANCE_AUTO_MIGRATE == "True":
|
|
new_compute = Compute.objects.order_by("?").first()
|
|
utils.migrate_instance(new_compute, new_instance, request.user, xml_del=True, offline=True)
|
|
|
|
return redirect(reverse("instances:instance", args=[new_instance.id]))
|
|
except Exception as e:
|
|
messages.error(request, e)
|
|
|
|
return redirect(request.META.get("HTTP_REFERER") + "#clone")
|
|
|
|
|
|
def update_console(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
try:
|
|
userinstance = instance.userinstance_set.get(user=request.user)
|
|
except Exception:
|
|
userinstance = UserInstance(is_vnc=False)
|
|
|
|
if request.user.is_superuser or request.user.is_staff or userinstance.is_vnc:
|
|
form = ConsoleForm(request.POST or None)
|
|
if form.is_valid():
|
|
if (
|
|
"generate_password" in form.changed_data
|
|
or "clear_password" in form.changed_data
|
|
or "password" in form.changed_data
|
|
):
|
|
if form.cleaned_data["generate_password"]:
|
|
password = randomPasswd()
|
|
elif form.cleaned_data["clear_password"]:
|
|
password = ""
|
|
else:
|
|
password = form.cleaned_data["password"]
|
|
|
|
if not instance.proxy.set_console_passwd(password):
|
|
msg = _(
|
|
"Error setting console password. "
|
|
+ "You should check that your instance have an graphic device."
|
|
)
|
|
messages.error(request, msg)
|
|
else:
|
|
msg = _("Set VNC password")
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
|
|
if "keymap" in form.changed_data or "clear_keymap" in form.changed_data:
|
|
if form.cleaned_data["clear_keymap"]:
|
|
instance.proxy.set_console_keymap("")
|
|
else:
|
|
instance.proxy.set_console_keymap(form.cleaned_data["keymap"])
|
|
|
|
msg = _("Set VNC keymap")
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
|
|
if "type" in form.changed_data:
|
|
instance.proxy.set_console_type(form.cleaned_data["type"])
|
|
msg = _("Set VNC type")
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
|
|
if "listen_on" in form.changed_data:
|
|
instance.proxy.set_console_listener_addr(form.cleaned_data["listen_on"])
|
|
msg = _("Set VNC listen address")
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
|
|
return redirect(request.META.get("HTTP_REFERER") + "#vncsettings")
|
|
|
|
|
|
def change_options(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
try:
|
|
userinstance = instance.userinstance_set.get(user=request.user)
|
|
except Exception:
|
|
userinstance = UserInstance(is_change=False)
|
|
|
|
if request.user.is_superuser or request.user.is_staff or userinstance.is_change:
|
|
instance.is_template = request.POST.get("is_template", False)
|
|
instance.save()
|
|
|
|
options = {}
|
|
for post in request.POST:
|
|
if post in ["title", "description"]:
|
|
options[post] = request.POST.get(post, "")
|
|
instance.proxy.set_options(options)
|
|
|
|
msg = _("Edit options")
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
return redirect(request.META.get("HTTP_REFERER") + "#options")
|
|
|
|
|
|
def getvvfile(request, pk):
|
|
instance = get_instance(request.user, pk)
|
|
conn = wvmInstances(
|
|
instance.compute.hostname,
|
|
instance.compute.login,
|
|
instance.compute.password,
|
|
instance.compute.type,
|
|
)
|
|
|
|
msg = _("Send console.vv file")
|
|
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
|
|
response = HttpResponse(
|
|
content="",
|
|
content_type="application/x-virt-viewer",
|
|
status=200,
|
|
reason=None,
|
|
charset="utf-8",
|
|
)
|
|
response.writelines("[virt-viewer]\n")
|
|
response.writelines("type=" + conn.graphics_type(instance.name) + "\n")
|
|
if conn.graphics_listen(instance.name) == "0.0.0.0":
|
|
response.writelines("host=" + conn.host + "\n")
|
|
else:
|
|
response.writelines("host=" + conn.graphics_listen(instance.name) + "\n")
|
|
response.writelines("port=" + conn.graphics_port(instance.name) + "\n")
|
|
response.writelines("title=" + conn.domain_name(instance.name) + "\n")
|
|
response.writelines("password=" + conn.graphics_passwd(instance.name) + "\n")
|
|
response.writelines("enable-usbredir=1\n")
|
|
response.writelines("disable-effects=all\n")
|
|
response.writelines("secure-attention=ctrl+alt+ins\n")
|
|
response.writelines("release-cursor=ctrl+alt\n")
|
|
response.writelines("fullscreen=1\n")
|
|
response.writelines("delete-this-file=1\n")
|
|
response["Content-Disposition"] = 'attachment; filename="console.vv"'
|
|
return response
|
|
|
|
|
|
@superuser_only
|
|
def create_instance_select_type(request, compute_id):
|
|
"""
|
|
:param request:
|
|
:param compute_id:
|
|
:return:
|
|
"""
|
|
|
|
conn = None
|
|
storages = list()
|
|
networks = list()
|
|
hypervisors = list()
|
|
meta_prealloc = False
|
|
compute = get_object_or_404(Compute, pk=compute_id)
|
|
|
|
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)
|
|
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 = app_settings.INSTANCE_MACHINE_DEFAULT_TYPE
|
|
default_arch = app_settings.INSTANCE_ARCH_DEFAULT_TYPE
|
|
|
|
if request.method == "POST":
|
|
if "create_xml" in request.POST:
|
|
xml = request.POST.get("dom_xml", "")
|
|
try:
|
|
name = util.get_xml_path(xml, "/domain/name")
|
|
except util.etree.Error:
|
|
name = None
|
|
if name in instances:
|
|
error_msg = _("A virtual machine with this name already exists")
|
|
messages.error(request, error_msg)
|
|
else:
|
|
conn._defineXML(xml)
|
|
utils.refr(compute)
|
|
instance = compute.instance_set.get(name=name)
|
|
return redirect(reverse("instances:instance", args=[instance.id]))
|
|
|
|
return render(request, "create_instance_w1.html", locals())
|
|
|
|
|
|
@superuser_only
|
|
def create_instance(request, compute_id, arch, machine):
|
|
"""
|
|
:param request:
|
|
:param compute_id:
|
|
:param arch:
|
|
:param machine:
|
|
:return:
|
|
"""
|
|
|
|
conn = None
|
|
storages = list()
|
|
networks = list()
|
|
hypervisors = list()
|
|
firmwares = list()
|
|
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 = app_settings.INSTANCE_FIRMWARE_DEFAULT_TYPE
|
|
default_cpu_mode = app_settings.INSTANCE_CPU_DEFAULT_MODE
|
|
instances = conn.get_instances()
|
|
videos = conn.get_video_models(arch, machine)
|
|
cache_modes = sorted(conn.get_cache_modes().items())
|
|
default_cache = app_settings.INSTANCE_VOLUME_DEFAULT_CACHE
|
|
default_io = app_settings.INSTANCE_VOLUME_DEFAULT_IO
|
|
default_zeroes = app_settings.INSTANCE_VOLUME_DEFAULT_DETECT_ZEROES
|
|
default_discard = app_settings.INSTANCE_VOLUME_DEFAULT_DISCARD
|
|
default_disk_format = app_settings.INSTANCE_VOLUME_DEFAULT_FORMAT
|
|
default_disk_owner_uid = int(app_settings.INSTANCE_VOLUME_DEFAULT_OWNER_UID)
|
|
default_disk_owner_gid = int(app_settings.INSTANCE_VOLUME_DEFAULT_OWNER_GID)
|
|
default_scsi_disk_model = app_settings.INSTANCE_VOLUME_DEFAULT_SCSI_CONTROLLER
|
|
listener_addr = settings.QEMU_CONSOLE_LISTENER_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 = app_settings.INSTANCE_VOLUME_DEFAULT_BUS
|
|
networks = sorted(conn.get_networks())
|
|
nwfilters = conn.get_nwfilters()
|
|
storages = sorted(conn.get_storages(only_actives=True))
|
|
default_graphics = app_settings.QEMU_CONSOLE_DEFAULT_TYPE
|
|
|
|
dom_caps = conn.get_dom_capabilities(arch, machine)
|
|
caps = conn.get_capabilities(arch)
|
|
|
|
virtio_support = conn.is_supports_virtio(arch, machine)
|
|
hv_supports_uefi = conn.supports_uefi_xml(dom_caps["loader_enums"])
|
|
# Add BIOS
|
|
label = conn.label_for_firmware_path(arch, None)
|
|
if label:
|
|
firmwares.append(label)
|
|
# Add UEFI
|
|
loader_path = conn.find_uefi_path_for_arch(arch, dom_caps["loaders"])
|
|
label = conn.label_for_firmware_path(arch, loader_path)
|
|
if label:
|
|
firmwares.append(label)
|
|
firmwares = list(set(firmwares))
|
|
|
|
flavor_form = FlavorForm()
|
|
|
|
if conn:
|
|
if not storages:
|
|
raise libvirtError(_("You haven't defined any storage pools"))
|
|
if not networks:
|
|
raise libvirtError(_("You haven't defined any network pools"))
|
|
|
|
if request.method == "POST":
|
|
if "create" in request.POST:
|
|
firmware = dict()
|
|
volume_list = list()
|
|
is_disk_created = False
|
|
clone_path = ""
|
|
form = NewVMForm(request.POST)
|
|
if form.is_valid():
|
|
data = form.cleaned_data
|
|
if data["meta_prealloc"]:
|
|
meta_prealloc = True
|
|
if instances:
|
|
if data["name"] in instances:
|
|
raise libvirtError(_("A virtual machine with this name already exists"))
|
|
if Instance.objects.filter(name__exact=data["name"]):
|
|
raise libvirtError(_("There is an instance with same name. Remove it and try again!"))
|
|
|
|
if data["hdd_size"]:
|
|
if not data["mac"]:
|
|
raise libvirtError(_("No Virtual Machine MAC has been entered"))
|
|
else:
|
|
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
|
|
volume["type"] = conn.get_volume_format_type(path)
|
|
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
|
|
|
|
elif data["template"]:
|
|
templ_path = conn.get_volume_path(data["template"])
|
|
dest_vol = conn.get_volume_path(data["name"] + ".img", data["storage"])
|
|
if dest_vol:
|
|
raise libvirtError(
|
|
_("Image has already exist. Please check volumes or change instance name")
|
|
)
|
|
else:
|
|
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_format_type(clone_path)
|
|
volume["device"] = "disk"
|
|
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:
|
|
if not data["images"]:
|
|
raise libvirtError(_("First you need to create or select an image"))
|
|
else:
|
|
for idx, vol in enumerate(data["images"].split(",")):
|
|
path = conn.get_volume_path(vol)
|
|
volume = dict()
|
|
volume["path"] = path
|
|
volume["type"] = conn.get_volume_format_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)
|
|
if data["cache_mode"] not in conn.get_cache_modes():
|
|
error_msg = _("Invalid cache mode")
|
|
raise libvirtError
|
|
|
|
if "UEFI" in data["firmware"]:
|
|
firmware["loader"] = data["firmware"].split(":")[1].strip()
|
|
firmware["secure"] = "no"
|
|
firmware["readonly"] = "yes"
|
|
firmware["type"] = "pflash"
|
|
if "secboot" in firmware["loader"] and machine != "q35":
|
|
messages.warning(
|
|
request,
|
|
"Changing machine type from '%s' to 'q35' "
|
|
"which is required for UEFI secure boot." % machine,
|
|
)
|
|
machine = "q35"
|
|
firmware["secure"] = "yes"
|
|
|
|
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,
|
|
firmware=firmware,
|
|
volumes=volume_list,
|
|
networks=data["networks"],
|
|
virtio=data["virtio"],
|
|
listener_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()
|
|
msg = _("Instance is created")
|
|
messages.success(request, msg)
|
|
addlogmsg(request.user.username, create_instance.compute.name, create_instance.name, msg)
|
|
return redirect(reverse("instances:instance", args=[create_instance.id]))
|
|
except libvirtError as lib_err:
|
|
if data["hdd_size"] or len(volume_list) > 0:
|
|
if is_disk_created:
|
|
for vol in volume_list:
|
|
conn.delete_volume(vol["path"])
|
|
messages.error(request, lib_err)
|
|
conn.close()
|
|
except libvirtError as lib_err:
|
|
messages.error(request, lib_err)
|
|
|
|
return render(request, "create_instance_w2.html", locals())
|
|
|
|
|
|
@superuser_only
|
|
def flavor_create(request):
|
|
form = FlavorForm(request.POST or None)
|
|
if form.is_valid():
|
|
form.save()
|
|
messages.success(request, _("Flavor Created"))
|
|
return redirect(request.META.get("HTTP_REFERER"))
|
|
|
|
return render(
|
|
request,
|
|
"common/form.html",
|
|
{"form": form, "title": _("Create Flavor")},
|
|
)
|
|
|
|
|
|
@superuser_only
|
|
def flavor_update(request, pk):
|
|
flavor = get_object_or_404(Flavor, pk=pk)
|
|
form = FlavorForm(request.POST or None, instance=flavor)
|
|
if form.is_valid():
|
|
form.save()
|
|
messages.success(request, _("Flavor Updated"))
|
|
return redirect(request.META.get("HTTP_REFERER"))
|
|
|
|
return render(
|
|
request,
|
|
"common/form.html",
|
|
{"form": form, "title": _("Update Flavor")},
|
|
)
|
|
|
|
|
|
@superuser_only
|
|
def flavor_delete(request, pk):
|
|
flavor = get_object_or_404(Flavor, pk=pk)
|
|
if request.method == "POST":
|
|
flavor.delete()
|
|
messages.success(request, _("Flavor Deleted"))
|
|
return redirect(request.META.get("HTTP_REFERER"))
|
|
|
|
return render(
|
|
request,
|
|
"common/confirm_delete.html",
|
|
{"object": flavor},
|
|
)
|