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. --- Output from the new option (hopefully this doesn't wrap): virsh # pool-list Name State Autostart ----------------------------------------- default active yes image_dir active yes tmp active no 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 image_dir Name Path ------------------------------------------------------------------------- snap1.img /export/backend/centos_home/jc/tmp/images/snap1.img testimage1 /export/backend/centos_home/jc/tmp/images/testimage1 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 default --details Name Type Capacity Allocation Path ---------------------------------------------------------------- CentOS-5.5-x86_64-bin-DVD-1of2.iso file 4.09 GB 4.10 GB /var/lib/libvirt/images/CentOS-5.5-x86_64-bin-DVD-1of2.iso CentOS-5.5-x86_64-bin-DVD-2of2.iso file 412.33 MB 412.74 MB /var/lib/libvirt/images/CentOS-5.5-x86_64-bin-DVD-2of2.iso virsh # vol-list image_dir --details Name Type Capacity Allocation Path ---------------------------------------------------------- snap1.img file 20.00 GB 140.00 KB /export/backend/centos_home/jc/tmp/images/snap1.img testimage1 file 20.00 GB 20.02 GB /export/backend/centos_home/jc/tmp/images/testimage1 virsh # vol-list tmp --details Name Type Capacity Allocation Path ------------------------------------------------------------- disk1.img file 20.00 GB 136.00 KB /tmp/images/disk1.img disk2.img file 20.00 GB 136.00 KB /tmp/images/disk2.img disk3.img file 20.00 GB 136.00 KB /tmp/images/disk3.img disk4.img file 20.00 GB 136.00 KB /tmp/images/disk4.img disk5.img file 20.00 GB 136.00 KB /tmp/images/disk5.img disk6.img file 20.00 GB 136.00 KB /tmp/images/disk6.img virsh # Much more practical than running vol-info individually on each volume. tools/virsh.c | 212 ++++++++++++++++++++++++++++++++++++++++++++++++------- tools/virsh.pod | 4 +- 2 files changed, 189 insertions(+), 27 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index afa84e6..7a12e15 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -6047,67 +6047,227 @@ 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 **volumeInfos = NULL; virStoragePoolPtr pool; - int maxactive = 0, i; + int details = vshCommandOptBool(cmd, "details"); + int maxName = 0, maxPath = 0; + int numVolumes = 0, i; char **activeNames = NULL; + char **volumePaths = NULL; + /* 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 */ + volumePaths = vshMalloc(ctl, sizeof(char *) * numVolumes); + volumeInfos = vshMalloc(ctl, sizeof(virStorageVolInfo *) * 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++) { + int stringLength; + virStorageVolPtr vol = virStorageVolLookupByName(pool, + activeNames[i]); - if ((path = virStorageVolGetPath(vol)) == NULL) { - virStorageVolFree(vol); - continue; + /* Retrieve the volume path */ + if ((volumePaths[i] = virStorageVolGetPath(vol)) == NULL) { + /* Something went wrong retrieving a volume path, cope with it */ + volumePaths[i] = vshStrdup(ctl, _("unknown")); } + /* Keep the length of path string if longest so far */ + stringLength = strlen(volumePaths[i]); + if (stringLength > maxPath) + maxPath = stringLength; + + /* Keep the length of name string if longest so far */ + stringLength = strlen(activeNames[i]); + if (stringLength > maxName) + maxName = stringLength; + + /* Retrieve the volume capacity and allocation */ + volumeInfos[i] = vshMalloc(ctl, sizeof(virStorageVolInfo)); + if (virStorageVolGetInfo(vol, volumeInfos[i]) != 0) { + /* Something went wrong retrieving volume info, cope with it */ + volumeInfos[i] = NULL; + } - vshPrint(ctl, "%-20s %-40s\n", - virStorageVolGetName(vol), - path); - VIR_FREE(path); + /* Cleanup memory allocation */ virStorageVolFree(vol); - VIR_FREE(activeNames[i]); } + + /* Display the volume information */ + vshDebug(ctl, 5, "Longest name string = %d chars\n", maxName); + vshDebug(ctl, 5, "Longest path string = %d chars\n", maxPath); + if (details) { + virBuffer formatStr = VIR_BUFFER_INITIALIZER; + + /* Is the output too long to fit on one line? */ + if ((maxName + maxPath + 30) < 80) { + virBuffer headerStr = VIR_BUFFER_INITIALIZER; + + /* Output is not too long - use one line per entry */ + virBufferVSprintf(&formatStr, + "%%-%us %%-6s %%-10s %%-10s %%-%us\n", + maxName < 5 ? 5 : maxName, + maxPath); + virBufferVSprintf(&headerStr, + virBufferContentAndReset(&formatStr), + _("Name"), _("Type"), _("Capacity"), + _("Allocation"), _("Path")); + unsigned int headerLength = strlen(headerStr.d); + vshPrintExtra(ctl, "%s", virBufferContentAndReset(&headerStr)); + + /* Display an underline of appropriate length */ + for (i = 0; i < headerLength - 1; i++) + vshPrintExtra(ctl, "-"); + vshPrintExtra(ctl, "\n"); + + /* Define output format - one line per row of volume info */ + virBufferVSprintf(&formatStr, + "%%-%us %%-6s %%-10s %%-10s %%-%us\n", + maxName < 5 ? 5 : maxName, + maxPath); + } else { + /* Output IS too long - use two lines per entry */ + virBufferVSprintf(&formatStr, + "%%-%us %%-6s %%-10s %%-10s\n %%-%us\n", + maxName < 5 ? 5 : maxName, + maxPath); + vshPrintExtra(ctl, virBufferContentAndReset(&formatStr), + _("Name"), _("Type"), _("Capacity"), + _("Allocation"), _("Path")); + + /* Display an underline of appropriate length */ + if ((maxName + 30) > (maxPath + 6)) { + /* 30 = # chars in the header not including the name field + * 6 = Padding number picked out of the air that seems + * to work ok + */ + for (i = 0; i < maxName + 30; i++) + vshPrintExtra(ctl, "-"); + } else { + for (i = 0; i < maxPath + 6; i++) + vshPrintExtra(ctl, "-"); + } + vshPrintExtra(ctl, "\n"); + + /* Define output format - two lines per row of volume info */ + virBufferVSprintf(&formatStr, + "%%-%us %%-6s %%-10s %%-10s\n %%-%us\n", + maxName < 5 ? 5 : maxName, + maxPath); + } + + /* Display the volume detail rows */ + for (i = 0; i < numVolumes; i++) { + /* Do we have detailed sizing info? */ + if (volumeInfos[i] != NULL) { + /* We have detailed sizing info */ + double capVal, allocVal; + const char *capUnit, *allocUnit; + virBuffer capBufStr = VIR_BUFFER_INITIALIZER; + virBuffer allocBufStr = VIR_BUFFER_INITIALIZER; + + /* Determine the capacity value to show */ + capVal = prettyCapacity(volumeInfos[i]->capacity, &capUnit); + virBufferVSprintf(&capBufStr, "%.2lf %s", capVal, capUnit); + + /* Determine the allocation value to show */ + allocVal = prettyCapacity(volumeInfos[i]->allocation, + &allocUnit); + virBufferVSprintf(&allocBufStr, "%.2lf %s", allocVal, + allocUnit); + + /* Output volume details, showing all volume info */ + vshPrintExtra(ctl, formatStr.d, + activeNames[i], + volumeInfos[i]->type == VIR_STORAGE_VOL_FILE ? + _("file") : _("block"), + virBufferContentAndReset(&capBufStr), + virBufferContentAndReset(&allocBufStr), + volumePaths[i]); + + /* Cleanup memory allocation */ + VIR_FREE(volumeInfos[i]); + } else { + /* We don't have detailed sizing info, so output + * what we have */ + vshPrintExtra(ctl, formatStr.d, + activeNames[i], + _("unknown"), + _("unknown"), + _("unknown"), + volumePaths[i]); + } + + /* Cleanup memory allocation for this volume */ + VIR_FREE(volumePaths[i]); + VIR_FREE(activeNames[i]); + } + } else { + /* Only basic volume information needs to be shown */ + if (maxName < 20) /* Minimum column widths in the output */ + maxName = 20; + if (maxPath < 4) /* Minimum column widths in the output */ + maxPath = 4; + virBuffer outputStr = VIR_BUFFER_INITIALIZER; + virBufferVSprintf(&outputStr, "%%-%us %%-%us\n", maxName, maxPath); + + /* Output header */ + vshPrintExtra(ctl, outputStr.d, _("Name"), _("Path")); + + /* Display underline */ + for (i = 0; i < maxName + maxPath + 1; i++) + vshPrintExtra(ctl, "-"); + vshPrintExtra(ctl, "\n"); + + /* Output the volume information rows */ + for (i = 0; i < numVolumes; i++) { + vshPrint(ctl, outputStr.d, activeNames[i], volumePaths[i]); + } + + /* Cleanup memory allocation */ + virBufferFreeAndReset(&outputStr); + } + + /* Cleanup remaining memory allocation */ + VIR_FREE(volumePaths); + VIR_FREE(volumeInfos); VIR_FREE(activeNames); virStoragePoolFree(pool); return TRUE; diff --git a/tools/virsh.pod b/tools/virsh.pod index 8432b44..0f387e6 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> 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 +persistence and capacity related information where available. =item B<vol-pool> I<vol-key-or-path> -- 1.7.0.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list