[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[libvirt] [PATCHv2 2/2] virsh: add new --details option to vol-list



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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]