From 9fe36cb53a3d4fdcae08d26e77daecfb19a6c9a5 Mon Sep 17 00:00:00 2001
From: Cedric Bosdonnat <cedric.bosdonnat@free.fr>
Date: Fri, 21 May 2021 13:04:42 +0200
Subject: [PATCH] Handle volumes on stopped pools in virt.vm_info
 (#374)

For VMs having at least a disk on a stopped volume, we don't want the
user to get an exception when running virt.vm_info. Instead just provide
less information.
---
 changelog/60132.fixed                         |  1 +
 salt/modules/virt.py                          | 73 +++++++++++--------
 .../pytests/unit/modules/virt/test_domain.py  |  9 ++-
 3 files changed, 50 insertions(+), 33 deletions(-)
 create mode 100644 changelog/60132.fixed

diff --git a/changelog/60132.fixed b/changelog/60132.fixed
new file mode 100644
index 0000000000..1e3bc96b98
--- /dev/null
+++ b/changelog/60132.fixed
@@ -0,0 +1 @@
+Gracefuly handle errors in virt.vm_info
diff --git a/salt/modules/virt.py b/salt/modules/virt.py
index c2ae0e44b8..4927dd4afe 100644
--- a/salt/modules/virt.py
+++ b/salt/modules/virt.py
@@ -518,41 +518,50 @@ def _get_disks(conn, dom):
             def _get_disk_volume_data(pool_name, volume_name):
                 qemu_target = "{}/{}".format(pool_name, volume_name)
                 pool = conn.storagePoolLookupByName(pool_name)
-                vol = pool.storageVolLookupByName(volume_name)
-                vol_info = vol.info()
-                extra_properties = {
-                    "virtual size": vol_info[1],
-                    "disk size": vol_info[2],
-                }
-
-                backing_files = [
-                    {
-                        "file": node.find("source").get("file"),
-                        "file format": node.find("format").get("type"),
+                extra_properties = {}
+                try:
+                    vol = pool.storageVolLookupByName(volume_name)
+                    vol_info = vol.info()
+                    extra_properties = {
+                        "virtual size": vol_info[1],
+                        "disk size": vol_info[2],
                     }
-                    for node in elem.findall(".//backingStore[source]")
-                ]
 
-                if backing_files:
-                    # We had the backing files in a flat list, nest them again.
-                    extra_properties["backing file"] = backing_files[0]
-                    parent = extra_properties["backing file"]
-                    for sub_backing_file in backing_files[1:]:
-                        parent["backing file"] = sub_backing_file
-                        parent = sub_backing_file
+                    backing_files = [
+                        {
+                            "file": node.find("source").get("file"),
+                            "file format": node.find("format").get("type"),
+                        }
+                        for node in elem.findall(".//backingStore[source]")
+                    ]
 
-                else:
-                    # In some cases the backing chain is not displayed by the domain definition
-                    # Try to see if we have some of it in the volume definition.
-                    vol_desc = ElementTree.fromstring(vol.XMLDesc())
-                    backing_path = vol_desc.find("./backingStore/path")
-                    backing_format = vol_desc.find("./backingStore/format")
-                    if backing_path is not None:
-                        extra_properties["backing file"] = {"file": backing_path.text}
-                        if backing_format is not None:
-                            extra_properties["backing file"][
-                                "file format"
-                            ] = backing_format.get("type")
+                    if backing_files:
+                        # We had the backing files in a flat list, nest them again.
+                        extra_properties["backing file"] = backing_files[0]
+                        parent = extra_properties["backing file"]
+                        for sub_backing_file in backing_files[1:]:
+                            parent["backing file"] = sub_backing_file
+                            parent = sub_backing_file
+
+                    else:
+                        # In some cases the backing chain is not displayed by the domain definition
+                        # Try to see if we have some of it in the volume definition.
+                        vol_desc = ElementTree.fromstring(vol.XMLDesc())
+                        backing_path = vol_desc.find("./backingStore/path")
+                        backing_format = vol_desc.find("./backingStore/format")
+                        if backing_path is not None:
+                            extra_properties["backing file"] = {
+                                "file": backing_path.text
+                            }
+                            if backing_format is not None:
+                                extra_properties["backing file"][
+                                    "file format"
+                                ] = backing_format.get("type")
+                except libvirt.libvirtError:
+                    # The volume won't be found if the pool is not started, just output less infos
+                    log.info(
+                        "Couldn't extract all volume informations: pool is likely not running or refreshed"
+                    )
                 return (qemu_target, extra_properties)
 
             if disk_type == "file":
diff --git a/tests/pytests/unit/modules/virt/test_domain.py b/tests/pytests/unit/modules/virt/test_domain.py
index 69bcd641b7..e35b3d849d 100644
--- a/tests/pytests/unit/modules/virt/test_domain.py
+++ b/tests/pytests/unit/modules/virt/test_domain.py
@@ -191,6 +191,11 @@ def test_get_disks(make_mock_vm, make_mock_storage_pool):
           <alias name='virtio-disk0'/>
           <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
         </disk>
+        <disk type='volume' device='disk'>
+          <driver name='qemu' type='qcow2' cache='none' io='native'/>
+          <source pool='stopped' volume='vm05_data'/>
+          <target dev='vdd' bus='virtio'/>
+        </disk>
         <disk type='network' device='cdrom'>
           <driver name='qemu' type='raw' cache='none' io='native'/>
           <source protocol='http' name='/pub/iso/myimage.iso' query='foo=bar&amp;baz=flurb' index='1'>
@@ -204,11 +209,12 @@ def test_get_disks(make_mock_vm, make_mock_storage_pool):
       </devices>
     </domain>
     """
-    domain_mock = make_mock_vm(vm_def)
+    make_mock_vm(vm_def)
 
     pool_mock = make_mock_storage_pool(
         "default", "dir", ["srv01_system", "srv01_data", "vm05_system"]
     )
+    make_mock_storage_pool("stopped", "dir", [])
 
     # Append backing store to srv01_data volume XML description
     srv1data_mock = pool_mock.storageVolLookupByName("srv01_data")
@@ -255,6 +261,7 @@ def test_get_disks(make_mock_vm, make_mock_storage_pool):
                 },
             },
         },
+        "vdd": {"type": "disk", "file": "stopped/vm05_data", "file format": "qcow2"},
         "hda": {
             "type": "cdrom",
             "file format": "raw",
-- 
2.31.1


