Adds two new commands and a new option:
- 'net-desc' to show/modify network title and description.
- 'net-metadata' to show/modify network metadata.
- Option '--title' for 'net-list' to print corresponding
network titles in an additional column.
- Documentation for all the above.
- XML Fallback function `virshNetworkGetXMLFromNet` for title and
description for compatibility with hosts running older versions
of libvirtd.
Signed-off-by: K Shiva Kiran <shiva_kr(a)riseup.net>
---
docs/manpages/virsh.rst | 77 ++++++++
tools/virsh-network.c | 411 ++++++++++++++++++++++++++++++++++++++--
tools/virsh-util.c | 25 +++
tools/virsh-util.h | 9 +
4 files changed, 511 insertions(+), 11 deletions(-)
diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst
index f4e5a0bd62..673812036d 100644
--- a/docs/manpages/virsh.rst
+++ b/docs/manpages/virsh.rst
@@ -5566,6 +5566,7 @@ to get a description of the XML network format used by libvirt.
Optionally, the format of the input XML file can be validated against an
internal RNG schema with *--validate*.
+
net-define
----------
@@ -5581,6 +5582,38 @@ Optionally, the format of the input XML file can be validated
against an
internal RNG schema with *--validate*.
+net-desc
+--------
+
+**Syntax:**
+
+::
+
+ net-desc network [[--live] [--config] |
+ [--current]] [--title] [--edit] [--new-desc
+ New description or title message]
+
+Show or modify description and title of a network. These values are user
+fields that allow storing arbitrary textual data to allow easy
+identification of networks. Title should be short, although it's not enforced.
+(See also ``net-metadata`` that works with XML based network metadata.)
+
+Flags *--live* or *--config* select whether this command works on live
+or persistent definitions of the network. If both *--live* and *--config*
+are specified, the *--config* option takes precedence on getting the current
+description and both live configuration and config are updated while setting
+the description. *--current* is exclusive and implied if none of these was
+specified.
+
+Flag *--edit* specifies that an editor with the contents of current
+description or title should be opened and the contents saved back afterwards.
+
+Flag *--title* selects operation on the title field instead of description.
+
+If neither of *--edit* and *--new-desc* are specified the note or description
+is displayed instead of being modified.
+
+
net-destroy
-----------
@@ -5689,6 +5722,7 @@ net-list
{ [--table] | --name | --uuid }
[--persistent] [<--transient>]
[--autostart] [<--no-autostart>]
+ [--title]
Returns the list of active networks, if *--all* is specified this will also
include defined but inactive networks, if *--inactive* is specified only the
@@ -5703,12 +5737,55 @@ instead of names. Flag *--table* specifies that the legacy
table-formatted
output should be used. This is the default. All of these are mutually
exclusive.
+If *--title* is specified, then the short network description (title) is
+printed in an extra column. This flag is usable only with the default
+*--table* output.
+
NOTE: When talking to older servers, this command is forced to use a series of
API calls with an inherent race, where a pool might not be listed or might appear
more than once if it changed state between calls while the list was being
collected. Newer servers do not have this problem.
+net-metadata
+------------
+
+**Syntax:**
+
+::
+
+ net-metadata network [[--live] [--config] | [--current]]
+ [--edit] [uri] [key] [set] [--remove]
+
+Show or modify custom XML metadata of a network. The metadata is a user
+defined XML that allows storing arbitrary XML data in the network definition.
+Multiple separate custom metadata pieces can be stored in the network XML.
+The pieces are identified by a private XML namespace provided via the
+*uri* argument. (See also ``net-desc`` that works with textual metadata of
+a network, such as title and description.)
+
+Flags *--live* or *--config* select whether this command works on live
+or persistent definitions of the network. If both *--live* and *--config*
+are specified, the *--config* option takes precedence on getting the current
+description and both live configuration and config are updated while setting
+the description. *--current* is exclusive and implied if none of these was
+specified.
+
+Flag *--remove* specifies that the metadata element specified by the *uri*
+argument should be removed rather than updated.
+
+Flag *--edit* specifies that an editor with the metadata identified by the
+*uri* argument should be opened and the contents saved back afterwards.
+Otherwise the new contents can be provided via the *set* argument.
+
+When setting metadata via *--edit* or *set* the *key* argument must be
+specified and is used to prefix the custom elements to bind them
+to the private namespace.
+
+If neither of *--edit* and *set* are specified the XML metadata corresponding
+to the *uri* namespace is displayed instead of being modified.
+
+
net-name
--------
diff --git a/tools/virsh-network.c b/tools/virsh-network.c
index 42b7dba761..5a5cd15e24 100644
--- a/tools/virsh-network.c
+++ b/tools/virsh-network.c
@@ -330,6 +330,353 @@ cmdNetworkDestroy(vshControl *ctl, const vshCmd *cmd)
return ret;
}
+/*
+ * "net-desc" command
+ */
+static const vshCmdInfo info_network_desc[] = {
+ {.name = "help",
+ .data = N_("show or set network's description or title")
+ },
+ {.name = "desc",
+ .data = N_("Allows setting or modifying the description or title of a
network.")
+ },
+ {.name = NULL}
+};
+
+static const vshCmdOptDef opts_network_desc[] = {
+ VIRSH_COMMON_OPT_NETWORK_FULL(0),
+ VIRSH_COMMON_OPT_LIVE(N_("modify/get running state")),
+ VIRSH_COMMON_OPT_CONFIG(N_("modify/get persistent configuration")),
+ VIRSH_COMMON_OPT_CURRENT(N_("modify/get current state configuration")),
+ {.name = "title",
+ .type = VSH_OT_BOOL,
+ .help = N_("modify/get the title instead of description")
+ },
+ {.name = "edit",
+ .type = VSH_OT_BOOL,
+ .help = N_("open an editor to modify the description")
+ },
+ {.name = "new-desc",
+ .type = VSH_OT_ARGV,
+ .help = N_("message")
+ },
+ {.name = NULL}
+};
+
+/* extract description or title from network xml */
+static char *
+virshGetNetworkDescription(vshControl *ctl, virNetworkPtr net,
+ bool title, unsigned int flags)
+{
+ char *desc = NULL;
+ g_autoptr(xmlDoc) doc = NULL;
+ g_autoptr(xmlXPathContext) ctxt = NULL;
+ int type;
+
+ if (title)
+ type = VIR_NETWORK_METADATA_TITLE;
+ else
+ type = VIR_NETWORK_METADATA_DESCRIPTION;
+
+ if ((desc = virNetworkGetMetadata(net, type, NULL, flags))) {
+ return desc;
+ } else {
+ int errCode = virGetLastErrorCode();
+
+ if (errCode == VIR_ERR_NO_NETWORK_METADATA) {
+ desc = g_strdup("");
+ vshResetLibvirtError();
+ return desc;
+ }
+
+ if (errCode != VIR_ERR_NO_SUPPORT)
+ return desc;
+ }
+
+ /* fall back to xml */
+ if (virshNetworkGetXMLFromNet(ctl, net, flags, &doc, &ctxt) < 0)
+ return NULL;
+
+ if (title)
+ desc = virXPathString("string(./title[1])", ctxt);
+ else
+ desc = virXPathString("string(./description[1])", ctxt);
+
+ if (!desc)
+ desc = g_strdup("");
+
+ return desc;
+}
+
+static bool
+cmdNetworkDesc(vshControl *ctl, const vshCmd *cmd)
+{
+ g_autoptr(virshNetwork) net = NULL;
+ bool config = vshCommandOptBool(cmd, "config");
+ bool live = vshCommandOptBool(cmd, "live");
+ bool current = vshCommandOptBool(cmd, "current");
+
+ bool title = vshCommandOptBool(cmd, "title");
+ bool edit = vshCommandOptBool(cmd, "edit");
+
+ int type;
+ g_autofree char *descArg = NULL;
+ const vshCmdOpt *opt = NULL;
+ g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+ unsigned int flags = VIR_NETWORK_UPDATE_AFFECT_CURRENT;
+ unsigned int queryflags = 0;
+
+ VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
+ VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
+
+ if (config) {
+ flags |= VIR_NETWORK_UPDATE_AFFECT_CONFIG;
+ queryflags |= VIR_NETWORK_XML_INACTIVE;
+ }
+ if (live)
+ flags |= VIR_NETWORK_UPDATE_AFFECT_LIVE;
+
+ if (!(net = virshCommandOptNetwork(ctl, cmd, NULL)))
+ return false;
+
+ if (title)
+ type = VIR_NETWORK_METADATA_TITLE;
+ else
+ type = VIR_NETWORK_METADATA_DESCRIPTION;
+
+ while ((opt = vshCommandOptArgv(ctl, cmd, opt)))
+ virBufferAsprintf(&buf, "%s ", opt->data);
+
+ virBufferTrim(&buf, " ");
+
+ descArg = virBufferContentAndReset(&buf);
+
+ if (edit || descArg) {
+ g_autofree char *descNet = NULL;
+ g_autofree char *descNew = NULL;
+
+ if (!(descNet = virshGetNetworkDescription(ctl, net, title, queryflags)))
+ return false;
+
+ if (!descArg)
+ descArg = g_strdup(descNet);
+
+ if (edit) {
+ g_autoptr(vshTempFile) tmp = NULL;
+ g_autofree char *desc_edited = NULL;
+ char *tmpstr;
+
+ /* Create and open the temporary file. */
+ if (!(tmp = vshEditWriteToTempFile(ctl, descArg)))
+ return false;
+
+ /* Start the editor. */
+ if (vshEditFile(ctl, tmp) == -1)
+ return false;
+
+ /* Read back the edited file. */
+ if (!(desc_edited = vshEditReadBackFile(ctl, tmp)))
+ return false;
+
+ /* strip a possible newline at the end of file; some
+ * editors enforce a newline, this makes editing the title
+ * more convenient */
+ if (title &&
+ (tmpstr = strrchr(desc_edited, '\n')) &&
+ *(tmpstr+1) == '\0')
+ *tmpstr = '\0';
+
+ /* Compare original XML with edited. Has it changed at all? */
+ if (STREQ(descNet, desc_edited)) {
+ if (title)
+ vshPrintExtra(ctl, "%s", _("Network title not
changed\n"));
+ else
+ vshPrintExtra(ctl, "%s", _("Network description not
changed\n"));
+
+ return true;
+ }
+
+ descNew = g_steal_pointer(&desc_edited);
+ } else {
+ descNew = g_steal_pointer(&descArg);
+ }
+
+ if (virNetworkSetMetadata(net, type, descNew, NULL, NULL, flags) < 0) {
+ if (title)
+ vshError(ctl, "%s", _("Failed to set new network
title"));
+ else
+ vshError(ctl, "%s", _("Failed to set new network
description"));
+
+ return false;
+ }
+
+ if (title)
+ vshPrintExtra(ctl, "%s", _("Network title updated
successfully"));
+ else
+ vshPrintExtra(ctl, "%s", _("Network description updated
successfully"));
+
+ } else {
+ g_autofree char *desc = virshGetNetworkDescription(ctl, net, title, queryflags);
+ if (!desc)
+ return false;
+
+ if (strlen(desc) > 0) {
+ vshPrint(ctl, "%s", desc);
+ } else {
+ if (title)
+ vshPrintExtra(ctl, _("No title for network: %1$s"),
virNetworkGetName(net));
+ else
+ vshPrintExtra(ctl, _("No description for network: %1$s"),
virNetworkGetName(net));
+ }
+ }
+
+ return true;
+}
+
+/*
+ * "net-metadata" command
+ */
+static const vshCmdInfo info_network_metadata[] = {
+ {.name = "help",
+ .data = N_("show or set network's custom XML metadata")
+ },
+ {.name = "desc",
+ .data = N_("Shows or modifies the XML metadata of a network.")
+ },
+ {.name = NULL}
+};
+
+static const vshCmdOptDef opts_network_metadata[] = {
+ VIRSH_COMMON_OPT_NETWORK_FULL(0),
+ {.name = "uri",
+ .type = VSH_OT_DATA,
+ .flags = VSH_OFLAG_REQ,
+ .help = N_("URI of the namespace")
+ },
+ VIRSH_COMMON_OPT_LIVE(N_("modify/get running state")),
+ VIRSH_COMMON_OPT_CONFIG(N_("modify/get persistent configuration")),
+ VIRSH_COMMON_OPT_CURRENT(N_("modify/get current state configuration")),
+ {.name = "edit",
+ .type = VSH_OT_BOOL,
+ .help = N_("use an editor to change the metadata")
+ },
+ {.name = "key",
+ .type = VSH_OT_STRING,
+ .help = N_("key to be used as a namespace identifier"),
+ },
+ {.name = "set",
+ .type = VSH_OT_STRING,
+ .completer = virshCompleteEmpty,
+ .help = N_("new metadata to set"),
+ },
+ {.name = "remove",
+ .type = VSH_OT_BOOL,
+ .help = N_("remove the metadata corresponding to an uri")
+ },
+ {.name = NULL}
+};
+
+/* helper to add new metadata using the --edit option */
+static char *
+virshNetworkGetEditMetadata(vshControl *ctl G_GNUC_UNUSED,
+ virNetworkPtr net,
+ const char *uri,
+ unsigned int flags)
+{
+ char *ret;
+
+ if (!(ret = virNetworkGetMetadata(net, VIR_NETWORK_METADATA_ELEMENT,
+ uri, flags))) {
+ vshResetLibvirtError();
+ ret = g_strdup("\n");
+ }
+
+ return ret;
+}
+
+static bool
+cmdNetworkMetadata(vshControl *ctl, const vshCmd *cmd)
+{
+ g_autoptr(virshNetwork) net = NULL;
+ g_autoptr(xmlXPathContext) ctxt = NULL;
+ bool config = vshCommandOptBool(cmd, "config");
+ bool live = vshCommandOptBool(cmd, "live");
+ bool current = vshCommandOptBool(cmd, "current");
+ bool edit = vshCommandOptBool(cmd, "edit");
+ bool rem = vshCommandOptBool(cmd, "remove");
+ const char *set = NULL;
+ const char *uri = NULL;
+ const char *key = NULL;
+ unsigned int flags = VIR_NETWORK_UPDATE_AFFECT_CURRENT;
+ bool ret = false;
+
+ VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
+ VSH_EXCLUSIVE_OPTIONS_VAR(current, config);
+ VSH_EXCLUSIVE_OPTIONS("edit", "set");
+ VSH_EXCLUSIVE_OPTIONS("remove", "set");
+ VSH_EXCLUSIVE_OPTIONS("remove", "edit");
+
+ if (config)
+ flags |= VIR_NETWORK_UPDATE_AFFECT_CONFIG;
+ if (live)
+ flags |= VIR_NETWORK_UPDATE_AFFECT_LIVE;
+
+ if (!(net = virshCommandOptNetwork(ctl, cmd, NULL)))
+ return false;
+
+ if (vshCommandOptStringReq(ctl, cmd, "uri", &uri) < 0 ||
+ vshCommandOptStringReq(ctl, cmd, "key", &key) < 0 ||
+ vshCommandOptStringReq(ctl, cmd, "set", &set) < 0)
+ return false;
+
+ if ((set || edit) && !key) {
+ vshError(ctl, "%s",
+ _("namespace key is required when modifying metadata"));
+ return false;
+ }
+
+ if (set || rem) {
+ if (virNetworkSetMetadata(net, VIR_NETWORK_METADATA_ELEMENT,
+ set, key, uri, flags))
+ return false;
+
+ if (rem)
+ vshPrintExtra(ctl, "%s\n", _("Metadata removed"));
+ else
+ vshPrintExtra(ctl, "%s\n", _("Metadata modified"));
+ } else if (edit) {
+#define EDIT_GET_XML \
+ virshNetworkGetEditMetadata(ctl, net, uri, flags)
+#define EDIT_NOT_CHANGED \
+ do { \
+ vshPrintExtra(ctl, "%s", _("Metadata not changed")); \
+ ret = true; \
+ goto edit_cleanup; \
+ } while (0)
+
+#define EDIT_DEFINE \
+ (virNetworkSetMetadata(net, VIR_NETWORK_METADATA_ELEMENT, doc_edited, \
+ key, uri, flags) == 0)
+#include "virsh-edit.c"
+
+ vshPrintExtra(ctl, "%s\n", _("Metadata modified"));
+ } else {
+ g_autofree char *data = NULL;
+ g_autoptr(xmlDoc) doc = NULL;
+ /* get */
+ if (!(data = virNetworkGetMetadata(net, VIR_NETWORK_METADATA_ELEMENT,
+ uri, flags)))
+ return false;
+
+ vshPrint(ctl, "%s\n", data);
+ }
+
+ ret = true;
+
+ cleanup:
+ return ret;
+}
+
/*
* "net-dumpxml" command
*/
@@ -708,6 +1055,10 @@ static const vshCmdOptDef opts_network_list[] = {
.type = VSH_OT_BOOL,
.help = N_("list table (default)")
},
+ {.name = "title",
+ .type = VSH_OT_BOOL,
+ .help = N_("show network title")
+ },
{.name = NULL}
};
@@ -721,6 +1072,7 @@ cmdNetworkList(vshControl *ctl, const vshCmd *cmd G_GNUC_UNUSED)
size_t i;
bool ret = false;
bool optName = vshCommandOptBool(cmd, "name");
+ bool optTitle = vshCommandOptBool(cmd, "title");
bool optTable = vshCommandOptBool(cmd, "table");
bool optUUID = vshCommandOptBool(cmd, "uuid");
char uuid[VIR_UUID_STRING_BUFLEN];
@@ -754,8 +1106,12 @@ cmdNetworkList(vshControl *ctl, const vshCmd *cmd G_GNUC_UNUSED)
return false;
if (optTable) {
- table = vshTableNew(_("Name"), _("State"),
_("Autostart"),
- _("Persistent"), NULL);
+ if (optTitle)
+ table = vshTableNew(_("Name"), _("State"),
_("Autostart"),
+ _("Persistent"), _("Title"), NULL);
+ else
+ table = vshTableNew(_("Name"), _("State"),
_("Autostart"),
+ _("Persistent"), NULL);
if (!table)
goto cleanup;
}
@@ -771,16 +1127,37 @@ cmdNetworkList(vshControl *ctl, const vshCmd *cmd G_GNUC_UNUSED)
else
autostartStr = is_autostart ? _("yes") : _("no");
- if (vshTableRowAppend(table,
- virNetworkGetName(network),
- virNetworkIsActive(network) ?
- _("active") : _("inactive"),
- autostartStr,
- virNetworkIsPersistent(network) ?
- _("yes") : _("no"),
- NULL) < 0)
- goto cleanup;
+ if (optTitle) {
+ g_autofree char *title = NULL;
+
+ if (!(title = virshGetNetworkDescription(ctl, network, true, 0)))
+ goto cleanup;
+ if (vshTableRowAppend(table,
+ virNetworkGetName(network),
+ virNetworkIsActive(network) ?
+ _("active") : _("inactive"),
+ autostartStr,
+ virNetworkIsPersistent(network) ?
+ _("yes") : _("no"),
+ title,
+ NULL) < 0)
+ goto cleanup;
+
+ } else {
+ if (vshTableRowAppend(table,
+ virNetworkGetName(network),
+ virNetworkIsActive(network) ?
+ _("active") : _("inactive"),
+ autostartStr,
+ virNetworkIsPersistent(network) ?
+ _("yes") : _("no"),
+ NULL) < 0)
+ goto cleanup;
+
+ }
+
} else if (optUUID) {
+
if (virNetworkGetUUIDString(network, uuid) < 0) {
vshError(ctl, "%s", _("Failed to get network's
UUID"));
goto cleanup;
@@ -1825,6 +2202,12 @@ const vshCmdDef networkCmds[] = {
.info = info_network_define,
.flags = 0
},
+ {.name = "net-desc",
+ .handler = cmdNetworkDesc,
+ .opts = opts_network_desc,
+ .info = info_network_desc,
+ .flags = 0
+ },
{.name = "net-destroy",
.handler = cmdNetworkDestroy,
.opts = opts_network_destroy,
@@ -1867,6 +2250,12 @@ const vshCmdDef networkCmds[] = {
.info = info_network_list,
.flags = 0
},
+ {.name = "net-metadata",
+ .handler = cmdNetworkMetadata,
+ .opts = opts_network_metadata,
+ .info = info_network_metadata,
+ .flags = 0
+ },
{.name = "net-name",
.handler = cmdNetworkName,
.opts = opts_network_name,
diff --git a/tools/virsh-util.c b/tools/virsh-util.c
index 61e403a636..fb6327613a 100644
--- a/tools/virsh-util.c
+++ b/tools/virsh-util.c
@@ -398,6 +398,31 @@ virshDomainGetXMLFromDom(vshControl *ctl,
}
+int
+virshNetworkGetXMLFromNet(vshControl *ctl,
+ virNetworkPtr net,
+ unsigned int flags,
+ xmlDocPtr *xml,
+ xmlXPathContextPtr *ctxt)
+{
+ g_autofree char *desc = NULL;
+
+ if (!(desc = virNetworkGetXMLDesc(net, flags))) {
+ vshError(ctl, _("Failed to get network description xml"));
+ return -1;
+ }
+
+ *xml = virXMLParseStringCtxt(desc, _("(network_definition)"), ctxt);
+
+ if (!(*xml)) {
+ vshError(ctl, _("Failed to parse network description xml"));
+ return -1;
+ }
+
+ return 0;
+}
+
+
int
virshDomainGetXML(vshControl *ctl,
const vshCmd *cmd,
diff --git a/tools/virsh-util.h b/tools/virsh-util.h
index 0f81a2771b..2386847072 100644
--- a/tools/virsh-util.h
+++ b/tools/virsh-util.h
@@ -143,6 +143,15 @@ virshDomainGetXMLFromDom(vshControl *ctl,
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(4)
ATTRIBUTE_NONNULL(5) G_GNUC_WARN_UNUSED_RESULT;
+int
+virshNetworkGetXMLFromNet(vshControl *ctl,
+ virNetworkPtr net,
+ unsigned int flags,
+ xmlDocPtr *xml,
+ xmlXPathContextPtr *ctxt)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(4)
+ ATTRIBUTE_NONNULL(5) G_GNUC_WARN_UNUSED_RESULT;
+
int
virshDomainGetXML(vshControl *ctl,
const vshCmd *cmd,
--
2.41.0