[libvirt] [PATCH] virsh: Enhance list command to ease creation of shell scripts

This patch adds new options to the "virsh list" command enabling filtering of persistent and transient domains along with the option to print only UUIDs or names of domains instead of printing the table. Option --name prints domain names (one per line) instead of the default table. Similarly --uuid prints domain's UUID. The option --table is an alias for the default behavior. Aditionaly --persistent and/or --transient may be specified to filter the output of domains. --- tools/virsh.c | 176 ++++++++++++++++++++++++++++++++++++++++-------------- tools/virsh.pod | 20 ++++++- 2 files changed, 148 insertions(+), 48 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 66ba61c..10160de 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -888,6 +888,11 @@ static const vshCmdInfo info_list[] = { static const vshCmdOptDef opts_list[] = { {"inactive", VSH_OT_BOOL, 0, N_("list inactive domains")}, {"all", VSH_OT_BOOL, 0, N_("list inactive & active domains")}, + {"transient", VSH_OT_BOOL, 0, N_("list transient domains")}, + {"persistent", VSH_OT_BOOL, 0, N_("list persistent domains")}, + {"uuid", VSH_OT_BOOL, 0, N_("list uuid's only")}, + {"name", VSH_OT_BOOL, 0, N_("list domain names only")}, + {"table", VSH_OT_BOOL, 0, N_("list table (default)")}, {"managed-save", VSH_OT_BOOL, 0, N_("mark domains with managed save state")}, {"title", VSH_OT_BOOL, 0, N_("show short domain description")}, @@ -905,13 +910,41 @@ cmdList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) char **names = NULL; int maxname = 0; bool managed = vshCommandOptBool(cmd, "managed-save"); - bool desc = vshCommandOptBool(cmd, "title"); + bool optTitle = vshCommandOptBool(cmd, "title"); + bool optTable = vshCommandOptBool(cmd, "table"); + bool optUUID = vshCommandOptBool(cmd, "uuid"); + bool optName = vshCommandOptBool(cmd, "name"); + bool optPersistent = vshCommandOptBool(cmd, "persistent"); + bool optTransient = vshCommandOptBool(cmd, "transient"); + bool persistUsed = true; + virDomainPtr dom = NULL; char *title; + char uuid[VIR_UUID_STRING_BUFLEN]; int state; + int persistent; bool ret = false; inactive |= all; + /* process arguments */ + if (!optPersistent && !optTransient) { + optPersistent = true; + optTransient = true; + persistUsed = false; + } + + if ((optTable && optName) || + (optTable && optUUID) || + (optName && optUUID)) { + vshError(ctl, "%s", + _("Only one argument from --table, --name and --uuid" + "may be specified.")); + return false; + } + + if (!optUUID && !optName) + optTable = true; + if (!vshConnectionUsability(ctl, ctl->conn)) return false; @@ -924,14 +957,15 @@ cmdList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) if (maxid) { ids = vshMalloc(ctl, sizeof(int) * maxid); - if ((maxid = virConnectListDomains(ctl->conn, &ids[0], maxid)) < 0) { + if ((maxid = virConnectListDomains(ctl->conn, ids, maxid)) < 0) { vshError(ctl, "%s", _("Failed to list active domains")); goto cleanup; } - qsort(&ids[0], maxid, sizeof(int), idsorter); + qsort(ids, maxid, sizeof(int), idsorter); } } + if (inactive) { maxname = virConnectNumOfDefinedDomains(ctl->conn); if (maxname < 0) { @@ -941,7 +975,9 @@ cmdList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) if (maxname) { names = vshMalloc(ctl, sizeof(char *) * maxname); - if ((maxname = virConnectListDefinedDomains(ctl->conn, names, maxname)) < 0) { + if ((maxname = virConnectListDefinedDomains(ctl->conn, + names, + maxname)) < 0) { vshError(ctl, "%s", _("Failed to list inactive domains")); goto cleanup; } @@ -950,77 +986,125 @@ cmdList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) } } - if (desc) { - vshPrintExtra(ctl, "%-5s %-30s %-10s %s\n", _("Id"), _("Name"), _("State"), _("Title")); - vshPrintExtra(ctl, "-----------------------------------------------------------\n"); - } else { - vshPrintExtra(ctl, " %-5s %-30s %s\n", _("Id"), _("Name"), _("State")); - vshPrintExtra(ctl, "----------------------------------------------------\n"); + /* print table header in legacy mode */ + if (optTable) { + vshPrintExtra(ctl, " %-5s %-30s %-10s", + _("Id"), _("Name"), _("State")); + if (optTitle) + vshPrintExtra(ctl, "%-20s", _("Title")); + + vshPrintExtra(ctl, "\n" + "-----------------------------------------------------------\n"); } for (i = 0; i < maxid; i++) { - virDomainPtr dom = virDomainLookupByID(ctl->conn, ids[i]); + dom = virDomainLookupByID(ctl->conn, ids[i]); /* this kind of work with domains is not atomic operation */ if (!dom) continue; - if (desc) { - if (!(title = vshGetDomainDescription(ctl, dom, true, 0))) + if (persistUsed) { + persistent = virDomainIsPersistent(dom); + if (persistent < 0) { + vshError(ctl, "%s", + _("Failed to determine domain's persistent state")); goto cleanup; + } - vshPrint(ctl, "%-5d %-30s %-10s %s\n", - virDomainGetID(dom), - virDomainGetName(dom), - _(vshDomainStateToString(vshDomainState(ctl, dom, NULL))), - title); - VIR_FREE(title); - } else { - vshPrint(ctl, " %-5d %-30s %s\n", - virDomainGetID(dom), - virDomainGetName(dom), - _(vshDomainStateToString(vshDomainState(ctl, dom, NULL)))); + if ((persistent && !optPersistent) || + (!persistent && !optTransient)) { + virDomainFree(dom); + dom = NULL; + continue; + } } + + if (optTable) { + vshPrint(ctl, " %-5d %-30s %-10s", + virDomainGetID(dom), + virDomainGetName(dom), + _(vshDomainStateToString(vshDomainState(ctl, dom, NULL)))); + + if (optTitle) { + if (!(title = vshGetDomainDescription(ctl, dom, true, 0))) + goto cleanup; + + vshPrint(ctl, "%-20s", title); + VIR_FREE(title); + } + + vshPrint(ctl, "\n"); + } else if (optUUID) { + if (virDomainGetUUIDString(dom, uuid) < 0) { + vshError(ctl, "%s", + _("Failed to get domain's UUID")); + goto cleanup; + } + vshPrint(ctl, "%s\n", uuid); + } else if (optName) { + vshPrint(ctl, "%s\n", virDomainGetName(dom)); + } + virDomainFree(dom); + dom = NULL; } + for (i = 0; i < maxname; i++) { - virDomainPtr dom = virDomainLookupByName(ctl->conn, names[i]); + dom = virDomainLookupByName(ctl->conn, names[i]); /* this kind of work with domains is not atomic operation */ - if (!dom) { - VIR_FREE(names[i]); + if (!dom) continue; - } - state = vshDomainState(ctl, dom, NULL); - if (managed && state == VIR_DOMAIN_SHUTOFF && - virDomainHasManagedSaveImage(dom, 0) > 0) - state = -2; + if (!optPersistent) { + virDomainFree(dom); + dom = NULL; + continue; + } - if (desc) { - if (!(title = vshGetDomainDescription(ctl, dom, true, 0))) - goto cleanup; + if (optTable) { + state = vshDomainState(ctl, dom, NULL); + if (managed && state == VIR_DOMAIN_SHUTOFF && + virDomainHasManagedSaveImage(dom, 0) > 0) + state = -2; - vshPrint(ctl, "%-5s %-30s %-10s %s\n", - "-", - names[i], - state == -2 ? _("saved") : _(vshDomainStateToString(state)), - title); - VIR_FREE(title); - } else { - vshPrint(ctl, " %-5s %-30s %s\n", + vshPrint(ctl, " %-5s %-30s %-10s", "-", names[i], state == -2 ? _("saved") : _(vshDomainStateToString(state))); - } - virDomainFree(dom); - VIR_FREE(names[i]); + if (optTitle) { + if (!(title = vshGetDomainDescription(ctl, dom, true, 0))) + goto cleanup; + + vshPrint(ctl, "%-20s", title); + VIR_FREE(title); + } + + vshPrint(ctl, "\n"); + } else if (optUUID) { + if (virDomainGetUUIDString(dom, uuid) < 0) { + vshError(ctl, "%s", + _("Failed to get domain's UUID")); + goto cleanup; + } + vshPrint(ctl, "%s\n", uuid); + } else if (optName) { + vshPrint(ctl, "%s\n", names[i]); + } + + virDomainFree(dom); + dom = NULL; } ret = true; cleanup: + if (dom) + virDomainFree(dom); VIR_FREE(ids); + for (i = 0; i < maxname; i++) + VIR_FREE(names[i]); VIR_FREE(names); return ret; } diff --git a/tools/virsh.pod b/tools/virsh.pod index 0d5a41d..aa29c00 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -280,6 +280,8 @@ The XML also show the NUMA topology information if available. Inject NMI to the guest. =item B<list> [I<--inactive> | I<--all>] [I<--managed-save>] [I<--title>] + [I<--name> | <I--table> | <I--uuid>] [I<--persistent>] + [I<--transient>] Prints information about existing domains. If no options are specified it prints out information about running domains. @@ -348,10 +350,24 @@ crashed. If I<--managed-save> is specified, then domains that have managed save state (only possible if they are in the B<shut off> state) will -instead show as B<saved> in the listing. +instead show as B<saved> in the listing. This flag is usable only with the +default I<--table> output. + +If I<--name> is specified, domain names are printed instead of the table +formated one per line. If I<--uuid> is specified domain's UUID's are printed +instead of names. Flag I<--table> specifies that the legacy table-formated +output should be used. This is the default. All of these are mutually +exclusive. + +Flag I<--persistent> specifies that persistent domains should be printed. +Similarly I<--transiet> enables output of transient domains. These flags +may be combined. Default behavior is as though both were specifiedi (Although +if any of these flags is specified, the check for the persistence state is done +and may fail. If none of these flags is specified, the check is skipped). If I<--title> is specified, then the domain note is printed. The output then -the output looks as follows. +the output looks as follows. This flag is usable only with the default +I<--table> output. B<virsh> list --title Id Name State Title -- 1.7.3.4

On 02/21/2012 09:05 AM, Peter Krempa wrote:
This patch adds new options to the "virsh list" command enabling filtering of persistent and transient domains along with the option to print only UUIDs or names of domains instead of printing the table.
Option --name prints domain names (one per line) instead of the default table. Similarly --uuid prints domain's UUID. The option --table is an alias for the default behavior.
I wonder if we also need --id. --table prints out the id, but then you have to parse out the first field. On the other hand, id is only valid for running guests, so --id + --persistent + --inactive would have no output unless we make it an error; and we are at least guaranteed that the id is always parseable (it is names that might have embedded spacing, and thus difficult to parse out of a table). At any rate, I'm less worried about id; getting just uuid or name is a great improvement in its own right.
Aditionaly --persistent and/or --transient may be specified to filter
s/Aditionaly/Additionally/
the output of domains. --- tools/virsh.c | 176 ++++++++++++++++++++++++++++++++++++++++-------------- tools/virsh.pod | 20 ++++++- 2 files changed, 148 insertions(+), 48 deletions(-)
Thanks for picking up on my suggestions, and implementing it so quickly!
+ + if ((optTable && optName) || + (optTable && optUUID) || + (optName && optUUID)) {
I think it might be easier to write this as: if (optTable + optName + optUUID > 1) {
+ vshError(ctl, "%s", + _("Only one argument from --table, --name and --uuid" + "may be specified."));
which would also make things easier if you later add an --id option.
@@ -950,77 +986,125 @@ cmdList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) } }
- if (desc) { - vshPrintExtra(ctl, "%-5s %-30s %-10s %s\n", _("Id"), _("Name"), _("State"), _("Title")); - vshPrintExtra(ctl, "-----------------------------------------------------------\n"); - } else { - vshPrintExtra(ctl, " %-5s %-30s %s\n", _("Id"), _("Name"), _("State")); - vshPrintExtra(ctl, "----------------------------------------------------\n");
The old style printed a variable-length ---- line, depending on whether title was in the mix...
+ /* print table header in legacy mode */ + if (optTable) { + vshPrintExtra(ctl, " %-5s %-30s %-10s", + _("Id"), _("Name"), _("State")); + if (optTitle) + vshPrintExtra(ctl, "%-20s", _("Title")); + + vshPrintExtra(ctl, "\n" + "-----------------------------------------------------------\n");
but your new version prints a fixed-width ---- line as if title were always present. Not necessarily a show-stopper, but worth thinking about.
+++ b/tools/virsh.pod @@ -280,6 +280,8 @@ The XML also show the NUMA topology information if available. Inject NMI to the guest.
=item B<list> [I<--inactive> | I<--all>] [I<--managed-save>] [I<--title>] + [I<--name> | <I--table> | <I--uuid>] [I<--persistent>]
I think we have been using {} to indicate mutually exclusive choices. Maybe: {I<--name> | I<--uuid> | [I<--table>]} as a way to show there are three modes, and --table is the default mode. In fact, maybe it's worth a slight change-up to the patch, to expose: virsh list --output={table|uuid|name} as a single option with a mandatory argument, rather than having three separate flag arguments, where if --output is omitted, you default to --output=table. But that's all bike-shedding; what you have works, so I don't think it is worth redoing anything.
+ +If I<--name> is specified, domain names are printed instead of the table +formated one per line. If I<--uuid> is specified domain's UUID's are printed
s/formated/formatted/
+instead of names. Flag I<--table> specifies that the legacy table-formated
s/formated/formatted/
+output should be used. This is the default. All of these are mutually +exclusive. + +Flag I<--persistent> specifies that persistent domains should be printed. +Similarly I<--transiet> enables output of transient domains. These flags
s/transiet/transient/
+may be combined. Default behavior is as though both were specifiedi (Although
s/specifiedi/specified./
+if any of these flags is specified, the check for the persistence state is done
s/(Although if/(Note that if/ ACK with the virsh.pod typo fixes. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

The 21/02/12, Eric Blake wrote:
On 02/21/2012 09:05 AM, Peter Krempa wrote:
@@ -950,77 +986,125 @@ cmdList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) } }
- if (desc) { - vshPrintExtra(ctl, "%-5s %-30s %-10s %s\n", _("Id"), _("Name"), _("State"), _("Title")); - vshPrintExtra(ctl, "-----------------------------------------------------------\n"); - } else { - vshPrintExtra(ctl, " %-5s %-30s %s\n", _("Id"), _("Name"), _("State")); - vshPrintExtra(ctl, "----------------------------------------------------\n");
The old style printed a variable-length ---- line, depending on whether title was in the mix...
+ /* print table header in legacy mode */ + if (optTable) { + vshPrintExtra(ctl, " %-5s %-30s %-10s", + _("Id"), _("Name"), _("State")); + if (optTitle) + vshPrintExtra(ctl, "%-20s", _("Title")); + + vshPrintExtra(ctl, "\n" + "-----------------------------------------------------------\n");
but your new version prints a fixed-width ---- line as if title were always present. Not necessarily a show-stopper, but worth thinking about.
BTW, I find that the %-ns format is not easy to parse from scripts. It would be easier with raw variable values and a dedicated separator like a tabulation. Human and scripts expectations are so... different! :-) -- Nicolas Sebrecht

On 02/21/2012 06:05 PM, Eric Blake wrote:
On 02/21/2012 09:05 AM, Peter Krempa wrote:
--- tools/virsh.c | 176 ++++++++++++++++++++++++++++++++++++++++-------------- tools/virsh.pod | 20 ++++++- 2 files changed, 148 insertions(+), 48 deletions(-)
Thanks for picking up on my suggestions, and implementing it so quickly!
Well it makes it a lot easier to write shell scripts :)
+ + if ((optTable&& optName) || + (optTable&& optUUID) || + (optName&& optUUID)) {
I think it might be easier to write this as:
if (optTable + optName + optUUID> 1) {
That's elegant.
- if (desc) { - vshPrintExtra(ctl, "%-5s %-30s %-10s %s\n", _("Id"), _("Name"), _("State"), _("Title")); - vshPrintExtra(ctl, "-----------------------------------------------------------\n"); - } else { - vshPrintExtra(ctl, " %-5s %-30s %s\n", _("Id"), _("Name"), _("State")); - vshPrintExtra(ctl, "----------------------------------------------------\n");
The old style printed a variable-length ---- line, depending on whether title was in the mix...
It also broke the tests.
+ /* print table header in legacy mode */ + if (optTable) { + vshPrintExtra(ctl, " %-5s %-30s %-10s", + _("Id"), _("Name"), _("State")); + if (optTitle) + vshPrintExtra(ctl, "%-20s", _("Title")); + + vshPrintExtra(ctl, "\n" + "-----------------------------------------------------------\n");
but your new version prints a fixed-width ---- line as if title were always present. Not necessarily a show-stopper, but worth thinking about.
ACK with the virsh.pod typo fixes.
I fixed the typos and modified the default output to comply with the tests and pushed. Thanks. Peter
participants (3)
-
Eric Blake
-
Nicolas Sebrecht
-
Peter Krempa