diff --git a/instances/test_live_xml_effects.py b/instances/test_live_xml_effects.py
index 59e42535..cf1b7b55 100644
--- a/instances/test_live_xml_effects.py
+++ b/instances/test_live_xml_effects.py
@@ -67,6 +67,7 @@ def domain_xml(name, disk, iso):
/dev/urandom
+
"""
@@ -142,7 +143,7 @@ EDITS = {
),
"umount_iso": (
lambda vm: vm.umount_iso("sda"),
- lambda t: not t.xpath("devices/disk[target/@dev='sda']/source"),
+ lambda t: not t.xpath("devices/disk[target/@dev='sda']/source/@file"),
["devices/disk[target/@dev='sda']/source"], True, True,
),
"set_link_state": (
@@ -166,6 +167,16 @@ EDITS = {
lambda t: not t.xpath("devices/interface"),
["devices/interface"], True, True,
),
+ "attach_disk": (
+ lambda vm: vm.attach_disk("vdc", livetest.create_volume(vm.wvm, P + "fx-extra"), target_bus="virtio", format_type="qcow2"),
+ lambda t: bool(t.xpath("devices/disk[target/@dev='vdc']")),
+ ["devices/disk[target/@dev='vdc']"], True, True,
+ ),
+ "add_guest_agent": (
+ lambda vm: vm.add_guest_agent(),
+ lambda t: bool(t.xpath("devices/channel/target[@name='org.qemu.guest_agent.0']")),
+ ["devices/channel[target/@name='org.qemu.guest_agent.0']"], True, True,
+ ),
"edit_disk (cache mode)": (
lambda vm: vm.edit_disk("vda", vm.get_disk_devices(config=True)[0]["path"], False, False, "virtio",
"keep-serial", "qcow2", "writeback", None, None, None),
@@ -246,6 +257,10 @@ class LiveXmlEffectsTestCase(SimpleTestCase):
after = self.persistent(dom)
self.assertTrue(effect(after), "edit had no effect")
self.assertEqual(canonical(after, touched), canonical(before, touched), "unrelated change")
+ # An unplug completes only when the guest acknowledges it; these
+ # VMs have no OS (test_live_guest covers it with a real guest).
+ if running and changes_live and name != "delete_network":
+ self.assertTrue(effect(self.live(dom)), "edit did not reach the running VM")
if running and not changes_live:
self.assertEqual(
canonical(self.live(dom), ["devices/graphics/@port"]),
diff --git a/vrtManager/instance.py b/vrtManager/instance.py
index c1a10eac..df563b7d 100644
--- a/vrtManager/instance.py
+++ b/vrtManager/instance.py
@@ -831,11 +831,8 @@ class wvmInstance(wvmConnect):
if serial is not None and serial != "None" and serial != "":
xml_disk += f"""{serial}"""
xml_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)
+ for _, affect in self._definitions():
+ self.instance.attachDeviceFlags(xml_disk, affect)
def detach_disk(self, target_dev):
tree = etree.fromstring(self._XMLDesc(0))
@@ -1927,24 +1924,17 @@ class wvmInstance(wvmConnect):
"""
- 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)
+ for _, affect in self._definitions():
+ self.instance.attachDeviceFlags(channel_xml, affect)
def remove_guest_agent(self):
- tree = etree.fromstring(self._XMLDesc(0))
- 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)
+ for xml_flags, affect in self._definitions():
+ tree = etree.fromstring(self._XMLDesc(xml_flags))
+ for target in tree.xpath(
+ "/domain/devices/channel[@type='unix']/target[@name='org.qemu.guest_agent.0']"
+ ):
+ channel_xml = etree.tostring(target.getparent()).decode()
+ self.instance.detachDeviceFlags(channel_xml, affect)
def get_guest_agent(self):
def _get_agent(doc):
diff --git a/vrtManager/test_persistent_edits.py b/vrtManager/test_persistent_edits.py
index 33636a02..8f5f345b 100644
--- a/vrtManager/test_persistent_edits.py
+++ b/vrtManager/test_persistent_edits.py
@@ -78,5 +78,47 @@ class XmlFidelityTestCase(unittest.TestCase):
)
+AGENT_DOMAIN = """vm
+"""
+
+
+class DeviceEditsByStateTestCase(unittest.TestCase):
+ """Disk and agent channel changes reach the running definition whenever the
+ VM is active, paused included, and the persistent one always."""
+
+ EDITS = {
+ "attach_disk": ("attachDeviceFlags", lambda vm: vm.attach_disk("vdb", "/var/lib/libvirt/images/d.qcow2")),
+ "add_guest_agent": ("attachDeviceFlags", lambda vm: vm.add_guest_agent()),
+ "remove_guest_agent": ("detachDeviceFlags", lambda vm: vm.remove_guest_agent()),
+ }
+
+ def affected(self, state, active):
+ from libvirt import VIR_DOMAIN_AFFECT_CONFIG, VIR_DOMAIN_AFFECT_LIVE
+
+ names = {VIR_DOMAIN_AFFECT_LIVE: "live", VIR_DOMAIN_AFFECT_CONFIG: "config"}
+ result = {}
+ for name, (method, edit) in self.EDITS.items():
+ vm = wvmInstance.__new__(wvmInstance)
+ vm.instance = MagicMock()
+ vm.instance.isActive.return_value = active
+ vm.get_status = MagicMock(return_value=state)
+ vm._XMLDesc = MagicMock(return_value=AGENT_DOMAIN)
+ edit(vm)
+ result[name] = [names[c.args[1]] for c in getattr(vm.instance, method).call_args_list]
+ return result
+
+ def test_paused_vm(self):
+ for name, affected in self.affected(state=3, active=True).items():
+ self.assertEqual(affected, ["live", "config"], name)
+
+ def test_running_vm(self):
+ for name, affected in self.affected(state=1, active=True).items():
+ self.assertEqual(affected, ["live", "config"], name)
+
+ def test_stopped_vm(self):
+ for name, affected in self.affected(state=5, active=False).items():
+ self.assertEqual(affected, ["config"], name)
+
+
if __name__ == "__main__":
unittest.main()