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

Enrich Instance Create operation: -add capability to create arm, ppc, i686, aarch64 instances, -add option to choose firmware, -add options to choose chipset. -add capability to choose volume driver options. -add new default settings

This commit is contained in:
catborise 2019-12-13 16:47:51 +03:00
parent 28b001e7cb
commit dd16a5b2d5
14 changed files with 828 additions and 211 deletions

View file

@ -1,6 +1,7 @@
import libvirt
import threading
import socket
import re
from vrtManager import util
from vrtManager.rwlock import ReadWriteLock
from django.conf import settings
@ -344,15 +345,88 @@ class wvmConnect(object):
"""Return xml capabilities"""
return self.wvm.getCapabilities()
def get_dom_cap_xml(self):
""" Return domcapabilities xml"""
arch = self.wvm.getInfo()[0]
machine = self.get_machines(arch)
def get_dom_cap_xml(self, arch, machine):
""" Return domain capabilities xml"""
emulatorbin = self.get_emulator(arch)
virttype = self.hypervisor_type()[arch][0]
virttype = self.get_hypervisors_domain_types()[arch][0]
machine_types = self.get_machine_types(arch)
if not machine or machine not in machine_types:
machine = 'pc' if 'pc' in machine_types else machine_types[0]
return self.wvm.getDomainCapabilities(emulatorbin, arch, machine, virttype)
def get_capabilities(self, arch):
""" Host Capabilities for specified architecture """
def guests(ctx):
result = dict()
for arch_el in ctx.xpath("/capabilities/guest/arch[@name='{}']".format(arch)):
result["wordsize"] = arch_el.find("wordsize").text
result["emulator"] = arch_el.find("emulator").text
result["domain"] = [v for v in arch_el.xpath("domain/@type")]
result["machines"] = []
for m in arch_el.xpath("machine"):
result["machines"].append({"machine": m.text,
"max_cpu": m.get("maxCpus"),
"canonical": m.get("canonical")})
guest_el = arch_el.getparent()
for f in guest_el.xpath("features"):
result["features"] = [t.tag for t in f.getchildren()]
result["os_type"] = guest_el.find("os_type").text
return result
return util.get_xml_path(self.get_cap_xml(), func=guests)
def get_dom_capabilities(self, arch, machine):
"""Return domain capabilities"""
result = dict()
xml = self.get_dom_cap_xml(arch, machine)
result["path"] = util.get_xml_path(xml,"/domainCapabilities/path")
result["domain"] = util.get_xml_path(xml, "/domainCapabilities/domain")
result["machine"] = util.get_xml_path(xml, "/domainCapabilities/machine")
result["vcpu_max"] = util.get_xml_path(xml, "/domainCapabilities/vcpu/@max")
result["iothreads_support"] = util.get_xml_path(xml, "/domainCapabilities/iothreads/@supported")
result["os_support"] = util.get_xml_path(xml, "/domainCapabilities/os/@supported")
result["loader_support"] = util.get_xml_path(xml, "/domainCapabilities/os/loader/@supported")
if result["loader_support"] == 'yes':
result["loaders"] = self.get_os_loaders(arch, machine)
result["loader_enums"] = self.get_os_loader_enums(arch, machine)
result["cpu_modes"] = self.get_cpu_modes(arch, machine)
if "custom" in result["cpu_modes"]:
# supported and unknown cpu models
result["cpu_custom_models"] = self.get_cpu_custom_types(arch, machine)
result["disk_support"] = util.get_xml_path(xml, "/domainCapabilities/devices/disk/@supported")
if result["disk_support"] == 'yes':
result["disk_devices"] = self.get_disk_device_types(arch, machine)
result["disk_bus"] = self.get_disk_bus_types(arch, machine)
result["graphics_support"] = util.get_xml_path(xml, "/domainCapabilities/devices/graphics/@supported")
if result["graphics_support"] == 'yes':
result["graphics_types"] = self.get_graphics_types(arch, machine)
result["video_support"] = util.get_xml_path(xml, "/domainCapabilities/devices/video/@supported")
if result["video_support"] == 'yes':
result["video_types"] = self.get_video_models(arch, machine)
result["hostdev_support"] = util.get_xml_path(xml, "/domainCapabilities/devices/hostdev/@supported")
if result["hostdev_support"] == 'yes':
result["hostdev_types"] = self.get_hostdev_modes(arch, machine)
result["hostdev_startup_policies"] = self.get_hostdev_startup_policies(arch, machine)
result["hostdev_subsys_types"] = self.get_hostdev_subsys_types(arch, machine)
result["features_gic_support"] = util.get_xml_path(xml, "/domainCapabilities/features/gic/@supported")
result["features_genid_support"] = util.get_xml_path(xml, "/domainCapabilities/features/genid/@supported")
result["features_vmcoreinfo_support"] = util.get_xml_path(xml, "/domainCapabilities/features/vmcoreinfo/@supported")
result["features_sev_support"] = util.get_xml_path(xml, "/domainCapabilities/features/sev/@supported")
return result
def get_version(self):
ver = self.wvm.getVersion()
major = ver / 1000000
@ -417,7 +491,7 @@ class wvmConnect(object):
'unsafe': 'Unsafe', # since libvirt 0.9.7
}
def hypervisor_type(self):
def get_hypervisors_domain_types(self):
"""Return hypervisor type"""
def hypervisors(ctx):
result = {}
@ -428,10 +502,34 @@ class wvmConnect(object):
return result
return util.get_xml_path(self.get_cap_xml(), func=hypervisors)
def get_hypervisors_machines(self):
"""Return hypervisor and its machine types"""
def machines(ctx):
result = dict()
for arche in ctx.xpath('/capabilities/guest/arch'):
arch = arche.get("name")
result[arch] = self.get_machine_types(arch)
return result
return util.get_xml_path(self.get_cap_xml(), func=machines)
def get_emulator(self, arch):
"""Return emulator """
return util.get_xml_path(self.get_cap_xml(), "/capabilities/guest/arch[@name='{}']/emulator".format(arch))
def get_machine_types(self, arch):
"""Return canonical(if exist) name of machine types """
def machines(ctx):
result = list()
canonical_name = ctx.xpath("/capabilities/guest/arch[@name='{}']/machine[@canonical]".format(arch))
if not canonical_name:
canonical_name = ctx.xpath("/capabilities/guest/arch[@name='{}']/machine".format(arch))
for archi in canonical_name:
result.append(archi.text)
return result
return util.get_xml_path(self.get_cap_xml(), func=machines)
def get_emulators(self):
def emulators(ctx):
result = {}
@ -442,35 +540,76 @@ class wvmConnect(object):
return result
return util.get_xml_path(self.get_cap_xml(), func=emulators)
def get_machines(self, arch):
""" Return machine type of emulation"""
return util.get_xml_path(self.get_cap_xml(), "/capabilities/guest/arch[@name='{}']/machine".format(arch))
def get_os_loaders(self, arch='x86_64', machine='pc'):
"""Get available os loaders list"""
def get_os_loaders(ctx):
return [v.text for v in ctx.xpath("/domainCapabilities/os/loader[@supported='yes']/value")]
return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_os_loaders)
def get_disk_bus_types(self):
def get_os_loader_enums(self, arch, machine):
"""Get available os loaders list"""
def get_os_loader_enums(ctx):
result = dict()
enums = [v for v in ctx.xpath("/domainCapabilities/os/loader[@supported='yes']/enum/@name")]
for enum in enums:
path = "/domainCapabilities/os/loader[@supported='yes']/enum[@name='{}']/value".format(enum)
result[enum] = [v.text for v in ctx.xpath(path)]
return result
return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_os_loader_enums)
def get_disk_bus_types(self, arch, machine):
"""Get available disk bus types list"""
def get_bus_list(ctx):
result = []
for disk_enum in ctx.xpath('/domainCapabilities/devices/disk/enum'):
if disk_enum.xpath("@name")[0] == "bus":
for values in disk_enum: result.append(values.text)
return result
return [v.text for v in ctx.xpath("/domainCapabilities/devices/disk/enum[@name='bus']/value")]
# return [ 'ide', 'scsi', 'usb', 'virtio' ]
return util.get_xml_path(self.get_dom_cap_xml(), func=get_bus_list)
return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_bus_list)
def get_disk_device_types(self):
def get_disk_device_types(self, arch, machine):
"""Get available disk device type list"""
def get_device_list(ctx):
result = []
for disk_enum in ctx.xpath('/domainCapabilities/devices/disk/enum'):
if disk_enum.xpath("@name")[0] == "diskDevice":
for values in disk_enum: result.append(values.text)
return result
return [v.text for v in ctx.xpath("/domainCapabilities/devices/disk/enum[@name='diskDevice']/value")]
# return [ 'disk', 'cdrom', 'floppy', 'lun' ]
return util.get_xml_path(self.get_dom_cap_xml(), func=get_device_list)
return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_device_list)
def get_graphics_types(self, arch, machine):
"""Get available graphics types """
def get_graphics_list(ctx):
return [ v.text for v in ctx.xpath("/domainCapabilities/devices/graphics/enum[@name='type']/value")]
return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_graphics_list)
def get_cpu_modes(self, arch, machine):
"""Get available cpu modes """
def get_cpu_modes(ctx):
return [v for v in ctx.xpath("/domainCapabilities/cpu/mode[@supported='yes']/@name")]
return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_cpu_modes)
def get_cpu_custom_types(self, arch, machine):
"""Get available graphics types """
def get_custom_list(ctx):
usable_yes = "/domainCapabilities/cpu/mode[@name='custom'][@supported='yes']/model[@usable='yes']"
usable_unknown = "/domainCapabilities/cpu/mode[@name='custom'][@supported='yes']/model[@usable='unknown']"
result = [v.text for v in ctx.xpath(usable_yes)]
result += [v.text for v in ctx.xpath(usable_unknown)]
return result
return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_custom_list)
def get_hostdev_modes(self, arch, machine):
"""Get available nodedev modes """
def get_hostdev_list(ctx):
return [v.text for v in ctx.xpath("/domainCapabilities/devices/hostdev/enum[@name='mode']/value")]
return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_hostdev_list)
def get_hostdev_startup_policies(self, arch, machine):
"""Get available hostdev modes """
def get_hostdev_list(ctx):
return [v.text for v in ctx.xpath("/domainCapabilities/devices/hostdev/enum[@name='startupPolicy']/value")]
return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_hostdev_list)
def get_hostdev_subsys_types(self, arch, machine):
"""Get available nodedev sub system types """
def get_hostdev_list(ctx):
return [v.text for v in ctx.xpath("/domainCapabilities/devices/hostdev/enum[@name='subsysType']/value")]
return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_hostdev_list)
def get_image_formats(self):
"""Get available image formats"""
@ -480,7 +619,7 @@ class wvmConnect(object):
"""Get available image filename extensions"""
return ['img', 'qcow', 'qcow2']
def get_video_models(self):
def get_video_models(self, arch, machine):
""" Get available graphics video types """
def get_video_list(ctx):
result = []
@ -488,7 +627,7 @@ class wvmConnect(object):
if video_enum.xpath("@name")[0] == "modelType":
for values in video_enum: result.append(values.text)
return result
return util.get_xml_path(self.get_dom_cap_xml(), func=get_video_list)
return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_video_list)
def get_iface(self, name):
return self.wvm.interfaceLookupByName(name)
@ -624,3 +763,49 @@ class wvmConnect(object):
# to-do: do not close connection ;)
# self.wvm.close()
pass
def find_uefi_path_for_arch(self, arch, machine):
"""
Search the loader paths for one that matches the passed arch
"""
if not self.arch_can_uefi(arch):
return
loaders = self.get_os_loaders(arch, machine)
patterns = util.uefi_arch_patterns.get(arch)
for pattern in patterns:
for path in loaders:
if re.match(pattern, path):
return path
def label_for_firmware_path(self, arch, path):
"""
Return a pretty label for passed path, based on if we know
about it or not
"""
if not path:
if arch in ["i686", "x86_64"]:
return "BIOS"
return
for arch, patterns in util.uefi_arch_patterns.items():
for pattern in patterns:
if re.match(pattern, path):
return ("UEFI %(arch)s: %(path)s" %
{"arch": arch, "path": path})
return "Custom: %(path)s" % {"path": path}
def arch_can_uefi(self, arch):
"""
Return True if we know how to setup UEFI for the passed arch
"""
return arch in list(util.uefi_arch_patterns.keys())
def supports_uefi_xml(self, loader_enums):
"""
Return True if libvirt advertises support for proper UEFI setup
"""
return ("readonly" in loader_enums and
"yes" in loader_enums.get("readonly"))

View file

@ -1,9 +1,11 @@
import string
from vrtManager import util
from vrtManager.connection import wvmConnect
from webvirtcloud.settings import QEMU_CONSOLE_DEFAULT_TYPE
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_OWNER as default_owner
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_OWNER as DEFAULT_OWNER
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_FORMAT
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_SCSI_CONTROLLER
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_DRIVER_OPTS as OPTS
def get_rbd_storage_data(stg):
@ -11,7 +13,7 @@ def get_rbd_storage_data(stg):
ceph_user = util.get_xml_path(xml, "/pool/source/auth/@username")
def get_ceph_hosts(doc):
hosts = []
hosts = list()
for host in doc.xpath("/pool/source/host"):
name = host.prop("name")
if name:
@ -29,7 +31,7 @@ class wvmCreate(wvmConnect):
"""
Function return all images on all storages
"""
images = []
images = list()
storages = self.get_storages(only_actives=True)
for storage in storages:
stg = self.get_storage(storage)
@ -45,14 +47,14 @@ class wvmCreate(wvmConnect):
return images
def get_os_type(self):
"""Get guest capabilities"""
"""Get guest os type"""
return util.get_xml_path(self.get_cap_xml(), "/capabilities/guest/os_type")
def get_host_arch(self):
"""Get guest capabilities"""
return util.get_xml_path(self.get_cap_xml(), "/capabilities/host/cpu/arch")
def create_volume(self, storage, name, size, image_format=image_format, metadata=False, owner=default_owner):
def create_volume(self, storage, name, size, image_format=image_format, metadata=False, owner=DEFAULT_OWNER):
size = int(size) * 1073741824
stg = self.get_storage(storage)
storage_type = util.get_xml_path(stg.XMLDesc(0), "/pool/@type")
@ -120,7 +122,7 @@ class wvmCreate(wvmConnect):
vol = self.get_volume_by_path(vol_path)
return vol.storagePoolLookupByVolume()
def clone_from_template(self, clone, template, storage=None, metadata=False, owner=default_owner):
def clone_from_template(self, clone, template, storage=None, metadata=False, owner=DEFAULT_OWNER):
vol = self.get_volume_by_path(template)
if not storage:
stg = vol.storagePoolLookupByVolume()
@ -163,16 +165,15 @@ class wvmCreate(wvmConnect):
vol = self.get_volume_by_path(path)
vol.delete()
def create_instance(self, name, memory, vcpu, host_model, uuid, images, cache_mode, networks, virtio, listen_addr, nwfilter=None, video="cirrus", console_pass="random", mac=None, qemu_ga=False):
def create_instance(self, name, memory, vcpu, vcpu_mode, uuid, arch, machine, firmware, images, cache_mode, networks, nwfilter, graphics, virtio, listen_addr, video="vga", console_pass="random", mac=None, qemu_ga=False):
"""
Create VM function
"""
memory = int(memory) * 1024
caps = self.get_capabilities(arch)
dom_caps = self.get_dom_capabilities(arch, machine)
if self.is_kvm_supported():
hypervisor_type = 'kvm'
else:
hypervisor_type = 'qemu'
memory = int(memory) * 1024
#hypervisor_type = 'kvm' if self.is_kvm_supported() else 'qemu'
xml = """
<domain type='%s'>
@ -180,23 +181,46 @@ class wvmCreate(wvmConnect):
<description>None</description>
<uuid>%s</uuid>
<memory unit='KiB'>%s</memory>
<vcpu>%s</vcpu>""" % (hypervisor_type, name, uuid, memory, vcpu)
if host_model:
<vcpu>%s</vcpu>""" % (dom_caps["domain"], name, uuid, memory, vcpu)
if dom_caps["os_support"] == 'yes':
xml += """<os>
<type arch='%s' machine='%s'>%s</type>""" % (arch, machine, caps["os_type"])
xml += """ <boot dev='hd'/>
<boot dev='cdrom'/>
<bootmenu enable='yes'/>"""
if 'UEFI' in firmware:
xml += """<loader readonly='yes' type='pflash'>%s</loader>""" % firmware.split(":")[1].strip()
xml += """</os>"""
if caps["features"]:
xml += """<features>"""
if 'acpi' in caps["features"]:
xml += """<acpi/>"""
if 'apic' in caps["features"]:
xml += """<apic/>"""
if 'pae' in caps["features"]:
xml += """<pae/>"""
xml += """</features>"""
if vcpu_mode == "host-model":
xml += """<cpu mode='host-model'/>"""
xml += """<os>
<type arch='%s'>%s</type>
<boot dev='hd'/>
<boot dev='cdrom'/>
<bootmenu enable='yes'/>
</os>""" % (self.get_host_arch(), self.get_os_type())
xml += """<features>
<acpi/><apic/><pae/>
</features>
elif vcpu_mode == "host-passthrough":
xml += """<cpu mode='host-passthrough'/>"""
elif vcpu_mode == "":
pass
else:
xml += """<cpu mode='custom' match='exact' check='none'>
<model fallback='allow'>{}</model>
</cpu>""".format(vcpu_mode)
xml += """
<clock offset="utc"/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>restart</on_crash>
<devices>"""
"""
xml += """<devices>"""
vd_disk_letters = list(string.lowercase)
fd_disk_letters = list(string.lowercase)
@ -212,11 +236,11 @@ class wvmCreate(wvmConnect):
if stg_type == 'rbd':
ceph_user, secret_uuid, ceph_hosts = get_rbd_storage_data(stg)
xml += """<disk type='network' device='disk'>
<driver name='qemu' type='%s' cache='%s'/>
<auth username='%s'>
<driver name='qemu' type='%s' cache='%s' %s />""" % (volume['type'], cache_mode, OPTS.get("network", ''))
xml += """ <auth username='%s'>
<secret type='ceph' uuid='%s'/>
</auth>
<source protocol='rbd' name='%s'>""" % (volume['type'], cache_mode, ceph_user, secret_uuid, volume['path'])
<source protocol='rbd' name='%s'>""" % (ceph_user, secret_uuid, volume['path'])
if isinstance(ceph_hosts, list):
for host in ceph_hosts:
if host.get('port'):
@ -225,12 +249,11 @@ class wvmCreate(wvmConnect):
else:
xml += """
<host name='%s'/>""" % host.get('name')
xml += """
</source>"""
xml += """</source>"""
else:
xml += """<disk type='file' device='%s'>
<driver name='qemu' type='%s' cache='%s'/>
<source file='%s'/>""" % (volume['device'], volume['type'], cache_mode, volume['path'])
xml += """<disk type='file' device='%s'>""" % volume['device']
xml += """ <driver name='qemu' type='%s' cache='%s' %s/>""" % (volume['type'], cache_mode, OPTS.get("file", ''))
xml += """ <source file='%s'/>""" % volume['path']
if volume['bus'] == 'virtio':
xml += """<target dev='vd%s' bus='%s'/>""" % (vd_disk_letters.pop(0), volume['bus'])
@ -242,12 +265,23 @@ class wvmCreate(wvmConnect):
xml += """<target dev='sd%s' bus='%s'/>""" % (sd_disk_letters.pop(0), volume['bus'])
xml += """</disk>"""
if add_cd:
xml += """ <disk type='file' device='cdrom'>
xml += """<disk type='file' device='cdrom'>
<driver name='qemu' type='raw'/>
<source file=''/>
<target dev='hd%s' bus='ide'/>
<readonly/>
</disk>""" % (hd_disk_letters.pop(0),)
<source file = '' />
<readonly/>"""
if 'ide' in dom_caps['disk_bus']:
xml += """<target dev='hd%s' bus='%s'/>""" % (hd_disk_letters.pop(0), 'ide')
elif 'sata' in dom_caps['disk_bus']:
xml += """<target dev='sd%s' bus='%s'/>""" % (sd_disk_letters.pop(0), 'sata')
elif 'scsi' in dom_caps['disk_bus']:
xml += """<target dev='sd%s' bus='%s'/>""" % (sd_disk_letters.pop(0), 'scsi')
else:
xml += """<target dev='vd%s' bus='%s'/>""" % (vd_disk_letters.pop(0), 'virtio')
xml += """</disk>"""
if volume['bus'] == 'scsi':
xml += """<controller type='scsi' model='%s'/>""" % INSTANCE_VOLUME_DEFAULT_SCSI_CONTROLLER
for net in networks.split(','):
xml += """<interface type='network'>"""
if mac:
@ -265,10 +299,11 @@ class wvmCreate(wvmConnect):
if not console_pass == "":
console_pass = "passwd='" + console_pass + "'"
xml += """ <input type='mouse' bus='ps2'/>
<input type='tablet' bus='usb'/>
<graphics type='%s' port='-1' autoport='yes' %s listen='%s'/>
<console type='pty'/> """ % (QEMU_CONSOLE_DEFAULT_TYPE, console_pass, listen_addr)
xml += """<input type='mouse' bus='virtio'/>"""
xml += """<input type='tablet' bus='virtio'/>"""
xml += """
<graphics type='%s' port='-1' autoport='yes' %s listen='%s'/>
<console type='pty'/> """ % (graphics, console_pass, listen_addr)
if qemu_ga:
xml += """ <channel type='unix'>
@ -278,7 +313,6 @@ class wvmCreate(wvmConnect):
xml += """ <video>
<model type='%s'/>
</video>
<memballoon model='virtio'/>
</devices>
</domain>""" % video
self._defineXML(xml)