From a6f53b7da4ac6ff0139e7a130ba875a61a877617 Mon Sep 17 00:00:00 2001 Message-ID: From: Pavel Hrdina Date: Tue, 17 Sep 2024 10:02:59 -0400 Subject: [PATCH] guest: add convert_to_vnc() From: Cole Robinson This is the beginnings of support for a `virt-xml --convert-to-vnc` option. Take an existing VM, strip out most of the previous graphics config, and add VNC graphics. We try to convert over some of the shared graphic bits, like listen and port settings, if they were previously specified. If spice GL was enabled, we convert to egl-headless config Signed-off-by: Cole Robinson (cherry picked from commit 229b905053f3d4bc17e7ad0f8d3fc2c3f23c47cd) Conflicts: tests/test_xmlparse.py virtinst/guest.py https://issues.redhat.com/browse/RHEL-17435 Signed-off-by: Pavel Hrdina --- .../data/xmlparse/convert-to-vnc-empty-in.xml | 13 +++ .../xmlparse/convert-to-vnc-empty-out.xml | 13 +++ .../xmlparse/convert-to-vnc-has-vnc-in.xml | 31 +++++++ .../xmlparse/convert-to-vnc-has-vnc-out.xml | 22 +++++ .../convert-to-vnc-spice-devices-in.xml | 30 +++++++ .../convert-to-vnc-spice-devices-out.xml | 22 +++++ .../convert-to-vnc-spice-manyopts-in.xml | 20 +++++ .../convert-to-vnc-spice-manyopts-out.xml | 20 +++++ tests/test_xmlparse.py | 14 +++ virtinst/guest.py | 88 ++++++++++++++++++- 10 files changed, 269 insertions(+), 4 deletions(-) create mode 100644 tests/data/xmlparse/convert-to-vnc-empty-in.xml create mode 100644 tests/data/xmlparse/convert-to-vnc-empty-out.xml create mode 100644 tests/data/xmlparse/convert-to-vnc-has-vnc-in.xml create mode 100644 tests/data/xmlparse/convert-to-vnc-has-vnc-out.xml create mode 100644 tests/data/xmlparse/convert-to-vnc-spice-devices-in.xml create mode 100644 tests/data/xmlparse/convert-to-vnc-spice-devices-out.xml create mode 100644 tests/data/xmlparse/convert-to-vnc-spice-manyopts-in.xml create mode 100644 tests/data/xmlparse/convert-to-vnc-spice-manyopts-out.xml diff --git a/tests/data/xmlparse/convert-to-vnc-empty-in.xml b/tests/data/xmlparse/convert-to-vnc-empty-in.xml new file mode 100644 index 000000000..bc370a8a2 --- /dev/null +++ b/tests/data/xmlparse/convert-to-vnc-empty-in.xml @@ -0,0 +1,13 @@ + + convert-me + 8388608 + 2097152 + 2 + + hvm + + + + + + diff --git a/tests/data/xmlparse/convert-to-vnc-empty-out.xml b/tests/data/xmlparse/convert-to-vnc-empty-out.xml new file mode 100644 index 000000000..8612a6567 --- /dev/null +++ b/tests/data/xmlparse/convert-to-vnc-empty-out.xml @@ -0,0 +1,13 @@ + + convert-me + 8388608 + 2097152 + 2 + + hvm + + + + + + diff --git a/tests/data/xmlparse/convert-to-vnc-has-vnc-in.xml b/tests/data/xmlparse/convert-to-vnc-has-vnc-in.xml new file mode 100644 index 000000000..29ee53d4c --- /dev/null +++ b/tests/data/xmlparse/convert-to-vnc-has-vnc-in.xml @@ -0,0 +1,31 @@ + + convert-me + 2097152 + 2097152 + 2 + + hvm + + + + + + + + + + + + + + + + + + diff --git a/tests/data/xmlparse/convert-to-vnc-has-vnc-out.xml b/tests/data/xmlparse/convert-to-vnc-has-vnc-out.xml new file mode 100644 index 000000000..113f70a1e --- /dev/null +++ b/tests/data/xmlparse/convert-to-vnc-has-vnc-out.xml @@ -0,0 +1,22 @@ + + convert-me + 2097152 + 2097152 + 2 + + hvm + + + + + + + + + + + + + diff --git a/tests/data/xmlparse/convert-to-vnc-spice-devices-in.xml b/tests/data/xmlparse/convert-to-vnc-spice-devices-in.xml new file mode 100644 index 000000000..8c5c63bdf --- /dev/null +++ b/tests/data/xmlparse/convert-to-vnc-spice-devices-in.xml @@ -0,0 +1,30 @@ + + convert-me + 2097152 + 2097152 + 2 + + hvm + + + + + + + + + + + + + + + + + diff --git a/tests/data/xmlparse/convert-to-vnc-spice-devices-out.xml b/tests/data/xmlparse/convert-to-vnc-spice-devices-out.xml new file mode 100644 index 000000000..cc3fefabe --- /dev/null +++ b/tests/data/xmlparse/convert-to-vnc-spice-devices-out.xml @@ -0,0 +1,22 @@ + + convert-me + 2097152 + 2097152 + 2 + + hvm + + + + + + + + + + + + + diff --git a/tests/data/xmlparse/convert-to-vnc-spice-manyopts-in.xml b/tests/data/xmlparse/convert-to-vnc-spice-manyopts-in.xml new file mode 100644 index 000000000..ebd20a56d --- /dev/null +++ b/tests/data/xmlparse/convert-to-vnc-spice-manyopts-in.xml @@ -0,0 +1,20 @@ + + convert-me + 8388608 + 2097152 + 2 + + hvm + + + + + + + + + + + + + diff --git a/tests/data/xmlparse/convert-to-vnc-spice-manyopts-out.xml b/tests/data/xmlparse/convert-to-vnc-spice-manyopts-out.xml new file mode 100644 index 000000000..c98c63830 --- /dev/null +++ b/tests/data/xmlparse/convert-to-vnc-spice-manyopts-out.xml @@ -0,0 +1,20 @@ + + convert-me + 8388608 + 2097152 + 2 + + hvm + + + + + + + + + + + + + diff --git a/tests/test_xmlparse.py b/tests/test_xmlparse.py index ac2fb38d2..e11d15d07 100644 --- a/tests/test_xmlparse.py +++ b/tests/test_xmlparse.py @@ -1135,3 +1135,17 @@ def testControllerAttachedDevices(): # Little test for DeviceAddress.pretty_desc assert devs[-1].address.pretty_desc() == "0:0:0:3" + + +def testConvertToVNC(): + conn = utils.URIs.openconn(utils.URIs.kvm_x86) + + def _test(filename_base): + guest, outfile = _get_test_content(conn, filename_base) + guest.convert_to_vnc() + _alter_compare(conn, guest.get_xml(), outfile) + + _test("convert-to-vnc-empty") + _test("convert-to-vnc-spice-devices") + _test("convert-to-vnc-spice-manyopts") + _test("convert-to-vnc-has-vnc") diff --git a/virtinst/guest.py b/virtinst/guest.py index 7aa62be49..93e71a149 100644 --- a/virtinst/guest.py +++ b/virtinst/guest.py @@ -706,6 +706,84 @@ class Guest(XMLBuilder): self.vcpus = self.cpu.vcpus_from_topology() self.cpu.set_topology_defaults(self.vcpus) + def _convert_spice_gl_to_egl_headless(self): + if not self.has_spice(): + return + + spicedev = [g for g in self.devices.graphics if g.type == "spice"][0] + if not spicedev.gl: + return + + dev = DeviceGraphics(self.conn) + dev.type = "egl-headless" + dev.set_defaults(self.conn) + if spicedev.rendernode: + dev.rendernode = spicedev.rendernode + self.add_device(dev) + + def _convert_to_vnc_graphics(self): + """ + If there's already VNC graphics configured, we leave it intact, + but rip out all evidence of other graphics devices. + + If there's other non-VNC, non-egl-headless configured, we try to + inplace convert the first device we encounter. + + If there's no graphics configured, set up a default VNC config.` + """ + vnc_devs = [g for g in self.devices.graphics if g.type == "vnc"] + # We ignore egl-headless, it's not a true graphical frontend + other_devs = [g for g in self.devices.graphics if + g.type != "vnc" and g.type != "egl-headless"] + + # Guest already had a vnc device. + # Remove all other devs and we are done + if vnc_devs: + for g in other_devs: + self.remove_device(g) + return + + # We didn't find any non-vnc device to convert. + # Add a vnc device with default config + if not other_devs: + dev = DeviceGraphics(self.conn) + dev.type = dev.TYPE_VNC + dev.set_defaults(self.conn) + self.add_device(dev) + return + + # Convert the pre-existing graphics device to vnc + # Remove the rest + dev = other_devs.pop(0) + srcdev = DeviceGraphics(self.conn, dev.get_xml()) + for g in other_devs: + self.remove_device(g) + + dev.clear() + dev.type = dev.TYPE_VNC + dev.keymap = srcdev.keymap + dev.port = srcdev.port + dev.autoport = srcdev.autoport + dev.passwd = srcdev.passwd + dev.passwdValidTo = srcdev.passwdValidTo + dev.listen = srcdev.listen + for listen in srcdev.listens: + srcdev.remove_child(listen) + dev.add_child(listen) + dev.set_defaults(self) + + def convert_to_vnc(self): + """ + Convert existing XML to have one VNC graphics connection. + """ + self._convert_spice_gl_to_egl_headless() + + # Rip out spice graphics devices unconditionally. + # Could be necessary if XML is in broken state. + self._force_remove_spice_devices() + + self._convert_to_vnc_graphics() + def set_defaults(self, _guest): self.set_capabilities_defaults() @@ -1061,10 +1139,12 @@ class Guest(XMLBuilder): if redirdev.type == "spicevmc": self.devices.remove_child(redirdev) - def _remove_spice_devices(self, rmdev): - if rmdev.DEVICE_TYPE != "graphics" or self.has_spice(): - return - + def _force_remove_spice_devices(self): self._remove_spice_audio() self._remove_spice_channels() self._remove_spice_usbredir() + + def _remove_spice_devices(self, rmdev): + if rmdev.DEVICE_TYPE != "graphics" or self.has_spice(): + return + self._force_remove_spice_devices() -- 2.48.1