diff --git a/tools/virsh-network.c b/tools/virsh-network.c index db204af9277148a5c557863c0d709dd61fee9ff6..f6623ff500a4b3e001253bc60660eaa641edd3f4 100644 --- a/tools/virsh-network.c +++ b/tools/virsh-network.c @@ -36,6 +36,7 @@ #include "memory.h" #include "util.h" #include "xml.h" +#include "conf/network_conf.h" virNetworkPtr vshCommandOptNetworkBy(vshControl *ctl, const vshCmd *cmd, @@ -342,6 +343,225 @@ cmdNetworkInfo(vshControl *ctl, const vshCmd *cmd) 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 (!MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) || + 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 (!MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) || + 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 (!MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) || + 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 (!MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) || + 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 (MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_PERSISTENT)) { + if ((persistent = virNetworkIsPersistent(net)) < 0) { + vshError(ctl, "%s", _("Failed to get network persistence info")); + goto cleanup; + } + + if (!((MATCH(VIR_CONNECT_LIST_NETWORKS_PERSISTENT) && persistent) || + (MATCH(VIR_CONNECT_LIST_NETWORKS_TRANSIENT) && !persistent))) + goto remove_entry; + } + + /* autostart filter */ + if (MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_AUTOSTART)) { + if (virNetworkGetAutostart(net, &autostart) < 0) { + vshError(ctl, "%s", _("Failed to get network autostart state")); + goto cleanup; + } + + if (!((MATCH(VIR_CONNECT_LIST_NETWORKS_AUTOSTART) && autostart) || + (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 */ @@ -354,114 +574,70 @@ static const vshCmdInfo info_network_list[] = { 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 active = !inactive || all; - int maxactive = 0, maxinactive = 0, i; - char **activeNames = NULL, **inactiveNames = NULL; - inactive |= all; - - if (active) { - maxactive = virConnectNumOfNetworks(ctl->conn); - if (maxactive < 0) { - vshError(ctl, "%s", _("Failed to list active networks")); - return false; - } - if (maxactive) { - activeNames = vshMalloc(ctl, sizeof(char *) * maxactive); - - if ((maxactive = virConnectListNetworks(ctl->conn, activeNames, - maxactive)) < 0) { - vshError(ctl, "%s", _("Failed to list active networks")); - VIR_FREE(activeNames); - return false; - } + 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; - qsort(&activeNames[0], maxactive, sizeof(char *), vshNameSorter); - } - } - if (inactive) { - maxinactive = virConnectNumOfDefinedNetworks(ctl->conn); - if (maxinactive < 0) { - vshError(ctl, "%s", _("Failed to list inactive networks")); - VIR_FREE(activeNames); - return false; - } - if (maxinactive) { - inactiveNames = vshMalloc(ctl, sizeof(char *) * maxinactive); - - if ((maxinactive = - virConnectListDefinedNetworks(ctl->conn, inactiveNames, - maxinactive)) < 0) { - vshError(ctl, "%s", _("Failed to list inactive networks")); - VIR_FREE(activeNames); - VIR_FREE(inactiveNames); - return false; - } + if (inactive) + flags = VIR_CONNECT_LIST_NETWORKS_INACTIVE; - qsort(&inactiveNames[0], maxinactive, sizeof(char*), vshNameSorter); - } - } - vshPrintExtra(ctl, "%-20s %-10s %s\n", _("Name"), _("State"), - _("Autostart")); - vshPrintExtra(ctl, "-----------------------------------------\n"); + if (all) + flags = VIR_CONNECT_LIST_NETWORKS_ACTIVE | + VIR_CONNECT_LIST_NETWORKS_INACTIVE; - for (i = 0; i < maxactive; i++) { - virNetworkPtr network = - virNetworkLookupByName(ctl->conn, activeNames[i]); - const char *autostartStr; - int autostart = 0; + if (persistent) + flags |= VIR_CONNECT_LIST_NETWORKS_PERSISTENT; - /* this kind of work with networks is not atomic operation */ - if (!network) { - VIR_FREE(activeNames[i]); - continue; - } + if (transient) + flags |= VIR_CONNECT_LIST_NETWORKS_TRANSIENT; - if (virNetworkGetAutostart(network, &autostart) < 0) - autostartStr = _("no autostart"); - else - autostartStr = autostart ? _("yes") : _("no"); + if (autostart) + flags |= VIR_CONNECT_LIST_NETWORKS_AUTOSTART; - vshPrint(ctl, "%-20s %-10s %-10s\n", - virNetworkGetName(network), - _("active"), - autostartStr); - virNetworkFree(network); - VIR_FREE(activeNames[i]); - } - for (i = 0; i < maxinactive; i++) { - virNetworkPtr network = virNetworkLookupByName(ctl->conn, inactiveNames[i]); - const char *autostartStr; - int autostart = 0; + if (no_autostart) + flags |= VIR_CONNECT_LIST_NETWORKS_NO_AUTOSTART; - /* this kind of work with networks is not atomic operation */ - if (!network) { - VIR_FREE(inactiveNames[i]); - continue; - } + if (!(list = vshNetworkListCollect(ctl, flags))) + return false; + + vshPrintExtra(ctl, "%-20s %-10s %-13s %s\n", _("Name"), _("State"), + _("Autostart"), _("Persistent")); + vshPrintExtra(ctl, "--------------------------------------------------\n"); - if (virNetworkGetAutostart(network, &autostart) < 0) + 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 = autostart ? _("yes") : _("no"); - - vshPrint(ctl, "%-20s %-10s %-10s\n", - inactiveNames[i], - _("inactive"), - autostartStr); + autostartStr = is_autostart ? _("yes") : _("no"); - virNetworkFree(network); - VIR_FREE(inactiveNames[i]); + vshPrint(ctl, "%-20s %-10s %-13s %s\n", + virNetworkGetName(network), + virNetworkIsActive(network) ? _("active") : _("inactive"), + autostartStr, + virNetworkIsPersistent(network) ? _("yes") : _("no")); } - VIR_FREE(activeNames); - VIR_FREE(inactiveNames); + + vshNetworkListFree(list); return true; } diff --git a/tools/virsh.pod b/tools/virsh.pod index 68138e53d995ab939045035039f01ddfd425a55e..559e64dce84c04cf56bc4f1afa0674f1fadd5c5c 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1934,10 +1934,20 @@ variables, and defaults to C. Returns basic information about the I object. =item B [I<--inactive> | I<--all>] + [I<--persistent>] [<--transient>] + [I<--autostart>] [<--no-autostart>] Returns the list of active networks, if I<--all> is specified this will also include defined but inactive networks, if I<--inactive> is specified only the -inactive ones will be listed. +inactive ones will be listed. You may also want to filter the returned networks +by I<--persistent> to list the persitent ones, I<--transient> to list the +transient ones, I<--autostart> to list the ones with autostart enabled, and +I<--no-autostart> to list the ones with autostart disabled. + +NOTE: When talking to older servers, this command is forced to use a series of +API calls with an inherent race, where a pool might not be listed or might appear +more than once if it changed state between calls while the list was being +collected. Newer servers do not have this problem. =item B I