diff --git a/vrtManager/instance.py b/vrtManager/instance.py
index df14a667..27417452 100644
--- a/vrtManager/instance.py
+++ b/vrtManager/instance.py
@@ -454,6 +454,8 @@ class wvmInstances(wvmConnect):
them), CD-ROM images, block devices and pool volumes. Network disks
(RBD, iSCSI, ...) are reached the same way from every host and are not
checked; other types (dir, nvme, ...) cannot be and count as missing.
+ A volume of an LVM or disk pool here counts as missing too: such
+ storage is each host's own, and one of the same name is other data.
Pools are refreshed before a disk counts as missing, as a file another
host made on shared storage is listed after that."""
disks = {}
@@ -483,7 +485,8 @@ class wvmInstances(wvmConnect):
continue
checked.add(key)
volume = self.find_volume(*key)
- if volume is None:
+ if volume is None or ElementTree.fromstring(
+ volume.storagePoolLookupByVolume().XMLDesc(0)).get("type") in ("logical", "disk"):
unseen.append((target, key))
continue
backing = ElementTree.fromstring(volume.XMLDesc(0)).findtext("backingStore/path")
diff --git a/vrtManager/tests/fakes.py b/vrtManager/tests/fakes.py
index 7611360a..fb73a158 100644
--- a/vrtManager/tests/fakes.py
+++ b/vrtManager/tests/fakes.py
@@ -88,8 +88,12 @@ def no_volume():
class FakeVolume:
- def __init__(self, backing):
+ def __init__(self, backing, pool_type="dir"):
self.backing = backing
+ self.pool_type = pool_type
+
+ def storagePoolLookupByVolume(self):
+ return MagicMock(XMLDesc=lambda flags: f"")
def XMLDesc(self, flags=0):
if self.backing is None:
@@ -121,7 +125,7 @@ class FakeHost:
"""The destination's libvirt connection. volumes: the paths its pools
list; unlisted: files its pools show after a refresh."""
- def __init__(self, existing=(), volumes=(), unlisted=(), backing=None, inactive_pools=()):
+ def __init__(self, existing=(), volumes=(), unlisted=(), backing=None, inactive_pools=(), pool_types=None):
self.existing = set(existing)
self.defined = []
self.new = []
@@ -129,12 +133,13 @@ class FakeHost:
self.unlisted = set(unlisted)
self.backing = backing or {} # path: the path of its backing file
self.inactive_pools = set(inactive_pools)
+ self.pool_types = pool_types or {} # path: the type of its volume's pool, dir if not named
self.refreshes = 0
def storageVolLookupByPath(self, path):
if path not in self.volumes:
raise no_volume()
- return FakeVolume(self.backing.get(path))
+ return FakeVolume(self.backing.get(path), self.pool_types.get(path, "dir"))
def storagePoolLookupByName(self, name):
return FakePool(self, active=name not in self.inactive_pools)
diff --git a/vrtManager/tests/test_migration.py b/vrtManager/tests/test_migration.py
index a951fcc1..92fd6782 100644
--- a/vrtManager/tests/test_migration.py
+++ b/vrtManager/tests/test_migration.py
@@ -246,6 +246,18 @@ class DestinationDisksTestCase(unittest.TestCase):
with self.assertRaisesRegex(util.OperationError, "vdb \\(dir disk, cannot be checked\\)"):
self.migrate(FakeHost(), xml, state=5)
+ def test_a_volume_of_a_host_s_own_lvm_or_disk_pool_is_missing(self):
+ # another host's logical volume of the same name is other data
+ for pool_type in ("logical", "disk"):
+ with self.subTest(pool_type=pool_type):
+ host = FakeHost(volumes={"/dev/vg/vm"}, pool_types={"/dev/vg/vm": pool_type})
+ xml = with_disks("")
+ with self.assertRaisesRegex(util.OperationError, "vda \\(/dev/vg/vm\\)"):
+ self.migrate(host, xml)
+ host = FakeHost(volumes={"/dev/sdz"}, pool_types={"/dev/sdz": "iscsi"})
+ xml = with_disks("")
+ self.assertEqual(len(self.migrate(host, xml).instance.calls), 1)
+
def test_a_volume_of_an_inactive_pool_is_missing(self):
host = FakeHost(volumes={"v"}, inactive_pools={"p"})
xml = with_disks("")