[libvirt] [PATCHv2 0/2] two different possibilities for virsh net-update command

The two following patches are alternate approaches that each have a slightly different commandline syntax. In V1 of the net-update patch, I tried having a single string option that could contain either an xml string, or the name of a file containing an xml string, then two options that would be used to make the decision of file vs. xml: net-update netname add ip-dhcp-host --xml "<host mac='xx..' .../>" net-update netname add ip-dhcp-host /tmp/myfile.xml (or 'net-update netname add ip-dhcp-host --file /tmp/myfile.xml') (the trick here is that "--xml" is a boolean option, *not* a string option that gets set to "<host...", and the string itself goes into a generic, usually unnamed option called xmldata. Likewise, the filename is being put into xmldata, and if the "--xml" boolean option isn't given on the commandline, it's assumed that it is a file. The ability to specifically say it is a file was just added for completeness, but really is semantically useless.) The problem with this is that someone looking at the syntax in the help might come up with a confusing commandline like this: net-update netname add --xmldata /tmp/myfile.xml --file \ --section ip-dhcp-host --live or net-update netname add --xmldata "<host mac='x:x...'/>" --xml \ --section ip-dhcp-host --live or even worse, they would specify "--xmldata", but leave out --xml, and virsh would attempt to interpret the text as a filename. On the other hand, if I named the string option "file" and the boolean option --xmldata, that would make things confusing even in the case that the command was *correct*: net-update netname add --file "<host mac='x:x...'/>" --xmldata \ --section ip-dhcp-host --live Of course, in any of those cases, if the user followed the "normal" command specification, it would look just fine, but it seems like there is a lot of room to cause confusion. So, for V2, I'm trying two different approaches: PATCHv2 1/2: Have a single required "xml" arg, and "auto-detect" whether it is xml text or a filename by looking at the first character - if the first character is "<", interpret it as an xml element. If not, interpret it as a filename. This leads to the simplest commandlines, since you *never* need to specify "--file" or "--xml" (unless you really want to): net-update netname add ip-dhcp-host "<host mac='xx..' .../>" net-update netname add ip-dhcp-host /tmp/myfile.xml (The ambiguous case of a filename starting with "<" can be avoided by specifying the filename as "./<....") There may be some circumstance I'm not aware of where a fully formed XML element doesn't necessarily start with a "<" though, or someone may be 100% against auto-disambiguating the string type, so I'm also sending a different version: PATCHv2 2/2: Have two separate optional string args, "file" and "xml". Manually check in the code that only one of them has been specified. Since both are optional, you must *always* provide the option name on the commandline ("--file" or "--xml"). In this case, the commandlines look like this: net-update netname add ip-dhcp-host --xml "<host mac='xx..' .../>" net-update netname add ip-dhcp-host --file /tmp/myfile.xml I'm (as usual) completely undecided about which of these versions to go with (including the first, which should still be considered, although I don't really like the possibility of creating confusing commandlines that it creates), so all votes/opinions/etc are welcome!

This new virsh command uses the new virNetworkUpdate() API to modify an existing network definition, and optionally have those modifications take effect immediately without restarting the network. An example usage: virsh net-update mynet add-last ip-dhcp-host \ "<host mac='00:11:22:33:44:55' ip='192.168.122.45'/>" \ --live --config If you like, you can instead put the xml into a file, and call like this: virsh net-update mynet add ip-dhcp-host /tmp/myxml.xml --live --config (since an xml element must always start with "<", but a filename rarely does, virsh just checks the first character of the supplied "--xml" argument, and decides whether to use the text directly or as a filename. In the rare event that someone really does have a filename starting with "<", they can specify it as "./<xxxx".) A --parent-index option is also available (to give the index within a list of parent objects, e.g. the index of the parent <ip> element when updating ip-dhcp-host elements), but is optional and at least for now will probably be used rarely. --live, --config, and --current options - if you specify --live, only the live state of the network will be updated. If you also specify --config, then the persistent configuration will also be updated; these two commands can be given separately, or both together. If you don't specify either (you can optionally specify "--current" for the same effect), then the "current" config will be updated (i.e. if the network is active, then only its live config is affected, but if the network is inactive, only the persistent config is affected). --- V2 changes: This has fixed the problems Eric found with V1, and also changed the way the args work. tools/virsh-network.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 40 +++++++++++++ 2 files changed, 194 insertions(+) diff --git a/tools/virsh-network.c b/tools/virsh-network.c index 2c32a78..cff39ac 100644 --- a/tools/virsh-network.c +++ b/tools/virsh-network.c @@ -737,6 +737,159 @@ cmdNetworkUndefine(vshControl *ctl, const vshCmd *cmd) } /* + * "net-update" command + */ +static const vshCmdInfo info_network_update[] = { + {"help", N_("update parts of an existing network's configuration")}, + {"desc", ""}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_network_update[] = { + {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")}, + {"command", VSH_OT_DATA, VSH_OFLAG_REQ, + N_("type of update (add-first, add-last (add), delete, or modify)")}, + {"section", VSH_OT_DATA, VSH_OFLAG_REQ, + N_("which section of network configuration to update")}, + {"xml", VSH_OT_DATA, VSH_OFLAG_REQ, + N_("name of file containing xml (or, if it starts with '<', the complete " + "xml element itself) to add/modify, or to be matched for search")}, + {"parent-index", VSH_OT_INT, 0, N_("which parent object to search through")}, + {"config", VSH_OT_BOOL, 0, N_("affect next boot")}, + {"live", VSH_OT_BOOL, 0, N_("affect running domain")}, + {"current", VSH_OT_BOOL, 0, N_("affect current domain")}, + {NULL, 0, 0, NULL} +}; + +VIR_ENUM_DECL(virNetworkUpdateCommand) +VIR_ENUM_IMPL(virNetworkUpdateCommand, VIR_NETWORK_UPDATE_COMMAND_LAST, + "none", "modify", "delete", "add-last", "add-first"); + +VIR_ENUM_DECL(virNetworkSection) +VIR_ENUM_IMPL(virNetworkSection, VIR_NETWORK_SECTION_LAST, + "none", "bridge", "domain", "ip", "ip-dhcp-host", + "ip-dhcp-range", "forward", "forward-interface", + "forward-pf", "portgroup", "dns-host", "dns-txt", + "dns-srv"); + +static bool +cmdNetworkUpdate(vshControl *ctl, const vshCmd *cmd) +{ + bool ret = false; + virNetworkPtr network; + const char *commandStr = NULL; + const char *sectionStr = NULL; + int command, section, parentIndex = -1; + const char *xml = NULL; + char *xmlFromFile = NULL; + bool current = vshCommandOptBool(cmd, "current"); + bool config = vshCommandOptBool(cmd, "config"); + bool live = vshCommandOptBool(cmd, "live"); + unsigned int flags = 0; + const char *affected; + + if (!(network = vshCommandOptNetwork(ctl, cmd, NULL))) + goto cleanup; + + if (vshCommandOptString(cmd, "command", &commandStr) < 0) { + vshError(ctl, "%s", _("missing or malformed command argument")); + goto cleanup; + } + + if (STREQ(commandStr, "add")) { + /* "add" is a synonym for "add-last" */ + command = VIR_NETWORK_UPDATE_COMMAND_ADD_LAST; + } else { + command = virNetworkUpdateCommandTypeFromString(commandStr); + if (command <= 0 || command >= VIR_NETWORK_UPDATE_COMMAND_LAST) { + vshError(ctl, _("unrecognized command name '%s'"), commandStr); + goto cleanup; + } + } + + if (vshCommandOptString(cmd, "section", §ionStr) < 0) { + vshError(ctl, "%s", _("missing or malformed section argument")); + goto cleanup; + } + section = virNetworkSectionTypeFromString(sectionStr); + if (section <= 0 || section >= VIR_NETWORK_SECTION_LAST) { + vshError(ctl, _("unrecognized section name '%s'"), sectionStr); + goto cleanup; + } + + if (vshCommandOptInt(cmd, "parent-index", &parentIndex) < 0) { + vshError(ctl, "%s", _("malformed parent-index argument")); + goto cleanup; + } + + /* The goal is to have a full xml element in the "xml" + * string. This is provided in the --xml option, either directly + * (detected by the first character being "<"), or indirectly by + * supplying a filename (first character isn't "<") that contains + * the desired xml. + */ + + if (vshCommandOptString(cmd, "xml", &xml) < 0) { + vshError(ctl, "%s", _("malformed or missing xml argument")); + goto cleanup; + } + + if (*xml != '<') { + /* contents of xmldata is actually the name of a file that + * contains the xml. + */ + if (virFileReadAll(xml, VSH_MAX_XML_FILE, &xmlFromFile) < 0) + return false; + /* NB: the original xml is just a const char * that points + * to a string owned by the Command object, and will be freed + * by vshCommandFree. so it's safe to lose its pointer here. + */ + xml = xmlFromFile; + } + + if (current) { + if (live || config) { + vshError(ctl, "%s", _("--current must be specified exclusively")); + return false; + } + flags |= VIR_NETWORK_UPDATE_AFFECT_CURRENT; + } else { + if (config) + flags |= VIR_NETWORK_UPDATE_AFFECT_CONFIG; + if (live) + flags |= VIR_NETWORK_UPDATE_AFFECT_LIVE; + } + + if (virNetworkUpdate(network, command, + section, parentIndex, xml, flags) < 0) { + vshError(ctl, _("Failed to update network %s"), + virNetworkGetName(network)); + goto cleanup; + } + + if (flags & VIR_NETWORK_UPDATE_AFFECT_CONFIG) { + if (flags & VIR_NETWORK_UPDATE_AFFECT_LIVE) + affected = _("persistent config and live state"); + else + affected = _("persistent config"); + } else if (flags & VIR_NETWORK_UPDATE_AFFECT_LIVE) { + affected = _("live state"); + } else if (virNetworkIsActive(network)) { + affected = _("live state"); + } else { + affected = _("persistent config"); + } + + vshPrint(ctl, _("Updated network %s %s"), + virNetworkGetName(network), affected); + ret = true; +cleanup: + virNetworkFree(network); + VIR_FREE(xmlFromFile); + return ret; +} + +/* * "net-uuid" command */ static const vshCmdInfo info_network_uuid[] = { @@ -854,6 +1007,7 @@ const vshCmdDef networkCmds[] = { {"net-start", cmdNetworkStart, opts_network_start, info_network_start, 0}, {"net-undefine", cmdNetworkUndefine, opts_network_undefine, info_network_undefine, 0}, + {"net-update", cmdNetworkUpdate, opts_network_update, info_network_update, 0}, {"net-uuid", cmdNetworkUuid, opts_network_uuid, info_network_uuid, 0}, {NULL, NULL, NULL, NULL, 0} }; diff --git a/tools/virsh.pod b/tools/virsh.pod index 4a79e12..5bd74c1 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -2006,6 +2006,46 @@ Undefine the configuration for an inactive network. Convert a network name to network UUID. +=item B<net-update> I<network> I<command> I<section> I<xml> + [I<--parent-index> I<index>] [[I<--live>] [I<--config>] | [I<--current>]] + +Update a the given section of an existing network definition, with the +changes optionally taking effect immediately, without needing to +destroy and re-start the network. + +I<command> is one of "add-first", "add-last", "add" (a synonym for +add-last), "delete", or "modify" + +I<section> is one of ""bridge", "domain", "ip", "ip-dhcp-host", +"ip-dhcp-range", "forward", "forward-interface", "forward-pf", +"portgroup", "dns-host", "dns-txt", or "dns-srv", each section being +named by a concatenation of the xml element hierarchy leading to the +element being changed. For example, "ip-dhcp-host" will change a +<host> element that is contained inside a <dhcp> element inside an +<ip> element of the network. + +I<xml> is either the text of a complete xml element of the type being +changed (e.g. "<host mac="00:11:22:33:44:55' ip='1.2.3.4'/>", or the +name of a file that contains a complete xml element. Disambiguation is +done by looking at the first character of the provided text - if the +first character is "<", it is xml text, if the first character is not +"<", it is the name of a file that contains the xml text to be used. + +The I<--parent-index> option is used to specify which of several +parent elements the requested element is in (0-based). For example, a +dhcp <host> element could be in any one of multiple <ip> elements in +the network; if a parent-index isn't provided, the "most appropriate" +<ip> element will be selected (usually the only one that already has a +<dhcp> element), but if I<--parent-index> is given, that particular +instance of <ip> will get the modification. + +If I<--live> is specified, affect a running guest. +If I<--config> is specified, affect the next boot of a persistent guest. +If I<--current> is specified, affect the current guest state. +Both I<--live> and I<--config> flags may be given, but I<--current> is +exclusive. Not specifying any flag is the same as specifying I<--current>. + + =back =head1 INTERFACE COMMANDS -- 1.7.11.4

On 09/20/2012 09:05 AM, Laine Stump wrote:
This new virsh command uses the new virNetworkUpdate() API to modify an existing network definition, and optionally have those modifications take effect immediately without restarting the network.
An example usage:
virsh net-update mynet add-last ip-dhcp-host \ "<host mac='00:11:22:33:44:55' ip='192.168.122.45'/>" \ --live --config
If you like, you can instead put the xml into a file, and call like this:
virsh net-update mynet add ip-dhcp-host /tmp/myxml.xml --live --config
(since an xml element must always start with "<", but a filename rarely does, virsh just checks the first character of the supplied "--xml" argument, and decides whether to use the text directly or as a filename. In the rare event that someone really does have a filename starting with "<", they can specify it as "./<xxxx".)
I like this version the best.
+ * "net-update" command + */ +static const vshCmdInfo info_network_update[] = { + {"help", N_("update parts of an existing network's configuration")}, + {"desc", ""}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_network_update[] = { + {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")}, + {"command", VSH_OT_DATA, VSH_OFLAG_REQ, + N_("type of update (add-first, add-last (add), delete, or modify)")}, + {"section", VSH_OT_DATA, VSH_OFLAG_REQ, + N_("which section of network configuration to update")}, + {"xml", VSH_OT_DATA, VSH_OFLAG_REQ, + N_("name of file containing xml (or, if it starts with '<', the complete " + "xml element itself) to add/modify, or to be matched for search")},
Two spaces after ')' looks fishy.
+ + /* The goal is to have a full xml element in the "xml" + * string. This is provided in the --xml option, either directly + * (detected by the first character being "<"), or indirectly by + * supplying a filename (first character isn't "<") that contains + * the desired xml. + */ + + if (vshCommandOptString(cmd, "xml", &xml) < 0) { + vshError(ctl, "%s", _("malformed or missing xml argument")); + goto cleanup; + } + + if (*xml != '<') { + /* contents of xmldata is actually the name of a file that + * contains the xml. + */ + if (virFileReadAll(xml, VSH_MAX_XML_FILE, &xmlFromFile) < 0) + return false; + /* NB: the original xml is just a const char * that points + * to a string owned by the Command object, and will be freed + * by vshCommandFree. so it's safe to lose its pointer here. + */ + xml = xmlFromFile; + } + + if (current) { + if (live || config) { + vshError(ctl, "%s", _("--current must be specified exclusively")); + return false; + } + flags |= VIR_NETWORK_UPDATE_AFFECT_CURRENT; + } else { + if (config) + flags |= VIR_NETWORK_UPDATE_AFFECT_CONFIG; + if (live) + flags |= VIR_NETWORK_UPDATE_AFFECT_LIVE; + } + + if (virNetworkUpdate(network, command, + section, parentIndex, xml, flags) < 0) { + vshError(ctl, _("Failed to update network %s"), + virNetworkGetName(network)); + goto cleanup; + } + + if (flags & VIR_NETWORK_UPDATE_AFFECT_CONFIG) {
If you want, this could be simplified to: 'if (config) {'
+ if (flags & VIR_NETWORK_UPDATE_AFFECT_LIVE) + affected = _("persistent config and live state"); + else + affected = _("persistent config"); + } else if (flags & VIR_NETWORK_UPDATE_AFFECT_LIVE) {
and likewise simplify these two bitwise ops to 'if (live)'. ACK (unless I get outvoted by other opinions :). -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 09/20/2012 12:55 PM, Eric Blake wrote:
On 09/20/2012 09:05 AM, Laine Stump wrote:
This new virsh command uses the new virNetworkUpdate() API to modify an existing network definition, and optionally have those modifications take effect immediately without restarting the network.
An example usage:
virsh net-update mynet add-last ip-dhcp-host \ "<host mac='00:11:22:33:44:55' ip='192.168.122.45'/>" \ --live --config
If you like, you can instead put the xml into a file, and call like this:
virsh net-update mynet add ip-dhcp-host /tmp/myxml.xml --live --config
(since an xml element must always start with "<", but a filename rarely does, virsh just checks the first character of the supplied "--xml" argument, and decides whether to use the text directly or as a filename. In the rare event that someone really does have a filename starting with "<", they can specify it as "./<xxxx".) I like this version the best.
+ * "net-update" command + */ +static const vshCmdInfo info_network_update[] = { + {"help", N_("update parts of an existing network's configuration")}, + {"desc", ""}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_network_update[] = { + {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")}, + {"command", VSH_OT_DATA, VSH_OFLAG_REQ, + N_("type of update (add-first, add-last (add), delete, or modify)")}, + {"section", VSH_OT_DATA, VSH_OFLAG_REQ, + N_("which section of network configuration to update")}, + {"xml", VSH_OT_DATA, VSH_OFLAG_REQ, + N_("name of file containing xml (or, if it starts with '<', the complete " + "xml element itself) to add/modify, or to be matched for search")}, Two spaces after ')' looks fishy.
+ + /* The goal is to have a full xml element in the "xml" + * string. This is provided in the --xml option, either directly + * (detected by the first character being "<"), or indirectly by + * supplying a filename (first character isn't "<") that contains + * the desired xml. + */ + + if (vshCommandOptString(cmd, "xml", &xml) < 0) { + vshError(ctl, "%s", _("malformed or missing xml argument")); + goto cleanup; + } + + if (*xml != '<') { + /* contents of xmldata is actually the name of a file that + * contains the xml. + */ + if (virFileReadAll(xml, VSH_MAX_XML_FILE, &xmlFromFile) < 0) + return false; + /* NB: the original xml is just a const char * that points + * to a string owned by the Command object, and will be freed + * by vshCommandFree. so it's safe to lose its pointer here. + */ + xml = xmlFromFile; + } + + if (current) { + if (live || config) { + vshError(ctl, "%s", _("--current must be specified exclusively")); + return false; + } + flags |= VIR_NETWORK_UPDATE_AFFECT_CURRENT; + } else { + if (config) + flags |= VIR_NETWORK_UPDATE_AFFECT_CONFIG; + if (live) + flags |= VIR_NETWORK_UPDATE_AFFECT_LIVE; + } + + if (virNetworkUpdate(network, command, + section, parentIndex, xml, flags) < 0) { + vshError(ctl, _("Failed to update network %s"), + virNetworkGetName(network)); + goto cleanup; + } + + if (flags & VIR_NETWORK_UPDATE_AFFECT_CONFIG) { If you want, this could be simplified to: 'if (config) {'
+ if (flags & VIR_NETWORK_UPDATE_AFFECT_LIVE) + affected = _("persistent config and live state"); + else + affected = _("persistent config"); + } else if (flags & VIR_NETWORK_UPDATE_AFFECT_LIVE) { and likewise simplify these two bitwise ops to 'if (live)'.
ACK (unless I get outvoted by other opinions :).
In the interest of getting maximum testing, I'm pushing this version (fixed up as you suggested, and also adding another fix to avoid leaking a virNetwork object when the requested file doesn't exist). If this method is successful for net-update, it can possibly be added to the other commands that take a file-containing-xml option (the only difference between the commands is in the name of that option, and that name is usually unspecified in commandlines anyway).

This new virsh command uses the new virNetworkUpdate() API to modify an existing network definition, and optionally have those modifications take effect immediately without restarting the network. An example usage: virsh net-update mynet add-last ip-dhcp-host \ --xml "<host mac='00:11:22:33:44:55' ip='192.168.122.45'/>" \ --live --config If you like, you can instead put the xml into a file, and call like this: virsh net-update mynet add ip-dhcp-host --file /tmp/myxml.xml --live --config (You must specify either --xml or --file, but not both.) A --parent-index option is also available (to give the index within a list of parent objects, e.g. the index of the parent <ip> element when updating ip-dhcp-host elements), but is optional and at least for now will probably be used rarely. --live, --config, and --current options - if you specify --live, only the live state of the network will be updated. If you also specify --config, then the persistent configuration will also be updated; these two commands can be given separately, or both together. If you don't specify either (you can optionally specify "--current" for the same effect), then the "current" config will be updated (i.e. if the network is active, then only its live config is affected, but if the network is inactive, only the persistent config is affected). --- Changes in V2: This has addressed the problems Eric found in V1, and changed the option handling. tools/virsh-network.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 39 ++++++++++++ 2 files changed, 200 insertions(+) diff --git a/tools/virsh-network.c b/tools/virsh-network.c index 2c32a78..dfd481e 100644 --- a/tools/virsh-network.c +++ b/tools/virsh-network.c @@ -737,6 +737,166 @@ cmdNetworkUndefine(vshControl *ctl, const vshCmd *cmd) } /* + * "net-update" command + */ +static const vshCmdInfo info_network_update[] = { + {"help", N_("update parts of an existing network's configuration")}, + {"desc", ""}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_network_update[] = { + {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")}, + {"command", VSH_OT_DATA, VSH_OFLAG_REQ, + N_("type of update (add-first, add-last (add), delete, or modify)")}, + {"section", VSH_OT_DATA, VSH_OFLAG_REQ, + N_("which section of network configuration to update")}, + {"xml", VSH_OT_STRING, 0, + N_("A complete xml element (including surrounding '<...>') to add/modify, " + "or to be matched for search")}, + {"file", VSH_OT_STRING, 0, + N_("name of file containing xml element to add/modify, or to be matched " + "for search")}, + {"parent-index", VSH_OT_INT, 0, N_("which parent object to search through")}, + {"config", VSH_OT_BOOL, 0, N_("affect next boot")}, + {"live", VSH_OT_BOOL, 0, N_("affect running domain")}, + {"current", VSH_OT_BOOL, 0, N_("affect current domain")}, + {NULL, 0, 0, NULL} +}; + +VIR_ENUM_DECL(virNetworkUpdateCommand) +VIR_ENUM_IMPL(virNetworkUpdateCommand, VIR_NETWORK_UPDATE_COMMAND_LAST, + "none", "modify", "delete", "add-last", "add-first"); + +VIR_ENUM_DECL(virNetworkSection) +VIR_ENUM_IMPL(virNetworkSection, VIR_NETWORK_SECTION_LAST, + "none", "bridge", "domain", "ip", "ip-dhcp-host", + "ip-dhcp-range", "forward", "forward-interface", + "forward-pf", "portgroup", "dns-host", "dns-txt", + "dns-srv"); + +static bool +cmdNetworkUpdate(vshControl *ctl, const vshCmd *cmd) +{ + bool ret = false; + virNetworkPtr network; + const char *commandStr = NULL; + const char *sectionStr = NULL; + int command, section, parentIndex = -1; + const char *xml = NULL; + const char *file = NULL; + char *xmlFromFile = NULL; + bool current = vshCommandOptBool(cmd, "current"); + bool config = vshCommandOptBool(cmd, "config"); + bool live = vshCommandOptBool(cmd, "live"); + unsigned int flags = 0; + const char *affected; + + if (!(network = vshCommandOptNetwork(ctl, cmd, NULL))) + goto cleanup; + + if (vshCommandOptString(cmd, "command", &commandStr) < 0) { + vshError(ctl, "%s", _("missing or malformed command argument")); + goto cleanup; + } + + if (STREQ(commandStr, "add")) { + /* "add" is a synonym for "add-last" */ + command = VIR_NETWORK_UPDATE_COMMAND_ADD_LAST; + } else { + command = virNetworkUpdateCommandTypeFromString(commandStr); + if (command <= 0 || command >= VIR_NETWORK_UPDATE_COMMAND_LAST) { + vshError(ctl, _("unrecognized command name '%s'"), commandStr); + goto cleanup; + } + } + + if (vshCommandOptString(cmd, "section", §ionStr) < 0) { + vshError(ctl, "%s", _("missing or malformed section argument")); + goto cleanup; + } + section = virNetworkSectionTypeFromString(sectionStr); + if (section <= 0 || section >= VIR_NETWORK_SECTION_LAST) { + vshError(ctl, _("unrecognized section name '%s'"), sectionStr); + goto cleanup; + } + + if (vshCommandOptInt(cmd, "parent-index", &parentIndex) < 0) { + vshError(ctl, "%s", _("malformed parent-index argument")); + goto cleanup; + } + + /* The goal is to have a full xml element in the "xml" + * string. This is provided either directly in the --xml option, + * or indirectly by supplying (with the --file option) a filename + * that contains the desired xml. + */ + + if (vshCommandOptString(cmd, "xml", &xml) < 0) { + vshError(ctl, "%s", _("malformed xml argument")); + goto cleanup; + } + + if (vshCommandOptString(cmd, "file", &file) < 0) { + vshError(ctl, "%s", _("malformed file argument")); + goto cleanup; + } + + if (!(file || xml) || (file && xml)) { + vshError(ctl, "%s", + _("you must specify either --file or --xml, but not both")); + goto cleanup; + } + + if (file) { + if (virFileReadAll(file, VSH_MAX_XML_FILE, &xmlFromFile) < 0) + return false; + xml = xmlFromFile; + } + + if (current) { + if (live || config) { + vshError(ctl, "%s", _("--current must be specified exclusively")); + return false; + } + flags |= VIR_NETWORK_UPDATE_AFFECT_CURRENT; + } else { + if (config) + flags |= VIR_NETWORK_UPDATE_AFFECT_CONFIG; + if (live) + flags |= VIR_NETWORK_UPDATE_AFFECT_LIVE; + } + + if (virNetworkUpdate(network, command, + section, parentIndex, xml, flags) < 0) { + vshError(ctl, _("Failed to update network %s"), + virNetworkGetName(network)); + goto cleanup; + } + + if (flags & VIR_NETWORK_UPDATE_AFFECT_CONFIG) { + if (flags & VIR_NETWORK_UPDATE_AFFECT_LIVE) + affected = _("persistent config and live state"); + else + affected = _("persistent config"); + } else if (flags & VIR_NETWORK_UPDATE_AFFECT_LIVE) { + affected = _("live state"); + } else if (virNetworkIsActive(network)) { + affected = _("live state"); + } else { + affected = _("persistent config"); + } + + vshPrint(ctl, _("Updated network %s %s"), + virNetworkGetName(network), affected); + ret = true; +cleanup: + virNetworkFree(network); + VIR_FREE(xmlFromFile); + return ret; +} + +/* * "net-uuid" command */ static const vshCmdInfo info_network_uuid[] = { @@ -854,6 +1014,7 @@ const vshCmdDef networkCmds[] = { {"net-start", cmdNetworkStart, opts_network_start, info_network_start, 0}, {"net-undefine", cmdNetworkUndefine, opts_network_undefine, info_network_undefine, 0}, + {"net-update", cmdNetworkUpdate, opts_network_update, info_network_update, 0}, {"net-uuid", cmdNetworkUuid, opts_network_uuid, info_network_uuid, 0}, {NULL, NULL, NULL, NULL, 0} }; diff --git a/tools/virsh.pod b/tools/virsh.pod index 4a79e12..02aa8b0 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -2006,6 +2006,45 @@ Undefine the configuration for an inactive network. Convert a network name to network UUID. +=item B<net-update> I<network> I<command> I<section> + [I<--xml> I<string>] | [I<--file> I<filename>] + [I<--parent-index> B<index>] [[I<--live>] [I<--config>] | [I<--current>]] + +Update a the given section of an existing network definition, with the +changes optionally taking effect immediately, without needing to +destroy and re-start the network. + +I<command> is one of "add-first", "add-last", "add" (a synonym for +add-last), "delete", or "modify" + +I<section> is one of ""bridge", "domain", "ip", "ip-dhcp-host", +"ip-dhcp-range", "forward", "forward-interface", "forward-pf", +"portgroup", "dns-host", "dns-txt", or "dns-srv", each section being +named by a concatenation of the xml element hierarchy leading to the +element being changed. For example, "ip-dhcp-host" will change a +<host> element that is contained inside a <dhcp> element inside an +<ip> element of the network. + +I<--xml> I<string> is the text of a complete xml element of the type being +changed (e.g. "<host mac="00:11:22:33:44:55' ip='1.2.3.4'/>". +I<--file> I<string> is the name of a file that contains a complete xml +element. Either --xml or --file must be specified, but not both. + +The I<--parent-index> option is used to specify which of several +parent elements the requested element is in (0-based). For example, a +dhcp <host> element could be in any one of multiple <ip> elements in +the network; if a parent-index isn't provided, the "most appropriate" +<ip> element will be selected (usually the only one that already has a +<dhcp> element), but if I<--parent-index> is given, that particular +instance of <ip> will get the modification. + +If I<--live> is specified, affect a running guest. +If I<--config> is specified, affect the next boot of a persistent guest. +If I<--current> is specified, affect the current guest state. +Both I<--live> and I<--config> flags may be given, but I<--current> is +exclusive. Not specifying any flag is the same as specifying I<--current>. + + =back =head1 INTERFACE COMMANDS -- 1.7.11.4
participants (2)
-
Eric Blake
-
Laine Stump