]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Fix up calculating space info for mirrored volumes
authorVishvananda Ishaya <vishvananda@gmail.com>
Fri, 17 Jan 2014 21:07:43 +0000 (14:07 -0700)
committerjohn-griffith <john.griffith@solidfire.com>
Mon, 20 Jan 2014 18:04:18 +0000 (11:04 -0700)
The status reporting in the lvm driver for cinder volume incorrectly
reports free capacity. This is due to a couple of issues:
  a) the number of mirrors isn't taken into account
  b) there are some pathological cases where a
     simple division by total mirrors isn't sufficient
     because all mirrors must be on separate physical volumes.

Co-authored-by: Vishvananda Ishaya <vishvananda@gmail.com>
Co-authored-by: John Griffith <john.griffith@solidfire.com>
Closes-Bug: 1269964
Change-Id: I65e16b24367b4093a52c1c52d895fb58ef6a29ff

cinder/brick/local_dev/lvm.py
cinder/tests/brick/test_brick_lvm.py
cinder/tests/test_volume.py
cinder/volume/drivers/lvm.py

index 5ff54630a837d780ee7004d9aa9f11371d832615..b5ba09b020e43d736ab576846816497831185cf4 100644 (file)
@@ -92,6 +92,7 @@ class LVM(executor.Executor):
                 self.vg_thin_pool = pool_name
 
             self.activate_lv(self.vg_thin_pool)
+        self.pv_list = self.get_all_physical_volumes(root_helper, vg_name)
 
     def _vg_exists(self):
         """Simple check to see if VG exists.
@@ -306,23 +307,21 @@ class LVM(executor.Executor):
         if no_suffix:
             cmd.append('--nosuffix')
 
-        if vg_name is not None:
-            cmd.append(vg_name)
-
         (out, err) = putils.execute(*cmd,
                                     root_helper=root_helper,
                                     run_as_root=True)
 
-        pv_list = []
-        if out is not None:
-            pvs = out.split()
-            for pv in pvs:
-                fields = pv.split(':')
-                pv_list.append({'vg': fields[0],
-                                'name': fields[1],
-                                'size': fields[2],
-                                'available': fields[3]})
+        pvs = out.split()
+        if vg_name is not None:
+            pvs = [pv for pv in pvs if vg_name == pv.split(':')[0]]
 
+        pv_list = []
+        for pv in pvs:
+            fields = pv.split(':')
+            pv_list.append({'vg': fields[0],
+                            'name': fields[1],
+                            'size': float(fields[2]),
+                            'available': float(fields[3])})
         return pv_list
 
     def get_physical_volumes(self):
@@ -625,3 +624,28 @@ class LVM(executor.Executor):
             LOG.error(_('StdOut  :%s') % err.stdout)
             LOG.error(_('StdErr  :%s') % err.stderr)
             raise
+
+    def vg_mirror_free_space(self, mirror_count):
+        free_capacity = 0.0
+
+        disks = []
+        for pv in self.pv_list:
+            disks.append(float(pv['available']))
+
+        while True:
+            disks = sorted([a for a in disks if a > 0.0], reverse=True)
+            if len(disks) <= mirror_count:
+                break
+            # consume the smallest disk
+            disk = disks[-1]
+            disks = disks[:-1]
+            # match extents for each mirror on the largest disks
+            for index in list(range(mirror_count)):
+                disks[index] -= disk
+            free_capacity += disk
+
+        return free_capacity
+
+    def vg_mirror_size(self, mirror_count):
+        return (float(self.vg_free_space) /
+                (mirror_count + 1))
index 03fde16d0bec712036e4ed9dedee248999b4ae02..5aef2f1695f47697bffe858265183bcbcb44b9e0 100644 (file)
@@ -35,7 +35,7 @@ class BrickLvmTestCase(test.TestCase):
     def setUp(self):
         self._mox = mox.Mox()
         self.configuration = mox.MockObject(conf.Configuration)
-        self.configuration.volume_group_name = 'fake-volumes'
+        self.configuration.volume_group_name = 'fake-vg'
         super(BrickLvmTestCase, self).setUp()
 
         #Stub processutils.execute for static methods
@@ -66,44 +66,42 @@ class BrickLvmTestCase(test.TestCase):
 
         if ('env, LC_ALL=C, vgs, --noheadings, --unit=g, -o, name' ==
                 cmd_string):
-            data = "  fake-volumes\n"
+            data = "  fake-vg\n"
             data += "  some-other-vg\n"
-        elif ('env, LC_ALL=C, vgs, --noheadings, -o, name, fake-volumes' ==
+        elif ('env, LC_ALL=C, vgs, --noheadings, -o, name, fake-vg' ==
                 cmd_string):
-            data = "  fake-volumes\n"
+            data = "  fake-vg\n"
         elif 'env, LC_ALL=C, vgs, --version' in cmd_string:
             data = "  LVM version:     2.02.95(2) (2012-03-06)\n"
-        elif ('env, LC_ALL=C, vgs, --noheadings, -o uuid, fake-volumes' in
+        elif ('env, LC_ALL=C, vgs, --noheadings, -o uuid, fake-vg' in
               cmd_string):
             data = "  kVxztV-dKpG-Rz7E-xtKY-jeju-QsYU-SLG6Z1\n"
         elif 'env, LC_ALL=C, vgs, --noheadings, --unit=g, ' \
              '-o, name,size,free,lv_count,uuid, ' \
              '--separator, :, --nosuffix' in cmd_string:
-            data = "  fake-volumes:10.00:10.00:0:"\
+            data = "  fake-vg:10.00:10.00:0:"\
                    "kVxztV-dKpG-Rz7E-xtKY-jeju-QsYU-SLG6Z1\n"
-            if 'fake-volumes' in cmd_string:
+            if 'fake-vg' in cmd_string:
                 return (data, "")
-            data += "  fake-volumes-2:10.00:10.00:0:"\
+            data += "  fake-vg-2:10.00:10.00:0:"\
                     "lWyauW-dKpG-Rz7E-xtKY-jeju-QsYU-SLG7Z2\n"
-            data += "  fake-volumes-3:10.00:10.00:0:"\
+            data += "  fake-vg-3:10.00:10.00:0:"\
                     "mXzbuX-dKpG-Rz7E-xtKY-jeju-QsYU-SLG8Z3\n"
         elif ('env, LC_ALL=C, lvs, --noheadings, '
               '--unit=g, -o, vg_name,name,size' in cmd_string):
-            data = "  fake-volumes fake-1 1.00g\n"
-            data += "  fake-volumes fake-2 1.00g\n"
+            data = "  fake-vg fake-1 1.00g\n"
+            data += "  fake-vg fake-2 1.00g\n"
         elif ('env, LC_ALL=C, lvdisplay, --noheading, -C, -o, Attr' in
               cmd_string):
             if 'test-volumes' in cmd_string:
                 data = '  wi-a-'
             else:
                 data = '  owi-a-'
-        elif 'env, LC_ALL=C, pvs, --noheadings' in cmd_string \
-                and 'fake-volumes' in cmd_string:
-            data = "  fake-volumes:/dev/sda:10.00g:8.99g\n"
         elif 'env, LC_ALL=C, pvs, --noheadings' in cmd_string:
-            data = "  fake-volumes:/dev/sda:10.00g:8.99g\n"
-            data += "  fake-volumes-2:/dev/sdb:10.00g:8.99g\n"
-            data += "  fake-volumes-3:/dev/sdc:10.00g:8.99g\n"
+            data = "  fake-vg:/dev/sda:10.00:1.00\n"
+            data += "  fake-vg:/dev/sdb:10.00:1.00\n"
+            data += "  fake-vg:/dev/sdc:10.00:8.99\n"
+            data += "  fake-vg-2:/dev/sdd:10.00:9.99\n"
         elif 'env, LC_ALL=C, lvs, --noheadings, --unit=g, -o, ' \
              'size,data_percent, ' \
              '--separator, :' in cmd_string:
@@ -129,23 +127,28 @@ class BrickLvmTestCase(test.TestCase):
 
         self.assertEqual(out[0]['name'], 'fake-1')
         self.assertEqual(out[0]['size'], '1.00g')
-        self.assertEqual(out[0]['vg'], 'fake-volumes')
+        self.assertEqual(out[0]['vg'], 'fake-vg')
 
     def test_get_volume(self):
         self.assertEqual(self.vg.get_volume('fake-1')['name'], 'fake-1')
 
     def test_get_all_physical_volumes(self):
-        pvs = self.vg.get_all_physical_volumes('sudo')
+        # Filtered VG version
+        pvs = self.vg.get_all_physical_volumes('sudo', 'fake-vg')
         self.assertEqual(len(pvs), 3)
 
+        # Non-Filtered, all VG's
+        pvs = self.vg.get_all_physical_volumes('sudo')
+        self.assertEqual(len(pvs), 4)
+
     def test_get_physical_volumes(self):
         pvs = self.vg.get_physical_volumes()
-        self.assertEqual(len(pvs), 1)
+        self.assertEqual(len(pvs), 3)
 
     def test_get_volume_groups(self):
         self.assertEqual(len(self.vg.get_all_volume_groups('sudo')), 3)
         self.assertEqual(len(self.vg.get_all_volume_groups('sudo',
-                                                           'fake-volumes')), 1)
+                                                           'fake-vg')), 1)
 
     def test_thin_support(self):
         # lvm.supports_thin() is a static method and doesn't
@@ -227,7 +230,7 @@ class BrickLvmTestCase(test.TestCase):
 
     def test_thin_pool_creation(self):
 
-        # The size of fake-volumes volume group is 10g, so the calculated thin
+        # The size of fake-vg volume group is 10g, so the calculated thin
         # pool size should be 9.5g (95% of 10g).
         self.assertEqual("9.5g", self.vg.create_thin_pool())
 
@@ -237,7 +240,7 @@ class BrickLvmTestCase(test.TestCase):
             self.assertEqual(size, self.vg.create_thin_pool(size_str=size))
 
     def test_thin_pool_free_space(self):
-        # The size of fake-volumes-pool is 9g and the allocated data sums up to
+        # The size of fake-vg-pool is 9g and the allocated data sums up to
         # 12% so the calculated free space should be 7.92
         self.assertEqual(float("7.92"),
                          self.vg._get_thin_pool_free_space("fake-vg",
@@ -263,7 +266,7 @@ class BrickLvmTestCase(test.TestCase):
         self.assertEqual(self.vg.vg_thin_pool, pool_name)
 
     def test_lv_has_snapshot(self):
-        self.assertTrue(self.vg.lv_has_snapshot('fake-volumes'))
+        self.assertTrue(self.vg.lv_has_snapshot('fake-vg'))
         self.assertFalse(self.vg.lv_has_snapshot('test-volumes'))
 
     def test_activate_lv(self):
@@ -271,7 +274,7 @@ class BrickLvmTestCase(test.TestCase):
         self.vg._supports_lvchange_ignoreskipactivation = True
 
         self.vg._execute('lvchange', '-a', 'y', '--yes', '-K',
-                         'fake-volumes/my-lv',
+                         'fake-vg/my-lv',
                          root_helper='sudo', run_as_root=True)
 
         self._mox.ReplayAll()
@@ -279,3 +282,6 @@ class BrickLvmTestCase(test.TestCase):
         self.vg.activate_lv('my-lv')
 
         self._mox.VerifyAll()
+
+    def test_get_mirrored_available_capacity(self):
+        self.assertEqual(self.vg.vg_mirror_free_space(1), 2.0)
index 1a43feacc63223643810f094b7e79396011cf00b..00afad0cddef404cb1514375996f398aed781319 100644 (file)
@@ -2683,6 +2683,13 @@ class LVMISCSIVolumeDriverTestCase(DriverTestCase):
             # host to test the check of dest VG existence.
             return [{'name': 'cinder-volumes-2'}]
 
+        def _fake_get_all_physical_volumes(obj, root_helper, vg_name):
+            return [{}]
+
+        self.stubs.Set(brick_lvm.LVM,
+                       'get_all_physical_volumes',
+                       _fake_get_all_physical_volumes)
+
         self.stubs.Set(self.volume.driver, '_execute', fake_execute)
 
         self.stubs.Set(volutils, 'copy_volume',
@@ -2876,6 +2883,9 @@ class ISCSITestCase(DriverTestCase):
 
     def test_get_volume_stats(self):
 
+        def _fake_get_all_physical_volumes(obj, root_helper, vg_name):
+            return [{}]
+
         def _fake_get_all_volume_groups(obj, vg_name=None, no_suffix=True):
             return [{'name': 'cinder-volumes',
                      'size': '5.52',
@@ -2886,6 +2896,11 @@ class ISCSITestCase(DriverTestCase):
         self.stubs.Set(brick_lvm.LVM,
                        'get_all_volume_groups',
                        _fake_get_all_volume_groups)
+
+        self.stubs.Set(brick_lvm.LVM,
+                       'get_all_physical_volumes',
+                       _fake_get_all_physical_volumes)
+
         self.volume.driver.vg = brick_lvm.LVM('cinder-volumes', 'sudo')
 
         self.volume.driver._update_volume_stats()
@@ -2924,6 +2939,9 @@ class ISERTestCase(ISCSITestCase):
         self.configuration.iser_port = 3260
 
     def test_get_volume_stats(self):
+        def _fake_get_all_physical_volumes(obj, root_helper, vg_name):
+            return [{}]
+
         def _fake_get_all_volume_groups(obj, vg_name=None, no_suffix=True):
             return [{'name': 'cinder-volumes',
                      'size': '5.52',
@@ -2931,6 +2949,10 @@ class ISERTestCase(ISCSITestCase):
                      'lv_count': '2',
                      'uuid': 'vR1JU3-FAKE-C4A9-PQFh-Mctm-9FwA-Xwzc1m'}]
 
+        self.stubs.Set(brick_lvm.LVM,
+                       'get_all_physical_volumes',
+                       _fake_get_all_physical_volumes)
+
         self.stubs.Set(brick_lvm.LVM,
                        'get_all_volume_groups',
                        _fake_get_all_volume_groups)
index 7c4ac314fb92009316b5057e860f7f8f81389a1d..0d7369146067f3446f153d40546d230833f741da 100644 (file)
@@ -371,11 +371,17 @@ class LVMVolumeDriver(driver.VolumeDriver):
         data["driver_version"] = self.VERSION
         data["storage_protocol"] = self.protocol
 
-        data['total_capacity_gb'] = float(self.vg.vg_size)
-        data['free_capacity_gb'] = float(self.vg.vg_free_space)
-        if self.configuration.lvm_type == 'thin':
+        if self.configuration.lvm_mirrors > 0:
+            data['total_capacity_gb'] =\
+                self.vg.vg_mirror_size(self.configuration.lvm_mirrors)
+            data['free_capacity_gb'] =\
+                self.vg.vg_mirror_free_space(self.configuration.lvm_mirrors)
+        elif self.configuration.lvm_type == 'thin':
             data['total_capacity_gb'] = float(self.vg.vg_thin_pool_size)
             data['free_capacity_gb'] = float(self.vg.vg_thin_pool_free_space)
+        else:
+            data['total_capacity_gb'] = float(self.vg.vg_size)
+            data['free_capacity_gb'] = float(self.vg.vg_free_space)
         data['reserved_percentage'] = self.configuration.reserved_percentage
         data['QoS_support'] = False
         data['location_info'] =\