1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2024-12-27 16:45:22 +00:00
webvirtcloud/vrtManager/instance.py

1884 lines
71 KiB
Python
Raw Normal View History

2022-11-02 13:05:41 +00:00
import contextlib
import json
2015-02-27 08:53:51 +00:00
import os.path
import time
2015-02-27 08:53:51 +00:00
try:
2020-09-25 12:57:17 +00:00
from libvirt import (
VIR_DOMAIN_AFFECT_CONFIG,
2022-11-02 13:05:41 +00:00
VIR_DOMAIN_AFFECT_LIVE,
VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT,
VIR_DOMAIN_RUNNING,
VIR_DOMAIN_XML_SECURE,
VIR_MIGRATE_AUTO_CONVERGE,
VIR_MIGRATE_COMPRESSED,
2020-09-25 12:57:17 +00:00
VIR_MIGRATE_LIVE,
VIR_MIGRATE_OFFLINE,
2022-11-02 13:05:41 +00:00
VIR_MIGRATE_PERSIST_DEST,
VIR_MIGRATE_POSTCOPY,
2022-11-02 13:05:41 +00:00
VIR_MIGRATE_UNDEFINE_SOURCE,
VIR_MIGRATE_UNSAFE,
2023-04-04 06:51:59 +00:00
VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY,
VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY,
VIR_DOMAIN_SNAPSHOT_LIST_INTERNAL,
VIR_DOMAIN_SNAPSHOT_LIST_EXTERNAL,
2023-04-04 13:38:12 +00:00
VIR_DOMAIN_BLOCK_COMMIT_DELETE,
VIR_DOMAIN_BLOCK_COMMIT_ACTIVE,
VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT,
VIR_DOMAIN_START_PAUSED,
2022-11-02 13:05:41 +00:00
libvirtError,
2020-09-25 12:57:17 +00:00
)
2022-11-02 13:05:41 +00:00
from libvirt_qemu import VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT, qemuAgentCommand
except Exception:
2015-02-27 08:53:51 +00:00
from libvirt import libvirtError, VIR_DOMAIN_XML_SECURE, VIR_MIGRATE_LIVE
from collections import OrderedDict
from datetime import datetime
2015-02-27 08:53:51 +00:00
from xml.etree import ElementTree
from lxml import etree
2020-04-24 16:34:29 +00:00
from vrtManager import util
2015-02-27 08:53:51 +00:00
from vrtManager.connection import wvmConnect
from vrtManager.storage import wvmStorage, wvmStorages
2015-02-27 08:53:51 +00:00
class wvmInstances(wvmConnect):
def get_instance_status(self, name):
inst = self.get_instance(name)
return inst.info()[0]
def get_instance_memory(self, name):
inst = self.get_instance(name)
mem = util.get_xml_path(inst.XMLDesc(0), "/domain/currentMemory")
return int(mem) // 1024
2015-02-27 08:53:51 +00:00
def get_instance_vcpu(self, name):
inst = self.get_instance(name)
cur_vcpu = util.get_xml_path(inst.XMLDesc(0), "/domain/vcpu/@current")
return cur_vcpu or util.get_xml_path(inst.XMLDesc(0), "/domain/vcpu")
2015-02-27 08:53:51 +00:00
def get_instance_managed_save_image(self, name):
inst = self.get_instance(name)
return inst.hasManagedSaveImage(0)
def get_uuid(self, name):
inst = self.get_instance(name)
return inst.UUIDString()
def start(self, name):
dom = self.get_instance(name)
dom.create()
def shutdown(self, name):
dom = self.get_instance(name)
dom.shutdown()
def force_shutdown(self, name):
dom = self.get_instance(name)
dom.destroy()
def managedsave(self, name):
dom = self.get_instance(name)
dom.managedSave(0)
def managed_save_remove(self, name):
dom = self.get_instance(name)
dom.managedSaveRemove(0)
def suspend(self, name):
dom = self.get_instance(name)
dom.suspend()
def resume(self, name):
dom = self.get_instance(name)
dom.resume()
2022-11-02 05:54:35 +00:00
def moveto(
self,
conn,
name,
live,
unsafe,
undefine,
offline,
autoconverge=False,
compress=False,
postcopy=False,
):
flags = VIR_MIGRATE_PERSIST_DEST
if live and conn.get_status() != 5:
2015-02-27 08:53:51 +00:00
flags |= VIR_MIGRATE_LIVE
if unsafe and conn.get_status() == 1:
flags |= VIR_MIGRATE_UNSAFE
if offline and conn.get_status() == 5:
flags |= VIR_MIGRATE_OFFLINE
if not offline and autoconverge:
flags |= VIR_MIGRATE_AUTO_CONVERGE
if not offline and compress and conn.get_status() == 1:
flags |= VIR_MIGRATE_COMPRESSED
if not offline and postcopy and conn.get_status() == 1:
flags |= VIR_MIGRATE_POSTCOPY
if undefine:
flags |= VIR_MIGRATE_UNDEFINE_SOURCE
dom = conn.get_instance(name)
dom_arch = conn.get_arch()
dom_emulator = conn.get_dom_emulator()
if dom_emulator != self.get_emulator(dom_arch):
2022-11-02 05:54:35 +00:00
raise libvirtError(
"Destination host emulator is different. Cannot be migrated"
)
dom.migrate(self.wvm, flags, None, None, 0)
2016-04-16 13:06:39 +00:00
def graphics_type(self, name):
inst = self.get_instance(name)
2022-11-02 05:54:35 +00:00
console_type = util.get_xml_path(
inst.XMLDesc(0), "/domain/devices/graphics/@type"
)
return "None" if console_type is None else console_type
def graphics_listen(self, name):
inst = self.get_instance(name)
2022-11-02 05:54:35 +00:00
listener_addr = util.get_xml_path(
inst.XMLDesc(0), "/domain/devices/graphics/@listen"
)
if listener_addr is None:
2022-11-02 05:54:35 +00:00
listener_addr = util.get_xml_path(
inst.XMLDesc(0), "/domain/devices/graphics/listen/@address"
)
return "None" if listener_addr is None else listener_addr
def graphics_port(self, name):
inst = self.get_instance(name)
2022-11-02 05:54:35 +00:00
console_port = util.get_xml_path(
inst.XMLDesc(0), "/domain/devices/graphics/@port"
)
return "None" if console_port is None else console_port
def domain_name(self, name):
inst = self.get_instance(name)
domname = util.get_xml_path(inst.XMLDesc(0), "/domain/name")
return "NoName" if domname is None else domname
def graphics_passwd(self, name):
inst = self.get_instance(name)
2022-11-02 05:54:35 +00:00
password = util.get_xml_path(
inst.XMLDesc(VIR_DOMAIN_XML_SECURE), "/domain/devices/graphics/@passwd"
)
return "None" if password is None else password
2015-02-27 08:53:51 +00:00
class wvmInstance(wvmConnect):
def __init__(self, host, login, passwd, conn, vname):
wvmConnect.__init__(self, host, login, passwd, conn)
self._ip_cache = None
2015-02-27 08:53:51 +00:00
self.instance = self.get_instance(vname)
def osinfo(self):
info_results = qemuAgentCommand(
2022-11-02 05:54:35 +00:00
self.instance,
'{"execute":"guest-get-osinfo"}',
VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT,
0,
)
timezone_results = qemuAgentCommand(
2022-11-02 05:54:35 +00:00
self.instance,
'{"execute":"guest-get-timezone"}',
VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT,
0,
)
hostname_results = qemuAgentCommand(
2022-11-02 05:54:35 +00:00
self.instance,
'{"execute":"guest-get-host-name"}',
VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT,
0,
)
2022-11-02 05:54:35 +00:00
info_results = json.loads(info_results).get("return")
2022-11-02 05:54:35 +00:00
timezone_results = json.loads(timezone_results).get("return")
hostname_results = json.loads(hostname_results).get("return")
info_results.update(timezone_results)
info_results.update(hostname_results)
return info_results
2023-04-13 09:24:13 +00:00
def start(self, flags=0):
self.instance.createWithFlags(flags)
2015-02-27 08:53:51 +00:00
def shutdown(self):
self.instance.shutdown()
def force_shutdown(self):
self.instance.destroy()
def managedsave(self):
self.instance.managedSave(0)
def managed_save_remove(self):
self.instance.managedSaveRemove(0)
def suspend(self):
self.instance.suspend()
def resume(self):
self.instance.resume()
def delete(self, flags=0):
self.instance.undefineFlags(flags)
2015-02-27 08:53:51 +00:00
def _XMLDesc(self, flag):
return self.instance.XMLDesc(flag)
def _defineXML(self, xml):
return self.wvm.defineXML(xml)
def get_status(self):
"""
VIR_DOMAIN_NOSTATE = 0
VIR_DOMAIN_RUNNING = 1
VIR_DOMAIN_PAUSED = 3
VIR_DOMAIN_SHUTOFF = 5
"""
2015-02-27 08:53:51 +00:00
return self.instance.info()[0]
def get_autostart(self):
return self.instance.autostart()
def set_autostart(self, flag):
return self.instance.setAutostart(flag)
def get_uuid(self):
return self.instance.UUIDString()
def get_vcpu(self):
vcpu = util.get_xml_path(self._XMLDesc(0), "/domain/vcpu")
return int(vcpu)
def get_cur_vcpu(self):
cur_vcpu = util.get_xml_path(self._XMLDesc(0), "/domain/vcpu/@current")
return int(cur_vcpu) if cur_vcpu else self.get_vcpu()
2015-02-27 08:53:51 +00:00
def get_vcpu_mode(self):
return util.get_xml_path(self._XMLDesc(0), "/domain/cpu/@current")
def get_arch(self):
return util.get_xml_path(self._XMLDesc(0), "/domain/os/type/@arch")
def get_machine_type(self):
return util.get_xml_path(self._XMLDesc(0), "/domain/os/type/@machine")
def get_dom_emulator(self):
return util.get_xml_path(self._XMLDesc(0), "/domain/devices/emulator")
def get_nvram(self):
return util.get_xml_path(self._XMLDesc(0), "/domain/os/nvram")
def get_loader(self):
xml = self._XMLDesc(0)
loader = util.get_xml_path(xml, "/domain/os/loader")
2020-04-24 16:34:29 +00:00
loader_type = util.get_xml_path(xml, "/domain/os/loader/@type")
readonly = util.get_xml_path(xml, "/domain/os/loader/@readonly")
2020-04-24 16:34:29 +00:00
return {"loader": loader, "type": loader_type, "readonly": readonly}
def get_vcpus(self):
vcpus = OrderedDict()
tree = etree.fromstring(self._XMLDesc(0))
for vcpu in tree.xpath("/domain/vcpus/vcpu"):
2020-04-24 16:34:29 +00:00
vcpu_id = vcpu.get("id")
enabled = vcpu.get("enabled")
hotplug = vcpu.get("hotpluggable")
order = vcpu.get("order")
2022-11-02 05:54:35 +00:00
vcpus[vcpu_id] = {
"enabled": enabled,
"hotpluggable": hotplug,
"order": order,
}
return vcpus
2015-02-27 08:53:51 +00:00
def get_memory(self):
mem = util.get_xml_path(self._XMLDesc(0), "/domain/memory")
return int(mem) // 1024
2015-02-27 08:53:51 +00:00
def get_cur_memory(self):
mem = util.get_xml_path(self._XMLDesc(0), "/domain/currentMemory")
return int(mem) // 1024
2015-02-27 08:53:51 +00:00
def get_title(self):
title = util.get_xml_path(self._XMLDesc(0), "/domain/title")
return title or ""
def get_filterrefs(self):
def filterrefs(ctx):
result = []
for net in ctx.xpath("/domain/devices/interface"):
filterref = net.xpath("filterref/@filter")
if filterref:
result.append(filterref[0])
return result
return util.get_xml_path(self._XMLDesc(0), func=filterrefs)
2015-02-27 08:53:51 +00:00
def get_description(self):
description = util.get_xml_path(self._XMLDesc(0), "/domain/description")
return description or ""
2015-02-27 08:53:51 +00:00
def get_max_memory(self):
return self.wvm.getInfo()[1] * 1048576
def get_max_cpus(self):
"""Get number of physical CPUs."""
hostinfo = self.wvm.getInfo()
pcpus = hostinfo[4] * hostinfo[5] * hostinfo[6] * hostinfo[7]
return range(1, int(pcpus + 1))
2015-02-27 08:53:51 +00:00
def get_interface_addresses(self, iface_mac):
if self._ip_cache is None:
self.refresh_interface_addresses()
qemuga = self._ip_cache["qemuga"]
arp = self._ip_cache["arp"]
leases = []
def extract_dom(info):
ipv4 = []
ipv6 = []
for addrs in info.values():
if addrs["hwaddr"] != iface_mac:
continue
if not addrs["addrs"]:
continue
for addr in addrs["addrs"]:
if addr["type"] == 0:
ipv4.append(addr["addr"])
elif addr["type"] == 1 and not str(addr["addr"]).startswith("fe80"):
ipv6.append(addr["addr"] + "/" + str(addr["prefix"]))
return ipv4, ipv6
def extract_lease(info):
ipv4 = []
ipv6 = []
if info["mac"] == iface_mac:
if info["type"] == 0:
ipv4.append(info["ipaddr"])
elif info["type"] == 1:
ipv6.append(info["ipaddr"])
return ipv4, ipv6
for ips in [qemuga] + leases + [arp]:
ipv4, ipv6 = extract_lease(ips) if "expirytime" in ips else extract_dom(ips)
if ipv4 or ipv6:
return ipv4, ipv6
return None, None
def _get_interface_addresses(self, source):
# ("Calling interfaceAddresses source=%s", source)
with contextlib.suppress(libvirtError):
return self.instance.interfaceAddresses(source)
return {}
def refresh_interface_addresses(self):
self._ip_cache = {"qemuga": {}, "arp": {}}
if self.get_status() != 1:
return
if self.is_agent_ready():
2022-11-02 05:54:35 +00:00
self._ip_cache["qemuga"] = self._get_interface_addresses(
VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT
)
2015-02-27 08:53:51 +00:00
arp_flag = 3 # libvirt."VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_ARP"
self._ip_cache["arp"] = self._get_interface_addresses(arp_flag)
def get_net_devices(self):
2015-02-27 08:53:51 +00:00
def networks(ctx):
result = []
inbound = outbound = []
for net in ctx.xpath("/domain/devices/interface"):
interface_type = net.xpath("@type")[0]
mac_inst = net.xpath("mac/@address")[0]
nic_inst = net.xpath("source/@network|source/@bridge|source/@dev")[0]
target_inst = net.xpath("target/@dev")[0] if net.xpath("target/@dev") else ""
link_state = net.xpath("link/@state")[0] if net.xpath("link") else "up"
filterref_inst = net.xpath("filterref/@filter")[0] if net.xpath("filterref/@filter") else ""
model_type = net.xpath("model/@type")[0]
if net.xpath("bandwidth/inbound"):
in_attr = net.xpath("bandwidth/inbound")[0]
in_av = in_attr.get("average")
in_peak = in_attr.get("peak")
in_burst = in_attr.get("burst")
inbound = {"average": in_av, "peak": in_peak, "burst": in_burst}
if net.xpath("bandwidth/outbound"):
out_attr = net.xpath("bandwidth/outbound")[0]
out_av = out_attr.get("average")
out_peak = out_attr.get("peak")
out_burst = out_attr.get("burst")
outbound = {"average": out_av, "peak": out_peak, "burst": out_burst}
2015-02-27 08:53:51 +00:00
try:
ipv4, ipv6 = self.get_interface_addresses(mac_inst)
2020-04-24 16:34:29 +00:00
except libvirtError:
ipv4, ipv6 = None, None
result.append(
{
"type": interface_type,
"mac": mac_inst,
"nic": nic_inst,
"target": target_inst,
"state": link_state,
"model": model_type,
"ipv4": ipv4,
"ipv6": ipv6,
"filterref": filterref_inst,
"inbound": inbound,
"outbound": outbound,
}
)
2015-02-27 08:53:51 +00:00
return result
return util.get_xml_path(self._XMLDesc(0), func=networks)
def get_disk_devices(self):
def disks(doc):
2015-02-27 08:53:51 +00:00
result = []
2020-07-13 09:33:09 +00:00
for disk in doc.xpath("/domain/devices/disk"):
dev = volume = storage = src_file = bus = None
disk_format = used_size = disk_size = None
disk_cache = disk_io = disk_discard = disk_zeroes = "default"
readonly = shareable = serial = None
2023-04-04 13:38:12 +00:00
backing_file = None
device = disk.xpath("@device")[0]
if device == "disk":
2015-02-27 08:53:51 +00:00
try:
dev = disk.xpath("target/@dev")[0]
bus = disk.xpath("target/@bus")[0]
2020-07-13 09:33:09 +00:00
try:
2022-11-02 05:54:35 +00:00
src_file = disk.xpath(
"source/@file|source/@dev|source/@name"
)[0]
except Exception:
v = disk.xpath("source/@volume")[0]
s_name = disk.xpath("source/@pool")[0]
2020-07-13 09:33:09 +00:00
s = self.wvm.storagePoolLookupByName(s_name)
src_file = s.storageVolLookupByName(v).path()
2022-11-02 13:05:41 +00:00
with contextlib.suppress(Exception):
disk_format = disk.xpath("driver/@type")[0]
2022-11-02 13:05:41 +00:00
with contextlib.suppress(Exception):
disk_cache = disk.xpath("driver/@cache")[0]
2022-11-02 13:05:41 +00:00
with contextlib.suppress(Exception):
disk_io = disk.xpath("driver/@io")[0]
2022-11-02 13:05:41 +00:00
with contextlib.suppress(Exception):
disk_discard = disk.xpath("driver/@discard")[0]
2022-11-02 13:05:41 +00:00
with contextlib.suppress(Exception):
disk_zeroes = disk.xpath("driver/@detect_zeroes")[0]
2023-04-04 13:38:12 +00:00
with contextlib.suppress(Exception):
backing_file = disk.xpath("backingStore/source/@file")[0]
readonly = bool(disk.xpath("readonly"))
shareable = bool(disk.xpath("shareable"))
2022-11-02 05:54:35 +00:00
serial = (
disk.xpath("serial")[0].text
if disk.xpath("serial")
else None
)
2015-02-27 08:53:51 +00:00
try:
2019-01-09 12:31:42 +00:00
vol = self.get_volume_by_path(src_file)
2015-02-27 08:53:51 +00:00
volume = vol.name()
2015-03-13 15:30:09 +00:00
disk_size = vol.info()[1]
used_size = vol.info()[2]
2015-02-27 08:53:51 +00:00
stg = vol.storagePoolLookupByVolume()
storage = stg.name()
except libvirtError:
2019-01-09 12:31:42 +00:00
volume = src_file
2020-07-13 09:33:09 +00:00
except Exception as e:
print(f"Exception: {e}")
2015-02-27 08:53:51 +00:00
finally:
result.append(
{
"dev": dev,
"bus": bus,
"image": volume,
"storage": storage,
"path": src_file,
"format": disk_format,
2023-04-04 13:38:12 +00:00
"backing_file": backing_file,
"size": disk_size,
"used": used_size,
"cache": disk_cache,
"io": disk_io,
"discard": disk_discard,
"detect_zeroes": disk_zeroes,
"readonly": readonly,
"shareable": shareable,
"serial": serial,
}
)
2015-02-27 08:53:51 +00:00
return result
return util.get_xml_path(self._XMLDesc(0), func=disks)
def get_media_devices(self):
def disks(doc):
2015-02-27 08:53:51 +00:00
result = []
dev = volume = storage = bus = None
2019-01-09 12:31:42 +00:00
src_file = None
for media in doc.xpath("/domain/devices/disk"):
device = media.xpath("@device")[0]
if device == "cdrom":
2015-02-27 08:53:51 +00:00
try:
dev = media.xpath("target/@dev")[0]
bus = media.xpath("target/@bus")[0]
2022-11-02 13:05:41 +00:00
src_file = None
volume = src_file
with contextlib.suppress(Exception):
src_file = media.xpath("source/@file")[0]
2019-01-09 12:31:42 +00:00
vol = self.get_volume_by_path(src_file)
2015-02-27 08:53:51 +00:00
volume = vol.name()
stg = vol.storagePoolLookupByVolume()
storage = stg.name()
except Exception:
2015-02-27 08:53:51 +00:00
pass
finally:
2022-11-02 05:54:35 +00:00
result.append(
{
"dev": dev,
"image": volume,
"storage": storage,
"path": src_file,
"bus": bus,
}
)
2015-02-27 08:53:51 +00:00
return result
return util.get_xml_path(self._XMLDesc(0), func=disks)
def get_bootmenu(self):
menu = util.get_xml_path(self._XMLDesc(0), "/domain/os/bootmenu/@enable")
return menu == "yes"
def set_bootmenu(self, flag):
tree = ElementTree.fromstring(self._XMLDesc(0))
os = tree.find("os")
menu = os.find("bootmenu")
2019-09-10 06:48:31 +00:00
if menu is None:
bootmenu = ElementTree.fromstring("<bootmenu enable='yes'/>")
os.append(bootmenu)
menu = os.find("bootmenu")
if flag == 0: # Disable
menu.attrib["enable"] = "no"
elif flag == 1: # Enable
menu.attrib["enable"] = "yes"
elif flag == -1: # Remove
os.remove(menu)
else:
2022-11-02 05:54:35 +00:00
raise Exception(
"Unknown boot menu option, please choose one of 0:disable, 1:enable, -1:remove"
)
xmldom = ElementTree.tostring(tree).decode()
self._defineXML(xmldom)
def get_bootorder(self):
boot_order = {}
dev_type = target = None
tree = ElementTree.fromstring(self._XMLDesc(0))
os = tree.find("os")
boot = os.findall("boot")
for idx, b in enumerate(boot):
dev = b.get("dev")
if dev == "cdrom":
target = "cdrom"
dev_type = "file"
elif dev == "fd":
target = "floppy"
dev_type = "file"
elif dev == "hd":
target = "disk"
dev_type = "file"
elif dev == "network":
target = "network"
dev_type = "network"
boot_order[idx] = {"type": dev_type, "dev": dev, "target": target}
devices = tree.find("devices")
for dev in devices:
dev_target = None
boot_dev = dev.find("boot")
2019-09-10 06:48:31 +00:00
if boot_dev is not None:
idx = boot_dev.get("order")
dev_type = dev.get("type")
dev_device = dev.get("device")
if dev_type == "file":
dev_target = dev.find("target").get("dev")
elif dev_type == "network":
dev_mac = dev.find("mac").get("address")
dev_device = "network"
dev_target = f"nic-{dev_mac[9:]}"
# pass dev_type usb
2022-11-02 05:54:35 +00:00
boot_order[int(idx) - 1] = {
"type": dev_type,
"dev": dev_device,
"target": dev_target,
}
return boot_order
def set_bootorder(self, devorder):
if not devorder:
return
def remove_bootorder():
tree = ElementTree.fromstring(self._XMLDesc(0))
os = tree.find("os")
boot = os.findall("boot")
# Remove old style boot order
for b in boot:
os.remove(b)
# Remove rest of them
for dev in tree.find("devices"):
boot_dev = dev.find("boot")
2019-09-10 06:48:31 +00:00
if boot_dev is not None:
dev.remove(boot_dev)
return tree
tree = remove_bootorder()
for idx, dev in devorder.items():
order = ElementTree.fromstring("<boot order='{}'/>".format(idx + 1))
if dev["type"] == "disk":
devices = tree.findall("./devices/disk[@device='disk']")
for d in devices:
device = d.find("./target[@dev='{}']".format(dev["dev"]))
2019-09-10 06:48:31 +00:00
if device is not None:
d.append(order)
elif dev["type"] == "cdrom":
devices = tree.findall("./devices/disk[@device='cdrom']")
for d in devices:
device = d.find("./target[@dev='{}']".format(dev["dev"]))
2019-09-10 06:48:31 +00:00
if device is not None:
d.append(order)
elif dev["type"] == "network":
devices = tree.findall("./devices/interface[@type='network']")
for d in devices:
device = d.find("mac[@address='{}']".format(dev["dev"]))
2019-09-10 06:48:31 +00:00
if device is not None:
d.append(order)
else:
raise Exception("Invalid Device Type for boot order")
self._defineXML(ElementTree.tostring(tree).decode())
2015-02-27 08:53:51 +00:00
def mount_iso(self, dev, image):
def attach_iso(dev, disk, vol):
if disk.get("device") == "cdrom":
2015-02-27 08:53:51 +00:00
for elm in disk:
if elm.tag == "target" and elm.get("dev") == dev:
src_media = ElementTree.Element("source")
src_media.set("file", vol.path())
disk.insert(2, src_media)
return True
2015-02-27 08:53:51 +00:00
vol = None
storages = self.get_storages(only_actives=True)
2015-02-27 08:53:51 +00:00
for storage in storages:
stg = self.get_storage(storage)
if stg.info()[0] != 0:
for img in stg.listVolumes():
if image == img:
vol = stg.storageVolLookupByName(image)
tree = ElementTree.fromstring(self._XMLDesc(0))
for disk in tree.findall("devices/disk"):
2015-02-27 08:53:51 +00:00
if attach_iso(dev, disk, vol):
break
if self.get_status() == 1:
xml = ElementTree.tostring(disk).decode()
2015-02-27 08:53:51 +00:00
self.instance.attachDevice(xml)
xmldom = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
if self.get_status() == 5:
xmldom = ElementTree.tostring(tree).decode()
2015-02-27 08:53:51 +00:00
self._defineXML(xmldom)
def umount_iso(self, dev, image):
tree = ElementTree.fromstring(self._XMLDesc(0))
for disk in tree.findall("devices/disk"):
if disk.get("device") == "cdrom":
2015-02-27 08:53:51 +00:00
for elm in disk:
if elm.tag == "source" and elm.get("file") == image:
src_media = elm
if elm.tag == "target" and elm.get("dev") == dev:
disk.remove(src_media)
2015-02-27 08:53:51 +00:00
if self.get_status() == 1:
xml_disk = ElementTree.tostring(disk).decode()
2015-02-27 08:53:51 +00:00
self.instance.attachDevice(xml_disk)
xmldom = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
if self.get_status() == 5:
xmldom = ElementTree.tostring(tree).decode()
2015-02-27 08:53:51 +00:00
self._defineXML(xmldom)
def attach_disk(
self,
target_dev,
source,
2022-11-02 05:54:35 +00:00
source_info=None,
pool_type="dir",
target_bus="ide",
disk_type="file",
disk_device="disk",
driver_name="qemu",
format_type="raw",
readonly=False,
shareable=False,
serial=None,
cache_mode=None,
io_mode=None,
discard_mode=None,
detect_zeroes_mode=None,
):
additionals = ""
2022-11-02 05:54:35 +00:00
if (
cache_mode is not None
and cache_mode != "default"
and disk_device != "cdrom"
):
2020-07-13 09:33:09 +00:00
additionals += f"cache='{cache_mode}' "
if io_mode is not None and io_mode != "default":
2020-05-21 13:17:25 +00:00
additionals += f"io='{io_mode}' "
if discard_mode is not None and discard_mode != "default":
2020-05-21 13:17:25 +00:00
additionals += f"discard='{discard_mode}' "
if detect_zeroes_mode is not None and detect_zeroes_mode != "default":
2020-05-21 13:17:25 +00:00
additionals += f"detect_zeroes='{detect_zeroes_mode}' "
2020-05-21 13:17:25 +00:00
xml_disk = f"<disk type='{disk_type}' device='{disk_device}'>"
if disk_device == "cdrom":
xml_disk += f"<driver name='{driver_name}' type='{format_type}'/>"
elif disk_device == "disk":
2022-11-02 05:54:35 +00:00
xml_disk += (
f"<driver name='{driver_name}' type='{format_type}' {additionals}/>"
)
2022-11-02 05:54:35 +00:00
if disk_type == "file":
xml_disk += f"<source file='{source}'/>"
2022-11-02 05:54:35 +00:00
elif disk_type == "network":
if pool_type == "rbd":
auth_type = source_info.get("auth_type")
auth_user = source_info.get("auth_user")
auth_uuid = source_info.get("auth_uuid")
xml_disk += f"""<auth username='{auth_user}'>
<secret type='{auth_type}' uuid='{auth_uuid}'/>
</auth>"""
xml_disk += f"""<source protocol='{pool_type}' name='{source}'>"""
for host in source_info.get("hosts"):
2022-11-02 05:54:35 +00:00
if host.get("hostport"):
xml_disk += f"""<host name="{host.get('hostname')}" port='{host.get('hostport')}'/>"""
else:
xml_disk += f"""<host name="{host.get('hostname')}"/>"""
2022-11-02 05:54:35 +00:00
xml_disk += """</source>"""
else:
raise Exception("Not implemented disk type")
else:
raise Exception("Not implemented disk type")
2022-11-02 05:54:35 +00:00
xml_disk += f"<target dev='{target_dev}' bus='{target_bus}'/>"
if readonly or disk_device == "cdrom":
xml_disk += """<readonly/>"""
if shareable:
xml_disk += """<shareable/>"""
if serial is not None and serial != "None" and serial != "":
2020-05-21 13:17:25 +00:00
xml_disk += f"""<serial>{serial}</serial>"""
xml_disk += """</disk>"""
if self.get_status() == 1:
self.instance.attachDeviceFlags(xml_disk, VIR_DOMAIN_AFFECT_LIVE)
self.instance.attachDeviceFlags(xml_disk, VIR_DOMAIN_AFFECT_CONFIG)
if self.get_status() == 5:
self.instance.attachDeviceFlags(xml_disk, VIR_DOMAIN_AFFECT_CONFIG)
def detach_disk(self, target_dev):
tree = etree.fromstring(self._XMLDesc(0))
2022-11-02 05:54:35 +00:00
disk_el = tree.xpath("./devices/disk/target[@dev='{}']".format(target_dev))[
0
].getparent()
xml_disk = etree.tostring(disk_el).decode()
devices = tree.find("devices")
devices.remove(disk_el)
if self.get_status() == 1:
self.instance.detachDeviceFlags(xml_disk, VIR_DOMAIN_AFFECT_LIVE)
self.instance.detachDeviceFlags(xml_disk, VIR_DOMAIN_AFFECT_CONFIG)
if self.get_status() == 5:
self.instance.detachDeviceFlags(xml_disk, VIR_DOMAIN_AFFECT_CONFIG)
def edit_disk(
self,
target_dev,
source,
readonly,
shareable,
target_bus,
serial,
format,
cache_mode,
io_mode,
discard_mode,
detect_zeroes_mode,
):
tree = etree.fromstring(self._XMLDesc(0))
2022-11-02 05:54:35 +00:00
disk_el = tree.xpath("./devices/disk/target[@dev='{}']".format(target_dev))[
0
].getparent()
old_disk_type = disk_el.get("type")
old_disk_device = disk_el.get("device")
old_driver_name = disk_el.xpath("driver/@name")[0]
old_target_bus = disk_el.xpath("target/@bus")[0]
additionals = ""
if cache_mode is not None and cache_mode != "default":
2020-05-21 13:17:25 +00:00
additionals += f"cache='{cache_mode}' "
if io_mode is not None and io_mode != "default":
2020-05-21 13:17:25 +00:00
additionals += f"io='{io_mode}' "
if discard_mode is not None and discard_mode != "default":
2020-05-21 13:17:25 +00:00
additionals += f"discard='{discard_mode}' "
if detect_zeroes_mode is not None and detect_zeroes_mode != "default":
2020-05-21 13:17:25 +00:00
additionals += f"detect_zeroes='{detect_zeroes_mode}' "
2020-05-21 13:17:25 +00:00
xml_disk = f"<disk type='{old_disk_type}' device='{old_disk_device}'>"
if old_disk_device == "cdrom":
2020-05-21 13:17:25 +00:00
xml_disk += f"<driver name='{old_driver_name}' type='{format}'/>"
elif old_disk_device == "disk":
2022-11-02 05:54:35 +00:00
xml_disk += (
f"<driver name='{old_driver_name}' type='{format}' {additionals}/>"
)
2020-05-21 13:17:25 +00:00
xml_disk += f"""<source file='{source}'/>
<target dev='{target_dev}' bus='{target_bus}'/>"""
if readonly:
xml_disk += """<readonly/>"""
if shareable:
xml_disk += """<shareable/>"""
if serial is not None and serial != "None" and serial != "":
2020-05-21 13:17:25 +00:00
xml_disk += f"""<serial>{serial}</serial>"""
xml_disk += """</disk>"""
self.instance.updateDeviceFlags(xml_disk, VIR_DOMAIN_AFFECT_CONFIG)
2015-02-27 08:53:51 +00:00
def cpu_usage(self):
cpu_usage = {}
if self.get_status() == 1:
nbcore = self.wvm.getInfo()[2]
cpu_use_ago = self.instance.info()[4]
time.sleep(1)
cpu_use_now = self.instance.info()[4]
diff_usage = cpu_use_now - cpu_use_ago
2022-11-02 05:54:35 +00:00
cpu_usage["cpu"] = 100 * diff_usage / (1 * nbcore * 10**9)
2015-02-27 08:53:51 +00:00
else:
cpu_usage["cpu"] = 0
2015-02-27 08:53:51 +00:00
return cpu_usage
def set_vcpu(self, cpu_id, enabled):
self.instance.setVcpu(str(cpu_id), enabled)
def set_vcpu_hotplug(self, status, vcpus_hotplug=0):
2022-11-02 05:54:35 +00:00
"""vcpus_hotplug = 0 make all vpus hotpluggable"""
vcpus_hotplug = int(self.get_vcpu()) if vcpus_hotplug == 0 else vcpus_hotplug
if self.get_status() == 5: # shutoff
if status:
xml = """ <vcpus>"""
xml += """<vcpu id='0' enabled='yes' hotpluggable='no' order='1'/>"""
for i in range(1, vcpus_hotplug):
2020-05-21 13:17:25 +00:00
xml += f"""<vcpu id='{i}' enabled='yes' hotpluggable='yes' order='{i+1}'/>"""
xml += """</vcpus>"""
tree = etree.fromstring(self._XMLDesc(0))
vcpus = tree.xpath("/domain/vcpus")
if not vcpus:
tree.append(etree.fromstring(xml))
self._defineXML(etree.tostring(tree).decode())
else:
tree = etree.fromstring(self._XMLDesc(0))
vcpus = tree.xpath("/domain/vcpus")
for vcpu in vcpus:
parent = vcpu.getparent()
parent.remove(vcpu)
self._defineXML(etree.tostring(tree).decode())
else:
2022-11-02 05:54:35 +00:00
raise libvirtError(
"Please shutdown the instance then try to enable vCPU hotplug"
)
2019-05-21 06:07:01 +00:00
def mem_usage(self):
mem_usage = {}
if self.get_status() == 1:
mem_stats = self.instance.memoryStats()
rss = mem_stats["rss"] if "rss" in mem_stats else 0
total = mem_stats["actual"] if "actual" in mem_stats else 0
2019-05-21 06:07:01 +00:00
available = total - rss
if available < 0:
available = 0
2019-05-21 06:07:01 +00:00
mem_usage["used"] = rss
mem_usage["total"] = total
2019-05-21 06:07:01 +00:00
else:
mem_usage["used"] = 0
mem_usage["total"] = 0
2019-05-21 06:07:01 +00:00
return mem_usage
2015-02-27 08:53:51 +00:00
def disk_usage(self):
devices = []
dev_usage = []
tree = ElementTree.fromstring(self._XMLDesc(0))
for disk in tree.findall("devices/disk"):
if disk.get("device") == "disk":
2015-02-27 08:53:51 +00:00
dev_file = None
dev_bus = None
network_disk = True
for elm in disk:
if elm.tag == "source":
if elm.get("protocol"):
dev_file = elm.get("protocol")
2015-02-27 08:53:51 +00:00
network_disk = True
if elm.get("file"):
dev_file = elm.get("file")
if elm.get("dev"):
dev_file = elm.get("dev")
if elm.tag == "target":
dev_bus = elm.get("dev")
2015-02-27 08:53:51 +00:00
if (dev_file and dev_bus) is not None:
if network_disk:
dev_file = dev_bus
devices.append([dev_file, dev_bus])
for dev in devices:
2015-03-23 12:28:24 +00:00
if self.get_status() == 1:
rd_use_ago = self.instance.blockStats(dev[0])[1]
wr_use_ago = self.instance.blockStats(dev[0])[3]
time.sleep(1)
rd_use_now = self.instance.blockStats(dev[0])[1]
wr_use_now = self.instance.blockStats(dev[0])[3]
rd_diff_usage = rd_use_now - rd_use_ago
wr_diff_usage = wr_use_now - wr_use_ago
else:
rd_diff_usage = 0
wr_diff_usage = 0
dev_usage.append({"dev": dev[1], "rd": rd_diff_usage, "wr": wr_diff_usage})
2015-02-27 08:53:51 +00:00
return dev_usage
def net_usage(self):
devices = []
dev_usage = []
2015-03-23 12:28:24 +00:00
if self.get_status() == 1:
tree = ElementTree.fromstring(self._XMLDesc(0))
for target in tree.findall("devices/interface/target"):
devices.append(target.get("dev"))
for i, dev in enumerate(devices):
rx_use_ago = self.instance.interfaceStats(dev)[0]
tx_use_ago = self.instance.interfaceStats(dev)[4]
time.sleep(1)
rx_use_now = self.instance.interfaceStats(dev)[0]
tx_use_now = self.instance.interfaceStats(dev)[4]
rx_diff_usage = (rx_use_now - rx_use_ago) * 8
tx_diff_usage = (tx_use_now - tx_use_ago) * 8
dev_usage.append({"dev": i, "rx": rx_diff_usage, "tx": tx_diff_usage})
2015-03-23 12:28:24 +00:00
else:
for i, dev in enumerate(self.get_net_devices()):
dev_usage.append({"dev": i, "rx": 0, "tx": 0})
2015-02-27 08:53:51 +00:00
return dev_usage
def get_telnet_port(self):
telnet_port = None
service_port = None
tree = ElementTree.fromstring(self._XMLDesc(0))
for console in tree.findall("devices/console"):
if console.get("type") == "tcp":
2015-02-27 08:53:51 +00:00
for elm in console:
if elm.tag == "source":
if elm.get("service"):
service_port = elm.get("service")
if elm.tag == "protocol":
if elm.get("type") == "telnet":
2015-02-27 08:53:51 +00:00
if service_port is not None:
telnet_port = service_port
return telnet_port
def get_console_listener_addr(self):
2022-11-02 05:54:35 +00:00
listener_addr = util.get_xml_path(
self._XMLDesc(0), "/domain/devices/graphics/@listen"
)
if listener_addr is None:
2022-11-02 05:54:35 +00:00
listener_addr = util.get_xml_path(
self._XMLDesc(0), "/domain/devices/graphics/listen/@address"
)
if listener_addr is None:
2019-09-10 06:48:31 +00:00
return "127.0.0.1"
return listener_addr
2015-02-27 08:53:51 +00:00
def set_console_listener_addr(self, listener_addr):
xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
root = ElementTree.fromstring(xml)
console_type = self.get_console_type()
try:
graphic = root.find("devices/graphics[@type='%s']" % console_type)
except SyntaxError:
# Little fix for old version ElementTree
graphic = root.find("devices/graphics")
if graphic is None:
return False
listen = graphic.find("listen[@type='address']")
if listen is None:
return False
if listener_addr:
graphic.set("listen", listener_addr)
listen.set("address", listener_addr)
else:
2022-11-02 13:05:41 +00:00
with contextlib.suppress(Exception):
graphic.attrib.pop("listen")
listen.attrib.pop("address")
2022-11-02 13:05:41 +00:00
newxml = ElementTree.tostring(root).decode()
return self._defineXML(newxml)
2020-04-24 16:34:29 +00:00
2015-02-27 08:53:51 +00:00
def get_console_socket(self):
2018-09-21 13:50:44 +00:00
socket = util.get_xml_path(self._XMLDesc(0), "/domain/devices/graphics/@socket")
2015-02-27 08:53:51 +00:00
return socket
def get_console_type(self):
2022-11-02 05:54:35 +00:00
console_type = util.get_xml_path(
self._XMLDesc(0), "/domain/devices/graphics/@type"
)
if console_type is None:
2022-11-02 05:54:35 +00:00
console_type = util.get_xml_path(
self._XMLDesc(0), "/domain/devices/console/@type"
)
2015-02-27 08:53:51 +00:00
return console_type
def set_console_type(self, console_type):
current_type = self.get_console_type()
if current_type == console_type:
return True
if console_type == "":
2015-02-27 08:53:51 +00:00
return False
xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
root = ElementTree.fromstring(xml)
try:
2020-05-21 13:17:25 +00:00
graphic = root.find(f"devices/graphics[@type='{current_type}']")
2015-02-27 08:53:51 +00:00
except SyntaxError:
# Little fix for old version ElementTree
graphic = root.find("devices/graphics")
graphic.set("type", console_type)
newxml = ElementTree.tostring(root).decode()
2015-02-27 08:53:51 +00:00
self._defineXML(newxml)
def get_console_port(self, console_type=None):
if console_type is None:
console_type = self.get_console_type()
2022-11-02 05:54:35 +00:00
port = util.get_xml_path(
self._XMLDesc(0),
"/domain/devices/graphics[@type='%s']/@port" % console_type,
)
2015-02-27 08:53:51 +00:00
return port
def get_console_websocket_port(self):
console_type = self.get_console_type()
websocket_port = util.get_xml_path(
2022-11-02 05:54:35 +00:00
self._XMLDesc(0),
"/domain/devices/graphics[@type='%s']/@websocket" % console_type,
)
2015-02-27 08:53:51 +00:00
return websocket_port
def get_console_passwd(self):
2022-11-02 05:54:35 +00:00
return util.get_xml_path(
self._XMLDesc(VIR_DOMAIN_XML_SECURE), "/domain/devices/graphics/@passwd"
)
2015-02-27 08:53:51 +00:00
def set_console_passwd(self, passwd):
xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
root = ElementTree.fromstring(xml)
console_type = self.get_console_type()
try:
2020-05-21 13:17:25 +00:00
graphic = root.find(f"devices/graphics[@type='{console_type}']")
2015-02-27 08:53:51 +00:00
except SyntaxError:
# Little fix for old version ElementTree
graphic = root.find("devices/graphics")
if graphic is None:
return False
if passwd:
graphic.set("passwd", passwd)
2015-02-27 08:53:51 +00:00
else:
2022-11-02 13:05:41 +00:00
with contextlib.suppress(Exception):
graphic.attrib.pop("passwd")
2022-11-02 13:05:41 +00:00
newxml = ElementTree.tostring(root).decode()
2015-02-27 08:53:51 +00:00
return self._defineXML(newxml)
def set_console_keymap(self, keymap):
xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
root = ElementTree.fromstring(xml)
console_type = self.get_console_type()
try:
graphic = root.find("devices/graphics[@type='%s']" % console_type)
except SyntaxError:
# Little fix for old version ElementTree
graphic = root.find("devices/graphics")
if keymap != "auto":
graphic.set("keymap", keymap)
2015-02-27 08:53:51 +00:00
else:
2022-11-02 13:05:41 +00:00
with contextlib.suppress(Exception):
graphic.attrib.pop("keymap")
2022-11-02 13:05:41 +00:00
newxml = ElementTree.tostring(root).decode()
2015-02-27 08:53:51 +00:00
self._defineXML(newxml)
def get_console_keymap(self):
2022-11-02 05:54:35 +00:00
return (
util.get_xml_path(
self._XMLDesc(VIR_DOMAIN_XML_SECURE), "/domain/devices/graphics/@keymap"
)
or ""
)
2015-02-27 08:53:51 +00:00
def get_video_model(self):
2022-11-02 05:54:35 +00:00
""":return only primary video card"""
xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
tree = etree.fromstring(xml)
video_models = tree.xpath("/domain/devices/video/model")
for model in video_models:
if model.get("primary") == "yes" or len(video_models) == 1:
return model.get("type")
def set_video_model(self, model):
2022-11-02 05:54:35 +00:00
"""Changes only primary video card"""
xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
tree = etree.fromstring(xml)
video_models = tree.xpath("/domain/devices/video/model")
video_xml = "<model type='{}'/>".format(model)
for model in video_models:
if model.get("primary") == "yes" or len(video_models) == 1:
parent = model.getparent()
parent.remove(model)
parent.append(etree.fromstring(video_xml))
self._defineXML(etree.tostring(tree).decode())
def resize_cpu(self, cur_vcpu, vcpu):
2015-02-27 08:53:51 +00:00
"""
Function change ram and cpu on instance.
2015-02-27 08:53:51 +00:00
"""
is_vcpus_enabled = self.get_vcpus()
if is_vcpus_enabled:
self.set_vcpu_hotplug(False)
2015-02-27 08:53:51 +00:00
xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
tree = etree.fromstring(xml)
2015-02-27 08:53:51 +00:00
vcpu_elem = tree.find("vcpu")
vcpu_elem.text = vcpu
vcpu_elem.set("current", cur_vcpu)
2015-02-27 08:53:51 +00:00
new_xml = etree.tostring(tree).decode()
2015-02-27 08:53:51 +00:00
self._defineXML(new_xml)
if is_vcpus_enabled:
self.set_vcpu_hotplug(True, int(cur_vcpu))
def resize_mem(self, cur_memory, memory):
"""
Function change ram and cpu on vds.
"""
memory = int(memory) * 1024
cur_memory = int(cur_memory) * 1024
# if dom is running change only ram
if self.get_status() == VIR_DOMAIN_RUNNING:
self.set_memory(cur_memory, VIR_DOMAIN_AFFECT_LIVE)
self.set_memory(cur_memory, VIR_DOMAIN_AFFECT_CONFIG)
return
xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
tree = etree.fromstring(xml)
mem_elem = tree.find("memory")
mem_elem.text = str(memory)
cur_mem_elem = tree.find("currentMemory")
cur_mem_elem.text = str(cur_memory)
new_xml = etree.tostring(tree).decode()
self._defineXML(new_xml)
2019-09-10 06:48:31 +00:00
def resize_disk(self, disks):
"""
Function change disks on vds.
"""
xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
tree = etree.fromstring(xml)
for disk in disks:
source_dev = disk["path"]
vol = self.get_volume_by_path(source_dev)
vol.resize(disk["size_new"])
new_xml = etree.tostring(tree).decode()
self._defineXML(new_xml)
2015-02-27 08:53:51 +00:00
def get_iso_media(self):
iso = []
storages = self.get_storages(only_actives=True)
2015-02-27 08:53:51 +00:00
for storage in storages:
stg = self.get_storage(storage)
if stg.info()[0] != 0:
2022-11-02 13:05:41 +00:00
with contextlib.suppress(Exception):
2015-02-27 08:53:51 +00:00
stg.refresh(0)
2022-11-02 13:05:41 +00:00
2015-02-27 08:53:51 +00:00
for img in stg.listVolumes():
if img.lower().endswith(".iso"):
2015-02-27 08:53:51 +00:00
iso.append(img)
return iso
def delete_all_disks(self):
2023-04-27 13:04:43 +00:00
self.refresh_instance_pools()
disks = self.get_disk_devices()
2015-02-27 08:53:51 +00:00
for disk in disks:
vol = self.get_volume_by_path(disk.get("path"))
2015-02-27 08:53:51 +00:00
vol.delete(0)
def _snapshotCreateXML(self, xml, flag):
self.instance.snapshotCreateXML(xml, flag)
def create_snapshot(self, name, desc=None):
2022-11-02 05:54:35 +00:00
state = "shutoff" if self.get_status() == 5 else "running"
2015-02-27 08:53:51 +00:00
xml = """<domainsnapshot>
<name>%s</name>
<description>%s</description>
<state>%s</state>
<creationTime>%d</creationTime>""" % (
name,
desc,
state,
time.time(),
)
self.change_snapshot_xml()
2015-02-27 08:53:51 +00:00
xml += self._XMLDesc(VIR_DOMAIN_XML_SECURE)
xml += """<active>0</active>
</domainsnapshot>"""
self._snapshotCreateXML(xml, 0)
self.recover_snapshot_xml()
def change_snapshot_xml(self):
2022-11-02 05:54:35 +00:00
xml_temp = self._XMLDesc(VIR_DOMAIN_XML_SECURE).replace(
"<loader readonly='yes' type='pflash'>",
"<loader readonly='yes' type='rom'>",
)
self._defineXML(xml_temp)
def recover_snapshot_xml(self):
2022-11-02 05:54:35 +00:00
xml_temp = self._XMLDesc(VIR_DOMAIN_XML_SECURE).replace(
"<loader readonly='yes' type='rom'>",
"<loader readonly='yes' type='pflash'>",
)
self._defineXML(xml_temp)
2015-02-27 08:53:51 +00:00
2023-04-13 09:24:13 +00:00
def create_external_snapshot(self, name, date=None, desc=None):
2023-04-04 06:51:59 +00:00
creation_time = time.time()
state = "shutoff" if self.get_status() == 5 else "running"
#<seclabel type='none' model='dac' relabel='no'/>
2023-04-04 06:51:59 +00:00
xml = """<domainsnapshot>
<name>%s</name>
<description>%s</description>
<state>%s</state>
2023-04-13 09:24:13 +00:00
<creationTime>%d</creationTime>
""" % (
2023-04-04 06:51:59 +00:00
name,
desc,
state,
creation_time,
)
2023-04-13 09:24:13 +00:00
2023-04-04 06:51:59 +00:00
self.change_snapshot_xml()
xml += self._XMLDesc(VIR_DOMAIN_XML_SECURE)
xml += """<active>0</active>
</domainsnapshot>"""
2023-04-04 13:38:12 +00:00
2023-04-04 06:51:59 +00:00
self._snapshotCreateXML(xml, VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY)
2023-04-27 13:04:43 +00:00
self.refresh_instance_pools()
2023-04-04 06:51:59 +00:00
def get_external_snapshots(self):
2023-04-13 09:24:13 +00:00
return self.get_snapshot(VIR_DOMAIN_SNAPSHOT_LIST_EXTERNAL)
2023-04-04 06:51:59 +00:00
2023-04-13 09:24:13 +00:00
def delete_external_snapshot(self, name):
2023-04-04 13:38:12 +00:00
disk_info = self.get_disk_devices()
for disk in disk_info:
target_dev = disk["dev"]
backing_file = disk["backing_file"]
2023-04-13 09:24:13 +00:00
snap_source_file = disk["path"]
self.instance.blockCommit(target_dev, backing_file, snap_source_file,
flags=VIR_DOMAIN_BLOCK_COMMIT_DELETE|
VIR_DOMAIN_BLOCK_COMMIT_ACTIVE)
2023-04-04 13:38:12 +00:00
while True:
info = self.instance.blockJobInfo(target_dev, 0)
if info.get('cur') == info.get('end'):
self.instance.blockJobAbort(target_dev,flags=VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT)
time.sleep(2)
2023-04-04 13:38:12 +00:00
break
2023-04-13 09:24:13 +00:00
# Check again pool for snapshot delta volume; if it exist, remove it manually
with contextlib.suppress(libvirtError):
vol_snap = self.get_volume_by_path(snap_source_file)
pool = vol_snap.storagePoolLookupByVolume()
pool.refresh(0)
vol_snap.delete(0)
2023-04-04 06:51:59 +00:00
snap = self.instance.snapshotLookupByName(name, 0)
snap.delete(VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY)
2023-04-13 09:24:13 +00:00
2023-04-04 06:51:59 +00:00
2023-04-13 09:24:13 +00:00
def revert_external_snapshot(self, name, date, desc):
2023-04-04 06:51:59 +00:00
snap = self.instance.snapshotLookupByName(name, 0)
2023-04-13 09:24:13 +00:00
snap_xml = snap.getXMLDesc(0)
snapXML = ElementTree.fromstring(snap_xml)
self.start(flags=VIR_DOMAIN_START_PAUSED) if self.get_status() == 5 else None
2023-04-27 13:04:43 +00:00
self.delete_all_disks()
self.force_shutdown()
2023-04-04 06:51:59 +00:00
2023-04-13 09:24:13 +00:00
snap.delete(VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY)
2023-04-27 13:04:43 +00:00
disks = snapXML.findall('inactiveDomain/devices/disk')
if not disks: disks = snapXML.findall('domain/devices/disk')
2023-04-04 06:51:59 +00:00
for disk in disks:
2023-04-13 09:24:13 +00:00
self.instance.updateDeviceFlags(ElementTree.tostring(disk).decode("UTF-8"))
name = name.replace("s1", "s2")
2023-04-13 09:24:13 +00:00
self.create_external_snapshot(name, date, desc)
2023-04-04 06:51:59 +00:00
2023-08-23 06:08:25 +00:00
def get_snapshot(self, flag=VIR_DOMAIN_SNAPSHOT_LIST_INTERNAL|VIR_DOMAIN_SNAPSHOT_LIST_EXTERNAL):
2015-02-27 08:53:51 +00:00
snapshots = []
2023-04-04 06:51:59 +00:00
snapshot_list = self.instance.snapshotListNames(flag)
2015-02-27 08:53:51 +00:00
for snapshot in snapshot_list:
snap = self.instance.snapshotLookupByName(snapshot, 0)
2022-11-02 05:54:35 +00:00
snap_description = util.get_xml_path(
snap.getXMLDesc(0), "/domainsnapshot/description"
)
snap_time_create = util.get_xml_path(
snap.getXMLDesc(0), "/domainsnapshot/creationTime"
)
snapshots.append(
{
"date": datetime.fromtimestamp(int(snap_time_create)),
"name": snapshot,
"description": snap_description,
}
)
2015-02-27 08:53:51 +00:00
return snapshots
def snapshot_delete(self, snapshot):
snap = self.instance.snapshotLookupByName(snapshot, 0)
snap.delete(0)
def snapshot_revert(self, snapshot):
self.change_snapshot_xml()
2015-02-27 08:53:51 +00:00
snap = self.instance.snapshotLookupByName(snapshot, 0)
self.instance.revertToSnapshot(snap, 0)
self.recover_snapshot_xml()
2015-02-27 08:53:51 +00:00
def get_managed_save_image(self):
return self.instance.hasManagedSaveImage(0)
def get_wvmStorage(self, pool):
return wvmStorage(self.host, self.login, self.passwd, self.conn, pool)
def get_wvmStorages(self):
return wvmStorages(self.host, self.login, self.passwd, self.conn)
2023-04-27 13:04:43 +00:00
def refresh_instance_pools(self):
disks = self.get_disk_devices()
target_paths = set()
for disk in disks:
disk_path = disk.get("path")
target_paths.add(os.path.dirname(disk_path))
for target_path in target_paths:
self.get_wvmStorages().get_pool_by_target(target_path).refresh(0)
def fix_mac(self, mac):
if ":" in mac:
return mac
# if mac does not contain ":", try to split into tuples and join with ":"
n = 2
mac_tuples = [mac[i : i + n] for i in range(0, len(mac), n)]
return ":".join(mac_tuples)
2015-02-27 08:53:51 +00:00
def clone_instance(self, clone_data):
clone_dev_path = []
xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
tree = etree.fromstring(xml)
name = tree.find("name")
name.text = clone_data["name"]
uuid = tree.find("uuid")
2015-02-27 08:53:51 +00:00
tree.remove(uuid)
options = {
"title": clone_data.get("clone-title", ""),
"description": clone_data.get("clone-description", ""),
}
self._set_options(tree, options)
src_nvram_path = self.get_nvram()
if src_nvram_path:
# Change XML for nvram
nvram = tree.find("os/nvram")
nvram.getparent().remove(nvram)
# NVRAM CLONE: create pool if nvram is not in a pool. then clone it
src_nvram_name = os.path.basename(src_nvram_path)
nvram_dir = os.path.dirname(src_nvram_path)
nvram_pool_name = os.path.basename(nvram_dir)
try:
self.get_volume_by_path(src_nvram_path)
except libvirtError:
stg_conn = self.get_wvmStorages()
stg_conn.create_storage("dir", nvram_pool_name, None, nvram_dir)
2020-05-21 13:17:25 +00:00
new_nvram_name = f"{clone_data['name']}_VARS"
nvram_stg = self.get_wvmStorage(nvram_pool_name)
nvram_stg.clone_volume(src_nvram_name, new_nvram_name, file_suffix="fd")
for num, net in enumerate(tree.findall("devices/interface")):
elm = net.find("mac")
mac_address = self.fix_mac(clone_data["clone-net-mac-" + str(num)])
elm.set("address", mac_address)
2015-02-27 08:53:51 +00:00
for disk in tree.findall("devices/disk"):
if disk.get("device") == "disk":
elm = disk.find("target")
device_name = elm.get("dev")
2015-02-27 08:53:51 +00:00
if device_name:
target_file = clone_data["disk-" + device_name]
2022-11-02 13:05:41 +00:00
meta_prealloc = False
with contextlib.suppress(Exception):
meta_prealloc = clone_data["meta-" + device_name]
2022-11-02 13:05:41 +00:00
elm.set("dev", device_name)
2015-02-27 08:53:51 +00:00
elm = disk.find("source")
source_file = elm.get("file")
2015-02-27 08:53:51 +00:00
if source_file:
clone_dev_path.append(source_file)
clone_path = os.path.join(os.path.dirname(source_file), target_file)
elm.set("file", clone_path)
2015-02-27 08:53:51 +00:00
vol = self.get_volume_by_path(source_file)
2022-11-02 05:54:35 +00:00
vol_format = util.get_xml_path(
vol.XMLDesc(0), "/volume/target/format/@type"
)
2015-02-27 08:53:51 +00:00
if vol_format == "qcow2" and meta_prealloc:
2015-02-27 08:53:51 +00:00
meta_prealloc = True
2020-05-21 13:17:25 +00:00
vol_clone_xml = f"""
2015-02-27 08:53:51 +00:00
<volume>
2020-05-21 13:17:25 +00:00
<name>{target_file}</name>
2015-02-27 08:53:51 +00:00
<capacity>0</capacity>
<allocation>0</allocation>
<target>
2020-05-21 13:17:25 +00:00
<format type='{vol_format}'/>
<permissions>
<owner>{clone_data['disk_owner_uid']}</owner>
<group>{clone_data['disk_owner_gid']}</group>
<mode>0644</mode>
<label>virt_image_t</label>
</permissions>
<compat>1.1</compat>
<features>
<lazy_refcounts/>
</features>
2015-02-27 08:53:51 +00:00
</target>
2020-05-21 13:17:25 +00:00
</volume>"""
2015-02-27 08:53:51 +00:00
stg = vol.storagePoolLookupByVolume()
stg.createXMLFrom(vol_clone_xml, vol, meta_prealloc)
2020-07-13 09:33:09 +00:00
source_protocol = elm.get("protocol")
if source_protocol == "rbd":
source_name = elm.get("name")
2018-09-04 13:40:47 +00:00
clone_name = "%s/%s" % (os.path.dirname(source_name), target_file)
elm.set("name", clone_name)
2018-09-04 13:40:47 +00:00
vol = self.get_volume_by_path(source_name)
2022-11-02 05:54:35 +00:00
vol_format = util.get_xml_path(
vol.XMLDesc(0), "/volume/target/format/@type"
)
2018-09-04 13:40:47 +00:00
2020-05-21 13:17:25 +00:00
vol_clone_xml = f"""
2018-09-04 13:40:47 +00:00
<volume type='network'>
2020-05-21 13:17:25 +00:00
<name>{target_file}</name>
2018-09-04 13:40:47 +00:00
<capacity>0</capacity>
<allocation>0</allocation>
<target>
2020-05-21 13:17:25 +00:00
<format type='{vol_format}'/>
2018-09-04 13:40:47 +00:00
</target>
2020-05-21 13:17:25 +00:00
</volume>"""
2018-09-04 13:40:47 +00:00
stg = vol.storagePoolLookupByVolume()
stg.createXMLFrom(vol_clone_xml, vol, meta_prealloc)
source_dev = elm.get("dev")
if source_dev:
clone_path = os.path.join(os.path.dirname(source_dev), target_file)
elm.set("dev", clone_path)
2020-07-13 09:33:09 +00:00
vol = self.get_volume_by_path(source_dev)
stg = vol.storagePoolLookupByVolume()
2020-07-13 09:33:09 +00:00
vol_name = util.get_xml_path(vol.XMLDesc(0), "/volume/name")
pool_name = util.get_xml_path(stg.XMLDesc(0), "/pool/name")
2020-07-13 09:33:09 +00:00
storage = self.get_wvmStorage(pool_name)
storage.clone_volume(vol_name, target_file)
self._defineXML(ElementTree.tostring(tree).decode())
return self.get_instance(clone_data["name"]).UUIDString()
def get_bridge_name(self, source, source_type="net"):
if source_type == "iface":
iface = self.get_iface(source)
bridge_name = iface.name()
else:
net = self.get_network(source)
try:
bridge_name = net.bridgeName()
except libvirtError:
bridge_name = None
return bridge_name
2020-04-24 16:34:29 +00:00
2022-11-02 05:54:35 +00:00
def add_network(
2022-11-02 13:05:41 +00:00
self, mac_address, source, source_type="net", model="virtio", nwfilter=None
2022-11-02 05:54:35 +00:00
):
if source_type == "net":
interface_type = "network"
elif source_type == "bridge":
interface_type = "bridge"
else:
interface_type = "direct"
2022-11-02 05:54:35 +00:00
# network modes not handled: default is bridge
2020-05-21 13:17:25 +00:00
xml_iface = f"""
<interface type='{interface_type}'>
<mac address='{mac_address}'/>"""
if interface_type == "network":
2020-05-21 13:17:25 +00:00
xml_iface += f"""<source network='{source}'/>"""
elif interface_type == "direct":
xml_iface += f"""<source dev='{source}' mode='bridge'/>"""
elif interface_type == "bridge":
xml_iface += f"""<source bridge='{source}'/>"""
else:
raise libvirtError(f"'{interface_type}' is an unexpected interface type.")
2020-05-21 13:17:25 +00:00
xml_iface += f"""<model type='{model}'/>"""
if nwfilter:
2020-05-21 13:17:25 +00:00
xml_iface += f"""<filterref filter='{nwfilter}'/>"""
xml_iface += """</interface>"""
if self.get_status() == 1:
self.instance.attachDeviceFlags(xml_iface, VIR_DOMAIN_AFFECT_LIVE)
self.instance.attachDeviceFlags(xml_iface, VIR_DOMAIN_AFFECT_CONFIG)
2018-08-28 08:55:27 +00:00
if self.get_status() == 5:
self.instance.attachDeviceFlags(xml_iface, VIR_DOMAIN_AFFECT_CONFIG)
2018-08-28 08:55:27 +00:00
def delete_network(self, mac_address):
tree = ElementTree.fromstring(self._XMLDesc(0))
for interface in tree.findall("devices/interface"):
source = interface.find("mac")
if source.get("address", "") == mac_address:
new_xml = ElementTree.tostring(interface).decode()
if self.get_status() == 1:
self.instance.detachDeviceFlags(new_xml, VIR_DOMAIN_AFFECT_LIVE)
self.instance.detachDeviceFlags(new_xml, VIR_DOMAIN_AFFECT_CONFIG)
if self.get_status() == 5:
self.instance.detachDeviceFlags(new_xml, VIR_DOMAIN_AFFECT_CONFIG)
return new_xml
return None
def change_network(self, network_data):
net_mac = network_data.get("net-mac-0")
net_source = network_data.get("net-source-0")
net_source_type = network_data.get("net-source-0-type")
net_filter = network_data.get("net-nwfilter-0")
net_model = network_data.get("net-model-0")
2022-11-02 05:54:35 +00:00
# Remove interface first, but keep network interface XML definition
# If there is an error happened while adding changed one, then add removed one to back.
status = self.delete_network(net_mac)
try:
2022-11-02 05:54:35 +00:00
self.add_network(
2022-11-02 13:05:41 +00:00
net_mac, net_source, net_source_type, net_model, net_filter
2022-11-02 05:54:35 +00:00
)
except libvirtError:
if status is not None:
if self.get_status() == 1:
self.instance.attachDeviceFlags(status, VIR_DOMAIN_AFFECT_LIVE)
self.instance.attachDeviceFlags(status, VIR_DOMAIN_AFFECT_CONFIG)
if self.get_status() == 5:
self.instance.attachDeviceFlags(status, VIR_DOMAIN_AFFECT_CONFIG)
def change_network_oldway(self, network_data):
2022-11-02 05:54:35 +00:00
"""
change network firsh version...
will be removed if new one works as expected for all scenarios
"""
xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
tree = ElementTree.fromstring(xml)
for num, interface in enumerate(tree.findall("devices/interface")):
net_mac = network_data.get("net-mac-" + str(num))
if net_mac is None:
continue
net_source = network_data.get("net-source-" + str(num))
net_source_type = network_data.get("net-source-" + str(num) + "-type")
net_filter = network_data.get("net-nwfilter-" + str(num))
net_model = network_data.get("net-model-" + str(num))
source = interface.find("source")
if interface.get("type") == "bridge":
2020-07-20 11:52:55 +00:00
bridge_name = self.get_bridge_name(net_source, net_source_type)
source.set("bridge", bridge_name)
elif interface.get("type") in ["network", "direct"]:
if net_source_type == "net":
source.set("network", net_source)
elif net_source_type == "iface":
source.set("dev", net_source)
else:
2022-11-02 05:54:35 +00:00
raise libvirtError(
"Unknown network type: {}".format(net_source_type)
)
else:
2022-11-02 05:54:35 +00:00
raise libvirtError(
"Unknown network type: {}".format(interface.get("type"))
)
source = interface.find("model")
if net_model != "default":
source.attrib["type"] = net_model
else:
interface.remove(source)
source = interface.find("mac")
source.set("address", net_mac)
source = interface.find("filterref")
if net_filter:
if source is not None:
source.set("filter", net_filter)
else:
element = ElementTree.Element("filterref")
element.attrib["filter"] = net_filter
interface.append(element)
else:
if source is not None:
interface.remove(source)
new_xml = ElementTree.tostring(tree).decode()
self._defineXML(new_xml)
def set_link_state(self, mac_address, state):
tree = etree.fromstring(self._XMLDesc(0))
for interface in tree.findall("devices/interface"):
source = interface.find("mac")
if source.get("address") == mac_address:
link = interface.find("link")
if link is not None:
interface.remove(link)
link_el = etree.Element("link")
link_el.attrib["state"] = state
interface.append(link_el)
new_xml = etree.tostring(interface).decode()
if self.get_status() == 1:
self.instance.updateDeviceFlags(new_xml, VIR_DOMAIN_AFFECT_LIVE)
self.instance.updateDeviceFlags(new_xml, VIR_DOMAIN_AFFECT_CONFIG)
if self.get_status() == 5:
self.instance.updateDeviceFlags(new_xml, VIR_DOMAIN_AFFECT_CONFIG)
def _set_options(self, tree, options):
for o in ["title", "description"]:
option = tree.find(o)
option_value = options.get(o, "").strip()
if not option_value:
2019-09-10 06:48:31 +00:00
if option is not None:
tree.remove(option)
else:
if option is None:
2020-07-13 09:33:09 +00:00
option = etree.SubElement(tree, o)
option.text = option_value
def set_options(self, options):
"""
Function change description, title
"""
xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
tree = etree.fromstring(xml)
self._set_options(tree, options)
new_xml = etree.tostring(tree).decode()
self._defineXML(new_xml)
def set_memory(self, size, flags=0):
self.instance.setMemoryFlags(size, flags)
def get_all_qos(self):
qos_values = dict()
tree = etree.fromstring(self._XMLDesc(0))
qos = tree.xpath("/domain/devices/interface")
for q in qos:
bound_list = list()
mac = q.xpath("mac/@address")
band = q.find("bandwidth")
if band is not None:
in_qos = band.find("inbound")
if in_qos is not None:
in_av = in_qos.get("average")
in_peak = in_qos.get("peak")
in_burst = in_qos.get("burst")
in_floor = in_qos.get("floor")
bound_list.append(
{
"direction": "inbound",
"average": in_av,
"peak": in_peak,
"floor": in_floor,
"burst": in_burst,
}
)
out_qos = band.find("outbound")
if out_qos is not None:
out_av = out_qos.get("average")
out_peak = out_qos.get("peak")
out_burst = out_qos.get("burst")
bound_list.append(
2022-11-02 05:54:35 +00:00
{
"direction": "outbound",
"average": out_av,
"peak": out_peak,
"burst": out_burst,
}
)
qos_values[mac[0]] = bound_list
return qos_values
def set_qos(self, mac, direction, average, peak, burst):
if direction == "inbound":
2020-05-21 13:17:25 +00:00
xml = f"<inbound average='{average}' peak='{peak}' burst='{burst}'/>"
elif direction == "outbound":
2020-05-21 13:17:25 +00:00
xml = f"<outbound average='{average}' peak='{peak}' burst='{burst}'/>"
else:
raise Exception("Direction must be inbound or outbound")
tree = etree.fromstring(self._XMLDesc(0))
macs = tree.xpath("/domain/devices/interface/mac")
for cur_mac in macs:
if cur_mac.get("address") == mac:
interface = cur_mac.getparent()
band = interface.find("bandwidth")
if band is None:
xml = "<bandwidth>" + xml + "</bandwidth>"
interface.append(etree.fromstring(xml))
else:
direct = band.find(direction)
if direct is not None:
parent = direct.getparent()
parent.remove(direct)
parent.append(etree.fromstring(xml))
else:
band.append(etree.fromstring(xml))
new_xml = etree.tostring(tree).decode()
self.wvm.defineXML(new_xml)
def unset_qos(self, mac, direction):
tree = etree.fromstring(self._XMLDesc(0))
2022-11-02 05:54:35 +00:00
for direct in tree.xpath(
"/domain/devices/interface/bandwidth/{}".format(direction)
):
band_el = direct.getparent()
2022-11-02 05:54:35 +00:00
interface_el = (
band_el.getparent()
) # parent bandwidth,its parent is interface
parent_mac = interface_el.xpath("mac/@address")
if parent_mac[0] == mac:
band_el.remove(direct)
self.wvm.defineXML(etree.tostring(tree).decode())
def add_guest_agent(self):
channel_xml = """
<channel type='unix'>
<target type='virtio' name='org.qemu.guest_agent.0'/>
</channel>
"""
if self.get_status() == 1:
self.instance.attachDeviceFlags(channel_xml, VIR_DOMAIN_AFFECT_LIVE)
self.instance.attachDeviceFlags(channel_xml, VIR_DOMAIN_AFFECT_CONFIG)
if self.get_status() == 5:
self.instance.attachDeviceFlags(channel_xml, VIR_DOMAIN_AFFECT_CONFIG)
def remove_guest_agent(self):
tree = etree.fromstring(self._XMLDesc(0))
2022-11-02 05:54:35 +00:00
for target in tree.xpath(
"/domain/devices/channel[@type='unix']/target[@name='org.qemu.guest_agent.0']"
):
parent = target.getparent()
channel_xml = etree.tostring(parent).decode()
if self.get_status() == 1:
self.instance.detachDeviceFlags(channel_xml, VIR_DOMAIN_AFFECT_LIVE)
self.instance.detachDeviceFlags(channel_xml, VIR_DOMAIN_AFFECT_CONFIG)
if self.get_status() == 5:
self.instance.detachDeviceFlags(channel_xml, VIR_DOMAIN_AFFECT_CONFIG)
def get_guest_agent(self):
def _get_agent(doc):
"""
Return agent channel object if it is defined.
"""
for channel in doc.xpath("/domain/devices/channel"):
ch_type = channel.get("type")
target = channel.find("target")
target_name = target.get("name")
if ch_type == "unix" and target_name == "org.qemu.guest_agent.0":
return channel
return None
return util.get_xml_path(self._XMLDesc(0), func=_get_agent)
def is_agent_ready(self):
"""
Return connected state of an agent.
"""
# we need to get a fresh agent channel object on each call so it
# reflects the current state
dev = self.get_guest_agent()
if dev is not None:
states = dev.xpath("target/@state")
state = states[0] if len(states) > 0 else ""
if state == "connected":
return True
return False