docs: Update documentation for spapr-vio addresses
[libvirt/ericb.git] / tools / virsh-interface.c
blob7e84ee3c52e0fb7ed997615641978a097ef98dd5
1 /*
2 * virsh-interface.c: Commands to manage host interface
4 * Copyright (C) 2005, 2007-2016 Red Hat, Inc.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see
18 * <http://www.gnu.org/licenses/>.
21 #define VIRSH_COMMON_OPT_INTERFACE(cflags) \
22 {.name = "interface", \
23 .type = VSH_OT_DATA, \
24 .flags = VSH_OFLAG_REQ, \
25 .help = N_("interface name or MAC address"), \
26 .completer = virshInterfaceNameCompleter, \
27 .completer_flags = cflags, \
30 #include <config.h>
31 #include "virsh-interface.h"
33 #include <libxml/parser.h>
34 #include <libxml/tree.h>
35 #include <libxml/xpath.h>
36 #include <libxml/xmlsave.h>
38 #include "internal.h"
39 #include "virbuffer.h"
40 #include "viralloc.h"
41 #include "virfile.h"
42 #include "virmacaddr.h"
43 #include "virutil.h"
44 #include "virxml.h"
45 #include "virstring.h"
46 #include "vsh-table.h"
48 virInterfacePtr
49 virshCommandOptInterfaceBy(vshControl *ctl, const vshCmd *cmd,
50 const char *optname,
51 const char **name, unsigned int flags)
53 virInterfacePtr iface = NULL;
54 const char *n = NULL;
55 bool is_mac = false;
56 virMacAddr dummy;
57 virCheckFlags(VIRSH_BYNAME | VIRSH_BYMAC, NULL);
58 virshControlPtr priv = ctl->privData;
60 if (!optname)
61 optname = "interface";
63 if (vshCommandOptStringReq(ctl, cmd, optname, &n) < 0)
64 return NULL;
66 vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n",
67 cmd->def->name, optname, n);
69 if (name)
70 *name = n;
72 if (virMacAddrParse(n, &dummy) == 0)
73 is_mac = true;
75 /* try it by NAME */
76 if (!is_mac && (flags & VIRSH_BYNAME)) {
77 vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as interface NAME\n",
78 cmd->def->name, optname);
79 iface = virInterfaceLookupByName(priv->conn, n);
81 /* try it by MAC */
82 } else if (is_mac && (flags & VIRSH_BYMAC)) {
83 vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as interface MAC\n",
84 cmd->def->name, optname);
85 iface = virInterfaceLookupByMACString(priv->conn, n);
88 if (!iface)
89 vshError(ctl, _("failed to get interface '%s'"), n);
91 return iface;
95 * "iface-edit" command
97 static const vshCmdInfo info_interface_edit[] = {
98 {.name = "help",
99 .data = N_("edit XML configuration for a physical host interface")
101 {.name = "desc",
102 .data = N_("Edit the XML configuration for a physical host interface.")
104 {.name = NULL}
107 static const vshCmdOptDef opts_interface_edit[] = {
108 VIRSH_COMMON_OPT_INTERFACE(0),
109 {.name = NULL}
112 static bool
113 cmdInterfaceEdit(vshControl *ctl, const vshCmd *cmd)
115 bool ret = false;
116 virInterfacePtr iface = NULL;
117 virInterfacePtr iface_edited = NULL;
118 unsigned int flags = VIR_INTERFACE_XML_INACTIVE;
119 virshControlPtr priv = ctl->privData;
121 iface = virshCommandOptInterface(ctl, cmd, NULL);
122 if (iface == NULL)
123 goto cleanup;
125 #define EDIT_GET_XML virInterfaceGetXMLDesc(iface, flags)
126 #define EDIT_NOT_CHANGED \
127 do { \
128 vshPrintExtra(ctl, _("Interface %s XML configuration not changed.\n"), \
129 virInterfaceGetName(iface)); \
130 ret = true; \
131 goto edit_cleanup; \
132 } while (0)
133 #define EDIT_DEFINE \
134 (iface_edited = virInterfaceDefineXML(priv->conn, doc_edited, 0))
135 #include "virsh-edit.c"
137 vshPrintExtra(ctl, _("Interface %s XML configuration edited.\n"),
138 virInterfaceGetName(iface_edited));
140 ret = true;
142 cleanup:
143 if (iface)
144 virInterfaceFree(iface);
145 if (iface_edited)
146 virInterfaceFree(iface_edited);
148 return ret;
151 static int
152 virshInterfaceSorter(const void *a, const void *b)
154 virInterfacePtr *ia = (virInterfacePtr *) a;
155 virInterfacePtr *ib = (virInterfacePtr *) b;
157 if (*ia && !*ib)
158 return -1;
160 if (!*ia)
161 return *ib != NULL;
163 return vshStrcasecmp(virInterfaceGetName(*ia),
164 virInterfaceGetName(*ib));
167 struct virshInterfaceList {
168 virInterfacePtr *ifaces;
169 size_t nifaces;
171 typedef struct virshInterfaceList *virshInterfaceListPtr;
173 static void
174 virshInterfaceListFree(virshInterfaceListPtr list)
176 size_t i;
178 if (list && list->ifaces) {
179 for (i = 0; i < list->nifaces; i++) {
180 if (list->ifaces[i])
181 virInterfaceFree(list->ifaces[i]);
183 VIR_FREE(list->ifaces);
185 VIR_FREE(list);
188 static virshInterfaceListPtr
189 virshInterfaceListCollect(vshControl *ctl,
190 unsigned int flags)
192 virshInterfaceListPtr list = vshMalloc(ctl, sizeof(*list));
193 size_t i;
194 int ret;
195 char **activeNames = NULL;
196 char **inactiveNames = NULL;
197 virInterfacePtr iface;
198 bool success = false;
199 size_t deleted = 0;
200 int nActiveIfaces = 0;
201 int nInactiveIfaces = 0;
202 int nAllIfaces = 0;
203 virshControlPtr priv = ctl->privData;
205 /* try the list with flags support (0.10.2 and later) */
206 if ((ret = virConnectListAllInterfaces(priv->conn,
207 &list->ifaces,
208 flags)) >= 0) {
209 list->nifaces = ret;
210 goto finished;
213 /* check if the command is actually supported */
214 if (last_error && last_error->code == VIR_ERR_NO_SUPPORT)
215 goto fallback;
217 /* there was an error during the first or second call */
218 vshError(ctl, "%s", _("Failed to list interfaces"));
219 goto cleanup;
222 fallback:
223 /* fall back to old method (0.10.1 and older) */
224 vshResetLibvirtError();
226 if (flags & VIR_CONNECT_LIST_INTERFACES_ACTIVE) {
227 nActiveIfaces = virConnectNumOfInterfaces(priv->conn);
228 if (nActiveIfaces < 0) {
229 vshError(ctl, "%s", _("Failed to list active interfaces"));
230 goto cleanup;
232 if (nActiveIfaces) {
233 activeNames = vshMalloc(ctl, sizeof(char *) * nActiveIfaces);
235 if ((nActiveIfaces = virConnectListInterfaces(priv->conn, activeNames,
236 nActiveIfaces)) < 0) {
237 vshError(ctl, "%s", _("Failed to list active interfaces"));
238 goto cleanup;
243 if (flags & VIR_CONNECT_LIST_INTERFACES_INACTIVE) {
244 nInactiveIfaces = virConnectNumOfDefinedInterfaces(priv->conn);
245 if (nInactiveIfaces < 0) {
246 vshError(ctl, "%s", _("Failed to list inactive interfaces"));
247 goto cleanup;
249 if (nInactiveIfaces) {
250 inactiveNames = vshMalloc(ctl, sizeof(char *) * nInactiveIfaces);
252 if ((nInactiveIfaces =
253 virConnectListDefinedInterfaces(priv->conn, inactiveNames,
254 nInactiveIfaces)) < 0) {
255 vshError(ctl, "%s", _("Failed to list inactive interfaces"));
256 goto cleanup;
261 nAllIfaces = nActiveIfaces + nInactiveIfaces;
262 if (nAllIfaces == 0) {
263 VIR_FREE(activeNames);
264 VIR_FREE(inactiveNames);
265 return list;
268 list->ifaces = vshMalloc(ctl, sizeof(virInterfacePtr) * (nAllIfaces));
269 list->nifaces = 0;
271 /* get active interfaces */
272 for (i = 0; i < nActiveIfaces; i++) {
273 if (!(iface = virInterfaceLookupByName(priv->conn, activeNames[i]))) {
274 vshResetLibvirtError();
275 continue;
277 list->ifaces[list->nifaces++] = iface;
280 /* get inactive interfaces */
281 for (i = 0; i < nInactiveIfaces; i++) {
282 if (!(iface = virInterfaceLookupByName(priv->conn, inactiveNames[i]))) {
283 vshResetLibvirtError();
284 continue;
286 list->ifaces[list->nifaces++] = iface;
289 /* truncate interfaces that weren't found */
290 deleted = nAllIfaces - list->nifaces;
292 finished:
293 /* sort the list */
294 if (list->ifaces && list->nifaces)
295 qsort(list->ifaces, list->nifaces,
296 sizeof(*list->ifaces), virshInterfaceSorter);
298 /* truncate the list if filter simulation deleted entries */
299 if (deleted)
300 VIR_SHRINK_N(list->ifaces, list->nifaces, deleted);
302 success = true;
304 cleanup:
305 for (i = 0; nActiveIfaces != -1 && i < nActiveIfaces; i++)
306 VIR_FREE(activeNames[i]);
308 for (i = 0; nInactiveIfaces != -1 && i < nInactiveIfaces; i++)
309 VIR_FREE(inactiveNames[i]);
311 VIR_FREE(activeNames);
312 VIR_FREE(inactiveNames);
314 if (!success) {
315 virshInterfaceListFree(list);
316 list = NULL;
319 return list;
323 * "iface-list" command
325 static const vshCmdInfo info_interface_list[] = {
326 {.name = "help",
327 .data = N_("list physical host interfaces")
329 {.name = "desc",
330 .data = N_("Returns list of physical host interfaces.")
332 {.name = NULL}
335 static const vshCmdOptDef opts_interface_list[] = {
336 {.name = "inactive",
337 .type = VSH_OT_BOOL,
338 .help = N_("list inactive interfaces")
340 {.name = "all",
341 .type = VSH_OT_BOOL,
342 .help = N_("list inactive & active interfaces")
344 {.name = NULL}
347 static bool
348 cmdInterfaceList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
350 bool inactive = vshCommandOptBool(cmd, "inactive");
351 bool all = vshCommandOptBool(cmd, "all");
352 unsigned int flags = VIR_CONNECT_LIST_INTERFACES_ACTIVE;
353 virshInterfaceListPtr list = NULL;
354 size_t i;
355 bool ret = false;
356 vshTablePtr table = NULL;
358 if (inactive)
359 flags = VIR_CONNECT_LIST_INTERFACES_INACTIVE;
360 if (all)
361 flags = VIR_CONNECT_LIST_INTERFACES_INACTIVE |
362 VIR_CONNECT_LIST_INTERFACES_ACTIVE;
364 if (!(list = virshInterfaceListCollect(ctl, flags)))
365 return false;
367 table = vshTableNew(_("Name"), _("State"), _("MAC Address"), NULL);
368 if (!table)
369 goto cleanup;
371 for (i = 0; i < list->nifaces; i++) {
372 virInterfacePtr iface = list->ifaces[i];
374 if (vshTableRowAppend(table,
375 virInterfaceGetName(iface),
376 virInterfaceIsActive(iface) ? _("active")
377 : _("inactive"),
378 virInterfaceGetMACString(iface),
379 NULL) < 0)
380 goto cleanup;
383 vshTablePrintToStdout(table, ctl);
385 ret = true;
386 cleanup:
387 vshTableFree(table);
388 virshInterfaceListFree(list);
389 return ret;
393 * "iface-name" command
395 static const vshCmdInfo info_interface_name[] = {
396 {.name = "help",
397 .data = N_("convert an interface MAC address to interface name")
399 {.name = "desc",
400 .data = ""
402 {.name = NULL}
405 static const vshCmdOptDef opts_interface_name[] = {
406 {.name = "interface",
407 .type = VSH_OT_DATA,
408 .flags = VSH_OFLAG_REQ,
409 .help = N_("interface mac")
411 {.name = NULL}
414 static bool
415 cmdInterfaceName(vshControl *ctl, const vshCmd *cmd)
417 virInterfacePtr iface;
419 if (!(iface = virshCommandOptInterfaceBy(ctl, cmd, NULL, NULL,
420 VIRSH_BYMAC)))
421 return false;
423 vshPrint(ctl, "%s\n", virInterfaceGetName(iface));
424 virInterfaceFree(iface);
425 return true;
429 * "iface-mac" command
431 static const vshCmdInfo info_interface_mac[] = {
432 {.name = "help",
433 .data = N_("convert an interface name to interface MAC address")
435 {.name = "desc",
436 .data = ""
438 {.name = NULL}
441 static const vshCmdOptDef opts_interface_mac[] = {
442 {.name = "interface",
443 .type = VSH_OT_DATA,
444 .flags = VSH_OFLAG_REQ,
445 .help = N_("interface name")
447 {.name = NULL}
450 static bool
451 cmdInterfaceMAC(vshControl *ctl, const vshCmd *cmd)
453 virInterfacePtr iface;
455 if (!(iface = virshCommandOptInterfaceBy(ctl, cmd, NULL, NULL,
456 VIRSH_BYNAME)))
457 return false;
459 vshPrint(ctl, "%s\n", virInterfaceGetMACString(iface));
460 virInterfaceFree(iface);
461 return true;
465 * "iface-dumpxml" command
467 static const vshCmdInfo info_interface_dumpxml[] = {
468 {.name = "help",
469 .data = N_("interface information in XML")
471 {.name = "desc",
472 .data = N_("Output the physical host interface information as an XML dump to stdout.")
474 {.name = NULL}
477 static const vshCmdOptDef opts_interface_dumpxml[] = {
478 VIRSH_COMMON_OPT_INTERFACE(0),
479 {.name = "inactive",
480 .type = VSH_OT_BOOL,
481 .help = N_("show inactive defined XML")
483 {.name = NULL}
486 static bool
487 cmdInterfaceDumpXML(vshControl *ctl, const vshCmd *cmd)
489 virInterfacePtr iface;
490 bool ret = true;
491 char *dump;
492 unsigned int flags = 0;
493 bool inactive = vshCommandOptBool(cmd, "inactive");
495 if (inactive)
496 flags |= VIR_INTERFACE_XML_INACTIVE;
498 if (!(iface = virshCommandOptInterface(ctl, cmd, NULL)))
499 return false;
501 dump = virInterfaceGetXMLDesc(iface, flags);
502 if (dump != NULL) {
503 vshPrint(ctl, "%s", dump);
504 VIR_FREE(dump);
505 } else {
506 ret = false;
509 virInterfaceFree(iface);
510 return ret;
514 * "iface-define" command
516 static const vshCmdInfo info_interface_define[] = {
517 {.name = "help",
518 .data = N_("define an inactive persistent physical host interface or "
519 "modify an existing persistent one from an XML file")
521 {.name = "desc",
522 .data = N_("Define or modify a persistent physical host interface.")
524 {.name = NULL}
527 static const vshCmdOptDef opts_interface_define[] = {
528 VIRSH_COMMON_OPT_FILE(N_("file containing an XML interface description")),
529 {.name = NULL}
532 static bool
533 cmdInterfaceDefine(vshControl *ctl, const vshCmd *cmd)
535 virInterfacePtr iface;
536 const char *from = NULL;
537 bool ret = true;
538 char *buffer;
539 virshControlPtr priv = ctl->privData;
541 if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
542 return false;
544 if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0)
545 return false;
547 iface = virInterfaceDefineXML(priv->conn, buffer, 0);
548 VIR_FREE(buffer);
550 if (iface != NULL) {
551 vshPrintExtra(ctl, _("Interface %s defined from %s\n"),
552 virInterfaceGetName(iface), from);
553 virInterfaceFree(iface);
554 } else {
555 vshError(ctl, _("Failed to define interface from %s"), from);
556 ret = false;
558 return ret;
562 * "iface-undefine" command
564 static const vshCmdInfo info_interface_undefine[] = {
565 {.name = "help",
566 .data = N_("undefine a physical host interface (remove it from configuration)")
568 {.name = "desc",
569 .data = N_("undefine an interface.")
571 {.name = NULL}
574 static const vshCmdOptDef opts_interface_undefine[] = {
575 VIRSH_COMMON_OPT_INTERFACE(0),
576 {.name = NULL}
579 static bool
580 cmdInterfaceUndefine(vshControl *ctl, const vshCmd *cmd)
582 virInterfacePtr iface;
583 bool ret = true;
584 const char *name;
586 if (!(iface = virshCommandOptInterface(ctl, cmd, &name)))
587 return false;
589 if (virInterfaceUndefine(iface) == 0) {
590 vshPrintExtra(ctl, _("Interface %s undefined\n"), name);
591 } else {
592 vshError(ctl, _("Failed to undefine interface %s"), name);
593 ret = false;
596 virInterfaceFree(iface);
597 return ret;
601 * "iface-start" command
603 static const vshCmdInfo info_interface_start[] = {
604 {.name = "help",
605 .data = N_("start a physical host interface (enable it / \"if-up\")")
607 {.name = "desc",
608 .data = N_("start a physical host interface.")
610 {.name = NULL}
613 static const vshCmdOptDef opts_interface_start[] = {
614 VIRSH_COMMON_OPT_INTERFACE(VIR_CONNECT_LIST_INTERFACES_INACTIVE),
615 {.name = NULL}
618 static bool
619 cmdInterfaceStart(vshControl *ctl, const vshCmd *cmd)
621 virInterfacePtr iface;
622 bool ret = true;
623 const char *name;
625 if (!(iface = virshCommandOptInterface(ctl, cmd, &name)))
626 return false;
628 if (virInterfaceCreate(iface, 0) == 0) {
629 vshPrintExtra(ctl, _("Interface %s started\n"), name);
630 } else {
631 vshError(ctl, _("Failed to start interface %s"), name);
632 ret = false;
635 virInterfaceFree(iface);
636 return ret;
640 * "iface-destroy" command
642 static const vshCmdInfo info_interface_destroy[] = {
643 {.name = "help",
644 .data = N_("destroy a physical host interface (disable it / \"if-down\")")
646 {.name = "desc",
647 .data = N_("forcefully stop a physical host interface.")
649 {.name = NULL}
652 static const vshCmdOptDef opts_interface_destroy[] = {
653 VIRSH_COMMON_OPT_INTERFACE(VIR_CONNECT_LIST_INTERFACES_ACTIVE),
654 {.name = NULL}
657 static bool
658 cmdInterfaceDestroy(vshControl *ctl, const vshCmd *cmd)
660 virInterfacePtr iface;
661 bool ret = true;
662 const char *name;
664 if (!(iface = virshCommandOptInterface(ctl, cmd, &name)))
665 return false;
667 if (virInterfaceDestroy(iface, 0) == 0) {
668 vshPrintExtra(ctl, _("Interface %s destroyed\n"), name);
669 } else {
670 vshError(ctl, _("Failed to destroy interface %s"), name);
671 ret = false;
674 virInterfaceFree(iface);
675 return ret;
679 * "iface-begin" command
681 static const vshCmdInfo info_interface_begin[] = {
682 {.name = "help",
683 .data = N_("create a snapshot of current interfaces settings, "
684 "which can be later committed (iface-commit) or "
685 "restored (iface-rollback)")
687 {.name = "desc",
688 .data = N_("Create a restore point for interfaces settings")
690 {.name = NULL}
693 static const vshCmdOptDef opts_interface_begin[] = {
694 {.name = NULL}
697 static bool
698 cmdInterfaceBegin(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
700 virshControlPtr priv = ctl->privData;
702 if (virInterfaceChangeBegin(priv->conn, 0) < 0) {
703 vshError(ctl, "%s", _("Failed to begin network config change transaction"));
704 return false;
707 vshPrintExtra(ctl, "%s", _("Network config change transaction started\n"));
708 return true;
712 * "iface-commit" command
714 static const vshCmdInfo info_interface_commit[] = {
715 {.name = "help",
716 .data = N_("commit changes made since iface-begin and free restore point")
718 {.name = "desc",
719 .data = N_("commit changes and free restore point")
721 {.name = NULL}
724 static const vshCmdOptDef opts_interface_commit[] = {
725 {.name = NULL}
728 static bool
729 cmdInterfaceCommit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
731 virshControlPtr priv = ctl->privData;
733 if (virInterfaceChangeCommit(priv->conn, 0) < 0) {
734 vshError(ctl, "%s", _("Failed to commit network config change transaction"));
735 return false;
738 vshPrintExtra(ctl, "%s", _("Network config change transaction committed\n"));
739 return true;
743 * "iface-rollback" command
745 static const vshCmdInfo info_interface_rollback[] = {
746 {.name = "help",
747 .data = N_("rollback to previous saved configuration created via iface-begin")
749 {.name = "desc",
750 .data = N_("rollback to previous restore point")
752 {.name = NULL}
755 static const vshCmdOptDef opts_interface_rollback[] = {
756 {.name = NULL}
759 static bool
760 cmdInterfaceRollback(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
762 virshControlPtr priv = ctl->privData;
764 if (virInterfaceChangeRollback(priv->conn, 0) < 0) {
765 vshError(ctl, "%s", _("Failed to rollback network config change transaction"));
766 return false;
769 vshPrintExtra(ctl, "%s", _("Network config change transaction rolled back\n"));
770 return true;
774 * "iface-bridge" command
776 static const vshCmdInfo info_interface_bridge[] = {
777 {.name = "help",
778 .data = N_("create a bridge device and attach an existing network device to it")
780 {.name = "desc",
781 .data = N_("bridge an existing network device")
783 {.name = NULL}
786 static const vshCmdOptDef opts_interface_bridge[] = {
787 {.name = "interface",
788 .type = VSH_OT_DATA,
789 .flags = VSH_OFLAG_REQ,
790 .help = N_("existing interface name")
792 {.name = "bridge",
793 .type = VSH_OT_DATA,
794 .flags = VSH_OFLAG_REQ,
795 .help = N_("new bridge device name")
797 {.name = "no-stp",
798 .type = VSH_OT_BOOL,
799 .help = N_("do not enable STP for this bridge")
801 {.name = "delay",
802 .type = VSH_OT_INT,
803 .help = N_("number of seconds to squelch traffic on newly connected ports")
805 {.name = "no-start",
806 .type = VSH_OT_BOOL,
807 .help = N_("don't start the bridge immediately")
809 {.name = NULL}
812 static bool
813 cmdInterfaceBridge(vshControl *ctl, const vshCmd *cmd)
815 bool ret = false;
816 virInterfacePtr if_handle = NULL, br_handle = NULL;
817 const char *if_name, *br_name;
818 char *if_type = NULL, *if2_name = NULL, *delay_str = NULL;
819 bool stp = false, nostart = false;
820 unsigned int delay = 0;
821 char *if_xml = NULL;
822 xmlChar *br_xml = NULL;
823 int br_xml_size;
824 xmlDocPtr xml_doc = NULL;
825 xmlXPathContextPtr ctxt = NULL;
826 xmlNodePtr top_node, br_node, if_node, cur;
827 virshControlPtr priv = ctl->privData;
829 /* Get a handle to the original device */
830 if (!(if_handle = virshCommandOptInterfaceBy(ctl, cmd, "interface",
831 &if_name, VIRSH_BYNAME))) {
832 goto cleanup;
835 /* Name for new bridge device */
836 if (vshCommandOptStringReq(ctl, cmd, "bridge", &br_name) < 0)
837 goto cleanup;
839 /* make sure "new" device doesn't already exist */
840 if ((br_handle = virInterfaceLookupByName(priv->conn, br_name))) {
841 vshError(ctl, _("Network device %s already exists"), br_name);
842 goto cleanup;
845 /* use "no-stp" because we want "stp" to default true */
846 stp = !vshCommandOptBool(cmd, "no-stp");
848 if (vshCommandOptUInt(ctl, cmd, "delay", &delay) < 0)
849 goto cleanup;
851 nostart = vshCommandOptBool(cmd, "no-start");
853 /* Get the original interface into an xmlDoc */
854 if (!(if_xml = virInterfaceGetXMLDesc(if_handle, VIR_INTERFACE_XML_INACTIVE)))
855 goto cleanup;
856 if (!(xml_doc = virXMLParseStringCtxt(if_xml,
857 _("(interface definition)"), &ctxt))) {
858 vshError(ctl, _("Failed to parse configuration of %s"), if_name);
859 goto cleanup;
861 top_node = ctxt->node;
863 /* Verify that the original device isn't already a bridge. */
864 if (!(if_type = virXMLPropString(top_node, "type"))) {
865 vshError(ctl, _("Existing device %s has no type"), if_name);
866 goto cleanup;
869 if (STREQ(if_type, "bridge")) {
870 vshError(ctl, _("Existing device %s is already a bridge"), if_name);
871 goto cleanup;
874 /* verify the name in the XML matches the device name */
875 if (!(if2_name = virXMLPropString(top_node, "name")) ||
876 STRNEQ(if2_name, if_name)) {
877 vshError(ctl, _("Interface name from config %s doesn't match given supplied name %s"),
878 if2_name, if_name);
879 goto cleanup;
882 /* Create a <bridge> node under <interface>. */
883 if (!(br_node = xmlNewChild(top_node, NULL, BAD_CAST "bridge", NULL))) {
884 vshError(ctl, "%s", _("Failed to create bridge node in xml document"));
885 goto cleanup;
888 /* Set stp and delay attributes in <bridge> according to the
889 * commandline options.
891 if (!xmlSetProp(br_node, BAD_CAST "stp", BAD_CAST(stp ? "on" : "off"))) {
892 vshError(ctl, "%s", _("Failed to set stp attribute in xml document"));
893 goto cleanup;
896 if (stp &&
897 ((virAsprintf(&delay_str, "%d", delay) < 0) ||
898 !xmlSetProp(br_node, BAD_CAST "delay", BAD_CAST delay_str))) {
899 vshError(ctl, _("Failed to set bridge delay %d in xml document"), delay);
900 goto cleanup;
903 /* Change the type of the outer/master interface to "bridge" and the
904 * name to the provided bridge name.
906 if (!xmlSetProp(top_node, BAD_CAST "type", BAD_CAST "bridge")) {
907 vshError(ctl, "%s", _("Failed to set bridge interface type to 'bridge' in xml document"));
908 goto cleanup;
911 if (!xmlSetProp(top_node, BAD_CAST "name", BAD_CAST br_name)) {
912 vshError(ctl, _("Failed to set master bridge interface name to '%s' in xml document"),
913 br_name);
914 goto cleanup;
917 /* Create an <interface> node under <bridge> that uses the
918 * original interface's type and name.
920 if (!(if_node = xmlNewChild(br_node, NULL, BAD_CAST "interface", NULL))) {
921 vshError(ctl, "%s", _("Failed to create interface node under bridge node in xml document"));
922 goto cleanup;
925 /* set the type of the inner/slave interface to the original
926 * if_type, and the name to the original if_name.
928 if (!xmlSetProp(if_node, BAD_CAST "type", BAD_CAST if_type)) {
929 vshError(ctl, _("Failed to set new slave interface type to '%s' in xml document"),
930 if_type);
931 goto cleanup;
934 if (!xmlSetProp(if_node, BAD_CAST "name", BAD_CAST if_name)) {
935 vshError(ctl, _("Failed to set new slave interface name to '%s' in xml document"),
936 if_name);
937 goto cleanup;
940 /* Cycle through all the nodes under the original <interface>,
941 * moving all <mac>, <bond> and <vlan> nodes down into the new
942 * lower level <interface>.
944 cur = top_node->children;
945 while (cur) {
946 xmlNodePtr old = cur;
948 cur = cur->next;
949 if ((old->type == XML_ELEMENT_NODE) &&
950 (virXMLNodeNameEqual(old, "mac") || /* ethernet stuff to move down */
951 virXMLNodeNameEqual(old, "bond") || /* bond stuff to move down */
952 virXMLNodeNameEqual(old, "vlan"))) { /* vlan stuff to move down */
953 xmlUnlinkNode(old);
954 if (!xmlAddChild(if_node, old)) {
955 vshError(ctl, _("Failed to move '%s' element in xml document"), old->name);
956 xmlFreeNode(old);
957 goto cleanup;
962 /* The document should now be fully converted; write it out to a string. */
963 xmlDocDumpMemory(xml_doc, &br_xml, &br_xml_size);
965 if (!br_xml || br_xml_size <= 0) {
966 vshError(ctl, _("Failed to format new xml document for bridge %s"), br_name);
967 goto cleanup;
971 /* br_xml is the new interface to define. It will automatically undefine the
972 * independent original interface.
974 if (!(br_handle = virInterfaceDefineXML(priv->conn, (char *) br_xml, 0))) {
975 vshError(ctl, _("Failed to define new bridge interface %s"),
976 br_name);
977 goto cleanup;
980 vshPrintExtra(ctl, _("Created bridge %s with attached device %s\n"),
981 br_name, if_name);
983 /* start it up unless requested not to */
984 if (!nostart) {
985 if (virInterfaceCreate(br_handle, 0) < 0) {
986 vshError(ctl, _("Failed to start bridge interface %s"), br_name);
987 goto cleanup;
989 vshPrintExtra(ctl, _("Bridge interface %s started\n"), br_name);
992 ret = true;
993 cleanup:
994 if (if_handle)
995 virInterfaceFree(if_handle);
996 if (br_handle)
997 virInterfaceFree(br_handle);
998 VIR_FREE(if_xml);
999 VIR_FREE(br_xml);
1000 VIR_FREE(if_type);
1001 VIR_FREE(if2_name);
1002 VIR_FREE(delay_str);
1003 xmlXPathFreeContext(ctxt);
1004 xmlFreeDoc(xml_doc);
1005 return ret;
1009 * "iface-unbridge" command
1011 static const vshCmdInfo info_interface_unbridge[] = {
1012 {.name = "help",
1013 .data = N_("undefine a bridge device after detaching its slave device")
1015 {.name = "desc",
1016 .data = N_("unbridge a network device")
1018 {.name = NULL}
1021 static const vshCmdOptDef opts_interface_unbridge[] = {
1022 {.name = "bridge",
1023 .type = VSH_OT_DATA,
1024 .flags = VSH_OFLAG_REQ,
1025 .help = N_("current bridge device name")
1027 {.name = "no-start",
1028 .type = VSH_OT_BOOL,
1029 .help = N_("don't start the un-slaved interface immediately (not recommended)")
1031 {.name = NULL}
1034 static bool
1035 cmdInterfaceUnbridge(vshControl *ctl, const vshCmd *cmd)
1037 bool ret = false;
1038 virInterfacePtr if_handle = NULL, br_handle = NULL;
1039 const char *br_name;
1040 char *if_type = NULL, *if_name = NULL;
1041 bool nostart = false;
1042 char *br_xml = NULL;
1043 xmlChar *if_xml = NULL;
1044 int if_xml_size;
1045 xmlDocPtr xml_doc = NULL;
1046 xmlXPathContextPtr ctxt = NULL;
1047 xmlNodePtr top_node, if_node, cur;
1048 virshControlPtr priv = ctl->privData;
1050 /* Get a handle to the original device */
1051 if (!(br_handle = virshCommandOptInterfaceBy(ctl, cmd, "bridge",
1052 &br_name, VIRSH_BYNAME))) {
1053 goto cleanup;
1056 nostart = vshCommandOptBool(cmd, "no-start");
1058 /* Get the bridge xml into an xmlDoc */
1059 if (!(br_xml = virInterfaceGetXMLDesc(br_handle, VIR_INTERFACE_XML_INACTIVE)))
1060 goto cleanup;
1061 if (!(xml_doc = virXMLParseStringCtxt(br_xml,
1062 _("(bridge interface definition)"),
1063 &ctxt))) {
1064 vshError(ctl, _("Failed to parse configuration of %s"), br_name);
1065 goto cleanup;
1067 top_node = ctxt->node;
1069 /* Verify that the device really is a bridge. */
1070 if (!(if_type = virXMLPropString(top_node, "type"))) {
1071 vshError(ctl, _("Existing device %s has no type"), br_name);
1072 goto cleanup;
1075 if (STRNEQ(if_type, "bridge")) {
1076 vshError(ctl, _("Device %s is not a bridge"), br_name);
1077 goto cleanup;
1079 VIR_FREE(if_type);
1081 /* verify the name in the XML matches the device name */
1082 if (!(if_name = virXMLPropString(top_node, "name")) ||
1083 STRNEQ(if_name, br_name)) {
1084 vshError(ctl, _("Interface name from config %s doesn't match given supplied name %s"),
1085 if_name, br_name);
1086 goto cleanup;
1088 VIR_FREE(if_name);
1090 /* Find the <bridge> node under <interface>. */
1091 if (virXPathNode("./bridge", ctxt) == NULL) {
1092 vshError(ctl, "%s", _("No bridge node in xml document"));
1093 goto cleanup;
1096 if (virXPathNode("./bridge/interface[2]", ctxt) != NULL) {
1097 vshError(ctl, "%s", _("Multiple interfaces attached to bridge"));
1098 goto cleanup;
1101 if (!(if_node = virXPathNode("./bridge/interface", ctxt))) {
1102 vshError(ctl, "%s", _("No interface attached to bridge"));
1103 goto cleanup;
1106 /* Change the type and name of the outer/master interface to
1107 * the type/name of the attached slave interface.
1109 if (!(if_name = virXMLPropString(if_node, "name"))) {
1110 vshError(ctl, _("Device attached to bridge %s has no name"), br_name);
1111 goto cleanup;
1114 if (!(if_type = virXMLPropString(if_node, "type"))) {
1115 vshError(ctl, _("Attached device %s has no type"), if_name);
1116 goto cleanup;
1119 if (!xmlSetProp(top_node, BAD_CAST "type", BAD_CAST if_type)) {
1120 vshError(ctl, _("Failed to set interface type to '%s' in xml document"),
1121 if_type);
1122 goto cleanup;
1125 if (!xmlSetProp(top_node, BAD_CAST "name", BAD_CAST if_name)) {
1126 vshError(ctl, _("Failed to set interface name to '%s' in xml document"),
1127 if_name);
1128 goto cleanup;
1131 /* Cycle through all the nodes under the attached <interface>,
1132 * moving all <mac>, <bond> and <vlan> nodes up into the toplevel
1133 * <interface>.
1135 cur = if_node->children;
1136 while (cur) {
1137 xmlNodePtr old = cur;
1139 cur = cur->next;
1140 if ((old->type == XML_ELEMENT_NODE) &&
1141 (virXMLNodeNameEqual(old, "mac") || /* ethernet stuff to move down */
1142 virXMLNodeNameEqual(old, "bond") || /* bond stuff to move down */
1143 virXMLNodeNameEqual(old, "vlan"))) { /* vlan stuff to move down */
1144 xmlUnlinkNode(old);
1145 if (!xmlAddChild(top_node, old)) {
1146 vshError(ctl, _("Failed to move '%s' element in xml document"), old->name);
1147 xmlFreeNode(old);
1148 goto cleanup;
1153 /* The document should now be fully converted; write it out to a string. */
1154 xmlDocDumpMemory(xml_doc, &if_xml, &if_xml_size);
1156 if (!if_xml || if_xml_size <= 0) {
1157 vshError(ctl, _("Failed to format new xml document for un-enslaved interface %s"),
1158 if_name);
1159 goto cleanup;
1162 /* Destroy and Undefine the bridge device, since we otherwise
1163 * can't safely define the unattached device.
1165 if (virInterfaceDestroy(br_handle, 0) < 0) {
1166 vshError(ctl, _("Failed to destroy bridge interface %s"), br_name);
1167 goto cleanup;
1169 if (virInterfaceUndefine(br_handle) < 0) {
1170 vshError(ctl, _("Failed to undefine bridge interface %s"), br_name);
1171 goto cleanup;
1174 /* if_xml is the new interface to define.
1176 if (!(if_handle = virInterfaceDefineXML(priv->conn, (char *) if_xml, 0))) {
1177 vshError(ctl, _("Failed to define new interface %s"), if_name);
1178 goto cleanup;
1181 vshPrintExtra(ctl, _("Device %s un-attached from bridge %s\n"),
1182 if_name, br_name);
1184 /* unless requested otherwise, undefine the bridge device */
1185 if (!nostart) {
1186 if (virInterfaceCreate(if_handle, 0) < 0) {
1187 vshError(ctl, _("Failed to start interface %s"), if_name);
1188 goto cleanup;
1190 vshPrintExtra(ctl, _("Interface %s started\n"), if_name);
1193 ret = true;
1194 cleanup:
1195 if (if_handle)
1196 virInterfaceFree(if_handle);
1197 if (br_handle)
1198 virInterfaceFree(br_handle);
1199 VIR_FREE(if_xml);
1200 VIR_FREE(br_xml);
1201 VIR_FREE(if_type);
1202 VIR_FREE(if_name);
1203 xmlXPathFreeContext(ctxt);
1204 xmlFreeDoc(xml_doc);
1205 return ret;
1208 const vshCmdDef ifaceCmds[] = {
1209 {.name = "iface-begin",
1210 .handler = cmdInterfaceBegin,
1211 .opts = opts_interface_begin,
1212 .info = info_interface_begin,
1213 .flags = 0
1215 {.name = "iface-bridge",
1216 .handler = cmdInterfaceBridge,
1217 .opts = opts_interface_bridge,
1218 .info = info_interface_bridge,
1219 .flags = 0
1221 {.name = "iface-commit",
1222 .handler = cmdInterfaceCommit,
1223 .opts = opts_interface_commit,
1224 .info = info_interface_commit,
1225 .flags = 0
1227 {.name = "iface-define",
1228 .handler = cmdInterfaceDefine,
1229 .opts = opts_interface_define,
1230 .info = info_interface_define,
1231 .flags = 0
1233 {.name = "iface-destroy",
1234 .handler = cmdInterfaceDestroy,
1235 .opts = opts_interface_destroy,
1236 .info = info_interface_destroy,
1237 .flags = 0
1239 {.name = "iface-dumpxml",
1240 .handler = cmdInterfaceDumpXML,
1241 .opts = opts_interface_dumpxml,
1242 .info = info_interface_dumpxml,
1243 .flags = 0
1245 {.name = "iface-edit",
1246 .handler = cmdInterfaceEdit,
1247 .opts = opts_interface_edit,
1248 .info = info_interface_edit,
1249 .flags = 0
1251 {.name = "iface-list",
1252 .handler = cmdInterfaceList,
1253 .opts = opts_interface_list,
1254 .info = info_interface_list,
1255 .flags = 0
1257 {.name = "iface-mac",
1258 .handler = cmdInterfaceMAC,
1259 .opts = opts_interface_mac,
1260 .info = info_interface_mac,
1261 .flags = 0
1263 {.name = "iface-name",
1264 .handler = cmdInterfaceName,
1265 .opts = opts_interface_name,
1266 .info = info_interface_name,
1267 .flags = 0
1269 {.name = "iface-rollback",
1270 .handler = cmdInterfaceRollback,
1271 .opts = opts_interface_rollback,
1272 .info = info_interface_rollback,
1273 .flags = 0
1275 {.name = "iface-start",
1276 .handler = cmdInterfaceStart,
1277 .opts = opts_interface_start,
1278 .info = info_interface_start,
1279 .flags = 0
1281 {.name = "iface-unbridge",
1282 .handler = cmdInterfaceUnbridge,
1283 .opts = opts_interface_unbridge,
1284 .info = info_interface_unbridge,
1285 .flags = 0
1287 {.name = "iface-undefine",
1288 .handler = cmdInterfaceUndefine,
1289 .opts = opts_interface_undefine,
1290 .info = info_interface_undefine,
1291 .flags = 0
1293 {.name = NULL}