On 09/04/2012 11:55 AM, Osier Yang wrote: > tools/virsh-network.c: > * vshNetworkSorter to sort networks by name > > * vshNetworkListFree to free the network objects list. > > * vshNetworkListCollect to collect the network objects, trying > to use new API first, fall back to older APIs if it's not supported. > > * New options --persistent, --transient, --autostart, --no-autostart, > for net-list, and new field 'Persistent' for its output. > > tools/virsh.pod: > * Add documents for the new options. > --- > tools/virsh-network.c | 352 ++++++++++++++++++++++++++++++++++++------------ > tools/virsh.pod | 12 ++- > 2 files changed, 275 insertions(+), 89 deletions(-) > > diff --git a/tools/virsh-network.c b/tools/virsh-network.c > index db204af..f6623ff 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" I've gotta say that (as discussed before) I don't like including something from the conf directory here. I think it's the case that this is only being done so that virsh can provide the new functionality even when only the old API is available, but I think it should be done in a self-contained manner, at least partly because people will look to virsh as an example of how to use the libvirt API. I guess I'm okay with leaving it this way for now, but I think it really needs to be cleaned up. > > 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)); This use of strcasecmp mmade me go back to verify that case *is* significant in the rest of the network_conf code (so, for example, you can have both a network named "ABC" and one named "Abc"). It's still useful to have strcasecmp here, though, as it makes the ordering ignore case, which is a "good thing"(tm). > +} > + > +struct vshNetworkList { > + virNetworkPtr *nets; > + size_t nnets; > +}; > +typedef struct vshNetworkList *vshNetworkListPtr; The fact that you're doing this leaves me wondering if maybe virNetworkList should be a part of the API (along with a "virNetworkListFree()" as I've previously mentioned). After all, the fact that this first example of using the new API is doing this is a reasonably good indicator that future users will be copying the same code. > + > +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) */ Well, okay, I'll mention this incorrect version number, because it's a different number than the others. > + vshResetLibvirtError(); As was pointed out in the nwfilter patches, this 2nd vshResetLibvirtError() is redundant; you only need it in one place or the other, but not both. > + > + /* 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"); Do you think we need to worry about adding a new column onto the output having an adverse affect on existing scripts that parse the output? > > - 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 9aeb47e..c806335 100644 > --- a/tools/virsh.pod > +++ b/tools/virsh.pod > @@ -1933,10 +1933,20 @@ variables, and defaults to C<vi>. > Returns basic information about the I<network> object. > > =item B<net-list> [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<net-name> I<network-UUID> > In the interest of having the API in the release, I would be okay with pushing as-is, but would rather have the questions above cleared up first. -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list