[libvirt PATCH 2/2] tools: add '--xpath EXPRESSION --wrap' args to all dumpxml commands

Daniel P. Berrangé berrange at redhat.com
Thu Jun 16 15:36:53 UTC 2022


While you can chain the virsh output up to a later 'xmllint' or 'xpath'
command, integrating it into virsh avoids needs for installing extra
binaries which we've often found to be missing on production installs
of libvirt. It also gives better response if the initial virsh command
hits an error, as you don't get an aborted pipeline.

    $ virsh pool-dumpxml --xpath //permissions default
    <permissions>
      <mode>0711</mode>
      <owner>1000</owner>
      <group>1000</group>
      <label>unconfined_u:object_r:svirt_home_t:s0</label>
    </permissions>

If multiple nodes match, they are emitted individually:

    $ virsh dumpxml --xpath '//devices/*/address[@type="pci"]' --wrap demo
    <address type="pci" domain="0x0000" bus="0x05" slot="0x00" function="0x0"/>
    <address type="pci" domain="0x0000" bus="0x03" slot="0x00" function="0x0"/>
    ...snip...
    <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x0" multifunction="on"/>
    <address type="pci" domain="0x0000" bus="0x07" slot="0x00" function="0x0"/>

but if intending to post-process the output further, the results
can be wrapped in a parent node

    $ virsh dumpxml --xpath '//devices/*/address[@type="pci"]' --wrap demo
    <nodes>
      <address type="pci" domain="0x0000" bus="0x05" slot="0x00" function="0x0"/>
      <address type="pci" domain="0x0000" bus="0x03" slot="0x00" function="0x0"/>
      ...snip...
      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x0" multifunction="on"/>
      <address type="pci" domain="0x0000" bus="0x07" slot="0x00" function="0x0"/>
    </nodes>

Fixes https://gitlab.com/libvirt/libvirt/-/issues/244
Signed-off-by: Daniel P. Berrangé <berrange at redhat.com>
---
 docs/manpages/virsh.rst  | 138 ++++++++++++++++++++++++++++++++++-----
 tools/virsh-backup.c     |  17 ++++-
 tools/virsh-checkpoint.c |  17 ++++-
 tools/virsh-domain.c     |  55 +++++++++++++---
 tools/virsh-interface.c  |  22 +++++--
 tools/virsh-network.c    |  47 +++++++++----
 tools/virsh-nodedev.c    |  17 ++++-
 tools/virsh-nwfilter.c   |  51 ++++++++++-----
 tools/virsh-pool.c       |  27 +++++---
 tools/virsh-secret.c     |  18 ++++-
 tools/virsh-snapshot.c   |  17 ++++-
 tools/virsh-volume.c     |  28 +++++---
 12 files changed, 370 insertions(+), 84 deletions(-)

diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst
index 580245adb1..4701791b04 100644
--- a/docs/manpages/virsh.rst
+++ b/docs/manpages/virsh.rst
@@ -1959,10 +1959,16 @@ backup-dumpxml
 
 ::
 
-   backup-dumpxml domain
+   backup-dumpxml [--xpath EXPRESSION] [--wrap] domain
 
 Output XML describing the current backup job.
 
+If the **--xpath** argument provides an XPath expression, it will be
+evaluated against the output XML and only those matching nodes will
+be printed. The default behaviour is to print each matching node as
+a standalone document, however, for ease of additional processing,
+the **--wrap** argument will cause the matching node to be wrapped
+in a common root node.
 
 domiflist
 ---------
@@ -2652,7 +2658,8 @@ dumpxml
 
 ::
 
-   dumpxml domain [--inactive] [--security-info] [--update-cpu] [--migratable]
+   dumpxml [--inactive] [--security-info] [--update-cpu] [--migratable]
+           [--xpath EXPRESSION] [--wrap] domain
 
 Output the domain information as an XML dump to stdout, this format can be used
 by the ``create`` command. Additional options affecting the XML dump may be
@@ -2665,6 +2672,13 @@ migrations, i.e., compatible with older libvirt releases and possibly amended
 with internal run-time options. This option may automatically enable other
 options (*--update-cpu*, *--security-info*, ...) as necessary.
 
+If the **--xpath** argument provides an XPath expression, it will be
+evaluated against the output XML and only those matching nodes will
+be printed. The default behaviour is to print each matching node as
+a standalone document, however, for ease of additional processing,
+the **--wrap** argument will cause the matching node to be wrapped
+in a common root node.
+
 
 edit
 ----
@@ -3090,12 +3104,19 @@ managedsave-dumpxml
 
 ::
 
-   managedsave-dumpxml domain [--security-info]
+   managedsave-dumpxml [--security-info] [--xpath EXPRESSION] [--wrap] domain
 
 Extract the domain XML that was in effect at the time the saved state
 file *file* was created with the ``managedsave`` command.  Using
 *--security-info* will also include security sensitive information.
 
+If the **--xpath** argument provides an XPath expression, it will be
+evaluated against the output XML and only those matching nodes will
+be printed. The default behaviour is to print each matching node as
+a standalone document, however, for ease of additional processing,
+the **--wrap** argument will cause the matching node to be wrapped
+in a common root node.
+
 
 managedsave-edit
 ----------------
@@ -3890,12 +3911,19 @@ save-image-dumpxml
 
 ::
 
-   save-image-dumpxml file [--security-info]
+   save-image-dumpxml [--security-info] [--xpath EXPRESSION] [--wrap] file
 
 Extract the domain XML that was in effect at the time the saved state
 file *file* was created with the ``save`` command.  Using
 *--security-info* will also include security sensitive information.
 
+If the **--xpath** argument provides an XPath expression, it will be
+evaluated against the output XML and only those matching nodes will
+be printed. The default behaviour is to print each matching node as
+a standalone document, however, for ease of additional processing,
+the **--wrap** argument will cause the matching node to be wrapped
+in a common root node.
+
 
 save-image-edit
 ---------------
@@ -5244,7 +5272,7 @@ nodedev-dumpxml
 
 ::
 
-   nodedev-dumpxml device
+   nodedev-dumpxml [--xpath EXPRESSION] [--wrap] device
 
 Dump a <device> XML representation for the given node device, including
 such information as the device name, which bus owns the device, the
@@ -5253,6 +5281,13 @@ libvirt (such as whether device reset is supported). *device* can
 be either device name or wwn pair in "wwnn,wwpn" format (only works
 for HBA).
 
+If the **--xpath** argument provides an XPath expression, it will be
+evaluated against the output XML and only those matching nodes will
+be printed. The default behaviour is to print each matching node as
+a standalone document, however, for ease of additional processing,
+the **--wrap** argument will cause the matching node to be wrapped
+in a common root node.
+
 
 nodedev-info
 ------------
@@ -5433,13 +5468,20 @@ net-dumpxml
 
 ::
 
-   net-dumpxml network [--inactive]
+   net-dumpxml [--inactive] [--xpath EXPRESSION] [--wrap] network
 
 
 Output the virtual network information as an XML dump to stdout.
 If *--inactive* is specified, then physical functions are not
 expanded into their associated virtual functions.
 
+If the **--xpath** argument provides an XPath expression, it will be
+evaluated against the output XML and only those matching nodes will
+be printed. The default behaviour is to print each matching node as
+a standalone document, however, for ease of additional processing,
+the **--wrap** argument will cause the matching node to be wrapped
+in a common root node.
+
 
 net-edit
 --------
@@ -5695,10 +5737,17 @@ net-port-dumpxml
 
 ::
 
-   net-port-dumpxml network port
+   net-port-dumpxml [--xpath EXPRESSION] [--wrap] network port
 
 Output the network port information as an XML dump to stdout.
 
+If the **--xpath** argument provides an XPath expression, it will be
+evaluated against the output XML and only those matching nodes will
+be printed. The default behaviour is to print each matching node as
+a standalone document, however, for ease of additional processing,
+the **--wrap** argument will cause the matching node to be wrapped
+in a common root node.
+
 
 net-port-delete
 ---------------
@@ -5782,12 +5831,19 @@ iface-dumpxml
 
 ::
 
-   iface-dumpxml interface [--inactive]
+   iface-dumpxml [--inactive] [--xpath EXPRESSION] [--wrap] interface
 
 Output the host interface information as an XML dump to stdout.  If
 *--inactive* is specified, then the output reflects the persistent
 state of the interface that will be used the next time it is started.
 
+If the **--xpath** argument provides an XPath expression, it will be
+evaluated against the output XML and only those matching nodes will
+be printed. The default behaviour is to print each matching node as
+a standalone document, however, for ease of additional processing,
+the **--wrap** argument will cause the matching node to be wrapped
+in a common root node.
+
 
 iface-edit
 ----------
@@ -6258,12 +6314,19 @@ pool-dumpxml
 
 ::
 
-   pool-dumpxml [--inactive] pool-or-uuid
+   pool-dumpxml [--inactive] [--xpath EXPRESSION] [--wrap] pool-or-uuid
 
 Returns the XML information about the *pool* object.
 *--inactive* tells virsh to dump pool configuration that will be used
 on next start of the pool as opposed to the current pool configuration.
 
+If the **--xpath** argument provides an XPath expression, it will be
+evaluated against the output XML and only those matching nodes will
+be printed. The default behaviour is to print each matching node as
+a standalone document, however, for ease of additional processing,
+the **--wrap** argument will cause the matching node to be wrapped
+in a common root node.
+
 
 pool-edit
 ---------
@@ -6750,7 +6813,8 @@ vol-dumpxml
 
 ::
 
-   vol-dumpxml vol-name-or-key-or-path [--pool pool-or-uuid]
+   vol-dumpxml [--pool pool-or-uuid] [--xpath EXPRESSION] [--wrap]
+               vol-name-or-key-or-path
 
 Output the volume information as an XML dump to stdout.
 
@@ -6762,6 +6826,13 @@ is in. If the volume name is provided instead of the key or path, then
 providing the pool is necessary to find the volume to be uploaded into;
 otherwise, the first volume found by the key or path will be used.
 
+If the **--xpath** argument provides an XPath expression, it will be
+evaluated against the output XML and only those matching nodes will
+be printed. The default behaviour is to print each matching node as
+a standalone document, however, for ease of additional processing,
+the **--wrap** argument will cause the matching node to be wrapped
+in a common root node.
+
 
 vol-info
 --------
@@ -6949,10 +7020,17 @@ secret-dumpxml
 
 ::
 
-   secret-dumpxml secret
+   secret-dumpxml [--xpath EXPRESSION] [--wrap] secret
 
 Output properties of *secret* (specified by its UUID) as an XML dump to stdout.
 
+If the **--xpath** argument provides an XPath expression, it will be
+evaluated against the output XML and only those matching nodes will
+be printed. The default behaviour is to print each matching node as
+a standalone document, however, for ease of additional processing,
+the **--wrap** argument will cause the matching node to be wrapped
+in a common root node.
+
 
 secret-event
 ------------
@@ -7370,12 +7448,20 @@ snapshot-dumpxml
 
 ::
 
-   snapshot-dumpxml domain snapshot [--security-info]
+   snapshot-dumpxml [--security-info] [--xpath EXPRESSION] [--wrap]
+                    domain snapshot
 
 Output the snapshot XML for the domain's snapshot named *snapshot*.
 Using *--security-info* will also include security sensitive information.
 Use ``snapshot-current`` to easily access the XML of the current snapshot.
 
+If the **--xpath** argument provides an XPath expression, it will be
+evaluated against the output XML and only those matching nodes will
+be printed. The default behaviour is to print each matching node as
+a standalone document, however, for ease of additional processing,
+the **--wrap** argument will cause the matching node to be wrapped
+in a common root node.
+
 
 snapshot-parent
 ---------------
@@ -7655,7 +7741,8 @@ checkpoint-dumpxml
 
 ::
 
-   checkpoint-dumpxml domain checkpoint [--security-info] [--no-domain] [--size]
+   checkpoint-dumpxml [--security-info] [--no-domain] [--size]
+                      [--xpath EXPRESSION] [--wrap] domain checkpoint
 
 Output the checkpoint XML for the domain's checkpoint named
 *checkpoint*.  Using
@@ -7671,6 +7758,13 @@ space). Note that some hypervisors may require that *domain* is running when
 Using *--no-domain* will omit the <domain> element from the
 output for a more compact view.
 
+If the **--xpath** argument provides an XPath expression, it will be
+evaluated against the output XML and only those matching nodes will
+be printed. The default behaviour is to print each matching node as
+a standalone document, however, for ease of additional processing,
+the **--wrap** argument will cause the matching node to be wrapped
+in a common root node.
+
 
 checkpoint-parent
 -----------------
@@ -7773,10 +7867,17 @@ nwfilter-dumpxml
 
 ::
 
-   nwfilter-dumpxml nwfilter-name
+   nwfilter-dumpxml [--xpath EXPRESSION] [--wrap] nwfilter-name
 
 Output the network filter XML.
 
+If the **--xpath** argument provides an XPath expression, it will be
+evaluated against the output XML and only those matching nodes will
+be printed. The default behaviour is to print each matching node as
+a standalone document, however, for ease of additional processing,
+the **--wrap** argument will cause the matching node to be wrapped
+in a common root node.
+
 
 nwfilter-edit
 -------------
@@ -7876,11 +7977,18 @@ nwfilter-binding-dumpxml
 
 ::
 
-   nwfilter-binding-dumpxml port-name
+   nwfilter-binding-dumpxml [--xpath EXPRESSION] [--wrap] port-name
 
 Output the network filter binding XML for the network device called
 ``port-name``.
 
+If the **--xpath** argument provides an XPath expression, it will be
+evaluated against the output XML and only those matching nodes will
+be printed. The default behaviour is to print each matching node as
+a standalone document, however, for ease of additional processing,
+the **--wrap** argument will cause the matching node to be wrapped
+in a common root node.
+
 
 HYPERVISOR-SPECIFIC COMMANDS
 ============================
diff --git a/tools/virsh-backup.c b/tools/virsh-backup.c
index 7bac1923a6..db122abc09 100644
--- a/tools/virsh-backup.c
+++ b/tools/virsh-backup.c
@@ -115,6 +115,15 @@ static const vshCmdInfo info_backup_dumpxml[] = {
 
 static const vshCmdOptDef opts_backup_dumpxml[] = {
     VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
+    {.name = "xpath",
+     .type = VSH_OT_STRING,
+     .completer = virshCompleteEmpty,
+     .help = N_("xpath expression to filter the XML document")
+    },
+    {.name = "wrap",
+     .type = VSH_OT_BOOL,
+     .help = N_("wrap xpath results in an common root element"),
+    },
     {.name = NULL}
 };
 
@@ -124,15 +133,19 @@ cmdBackupDumpXML(vshControl *ctl,
 {
     g_autoptr(virshDomain) dom = NULL;
     g_autofree char *xml = NULL;
+    bool wrap = vshCommandOptBool(cmd, "wrap");
+    const char *xpath = NULL;
 
     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
         return false;
 
+    if (vshCommandOptStringQuiet(ctl, cmd, "xpath", &xpath) < 0)
+        return false;
+
     if (!(xml = virDomainBackupGetXMLDesc(dom, 0)))
         return false;
 
-    vshPrint(ctl, "%s", xml);
-    return true;
+    return virshDumpXML(ctl, xml, "domain-backup", xpath, wrap);
 }
 
 
diff --git a/tools/virsh-checkpoint.c b/tools/virsh-checkpoint.c
index 8ad37ece69..59f9b08e37 100644
--- a/tools/virsh-checkpoint.c
+++ b/tools/virsh-checkpoint.c
@@ -854,6 +854,15 @@ static const vshCmdOptDef opts_checkpoint_dumpxml[] = {
      .type = VSH_OT_BOOL,
      .help = N_("include backup size estimate in XML dump")
     },
+    {.name = "xpath",
+     .type = VSH_OT_STRING,
+     .completer = virshCompleteEmpty,
+     .help = N_("xpath expression to filter the XML document")
+    },
+    {.name = "wrap",
+     .type = VSH_OT_BOOL,
+     .help = N_("wrap xpath results in an common root element"),
+    },
     {.name = NULL}
 };
 
@@ -866,6 +875,8 @@ cmdCheckpointDumpXML(vshControl *ctl,
     g_autoptr(virshDomainCheckpoint) checkpoint = NULL;
     g_autofree char *xml = NULL;
     unsigned int flags = 0;
+    bool wrap = vshCommandOptBool(cmd, "wrap");
+    const char *xpath = NULL;
 
     if (vshCommandOptBool(cmd, "security-info"))
         flags |= VIR_DOMAIN_CHECKPOINT_XML_SECURE;
@@ -877,6 +888,9 @@ cmdCheckpointDumpXML(vshControl *ctl,
     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
         return false;
 
+    if (vshCommandOptStringQuiet(ctl, cmd, "xpath", &xpath) < 0)
+        return false;
+
     if (virshLookupCheckpoint(ctl, cmd, "checkpointname", dom,
                               &checkpoint, &name) < 0)
         return false;
@@ -884,8 +898,7 @@ cmdCheckpointDumpXML(vshControl *ctl,
     if (!(xml = virDomainCheckpointGetXMLDesc(checkpoint, flags)))
         return false;
 
-    vshPrint(ctl, "%s", xml);
-    return true;
+    return virshDumpXML(ctl, xml, "domain-checkpoint", xpath, wrap);
 }
 
 
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c
index 06ed6ba9cf..1c9703563a 100644
--- a/tools/virsh-domain.c
+++ b/tools/virsh-domain.c
@@ -4528,6 +4528,15 @@ static const vshCmdOptDef opts_save_image_dumpxml[] = {
      .type = VSH_OT_BOOL,
      .help = N_("include security sensitive information in XML dump")
     },
+    {.name = "xpath",
+     .type = VSH_OT_STRING,
+     .completer = virshCompleteEmpty,
+     .help = N_("xpath expression to filter the XML document")
+    },
+    {.name = "wrap",
+     .type = VSH_OT_BOOL,
+     .help = N_("wrap xpath results in an common root element"),
+    },
     {.name = NULL}
 };
 
@@ -4538,6 +4547,8 @@ cmdSaveImageDumpxml(vshControl *ctl, const vshCmd *cmd)
     unsigned int flags = 0;
     g_autofree char *xml = NULL;
     virshControl *priv = ctl->privData;
+    bool wrap = vshCommandOptBool(cmd, "wrap");
+    const char *xpath = NULL;
 
     if (vshCommandOptBool(cmd, "security-info"))
         flags |= VIR_DOMAIN_XML_SECURE;
@@ -4545,12 +4556,14 @@ cmdSaveImageDumpxml(vshControl *ctl, const vshCmd *cmd)
     if (vshCommandOptStringReq(ctl, cmd, "file", &file) < 0)
         return false;
 
+    if (vshCommandOptStringQuiet(ctl, cmd, "xpath", &xpath) < 0)
+        return false;
+
     xml = virDomainSaveImageGetXMLDesc(priv->conn, file, flags);
     if (!xml)
         return false;
 
-    vshPrint(ctl, "%s", xml);
-    return true;
+    return virshDumpXML(ctl, xml, "domain-save-image", xpath, wrap);
 }
 
 /*
@@ -4947,6 +4960,15 @@ static const vshCmdOptDef opts_managed_save_dumpxml[] = {
      .type = VSH_OT_BOOL,
      .help = N_("include security sensitive information in XML dump")
     },
+    {.name = "xpath",
+     .type = VSH_OT_STRING,
+     .completer = virshCompleteEmpty,
+     .help = N_("xpath expression to filter the XML document")
+    },
+    {.name = "wrap",
+     .type = VSH_OT_BOOL,
+     .help = N_("wrap xpath results in an common root element"),
+    },
     {.name = NULL}
 };
 
@@ -4956,6 +4978,8 @@ cmdManagedSaveDumpxml(vshControl *ctl, const vshCmd *cmd)
     g_autoptr(virshDomain) dom = NULL;
     unsigned int flags = 0;
     g_autofree char *xml = NULL;
+    bool wrap = vshCommandOptBool(cmd, "wrap");
+    const char *xpath = NULL;
 
     if (vshCommandOptBool(cmd, "security-info"))
         flags |= VIR_DOMAIN_XML_SECURE;
@@ -4963,11 +4987,13 @@ cmdManagedSaveDumpxml(vshControl *ctl, const vshCmd *cmd)
     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
         return false;
 
+    if (vshCommandOptStringQuiet(ctl, cmd, "xpath", &xpath) < 0)
+        return false;
+
     if (!(xml = virDomainManagedSaveGetXMLDesc(dom, flags)))
         return false;
 
-    vshPrint(ctl, "%s", xml);
-    return true;
+    return virshDumpXML(ctl, xml, "domain-save-image", xpath, wrap);
 }
 
 /*
@@ -10434,6 +10460,15 @@ static const vshCmdOptDef opts_dumpxml[] = {
      .type = VSH_OT_BOOL,
      .help = N_("provide XML suitable for migrations")
     },
+    {.name = "xpath",
+     .type = VSH_OT_STRING,
+     .completer = virshCompleteEmpty,
+     .help = N_("xpath expression to filter the XML document")
+    },
+    {.name = "wrap",
+     .type = VSH_OT_BOOL,
+     .help = N_("wrap xpath results in an common root element"),
+    },
     {.name = NULL}
 };
 
@@ -10441,12 +10476,14 @@ static bool
 cmdDumpXML(vshControl *ctl, const vshCmd *cmd)
 {
     g_autoptr(virshDomain) dom = NULL;
-    g_autofree char *dump = NULL;
+    g_autofree char *xml = NULL;
     unsigned int flags = 0;
     bool inactive = vshCommandOptBool(cmd, "inactive");
     bool secure = vshCommandOptBool(cmd, "security-info");
     bool update = vshCommandOptBool(cmd, "update-cpu");
     bool migratable = vshCommandOptBool(cmd, "migratable");
+    bool wrap = vshCommandOptBool(cmd, "wrap");
+    const char *xpath = NULL;
 
     if (inactive)
         flags |= VIR_DOMAIN_XML_INACTIVE;
@@ -10460,11 +10497,13 @@ cmdDumpXML(vshControl *ctl, const vshCmd *cmd)
     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
         return false;
 
-    if (!(dump = virDomainGetXMLDesc(dom, flags)))
+    if (vshCommandOptStringQuiet(ctl, cmd, "xpath", &xpath) < 0)
         return false;
 
-    vshPrint(ctl, "%s", dump);
-    return true;
+    if (!(xml = virDomainGetXMLDesc(dom, flags)))
+        return false;
+
+    return virshDumpXML(ctl, xml, "domain", xpath, wrap);
 }
 
 /*
diff --git a/tools/virsh-interface.c b/tools/virsh-interface.c
index 55d3532c55..b29ffc9bef 100644
--- a/tools/virsh-interface.c
+++ b/tools/virsh-interface.c
@@ -470,6 +470,16 @@ static const vshCmdOptDef opts_interface_dumpxml[] = {
      .type = VSH_OT_BOOL,
      .help = N_("show inactive defined XML")
     },
+    {.name = "xpath",
+     .type = VSH_OT_STRING,
+     .completer = virshCompleteEmpty,
+     .help = N_("xpath expression to filter the XML document")
+    },
+    {.name = "wrap",
+     .type = VSH_OT_BOOL,
+     .help = N_("wrap xpath results in an common root element"),
+    },
+
     {.name = NULL}
 };
 
@@ -477,7 +487,9 @@ static bool
 cmdInterfaceDumpXML(vshControl *ctl, const vshCmd *cmd)
 {
     g_autoptr(virshInterface) iface = NULL;
-    g_autofree char *dump = NULL;
+    g_autofree char *xml = NULL;
+    bool wrap = vshCommandOptBool(cmd, "wrap");
+    const char *xpath = NULL;
     unsigned int flags = 0;
 
     if (vshCommandOptBool(cmd, "inactive"))
@@ -486,11 +498,13 @@ cmdInterfaceDumpXML(vshControl *ctl, const vshCmd *cmd)
     if (!(iface = virshCommandOptInterface(ctl, cmd, NULL)))
         return false;
 
-    if (!(dump = virInterfaceGetXMLDesc(iface, flags)))
+    if (vshCommandOptStringQuiet(ctl, cmd, "xpath", &xpath) < 0)
         return false;
 
-    vshPrint(ctl, "%s", dump);
-    return true;
+    if (!(xml = virInterfaceGetXMLDesc(iface, flags)))
+        return false;
+
+    return virshDumpXML(ctl, xml, "interface", xpath, wrap);
 }
 
 /*
diff --git a/tools/virsh-network.c b/tools/virsh-network.c
index 97a160f772..99ced6ccc6 100644
--- a/tools/virsh-network.c
+++ b/tools/virsh-network.c
@@ -349,6 +349,15 @@ static const vshCmdOptDef opts_network_dumpxml[] = {
      .type = VSH_OT_BOOL,
      .help = N_("show inactive defined XML")
     },
+    {.name = "xpath",
+     .type = VSH_OT_STRING,
+     .completer = virshCompleteEmpty,
+     .help = N_("xpath expression to filter the XML document")
+    },
+    {.name = "wrap",
+     .type = VSH_OT_BOOL,
+     .help = N_("wrap xpath results in an common root element"),
+    },
     {.name = NULL}
 };
 
@@ -356,8 +365,10 @@ static bool
 cmdNetworkDumpXML(vshControl *ctl, const vshCmd *cmd)
 {
     g_autoptr(virshNetwork) network = NULL;
-    g_autofree char *dump = NULL;
+    g_autofree char *xml = NULL;
     unsigned int flags = 0;
+    bool wrap = vshCommandOptBool(cmd, "wrap");
+    const char *xpath = NULL;
 
     if (!(network = virshCommandOptNetwork(ctl, cmd, NULL)))
         return false;
@@ -365,12 +376,13 @@ cmdNetworkDumpXML(vshControl *ctl, const vshCmd *cmd)
     if (vshCommandOptBool(cmd, "inactive"))
         flags |= VIR_NETWORK_XML_INACTIVE;
 
-    if (!(dump = virNetworkGetXMLDesc(network, flags))) {
+    if (vshCommandOptStringQuiet(ctl, cmd, "xpath", &xpath) < 0)
         return false;
-    }
 
-    vshPrint(ctl, "%s", dump);
-    return true;
+    if (!(xml = virNetworkGetXMLDesc(network, flags)))
+        return false;
+
+    return virshDumpXML(ctl, xml, "network", xpath, wrap);
 }
 
 /*
@@ -1542,6 +1554,15 @@ static const vshCmdInfo info_network_port_dumpxml[] = {
 static const vshCmdOptDef opts_network_port_dumpxml[] = {
     VIRSH_COMMON_OPT_NETWORK_FULL(VIR_CONNECT_LIST_NETWORKS_ACTIVE),
     VIRSH_COMMON_OPT_NETWORK_PORT(0),
+    {.name = "xpath",
+     .type = VSH_OT_STRING,
+     .completer = virshCompleteEmpty,
+     .help = N_("xpath expression to filter the XML document")
+    },
+    {.name = "wrap",
+     .type = VSH_OT_BOOL,
+     .help = N_("wrap xpath results in an common root element"),
+    },
     {.name = NULL}
 };
 
@@ -1551,8 +1572,10 @@ cmdNetworkPortDumpXML(vshControl *ctl, const vshCmd *cmd)
     g_autoptr(virshNetwork) network = NULL;
     virNetworkPortPtr port = NULL;
     bool ret = true;
-    g_autofree char *dump = NULL;
+    g_autofree char *xml = NULL;
     unsigned int flags = 0;
+    bool wrap = vshCommandOptBool(cmd, "wrap");
+    const char *xpath = NULL;
 
     if (!(network = virshCommandOptNetwork(ctl, cmd, NULL)))
         goto cleanup;
@@ -1560,13 +1583,13 @@ cmdNetworkPortDumpXML(vshControl *ctl, const vshCmd *cmd)
     if (!(port = virshCommandOptNetworkPort(ctl, cmd, network, NULL)))
         goto cleanup;
 
-    dump = virNetworkPortGetXMLDesc(port, flags);
+    if (vshCommandOptStringQuiet(ctl, cmd, "xpath", &xpath) < 0)
+        return false;
 
-    if (dump != NULL) {
-        vshPrint(ctl, "%s", dump);
-    } else {
-        ret = false;
-    }
+    if (!(xml = virNetworkPortGetXMLDesc(port, flags)))
+        goto cleanup;
+
+    ret = virshDumpXML(ctl, xml, "network-port", xpath, wrap);
 
  cleanup:
     if (port)
diff --git a/tools/virsh-nodedev.c b/tools/virsh-nodedev.c
index 90066249af..72516c89c3 100644
--- a/tools/virsh-nodedev.c
+++ b/tools/virsh-nodedev.c
@@ -566,6 +566,15 @@ static const vshCmdOptDef opts_node_device_dumpxml[] = {
      .help = N_("device name or wwn pair in 'wwnn,wwpn' format"),
      .completer = virshNodeDeviceNameCompleter,
     },
+    {.name = "xpath",
+     .type = VSH_OT_STRING,
+     .completer = virshCompleteEmpty,
+     .help = N_("xpath expression to filter the XML document")
+    },
+    {.name = "wrap",
+     .type = VSH_OT_BOOL,
+     .help = N_("wrap xpath results in an common root element"),
+    },
     {.name = NULL}
 };
 
@@ -575,10 +584,15 @@ cmdNodeDeviceDumpXML(vshControl *ctl, const vshCmd *cmd)
     g_autoptr(virshNodeDevice) device = NULL;
     g_autofree char *xml = NULL;
     const char *device_value = NULL;
+    bool wrap = vshCommandOptBool(cmd, "wrap");
+    const char *xpath = NULL;
 
     if (vshCommandOptStringReq(ctl, cmd, "device", &device_value) < 0)
          return false;
 
+    if (vshCommandOptStringQuiet(ctl, cmd, "xpath", &xpath) < 0)
+        return false;
+
     device = vshFindNodeDevice(ctl, device_value);
 
     if (!device)
@@ -587,8 +601,7 @@ cmdNodeDeviceDumpXML(vshControl *ctl, const vshCmd *cmd)
     if (!(xml = virNodeDeviceGetXMLDesc(device, 0)))
         return false;
 
-    vshPrint(ctl, "%s\n", xml);
-    return true;
+    return virshDumpXML(ctl, xml, "node-device", xpath, wrap);
 }
 
 /*
diff --git a/tools/virsh-nwfilter.c b/tools/virsh-nwfilter.c
index 5a9e57e3f5..ff7f6f4026 100644
--- a/tools/virsh-nwfilter.c
+++ b/tools/virsh-nwfilter.c
@@ -186,6 +186,15 @@ static const vshCmdOptDef opts_nwfilter_dumpxml[] = {
      .help = N_("network filter name or uuid"),
      .completer = virshNWFilterNameCompleter,
     },
+    {.name = "xpath",
+     .type = VSH_OT_STRING,
+     .completer = virshCompleteEmpty,
+     .help = N_("xpath expression to filter the XML document")
+    },
+    {.name = "wrap",
+     .type = VSH_OT_BOOL,
+     .help = N_("wrap xpath results in an common root element"),
+    },
     {.name = NULL}
 };
 
@@ -193,20 +202,20 @@ static bool
 cmdNWFilterDumpXML(vshControl *ctl, const vshCmd *cmd)
 {
     g_autoptr(virshNWFilter) nwfilter = NULL;
-    bool ret = true;
-    g_autofree char *dump = NULL;
+    g_autofree char *xml = NULL;
+    bool wrap = vshCommandOptBool(cmd, "wrap");
+    const char *xpath = NULL;
 
     if (!(nwfilter = virshCommandOptNWFilter(ctl, cmd, NULL)))
         return false;
 
-    dump = virNWFilterGetXMLDesc(nwfilter, 0);
-    if (dump != NULL) {
-        vshPrint(ctl, "%s", dump);
-    } else {
-        ret = false;
-    }
+    if (vshCommandOptStringQuiet(ctl, cmd, "xpath", &xpath) < 0)
+        return false;
 
-    return ret;
+    if (!(xml = virNWFilterGetXMLDesc(nwfilter, 0)))
+        return false;
+
+    return virshDumpXML(ctl, xml, "nwfilter", xpath, wrap);
 }
 
 static int
@@ -599,6 +608,15 @@ static const vshCmdOptDef opts_nwfilter_binding_dumpxml[] = {
      .help = N_("network filter binding portdev"),
      .completer = virshNWFilterBindingNameCompleter,
     },
+    {.name = "xpath",
+     .type = VSH_OT_STRING,
+     .completer = virshCompleteEmpty,
+     .help = N_("xpath expression to filter the XML document")
+    },
+    {.name = "wrap",
+     .type = VSH_OT_BOOL,
+     .help = N_("wrap xpath results in an common root element"),
+    },
     {.name = NULL}
 };
 
@@ -607,18 +625,19 @@ cmdNWFilterBindingDumpXML(vshControl *ctl, const vshCmd *cmd)
 {
     virNWFilterBindingPtr binding;
     bool ret = true;
-    g_autofree char *dump = NULL;
+    g_autofree char *xml = NULL;
+    bool wrap = vshCommandOptBool(cmd, "wrap");
+    const char *xpath = NULL;
 
     if (!(binding = virshCommandOptNWFilterBinding(ctl, cmd, NULL)))
         return false;
 
-    dump = virNWFilterBindingGetXMLDesc(binding, 0);
-    if (dump != NULL) {
-        vshPrint(ctl, "%s", dump);
-    } else {
-        ret = false;
-    }
+    if (!(xml = virNWFilterBindingGetXMLDesc(binding, 0)))
+        goto cleanup;
 
+    ret = virshDumpXML(ctl, xml, "nwfilter-binding", xpath, wrap);
+
+ cleanup:
     virNWFilterBindingFree(binding);
     return ret;
 }
diff --git a/tools/virsh-pool.c b/tools/virsh-pool.c
index 7e7a8d511f..4fb6875a98 100644
--- a/tools/virsh-pool.c
+++ b/tools/virsh-pool.c
@@ -776,6 +776,15 @@ static const vshCmdOptDef opts_pool_dumpxml[] = {
      .type = VSH_OT_BOOL,
      .help = N_("show inactive defined XML")
     },
+    {.name = "xpath",
+     .type = VSH_OT_STRING,
+     .completer = virshCompleteEmpty,
+     .help = N_("xpath expression to filter the XML document")
+    },
+    {.name = "wrap",
+     .type = VSH_OT_BOOL,
+     .help = N_("wrap xpath results in an common root element"),
+    },
     {.name = NULL}
 };
 
@@ -783,10 +792,11 @@ static bool
 cmdPoolDumpXML(vshControl *ctl, const vshCmd *cmd)
 {
     g_autoptr(virshStoragePool) pool = NULL;
-    bool ret = true;
     bool inactive = vshCommandOptBool(cmd, "inactive");
     unsigned int flags = 0;
-    g_autofree char *dump = NULL;
+    g_autofree char *xml = NULL;
+    bool wrap = vshCommandOptBool(cmd, "wrap");
+    const char *xpath = NULL;
 
     if (inactive)
         flags |= VIR_STORAGE_XML_INACTIVE;
@@ -794,14 +804,13 @@ cmdPoolDumpXML(vshControl *ctl, const vshCmd *cmd)
     if (!(pool = virshCommandOptPool(ctl, cmd, "pool", NULL)))
         return false;
 
-    dump = virStoragePoolGetXMLDesc(pool, flags);
-    if (dump != NULL) {
-        vshPrint(ctl, "%s", dump);
-    } else {
-        ret = false;
-    }
+    if (vshCommandOptStringQuiet(ctl, cmd, "xpath", &xpath) < 0)
+        return false;
 
-    return ret;
+    if (!(xml = virStoragePoolGetXMLDesc(pool, flags)))
+        return false;
+
+    return virshDumpXML(ctl, xml, "pool", xpath, wrap);
 }
 
 static int
diff --git a/tools/virsh-secret.c b/tools/virsh-secret.c
index 842647cb17..79fa3faf5a 100644
--- a/tools/virsh-secret.c
+++ b/tools/virsh-secret.c
@@ -138,6 +138,15 @@ static const vshCmdOptDef opts_secret_dumpxml[] = {
      .help = N_("secret UUID"),
      .completer = virshSecretUUIDCompleter,
     },
+    {.name = "xpath",
+     .type = VSH_OT_STRING,
+     .completer = virshCompleteEmpty,
+     .help = N_("xpath expression to filter the XML document")
+    },
+    {.name = "wrap",
+     .type = VSH_OT_BOOL,
+     .help = N_("wrap xpath results in an common root element"),
+    },
     {.name = NULL}
 };
 
@@ -147,16 +156,21 @@ cmdSecretDumpXML(vshControl *ctl, const vshCmd *cmd)
     virSecretPtr secret;
     bool ret = false;
     g_autofree char *xml = NULL;
+    bool wrap = vshCommandOptBool(cmd, "wrap");
+    const char *xpath = NULL;
 
     secret = virshCommandOptSecret(ctl, cmd, NULL);
     if (secret == NULL)
         return false;
 
+    if (vshCommandOptStringQuiet(ctl, cmd, "xpath", &xpath) < 0)
+        return false;
+
     xml = virSecretGetXMLDesc(secret, 0);
     if (xml == NULL)
         goto cleanup;
-    vshPrint(ctl, "%s", xml);
-    ret = true;
+
+    ret = virshDumpXML(ctl, xml, "secret", xpath, wrap);
 
  cleanup:
     virshSecretFree(secret);
diff --git a/tools/virsh-snapshot.c b/tools/virsh-snapshot.c
index 7cd76f39e2..73854d2486 100644
--- a/tools/virsh-snapshot.c
+++ b/tools/virsh-snapshot.c
@@ -1608,6 +1608,15 @@ static const vshCmdOptDef opts_snapshot_dumpxml[] = {
      .type = VSH_OT_BOOL,
      .help = N_("include security sensitive information in XML dump")
     },
+    {.name = "xpath",
+     .type = VSH_OT_STRING,
+     .completer = virshCompleteEmpty,
+     .help = N_("xpath expression to filter the XML document")
+    },
+    {.name = "wrap",
+     .type = VSH_OT_BOOL,
+     .help = N_("wrap xpath results in an common root element"),
+    },
     {.name = NULL}
 };
 
@@ -1619,6 +1628,8 @@ cmdSnapshotDumpXML(vshControl *ctl, const vshCmd *cmd)
     g_autoptr(virshDomainSnapshot) snapshot = NULL;
     g_autofree char *xml = NULL;
     unsigned int flags = 0;
+    bool wrap = vshCommandOptBool(cmd, "wrap");
+    const char *xpath = NULL;
 
     if (vshCommandOptBool(cmd, "security-info"))
         flags |= VIR_DOMAIN_XML_SECURE;
@@ -1632,11 +1643,13 @@ cmdSnapshotDumpXML(vshControl *ctl, const vshCmd *cmd)
     if (!(snapshot = virDomainSnapshotLookupByName(dom, name, 0)))
         return false;
 
+    if (vshCommandOptStringQuiet(ctl, cmd, "xpath", &xpath) < 0)
+        return false;
+
     if (!(xml = virDomainSnapshotGetXMLDesc(snapshot, flags)))
         return false;
 
-    vshPrint(ctl, "%s", xml);
-    return true;
+    return virshDumpXML(ctl, xml, "domain-snapshot", xpath, wrap);
 }
 
 /*
diff --git a/tools/virsh-volume.c b/tools/virsh-volume.c
index 503acda874..bf72d8135f 100644
--- a/tools/virsh-volume.c
+++ b/tools/virsh-volume.c
@@ -1159,6 +1159,15 @@ static const vshCmdInfo info_vol_dumpxml[] = {
 static const vshCmdOptDef opts_vol_dumpxml[] = {
     VIRSH_COMMON_OPT_VOL_FULL,
     VIRSH_COMMON_OPT_POOL_OPTIONAL,
+    {.name = "xpath",
+     .type = VSH_OT_STRING,
+     .completer = virshCompleteEmpty,
+     .help = N_("xpath expression to filter the XML document")
+    },
+    {.name = "wrap",
+     .type = VSH_OT_BOOL,
+     .help = N_("wrap xpath results in an common root element"),
+    },
     {.name = NULL}
 };
 
@@ -1166,21 +1175,20 @@ static bool
 cmdVolDumpXML(vshControl *ctl, const vshCmd *cmd)
 {
     g_autoptr(virshStorageVol) vol = NULL;
-    bool ret = true;
-    char *dump;
+    bool wrap = vshCommandOptBool(cmd, "wrap");
+    const char *xpath = NULL;
+    g_autofree char *xml = NULL;
 
     if (!(vol = virshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
         return false;
 
-    dump = virStorageVolGetXMLDesc(vol, 0);
-    if (dump != NULL) {
-        vshPrint(ctl, "%s", dump);
-        VIR_FREE(dump);
-    } else {
-        ret = false;
-    }
+    if (vshCommandOptStringQuiet(ctl, cmd, "xpath", &xpath) < 0)
+        return false;
 
-    return ret;
+    if (!(xml = virStorageVolGetXMLDesc(vol, 0)))
+        return false;
+
+    return virshDumpXML(ctl, xml, "volume", xpath, wrap);
 }
 
 static int
-- 
2.36.1



More information about the libvir-list mailing list