This patch adds a new --details option to the virsh vol-list command, making its output more useful to people who use virsh for significant lengths of time. Addresses BZ # 605543 https://bugzilla.redhat.com/show_bug.cgi?id=605543 --- This new version of the patch uses the existing virsh output format when the --details option isn't given, maintaining backwards compatibility for existing scripts. When the new --details option is given though, the additional info is displayed and all columns are sized to their widest string. Output from the new option (hopefully this doesn't wrap): virsh # vol-list default Name Path ----------------------------------------- CentOS-5.5-x86_64-bin-DVD-1of2.iso /var/lib/libvirt/images/CentOS-5.5-x86_64-bin-DVD-1of2.iso CentOS-5.5-x86_64-bin-DVD-2of2.iso /var/lib/libvirt/images/CentOS-5.5-x86_64-bin-DVD-2of2.iso virsh # vol-list default --details Name Path Type Capacity Allocation --------------------------------------------------------------------------------------------------------------------------- CentOS-5.5-x86_64-bin-DVD-1of2.iso /var/lib/libvirt/images/CentOS-5.5-x86_64-bin-DVD-1of2.iso file 4.09 GB 4.10 GB CentOS-5.5-x86_64-bin-DVD-2of2.iso /var/lib/libvirt/images/CentOS-5.5-x86_64-bin-DVD-2of2.iso file 412.33 MB 412.74 MB virsh # vol-list tmp Name Path ----------------------------------------- disk1.img /tmp/images/disk1.img disk2.img /tmp/images/disk2.img disk3.img /tmp/images/disk3.img disk4.img /tmp/images/disk4.img disk5.img /tmp/images/disk5.img disk6.img /tmp/images/disk6.img virsh # vol-list tmp --details Name Path Type Capacity Allocation ------------------------------------------------------------ disk1.img /tmp/images/disk1.img file 20.00 GB 136.00 KB disk2.img /tmp/images/disk2.img file 20.00 GB 136.00 KB disk3.img /tmp/images/disk3.img file 20.00 GB 136.00 KB disk4.img /tmp/images/disk4.img file 20.00 GB 136.00 KB disk5.img /tmp/images/disk5.img file 20.00 GB 136.00 KB disk6.img /tmp/images/disk6.img file 20.00 GB 136.00 KB virsh # Much nicer to use when pools have a bunch of luns in them. :) tools/virsh.c | 225 ++++++++++++++++++++++++++++++++++++++++++++++++------ tools/virsh.pod | 4 +- 2 files changed, 203 insertions(+), 26 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 7261d19..2a9c353 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -6075,67 +6075,242 @@ static const vshCmdInfo info_vol_list[] = { static const vshCmdOptDef opts_vol_list[] = { {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pool name or uuid")}, + {"details", VSH_OT_BOOL, 0, N_("display extended details for volumes")}, {NULL, 0, 0, NULL} }; static int cmdVolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) { + virStorageVolInfo volumeInfo; virStoragePoolPtr pool; - int maxactive = 0, i; + int details = vshCommandOptBool(cmd, "details"); + int maxAlloc = 0, maxCap = 0, maxName = 0; + int maxPath = 0, maxType = 0; + int numVolumes = 0, i; + int stringLength = 0; + double val; + const char *unit; char **activeNames = NULL; + struct volInfoText { + char *allocation; + char *capacity; + char *path; + char *type; + }; + struct volInfoText **volInfoTexts; + /* Check the connection to libvirtd daemon is still working */ if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) return FALSE; + /* Look up the pool information given to us by the user */ if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL))) return FALSE; - maxactive = virStoragePoolNumOfVolumes(pool); - if (maxactive < 0) { + /* Determine the number of volumes in the pool */ + numVolumes = virStoragePoolNumOfVolumes(pool); + if (numVolumes < 0) { virStoragePoolFree(pool); vshError(ctl, "%s", _("Failed to list active vols")); return FALSE; } - if (maxactive) { - activeNames = vshMalloc(ctl, sizeof(char *) * maxactive); - if ((maxactive = virStoragePoolListVolumes(pool, activeNames, - maxactive)) < 0) { + /* Retrieve the list of volume names in the pool */ + if (numVolumes) { + activeNames = vshMalloc(ctl, sizeof(char *) * numVolumes); + if ((numVolumes = virStoragePoolListVolumes(pool, activeNames, + numVolumes)) < 0) { vshError(ctl, "%s", _("Failed to list active vols")); VIR_FREE(activeNames); virStoragePoolFree(pool); return FALSE; } - qsort(&activeNames[0], maxactive, sizeof(char *), namesorter); + /* Sort the volume names */ + qsort(&activeNames[0], numVolumes, sizeof(char *), namesorter); } - vshPrintExtra(ctl, "%-20s %-40s\n", _("Name"), _("Path")); - vshPrintExtra(ctl, "-----------------------------------------\n"); - for (i = 0; i < maxactive; i++) { - virStorageVolPtr vol = virStorageVolLookupByName(pool, activeNames[i]); - char *path; + /* Set aside memory for volume information pointers */ + volInfoTexts = vshMalloc(ctl, sizeof(struct volInfoText *) * numVolumes); - /* this kind of work with vols is not atomic operation */ - if (!vol) { - VIR_FREE(activeNames[i]); - continue; + /* Collect the rest of the volume information for display */ + for (i = 0; i < numVolumes; i++) { + /* Retrieve volume info */ + virStorageVolPtr vol = virStorageVolLookupByName(pool, + activeNames[i]); + + /* Allocate memory for one row of volume info */ + volInfoTexts[i] = vshMalloc(ctl, sizeof(struct volInfoText)); + + /* Retrieve the volume path */ + if ((volInfoTexts[i]->path = virStorageVolGetPath(vol)) == NULL) { + /* Something went wrong retrieving a volume path, cope with it */ + volInfoTexts[i]->path = vshStrdup(ctl, _("unknown")); } - if ((path = virStorageVolGetPath(vol)) == NULL) { - virStorageVolFree(vol); - continue; + /* Retrieve volume type and sizing information */ + if (virStorageVolGetInfo(vol, &volumeInfo) != 0) { + /* Something went wrong retrieving volume info, cope with it */ + volInfoTexts[i]->allocation = vshStrdup(ctl, _("unknown")); + volInfoTexts[i]->capacity = vshStrdup(ctl, _("unknown")); + volInfoTexts[i]->type = vshStrdup(ctl, _("unknown")); + } else { + /* Convert the returned volume info into output strings */ + virBuffer bufStr = VIR_BUFFER_INITIALIZER; + + /* Volume type */ + if (volumeInfo.type == VIR_STORAGE_VOL_FILE) + volInfoTexts[i]->type = vshStrdup(ctl, _("file")); + else + volInfoTexts[i]->type = vshStrdup(ctl, _("block")); + + // The capacity value to output + val = prettyCapacity(volumeInfo.capacity, &unit); + virBufferVSprintf(&bufStr, "%.2lf %s", val, unit); + volInfoTexts[i]->capacity = + vshStrdup(ctl, virBufferContentAndReset(&bufStr)); + + // The allocation value to output + val = prettyCapacity(volumeInfo.allocation, &unit); + virBufferVSprintf(&bufStr, "%.2lf %s", val, unit); + volInfoTexts[i]->allocation = + vshStrdup(ctl, virBufferContentAndReset(&bufStr)); } + /** Remember the longest output size of each string, ** + ** so we can use a printf style output format template ** + ** later on for both the header and volume info rows **/ + + /* Keep the length of name string if longest so far */ + stringLength = strlen(activeNames[i]); + if (stringLength > maxName) + maxName = stringLength; - vshPrint(ctl, "%-20s %-40s\n", - virStorageVolGetName(vol), - path); - VIR_FREE(path); + /* Keep the length of path string if longest so far */ + stringLength = strlen(volInfoTexts[i]->path); + if (stringLength > maxPath) + maxPath = stringLength; + + /* Keep the length of type string if longest so far */ + stringLength = strlen(volInfoTexts[i]->type); + if (stringLength > maxType) + maxType = stringLength; + + /* Keep the length of capacity string if longest so far */ + stringLength = strlen(volInfoTexts[i]->capacity); + if (stringLength > maxCap) + maxCap = stringLength; + + /* Keep the length of allocation string if longest so far */ + stringLength = strlen(volInfoTexts[i]->allocation); + if (stringLength > maxAlloc) + maxAlloc = stringLength; + + /* Cleanup memory allocation */ virStorageVolFree(vol); - VIR_FREE(activeNames[i]); } + + /** If the --details option wasn't selected, we output the volume ** + ** info using the fixed string format from previous versions to ** + ** maintain backward compatibility. **/ + + /* Output basic info then return if --details option not selected */ + if (!details) { + /* The old output format */ + vshPrintExtra(ctl, "%-20s %-40s\n", _("Name"), _("Path")); + vshPrintExtra(ctl, "-----------------------------------------\n"); + for (i = 0; i < numVolumes; i++) { + vshPrint(ctl, "%-20s %-40s\n", activeNames[i], + volInfoTexts[i]->path); + + /* Cleanup the memory for this volume row */ + VIR_FREE(volInfoTexts[i]->path); + VIR_FREE(volInfoTexts[i]->type); + VIR_FREE(volInfoTexts[i]->capacity); + VIR_FREE(volInfoTexts[i]->allocation); + VIR_FREE(volInfoTexts[i]); + } + + /* Cleanup remaining memory and return */ + VIR_FREE(volInfoTexts); + VIR_FREE(activeNames); + virStoragePoolFree(pool); + return TRUE; + } + + /** We only get here if the --details option was selected. ** + ** Column now resize to the longest string to be output. **/ + + /* Determine the length of the header strings. These must be + * calculated because we may be outputing a translated heading + */ + /* Use the length of name header string if it's longest */ + stringLength = strlen(_("Name")); + if (stringLength > maxName) + maxName = stringLength; + + /* Use the length of path header string if it's longest */ + stringLength = strlen(_("Path")); + if (stringLength > maxPath) + maxPath = stringLength; + + /* Use the length of type header string if it's longest */ + stringLength = strlen(_("Type")); + if (stringLength > maxType) + maxType = stringLength; + + /* Use the length of capacity header string if it's longest */ + stringLength = strlen(_("Capacity")); + if (stringLength > maxCap) + maxCap = stringLength; + + /* Use the length of allocation header string if it's longest */ + stringLength = strlen(_("Allocation")); + if (stringLength > maxAlloc) + maxAlloc = stringLength; + + /* Display the string lengths for debugging */ + vshDebug(ctl, 5, "Longest name string = %d chars\n", maxName); + vshDebug(ctl, 5, "Longest path string = %d chars\n", maxPath); + vshDebug(ctl, 5, "Longest type string = %d chars\n", maxType); + vshDebug(ctl, 5, "Longest capacity string = %d chars\n", maxCap); + vshDebug(ctl, 5, "Longest allocation string = %d chars\n", maxAlloc); + + /* Create the output template */ + char *outputStr; + virBuffer bufStr = VIR_BUFFER_INITIALIZER; + virBufferVSprintf(&bufStr, "%%-%us %%-%us %%-%us %%-%us %%-%us\n", + maxName, maxPath, maxType, maxCap, maxAlloc); + outputStr = virBufferContentAndReset(&bufStr); + + /* Display the header */ + vshPrint(ctl, outputStr, _("Name"), _("Path"), _("Type"), + ("Capacity"), _("Allocation")); + for (i = maxName + maxPath + maxType + maxCap + maxAlloc + 8; i > 0; i--) + vshPrintExtra(ctl, "-"); + vshPrintExtra(ctl, "\n"); + + /* Display the volume info rows */ + for (i = 0; i < numVolumes; i++) { + vshPrint(ctl, outputStr, + activeNames[i], + volInfoTexts[i]->path, + volInfoTexts[i]->type, + volInfoTexts[i]->capacity, + volInfoTexts[i]->allocation); + + /* Cleanup the memory for this volume row */ + VIR_FREE(volInfoTexts[i]->path); + VIR_FREE(volInfoTexts[i]->type); + VIR_FREE(volInfoTexts[i]->capacity); + VIR_FREE(volInfoTexts[i]->allocation); + VIR_FREE(volInfoTexts[i]); + } + + /* Cleanup remaining memory */ + VIR_FREE(outputStr); + VIR_FREE(volInfoTexts); VIR_FREE(activeNames); virStoragePoolFree(pool); return TRUE; diff --git a/tools/virsh.pod b/tools/virsh.pod index 7c75edc..a217cba 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -839,10 +839,12 @@ Returns basic information about the given storage volume. I<--pool> I<pool-or-uuid> is the name or UUID of the storage pool the volume is in. I<vol-name-or-key-or-path> is the name or key or path of the volume to return information for. -=item B<vol-list> I<--pool> I<pool-or-uuid> +=item B<vol-list> [optional I<--pool>] I<pool-or-uuid> optional I<--details> Return the list of volumes in the given storage pool. I<--pool> I<pool-or-uuid> is the name or UUID of the storage pool. +The I<--details> option instructs virsh to additionally display volume +type and capacity related information where available. =item B<vol-pool> [optional I<--uuid>] I<vol-key-or-path> -- 1.7.0.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list