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()