3737CONSOLE_LOG_DIR = "/var/log/libvirt/qemu"
3838
3939
40+ def _memory_mib (el : ET .Element ) -> int :
41+ """Read a libvirt <memory>/<currentMemory> element's value, in MiB.
42+
43+ libvirt defaults the `unit` attribute to KiB when absent; Genesis's
44+ own genesis:mem tag (handled separately) is always a plain MiB int.
45+ """
46+ value = int (el .text )
47+ unit = (el .get ("unit" ) or "KiB" ).lower ()
48+ if unit in ("gib" , "gb" , "g" ):
49+ return value * 1024
50+ if unit in ("mib" , "mb" , "m" ):
51+ return value
52+ if unit in ("b" , "bytes" ):
53+ return value // (1024 * 1024 )
54+ return value // 1024 # KiB, libvirt's own default
55+
56+
4057def _interface_source (
4158 iface : ET .Element , source_el : tp .Optional [ET .Element ]
4259) -> tp .Optional [str ]:
@@ -261,45 +278,45 @@ def add_element(
261278 @classmethod
262279 def document_set_tag (
263280 cls ,
264- docement : minidom .Document ,
281+ document : minidom .Document ,
265282 tag_name : str ,
266283 text : tp .Optional [str ] = None ,
267284 meta_tag : tp .Optional [str ] = None ,
268285 parent : tp .Optional [minidom .Element ] = None ,
269286 ** kwargs ,
270287 ) -> None :
271- root = parent or docement .firstChild
288+ root = parent or document .firstChild
272289 # Firstly we need to remove the old value
273290 for node in root .getElementsByTagName (tag_name ):
274291 root .removeChild (node )
275292
276293 # Also we need to remove the old value from the meta
277294 if meta_tag is not None :
278- meta_node = docement .getElementsByTagName (META_TAG )[0 ]
279- for node in docement .getElementsByTagName (meta_tag ):
295+ meta_node = document .getElementsByTagName (META_TAG )[0 ]
296+ for node in document .getElementsByTagName (meta_tag ):
280297 meta_node .removeChild (node )
281298
282299 # Add the new value
283- cls .add_element (docement , meta_tag , parent = meta_node , text = text )
300+ cls .add_element (document , meta_tag , parent = meta_node , text = text )
284301
285302 # Set the new value
286- cls .add_element (docement , tag_name , parent = root , text = text , ** kwargs )
303+ cls .add_element (document , tag_name , parent = root , text = text , ** kwargs )
287304
288305 @classmethod
289306 def document_meta_set_tag (
290307 cls ,
291- docement : minidom .Document ,
308+ document : minidom .Document ,
292309 tag : str ,
293310 text : tp .Optional [str ] = None ,
294311 ** kwargs ,
295312 ) -> None :
296313 # Remove the old value from the meta
297- meta_node = docement .getElementsByTagName (META_TAG )[0 ]
298- for node in docement .getElementsByTagName (tag ):
314+ meta_node = document .getElementsByTagName (META_TAG )[0 ]
315+ for node in document .getElementsByTagName (tag ):
299316 meta_node .removeChild (node )
300317
301318 # Add the new value
302- cls .add_element (docement , tag , parent = meta_node , text = text , ** kwargs )
319+ cls .add_element (document , tag , parent = meta_node , text = text , ** kwargs )
303320
304321
305322class XMLLibvirtVolume (XMLLibvirtMixin ):
@@ -603,10 +620,24 @@ def _domain2machine(
603620 ) -> tp .Tuple [pool_base .Machine , tp .Tuple [pool_base .Port , ...]]:
604621 element = element or ET .fromstring (domain .XMLDesc ())
605622
623+ # listAllDomains() enumerates every domain libvirt knows about, not
624+ # just ones this driver created - a domain without Genesis metadata
625+ # (pre-existing/foreign, or from before these tags existed) falls
626+ # back to the standard libvirt elements every domain has, rather
627+ # than crashing pool listing for the whole hypervisor.
606628 cores_xml = element .find (f".//{{{ GENESIS_NS } }}vcpu" )
629+ if cores_xml is None :
630+ cores_xml = element .find (".//vcpu" )
607631 cores = int (cores_xml .text )
632+
608633 ram_xml = element .find (f".//{{{ GENESIS_NS } }}mem" )
609- ram = int (ram_xml .text )
634+ if ram_xml is not None :
635+ ram = int (ram_xml .text )
636+ else :
637+ mem_xml = element .find (".//currentMemory" )
638+ if mem_xml is None :
639+ mem_xml = element .find (".//memory" )
640+ ram = _memory_mib (mem_xml )
610641 image_el = element .find (f".//{{{ GENESIS_NS } }}image" )
611642 image = image_el .get ("uri" ) if image_el is not None else None
612643
@@ -820,7 +851,7 @@ def _find_attached_volume_element(
820851 self , domain : ET .Element , volume : pool_base .MachineVolume
821852 ) -> tp .Optional [ET .Element ]:
822853 # Check the volume is attached to the domain
823- for disk in domain .find ( "devices" ). findall ("disk" ):
854+ for disk in domain .findall (".//devices/ disk" ):
824855 # Check source and path
825856 source_node = disk .find ("source" )
826857 if source_node is None :
@@ -1002,7 +1033,7 @@ def delete_volume(self, volume: pool_base.MachineVolume) -> None:
10021033 v .wipe ()
10031034 except libvirt .libvirtError as e :
10041035 # Some backends don't need wiping, for ex. ZFS
1005- if e .get_error_code () != 3 : # VIR_ERR_NO_SUPPORT
1036+ if e .get_error_code () != libvirt . VIR_ERR_NO_SUPPORT :
10061037 raise
10071038 max_iters = 20
10081039 for i in range (max_iters + 1 ):
@@ -1039,7 +1070,7 @@ def attach_volume(self, volume: pool_base.MachineVolume) -> None:
10391070 # FIXME(akremenetsky): Use the simplest check by UUID
10401071 # FIXME(akremenetsky): Check volume name in path. We can check by
10411072 # name as the name is actual UUID of the volume.
1042- disks = domain_element .find ( "devices" ). findall ("disk" )
1073+ disks = domain_element .findall (".//devices/ disk" )
10431074 for disk in disks :
10441075 if bytes (volume .name , "utf-8" ) in ET .tostring (disk ):
10451076 raise pool_base .VolumeAlreadyAttachedError (
@@ -1061,7 +1092,7 @@ def attach_volume(self, volume: pool_base.MachineVolume) -> None:
10611092 image_path = vir_volume .path ()
10621093
10631094 # Detect next device name from domain XML
1064- devices = len (domain_element .find ( "devices" ). findall ("disk" ))
1095+ devices = len (domain_element .findall (".//devices/ disk" ))
10651096 device_name = "vd" + chr (ord ("a" ) + devices )
10661097
10671098 disk_xml = XMLLibvirtInstance .disk_device_xml (image_path , device_name )
@@ -1143,34 +1174,34 @@ def resize_volume(self, volume: pool_base.MachineVolume) -> None:
11431174
11441175 # If the volume is attached and a domain is active use
11451176 # `blockResize`
1146- while volume .machine is not None :
1177+ if volume .machine is not None :
11471178 domain = self ._client .lookupByUUIDString (str (volume .machine ))
11481179
1149- # Not active, break
1150- if domain .isActive () == 0 :
1151- break
1152-
1153- domain_xml = domain .XMLDesc ()
1154- domain_element = ET .fromstring (domain_xml )
1155- disk = self ._find_attached_volume_element (domain_element , volume )
1156- if disk is None :
1157- raise pool_base .VolumeNotAttachedError (
1158- volume = volume .uuid , machine = volume .machine
1180+ if domain .isActive () != 0 :
1181+ domain_xml = domain .XMLDesc ()
1182+ domain_element = ET .fromstring (domain_xml )
1183+ disk = self ._find_attached_volume_element (domain_element , volume )
1184+ if disk is None :
1185+ raise pool_base .VolumeNotAttachedError (
1186+ volume = volume .uuid , machine = volume .machine
1187+ )
1188+ target = disk .find ("target" )
1189+ dev = target .get ("dev" ) if target is not None else None
1190+ if not dev :
1191+ raise ValueError (f"Disk { disk } has no target dev" )
1192+
1193+ pool_xml = ET .fromstring (storage_pool .XMLDesc ())
1194+ pool_type = StoragePoolType (pool_xml .get ("type" ))
1195+
1196+ # ZFS zvols are block devices; QEMU cannot grow them directly.
1197+ # Resize the zvol first, then notify the running guest.
1198+ if pool_type == StoragePoolType .ZFS :
1199+ vir_volume .resize (new_size_bytes )
1200+
1201+ domain .blockResize (
1202+ dev , new_size_bytes , libvirt .VIR_DOMAIN_BLOCK_RESIZE_BYTES
11591203 )
1160- dev = disk .find ("target" ).get ("dev" )
1161-
1162- pool_xml = ET .fromstring (storage_pool .XMLDesc ())
1163- pool_type = StoragePoolType (pool_xml .get ("type" ))
1164-
1165- # ZFS zvols are block devices; QEMU cannot grow them directly.
1166- # Resize the zvol first, then notify the running guest.
1167- if pool_type == StoragePoolType .ZFS :
1168- vir_volume .resize (new_size_bytes )
1169-
1170- domain .blockResize (
1171- dev , new_size_bytes , libvirt .VIR_DOMAIN_BLOCK_RESIZE_BYTES
1172- )
1173- return
1204+ return
11741205
11751206 # Ordinary resize via qemu-img
11761207 vir_volume .resize (new_size_bytes )
@@ -1306,7 +1337,13 @@ def create_machine(
13061337
13071338 storage_pool_xml = ET .fromstring (storage_pool .XMLDesc ())
13081339 pool_type = StoragePoolType (storage_pool_xml .get ("type" ))
1309- pool_path = storage_pool_xml .find ("target" ).find ("path" ).text
1340+ target_el = storage_pool_xml .find ("target" )
1341+ path_el = target_el .find ("path" ) if target_el is not None else None
1342+ if path_el is None or not path_el .text :
1343+ raise ValueError (
1344+ f"Storage pool { self ._spec .storage_pool } has no target path"
1345+ )
1346+ pool_path = path_el .text
13101347
13111348 # Add the volumes to the domain
13121349 for i , volume in enumerate (volumes ):
@@ -1469,7 +1506,7 @@ def rename_machine(self, machine: pool_base.Machine, name: str) -> None:
14691506 ports = ports ,
14701507 legacy_machine = legacy_domain ,
14711508 )
1472- except :
1509+ except Exception :
14731510 machine .name = origin_name
14741511 raise
14751512 LOG .debug ("The domain %s was renamed to %s" , origin_name , name )
0 commit comments