diff --git a/man/virt-install.rst b/man/virt-install.rst
index b767afe1..6008adf7 100644
--- a/man/virt-install.rst
+++ b/man/virt-install.rst
@@ -258,6 +258,24 @@ The --xml option has 4 sub options:
+**xpath subarguments**
+``````````````````````
+
+Similar to the ``--xml`` option, most top level options have ``xpath.*``
+suboptions. For example, ``--disk xpath1.set=./@foo=bar,xpath2.create=./newelement``
+would generate XML alterations like
+
+.. code-block::
+
+
+
+
+
+This is useful for setting XML options per device, when virt-install does not
+support those options yet.
+
+
+
``--qemu-commandline``
^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/data/cli/compare/virt-install-many-devices.xml b/tests/data/cli/compare/virt-install-many-devices.xml
index eefdd1ae..0c631916 100644
--- a/tests/data/cli/compare/virt-install-many-devices.xml
+++ b/tests/data/cli/compare/virt-install-many-devices.xml
@@ -657,6 +657,10 @@
+
+
+
+
diff --git a/tests/data/cli/compare/virt-xml-edit-device-xpath.xml b/tests/data/cli/compare/virt-xml-edit-device-xpath.xml
new file mode 100644
index 00000000..c6f76ddf
--- /dev/null
+++ b/tests/data/cli/compare/virt-xml-edit-device-xpath.xml
@@ -0,0 +1,15 @@
+
+ /usr/lib/xen/bin/qemu-dm
+
+-
+
+-
++
+
++
+
+
+
+
+Domain 'test-for-virtxml' 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 d3629350..80982124 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -646,6 +646,7 @@ source.reservations.managed=no,source.reservations.source.type=unix,source.reser
--input mouse,bus=virtio,model=virtio-non-transitional
--input passthrough,source.evdev=/dev/input/event1,bus=virtio
--input evdev,source.dev=/dev/input/event1234,source.repeat=on,source.grab=all,source.grabToggle=ctrl-ctrl
+--input mouse,model=FOOBAR,xpath0.set=./@bus=usb,xpath2.set=./address/@type=usb,xpath6.set=./willbeoverwritten=foo,xpath6.create=./randomelement,xpath7.create=./deleteme,xpath8.delete=./deleteme,xpath9.set=./@model=,xpath10.set=./@type,xpath10.value=keyboard
--serial char_type=tcp,host=:2222,mode=bind,protocol=telnet,log.file=/tmp/foo.log,log.append=yes,,target.model.name=pci-serial
@@ -1360,6 +1361,7 @@ c.add_compare("--clock offset=localtime,hpet_present=yes,kvmclock_present=no,kvm
c.add_compare("--pm suspend_to_mem.enabled=yes,suspend_to_disk.enabled=no", "edit-simple-pm")
c.add_compare("--disk /dev/zero,perms=ro,source.startupPolicy=optional", "edit-simple-disk")
c.add_compare("--disk path=", "edit-simple-disk-remove-path")
+c.add_compare("--disk xpath1.delete=./source,xpath2.set=./boot/@order,xpath2.value=6,xpath3.create=./fakeelement", "edit-device-xpath")
c.add_compare("--network source=br0,type=bridge,model=virtio,mac=", "edit-simple-network")
c.add_compare("--graphics tlsport=5902,keymap=ja", "edit-simple-graphics")
c.add_compare("--graphics listen=none", "edit-graphics-listen-none")
diff --git a/virtinst/cli.py b/virtinst/cli.py
index 7580234a..f42cb5d9 100644
--- a/virtinst/cli.py
+++ b/virtinst/cli.py
@@ -1366,6 +1366,8 @@ class VirtCLIParser(metaclass=_InitClass):
prefix = "0"
if virtarg.cliname.startswith("address."):
prefix = "1"
+ if virtarg.cliname.startswith("xpath"):
+ prefix = "2"
return prefix + virtarg.cliname
print("%s options:" % cls.cli_flag_name())
@@ -1400,7 +1402,9 @@ class VirtCLIParser(metaclass=_InitClass):
@staticmethod
def _virtcli_class_init_common(subclass):
- pass
+ if subclass and subclass.guest_propname:
+ _add_xpath_args(subclass)
+
def __init__(self, optstr, guest=None, editing=None):
self.optstr = optstr
@@ -1615,6 +1619,31 @@ def parse_xmlcli(guest, options):
guest.xml_actions.append(inst)
+def _add_xpath_args(cls):
+ """
+ Add xpath.* subarguments to XML based CLI options
+ """
+ def find_xpath_cb(self, *args, **kwargs):
+ cliarg = "xpath" # xpath[0-9]*
+ list_propname = "xml_actions"
+ # pylint: disable=protected-access
+ cb = self._make_find_inst_cb(cliarg, list_propname)
+ return cb(*args, **kwargs)
+
+ def _add_arg(*args, **kwargs):
+ kwargs["skip_testsuite_tracking"] = True
+ cls.add_arg(*args, **kwargs)
+
+ _add_arg("xpath[0-9]*.delete", "xpath_delete",
+ can_comma=True, lookup_cb=None, find_inst_cb=find_xpath_cb)
+ _add_arg("xpath[0-9]*.set", "xpath_set",
+ can_comma=True, lookup_cb=None, find_inst_cb=find_xpath_cb)
+ _add_arg("xpath[0-9]*.create", "xpath_create",
+ can_comma=True, lookup_cb=None, find_inst_cb=find_xpath_cb)
+ _add_arg("xpath[0-9]*.value", "xpath_value",
+ can_comma=True, lookup_cb=None, find_inst_cb=find_xpath_cb)
+
+
########################
# --unattended parsing #
########################
@@ -4422,6 +4451,7 @@ class _ParserChar(VirtCLIParser):
VirtCLIParser._virtcli_class_init_common(cls)
_add_common_device_args(cls)
+ _add_xpath_args(cls)
cls.add_arg("type", "type")