Two new commands are added to virsh that wrap usage of
virDomainUpdateDeviceFlags for changing link state of domain's network
interfaces. These wrappers extract network devices's xml configuration
and modify the link state for easy manipulation from user's perspective.
* domif-setlink - set link state of a domains virtual network interface
* domif-getlink - get link state
tools/virsh.c - Add functionality to virsh
tools/virsh.pod - Manpage documentation
---
tools/virsh.c | 286 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
tools/virsh.pod | 11 ++
2 files changed, 297 insertions(+), 0 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c
index c7240e5..dff048a 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -1173,6 +1173,290 @@ cmdDomIfstat (vshControl *ctl, const vshCmd *cmd)
return true;
}
+/* "domif-setlink" command
+ */
+static const vshCmdInfo info_domif_setlink[] = {
+ {"help", N_("set link state of a virtual interface")},
+ {"desc", N_("Set link state of a domain's virtual interface. This
command wraps usage of update-device command.")},
+ {NULL,NULL}
+};
+
+static const vshCmdOptDef opts_domif_setlink[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or
uuid")},
+ {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface device (MAC
Address)")},
+ {"state", VSH_OT_DATA, VSH_OFLAG_REQ, N_("new state of the
device")},
+ {"persistent", VSH_OT_BOOL, 0, N_("persist interface state")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomIfSetLink (vshControl *ctl, const vshCmd *cmd)
+{
+
+ virDomainPtr dom;
+ const char *interface;
+ const char *state;
+ const char *mac;
+ const char *desc;
+ bool persistent;
+ bool ret = false;
+ unsigned int flags = 0;
+ int i;
+
+ xmlDocPtr xml = NULL;
+ xmlXPathContextPtr ctxt = NULL;
+ xmlXPathObjectPtr obj = NULL;
+ xmlNodePtr cur = NULL;
+ xmlBufferPtr xml_buf = NULL;
+
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if (vshCommandOptString(cmd, "interface", &interface) <= 0)
+ goto cleanup;
+
+ if (vshCommandOptString(cmd, "state", &state) <= 0)
+ goto cleanup;
+
+ persistent = vshCommandOptBool(cmd, "persistent");
+
+ if (STRNEQ(state, "up") && STRNEQ(state, "down")) {
+ vshError(ctl, _("invalid link state '%s'"), state);
+ goto cleanup;
+ }
+
+ /* get persistent or live description of network device */
+ desc = virDomainGetXMLDesc(dom, persistent?VIR_DOMAIN_XML_INACTIVE:0);
+ if (desc == NULL) {
+ vshError(ctl, _("Failed to get domain description xml"));
+ goto cleanup;
+ }
+
+ if (persistent)
+ flags = VIR_DOMAIN_AFFECT_CONFIG;
+ else
+ flags = VIR_DOMAIN_AFFECT_LIVE;
+
+ if (virDomainIsActive(dom) == 0)
+ flags = VIR_DOMAIN_AFFECT_CONFIG;
+
+ /* extract current network device description */
+ xml = virXMLParseStringCtxt(desc, "domain configuration", &ctxt);
+ VIR_FREE(desc);
+ if (!xml) {
+ vshError(ctl, _("Failed to parse domain description xml"));
+ goto cleanup;
+ }
+
+ obj = xmlXPathEval(BAD_CAST "/domain/devices/interface", ctxt);
+ if (obj == NULL || obj->type != XPATH_NODESET ||
+ obj->nodesetval == NULL || obj->nodesetval->nodeNr == 0) {
+ vshError(ctl, _("Failed to extract interface information or no interfaces
found"));
+ goto cleanup;
+ }
+
+ /* find interface with matching mac addr */
+ for (i = 0; i < obj->nodesetval->nodeNr; i++) {
+ cur = obj->nodesetval->nodeTab[i]->children;
+
+ while (cur) {
+ if (cur->type == XML_ELEMENT_NODE &&
+ xmlStrEqual(cur->name, BAD_CAST "mac")) {
+ mac = virXMLPropString(cur, "address");
+
+ if (STRCASEEQ(mac, interface)) {
+ VIR_FREE(mac);
+ goto hit;
+ }
+ VIR_FREE(mac);
+ }
+ cur = cur->next;
+ }
+ }
+
+ vshError(ctl, _("interface with address '%s' not found"),
interface);
+ goto cleanup;
+
+hit:
+ /* find and modify/add link state node */
+ /* try to find <link> element */
+ cur = obj->nodesetval->nodeTab[i]->children;
+
+ while (cur) {
+ if (cur->type == XML_ELEMENT_NODE &&
+ xmlStrEqual(cur->name, BAD_CAST "link")) {
+ /* found, just modify the property */
+ xmlSetProp(cur, BAD_CAST "state", BAD_CAST state);
+
+ break;
+ }
+ cur = cur->next;
+ }
+
+ if (!cur) {
+ /* element <link> not found, add one */
+ cur = xmlNewChild(obj->nodesetval->nodeTab[i],
+ NULL,
+ BAD_CAST "link",
+ NULL);
+ if (!cur)
+ goto cleanup;
+
+ if (xmlNewProp(cur, BAD_CAST "state", BAD_CAST state) == NULL)
+ goto cleanup;
+ }
+
+ xml_buf = xmlBufferCreate();
+ if (!xml_buf) {
+ vshError(ctl, _("Failed to allocate memory"));
+ goto cleanup;
+ }
+
+ if (xmlNodeDump(xml_buf, xml, obj->nodesetval->nodeTab[i], 0, 0) < 0 ) {
+ vshError(ctl, _("Failed to create XML"));
+ goto cleanup;
+ }
+
+ if (virDomainUpdateDeviceFlags(dom, (char *)xmlBufferContent(xml_buf), flags) < 0)
{
+ vshError(ctl, _("Failed to update interface link state"));
+ goto cleanup;
+ } else {
+ vshPrint(ctl, "%s", _("Device updated successfully\n"));
+ ret = true;
+ }
+
+cleanup:
+ xmlXPathFreeObject(obj);
+ xmlXPathFreeContext(ctxt);
+ xmlFreeDoc(xml);
+ xmlBufferFree(xml_buf);
+
+ if (dom)
+ virDomainFree(dom);
+
+ return ret;
+}
+
+/* "domif-getlink" command
+ */
+static const vshCmdInfo info_domif_getlink[] = {
+ {"help", N_("get link state of a virtual interface")},
+ {"desc", N_("Get link state of a domain's virtual
interface.")},
+ {NULL,NULL}
+};
+
+static const vshCmdOptDef opts_domif_getlink[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or
uuid")},
+ {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface device (MAC
Address)")},
+ {"persistent", VSH_OT_BOOL, 0, N_("Get persistent interface
state")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomIfGetLink (vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom;
+ const char *interface = NULL;
+ int flags = 0;
+ char *state = NULL;
+ char *mac = NULL;
+ bool ret = false;
+ int i;
+
+ char *desc;
+ xmlDocPtr xml = NULL;
+ xmlXPathContextPtr ctxt = NULL;
+ xmlNodePtr cur = NULL;
+ xmlXPathObjectPtr obj = NULL;
+
+ if (!vshConnectionUsability (ctl, ctl->conn))
+ return false;
+
+ if (!(dom = vshCommandOptDomain (ctl, cmd, NULL)))
+ return false;
+
+ if (vshCommandOptString (cmd, "interface", &interface) <= 0) {
+ virDomainFree(dom);
+ return false;
+ }
+
+ if (vshCommandOptBool(cmd, "persistent"))
+ flags = VIR_DOMAIN_XML_INACTIVE;
+
+ desc = virDomainGetXMLDesc(dom, flags);
+ if (desc == NULL) {
+ vshError(ctl, _("Failed to get domain description xml"));
+ goto cleanup;
+ }
+
+ xml = virXMLParseStringCtxt(desc, "domain configuration", &ctxt);
+ VIR_FREE(desc);
+ if (!xml) {
+ vshError(ctl, _("Failed to parse domain description xml"));
+ goto cleanup;
+ }
+
+ obj = xmlXPathEval(BAD_CAST "/domain/devices/interface", ctxt);
+ if (obj == NULL || obj->type != XPATH_NODESET ||
+ obj->nodesetval == NULL || obj->nodesetval->nodeNr == 0) {
+ vshError(ctl, _("Failed to extract interface information or no interfaces
found"));
+ goto cleanup;
+ }
+
+ /* find interface with matching mac addr */
+ for (i = 0; i < obj->nodesetval->nodeNr; i++) {
+ cur = obj->nodesetval->nodeTab[i]->children;
+
+ while (cur) {
+ if (cur->type == XML_ELEMENT_NODE &&
+ xmlStrEqual(cur->name, BAD_CAST "mac")) {
+
+ mac = virXMLPropString(cur, "address");
+
+ if (STRCASEEQ(mac, interface)){
+ VIR_FREE(mac);
+ goto hit;
+ }
+ }
+ cur = cur->next;
+ }
+ }
+
+ vshError(ctl, _("Interface with address '%s' not found."),
interface);
+ goto cleanup;
+
+hit:
+ cur = obj->nodesetval->nodeTab[i]->children;
+ while (cur) {
+ if (cur->type == XML_ELEMENT_NODE &&
+ xmlStrEqual(cur->name, BAD_CAST "link")) {
+
+ state = virXMLPropString(cur, "state");
+ vshPrint(ctl, "%s %s", interface, state);
+ VIR_FREE(state);
+
+ goto cleanup;
+ }
+ cur = cur->next;
+ }
+
+ /* attribute not found */
+ vshPrint(ctl, "%s default", interface);
+
+cleanup:
+ xmlXPathFreeObject(obj);
+ xmlXPathFreeContext(ctxt);
+ xmlFreeDoc(xml);
+ if (dom)
+ virDomainFree(dom);
+
+ return ret;
+}
+
/*
* "dommemstats" command
*/
@@ -12953,6 +13237,7 @@ static const vshCmdDef domManagementCmds[] = {
{"detach-interface", cmdDetachInterface, opts_detach_interface,
info_detach_interface, 0},
{"domid", cmdDomid, opts_domid, info_domid, 0},
+ {"domif-setlink", cmdDomIfSetLink, opts_domif_setlink, info_domif_setlink,
0},
{"domjobabort", cmdDomjobabort, opts_domjobabort, info_domjobabort, 0},
{"domjobinfo", cmdDomjobinfo, opts_domjobinfo, info_domjobinfo, 0},
{"domname", cmdDomname, opts_domname, info_domname, 0},
@@ -13012,6 +13297,7 @@ static const vshCmdDef domMonitoringCmds[] = {
{"domblkinfo", cmdDomblkinfo, opts_domblkinfo, info_domblkinfo, 0},
{"domblkstat", cmdDomblkstat, opts_domblkstat, info_domblkstat, 0},
{"domcontrol", cmdDomControl, opts_domcontrol, info_domcontrol, 0},
+ {"domif-getlink", cmdDomIfGetLink, opts_domif_getlink, info_domif_getlink,
0},
{"domifstat", cmdDomIfstat, opts_domifstat, info_domifstat, 0},
{"dominfo", cmdDominfo, opts_dominfo, info_dominfo, 0},
{"dommemstat", cmdDomMemStat, opts_dommemstat, info_dommemstat, 0},
diff --git a/tools/virsh.pod b/tools/virsh.pod
index 30c0721..7e2ced2 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -509,6 +509,17 @@ Get device block stats for a running domain.
Get network interface stats for a running domain.
+=item B<domif-setlink> I<domain> I<interface-MAC> I<state>
I<--persistent>
+
+Modify link state of the domain's virtual interface. Possible values for
+state are "up" and "down. If --persistent is specified, only the
persistent
+configuration of the domain is modified.
+
+=item B<domif-setlink> I<domain> I<interface-MAC>
I<--persistent>
+
+Query link state of the domain's virtual interface. If --persistent
+is specified, query the persistent configuration.
+
=item B<dommemstat> I<domain>
Get memory stats for a running domain.
--
1.7.3.4