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