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, \
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>
39 #include "virbuffer.h"
42 #include "virmacaddr.h"
45 #include "virstring.h"
46 #include "vsh-table.h"
49 virshCommandOptInterfaceBy(vshControl
*ctl
, const vshCmd
*cmd
,
51 const char **name
, unsigned int flags
)
53 virInterfacePtr iface
= NULL
;
57 virCheckFlags(VIRSH_BYNAME
| VIRSH_BYMAC
, NULL
);
58 virshControlPtr priv
= ctl
->privData
;
61 optname
= "interface";
63 if (vshCommandOptStringReq(ctl
, cmd
, optname
, &n
) < 0)
66 vshDebug(ctl
, VSH_ERR_INFO
, "%s: found option <%s>: %s\n",
67 cmd
->def
->name
, optname
, n
);
72 if (virMacAddrParse(n
, &dummy
) == 0)
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
);
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
);
89 vshError(ctl
, _("failed to get interface '%s'"), n
);
95 * "iface-edit" command
97 static const vshCmdInfo info_interface_edit
[] = {
99 .data
= N_("edit XML configuration for a physical host interface")
102 .data
= N_("Edit the XML configuration for a physical host interface.")
107 static const vshCmdOptDef opts_interface_edit
[] = {
108 VIRSH_COMMON_OPT_INTERFACE(0),
113 cmdInterfaceEdit(vshControl
*ctl
, const vshCmd
*cmd
)
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
);
125 #define EDIT_GET_XML virInterfaceGetXMLDesc(iface, flags)
126 #define EDIT_NOT_CHANGED \
128 vshPrintExtra(ctl, _("Interface %s XML configuration not changed.\n"), \
129 virInterfaceGetName(iface)); \
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
));
144 virInterfaceFree(iface
);
146 virInterfaceFree(iface_edited
);
152 virshInterfaceSorter(const void *a
, const void *b
)
154 virInterfacePtr
*ia
= (virInterfacePtr
*) a
;
155 virInterfacePtr
*ib
= (virInterfacePtr
*) b
;
163 return vshStrcasecmp(virInterfaceGetName(*ia
),
164 virInterfaceGetName(*ib
));
167 struct virshInterfaceList
{
168 virInterfacePtr
*ifaces
;
171 typedef struct virshInterfaceList
*virshInterfaceListPtr
;
174 virshInterfaceListFree(virshInterfaceListPtr list
)
178 if (list
&& list
->ifaces
) {
179 for (i
= 0; i
< list
->nifaces
; i
++) {
181 virInterfaceFree(list
->ifaces
[i
]);
183 VIR_FREE(list
->ifaces
);
188 static virshInterfaceListPtr
189 virshInterfaceListCollect(vshControl
*ctl
,
192 virshInterfaceListPtr list
= vshMalloc(ctl
, sizeof(*list
));
195 char **activeNames
= NULL
;
196 char **inactiveNames
= NULL
;
197 virInterfacePtr iface
;
198 bool success
= false;
200 int nActiveIfaces
= 0;
201 int nInactiveIfaces
= 0;
203 virshControlPtr priv
= ctl
->privData
;
205 /* try the list with flags support (0.10.2 and later) */
206 if ((ret
= virConnectListAllInterfaces(priv
->conn
,
213 /* check if the command is actually supported */
214 if (last_error
&& last_error
->code
== VIR_ERR_NO_SUPPORT
)
217 /* there was an error during the first or second call */
218 vshError(ctl
, "%s", _("Failed to list interfaces"));
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"));
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"));
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"));
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"));
261 nAllIfaces
= nActiveIfaces
+ nInactiveIfaces
;
262 if (nAllIfaces
== 0) {
263 VIR_FREE(activeNames
);
264 VIR_FREE(inactiveNames
);
268 list
->ifaces
= vshMalloc(ctl
, sizeof(virInterfacePtr
) * (nAllIfaces
));
271 /* get active interfaces */
272 for (i
= 0; i
< nActiveIfaces
; i
++) {
273 if (!(iface
= virInterfaceLookupByName(priv
->conn
, activeNames
[i
]))) {
274 vshResetLibvirtError();
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();
286 list
->ifaces
[list
->nifaces
++] = iface
;
289 /* truncate interfaces that weren't found */
290 deleted
= nAllIfaces
- list
->nifaces
;
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 */
300 VIR_SHRINK_N(list
->ifaces
, list
->nifaces
, deleted
);
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
);
315 virshInterfaceListFree(list
);
323 * "iface-list" command
325 static const vshCmdInfo info_interface_list
[] = {
327 .data
= N_("list physical host interfaces")
330 .data
= N_("Returns list of physical host interfaces.")
335 static const vshCmdOptDef opts_interface_list
[] = {
338 .help
= N_("list inactive interfaces")
342 .help
= N_("list inactive & active interfaces")
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
;
356 vshTablePtr table
= NULL
;
359 flags
= VIR_CONNECT_LIST_INTERFACES_INACTIVE
;
361 flags
= VIR_CONNECT_LIST_INTERFACES_INACTIVE
|
362 VIR_CONNECT_LIST_INTERFACES_ACTIVE
;
364 if (!(list
= virshInterfaceListCollect(ctl
, flags
)))
367 table
= vshTableNew(_("Name"), _("State"), _("MAC Address"), NULL
);
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")
378 virInterfaceGetMACString(iface
),
383 vshTablePrintToStdout(table
, ctl
);
388 virshInterfaceListFree(list
);
393 * "iface-name" command
395 static const vshCmdInfo info_interface_name
[] = {
397 .data
= N_("convert an interface MAC address to interface name")
405 static const vshCmdOptDef opts_interface_name
[] = {
406 {.name
= "interface",
408 .flags
= VSH_OFLAG_REQ
,
409 .help
= N_("interface mac")
415 cmdInterfaceName(vshControl
*ctl
, const vshCmd
*cmd
)
417 virInterfacePtr iface
;
419 if (!(iface
= virshCommandOptInterfaceBy(ctl
, cmd
, NULL
, NULL
,
423 vshPrint(ctl
, "%s\n", virInterfaceGetName(iface
));
424 virInterfaceFree(iface
);
429 * "iface-mac" command
431 static const vshCmdInfo info_interface_mac
[] = {
433 .data
= N_("convert an interface name to interface MAC address")
441 static const vshCmdOptDef opts_interface_mac
[] = {
442 {.name
= "interface",
444 .flags
= VSH_OFLAG_REQ
,
445 .help
= N_("interface name")
451 cmdInterfaceMAC(vshControl
*ctl
, const vshCmd
*cmd
)
453 virInterfacePtr iface
;
455 if (!(iface
= virshCommandOptInterfaceBy(ctl
, cmd
, NULL
, NULL
,
459 vshPrint(ctl
, "%s\n", virInterfaceGetMACString(iface
));
460 virInterfaceFree(iface
);
465 * "iface-dumpxml" command
467 static const vshCmdInfo info_interface_dumpxml
[] = {
469 .data
= N_("interface information in XML")
472 .data
= N_("Output the physical host interface information as an XML dump to stdout.")
477 static const vshCmdOptDef opts_interface_dumpxml
[] = {
478 VIRSH_COMMON_OPT_INTERFACE(0),
481 .help
= N_("show inactive defined XML")
487 cmdInterfaceDumpXML(vshControl
*ctl
, const vshCmd
*cmd
)
489 virInterfacePtr iface
;
492 unsigned int flags
= 0;
493 bool inactive
= vshCommandOptBool(cmd
, "inactive");
496 flags
|= VIR_INTERFACE_XML_INACTIVE
;
498 if (!(iface
= virshCommandOptInterface(ctl
, cmd
, NULL
)))
501 dump
= virInterfaceGetXMLDesc(iface
, flags
);
503 vshPrint(ctl
, "%s", dump
);
509 virInterfaceFree(iface
);
514 * "iface-define" command
516 static const vshCmdInfo info_interface_define
[] = {
518 .data
= N_("define an inactive persistent physical host interface or "
519 "modify an existing persistent one from an XML file")
522 .data
= N_("Define or modify a persistent physical host interface.")
527 static const vshCmdOptDef opts_interface_define
[] = {
528 VIRSH_COMMON_OPT_FILE(N_("file containing an XML interface description")),
533 cmdInterfaceDefine(vshControl
*ctl
, const vshCmd
*cmd
)
535 virInterfacePtr iface
;
536 const char *from
= NULL
;
539 virshControlPtr priv
= ctl
->privData
;
541 if (vshCommandOptStringReq(ctl
, cmd
, "file", &from
) < 0)
544 if (virFileReadAll(from
, VSH_MAX_XML_FILE
, &buffer
) < 0)
547 iface
= virInterfaceDefineXML(priv
->conn
, buffer
, 0);
551 vshPrintExtra(ctl
, _("Interface %s defined from %s\n"),
552 virInterfaceGetName(iface
), from
);
553 virInterfaceFree(iface
);
555 vshError(ctl
, _("Failed to define interface from %s"), from
);
562 * "iface-undefine" command
564 static const vshCmdInfo info_interface_undefine
[] = {
566 .data
= N_("undefine a physical host interface (remove it from configuration)")
569 .data
= N_("undefine an interface.")
574 static const vshCmdOptDef opts_interface_undefine
[] = {
575 VIRSH_COMMON_OPT_INTERFACE(0),
580 cmdInterfaceUndefine(vshControl
*ctl
, const vshCmd
*cmd
)
582 virInterfacePtr iface
;
586 if (!(iface
= virshCommandOptInterface(ctl
, cmd
, &name
)))
589 if (virInterfaceUndefine(iface
) == 0) {
590 vshPrintExtra(ctl
, _("Interface %s undefined\n"), name
);
592 vshError(ctl
, _("Failed to undefine interface %s"), name
);
596 virInterfaceFree(iface
);
601 * "iface-start" command
603 static const vshCmdInfo info_interface_start
[] = {
605 .data
= N_("start a physical host interface (enable it / \"if-up\")")
608 .data
= N_("start a physical host interface.")
613 static const vshCmdOptDef opts_interface_start
[] = {
614 VIRSH_COMMON_OPT_INTERFACE(VIR_CONNECT_LIST_INTERFACES_INACTIVE
),
619 cmdInterfaceStart(vshControl
*ctl
, const vshCmd
*cmd
)
621 virInterfacePtr iface
;
625 if (!(iface
= virshCommandOptInterface(ctl
, cmd
, &name
)))
628 if (virInterfaceCreate(iface
, 0) == 0) {
629 vshPrintExtra(ctl
, _("Interface %s started\n"), name
);
631 vshError(ctl
, _("Failed to start interface %s"), name
);
635 virInterfaceFree(iface
);
640 * "iface-destroy" command
642 static const vshCmdInfo info_interface_destroy
[] = {
644 .data
= N_("destroy a physical host interface (disable it / \"if-down\")")
647 .data
= N_("forcefully stop a physical host interface.")
652 static const vshCmdOptDef opts_interface_destroy
[] = {
653 VIRSH_COMMON_OPT_INTERFACE(VIR_CONNECT_LIST_INTERFACES_ACTIVE
),
658 cmdInterfaceDestroy(vshControl
*ctl
, const vshCmd
*cmd
)
660 virInterfacePtr iface
;
664 if (!(iface
= virshCommandOptInterface(ctl
, cmd
, &name
)))
667 if (virInterfaceDestroy(iface
, 0) == 0) {
668 vshPrintExtra(ctl
, _("Interface %s destroyed\n"), name
);
670 vshError(ctl
, _("Failed to destroy interface %s"), name
);
674 virInterfaceFree(iface
);
679 * "iface-begin" command
681 static const vshCmdInfo info_interface_begin
[] = {
683 .data
= N_("create a snapshot of current interfaces settings, "
684 "which can be later committed (iface-commit) or "
685 "restored (iface-rollback)")
688 .data
= N_("Create a restore point for interfaces settings")
693 static const vshCmdOptDef opts_interface_begin
[] = {
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"));
707 vshPrintExtra(ctl
, "%s", _("Network config change transaction started\n"));
712 * "iface-commit" command
714 static const vshCmdInfo info_interface_commit
[] = {
716 .data
= N_("commit changes made since iface-begin and free restore point")
719 .data
= N_("commit changes and free restore point")
724 static const vshCmdOptDef opts_interface_commit
[] = {
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"));
738 vshPrintExtra(ctl
, "%s", _("Network config change transaction committed\n"));
743 * "iface-rollback" command
745 static const vshCmdInfo info_interface_rollback
[] = {
747 .data
= N_("rollback to previous saved configuration created via iface-begin")
750 .data
= N_("rollback to previous restore point")
755 static const vshCmdOptDef opts_interface_rollback
[] = {
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"));
769 vshPrintExtra(ctl
, "%s", _("Network config change transaction rolled back\n"));
774 * "iface-bridge" command
776 static const vshCmdInfo info_interface_bridge
[] = {
778 .data
= N_("create a bridge device and attach an existing network device to it")
781 .data
= N_("bridge an existing network device")
786 static const vshCmdOptDef opts_interface_bridge
[] = {
787 {.name
= "interface",
789 .flags
= VSH_OFLAG_REQ
,
790 .help
= N_("existing interface name")
794 .flags
= VSH_OFLAG_REQ
,
795 .help
= N_("new bridge device name")
799 .help
= N_("do not enable STP for this bridge")
803 .help
= N_("number of seconds to squelch traffic on newly connected ports")
807 .help
= N_("don't start the bridge immediately")
813 cmdInterfaceBridge(vshControl
*ctl
, const vshCmd
*cmd
)
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;
822 xmlChar
*br_xml
= NULL
;
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
))) {
835 /* Name for new bridge device */
836 if (vshCommandOptStringReq(ctl
, cmd
, "bridge", &br_name
) < 0)
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
);
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)
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
)))
856 if (!(xml_doc
= virXMLParseStringCtxt(if_xml
,
857 _("(interface definition)"), &ctxt
))) {
858 vshError(ctl
, _("Failed to parse configuration of %s"), if_name
);
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
);
869 if (STREQ(if_type
, "bridge")) {
870 vshError(ctl
, _("Existing device %s is already a bridge"), if_name
);
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"),
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"));
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"));
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
);
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"));
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"),
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"));
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"),
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"),
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
;
946 xmlNodePtr old
= cur
;
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 */
954 if (!xmlAddChild(if_node
, old
)) {
955 vshError(ctl
, _("Failed to move '%s' element in xml document"), old
->name
);
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
);
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"),
980 vshPrintExtra(ctl
, _("Created bridge %s with attached device %s\n"),
983 /* start it up unless requested not to */
985 if (virInterfaceCreate(br_handle
, 0) < 0) {
986 vshError(ctl
, _("Failed to start bridge interface %s"), br_name
);
989 vshPrintExtra(ctl
, _("Bridge interface %s started\n"), br_name
);
995 virInterfaceFree(if_handle
);
997 virInterfaceFree(br_handle
);
1002 VIR_FREE(delay_str
);
1003 xmlXPathFreeContext(ctxt
);
1004 xmlFreeDoc(xml_doc
);
1009 * "iface-unbridge" command
1011 static const vshCmdInfo info_interface_unbridge
[] = {
1013 .data
= N_("undefine a bridge device after detaching its slave device")
1016 .data
= N_("unbridge a network device")
1021 static const vshCmdOptDef opts_interface_unbridge
[] = {
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)")
1035 cmdInterfaceUnbridge(vshControl
*ctl
, const vshCmd
*cmd
)
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
;
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
))) {
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
)))
1061 if (!(xml_doc
= virXMLParseStringCtxt(br_xml
,
1062 _("(bridge interface definition)"),
1064 vshError(ctl
, _("Failed to parse configuration of %s"), br_name
);
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
);
1075 if (STRNEQ(if_type
, "bridge")) {
1076 vshError(ctl
, _("Device %s is not a bridge"), br_name
);
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"),
1090 /* Find the <bridge> node under <interface>. */
1091 if (virXPathNode("./bridge", ctxt
) == NULL
) {
1092 vshError(ctl
, "%s", _("No bridge node in xml document"));
1096 if (virXPathNode("./bridge/interface[2]", ctxt
) != NULL
) {
1097 vshError(ctl
, "%s", _("Multiple interfaces attached to bridge"));
1101 if (!(if_node
= virXPathNode("./bridge/interface", ctxt
))) {
1102 vshError(ctl
, "%s", _("No interface attached to bridge"));
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
);
1114 if (!(if_type
= virXMLPropString(if_node
, "type"))) {
1115 vshError(ctl
, _("Attached device %s has no type"), if_name
);
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"),
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"),
1131 /* Cycle through all the nodes under the attached <interface>,
1132 * moving all <mac>, <bond> and <vlan> nodes up into the toplevel
1135 cur
= if_node
->children
;
1137 xmlNodePtr old
= cur
;
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 */
1145 if (!xmlAddChild(top_node
, old
)) {
1146 vshError(ctl
, _("Failed to move '%s' element in xml document"), old
->name
);
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"),
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
);
1169 if (virInterfaceUndefine(br_handle
) < 0) {
1170 vshError(ctl
, _("Failed to undefine bridge interface %s"), br_name
);
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
);
1181 vshPrintExtra(ctl
, _("Device %s un-attached from bridge %s\n"),
1184 /* unless requested otherwise, undefine the bridge device */
1186 if (virInterfaceCreate(if_handle
, 0) < 0) {
1187 vshError(ctl
, _("Failed to start interface %s"), if_name
);
1190 vshPrintExtra(ctl
, _("Interface %s started\n"), if_name
);
1196 virInterfaceFree(if_handle
);
1198 virInterfaceFree(br_handle
);
1203 xmlXPathFreeContext(ctxt
);
1204 xmlFreeDoc(xml_doc
);
1208 const vshCmdDef ifaceCmds
[] = {
1209 {.name
= "iface-begin",
1210 .handler
= cmdInterfaceBegin
,
1211 .opts
= opts_interface_begin
,
1212 .info
= info_interface_begin
,
1215 {.name
= "iface-bridge",
1216 .handler
= cmdInterfaceBridge
,
1217 .opts
= opts_interface_bridge
,
1218 .info
= info_interface_bridge
,
1221 {.name
= "iface-commit",
1222 .handler
= cmdInterfaceCommit
,
1223 .opts
= opts_interface_commit
,
1224 .info
= info_interface_commit
,
1227 {.name
= "iface-define",
1228 .handler
= cmdInterfaceDefine
,
1229 .opts
= opts_interface_define
,
1230 .info
= info_interface_define
,
1233 {.name
= "iface-destroy",
1234 .handler
= cmdInterfaceDestroy
,
1235 .opts
= opts_interface_destroy
,
1236 .info
= info_interface_destroy
,
1239 {.name
= "iface-dumpxml",
1240 .handler
= cmdInterfaceDumpXML
,
1241 .opts
= opts_interface_dumpxml
,
1242 .info
= info_interface_dumpxml
,
1245 {.name
= "iface-edit",
1246 .handler
= cmdInterfaceEdit
,
1247 .opts
= opts_interface_edit
,
1248 .info
= info_interface_edit
,
1251 {.name
= "iface-list",
1252 .handler
= cmdInterfaceList
,
1253 .opts
= opts_interface_list
,
1254 .info
= info_interface_list
,
1257 {.name
= "iface-mac",
1258 .handler
= cmdInterfaceMAC
,
1259 .opts
= opts_interface_mac
,
1260 .info
= info_interface_mac
,
1263 {.name
= "iface-name",
1264 .handler
= cmdInterfaceName
,
1265 .opts
= opts_interface_name
,
1266 .info
= info_interface_name
,
1269 {.name
= "iface-rollback",
1270 .handler
= cmdInterfaceRollback
,
1271 .opts
= opts_interface_rollback
,
1272 .info
= info_interface_rollback
,
1275 {.name
= "iface-start",
1276 .handler
= cmdInterfaceStart
,
1277 .opts
= opts_interface_start
,
1278 .info
= info_interface_start
,
1281 {.name
= "iface-unbridge",
1282 .handler
= cmdInterfaceUnbridge
,
1283 .opts
= opts_interface_unbridge
,
1284 .info
= info_interface_unbridge
,
1287 {.name
= "iface-undefine",
1288 .handler
= cmdInterfaceUndefine
,
1289 .opts
= opts_interface_undefine
,
1290 .info
= info_interface_undefine
,