/* * virsh-network.c: Commands to manage network * * Copyright (C) 2005, 2007-2012 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see * . * * Daniel Veillard * Karel Zak * Daniel P. Berrange * */ #include #include "virsh-network.h" #include #include #include #include #include "internal.h" #include "buf.h" #include "memory.h" #include "util.h" #include "xml.h" #include "conf/network_conf.h" virNetworkPtr vshCommandOptNetworkBy(vshControl *ctl, const vshCmd *cmd, const char **name, unsigned int flags) { virNetworkPtr network = NULL; const char *n = NULL; const char *optname = "network"; virCheckFlags(VSH_BYUUID | VSH_BYNAME, NULL); if (!vshCmdHasOption(ctl, cmd, optname)) return NULL; if (vshCommandOptString(cmd, optname, &n) <= 0) return NULL; vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n", cmd->def->name, optname, n); if (name) *name = n; /* try it by UUID */ if ((flags & VSH_BYUUID) && strlen(n) == VIR_UUID_STRING_BUFLEN-1) { vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as network UUID\n", cmd->def->name, optname); network = virNetworkLookupByUUIDString(ctl->conn, n); } /* try it by NAME */ if (!network && (flags & VSH_BYNAME)) { vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as network NAME\n", cmd->def->name, optname); network = virNetworkLookupByName(ctl->conn, n); } if (!network) vshError(ctl, _("failed to get network '%s'"), n); return network; } /* * "net-autostart" command */ static const vshCmdInfo info_network_autostart[] = { {"help", N_("autostart a network")}, {"desc", N_("Configure a network to be automatically started at boot.")}, {NULL, NULL} }; static const vshCmdOptDef opts_network_autostart[] = { {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")}, {"disable", VSH_OT_BOOL, 0, N_("disable autostarting")}, {NULL, 0, 0, NULL} }; static bool cmdNetworkAutostart(vshControl *ctl, const vshCmd *cmd) { virNetworkPtr network; const char *name; int autostart; if (!(network = vshCommandOptNetwork(ctl, cmd, &name))) return false; autostart = !vshCommandOptBool(cmd, "disable"); if (virNetworkSetAutostart(network, autostart) < 0) { if (autostart) vshError(ctl, _("failed to mark network %s as autostarted"), name); else vshError(ctl, _("failed to unmark network %s as autostarted"), name); virNetworkFree(network); return false; } if (autostart) vshPrint(ctl, _("Network %s marked as autostarted\n"), name); else vshPrint(ctl, _("Network %s unmarked as autostarted\n"), name); virNetworkFree(network); return true; } /* * "net-create" command */ static const vshCmdInfo info_network_create[] = { {"help", N_("create a network from an XML file")}, {"desc", N_("Create a network.")}, {NULL, NULL} }; static const vshCmdOptDef opts_network_create[] = { {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML network description")}, {NULL, 0, 0, NULL} }; static bool cmdNetworkCreate(vshControl *ctl, const vshCmd *cmd) { virNetworkPtr network; const char *from = NULL; bool ret = true; char *buffer; if (vshCommandOptString(cmd, "file", &from) <= 0) return false; if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) return false; network = virNetworkCreateXML(ctl->conn, buffer); VIR_FREE(buffer); if (network != NULL) { vshPrint(ctl, _("Network %s created from %s\n"), virNetworkGetName(network), from); virNetworkFree(network); } else { vshError(ctl, _("Failed to create network from %s"), from); ret = false; } return ret; } /* * "net-define" command */ static const vshCmdInfo info_network_define[] = { {"help", N_("define (but don't start) a network from an XML file")}, {"desc", N_("Define a network.")}, {NULL, NULL} }; static const vshCmdOptDef opts_network_define[] = { {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML network description")}, {NULL, 0, 0, NULL} }; static bool cmdNetworkDefine(vshControl *ctl, const vshCmd *cmd) { virNetworkPtr network; const char *from = NULL; bool ret = true; char *buffer; if (vshCommandOptString(cmd, "file", &from) <= 0) return false; if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) return false; network = virNetworkDefineXML(ctl->conn, buffer); VIR_FREE(buffer); if (network != NULL) { vshPrint(ctl, _("Network %s defined from %s\n"), virNetworkGetName(network), from); virNetworkFree(network); } else { vshError(ctl, _("Failed to define network from %s"), from); ret = false; } return ret; } /* * "net-destroy" command */ static const vshCmdInfo info_network_destroy[] = { {"help", N_("destroy (stop) a network")}, {"desc", N_("Forcefully stop a given network.")}, {NULL, NULL} }; static const vshCmdOptDef opts_network_destroy[] = { {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")}, {NULL, 0, 0, NULL} }; static bool cmdNetworkDestroy(vshControl *ctl, const vshCmd *cmd) { virNetworkPtr network; bool ret = true; const char *name; if (!(network = vshCommandOptNetwork(ctl, cmd, &name))) return false; if (virNetworkDestroy(network) == 0) { vshPrint(ctl, _("Network %s destroyed\n"), name); } else { vshError(ctl, _("Failed to destroy network %s"), name); ret = false; } virNetworkFree(network); return ret; } /* * "net-dumpxml" command */ static const vshCmdInfo info_network_dumpxml[] = { {"help", N_("network information in XML")}, {"desc", N_("Output the network information as an XML dump to stdout.")}, {NULL, NULL} }; static const vshCmdOptDef opts_network_dumpxml[] = { {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")}, {"inactive", VSH_OT_BOOL, VSH_OFLAG_NONE, N_("network information of an inactive domain")}, {NULL, 0, 0, NULL} }; static bool cmdNetworkDumpXML(vshControl *ctl, const vshCmd *cmd) { virNetworkPtr network; bool ret = true; char *dump; unsigned int flags = 0; int inactive; if (!(network = vshCommandOptNetwork(ctl, cmd, NULL))) return false; inactive = vshCommandOptBool(cmd, "inactive"); if (inactive) flags |= VIR_NETWORK_XML_INACTIVE; dump = virNetworkGetXMLDesc(network, flags); if (dump != NULL) { vshPrint(ctl, "%s", dump); VIR_FREE(dump); } else { ret = false; } virNetworkFree(network); return ret; } /* * "net-info" command */ static const vshCmdInfo info_network_info[] = { {"help", N_("network information")}, {"desc", N_("Returns basic information about the network")}, {NULL, NULL} }; static const vshCmdOptDef opts_network_info[] = { {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")}, {NULL, 0, 0, NULL} }; static bool cmdNetworkInfo(vshControl *ctl, const vshCmd *cmd) { virNetworkPtr network; char uuid[VIR_UUID_STRING_BUFLEN]; int autostart; int persistent = -1; int active = -1; char *bridge = NULL; if (!(network = vshCommandOptNetwork(ctl, cmd, NULL))) return false; vshPrint(ctl, "%-15s %s\n", _("Name"), virNetworkGetName(network)); if (virNetworkGetUUIDString(network, uuid) == 0) vshPrint(ctl, "%-15s %s\n", _("UUID"), uuid); active = virNetworkIsActive(network); if (active >= 0) vshPrint(ctl, "%-15s %s\n", _("Active:"), active? _("yes") : _("no")); persistent = virNetworkIsPersistent(network); if (persistent < 0) vshPrint(ctl, "%-15s %s\n", _("Persistent:"), _("unknown")); else vshPrint(ctl, "%-15s %s\n", _("Persistent:"), persistent ? _("yes") : _("no")); if (virNetworkGetAutostart(network, &autostart) < 0) vshPrint(ctl, "%-15s %s\n", _("Autostart:"), _("no autostart")); else vshPrint(ctl, "%-15s %s\n", _("Autostart:"), autostart ? _("yes") : _("no")); bridge = virNetworkGetBridgeName(network); if (bridge) vshPrint(ctl, "%-15s %s\n", _("Bridge:"), bridge); VIR_FREE(bridge); virNetworkFree(network); return true; } static int vshNetworkSorter(const void *a, const void *b) { virNetworkPtr *na = (virNetworkPtr *) a; virNetworkPtr *nb = (virNetworkPtr *) b; if (*na && !*nb) return -1; if (!*na) return *nb != NULL; return vshStrcasecmp(virNetworkGetName(*na), virNetworkGetName(*nb)); } struct vshNetworkList { virNetworkPtr *nets; size_t nnets; }; typedef struct vshNetworkList *vshNetworkListPtr; static void vshNetworkListFree(vshNetworkListPtr list) { int i; if (list && list->nnets) { for (i = 0; i < list->nnets; i++) { if (list->nets[i]) virNetworkFree(list->nets[i]); } VIR_FREE(list->nets); } VIR_FREE(list); } static vshNetworkListPtr vshNetworkListCollect(vshControl *ctl, unsigned int flags) { vshNetworkListPtr list = vshMalloc(ctl, sizeof(*list)); int i; int ret; char **names = NULL; virNetworkPtr net; bool success = false; size_t deleted = 0; int persistent; int autostart; int nActiveNets = 0; int nInactiveNets = 0; int nAllNets = 0; /* try the list with flags support (0.10.0 and later) */ if ((ret = virConnectListAllNetworks(ctl->conn, &list->nets, flags)) >= 0) { list->nnets = ret; goto finished; } /* check if the command is actually supported */ if (last_error && last_error->code == VIR_ERR_NO_SUPPORT) { vshResetLibvirtError(); goto fallback; } if (last_error && last_error->code == VIR_ERR_INVALID_ARG) { /* try the new API again but mask non-guaranteed flags */ unsigned int newflags = flags & (VIR_CONNECT_LIST_NETWORKS_ACTIVE | VIR_CONNECT_LIST_NETWORKS_INACTIVE); vshResetLibvirtError(); if ((ret = virConnectListAllNetworks(ctl->conn, &list->nets, newflags)) >= 0) { list->nnets = ret; goto filter; } } /* there was an error during the first or second call */ vshError(ctl, "%s", _("Failed to list networks")); goto cleanup; fallback: /* fall back to old method (0.9.13 and older) */ vshResetLibvirtError(); /* Get the number of active networks */ if (!VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) || VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_ACTIVE)) { if ((nActiveNets = virConnectNumOfNetworks(ctl->conn)) < 0) { vshError(ctl, "%s", _("Failed to get the number of active networks")); goto cleanup; } } /* Get the number of inactive networks */ if (!VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) || VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_INACTIVE)) { if ((nInactiveNets = virConnectNumOfDefinedNetworks(ctl->conn)) < 0) { vshError(ctl, "%s", _("Failed to get the number of inactive networks")); goto cleanup; } } nAllNets = nActiveNets + nInactiveNets; if (nAllNets == 0) return list; names = vshMalloc(ctl, sizeof(char *) * nAllNets); /* Retrieve a list of active network names */ if (!VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) || VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_ACTIVE)) { if (virConnectListNetworks(ctl->conn, names, nActiveNets) < 0) { vshError(ctl, "%s", _("Failed to list active networks")); goto cleanup; } } /* Add the inactive networks to the end of the name list */ if (!VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) || VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_ACTIVE)) { if (virConnectListDefinedNetworks(ctl->conn, &names[nActiveNets], nInactiveNets) < 0) { vshError(ctl, "%s", _("Failed to list inactive networks")); goto cleanup; } } list->nets = vshMalloc(ctl, sizeof(virNetworkPtr) * (nAllNets)); list->nnets = 0; /* get active networks */ for (i = 0; i < nActiveNets; i++) { if (!(net = virNetworkLookupByName(ctl->conn, names[i]))) continue; list->nets[list->nnets++] = net; } /* get inactive networks */ for (i = 0; i < nInactiveNets; i++) { if (!(net = virNetworkLookupByName(ctl->conn, names[i]))) continue; list->nets[list->nnets++] = net; } /* truncate networks that weren't found */ deleted = nAllNets - list->nnets; filter: /* filter list the list if the list was acquired by fallback means */ for (i = 0; i < list->nnets; i++) { net = list->nets[i]; /* persistence filter */ if (VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_PERSISTENT)) { if ((persistent = virNetworkIsPersistent(net)) < 0) { vshError(ctl, "%s", _("Failed to get network persistence info")); goto cleanup; } if (!((VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_PERSISTENT) && persistent) || (VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_TRANSIENT) && !persistent))) goto remove_entry; } /* autostart filter */ if (VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_AUTOSTART)) { if (virNetworkGetAutostart(net, &autostart) < 0) { vshError(ctl, "%s", _("Failed to get network autostart state")); goto cleanup; } if (!((VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_AUTOSTART) && autostart) || (VSH_MATCH(VIR_CONNECT_LIST_NETWORKS_NO_AUTOSTART) && !autostart))) goto remove_entry; } /* the pool matched all filters, it may stay */ continue; remove_entry: /* the pool has to be removed as it failed one of the filters */ virNetworkFree(list->nets[i]); list->nets[i] = NULL; deleted++; } finished: /* sort the list */ if (list->nets && list->nnets) qsort(list->nets, list->nnets, sizeof(*list->nets), vshNetworkSorter); /* truncate the list if filter simulation deleted entries */ if (deleted) VIR_SHRINK_N(list->nets, list->nnets, deleted); success = true; cleanup: for (i = 0; i < nAllNets; i++) VIR_FREE(names[i]); VIR_FREE(names); if (!success) { vshNetworkListFree(list); list = NULL; } return list; } /* * "net-list" command */ static const vshCmdInfo info_network_list[] = { {"help", N_("list networks")}, {"desc", N_("Returns list of networks.")}, {NULL, NULL} }; static const vshCmdOptDef opts_network_list[] = { {"inactive", VSH_OT_BOOL, 0, N_("list inactive networks")}, {"all", VSH_OT_BOOL, 0, N_("list inactive & active networks")}, {"persistent", VSH_OT_BOOL, 0, N_("list persistent networks")}, {"transient", VSH_OT_BOOL, 0, N_("list transient networks")}, {"autostart", VSH_OT_BOOL, 0, N_("list networks with autostart enabled")}, {"no-autostart", VSH_OT_BOOL, 0, N_("list networks with autostart disabled")}, {NULL, 0, 0, NULL} }; static bool cmdNetworkList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) { vshNetworkListPtr list = NULL; int i; bool inactive = vshCommandOptBool(cmd, "inactive"); bool all = vshCommandOptBool(cmd, "all"); bool persistent = vshCommandOptBool(cmd, "persistent"); bool transient = vshCommandOptBool(cmd, "transient"); bool autostart = vshCommandOptBool(cmd, "autostart"); bool no_autostart = vshCommandOptBool(cmd, "no-autostart"); unsigned int flags = VIR_CONNECT_LIST_NETWORKS_ACTIVE; if (inactive) flags = VIR_CONNECT_LIST_NETWORKS_INACTIVE; if (all) flags = VIR_CONNECT_LIST_NETWORKS_ACTIVE | VIR_CONNECT_LIST_NETWORKS_INACTIVE; if (persistent) flags |= VIR_CONNECT_LIST_NETWORKS_PERSISTENT; if (transient) flags |= VIR_CONNECT_LIST_NETWORKS_TRANSIENT; if (autostart) flags |= VIR_CONNECT_LIST_NETWORKS_AUTOSTART; if (no_autostart) flags |= VIR_CONNECT_LIST_NETWORKS_NO_AUTOSTART; if (!(list = vshNetworkListCollect(ctl, flags))) return false; vshPrintExtra(ctl, "%-20s %-10s %-13s %s\n", _("Name"), _("State"), _("Autostart"), _("Persistent")); vshPrintExtra(ctl, "--------------------------------------------------\n"); for (i = 0; i < list->nnets; i++) { virNetworkPtr network = list->nets[i]; const char *autostartStr; int is_autostart = 0; if (virNetworkGetAutostart(network, &is_autostart) < 0) autostartStr = _("no autostart"); else autostartStr = is_autostart ? _("yes") : _("no"); vshPrint(ctl, "%-20s %-10s %-13s %s\n", virNetworkGetName(network), virNetworkIsActive(network) ? _("active") : _("inactive"), autostartStr, virNetworkIsPersistent(network) ? _("yes") : _("no")); } vshNetworkListFree(list); return true; } /* * "net-name" command */ static const vshCmdInfo info_network_name[] = { {"help", N_("convert a network UUID to network name")}, {"desc", ""}, {NULL, NULL} }; static const vshCmdOptDef opts_network_name[] = { {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network uuid")}, {NULL, 0, 0, NULL} }; static bool cmdNetworkName(vshControl *ctl, const vshCmd *cmd) { virNetworkPtr network; if (!(network = vshCommandOptNetworkBy(ctl, cmd, NULL, VSH_BYUUID))) return false; vshPrint(ctl, "%s\n", virNetworkGetName(network)); virNetworkFree(network); return true; } /* * "net-start" command */ static const vshCmdInfo info_network_start[] = { {"help", N_("start a (previously defined) inactive network")}, {"desc", N_("Start a network.")}, {NULL, NULL} }; static const vshCmdOptDef opts_network_start[] = { {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")}, {NULL, 0, 0, NULL} }; static bool cmdNetworkStart(vshControl *ctl, const vshCmd *cmd) { virNetworkPtr network; bool ret = true; const char *name = NULL; if (!(network = vshCommandOptNetwork(ctl, cmd, &name))) return false; if (virNetworkCreate(network) == 0) { vshPrint(ctl, _("Network %s started\n"), name); } else { vshError(ctl, _("Failed to start network %s"), name); ret = false; } virNetworkFree(network); return ret; } /* * "net-undefine" command */ static const vshCmdInfo info_network_undefine[] = { {"help", N_("undefine an inactive network")}, {"desc", N_("Undefine the configuration for an inactive network.")}, {NULL, NULL} }; static const vshCmdOptDef opts_network_undefine[] = { {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")}, {NULL, 0, 0, NULL} }; static bool cmdNetworkUndefine(vshControl *ctl, const vshCmd *cmd) { virNetworkPtr network; bool ret = true; const char *name; if (!(network = vshCommandOptNetwork(ctl, cmd, &name))) return false; if (virNetworkUndefine(network) == 0) { vshPrint(ctl, _("Network %s has been undefined\n"), name); } else { vshError(ctl, _("Failed to undefine network %s"), name); ret = false; } virNetworkFree(network); return ret; } /* * "net-uuid" command */ static const vshCmdInfo info_network_uuid[] = { {"help", N_("convert a network name to network UUID")}, {"desc", ""}, {NULL, NULL} }; static const vshCmdOptDef opts_network_uuid[] = { {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name")}, {NULL, 0, 0, NULL} }; static bool cmdNetworkUuid(vshControl *ctl, const vshCmd *cmd) { virNetworkPtr network; char uuid[VIR_UUID_STRING_BUFLEN]; if (!(network = vshCommandOptNetworkBy(ctl, cmd, NULL, VSH_BYNAME))) return false; if (virNetworkGetUUIDString(network, uuid) != -1) vshPrint(ctl, "%s\n", uuid); else vshError(ctl, "%s", _("failed to get network UUID")); virNetworkFree(network); return true; } /* * "net-edit" command */ static const vshCmdInfo info_network_edit[] = { {"help", N_("edit XML configuration for a network")}, {"desc", N_("Edit the XML configuration for a network.")}, {NULL, NULL} }; static const vshCmdOptDef opts_network_edit[] = { {"network", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network name or uuid")}, {NULL, 0, 0, NULL} }; static char *vshNetworkGetXMLDesc(virNetworkPtr network) { unsigned int flags = VIR_NETWORK_XML_INACTIVE; char *doc = virNetworkGetXMLDesc(network, flags); if (!doc && last_error->code == VIR_ERR_INVALID_ARG) { /* The server side libvirt doesn't support * VIR_NETWORK_XML_INACTIVE, so retry without it. */ vshResetLibvirtError(); flags &= ~VIR_NETWORK_XML_INACTIVE; doc = virNetworkGetXMLDesc(network, flags); } return doc; } static bool cmdNetworkEdit(vshControl *ctl, const vshCmd *cmd) { bool ret = false; virNetworkPtr network = NULL; virNetworkPtr network_edited = NULL; network = vshCommandOptNetwork(ctl, cmd, NULL); if (network == NULL) goto cleanup; #define EDIT_GET_XML vshNetworkGetXMLDesc(network) #define EDIT_NOT_CHANGED \ vshPrint(ctl, _("Network %s XML configuration not changed.\n"), \ virNetworkGetName(network)); \ ret = true; goto edit_cleanup; #define EDIT_DEFINE \ (network_edited = virNetworkDefineXML(ctl->conn, doc_edited)) #define EDIT_FREE \ if (network_edited) \ virNetworkFree(network_edited); #include "virsh-edit.c" vshPrint(ctl, _("Network %s XML configuration edited.\n"), virNetworkGetName(network_edited)); ret = true; cleanup: if (network) virNetworkFree(network); if (network_edited) virNetworkFree(network_edited); return ret; } const vshCmdDef networkCmds[] = { {"net-autostart", cmdNetworkAutostart, opts_network_autostart, info_network_autostart, 0}, {"net-create", cmdNetworkCreate, opts_network_create, info_network_create, 0}, {"net-define", cmdNetworkDefine, opts_network_define, info_network_define, 0}, {"net-destroy", cmdNetworkDestroy, opts_network_destroy, info_network_destroy, 0}, {"net-dumpxml", cmdNetworkDumpXML, opts_network_dumpxml, info_network_dumpxml, 0}, {"net-edit", cmdNetworkEdit, opts_network_edit, info_network_edit, 0}, {"net-info", cmdNetworkInfo, opts_network_info, info_network_info, 0}, {"net-list", cmdNetworkList, opts_network_list, info_network_list, 0}, {"net-name", cmdNetworkName, opts_network_name, info_network_name, 0}, {"net-start", cmdNetworkStart, opts_network_start, info_network_start, 0}, {"net-undefine", cmdNetworkUndefine, opts_network_undefine, info_network_undefine, 0}, {"net-uuid", cmdNetworkUuid, opts_network_uuid, info_network_uuid, 0}, {NULL, NULL, NULL, NULL, 0} };