2015-02-27 08:53:51 +00:00
|
|
|
import string
|
|
|
|
from vrtManager import util
|
|
|
|
from vrtManager.connection import wvmConnect
|
2020-01-08 08:23:10 +00:00
|
|
|
|
|
|
|
|
2015-02-27 08:53:51 +00:00
|
|
|
def get_rbd_storage_data(stg):
|
|
|
|
xml = stg.XMLDesc(0)
|
|
|
|
ceph_user = util.get_xml_path(xml, "/pool/source/auth/@username")
|
2016-04-16 13:04:28 +00:00
|
|
|
|
2018-07-20 12:43:29 +00:00
|
|
|
def get_ceph_hosts(doc):
|
2019-12-13 13:47:51 +00:00
|
|
|
hosts = list()
|
2018-07-20 12:43:29 +00:00
|
|
|
for host in doc.xpath("/pool/source/host"):
|
2016-04-16 13:04:28 +00:00
|
|
|
name = host.prop("name")
|
|
|
|
if name:
|
|
|
|
hosts.append({'name': name, 'port': host.prop("port")})
|
|
|
|
return hosts
|
|
|
|
ceph_hosts = util.get_xml_path(xml, func=get_ceph_hosts)
|
|
|
|
secret_uuid = util.get_xml_path(xml, "/pool/source/auth/secret/@uuid")
|
|
|
|
return ceph_user, secret_uuid, ceph_hosts
|
2015-02-27 08:53:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
class wvmCreate(wvmConnect):
|
2018-07-20 10:40:49 +00:00
|
|
|
|
2015-02-27 08:53:51 +00:00
|
|
|
def get_storages_images(self):
|
|
|
|
"""
|
|
|
|
Function return all images on all storages
|
|
|
|
"""
|
2019-12-13 13:47:51 +00:00
|
|
|
images = list()
|
2018-07-20 11:04:13 +00:00
|
|
|
storages = self.get_storages(only_actives=True)
|
2015-02-27 08:53:51 +00:00
|
|
|
for storage in storages:
|
|
|
|
stg = self.get_storage(storage)
|
|
|
|
try:
|
|
|
|
stg.refresh(0)
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
for img in stg.listVolumes():
|
2019-10-24 10:26:58 +00:00
|
|
|
if img.lower().endswith('.iso'):
|
2015-02-27 08:53:51 +00:00
|
|
|
pass
|
|
|
|
else:
|
|
|
|
images.append(img)
|
|
|
|
return images
|
|
|
|
|
|
|
|
def get_os_type(self):
|
2019-12-13 13:47:51 +00:00
|
|
|
"""Get guest os type"""
|
2015-02-27 08:53:51 +00:00
|
|
|
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")
|
|
|
|
|
2020-05-28 21:43:26 +00:00
|
|
|
def create_volume(self, storage, name, size, image_format, metadata=False, disk_owner_uid=0, disk_owner_gid=0):
|
2015-02-27 08:53:51 +00:00
|
|
|
size = int(size) * 1073741824
|
|
|
|
stg = self.get_storage(storage)
|
|
|
|
storage_type = util.get_xml_path(stg.XMLDesc(0), "/pool/@type")
|
|
|
|
if storage_type == 'dir':
|
2019-03-20 11:30:41 +00:00
|
|
|
if image_format in ('qcow', 'qcow2'):
|
|
|
|
name += '.' + image_format
|
|
|
|
else:
|
|
|
|
name += '.img'
|
2015-02-27 08:53:51 +00:00
|
|
|
alloc = 0
|
|
|
|
else:
|
|
|
|
alloc = size
|
|
|
|
metadata = False
|
2020-05-28 21:43:26 +00:00
|
|
|
xml = f"""
|
2015-02-27 08:53:51 +00:00
|
|
|
<volume>
|
2020-05-28 21:43:26 +00:00
|
|
|
<name>{name}</name>
|
|
|
|
<capacity>{size}</capacity>
|
|
|
|
<allocation>{alloc}</allocation>
|
2015-02-27 08:53:51 +00:00
|
|
|
<target>
|
2020-05-28 21:43:26 +00:00
|
|
|
<format type='{image_format}'/>
|
2018-07-25 08:16:03 +00:00
|
|
|
<permissions>
|
2020-05-28 21:43:26 +00:00
|
|
|
<owner>{disk_owner_uid}</owner>
|
|
|
|
<group>{disk_owner_gid}</group>
|
2018-07-25 08:16:03 +00:00
|
|
|
<mode>0644</mode>
|
|
|
|
<label>virt_image_t</label>
|
|
|
|
</permissions>
|
2018-10-05 14:07:25 +00:00
|
|
|
<compat>1.1</compat>
|
|
|
|
<features>
|
|
|
|
<lazy_refcounts/>
|
|
|
|
</features>
|
2015-02-27 08:53:51 +00:00
|
|
|
</target>
|
2020-05-28 21:43:26 +00:00
|
|
|
</volume>"""
|
2015-02-27 08:53:51 +00:00
|
|
|
stg.createXML(xml, metadata)
|
|
|
|
try:
|
|
|
|
stg.refresh(0)
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
vol = stg.storageVolLookupByName(name)
|
|
|
|
return vol.path()
|
|
|
|
|
|
|
|
def get_volume_type(self, path):
|
|
|
|
vol = self.get_volume_by_path(path)
|
|
|
|
vol_type = util.get_xml_path(vol.XMLDesc(0), "/volume/target/format/@type")
|
2018-11-23 12:18:32 +00:00
|
|
|
if vol_type == 'unknown' or vol_type == 'iso':
|
2015-02-27 08:53:51 +00:00
|
|
|
return 'raw'
|
|
|
|
if vol_type:
|
|
|
|
return vol_type
|
|
|
|
else:
|
|
|
|
return 'raw'
|
|
|
|
|
2018-12-18 07:46:23 +00:00
|
|
|
def get_volume_path(self, volume, pool=None):
|
|
|
|
if not pool:
|
|
|
|
storages = self.get_storages(only_actives=True)
|
|
|
|
else:
|
2019-09-10 06:48:31 +00:00
|
|
|
storages = [pool]
|
2015-02-27 08:53:51 +00:00
|
|
|
for storage in storages:
|
|
|
|
stg = self.get_storage(storage)
|
|
|
|
if stg.info()[0] != 0:
|
|
|
|
stg.refresh(0)
|
|
|
|
for img in stg.listVolumes():
|
|
|
|
if img == volume:
|
|
|
|
vol = stg.storageVolLookupByName(img)
|
|
|
|
return vol.path()
|
|
|
|
|
|
|
|
def get_storage_by_vol_path(self, vol_path):
|
|
|
|
vol = self.get_volume_by_path(vol_path)
|
|
|
|
return vol.storagePoolLookupByVolume()
|
|
|
|
|
2020-05-28 21:43:26 +00:00
|
|
|
def clone_from_template(self, clone, template, storage=None, metadata=False, disk_owner_uid=0, disk_owner_gid=0):
|
2015-02-27 08:53:51 +00:00
|
|
|
vol = self.get_volume_by_path(template)
|
2019-01-09 11:16:51 +00:00
|
|
|
if not storage:
|
|
|
|
stg = vol.storagePoolLookupByVolume()
|
|
|
|
else:
|
|
|
|
stg = self.get_storage(storage)
|
|
|
|
|
2015-02-27 08:53:51 +00:00
|
|
|
storage_type = util.get_xml_path(stg.XMLDesc(0), "/pool/@type")
|
|
|
|
format = util.get_xml_path(vol.XMLDesc(0), "/volume/target/format/@type")
|
|
|
|
if storage_type == 'dir':
|
|
|
|
clone += '.img'
|
|
|
|
else:
|
|
|
|
metadata = False
|
2020-05-28 21:43:26 +00:00
|
|
|
xml = f"""
|
2015-02-27 08:53:51 +00:00
|
|
|
<volume>
|
2020-05-28 21:43:26 +00:00
|
|
|
<name>{clone}</name>
|
2015-02-27 08:53:51 +00:00
|
|
|
<capacity>0</capacity>
|
|
|
|
<allocation>0</allocation>
|
|
|
|
<target>
|
2020-05-28 21:43:26 +00:00
|
|
|
<format type='{format}'/>
|
2018-07-25 08:16:03 +00:00
|
|
|
<permissions>
|
2020-05-28 21:43:26 +00:00
|
|
|
<owner>{disk_owner_uid}</owner>
|
|
|
|
<group>{disk_owner_gid}</group>
|
2018-07-25 08:16:03 +00:00
|
|
|
<mode>0644</mode>
|
|
|
|
<label>virt_image_t</label>
|
|
|
|
</permissions>
|
2018-07-31 13:26:28 +00:00
|
|
|
<compat>1.1</compat>
|
|
|
|
<features>
|
|
|
|
<lazy_refcounts/>
|
|
|
|
</features>
|
2015-02-27 08:53:51 +00:00
|
|
|
</target>
|
2020-05-28 21:43:26 +00:00
|
|
|
</volume>"""
|
2015-02-27 08:53:51 +00:00
|
|
|
stg.createXMLFrom(xml, vol, metadata)
|
|
|
|
clone_vol = stg.storageVolLookupByName(clone)
|
|
|
|
return clone_vol.path()
|
|
|
|
|
|
|
|
def _defineXML(self, xml):
|
|
|
|
self.wvm.defineXML(xml)
|
|
|
|
|
|
|
|
def delete_volume(self, path):
|
|
|
|
vol = self.get_volume_by_path(path)
|
|
|
|
vol.delete()
|
|
|
|
|
2020-05-28 21:43:26 +00:00
|
|
|
def create_instance(self, name, memory, vcpu, vcpu_mode, uuid, arch, machine, firmware, volumes,
|
2020-01-08 08:23:10 +00:00
|
|
|
networks, nwfilter, graphics, virtio, listen_addr,
|
2020-05-28 21:43:26 +00:00
|
|
|
video="vga", console_pass="random", mac=None, qemu_ga=True):
|
2015-02-27 08:53:51 +00:00
|
|
|
"""
|
|
|
|
Create VM function
|
|
|
|
"""
|
2019-12-13 13:47:51 +00:00
|
|
|
caps = self.get_capabilities(arch)
|
|
|
|
dom_caps = self.get_dom_capabilities(arch, machine)
|
2015-02-27 08:53:51 +00:00
|
|
|
|
2019-12-13 13:47:51 +00:00
|
|
|
memory = int(memory) * 1024
|
2015-02-27 08:53:51 +00:00
|
|
|
|
2020-05-28 21:43:26 +00:00
|
|
|
xml = f"""
|
|
|
|
<domain type='{dom_caps["domain"]}'>
|
|
|
|
<name>{name}</name>
|
2015-02-27 08:53:51 +00:00
|
|
|
<description>None</description>
|
2020-05-28 21:43:26 +00:00
|
|
|
<uuid>{uuid}</uuid>
|
|
|
|
<memory unit='KiB'>{memory}</memory>
|
|
|
|
<vcpu>{vcpu}</vcpu>"""
|
2019-12-13 13:47:51 +00:00
|
|
|
|
|
|
|
if dom_caps["os_support"] == 'yes':
|
2020-05-28 21:43:26 +00:00
|
|
|
xml += f"""<os>
|
|
|
|
<type arch='{arch}' machine='{machine}'>{caps["os_type"]}</type>"""
|
2019-12-13 13:47:51 +00:00
|
|
|
xml += """ <boot dev='hd'/>
|
|
|
|
<boot dev='cdrom'/>
|
|
|
|
<bootmenu enable='yes'/>"""
|
2019-12-19 10:49:41 +00:00
|
|
|
if firmware:
|
|
|
|
if firmware["secure"] == 'yes':
|
|
|
|
xml += """<loader readonly='%s' type='%s' secure='%s'>%s</loader>""" % (firmware["readonly"],
|
|
|
|
firmware["type"],
|
|
|
|
firmware["secure"],
|
|
|
|
firmware["loader"])
|
|
|
|
if firmware["secure"] == 'no':
|
|
|
|
xml += """<loader readonly='%s' type='%s'>%s</loader>""" % (firmware["readonly"],
|
|
|
|
firmware["type"],
|
|
|
|
firmware["loader"])
|
2019-12-13 13:47:51 +00:00
|
|
|
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/>"""
|
2020-04-24 16:34:29 +00:00
|
|
|
if firmware.get("secure", 'no') == 'yes':
|
2019-12-19 10:49:41 +00:00
|
|
|
xml += """<smm state="on"/>"""
|
2019-12-13 13:47:51 +00:00
|
|
|
xml += """</features>"""
|
|
|
|
|
|
|
|
if vcpu_mode == "host-model":
|
2015-02-27 08:53:51 +00:00
|
|
|
xml += """<cpu mode='host-model'/>"""
|
2019-12-13 13:47:51 +00:00
|
|
|
elif vcpu_mode == "host-passthrough":
|
|
|
|
xml += """<cpu mode='host-passthrough'/>"""
|
|
|
|
elif vcpu_mode == "":
|
|
|
|
pass
|
|
|
|
else:
|
2020-05-28 21:43:26 +00:00
|
|
|
xml += f"""<cpu mode='custom' match='exact' check='none'>
|
|
|
|
<model fallback='allow'>{vcpu_mode}</model>"""
|
2019-12-19 10:49:41 +00:00
|
|
|
xml += """</cpu>"""
|
2019-12-13 13:47:51 +00:00
|
|
|
|
|
|
|
xml += """
|
2015-02-27 08:53:51 +00:00
|
|
|
<clock offset="utc"/>
|
|
|
|
<on_poweroff>destroy</on_poweroff>
|
|
|
|
<on_reboot>restart</on_reboot>
|
|
|
|
<on_crash>restart</on_crash>
|
2019-12-13 13:47:51 +00:00
|
|
|
"""
|
|
|
|
xml += """<devices>"""
|
2015-02-27 08:53:51 +00:00
|
|
|
|
2020-03-16 13:59:45 +00:00
|
|
|
vd_disk_letters = list(string.ascii_lowercase)
|
|
|
|
fd_disk_letters = list(string.ascii_lowercase)
|
|
|
|
hd_disk_letters = list(string.ascii_lowercase)
|
|
|
|
sd_disk_letters = list(string.ascii_lowercase)
|
2018-11-23 12:18:32 +00:00
|
|
|
add_cd = True
|
2020-01-08 08:23:10 +00:00
|
|
|
|
2020-05-28 21:43:26 +00:00
|
|
|
for volume in volumes:
|
|
|
|
|
|
|
|
disk_opts = ''
|
|
|
|
if volume['cache_mode'] is not None and volume['cache_mode'] != 'default':
|
|
|
|
disk_opts += f"cache='{volume['cache_mode']}' "
|
|
|
|
if volume['io_mode'] is not None and volume['io_mode'] != 'default':
|
|
|
|
disk_opts += f"io='{volume['io_mode']}' "
|
|
|
|
if volume['discard_mode'] is not None and volume['discard_mode'] != 'default':
|
|
|
|
disk_opts += f"discard='{volume['discard_mode']}' "
|
|
|
|
if volume['detect_zeroes_mode'] is not None and volume['detect_zeroes_mode'] != 'default':
|
|
|
|
disk_opts += f"detect_zeroes='{volume['detect_zeroes_mode']}' "
|
|
|
|
|
2018-11-23 12:18:32 +00:00
|
|
|
stg = self.get_storage_by_vol_path(volume['path'])
|
2015-02-27 08:53:51 +00:00
|
|
|
stg_type = util.get_xml_path(stg.XMLDesc(0), "/pool/@type")
|
|
|
|
|
2018-11-23 12:18:32 +00:00
|
|
|
if volume['device'] == 'cdrom': add_cd = False
|
|
|
|
|
2015-02-27 08:53:51 +00:00
|
|
|
if stg_type == 'rbd':
|
2016-04-16 13:04:28 +00:00
|
|
|
ceph_user, secret_uuid, ceph_hosts = get_rbd_storage_data(stg)
|
2015-02-27 08:53:51 +00:00
|
|
|
xml += """<disk type='network' device='disk'>
|
2020-01-08 08:23:10 +00:00
|
|
|
<driver name='qemu' type='%s' %s />""" % (volume['type'], disk_opts)
|
2019-12-13 13:47:51 +00:00
|
|
|
xml += """ <auth username='%s'>
|
2015-02-27 08:53:51 +00:00
|
|
|
<secret type='ceph' uuid='%s'/>
|
|
|
|
</auth>
|
2019-12-13 13:47:51 +00:00
|
|
|
<source protocol='rbd' name='%s'>""" % (ceph_user, secret_uuid, volume['path'])
|
2016-04-16 13:04:28 +00:00
|
|
|
if isinstance(ceph_hosts, list):
|
|
|
|
for host in ceph_hosts:
|
|
|
|
if host.get('port'):
|
|
|
|
xml += """
|
|
|
|
<host name='%s' port='%s'/>""" % (host.get('name'), host.get('port'))
|
|
|
|
else:
|
|
|
|
xml += """
|
|
|
|
<host name='%s'/>""" % host.get('name')
|
2019-12-13 13:47:51 +00:00
|
|
|
xml += """</source>"""
|
2015-02-27 08:53:51 +00:00
|
|
|
else:
|
2019-12-13 13:47:51 +00:00
|
|
|
xml += """<disk type='file' device='%s'>""" % volume['device']
|
2020-01-08 08:23:10 +00:00
|
|
|
xml += """ <driver name='qemu' type='%s' %s/>""" % (volume['type'], disk_opts)
|
2020-05-28 21:43:26 +00:00
|
|
|
xml += f""" <source file='%s'/>""" % volume['path']
|
2018-11-23 12:18:32 +00:00
|
|
|
|
2019-12-19 10:49:41 +00:00
|
|
|
if volume.get('bus') == 'virtio':
|
|
|
|
xml += """<target dev='vd%s' bus='%s'/>""" % (vd_disk_letters.pop(0), volume.get('bus'))
|
|
|
|
elif volume.get('bus') == 'ide':
|
|
|
|
xml += """<target dev='hd%s' bus='%s'/>""" % (hd_disk_letters.pop(0), volume.get('bus'))
|
|
|
|
elif volume.get('bus') == 'fdc':
|
|
|
|
xml += """<target dev='fd%s' bus='%s'/>""" % (fd_disk_letters.pop(0), volume.get('bus'))
|
|
|
|
elif volume.get('bus') == 'sata' or volume.get('bus') == 'scsi':
|
|
|
|
xml += """<target dev='sd%s' bus='%s'/>""" % (sd_disk_letters.pop(0), volume.get('bus'))
|
2015-02-27 08:53:51 +00:00
|
|
|
else:
|
2019-12-19 10:49:41 +00:00
|
|
|
xml += """<target dev='sd%s'/>""" % sd_disk_letters.pop(0)
|
2015-02-27 08:53:51 +00:00
|
|
|
xml += """</disk>"""
|
2020-08-17 06:58:42 +00:00
|
|
|
|
|
|
|
if volume.get('bus') == 'scsi':
|
|
|
|
xml += f"""<controller type='scsi' model='{volume.get('scsi_model')}'/>"""
|
|
|
|
|
2018-11-23 12:18:32 +00:00
|
|
|
if add_cd:
|
2019-12-13 13:47:51 +00:00
|
|
|
xml += """<disk type='file' device='cdrom'>
|
2018-11-23 12:18:32 +00:00
|
|
|
<driver name='qemu' type='raw'/>
|
2019-12-13 13:47:51 +00:00
|
|
|
<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>"""
|
|
|
|
|
2015-02-27 08:53:51 +00:00
|
|
|
for net in networks.split(','):
|
|
|
|
xml += """<interface type='network'>"""
|
|
|
|
if mac:
|
2020-05-28 21:43:26 +00:00
|
|
|
xml += f"""<mac address='{mac}'/>"""
|
|
|
|
xml += f"""<source network='{net}'/>"""
|
2018-09-14 13:23:25 +00:00
|
|
|
if nwfilter:
|
2020-05-28 21:43:26 +00:00
|
|
|
xml += f"""<filterref filter='{nwfilter}'/>"""
|
2015-02-27 08:53:51 +00:00
|
|
|
if virtio:
|
|
|
|
xml += """<model type='virtio'/>"""
|
|
|
|
xml += """</interface>"""
|
|
|
|
|
2018-09-14 20:02:07 +00:00
|
|
|
if console_pass == "random":
|
|
|
|
console_pass = "passwd='" + util.randomPasswd() + "'"
|
|
|
|
else:
|
|
|
|
if not console_pass == "":
|
|
|
|
console_pass = "passwd='" + console_pass + "'"
|
2018-09-14 13:23:25 +00:00
|
|
|
|
2019-12-19 10:49:41 +00:00
|
|
|
if 'usb' in dom_caps['disk_bus']:
|
|
|
|
xml += """<input type='mouse' bus='{}'/>""".format('virtio' if virtio else 'usb')
|
2020-01-08 08:23:10 +00:00
|
|
|
xml += """<input type='keyboard' bus='{}'/>""".format('virtio' if virtio else 'usb')
|
2019-12-19 10:49:41 +00:00
|
|
|
xml += """<input type='tablet' bus='{}'/>""".format('virtio' if virtio else 'usb')
|
2020-01-08 08:23:10 +00:00
|
|
|
else:
|
|
|
|
xml += """<input type='mouse'/>"""
|
|
|
|
xml += """<input type='keyboard'/>"""
|
|
|
|
xml += """<input type='tablet'/>"""
|
2019-12-19 10:49:41 +00:00
|
|
|
|
2020-05-28 21:43:26 +00:00
|
|
|
xml += f"""
|
|
|
|
<graphics type='{graphics}' port='-1' autoport='yes' {console_pass} listen='{listen_addr}'/>
|
|
|
|
<console type='pty'/> """
|
2018-11-26 12:40:02 +00:00
|
|
|
|
2019-12-19 10:49:41 +00:00
|
|
|
if qemu_ga and virtio:
|
2018-11-26 12:40:02 +00:00
|
|
|
xml += """ <channel type='unix'>
|
|
|
|
<target type='virtio' name='org.qemu.guest_agent.0'/>
|
|
|
|
</channel>"""
|
|
|
|
|
2020-05-28 21:43:26 +00:00
|
|
|
xml += f""" <video>
|
|
|
|
<model type='{video}'/>
|
2018-11-26 12:40:02 +00:00
|
|
|
</video>
|
|
|
|
</devices>
|
2020-05-28 21:43:26 +00:00
|
|
|
</domain>"""
|
2015-02-27 08:53:51 +00:00
|
|
|
self._defineXML(xml)
|