diff --git a/man/virt-xml.rst b/man/virt-xml.rst
index 08f4f3194..dfb6fd9fb 100644
--- a/man/virt-xml.rst
+++ b/man/virt-xml.rst
@@ -222,6 +222,28 @@ Sub options are:
we add to the VM by default.
+``--convert-to-vnc``
+^^^^^^^^^^^^^^^^^^^^
+
+**Syntax:** ``--convert-to-vnc`` [OPTIONS]
+
+Convert an existing VM to exclusively use a single VNC graphics device.
+
+It will attempt to remove all references to any non-VNC graphics config, like
+Spice. For example:
+
+* ``qxl`` devices will be replaced
+* all ``spicevmc`` and ``spiceport`` devices will be removed
+* spice GL will be converted to ``egl-headless``
+
+Sub options are:
+
+``qemu-vdagent=on|off``
+ Add a ``qemu-vdagent`` device if one is not already configured.
+ This replaces some functionality of the spice vdagent.
+ This defaults to ``off`` but that could change in the future.
+
+
XML OPTIONS
===========
diff --git a/tests/data/cli/compare/virt-xml-convert-to-vnc-vdagent.xml b/tests/data/cli/compare/virt-xml-convert-to-vnc-vdagent.xml
new file mode 100644
index 000000000..45764e132
--- /dev/null
+++ b/tests/data/cli/compare/virt-xml-convert-to-vnc-vdagent.xml
@@ -0,0 +1,15 @@
+ restart
+ destroy
+
++
++
++
++
++
+
+
+
+Domain 'test' defined successfully.
+Changes will take effect after the domain is fully powered off.
diff --git a/tests/data/cli/compare/virt-xml-convert-to-vnc.xml b/tests/data/cli/compare/virt-xml-convert-to-vnc.xml
new file mode 100644
index 000000000..a13ee9b51
--- /dev/null
+++ b/tests/data/cli/compare/virt-xml-convert-to-vnc.xml
@@ -0,0 +1,12 @@
+ restart
+ destroy
+
++
++
+
+
+
+Domain 'test' defined successfully.
+Changes will take effect after the domain is fully powered off.
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 015760ffa..e3894398b 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -1439,6 +1439,8 @@ c.add_compare("test --add-device --network default --update --confirm --no-defin
# --convert-* tests
c.add_compare("--connect %(URI-KVM-X86)s --print-diff --define --edit --convert-to-q35", "convert-to-q35", input_file=(_VIRTXMLDIR + "convert-to-q35-win10-in.xml"))
c.add_compare("--connect %(URI-KVM-X86)s --print-diff --define --edit --convert-to-q35 num_pcie_root_ports=7", "convert-to-q35-numports", input_file=(_VIRTXMLDIR + "convert-to-q35-win10-in.xml"))
+c.add_compare("--connect %(URI-KVM-X86)s test --print-diff --define --edit --convert-to-vnc", "convert-to-vnc")
+c.add_compare("--connect %(URI-KVM-X86)s test --print-diff --define --edit --convert-to-vnc qemu-vdagent=on", "convert-to-vnc-vdagent")
# Regression testing for historical --add-device/--remove-device/--edit multi option handling
# Single `--edit` with multiple options are processed in sequence
diff --git a/virtinst/cli.py b/virtinst/cli.py
index 9b2834daa..38580fdb2 100644
--- a/virtinst/cli.py
+++ b/virtinst/cli.py
@@ -1678,6 +1678,28 @@ class ParserConvertToQ35(VirtCLIParser):
self.guest.convert_to_q35(**inst.__dict__)
+############################
+# --convert-to-vnc parsing #
+############################
+
+class ParserConvertToVNC(VirtCLIParser):
+ cli_arg_name = "convert_to_vnc"
+ supports_clearxml = False
+
+ @classmethod
+ def _virtcli_class_init(cls):
+ VirtCLIParser._virtcli_class_init_common(cls)
+ cls.add_arg("qemu-vdagent", "qemu_vdagent")
+
+ def parse(self, inst):
+ class ConvertToVNCData:
+ qemu_vdagent = None
+
+ inst = ConvertToVNCData()
+ super().parse(inst)
+ self.guest.convert_to_vnc(**inst.__dict__)
+
+
########################
# --unattended parsing #
########################
diff --git a/virtinst/virtxml.py b/virtinst/virtxml.py
index 4dc876476..6a16532cd 100644
--- a/virtinst/virtxml.py
+++ b/virtinst/virtxml.py
@@ -491,6 +491,12 @@ def parse_args():
const=cli.VirtCLIParser.OPTSTR_EMPTY,
help=_("Convert an existing VM from PC/i440FX to Q35."))
+ cli.ParserConvertToVNC.register()
+ conv.add_argument("--convert-to-vnc", nargs="?",
+ const=cli.VirtCLIParser.OPTSTR_EMPTY,
+ help=_("Convert an existing VM to use VNC graphics. "
+ "This removes any remnants of Spice graphics."))
+
g = parser.add_argument_group(_("XML options"))
cli.add_disk_option(g, editexample=True)
cli.add_net_option(g)