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
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
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', '')
"""
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()
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',
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:
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
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:
: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):
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
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:
: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
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:
: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)
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.
"""
- 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.'))
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
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'))
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'))
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.
"""
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'):
import datetime
import os
-import re
import shutil
import socket
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
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
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:
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",
"""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']
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)
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'))
'disk_format': 'raw',
'container_format': 'bare'}
- image_id = '70a599e0-31e7-49b7-b260-868f441e862b'
-
try:
volume_id = None
volume_api = cinder.volume.api.API(
'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,
'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,
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,
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."""
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})
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',
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)
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()
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()
"""
-import math
import os
import re
import socket
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
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
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,
# 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)
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."""
# 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:
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']
'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),
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.
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
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
# 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'],
"""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,
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
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
"""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,
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):
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