import crypt
import json
import os
import re
import socket
import time
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:
        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_listen_address,
            "password": instance.console_passwd,
            "keymap": instance.console_keymap,
        }
    )
    console_listen_addresses = settings.QEMU_CONSOLE_LISTEN_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.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.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()

    return render(request, "instance.html", locals())


def status(request, pk):
    instance = get_instance(request.user, pk)
    return JsonResponse({"status": instance.proxy.get_status()})


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 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.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.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.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.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.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.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 == "POST" 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, 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)

    new_compute = Compute.objects.get(id=compute_id)

    try:
        utils.migrate_instance(new_compute, instance, request.user, live, unsafe, xml_del, offline)
    except libvirtError as err:
        messages.error(request, err)

    msg = _("Instance is migrated to %(hostname)s") % {"hostname": new_compute.hostname}
    addlogmsg(request.user, 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.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.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.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.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.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),
        )
        instance.proxy.attach_disk(target_dev, source, target_bus=bus, driver_type=format, cache_mode=cache)
        msg = _("Attach new disk: %(name)s (%(format)s)") % {"name": name, "format": format}
        addlogmsg(request.user.username, 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,
        )

        driver_type = conn_create.get_volume_type(name)
        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, target_bus=bus, driver_type=driver_type, cache_mode=cache)
        msg = _("Attach Existing disk: %(target_dev)s") % {"target_dev": target_dev}
        addlogmsg(request.user.username, 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", "")
        new_bus = request.POST.get("vol_bus", "")
        bus = request.POST.get("vol_bus_old", "")
        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.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.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.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.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.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.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.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:
        name = request.POST.get("name", "")
        instance.proxy.create_snapshot(name)
        msg = _("Create snapshot: %(snap)s") % {"snap": name}
        addlogmsg(request.user.username, 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:
        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.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:
        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.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.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 = request.POST.get("vcpu_hotplug", "")
    msg = _("VCPU Hot-plug is enabled=%(status)s") % {"status": status}
    instance.proxy.set_vcpu_hotplug(eval(status))
    addlogmsg(request.user.username, 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.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.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.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.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.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.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.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.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.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.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.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.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.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.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, 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.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.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.name, msg)

            if "listen_on" in form.changed_data:
                instance.proxy.set_console_listen_addr(form.cleaned_data["listen_on"])
                msg = _("Set VNC listen address")
                addlogmsg(request.user.username, 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.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.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_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 = 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_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_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_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"],
                                listen_addr=data["listener_addr"],
                                nwfilter=data["nwfilter"],
                                graphics=data["graphics"],
                                video=data["video"],
                                console_pass=data["console_pass"],
                                mac=data["mac"],
                                qemu_ga=data["qemu_ga"],
                            )
                            create_instance = Instance(compute_id=compute_id, name=data["name"], uuid=uuid)
                            create_instance.save()
                            msg = _("Instance is created")
                            messages.success(request, msg)
                            addlogmsg(request.user.username, 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},
    )