[libvirt] [PATCH 1/2] logical: Use correct syntax for thin/sparse pool creation

John Ferlan jferlan at redhat.com
Fri Dec 12 21:14:29 UTC 2014


Commit id '1ffc78b5' was supposed to support for thin logical volumes;
however, instead it added support for thin snapshot volumes. This worked
fine for a few releases of LVM; however, more recent versions (not sure
exactly which one) made a differentiation between a thin snapshot volume
and a thin volume in a thin pool.  Furthermore, the creation command
used by libvirt:

  lvcreate --name <name> -L <allocation> --virtualsize <capacity> <VGname>

that used to create a thin snapshot volume will now create a thin volume
and a thin pool which libvirt doesn't know how to parse, for example:

  # lvcreate --name test -L 2M -V 5M lvm_test
  Rounding up size to full physical extent 4.00 MiB
  Rounding up size to full physical extent 8.00 MiB
  Logical volume "test" created.
  # lvs lvm_test
  LV    VG       Attr       LSize Pool  Origin Data%  Meta%  Move Log Cpy%Sync Convert
  lvol1 lvm_test twi-a-tz-- 4.00m              0.00   0.98
  test  lvm_test Vwi-a-tz-- 8.00m lvol1        0.00

compared to the former code which had the following:

  LV   VG       Attr       LSize  Pool Origin         Data%  Move Log Cpy%Sync Convert
  test LVM_Test swi-a-s---  4.00m      [test_vorigin]   0.00
When using the virsh vol-create-as or vol-create xmlfile commands, this
will cause libvirt to not find the 'test' volume nor mark it as sparse.
It cannot find since the command used to find/parse returns a thin volume
'test' with no associated device, for example the output is:

  lvol1##UgUwkp-fTFP-C0rc-ufue-xrYh-dkPr-FGPFPx#lvol1_tdata(0)#thin-pool#1#4194304#4194304#4194304#twi-a-tz--
  test##NcaIoH-4YWJ-QKu3-sJc3-EOcS-goff-cThLIL##thin#0#8388608#4194304#8388608#Vwi-a-tz--

as compared to the former which had the following:

  test#[test_vorigin]#Dt5Of3-4WE6-buvw-CWJ4-XOiz-ywOU-YULYw6#/dev/sda3(1300)#linear#1#4194304#4194304#4194304#swi-a-s---

The relevant changes being:

  1. The field after the UUID and before "thin" is where it's expected
     to return a device now has nothing. This is used in the vol-dumpxml
     output; however, it doesn't seem to have other uses

  2. The field after "thin" is a count of the number of extents. Which is
     assumed to be at least 1 by the LV processing code, but is only ever
     checked for a "striped" LV.

  3. The first character of the last field is used to determine LV attributes.
     A 'V' signifies a "thin Volume", while an 's' signifies a "snapshot"
     (and in our usage a thin snapshot).

  4. The LSize field in the new view is not the 'real' capacity of the
     'test' LV - it is now kept in the pool. Formerly (and for the thin
     snapshot), it's managed by LVM (it can be seen via a 'lvs -a' command).

While continuing to use a thin snapshot would be possible by simply changing
the "lvcreate" command to add a "--type snapshot" option, it's not clear
whether that was the desired result and if libvirt's model is the intended
usage for LVM of a thin shapshot. However, going forward the thin volume
support is what the following patch will utilize for new LV's created while
also still supporting the thin snapshots since it's not clear whether there
is a need to convert them and whether it's feasible/desired. Also we have
to support the old format for back-compat reasons, so it just seems safer
to keep things as they are.

This patch will adjust the lvcreate to be a sequence of two commands when
it's determined that the allocation and capacity do not match.  First a

  lvcreate --type thin-pool -L <allocation> --thinpool thinpool_<name> <VGname>

followed by a

  lvcreate --name <name> --thin <VGname>/thinpool_<name> \
           --type thin -V <capacity>

For non thin pools, it'll remain as it was:

  lvcreate --name <name> -L <capacity> <VGname>  (or -s <backingStorePath>)

Additionally, a new flag 'thinVolume' will be set to indicate the type
of volume. This will be essential during delete and will also be useful
for perhaps a new VolumeRefresh option to get the "correct" sizes for
thin volumes.

The volume processing callback (virStorageBackendLogicalMakeVol) will
be changed to handle/recognize the thinVolume. It will ignore the
extents and device 'source'. The effect of this is to have an empty
source in the vol-dumpxml output. Being able to ignore the devices
field also requires a change to the regex since it previously required
something in the field. For non thin volumes that don't have the
device field, the parsing algorithm already handles with a "malformed
volume extent devices value" failure.

The volume deletion code will now have to delete not only the LV, but
the thin pool associated with the volume. NB: If we didn't provide our
own name, LVM would generate one and it's at this point we'd have to
figure that out; otherwise, we'd leave around thin pools in the volume
group and eventually with enough of them, the VG would be needlessly
exhausted.

Signed-off-by: John Ferlan <jferlan at redhat.com>
---
 src/storage/storage_backend_logical.c | 119 +++++++++++++++++++++++++++++-----
 src/util/virstoragefile.h             |   1 +
 2 files changed, 102 insertions(+), 18 deletions(-)

diff --git a/src/storage/storage_backend_logical.c b/src/storage/storage_backend_logical.c
index 4959985..45df38c 100644
--- a/src/storage/storage_backend_logical.c
+++ b/src/storage/storage_backend_logical.c
@@ -140,6 +140,20 @@ virStorageBackendLogicalMakeVol(char **const groups,
     if (attrs[0] == 's')
         vol->target.sparse = true;
 
+    /* The initial implementation mistakenly used thin snapshots (the 's')
+     * so we're stuck with back-compat issues dealing with two types of
+     * sparse or thin volumes.  We'll mark those that have the 'V' in
+     * the attrs[0] as ones using the correct methodology of adding
+     * "--type thin" and a "--thin thinpool_<name>" as having both
+     * the sparse attribute (thus inhibiting volume wipe functionality)
+     * and the thin attribute indicing more information about the volume
+     * is available from the thin pool.
+     */
+    if (attrs[0] == 'V') {
+        vol->target.sparse = true;
+        vol->target.thinVolume = true;
+    }
+
     /* Skips the backingStore of lv created with "--virtualsize",
      * its original device "/dev/$vgname/$lvname_vorigin" is
      * just for lvm internal use, one should never use it.
@@ -161,10 +175,20 @@ virStorageBackendLogicalMakeVol(char **const groups,
     if (!vol->key && VIR_STRDUP(vol->key, groups[2]) < 0)
         goto cleanup;
 
+    /* Updates capacity and allocation. The allocation is overwritten
+     * later in the processing of extents
+     */
     if (virStorageBackendUpdateVolInfo(vol, true, false,
                                        VIR_STORAGE_VOL_OPEN_DEFAULT) < 0)
         goto cleanup;
 
+    /* Thin LV's are managed by LVM, thus there's no "devices" field and
+     * the extents are managed by the thin pool which we've conveniently
+     * ignored (attrs[0] == 't'), so let's just skip the extents mgmt here.
+     */
+    if (vol->target.thinVolume)
+        goto skip_extents;
+
     nextents = 1;
     if (STREQ(groups[4], VIR_STORAGE_VOL_LOGICAL_SEGTYPE_STRIPED)) {
         if (virStrToLong_i(groups[5], NULL, 10, &nextents) < 0) {
@@ -269,6 +293,7 @@ virStorageBackendLogicalMakeVol(char **const groups,
         vol->source.nextent++;
     }
 
+ skip_extents:
     if (is_new_vol &&
         VIR_APPEND_ELEMENT(pool->volumes.objs, pool->volumes.count, vol) < 0)
         goto cleanup;
@@ -309,9 +334,10 @@ virStorageBackendLogicalFindLVs(virStoragePoolObjPtr pool,
      *    not a suitable separator (rhbz 470693).
      * NB "devices" field has multiple device paths and "," if the volume is
      *    striped, so "," is not a suitable separator either (rhbz 727474).
+     * NB "devices" field is empty for thin logical volumes (rhbz 1166592)
      */
     const char *regexes[] = {
-       "^\\s*(\\S+)#(\\S*)#(\\S+)#(\\S+)#(\\S+)#([0-9]+)#(\\S+)#([0-9]+)#([0-9]+)#(\\S+)#?\\s*$"
+       "^\\s*(\\S+)#(\\S*)#(\\S+)#(\\S*)#(\\S+)#([0-9]+)#(\\S+)#([0-9]+)#([0-9]+)#(\\S+)#?\\s*$"
     };
     int vars[] = {
         10
@@ -690,11 +716,12 @@ virStorageBackendLogicalDeletePool(virConnectPtr conn ATTRIBUTE_UNUSED,
 
 static int
 virStorageBackendLogicalDeleteVol(virConnectPtr conn ATTRIBUTE_UNUSED,
-                                  virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
+                                  virStoragePoolObjPtr pool,
                                   virStorageVolDefPtr vol,
                                   unsigned int flags)
 {
     int ret = -1;
+    bool del_pool_try;
 
     virCommandPtr lvchange_cmd = NULL;
     virCommandPtr lvremove_cmd = NULL;
@@ -706,14 +733,37 @@ virStorageBackendLogicalDeleteVol(virConnectPtr conn ATTRIBUTE_UNUSED,
     lvchange_cmd = virCommandNewArgList(LVCHANGE, "-aln", vol->target.path, NULL);
     lvremove_cmd = virCommandNewArgList(LVREMOVE, "-f", vol->target.path, NULL);
 
-    if (virCommandRun(lvremove_cmd, NULL) < 0) {
-        if (virCommandRun(lvchange_cmd, NULL) < 0) {
-            goto cleanup;
-        } else {
-            if (virCommandRun(lvremove_cmd, NULL) < 0)
+
+    do {
+        if (virCommandRun(lvremove_cmd, NULL) < 0) {
+            if (virCommandRun(lvchange_cmd, NULL) < 0) {
                 goto cleanup;
+            } else {
+                if (virCommandRun(lvremove_cmd, NULL) < 0)
+                    goto cleanup;
+            }
         }
-    }
+
+        /* When a thin volume is created, there are two elements added to the
+         * logical volume - the thin volume by name and a thin volume pool.
+         * We need to try to remove both of them - only once though - the
+         * do {} while; is just an optimization to avoid copying the above
+         * run commands again.
+         */
+        del_pool_try = false;
+        if (vol->target.thinVolume) {
+            virCommandFree(lvchange_cmd);
+            virCommandFree(lvremove_cmd);
+            lvchange_cmd = virCommandNewArgList(LVCHANGE, "-aln", NULL);
+            virCommandAddArgFormat(lvchange_cmd, "%s/thinpool_%s",
+                                   pool->def->source.name, vol->name);
+            lvremove_cmd = virCommandNewArgList(LVREMOVE, "-f", NULL);
+            virCommandAddArgFormat(lvremove_cmd, "%s/thinpool_%s",
+                                   pool->def->source.name, vol->name);
+            del_pool_try = true;
+            vol->target.thinVolume = false; /* To avoid another attempt */
+        }
+    } while (del_pool_try);
 
     ret = 0;
  cleanup:
@@ -750,23 +800,56 @@ virStorageBackendLogicalCreateVol(virConnectPtr conn,
                     vol->name) == -1)
         return -1;
 
+    /* Creation of thin/sparse volumes is a two step process (it cannot be
+     * done in one command).  First we need to create a thin pool into which
+     * the logical volume will be placed. Rather than having LVM pick a name
+     * for us, we'll go with the volume name prefixed by "thinpool_".
+     * This way when we go to delete/remove the volume - we don't have to
+     * fish around LVM in order to figure out the name created for us and
+     * ensure nothing else is using it.
+     */
+    if (vol->target.capacity != vol->target.allocation) {
+        cmd = virCommandNewArgList(LVCREATE,
+                                   "--type", "thin-pool",
+                                   NULL);
+        virCommandAddArg(cmd, "-L");
+        virCommandAddArgFormat(cmd, "%lluK",
+                               VIR_DIV_UP(vol->target.allocation
+                                          ? vol->target.allocation : 1, 1024));
+        virCommandAddArg(cmd, "--thinpool");
+        virCommandAddArgFormat(cmd, "thinpool_%s", vol->name);
+        virCommandAddArg(cmd, pool->def->source.name);
+        if (virCommandRun(cmd, NULL) < 0)
+            goto error;
+
+        virCommandFree(cmd);
+        cmd = NULL;
+    }
+
     cmd = virCommandNewArgList(LVCREATE,
                                "--name", vol->name,
                                NULL);
-    virCommandAddArg(cmd, "-L");
     if (vol->target.capacity != vol->target.allocation) {
+        virCommandAddArg(cmd, "--thin");
+        virCommandAddArgFormat(cmd, "%s/thinpool_%s",
+                               pool->def->source.name, vol->name);
+        virCommandAddArgList(cmd, "--type", "thin", NULL);
+        virCommandAddArg(cmd, "-V");
         virCommandAddArgFormat(cmd, "%lluK",
-                               VIR_DIV_UP(vol->target.allocation
-                                          ? vol->target.allocation : 1, 1024));
-        virCommandAddArg(cmd, "--virtualsize");
+                               VIR_DIV_UP(vol->target.capacity, 1024));
+
+        /* Set the sparse and thinVolume flags for later use */
         vol->target.sparse = true;
+        vol->target.thinVolume = true;
+    } else {
+        virCommandAddArg(cmd, "-L");
+        virCommandAddArgFormat(cmd, "%lluK",
+                               VIR_DIV_UP(vol->target.capacity, 1024));
+        if (vol->target.backingStore)
+            virCommandAddArgList(cmd, "-s", vol->target.backingStore->path, NULL);
+        else
+            virCommandAddArg(cmd, pool->def->source.name);
     }
-    virCommandAddArgFormat(cmd, "%lluK", VIR_DIV_UP(vol->target.capacity,
-                                                    1024));
-    if (vol->target.backingStore)
-        virCommandAddArgList(cmd, "-s", vol->target.backingStore->path, NULL);
-    else
-        virCommandAddArg(cmd, pool->def->source.name);
 
     if (virCommandRun(cmd, NULL) < 0)
         goto error;
diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h
index e05b843..7522dcd 100644
--- a/src/util/virstoragefile.h
+++ b/src/util/virstoragefile.h
@@ -254,6 +254,7 @@ struct _virStorageSource {
     char *compat;
     bool nocow;
     bool sparse;
+    bool thinVolume;
 
     virStoragePermsPtr perms;
     virStorageTimestampsPtr timestamps;
-- 
1.9.3




More information about the libvir-list mailing list