]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Refactor LVM driver to use Brick VG utility
authorJohn Griffith <john.griffith@solidfire.com>
Mon, 22 Jul 2013 16:39:54 +0000 (10:39 -0600)
committerJohn Griffith <john.griffith@solidfire.com>
Fri, 16 Aug 2013 22:45:52 +0000 (16:45 -0600)
Refactor the LVM driver to utilize bricks LVM
module.  This includes significant modification to
tests and fake objects related to testing.

implements blueprint: refactor-lvm-and-iscsi-driver

Change-Id: I94e41abf90d0c5b77e732d40ed8b81b0a2f5d7dd

cinder/brick/local_dev/lvm.py
cinder/tests/api/contrib/test_admin_actions.py
cinder/tests/brick/fake_lvm.py [new file with mode: 0644]
cinder/tests/brick/test_brick_lvm.py
cinder/tests/fake_driver.py
cinder/tests/test_volume.py
cinder/volume/drivers/lvm.py
cinder/volume/utils.py

index b67fab64916e1a78d38865ca8d49b51cbb0be49b..2543a8735eed8a6043b9659d82aa0ee8a955a739 100644 (file)
@@ -35,21 +35,22 @@ LOG = logging.getLogger(__name__)
 class LVM(object):
     """LVM object to enable various LVM related operations."""
 
-    def __init__(self,
-                 vg_name,
-                 create_vg=False,
-                 physical_volumes=None,
-                 lvm_type='default',
+    def __init__(self, vg_name, root_helper, create_vg=False,
+                 physical_volumes=None, lvm_type='default',
                  executor=putils.execute):
+
         """Initialize the LVM object.
 
         The LVM object is based on an LVM VolumeGroup, one instantiation
         for each VolumeGroup you have/use.
 
         :param vg_name: Name of existing VG or VG to create
+        :param root_helper: Execution root_helper method to use
         :param create_vg: Indicates the VG doesn't exist
                           and we want to create it
         :param physical_volumes: List of PVs to build VG on
+        :param lvm_type: VG and Volume type (default, or thin)
+        :param executor: Execute method to use, None uses common/processutils
 
         """
         self.vg_name = vg_name
@@ -61,7 +62,8 @@ class LVM(object):
         self.vg_uuid = None
         self.vg_thin_pool = None
         self.vg_thin_pool_size = 0
-        self._execute = executor
+        self.root_helper = root_helper
+        self._set_execute(executor)
 
         if create_vg and physical_volumes is not None:
             self.pv_list = physical_volumes
@@ -86,6 +88,9 @@ class LVM(object):
             else:
                 self.vg_thin_pool = pool_name
 
+    def _set_execute(self, execute):
+        self._execute = execute
+
     def _size_str(self, size_in_g):
         if '.00' in size_in_g:
             size_in_g = size_in_g.replace('.00', '')
@@ -103,7 +108,9 @@ class LVM(object):
         """
         exists = False
         cmd = ['vgs', '--noheadings', '-o', 'name']
-        (out, err) = self._execute(*cmd, root_helper='sudo', run_as_root=True)
+        (out, err) = self._execute(*cmd,
+                                   root_helper=self.root_helper,
+                                   run_as_root=True)
 
         if out is not None:
             volume_groups = out.split()
@@ -114,7 +121,7 @@ class LVM(object):
 
     def _create_vg(self, pv_list):
         cmd = ['vgcreate', self.vg_name, ','.join(pv_list)]
-        self._execute(*cmd, root_helper='sudo', run_as_root=True)
+        self._execute(*cmd, root_helper=self.root_helper, run_as_root=True)
 
     def _get_vg_uuid(self):
         (out, err) = self._execute('vgs', '--noheadings',
@@ -125,14 +132,17 @@ class LVM(object):
             return []
 
     @staticmethod
-    def supports_thin_provisioning():
+    def supports_thin_provisioning(root_helper):
         """Static method to check for thin LVM support on a system.
 
+        :param root_helper: root_helper to use for execute
         :returns: True if supported, False otherwise
 
         """
         cmd = ['vgs', '--version']
-        (out, err) = putils.execute(*cmd, root_helper='sudo', run_as_root=True)
+        (out, err) = putils.execute(*cmd,
+                                    root_helper=root_helper,
+                                    run_as_root=True)
         lines = out.split('\n')
 
         for line in lines:
@@ -147,9 +157,10 @@ class LVM(object):
         return False
 
     @staticmethod
-    def get_all_volumes(vg_name=None, no_suffix=True):
+    def get_all_volumes(root_helper, vg_name=None, no_suffix=True):
         """Static method to get all LV's on a system.
 
+        :param root_helper: root_helper to use for execute
         :param vg_name: optional, gathers info for only the specified VG
         :param no_suffix: optional, reports sizes in g with no suffix
         :returns: List of Dictionaries with LV info
@@ -163,7 +174,9 @@ class LVM(object):
         if vg_name is not None:
             cmd.append(vg_name)
 
-        (out, err) = putils.execute(*cmd, root_helper='sudo', run_as_root=True)
+        (out, err) = putils.execute(*cmd,
+                                    root_helper=root_helper,
+                                    run_as_root=True)
 
         lv_list = []
         if out is not None:
@@ -179,7 +192,7 @@ class LVM(object):
         :returns: List of Dictionaries with LV info
 
         """
-        self.lv_list = self.get_all_volumes(self.vg_name)
+        self.lv_list = self.get_all_volumes(self.root_helper, self.vg_name)
         return self.lv_list
 
     def get_volume(self, name):
@@ -194,9 +207,10 @@ class LVM(object):
                 return r
 
     @staticmethod
-    def get_all_physical_volumes(vg_name=None, no_suffix=True):
+    def get_all_physical_volumes(root_helper, vg_name=None, no_suffix=True):
         """Static method to get all PVs on a system.
 
+        :param root_helper: root_helper to use for execute
         :param vg_name: optional, gathers info for only the specified VG
         :param no_suffix: optional, reports sizes in g with no suffix
         :returns: List of Dictionaries with PV info
@@ -212,7 +226,9 @@ class LVM(object):
         if vg_name is not None:
             cmd.append(vg_name)
 
-        (out, err) = putils.execute(*cmd, root_helper='sudo', run_as_root=True)
+        (out, err) = putils.execute(*cmd,
+                                    root_helper=root_helper,
+                                    run_as_root=True)
 
         pv_list = []
         if out is not None:
@@ -232,13 +248,15 @@ class LVM(object):
         :returns: List of Dictionaries with PV info
 
         """
-        self.pv_list = self.get_all_physical_volumes(self.vg_name)
+        self.pv_list = self.get_all_physical_volumes(self.root_helper,
+                                                     self.vg_name)
         return self.pv_list
 
     @staticmethod
-    def get_all_volume_groups(vg_name=None, no_suffix=True):
+    def get_all_volume_groups(root_helper, vg_name=None, no_suffix=True):
         """Static method to get all VGs on a system.
 
+        :param root_helper: root_helper to use for execute
         :param vg_name: optional, gathers info for only the specified VG
         :param no_suffix: optional, reports sizes in g with no suffix
         :returns: List of Dictionaries with VG info
@@ -255,7 +273,9 @@ class LVM(object):
         if vg_name is not None:
             cmd.append(vg_name)
 
-        (out, err) = putils.execute(*cmd, root_helper='sudo', run_as_root=True)
+        (out, err) = putils.execute(*cmd,
+                                    root_helper=root_helper,
+                                    run_as_root=True)
 
         vg_list = []
         if out is not None:
@@ -279,7 +299,7 @@ class LVM(object):
         :returns: Dictionaries of VG info
 
         """
-        vg_list = self.get_all_volume_groups(self.vg_name)
+        vg_list = self.get_all_volume_groups(self.root_helper, self.vg_name)
 
         if len(vg_list) != 1:
             LOG.error(_('Unable to find VG: %s') % self.vg_name)
@@ -291,9 +311,9 @@ class LVM(object):
         self.vg_uuid = vg_list[0]['uuid']
 
         if self.vg_thin_pool is not None:
-            for lv in self.get_all_volumes(self.vg_name):
-                if lv[1] == self.vg_thin_pool:
-                    self.vg_thin_pool_size = lv[2]
+            for lv in self.get_all_volumes(self.root_helper, self.vg_name):
+                if lv['name'] == self.vg_thin_pool:
+                    self.vg_thin_pool_size = lv['size']
 
     def create_thin_pool(self, name=None, size_str=0):
         """Creates a thin provisioning pool for this VG.
@@ -307,7 +327,7 @@ class LVM(object):
 
         """
 
-        if not self.supports_thin_provisioning():
+        if not self.supports_thin_provisioning(self.root_helper):
             LOG.error(_('Requested to setup thin provisioning, '
                         'however current LVM version does not '
                         'support it.'))
@@ -327,7 +347,7 @@ class LVM(object):
         cmd = ['lvcreate', '-T', '-L', size_str, pool_path]
 
         self._execute(*cmd,
-                      root_helper='sudo',
+                      root_helper=self.root_helper,
                       run_as_root=True)
         self.vg_thin_pool = pool_path
 
@@ -358,7 +378,7 @@ class LVM(object):
 
         try:
             self._execute(*cmd,
-                          root_helper='sudo',
+                          root_helper=self.root_helper,
                           run_as_root=True)
         except putils.ProcessExecutionError as err:
             LOG.exception(_('Error creating Volume'))
@@ -387,7 +407,7 @@ class LVM(object):
 
         try:
             self._execute(*cmd,
-                          root_helper='sudo',
+                          root_helper=self.root_helper,
                           run_as_root=True)
         except putils.ProcessExecutionError as err:
             LOG.exception(_('Error creating snapshot'))
@@ -405,7 +425,7 @@ class LVM(object):
         self._execute('lvremove',
                       '-f',
                       '%s/%s' % (self.vg_name, name),
-                      root_helper='sudo', run_as_root=True)
+                      root_helper=self.root_helper, run_as_root=True)
 
     def revert(self, snapshot_name):
         """Revert an LV from snapshot.
@@ -414,13 +434,15 @@ class LVM(object):
 
         """
         self._execute('lvconvert', '--merge',
-                      snapshot_name, root_helper='sudo',
+                      snapshot_name, root_helper=self.root_helper,
                       run_as_root=True)
 
     def lv_has_snapshot(self, name):
         out, err = self._execute('lvdisplay', '--noheading',
                                  '-C', '-o', 'Attr',
-                                 '%s/%s' % (self.vg_name, name))
+                                 '%s/%s' % (self.vg_name, name),
+                                 root_helper=self.root_helper,
+                                 run_as_root=True)
         if out:
             out = out.strip()
             if (out[0] == 'o') or (out[0] == 'O'):
index 31132550d8dce1be00d37be8cfa70241942fd3bc..942f41686ed89ed8a751297cfe09b0a6107bb79b 100644 (file)
@@ -16,6 +16,7 @@ import webob
 
 from oslo.config import cfg
 
+from cinder.brick.local_dev import lvm as brick_lvm
 from cinder import context
 from cinder import db
 from cinder import exception
@@ -45,6 +46,7 @@ class AdminActionsTest(test.TestCase):
         self.flags(rpc_backend='cinder.openstack.common.rpc.impl_fake')
         self.flags(lock_path=self.tempdir)
         self.volume_api = volume_api.API()
+        self.stubs.Set(brick_lvm.LVM, '_vg_exists', lambda x: True)
 
     def tearDown(self):
         shutil.rmtree(self.tempdir)
@@ -432,7 +434,6 @@ class AdminActionsTest(test.TestCase):
 
     def test_attach_attaching_volume_with_different_instance(self):
         """Test that attaching volume reserved for another instance fails."""
-        # admin context
         ctx = context.RequestContext('admin', 'fake', True)
         # current status is available
         volume = db.volume_create(ctx, {'status': 'available', 'host': 'test',
diff --git a/cinder/tests/brick/fake_lvm.py b/cinder/tests/brick/fake_lvm.py
new file mode 100644 (file)
index 0000000..a6f76ab
--- /dev/null
@@ -0,0 +1,69 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from cinder.openstack.common import log as logging
+
+
+LOG = logging.getLogger(__name__)
+
+
+class FakeBrickLVM(object):
+    """Logs and records calls, for unit tests."""
+    def __init__(self, vg_name, create, pv_list, vtype, execute=None):
+        super(FakeBrickLVM, self).__init__()
+        self.vg_size = '5.00'
+        self.vg_free_space = '5.00'
+        self.vg_name = vg_name
+
+    def supports_thin_provisioning():
+        return False
+
+    def get_all_volumes(vg_name=None, no_suffix=True):
+        if vg_name is not None:
+            return [vg_name]
+        return ['cinder-volumes', 'fake-vg-1']
+
+    def get_volumes(self):
+        return ['fake-volume']
+
+    def get_volume(self, name):
+        return ['name']
+
+    def get_all_physical_volumes(vg_name=None, no_suffix=True):
+        return []
+
+    def get_physical_volumes(self):
+        return []
+
+    def get_all_volume_groups(vg_name=None, no_suffix=True):
+        return ['cinder-volumes', 'fake-vg']
+
+    def update_volume_group_info(self):
+        pass
+
+    def create_thin_pool(self, name=None, size_str=0):
+        pass
+
+    def create_volume(self, name, size_str, lv_type='default', mirror_count=0):
+        pass
+
+    def create_lv_snapshot(self, name, source_lv_name, lv_type='default'):
+        pass
+
+    def delete(self, name):
+        pass
+
+    def revert(self, snapshot_name):
+        pass
+
+    def lv_has_snapshot(self, name):
+        return False
index 1751ece7d875d2acb98df1ff8197cda738b60b7a..264be48597c1ecf5b2ac15b7cb9864748cfd71f8 100644 (file)
@@ -44,6 +44,7 @@ class BrickLvmTestCase(test.TestCase):
         self.stubs.Set(processutils, 'execute',
                        self.fake_execute)
         self.vg = brick.LVM(self.configuration.volume_group_name,
+                            'sudo',
                             False, None,
                             'default',
                             self.fake_execute)
@@ -117,7 +118,7 @@ class BrickLvmTestCase(test.TestCase):
         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()
+        pvs = self.vg.get_all_physical_volumes('sudo')
         self.assertEqual(len(pvs), 3)
 
     def test_get_physical_volumes(self):
@@ -125,8 +126,9 @@ class BrickLvmTestCase(test.TestCase):
         self.assertEqual(len(pvs), 1)
 
     def test_get_volume_groups(self):
-        self.assertEqual(len(self.vg.get_all_volume_groups()), 3)
-        self.assertEqual(len(self.vg.get_all_volume_groups('fake-volumes')), 1)
+        self.assertEqual(len(self.vg.get_all_volume_groups('sudo')), 3)
+        self.assertEqual(len(self.vg.get_all_volume_groups('sudo',
+                                                           'fake-volumes')), 1)
 
     def test_thin_support(self):
         # lvm.supports_thin() is a static method and doesn't
@@ -134,13 +136,13 @@ class BrickLvmTestCase(test.TestCase):
         # so we need to stub proessutils.execute appropriately
 
         self.stubs.Set(processutils, 'execute', self.fake_execute)
-        self.assertTrue(self.vg.supports_thin_provisioning())
+        self.assertTrue(self.vg.supports_thin_provisioning('sudo'))
 
         self.stubs.Set(processutils, 'execute', self.fake_pretend_lvm_version)
-        self.assertTrue(self.vg.supports_thin_provisioning())
+        self.assertTrue(self.vg.supports_thin_provisioning('sudo'))
 
         self.stubs.Set(processutils, 'execute', self.fake_old_lvm_version)
-        self.assertFalse(self.vg.supports_thin_provisioning())
+        self.assertFalse(self.vg.supports_thin_provisioning('sudo'))
 
     def test_lv_has_snapshot(self):
         self.assertTrue(self.vg.lv_has_snapshot('fake-volumes'))
index 6f352a7c7739a152efae3516a025b95b61a85759..37f5621bd75ec91c5bcc08fa6eea0fa33dbd3148 100644 (file)
@@ -13,6 +13,7 @@
 #    under the License.
 
 from cinder.openstack.common import log as logging
+from cinder.tests.brick.fake_lvm import FakeBrickLVM
 from cinder.volume import driver
 from cinder.volume.drivers import lvm
 
@@ -25,6 +26,9 @@ class FakeISCSIDriver(lvm.LVMISCSIDriver):
     def __init__(self, *args, **kwargs):
         super(FakeISCSIDriver, self).__init__(execute=self.fake_execute,
                                               *args, **kwargs)
+        self.vg = FakeBrickLVM('cinder-volumes', False,
+                               None, 'default',
+                               self.fake_execute)
 
     def check_for_setup_error(self):
         """No setup necessary in fake mode."""
index c000e01ed630e309f44722ea48880eedb92f3e2c..aa91f3d9e0cf98c870d9cc25b10a223fe205ec24 100644 (file)
@@ -22,7 +22,6 @@ Tests for Volume Code.
 
 import datetime
 import os
-import re
 import shutil
 import socket
 import tempfile
@@ -30,9 +29,8 @@ import tempfile
 import mox
 from oslo.config import cfg
 
-from cinder.brick.initiator import connector as brick_conn
 from cinder.brick.iscsi import iscsi
-from cinder.brick.iser import iser
+from cinder.brick.local_dev import lvm as brick_lvm
 from cinder import context
 from cinder import db
 from cinder import exception
@@ -45,6 +43,7 @@ from cinder.openstack.common import rpc
 import cinder.policy
 from cinder import quota
 from cinder import test
+from cinder.tests.brick.fake_lvm import FakeBrickLVM
 from cinder.tests import conf_fixture
 from cinder.tests.image import fake as fake_image
 from cinder.tests.keymgr import fake as fake_keymgr
@@ -78,8 +77,12 @@ class VolumeTestCase(test.TestCase):
         self.volume = importutils.import_object(CONF.volume_manager)
         self.context = context.get_admin_context()
         self.stubs.Set(iscsi.TgtAdm, '_get_target', self.fake_get_target)
+        self.stubs.Set(brick_lvm.LVM,
+                       'get_all_volume_groups',
+                       self.fake_get_all_volume_groups)
         fake_image.stub_out_image_service(self.stubs)
         test_notifier.NOTIFICATIONS = []
+        self.stubs.Set(brick_lvm.LVM, '_vg_exists', lambda x: True)
 
     def tearDown(self):
         try:
@@ -92,6 +95,13 @@ class VolumeTestCase(test.TestCase):
     def fake_get_target(obj, iqn):
         return 1
 
+    def fake_get_all_volume_groups(obj, vg_name=None, no_suffix=True):
+        return [{'name': 'cinder-volumes',
+                 'size': '5.00',
+                 'available': '2.50',
+                 'lv_count': '2',
+                 'uuid': 'vR1JU3-FAKE-C4A9-PQFh-Mctm-9FwA-Xwzc1m'}]
+
     @staticmethod
     def _create_volume(size=0, snapshot_id=None, image_id=None,
                        source_volid=None, metadata=None, status="creating",
@@ -707,7 +717,6 @@ class VolumeTestCase(test.TestCase):
         """Test snapshot can be created with metadata and deleted."""
         test_meta = {'fake_key': 'fake_value'}
         volume = self._create_volume(0, None)
-        volume_id = volume['id']
         snapshot = self._create_snapshot(volume['id'], metadata=test_meta)
         snapshot_id = snapshot['id']
 
@@ -877,6 +886,12 @@ class VolumeTestCase(test.TestCase):
 
     def test_delete_busy_snapshot(self):
         """Test snapshot can be created and deleted."""
+
+        self.volume.driver.vg = FakeBrickLVM('cinder-volumes',
+                                             False,
+                                             None,
+                                             'default')
+
         volume = self._create_volume()
         volume_id = volume['id']
         self.volume.create_volume(self.context, volume_id)
@@ -884,6 +899,7 @@ class VolumeTestCase(test.TestCase):
         self.volume.create_snapshot(self.context, volume_id, snapshot_id)
 
         self.mox.StubOutWithMock(self.volume.driver, 'delete_snapshot')
+
         self.volume.driver.delete_snapshot(
             mox.IgnoreArg()).AndRaise(
                 exception.SnapshotIsBusy(snapshot_name='fake'))
@@ -1099,8 +1115,6 @@ class VolumeTestCase(test.TestCase):
                         'disk_format': 'raw',
                         'container_format': 'bare'}
 
-        image_id = '70a599e0-31e7-49b7-b260-868f441e862b'
-
         try:
             volume_id = None
             volume_api = cinder.volume.api.API(
@@ -1125,8 +1139,6 @@ class VolumeTestCase(test.TestCase):
                         'disk_format': 'raw',
                         'container_format': 'bare'}
 
-        image_id = '70a599e0-31e7-49b7-b260-868f441e862b'
-
         volume_api = cinder.volume.api.API(image_service=_FakeImageService())
 
         self.assertRaises(exception.InvalidInput,
@@ -1146,8 +1158,6 @@ class VolumeTestCase(test.TestCase):
                         'container_format': 'bare',
                         'min_disk': 5}
 
-        image_id = '70a599e0-31e7-49b7-b260-868f441e862b'
-
         volume_api = cinder.volume.api.API(image_service=_FakeImageService())
 
         self.assertRaises(exception.InvalidInput,
@@ -1317,8 +1327,6 @@ class VolumeTestCase(test.TestCase):
         ctx = context.get_admin_context(read_deleted="yes")
 
         # Find all all snapshots valid within a timeframe window.
-        vol = db.volume_create(self.context, {'id': 1})
-
         # Not in window
         db.snapshot_create(
             ctx,
@@ -1699,6 +1707,7 @@ class DriverTestCase(test.TestCase):
         self.context = context.get_admin_context()
         self.output = ""
         self.stubs.Set(iscsi.TgtAdm, '_get_target', self.fake_get_target)
+        self.stubs.Set(brick_lvm.LVM, '_vg_exists', lambda x: True)
 
         def _fake_execute(_command, *_args, **_kwargs):
             """Fake _execute."""
@@ -1736,14 +1745,20 @@ class LVMISCSIVolumeDriverTestCase(DriverTestCase):
                        lambda x: False)
         self.stubs.Set(self.volume.driver, '_delete_volume',
                        lambda x: False)
-        # Want DriverTestCase._fake_execute to return 'o' so that
-        # volume.driver.delete_volume() raises the VolumeIsBusy exception.
-        self.output = 'o'
+
+        self.volume.driver.vg = FakeBrickLVM('cinder-volumes',
+                                             False,
+                                             None,
+                                             'default')
+
+        self.stubs.Set(self.volume.driver.vg, 'lv_has_snapshot',
+                       lambda x: True)
         self.assertRaises(exception.VolumeIsBusy,
                           self.volume.driver.delete_volume,
                           {'name': 'test1', 'size': 1024})
-        # when DriverTestCase._fake_execute returns something other than
-        # 'o' volume.driver.delete_volume() does not raise an exception.
+
+        self.stubs.Set(self.volume.driver.vg, 'lv_has_snapshot',
+                       lambda x: False)
         self.output = 'x'
         self.volume.driver.delete_volume({'name': 'test1', 'size': 1024})
 
@@ -1784,7 +1799,8 @@ class LVMISCSIVolumeDriverTestCase(DriverTestCase):
 
     def test_lvm_migrate_volume_proceed(self):
         hostname = socket.gethostname()
-        capabilities = {'location_info': 'LVMVolumeDriver:%s:bar' % hostname}
+        capabilities = {'location_info': 'LVMVolumeDriver:%s:'
+                        'cinder-volumes:default:0' % hostname}
         host = {'capabilities': capabilities}
         vol = {'name': 'test', 'id': 1, 'size': 1}
         self.stubs.Set(self.volume.driver, 'remove_export',
@@ -1797,6 +1813,11 @@ class LVMISCSIVolumeDriverTestCase(DriverTestCase):
                        lambda x: None)
         self.stubs.Set(self.volume.driver, '_create_export',
                        lambda x, y, vg='vg': None)
+
+        self.volume.driver.vg = FakeBrickLVM('cinder-volumes',
+                                             False,
+                                             None,
+                                             'default')
         moved, model_update = self.volume.driver.migrate_volume(self.context,
                                                                 vol, host)
         self.assertEqual(moved, True)
@@ -1884,7 +1905,18 @@ class ISCSITestCase(DriverTestCase):
             out += " test2-volumes  5.52  0.52"
             return out, None
 
+        def _fake_get_all_volume_groups(obj, vg_name=None, no_suffix=True):
+            return [{'name': 'cinder-volumes',
+                     'size': '5.52',
+                     'available': '0.52',
+                     'lv_count': '2',
+                     'uuid': 'vR1JU3-FAKE-C4A9-PQFh-Mctm-9FwA-Xwzc1m'}]
+
+        self.stubs.Set(brick_lvm.LVM,
+                       'get_all_volume_groups',
+                       _fake_get_all_volume_groups)
         self.volume.driver.set_execute(_emulate_vgs_execute)
+        self.volume.driver.vg = brick_lvm.LVM('cinder-volumes', 'sudo')
 
         self.volume.driver._update_volume_stats()
 
@@ -1955,6 +1987,7 @@ class VolumePolicyTestCase(test.TestCase):
         cinder.policy.init()
 
         self.context = context.get_admin_context()
+        self.stubs.Set(brick_lvm.LVM, '_vg_exists', lambda x: True)
 
     def tearDown(self):
         super(VolumePolicyTestCase, self).tearDown()
index 7c596554ac75948ed9257e0aecf3746e7823d38a..2b01cc031eb78feeb2dc8bdd9c883f6f1f1e38ef 100644 (file)
@@ -20,7 +20,6 @@ Driver for Linux servers running LVM.
 
 """
 
-import math
 import os
 import re
 import socket
@@ -29,6 +28,7 @@ from oslo.config import cfg
 
 from cinder.brick.iscsi import iscsi
 from cinder.brick.iser import iser
+from cinder.brick.local_dev import lvm as lvm
 from cinder import exception
 from cinder.image import image_utils
 from cinder.openstack.common import fileutils
@@ -51,6 +51,10 @@ volume_opts = [
                default=0,
                help='If set, create lvms with multiple mirrors. Note that '
                     'this requires lvm_mirrors + 2 pvs with available space'),
+    cfg.StrOpt('lvm_type',
+               default='default',
+               help='Type of LVM volumes to deploy; (default or thin)'),
+
 ]
 
 CONF = cfg.CONF
@@ -60,84 +64,115 @@ CONF.register_opts(volume_opts)
 class LVMVolumeDriver(driver.VolumeDriver):
     """Executes commands relating to Volumes."""
 
-    VERSION = '1.0.0'
+    VERSION = '2.0.0'
 
-    def __init__(self, *args, **kwargs):
+    def __init__(self, vg_obj=None, *args, **kwargs):
         super(LVMVolumeDriver, self).__init__(*args, **kwargs)
         self.configuration.append_config_values(volume_opts)
         self.hostname = socket.gethostname()
+        self.vg = vg_obj
 
-    def check_for_setup_error(self):
-        """Returns an error if prerequisites aren't met"""
-        out, err = self._execute('vgs', '--noheadings', '-o', 'name',
-                                 run_as_root=True)
-        volume_groups = out.split()
-        if self.configuration.volume_group not in volume_groups:
-            exception_message = (_("volume group %s doesn't exist")
-                                 % self.configuration.volume_group)
-            raise exception.VolumeBackendAPIException(data=exception_message)
-
-    def _create_volume(self, volume_name, sizestr, vg=None):
-        if vg is None:
-            vg = self.configuration.volume_group
-        no_retry_list = ['Insufficient free extents',
-                         'One or more specified logical volume(s) not found']
+    def set_execute(self, execute):
+        self._execute = execute
 
-        cmd = ['lvcreate', '-L', sizestr, '-n', volume_name, vg]
-        if self.configuration.lvm_mirrors:
-            cmd.extend(['-m', self.configuration.lvm_mirrors, '--nosync'])
-            terras = int(sizestr[:-1]) / 1024.0
-            if terras >= 1.5:
-                rsize = int(2 ** math.ceil(math.log(terras) / math.log(2)))
-                # NOTE(vish): Next power of two for region size. See:
-                #             http://red.ht/U2BPOD
-                cmd.extend(['-R', str(rsize)])
+    def check_for_setup_error(self):
+        """Verify that requirements are in place to use LVM driver."""
+        if self.vg is None:
+            root_helper = 'sudo cinder-rootwrap %s' % CONF.rootwrap_config
+            try:
+                self.vg = lvm.LVM(self.configuration.volume_group,
+                                  root_helper,
+                                  lvm_type=self.configuration.lvm_type,
+                                  executor=self._execute)
+            except lvm.VolumeGroupNotFound:
+                message = ("Volume Group %s does not exist" %
+                           self.configuration.volume_group)
+                raise exception.VolumeBackendAPIException(data=message)
+
+        vg_list = volutils.get_all_volume_groups(
+            self.configuration.volume_group)
+        vg_dict = \
+            (vg for vg in vg_list if vg['name'] == self.vg.vg_name).next()
+        if vg_dict is None:
+            message = ("Volume Group %s does not exist" %
+                       self.configuration.volume_group)
+            raise exception.VolumeBackendAPIException(data=message)
+
+        if self.configuration.lvm_type == 'thin':
+            # Specific checks for using Thin provisioned LV's
+            if not volutils.supports_thin_provisioning():
+                message = ("Thin provisioning not supported "
+                           "on this version of LVM.")
+                raise exception.VolumeBackendAPIException(data=message)
+
+            pool_name = "%s-pool" % self.configuration.volume_group
+            if self.vg.get_volume(pool_name) is None:
+                try:
+                    self.vg.create_thin_pool(pool_name)
+                except exception.ProcessExecutionError as exc:
+                    exception_message = ("Failed to create thin pool, "
+                                         "error message was: %s"
+                                         % exc.stderr)
+                    raise exception.VolumeBackendAPIException(
+                        data=exception_message)
 
-        self._try_execute(*cmd, run_as_root=True, no_retry_list=no_retry_list)
+    def _sizestr(self, size_in_g):
+        if int(size_in_g) == 0:
+            return '100m'
+        return '%sg' % size_in_g
 
     def _volume_not_present(self, volume_name):
-        path_name = '%s/%s' % (self.configuration.volume_group, volume_name)
-        try:
-            self._try_execute('lvdisplay', path_name, run_as_root=True)
-        except Exception as e:
-            # If the volume isn't present
-            return True
-        return False
+        return self.vg.get_volume(volume_name) is None
 
-    def _delete_volume(self, volume):
+    def _delete_volume(self, volume, is_snapshot=False):
         """Deletes a logical volume."""
+
         # zero out old volumes to prevent data leaking between users
         # TODO(ja): reclaiming space should be done lazy and low priority
         dev_path = self.local_path(volume)
-        if os.path.exists(dev_path):
-            self.clear_volume(volume)
 
-        self._try_execute('lvremove', '-f', "%s/%s" %
-                          (self.configuration.volume_group,
-                           self._escape_snapshot(volume['name'])),
-                          run_as_root=True)
-
-    def _sizestr(self, size_in_g):
-        if int(size_in_g) == 0:
-            return '100M'
-        return '%sG' % size_in_g
+        # TODO(jdg): Maybe we could optimize this for snaps by looking at
+        # the cow table and only overwriting what's necessary?
+        # for now we're still skipping on snaps due to hang issue
+        if os.path.exists(dev_path) and not is_snapshot:
+            self.clear_volume(volume)
+        name = volume['name']
+        if is_snapshot:
+            name = self._escape_snapshot(volume['name'])
+        self.vg.delete(name)
 
-    # Linux LVM reserves name that starts with snapshot, so that
-    # such volume name can't be created. Mangle it.
     def _escape_snapshot(self, snapshot_name):
+        # Linux LVM reserves name that starts with snapshot, so that
+        # such volume name can't be created. Mangle it.
         if not snapshot_name.startswith('snapshot'):
             return snapshot_name
         return '_' + snapshot_name
 
+    def _create_volume(self, name, size, lvm_type, mirror_count, vg=None):
+        vg_ref = self.vg
+        if vg is not None:
+            vg_ref = vg
+
+        vg_ref.create_volume(name, size, lvm_type, mirror_count)
+
     def create_volume(self, volume):
-        """Creates a logical volume. Can optionally return a Dictionary of
-        changes to the volume object to be persisted.
-        """
-        self._create_volume(volume['name'], self._sizestr(volume['size']))
+        """Creates a logical volume."""
+        mirror_count = 0
+        if self.configuration.lvm_mirrors:
+            mirror_count = self.configuration.lvm_mirrors
+
+        self._create_volume(volume['name'],
+                            self._sizestr(volume['size']),
+                            self.configuration.lvm_type,
+                            mirror_count)
 
     def create_volume_from_snapshot(self, volume, snapshot):
         """Creates a volume from a snapshot."""
-        self._create_volume(volume['name'], self._sizestr(volume['size']))
+        self._create_volume(volume['name'],
+                            self._sizestr(volume['size']),
+                            self.configuration.lvm_type,
+                            self.configuration.lvm_mirrors)
+
         volutils.copy_volume(self.local_path(snapshot),
                              self.local_path(volume),
                              snapshot['volume_size'] * 1024,
@@ -149,18 +184,8 @@ class LVMVolumeDriver(driver.VolumeDriver):
             # If the volume isn't present, then don't attempt to delete
             return True
 
-        # TODO(yamahata): lvm can't delete origin volume only without
-        # deleting derived snapshots. Can we do something fancy?
-        out, err = self._execute('lvdisplay', '--noheading',
-                                 '-C', '-o', 'Attr',
-                                 '%s/%s' % (self.configuration.volume_group,
-                                            volume['name']),
-                                 run_as_root=True)
-        # fake_execute returns None resulting unit test error
-        if out:
-            out = out.strip()
-            if (out[0] == 'o') or (out[0] == 'O'):
-                raise exception.VolumeIsBusy(volume_name=volume['name'])
+        if self.vg.lv_has_snapshot(volume['name']):
+            raise exception.VolumeIsBusy(volume_name=volume['name'])
 
         self._delete_volume(volume)
 
@@ -202,12 +227,10 @@ class LVMVolumeDriver(driver.VolumeDriver):
 
     def create_snapshot(self, snapshot):
         """Creates a snapshot."""
-        orig_lv_name = "%s/%s" % (self.configuration.volume_group,
-                                  snapshot['volume_name'])
-        self._try_execute('lvcreate', '-L',
-                          self._sizestr(snapshot['volume_size']),
-                          '--name', self._escape_snapshot(snapshot['name']),
-                          '--snapshot', orig_lv_name, run_as_root=True)
+
+        self.vg.create_lv_snapshot(self._escape_snapshot(snapshot['name']),
+                                   snapshot['volume_name'],
+                                   self.configuration.lvm_type)
 
     def delete_snapshot(self, snapshot):
         """Deletes a snapshot."""
@@ -219,7 +242,7 @@ class LVMVolumeDriver(driver.VolumeDriver):
 
         # TODO(yamahata): zeroing out the whole snapshot triggers COW.
         # it's quite slow.
-        self._delete_volume(snapshot)
+        self._delete_volume(snapshot, is_snapshot=True)
 
     def local_path(self, volume, vg=None):
         if vg is None:
@@ -245,6 +268,10 @@ class LVMVolumeDriver(driver.VolumeDriver):
 
     def create_cloned_volume(self, volume, src_vref):
         """Creates a clone of the specified volume."""
+
+        mirror_count = 0
+        if self.configuration.lvm_mirrors:
+            mirror_count = self.configuration.lvm_mirrors
         LOG.info(_('Creating clone of volume: %s') % src_vref['id'])
         volume_name = src_vref['name']
         temp_id = 'tmp-snap-%s' % volume['id']
@@ -253,8 +280,13 @@ class LVMVolumeDriver(driver.VolumeDriver):
                          'volume_size': src_vref['size'],
                          'name': 'clone-snap-%s' % volume['id'],
                          'id': temp_id}
+
         self.create_snapshot(temp_snapshot)
-        self._create_volume(volume['name'], self._sizestr(volume['size']))
+        self._create_volume(volume['name'],
+                            self._sizestr(volume['size']),
+                            self.configuration.lvm_type,
+                            mirror_count)
+
         try:
             volutils.copy_volume(self.local_path(temp_snapshot),
                                  self.local_path(volume),
@@ -281,6 +313,47 @@ class LVMVolumeDriver(driver.VolumeDriver):
             with fileutils.file_open(volume_path, 'wb') as volume_file:
                 backup_service.restore(backup, volume['id'], volume_file)
 
+    def get_volume_stats(self, refresh=False):
+        """Get volume status.
+
+        If 'refresh' is True, run update the stats first.
+        """
+
+        if refresh:
+            self._update_volume_status()
+        self._update_volume_status()
+
+        return self._stats
+
+    def _update_volume_status(self):
+        """Retrieve status info from volume group."""
+
+        # FIXME(jdg): Fix up the duplicate code between
+        # LVM, LVMISCSI and ISER starting with this section
+        LOG.debug(_("Updating volume status"))
+        data = {}
+
+        backend_name = self.configuration.safe_get('volume_backend_name')
+        data["volume_backend_name"] = backend_name or 'LVM'
+        data["vendor_name"] = 'Open Source'
+        data["driver_version"] = self.VERSION
+        data["storage_protocol"] = 'local'
+
+        data['total_capacity_gb'] = float(self.vg.vg_size.replace(',', '.'))
+        data['free_capacity_gb'] =\
+            float(self.vg.vg_free_space.replace(',', '.'))
+        data['reserved_percentage'] = self.configuration.reserved_percentage
+        data['QoS_support'] = False
+        data['location_info'] =\
+            ('LVMVolumeDriver:%(hostname)s:%(vg)s'
+             ':%(lvm_type)s:%(lvm_mirrors)s' %
+             {'hostname': self.hostname,
+              'vg': self.configuration.volume_group,
+              'lvm_type': self.configuration.lvm_type,
+              'lvm_mirrors': self.configuration.lvm_mirrors})
+
+        self._stats = data
+
 
 class LVMISCSIDriver(LVMVolumeDriver, driver.ISCSIDriver):
     """Executes commands relating to ISCSI volumes.
@@ -322,7 +395,7 @@ class LVMISCSIDriver(LVMVolumeDriver, driver.ISCSIDriver):
                                                        auth_user,
                                                        auth_pass)
             except exception.NotFound:
-                LOG.debug("volume_info:", volume_info)
+                LOG.debug(_("volume_info:%s"), volume_info)
                 LOG.info(_("Skipping ensure_export. No iscsi_target "
                            "provision for volume: %s"), volume['id'])
                 return
@@ -359,7 +432,7 @@ class LVMISCSIDriver(LVMVolumeDriver, driver.ISCSIDriver):
                 volume['name'] not in volume['provider_location']):
 
             msg = _('Detected inconsistency in provider_location id')
-            LOG.debug(msg)
+            LOG.debug(_('%s'), msg)
             old_name = self._fix_id_migration(context, volume)
             if 'in-use' in volume['status']:
                 volume_name = old_name
@@ -525,35 +598,52 @@ class LVMISCSIDriver(LVMVolumeDriver, driver.ISCSIDriver):
             # this export has already been removed
             self.tgtadm.show_target(iscsi_target, iqn=iqn)
 
-        except Exception as e:
+        except Exception:
             LOG.info(_("Skipping remove_export. No iscsi_target "
                        "is presently exported for volume: %s"), volume['id'])
             return
 
         self.tgtadm.remove_iscsi_target(iscsi_target, 0, volume['id'])
 
-    def migrate_volume(self, ctxt, volume, host):
+    def migrate_volume(self, ctxt, volume, host, thin=False, mirror_count=0):
         """Optimize the migration if the destination is on the same server.
 
         If the specified host is another back-end on the same server, and
         the volume is not attached, we can do the migration locally without
         going through iSCSI.
         """
+
         false_ret = (False, None)
         if 'location_info' not in host['capabilities']:
             return false_ret
         info = host['capabilities']['location_info']
         try:
-            (dest_type, dest_hostname, dest_vg) = info.split(':')
+            (dest_type, dest_hostname, dest_vg, lvm_type, lvm_mirrors) =\
+                info.split(':')
         except ValueError:
             return false_ret
         if (dest_type != 'LVMVolumeDriver' or dest_hostname != self.hostname):
             return false_ret
 
-        self.remove_export(ctxt, volume)
-        self._create_volume(volume['name'],
-                            self._sizestr(volume['size']),
-                            dest_vg)
+        if dest_vg != self.vg.vg_name:
+            vg_list = volutils.get_all_volume_groups()
+            vg_dict = \
+                (vg for vg in vg_list if vg['name'] == self.vg.vg_name).next()
+            if vg_dict is None:
+                message = ("Destination Volume Group %s does not exist" %
+                           dest_vg)
+                LOG.error(_('%s'), message)
+                return false_ret
+
+            helper = 'sudo cinder-rootwrap %s' % CONF.rootwrap_config
+            dest_vg_ref = lvm.LVM(dest_vg, helper, lvm_type, self._execute)
+            self.remove_export(ctxt, volume)
+            self._create_volume(volume['name'],
+                                self._sizestr(volume['size']),
+                                lvm_type,
+                                lvm_mirrors,
+                                dest_vg_ref)
+
         volutils.copy_volume(self.local_path(volume),
                              self.local_path(volume, vg=dest_vg),
                              volume['size'],
@@ -582,6 +672,7 @@ class LVMISCSIDriver(LVMVolumeDriver, driver.ISCSIDriver):
         """Retrieve stats info from volume group."""
 
         LOG.debug(_("Updating volume stats"))
+        self.vg.update_volume_group_info()
         data = {}
 
         # Note(zhiteng): These information are driver/backend specific,
@@ -593,27 +684,17 @@ class LVMISCSIDriver(LVMVolumeDriver, driver.ISCSIDriver):
         data["driver_version"] = self.VERSION
         data["storage_protocol"] = 'iSCSI'
 
-        data['total_capacity_gb'] = 0
-        data['free_capacity_gb'] = 0
+        data['total_capacity_gb'] = float(self.vg.vg_size.replace(',', '.'))
+        data['free_capacity_gb'] = float(self.vg.vg_free_space)
         data['reserved_percentage'] = self.configuration.reserved_percentage
         data['QoS_support'] = False
-        data['location_info'] = ('LVMVolumeDriver:%(hostname)s:%(vg)s' %
-                                 {'hostname': self.hostname,
-                                 'vg': self.configuration.volume_group})
-
-        try:
-            out, err = self._execute('vgs', '--noheadings', '--nosuffix',
-                                     '--unit=G', '-o', 'name,size,free',
-                                     self.configuration.volume_group,
-                                     run_as_root=True)
-        except exception.ProcessExecutionError as exc:
-            LOG.error(_("Error retrieving volume stats: %s"), exc.stderr)
-            out = False
-
-        if out:
-            volume = out.split()
-            data['total_capacity_gb'] = float(volume[1].replace(',', '.'))
-            data['free_capacity_gb'] = float(volume[2].replace(',', '.'))
+        data['location_info'] =\
+            ('LVMVolumeDriver:%(hostname)s:%(vg)s'
+             ':%(lvm_type)s:%(lvm_mirrors)s' %
+             {'hostname': self.hostname,
+              'vg': self.configuration.volume_group,
+              'lvm_type': self.configuration.lvm_type,
+              'lvm_mirrors': self.configuration.lvm_mirrors})
 
         self._stats = data
 
@@ -764,7 +845,7 @@ class LVMISERDriver(LVMISCSIDriver, driver.ISERDriver):
 
             self.tgtadm.show_target(iser_target, iqn=iqn)
 
-        except Exception as e:
+        except Exception:
             LOG.info(_("Skipping remove_export. No iser_target "
                        "is presently exported for volume: %s"), volume['id'])
             return
@@ -775,6 +856,7 @@ class LVMISERDriver(LVMISCSIDriver, driver.ISERDriver):
         """Retrieve status info from volume group."""
 
         LOG.debug(_("Updating volume status"))
+        self.vg.update_volume_group_info()
         data = {}
 
         # Note(zhiteng): These information are driver/backend specific,
@@ -785,26 +867,13 @@ class LVMISERDriver(LVMISCSIDriver, driver.ISERDriver):
         data["vendor_name"] = 'Open Source'
         data["driver_version"] = self.VERSION
         data["storage_protocol"] = 'iSER'
+        data['total_capacity_gb'] = float(self.vg.vg_size.replace(',', '.'))
+        data['free_capacity_gb'] =\
+            float(self.vg.vg_free_space.replace(',', '.'))
 
-        data['total_capacity_gb'] = 0
-        data['free_capacity_gb'] = 0
         data['reserved_percentage'] = self.configuration.reserved_percentage
         data['QoS_support'] = False
 
-        try:
-            out, err = self._execute('vgs', '--noheadings', '--nosuffix',
-                                     '--unit=G', '-o', 'name,size,free',
-                                     self.configuration.volume_group,
-                                     run_as_root=True)
-        except exception.ProcessExecutionError as exc:
-            LOG.error(_("Error retrieving volume status: %s"), exc.stderr)
-            out = False
-
-        if out:
-            volume = out.split()
-            data['total_capacity_gb'] = float(volume[1].replace(',', '.'))
-            data['free_capacity_gb'] = float(volume[2].replace(',', '.'))
-
         self._stats = data
 
     def _iser_location(self, ip, target, iqn, lun=None):
@@ -813,102 +882,3 @@ class LVMISERDriver(LVMISCSIDriver, driver.ISERDriver):
 
     def _iser_authentication(self, chap, name, password):
         return "%s %s %s" % (chap, name, password)
-
-
-class ThinLVMVolumeDriver(LVMISCSIDriver):
-    """Subclass for thin provisioned LVM's."""
-
-    VERSION = '1.0'
-
-    def __init__(self, *args, **kwargs):
-        super(ThinLVMVolumeDriver, self).__init__(*args, **kwargs)
-
-    def check_for_setup_error(self):
-        """Returns an error if prerequisites aren't met"""
-        out, err = self._execute('lvs', '--option',
-                                 'name', '--noheadings',
-                                 run_as_root=True)
-        pool_name = "%s-pool" % self.configuration.volume_group
-        if pool_name not in out:
-            if not self.configuration.pool_size:
-                out, err = self._execute('vgs',
-                                         self.configuration.volume_group,
-                                         '--noheadings',
-                                         '--options',
-                                         'name,size',
-                                         run_as_root=True)
-
-                size = re.sub(r'[\.][\d][\d]', '', out.split()[1])
-            else:
-                size = "%s" % self.configuration.pool_size
-
-            pool_path = '%s/%s' % (self.configuration.volume_group,
-                                   pool_name)
-            out, err = self._execute('lvcreate', '-T', '-L', size,
-                                     pool_path, run_as_root=True)
-
-    def _do_lvm_snapshot(self, src_lvm_name, dest_vref, is_cinder_snap=True):
-            if is_cinder_snap:
-                new_name = self._escape_snapshot(dest_vref['name'])
-            else:
-                new_name = dest_vref['name']
-
-            self._try_execute('lvcreate', '-s', '-n', new_name,
-                              src_lvm_name, run_as_root=True)
-
-    def _create_volume(self, volume_name, sizestr):
-        vg_name = ("%s/%s-pool" % (self.configuration.volume_group,
-                                   self.configuration.volume_group))
-        self._try_execute('lvcreate', '-T', '-V', sizestr, '-n',
-                          volume_name, vg_name, run_as_root=True)
-
-    def delete_volume(self, volume):
-        """Deletes a logical volume."""
-        if self._volume_not_present(volume['name']):
-            return True
-        self._try_execute('lvremove', '-f', "%s/%s" %
-                          (self.configuration.volume_group,
-                           self._escape_snapshot(volume['name'])),
-                          run_as_root=True)
-
-    def create_cloned_volume(self, volume, src_vref):
-        """Creates a clone of the specified volume."""
-        LOG.info(_('Creating clone of volume: %s') % src_vref['id'])
-        orig_lv_name = "%s/%s" % (self.configuration.volume_group,
-                                  src_vref['name'])
-        self._do_lvm_snapshot(orig_lv_name, volume, False)
-
-    def create_snapshot(self, snapshot):
-        """Creates a snapshot of a volume."""
-        orig_lv_name = "%s/%s" % (self.configuration.volume_group,
-                                  snapshot['volume_name'])
-        self._do_lvm_snapshot(orig_lv_name, snapshot)
-
-    def get_volume_stats(self, refresh=False):
-        """Get volume stats.
-        If 'refresh' is True, run update the stats first.
-        """
-        if refresh:
-            self._update_volume_stats()
-
-        return self._stats
-
-    def _update_volume_stats(self):
-        """Retrieve stats info from volume group."""
-
-        LOG.debug(_("Updating volume stats"))
-        data = {}
-
-        backend_name = self.configuration.safe_get('volume_backend_name')
-        data["volume_backend_name"] = backend_name or self.__class__.__name__
-        data["vendor_name"] = 'Open Source'
-        data["driver_version"] = self.VERSION
-        data["storage_protocol"] = 'iSCSI'
-        data['reserved_percentage'] = self.configuration.reserved_percentage
-        data['QoS_support'] = False
-        data['total_capacity_gb'] = 'infinite'
-        data['free_capacity_gb'] = 'infinite'
-        data['location_info'] = ('LVMVolumeDriver:%(hostname)s:%(vg)s' %
-                                 {'hostname': self.hostname,
-                                 'vg': self.configuration.volume_group})
-        self._stats = data
index d29e7649114b0f0225b240818a667492391a7d1e..41a2e19ec3effad7945580954b900baa6b2a06cd 100644 (file)
@@ -23,6 +23,7 @@ import stat
 
 from oslo.config import cfg
 
+from cinder.brick.local_dev import lvm as brick_lvm
 from cinder import exception
 from cinder.openstack.common import log as logging
 from cinder.openstack.common.notifier import api as notifier_api
@@ -192,3 +193,26 @@ def copy_volume(srcstr, deststr, size_in_m, sync=False,
             'count=%d' % count,
             'bs=%s' % blocksize,
             *extra_flags, run_as_root=True)
+
+
+def supports_thin_provisioning():
+    return brick_lvm.LVM.supports_thin_provisioning(
+        'sudo cinder-rootwrap %s' % CONF.rootwrap_config)
+
+
+def get_all_volumes(vg_name=None, no_suffix=True):
+    return brick_lvm.LVM.get_all_volumes(
+        'sudo cinder-rootwrap %s' % CONF.rootwrap_config,
+        vg_name, no_suffix)
+
+
+def get_all_physical_volumes(vg_name=None, no_suffix=True):
+    return brick_lvm.LVM.get_all_physical_volumes(
+        'sudo cinder-rootwrap %s' % CONF.rootwrap_config,
+        vg_name, no_suffix)
+
+
+def get_all_volume_groups(vg_name=None, no_suffix=True):
+    return brick_lvm.LVM.get_all_volume_groups(
+        'sudo cinder-rootwrap %s' % CONF.rootwrap_config,
+        vg_name, no_suffix)