cinder/cinder/tests/unit/volume/drivers/test_lvm_driver.py

988 lines
46 KiB
Python

# 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.
import ddt
import os
import socket
import mock
from oslo_concurrency import processutils
from oslo_config import cfg
from cinder.brick.local_dev import lvm as brick_lvm
from cinder import db
from cinder import exception
from cinder.objects import fields
from cinder.tests import fake_driver
from cinder.tests.unit.brick import fake_lvm
from cinder.tests.unit import fake_constants as fake
from cinder.tests.unit import utils as tests_utils
from cinder.tests.unit.volume import test_driver
from cinder.volume import configuration as conf
from cinder.volume.drivers import lvm
import cinder.volume.utils
from cinder.volume import utils as volutils
CONF = cfg.CONF
fake_opt = [
cfg.StrOpt('fake_opt1', default='fake', help='fake opts')
]
@ddt.ddt
class LVMVolumeDriverTestCase(test_driver.BaseDriverTestCase):
"""Test case for VolumeDriver"""
driver_name = "cinder.volume.drivers.lvm.LVMVolumeDriver"
FAKE_VOLUME = {'name': 'test1',
'id': 'test1'}
@mock.patch.object(os.path, 'exists', return_value=True)
@mock.patch.object(fake_driver.FakeLoggingVolumeDriver, 'create_export')
def test_delete_volume_invalid_parameter(self, _mock_create_export,
mock_exists):
self.configuration.volume_clear = 'zero'
self.configuration.volume_clear_size = 0
lvm_driver = lvm.LVMVolumeDriver(configuration=self.configuration,
db=db)
# Test volume without 'size' field and 'volume_size' field
self.assertRaises(exception.InvalidParameterValue,
lvm_driver._delete_volume,
self.FAKE_VOLUME)
@mock.patch.object(os.path, 'exists', return_value=False)
@mock.patch.object(fake_driver.FakeLoggingVolumeDriver, 'create_export')
def test_delete_volume_bad_path(self, _mock_create_export, mock_exists):
self.configuration.volume_clear = 'zero'
self.configuration.volume_clear_size = 0
self.configuration.volume_type = 'default'
volume = dict(self.FAKE_VOLUME, size=1)
lvm_driver = lvm.LVMVolumeDriver(configuration=self.configuration,
db=db)
self.assertRaises(exception.VolumeBackendAPIException,
lvm_driver._delete_volume, volume)
@mock.patch.object(volutils, 'clear_volume')
@mock.patch.object(volutils, 'copy_volume')
@mock.patch.object(fake_driver.FakeLoggingVolumeDriver, 'create_export')
def test_delete_volume_thinlvm_snap(self, _mock_create_export,
mock_copy, mock_clear):
vg_obj = fake_lvm.FakeBrickLVM('cinder-volumes',
False,
None,
'default')
self.configuration.volume_clear = 'zero'
self.configuration.volume_clear_size = 0
self.configuration.lvm_type = 'thin'
self.configuration.target_helper = 'tgtadm'
lvm_driver = lvm.LVMVolumeDriver(configuration=self.configuration,
vg_obj=vg_obj, db=db)
uuid = '00000000-0000-0000-0000-c3aa7ee01536'
fake_snapshot = {'name': 'volume-' + uuid,
'id': uuid,
'size': 123}
lvm_driver._delete_volume(fake_snapshot, is_snapshot=True)
@mock.patch.object(volutils, 'get_all_volume_groups',
return_value=[{'name': 'cinder-volumes'}])
@mock.patch('cinder.brick.local_dev.lvm.LVM.get_lvm_version',
return_value=(2, 2, 100))
def test_check_for_setup_error(self, _mock_get_version, vgs):
vg_obj = fake_lvm.FakeBrickLVM('cinder-volumes',
False,
None,
'auto')
configuration = conf.Configuration(fake_opt, 'fake_group')
lvm_driver = lvm.LVMVolumeDriver(configuration=configuration,
vg_obj=vg_obj, db=db)
lvm_driver.delete_snapshot = mock.Mock()
volume = tests_utils.create_volume(self.context,
host=socket.gethostname())
volume_id = volume['id']
backup = {}
backup['volume_id'] = volume_id
backup['user_id'] = fake.USER_ID
backup['project_id'] = fake.PROJECT_ID
backup['host'] = socket.gethostname()
backup['availability_zone'] = '1'
backup['display_name'] = 'test_check_for_setup_error'
backup['display_description'] = 'test_check_for_setup_error'
backup['container'] = 'fake'
backup['status'] = fields.BackupStatus.CREATING
backup['fail_reason'] = ''
backup['service'] = 'fake'
backup['parent_id'] = None
backup['size'] = 5 * 1024 * 1024
backup['object_count'] = 22
db.backup_create(self.context, backup)
lvm_driver.check_for_setup_error()
def test_retype_volume(self):
vol = tests_utils.create_volume(self.context)
new_type = fake.VOLUME_TYPE_ID
diff = {}
host = 'fake_host'
retyped = self.volume.driver.retype(self.context, vol, new_type,
diff, host)
self.assertTrue(retyped)
def test_update_migrated_volume(self):
fake_volume_id = fake.VOLUME_ID
fake_new_volume_id = fake.VOLUME2_ID
fake_provider = 'fake_provider'
original_volume_name = CONF.volume_name_template % fake_volume_id
current_name = CONF.volume_name_template % fake_new_volume_id
fake_volume = tests_utils.create_volume(self.context)
fake_volume['id'] = fake_volume_id
fake_new_volume = tests_utils.create_volume(self.context)
fake_new_volume['id'] = fake_new_volume_id
fake_new_volume['provider_location'] = fake_provider
fake_vg = fake_lvm.FakeBrickLVM('cinder-volumes', False,
None, 'default')
with mock.patch.object(self.volume.driver, 'vg') as vg:
vg.return_value = fake_vg
vg.rename_volume.return_value = None
update = self.volume.driver.update_migrated_volume(self.context,
fake_volume,
fake_new_volume,
'available')
vg.rename_volume.assert_called_once_with(current_name,
original_volume_name)
self.assertEqual({'_name_id': None,
'provider_location': None}, update)
vg.rename_volume.reset_mock()
vg.rename_volume.side_effect = processutils.ProcessExecutionError
update = self.volume.driver.update_migrated_volume(self.context,
fake_volume,
fake_new_volume,
'available')
vg.rename_volume.assert_called_once_with(current_name,
original_volume_name)
self.assertEqual({'_name_id': fake_new_volume_id,
'provider_location': fake_provider},
update)
def test_create_volume_from_snapshot_none_sparse(self):
with mock.patch.object(self.volume.driver, 'vg'), \
mock.patch.object(self.volume.driver, '_create_volume'), \
mock.patch.object(volutils, 'copy_volume') as mock_copy:
# Test case for thick LVM
src_volume = tests_utils.create_volume(self.context)
snapshot_ref = tests_utils.create_snapshot(self.context,
src_volume['id'])
dst_volume = tests_utils.create_volume(self.context)
self.volume.driver.create_volume_from_snapshot(dst_volume,
snapshot_ref)
volume_path = self.volume.driver.local_path(dst_volume)
snapshot_path = self.volume.driver.local_path(snapshot_ref)
volume_size = 1024
block_size = '1M'
mock_copy.assert_called_with(snapshot_path,
volume_path,
volume_size,
block_size,
execute=self.volume.driver._execute,
sparse=False)
def test_create_volume_from_snapshot_sparse(self):
self.configuration.lvm_type = 'thin'
lvm_driver = lvm.LVMVolumeDriver(configuration=self.configuration,
db=db)
with mock.patch.object(lvm_driver, 'vg'):
# Test case for thin LVM
lvm_driver._sparse_copy_volume = True
src_volume = tests_utils.create_volume(self.context)
snapshot_ref = tests_utils.create_snapshot(self.context,
src_volume['id'])
dst_volume = tests_utils.create_volume(self.context)
lvm_driver.create_volume_from_snapshot(dst_volume,
snapshot_ref)
def test_create_volume_from_snapshot_sparse_extend(self):
self.configuration.lvm_type = 'thin'
lvm_driver = lvm.LVMVolumeDriver(configuration=self.configuration,
db=db)
with mock.patch.object(lvm_driver, 'vg'), \
mock.patch.object(lvm_driver, 'extend_volume') as mock_extend:
# Test case for thin LVM
lvm_driver._sparse_copy_volume = True
src_volume = tests_utils.create_volume(self.context)
snapshot_ref = tests_utils.create_snapshot(self.context,
src_volume['id'])
dst_volume = tests_utils.create_volume(self.context)
dst_volume['size'] = snapshot_ref['volume_size'] + 1
lvm_driver.create_volume_from_snapshot(dst_volume,
snapshot_ref)
mock_extend.assert_called_with(dst_volume, dst_volume['size'])
@mock.patch.object(cinder.volume.utils, 'get_all_volume_groups',
return_value=[{'name': 'cinder-volumes'}])
@mock.patch('cinder.brick.local_dev.lvm.LVM.update_volume_group_info')
@mock.patch('cinder.brick.local_dev.lvm.LVM.get_all_physical_volumes')
@mock.patch('cinder.brick.local_dev.lvm.LVM.supports_thin_provisioning',
return_value=True)
def test_lvm_type_auto_thin_pool_exists(self, *_unused_mocks):
configuration = conf.Configuration(fake_opt, 'fake_group')
configuration.lvm_type = 'auto'
vg_obj = fake_lvm.FakeBrickLVM('cinder-volumes',
False,
None,
'default')
lvm_driver = lvm.LVMVolumeDriver(configuration=configuration,
vg_obj=vg_obj)
lvm_driver.check_for_setup_error()
self.assertEqual('thin', lvm_driver.configuration.lvm_type)
@mock.patch.object(cinder.volume.utils, 'get_all_volume_groups',
return_value=[{'name': 'cinder-volumes'}])
@mock.patch.object(cinder.brick.local_dev.lvm.LVM, 'get_volumes',
return_value=[])
@mock.patch('cinder.brick.local_dev.lvm.LVM.update_volume_group_info')
@mock.patch('cinder.brick.local_dev.lvm.LVM.get_all_physical_volumes')
@mock.patch('cinder.brick.local_dev.lvm.LVM.supports_thin_provisioning',
return_value=True)
def test_lvm_type_auto_no_lvs(self, *_unused_mocks):
configuration = conf.Configuration(fake_opt, 'fake_group')
configuration.lvm_type = 'auto'
vg_obj = fake_lvm.FakeBrickLVM('cinder-volumes',
False,
None,
'default')
lvm_driver = lvm.LVMVolumeDriver(configuration=configuration,
vg_obj=vg_obj)
lvm_driver.check_for_setup_error()
self.assertEqual('thin', lvm_driver.configuration.lvm_type)
@mock.patch.object(cinder.volume.utils, 'get_all_volume_groups',
return_value=[{'name': 'cinder-volumes'}])
@mock.patch('cinder.brick.local_dev.lvm.LVM.get_lv_info')
@mock.patch('cinder.brick.local_dev.lvm.LVM.activate_lv')
@mock.patch('cinder.brick.local_dev.lvm.LVM.'
'supports_lvchange_ignoreskipactivation')
@mock.patch('cinder.brick.local_dev.lvm.LVM.update_volume_group_info')
@mock.patch('cinder.brick.local_dev.lvm.LVM.get_all_physical_volumes')
@mock.patch('cinder.brick.local_dev.lvm.LVM.supports_thin_provisioning',
return_value=False)
def test_lvm_type_auto_no_thin_support(self, *_unused_mocks):
configuration = conf.Configuration(fake_opt, 'fake_group')
configuration.lvm_type = 'auto'
lvm_driver = lvm.LVMVolumeDriver(configuration=configuration)
lvm_driver.check_for_setup_error()
self.assertEqual('default', lvm_driver.configuration.lvm_type)
@mock.patch.object(cinder.volume.utils, 'get_all_volume_groups',
return_value=[{'name': 'cinder-volumes'}])
@mock.patch('cinder.brick.local_dev.lvm.LVM.get_lv_info')
@mock.patch('cinder.brick.local_dev.lvm.LVM.activate_lv')
@mock.patch('cinder.brick.local_dev.lvm.LVM.'
'supports_lvchange_ignoreskipactivation')
@mock.patch('cinder.brick.local_dev.lvm.LVM.update_volume_group_info')
@mock.patch('cinder.brick.local_dev.lvm.LVM.get_all_physical_volumes')
@mock.patch('cinder.brick.local_dev.lvm.LVM.get_volume')
@mock.patch('cinder.brick.local_dev.lvm.LVM.supports_thin_provisioning',
return_value=False)
def test_lvm_type_auto_no_thin_pool(self, *_unused_mocks):
configuration = conf.Configuration(fake_opt, 'fake_group')
configuration.lvm_type = 'auto'
lvm_driver = lvm.LVMVolumeDriver(configuration=configuration)
lvm_driver.check_for_setup_error()
self.assertEqual('default', lvm_driver.configuration.lvm_type)
@mock.patch.object(lvm.LVMVolumeDriver, 'extend_volume')
def test_create_cloned_volume_by_thin_snapshot(self, mock_extend):
self.configuration.lvm_type = 'thin'
fake_vg = mock.Mock(fake_lvm.FakeBrickLVM('cinder-volumes', False,
None, 'default'))
lvm_driver = lvm.LVMVolumeDriver(configuration=self.configuration,
vg_obj=fake_vg,
db=db)
fake_volume = tests_utils.create_volume(self.context, size=1)
fake_new_volume = tests_utils.create_volume(self.context, size=2)
lvm_driver.create_cloned_volume(fake_new_volume, fake_volume)
fake_vg.create_lv_snapshot.assert_called_once_with(
fake_new_volume['name'], fake_volume['name'], 'thin')
mock_extend.assert_called_once_with(fake_new_volume, 2)
fake_vg.activate_lv.assert_called_once_with(
fake_new_volume['name'], is_snapshot=True, permanent=True)
def test_lvm_migrate_volume_no_loc_info(self):
host = {'capabilities': {}}
vol = {'name': 'test', 'id': 1, 'size': 1, 'status': 'available'}
moved, model_update = self.volume.driver.migrate_volume(self.context,
vol, host)
self.assertFalse(moved)
self.assertIsNone(model_update)
def test_lvm_migrate_volume_bad_loc_info(self):
capabilities = {'location_info': 'foo'}
host = {'capabilities': capabilities}
vol = {'name': 'test', 'id': 1, 'size': 1, 'status': 'available'}
moved, model_update = self.volume.driver.migrate_volume(self.context,
vol, host)
self.assertFalse(moved)
self.assertIsNone(model_update)
def test_lvm_migrate_volume_diff_driver(self):
capabilities = {'location_info': 'FooDriver:foo:bar:default:0'}
host = {'capabilities': capabilities}
vol = {'name': 'test', 'id': 1, 'size': 1, 'status': 'available'}
moved, model_update = self.volume.driver.migrate_volume(self.context,
vol, host)
self.assertFalse(moved)
self.assertIsNone(model_update)
def test_lvm_migrate_volume_diff_host(self):
capabilities = {'location_info': 'LVMVolumeDriver:foo:bar:default:0'}
host = {'capabilities': capabilities}
vol = {'name': 'test', 'id': 1, 'size': 1, 'status': 'available'}
moved, model_update = self.volume.driver.migrate_volume(self.context,
vol, host)
self.assertFalse(moved)
self.assertIsNone(model_update)
def test_lvm_migrate_volume_in_use(self):
hostname = socket.gethostname()
capabilities = {'location_info': 'LVMVolumeDriver:%s:bar' % hostname}
host = {'capabilities': capabilities}
vol = {'name': 'test', 'id': 1, 'size': 1, 'status': 'in-use'}
moved, model_update = self.volume.driver.migrate_volume(self.context,
vol, host)
self.assertFalse(moved)
self.assertIsNone(model_update)
@mock.patch.object(volutils, 'get_all_volume_groups',
return_value=[{'name': 'cinder-volumes'}])
def test_lvm_migrate_volume_same_volume_group(self, vgs):
hostname = socket.gethostname()
capabilities = {'location_info': 'LVMVolumeDriver:%s:'
'cinder-volumes:default:0' % hostname}
host = {'capabilities': capabilities}
vol = {'name': 'test', 'id': 1, 'size': 1, 'status': 'available'}
self.volume.driver.vg = fake_lvm.FakeBrickLVM('cinder-volumes',
False,
None,
'default')
self.assertRaises(exception.VolumeBackendAPIException,
self.volume.driver.migrate_volume, self.context,
vol, host)
@mock.patch.object(lvm.LVMVolumeDriver, '_create_volume')
@mock.patch.object(brick_lvm.LVM, 'get_all_physical_volumes')
@mock.patch.object(brick_lvm.LVM, 'delete')
@mock.patch.object(volutils, 'copy_volume',
side_effect=processutils.ProcessExecutionError)
@mock.patch.object(volutils, 'get_all_volume_groups',
return_value=[{'name': 'cinder-volumes'}])
def test_lvm_migrate_volume_volume_copy_error(self, vgs, copy_volume,
mock_delete, mock_pvs,
mock_create):
hostname = socket.gethostname()
capabilities = {'location_info': 'LVMVolumeDriver:%s:'
'cinder-volumes:default:0' % hostname}
host = {'capabilities': capabilities}
vol = {'name': 'test', 'id': 1, 'size': 1, 'status': 'available'}
self.volume.driver.vg = fake_lvm.FakeBrickLVM('cinder-volumes-old',
False, None, 'default')
self.assertRaises(processutils.ProcessExecutionError,
self.volume.driver.migrate_volume, self.context,
vol, host)
mock_delete.assert_called_once_with(vol)
@mock.patch.object(volutils, 'get_all_volume_groups',
return_value=[{'name': 'cinder-volumes-2'}])
def test_lvm_volume_group_missing(self, vgs):
hostname = socket.gethostname()
capabilities = {'location_info': 'LVMVolumeDriver:%s:'
'cinder-volumes-3:default:0' % hostname}
host = {'capabilities': capabilities}
vol = {'name': 'test', 'id': 1, 'size': 1, 'status': 'available'}
self.volume.driver.vg = fake_lvm.FakeBrickLVM('cinder-volumes',
False,
None,
'default')
moved, model_update = self.volume.driver.migrate_volume(self.context,
vol, host)
self.assertFalse(moved)
self.assertIsNone(model_update)
def test_lvm_migrate_volume_proceed(self):
hostname = socket.gethostname()
capabilities = {'location_info': 'LVMVolumeDriver:%s:'
'cinder-volumes-2:default:0' % hostname}
host = {'capabilities': capabilities}
vol = {'name': 'testvol', 'id': 1, 'size': 2, 'status': 'available'}
def fake_execute(*args, **kwargs):
pass
def get_all_volume_groups():
# NOTE(flaper87) Return just the destination
# 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 [{}]
with mock.patch.object(brick_lvm.LVM, 'get_all_physical_volumes',
return_value = [{}]), \
mock.patch.object(self.volume.driver, '_execute') \
as mock_execute, \
mock.patch.object(volutils, 'copy_volume') as mock_copy, \
mock.patch.object(volutils, 'get_all_volume_groups',
side_effect = get_all_volume_groups), \
mock.patch.object(self.volume.driver, '_delete_volume'):
self.volume.driver.vg = fake_lvm.FakeBrickLVM('cinder-volumes',
False,
None,
'default')
mock_execute.return_value = ("mock_outs", "mock_errs")
moved, model_update = \
self.volume.driver.migrate_volume(self.context, vol, host)
self.assertTrue(moved)
self.assertIsNone(model_update)
mock_copy.assert_called_once_with(
'/dev/mapper/cinder--volumes-testvol',
'/dev/mapper/cinder--volumes--2-testvol',
2048,
'1M',
execute=mock_execute,
sparse=False)
def test_lvm_migrate_volume_proceed_with_thin(self):
hostname = socket.gethostname()
capabilities = {'location_info': 'LVMVolumeDriver:%s:'
'cinder-volumes-2:default:0' % hostname}
host = {'capabilities': capabilities}
vol = {'name': 'testvol', 'id': 1, 'size': 2, 'status': 'available'}
def fake_execute(*args, **kwargs):
pass
def get_all_volume_groups():
# NOTE(flaper87) Return just the destination
# 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.configuration.lvm_type = 'thin'
lvm_driver = lvm.LVMVolumeDriver(configuration=self.configuration,
db=db)
with mock.patch.object(brick_lvm.LVM, 'get_all_physical_volumes',
return_value = [{}]), \
mock.patch.object(lvm_driver, '_execute') \
as mock_execute, \
mock.patch.object(volutils, 'copy_volume') as mock_copy, \
mock.patch.object(volutils, 'get_all_volume_groups',
side_effect = get_all_volume_groups), \
mock.patch.object(lvm_driver, '_delete_volume'):
lvm_driver.vg = fake_lvm.FakeBrickLVM('cinder-volumes',
False,
None,
'default')
lvm_driver._sparse_copy_volume = True
mock_execute.return_value = ("mock_outs", "mock_errs")
moved, model_update = \
lvm_driver.migrate_volume(self.context, vol, host)
self.assertTrue(moved)
self.assertIsNone(model_update)
mock_copy.assert_called_once_with(
'/dev/mapper/cinder--volumes-testvol',
'/dev/mapper/cinder--volumes--2-testvol',
2048,
'1M',
execute=mock_execute,
sparse=True)
@staticmethod
def _get_manage_existing_lvs(name):
"""Helper method used by the manage_existing tests below."""
lvs = [{'name': 'fake_lv', 'size': '1.75'},
{'name': 'fake_lv_bad_size', 'size': 'Not a float'}]
for lv in lvs:
if lv['name'] == name:
return lv
def _setup_stubs_for_manage_existing(self):
"""Helper to set up common stubs for the manage_existing tests."""
self.volume.driver.vg = fake_lvm.FakeBrickLVM('cinder-volumes',
False,
None,
'default')
@mock.patch.object(db.sqlalchemy.api, 'volume_get',
side_effect=exception.VolumeNotFound(
volume_id='d8cd1feb-2dcc-404d-9b15-b86fe3bec0a1'))
def test_lvm_manage_existing_not_found(self, mock_vol_get):
self._setup_stubs_for_manage_existing()
vol_name = 'volume-d8cd1feb-2dcc-404d-9b15-b86fe3bec0a1'
ref = {'source-name': 'fake_lv'}
vol = {'name': vol_name, 'id': fake.VOLUME_ID, 'size': 0}
with mock.patch.object(self.volume.driver.vg, 'rename_volume'):
model_update = self.volume.driver.manage_existing(vol, ref)
self.assertIsNone(model_update)
@mock.patch('cinder.db.sqlalchemy.api.resource_exists', return_value=True)
def test_lvm_manage_existing_already_managed(self, exists_mock):
self._setup_stubs_for_manage_existing()
vol_name = 'volume-d8cd1feb-2dcc-404d-9b15-b86fe3bec0a1'
ref = {'source-name': vol_name}
vol = {'name': 'test', 'id': 1, 'size': 0}
with mock.patch.object(self.volume.driver.vg, 'rename_volume'):
self.assertRaises(exception.ManageExistingAlreadyManaged,
self.volume.driver.manage_existing,
vol, ref)
def test_lvm_manage_existing(self):
"""Good pass on managing an LVM volume.
This test case ensures that, when a logical volume with the
specified name exists, and the size is as expected, no error is
returned from driver.manage_existing, and that the rename_volume
function is called in the Brick LVM code with the correct arguments.
"""
self._setup_stubs_for_manage_existing()
ref = {'source-name': 'fake_lv'}
vol = {'name': 'test', 'id': fake.VOLUME_ID, 'size': 0}
def _rename_volume(old_name, new_name):
self.assertEqual(ref['source-name'], old_name)
self.assertEqual(vol['name'], new_name)
with mock.patch.object(self.volume.driver.vg,
'rename_volume') as mock_rename_volume, \
mock.patch.object(self.volume.driver.vg, 'get_volume',
self._get_manage_existing_lvs):
mock_rename_volume.return_value = _rename_volume
size = self.volume.driver.manage_existing_get_size(vol, ref)
self.assertEqual(2, size)
model_update = self.volume.driver.manage_existing(vol, ref)
self.assertIsNone(model_update)
def test_lvm_manage_existing_bad_size(self):
"""Make sure correct exception on bad size returned from LVM.
This test case ensures that the correct exception is raised when
the information returned for the existing LVs is not in the format
that the manage_existing code expects.
"""
self._setup_stubs_for_manage_existing()
ref = {'source-name': 'fake_lv_bad_size'}
vol = {'name': 'test', 'id': fake.VOLUME_ID, 'size': 2}
with mock.patch.object(self.volume.driver.vg, 'get_volume',
self._get_manage_existing_lvs):
self.assertRaises(exception.VolumeBackendAPIException,
self.volume.driver.manage_existing_get_size,
vol, ref)
def test_lvm_manage_existing_bad_ref(self):
"""Error case where specified LV doesn't exist.
This test case ensures that the correct exception is raised when
the caller attempts to manage a volume that does not exist.
"""
self._setup_stubs_for_manage_existing()
ref = {'source-name': 'fake_nonexistent_lv'}
vol = {'name': 'test', 'id': 1, 'size': 0, 'status': 'available'}
with mock.patch.object(self.volume.driver.vg, 'get_volume',
self._get_manage_existing_lvs):
self.assertRaises(exception.ManageExistingInvalidReference,
self.volume.driver.manage_existing_get_size,
vol, ref)
def test_lvm_manage_existing_snapshot(self):
"""Good pass on managing an LVM snapshot.
This test case ensures that, when a logical volume's snapshot with the
specified name exists, and the size is as expected, no error is
returned from driver.manage_existing_snapshot, and that the
rename_volume function is called in the Brick LVM code with the correct
arguments.
"""
self._setup_stubs_for_manage_existing()
ref = {'source-name': 'fake_lv'}
snp = {'name': 'test', 'id': fake.SNAPSHOT_ID, 'size': 0}
def _rename_volume(old_name, new_name):
self.assertEqual(ref['source-name'], old_name)
self.assertEqual(snp['name'], new_name)
with mock.patch.object(self.volume.driver.vg,
'rename_volume') as mock_rename_volume, \
mock.patch.object(self.volume.driver.vg, 'get_volume',
self._get_manage_existing_lvs):
mock_rename_volume.return_value = _rename_volume
size = self.volume.driver.manage_existing_snapshot_get_size(
snp, ref)
self.assertEqual(2, size)
model_update = self.volume.driver.manage_existing_snapshot(
snp, ref)
self.assertIsNone(model_update)
def test_lvm_manage_existing_snapshot_bad_ref(self):
"""Error case where specified LV snapshot doesn't exist.
This test case ensures that the correct exception is raised when
the caller attempts to manage a snapshot that does not exist.
"""
self._setup_stubs_for_manage_existing()
ref = {'source-name': 'fake_nonexistent_lv'}
snp = {
'name': 'test',
'id': fake.SNAPSHOT_ID,
'size': 0,
'status': 'available',
}
with mock.patch.object(self.volume.driver.vg, 'get_volume',
self._get_manage_existing_lvs):
self.assertRaises(
exception.ManageExistingInvalidReference,
self.volume.driver.manage_existing_snapshot_get_size,
snp, ref)
def test_revert_snapshot(self):
self._setup_stubs_for_manage_existing()
self.configuration.lvm_type = 'auto'
fake_volume = tests_utils.create_volume(self.context,
display_name='fake_volume')
fake_snapshot = tests_utils.create_snapshot(
self.context, fake_volume.id)
with mock.patch.object(self.volume.driver.vg,
'revert') as mock_revert,\
mock.patch.object(self.volume.driver.vg,
'create_lv_snapshot') as mock_create,\
mock.patch.object(self.volume.driver.vg,
'deactivate_lv') as mock_deactive,\
mock.patch.object(self.volume.driver.vg,
'activate_lv') as mock_active:
self.volume.driver.revert_to_snapshot(self.context,
fake_volume,
fake_snapshot)
mock_revert.assert_called_once_with(
self.volume.driver._escape_snapshot(fake_snapshot.name))
mock_deactive.assert_called_once_with(fake_volume.name)
mock_active.assert_called_once_with(fake_volume.name)
mock_create.assert_called_once_with(
self.volume.driver._escape_snapshot(fake_snapshot.name),
fake_volume.name, self.configuration.lvm_type)
def test_revert_thin_snapshot(self):
configuration = conf.Configuration(fake_opt, 'fake_group')
configuration.lvm_type = 'thin'
lvm_driver = lvm.LVMVolumeDriver(configuration=configuration,
db=db)
fake_volume = tests_utils.create_volume(self.context,
display_name='fake_volume')
fake_snapshot = tests_utils.create_snapshot(
self.context, fake_volume.id)
self.assertRaises(NotImplementedError,
lvm_driver.revert_to_snapshot,
self.context, fake_volume,
fake_snapshot)
def test_lvm_manage_existing_snapshot_bad_size(self):
"""Make sure correct exception on bad size returned from LVM.
This test case ensures that the correct exception is raised when
the information returned for the existing LVs is not in the format
that the manage_existing_snapshot code expects.
"""
self._setup_stubs_for_manage_existing()
ref = {'source-name': 'fake_lv_bad_size'}
snp = {'name': 'test', 'id': fake.SNAPSHOT_ID, 'size': 2}
with mock.patch.object(self.volume.driver.vg, 'get_volume',
self._get_manage_existing_lvs):
self.assertRaises(
exception.VolumeBackendAPIException,
self.volume.driver.manage_existing_snapshot_get_size,
snp, ref)
def test_lvm_unmanage(self):
volume = tests_utils.create_volume(self.context, status='available',
size=1, host=CONF.host)
ret = self.volume.driver.unmanage(volume)
self.assertIsNone(ret)
def test_lvm_get_manageable_volumes(self):
cinder_vols = [{'id': '00000000-0000-0000-0000-000000000000'}]
lvs = [{'name': 'volume-00000000-0000-0000-0000-000000000000',
'size': '1.75'},
{'name': 'volume-00000000-0000-0000-0000-000000000001',
'size': '3.0'},
{'name': 'snapshot-00000000-0000-0000-0000-000000000002',
'size': '2.2'},
{'name': 'myvol', 'size': '4.0'}]
self.volume.driver.vg = mock.Mock()
self.volume.driver.vg.get_volumes.return_value = lvs
self.volume.driver.vg.lv_is_snapshot.side_effect = [False, False,
True, False]
self.volume.driver.vg.lv_is_open.side_effect = [True, False]
res = self.volume.driver.get_manageable_volumes(cinder_vols, None,
1000, 0,
['size'], ['asc'])
exp = [{'size': 2, 'reason_not_safe': 'already managed',
'extra_info': None,
'reference': {'source-name':
'volume-00000000-0000-0000-0000-000000000000'},
'cinder_id': '00000000-0000-0000-0000-000000000000',
'safe_to_manage': False},
{'size': 3, 'reason_not_safe': 'volume in use',
'reference': {'source-name':
'volume-00000000-0000-0000-0000-000000000001'},
'safe_to_manage': False, 'cinder_id': None,
'extra_info': None},
{'size': 4, 'reason_not_safe': None,
'safe_to_manage': True, 'reference': {'source-name': 'myvol'},
'cinder_id': None, 'extra_info': None}]
self.assertEqual(exp, res)
def test_lvm_get_manageable_snapshots(self):
cinder_snaps = [{'id': '00000000-0000-0000-0000-000000000000'}]
lvs = [{'name': 'snapshot-00000000-0000-0000-0000-000000000000',
'size': '1.75'},
{'name': 'volume-00000000-0000-0000-0000-000000000001',
'size': '3.0'},
{'name': 'snapshot-00000000-0000-0000-0000-000000000002',
'size': '2.2'},
{'name': 'mysnap', 'size': '4.0'}]
self.volume.driver.vg = mock.Mock()
self.volume.driver.vg.get_volumes.return_value = lvs
self.volume.driver.vg.lv_is_snapshot.side_effect = [True, False, True,
True]
self.volume.driver.vg.lv_is_open.side_effect = [True, False]
self.volume.driver.vg.lv_get_origin.side_effect = [
'volume-00000000-0000-0000-0000-000000000000',
'volume-00000000-0000-0000-0000-000000000002',
'myvol']
res = self.volume.driver.get_manageable_snapshots(cinder_snaps, None,
1000, 0,
['size'], ['asc'])
exp = [{'size': 2, 'reason_not_safe': 'already managed',
'reference':
{'source-name':
'snapshot-00000000-0000-0000-0000-000000000000'},
'safe_to_manage': False, 'extra_info': None,
'cinder_id': '00000000-0000-0000-0000-000000000000',
'source_reference':
{'source-name':
'volume-00000000-0000-0000-0000-000000000000'}},
{'size': 3, 'reason_not_safe': 'snapshot in use',
'reference':
{'source-name':
'snapshot-00000000-0000-0000-0000-000000000002'},
'safe_to_manage': False, 'extra_info': None,
'cinder_id': None,
'source_reference':
{'source-name':
'volume-00000000-0000-0000-0000-000000000002'}},
{'size': 4, 'reason_not_safe': None,
'reference': {'source-name': 'mysnap'},
'safe_to_manage': True, 'cinder_id': None,
'source_reference': {'source-name': 'myvol'},
'extra_info': None}]
self.assertEqual(exp, res)
class LVMISCSITestCase(test_driver.BaseDriverTestCase):
"""Test Case for LVMISCSIDriver"""
driver_name = "cinder.volume.drivers.lvm.LVMVolumeDriver"
def setUp(self):
super(LVMISCSITestCase, self).setUp()
self.configuration = mock.Mock(conf.Configuration)
self.configuration.target_prefix = 'iqn.2010-10.org.openstack:'
self.configuration.target_ip_address = '0.0.0.0'
self.configuration.target_port = 3260
def _attach_volume(self):
"""Attach volumes to an instance."""
volume_id_list = []
for index in range(3):
vol = {}
vol['size'] = 0
vol_ref = db.volume_create(self.context, vol)
self.volume.create_volume(self.context, vol_ref)
vol_ref = db.volume_get(self.context, vol_ref['id'])
# each volume has a different mountpoint
mountpoint = "/dev/sd" + chr((ord('b') + index))
instance_uuid = '12345678-1234-5678-1234-567812345678'
db.volume_attached(self.context, vol_ref['id'], instance_uuid,
mountpoint)
volume_id_list.append(vol_ref['id'])
return volume_id_list
def test_do_iscsi_discovery(self):
self.configuration = conf.Configuration(None)
iscsi_driver = \
cinder.volume.targets.tgt.TgtAdm(
configuration=self.configuration)
ret = ("%s dummy" % CONF.target_ip_address, '')
with mock.patch('cinder.utils.execute',
return_value=ret):
volume = {"name": "dummy",
"host": "0.0.0.0",
"id": "12345678-1234-5678-1234-567812345678"}
iscsi_driver._do_iscsi_discovery(volume)
def test_get_iscsi_properties(self):
volume = {"provider_location": '',
"id": "0",
"provider_auth": "a b c",
"attached_mode": "rw"}
iscsi_driver = \
cinder.volume.targets.tgt.TgtAdm(configuration=self.configuration)
iscsi_driver._do_iscsi_discovery = lambda v: "0.0.0.0:0000,0 iqn:iqn 0"
result = iscsi_driver._get_iscsi_properties(volume)
self.assertEqual("0.0.0.0:0000", result["target_portal"])
self.assertEqual("iqn:iqn", result["target_iqn"])
self.assertEqual(0, result["target_lun"])
def test_get_iscsi_properties_multiple_portals(self):
volume = {"provider_location": '1.1.1.1:3260;2.2.2.2:3261,1 iqn:iqn 0',
"id": "0",
"provider_auth": "a b c",
"attached_mode": "rw"}
iscsi_driver = \
cinder.volume.targets.tgt.TgtAdm(configuration=self.configuration)
result = iscsi_driver._get_iscsi_properties(volume)
self.assertEqual("1.1.1.1:3260", result["target_portal"])
self.assertEqual("iqn:iqn", result["target_iqn"])
self.assertEqual(0, result["target_lun"])
self.assertEqual(["1.1.1.1:3260", "2.2.2.2:3261"],
result["target_portals"])
self.assertEqual(["iqn:iqn", "iqn:iqn"], result["target_iqns"])
self.assertEqual([0, 0], result["target_luns"])
@mock.patch.object(brick_lvm.LVM, 'get_volumes',
return_value=[{'vg': 'fake_vg', 'name': 'fake_vol',
'size': '1000'}])
@mock.patch.object(brick_lvm.LVM, 'get_all_physical_volumes')
@mock.patch.object(brick_lvm.LVM, 'get_all_volume_groups',
return_value=[{'name': 'cinder-volumes',
'size': '5.52',
'available': '0.52',
'lv_count': '2',
'uuid': 'vR1JU3-FAKE-C4A9-PQFh-Mctm'}])
@mock.patch('cinder.brick.local_dev.lvm.LVM.get_lvm_version',
return_value=(2, 2, 100))
def test_get_volume_stats(self, _mock_get_version, mock_vgs, mock_pvs,
mock_get_volumes):
self.volume.driver.vg = brick_lvm.LVM('cinder-volumes', 'sudo')
self.volume.driver._update_volume_stats()
stats = self.volume.driver._stats
self.assertEqual(
float('5.52'), stats['pools'][0]['total_capacity_gb'])
self.assertEqual(
float('0.52'), stats['pools'][0]['free_capacity_gb'])
self.assertEqual(
float('5.0'), stats['pools'][0]['provisioned_capacity_gb'])
self.assertEqual(
int('1'), stats['pools'][0]['total_volumes'])
self.assertFalse(stats['sparse_copy_volume'])
# Check value of sparse_copy_volume for thin enabled case.
# This value is set in check_for_setup_error.
self.configuration = conf.Configuration(None)
self.configuration.lvm_type = 'thin'
vg_obj = fake_lvm.FakeBrickLVM('cinder-volumes',
False,
None,
'default')
lvm_driver = lvm.LVMVolumeDriver(configuration=self.configuration,
db=db,
vg_obj=vg_obj)
lvm_driver.check_for_setup_error()
lvm_driver.vg = brick_lvm.LVM('cinder-volumes', 'sudo')
lvm_driver._update_volume_stats()
stats = lvm_driver._stats
self.assertTrue(stats['sparse_copy_volume'])
def test_validate_connector(self):
iscsi_driver =\
cinder.volume.targets.tgt.TgtAdm(
configuration=self.configuration)
# Validate a valid connector
connector = {'ip': '10.0.0.2',
'host': 'fakehost',
'initiator': 'iqn.2012-07.org.fake:01'}
iscsi_driver.validate_connector(connector)
# Validate a connector without the initiator
connector = {'ip': '10.0.0.2', 'host': 'fakehost'}
self.assertRaises(exception.InvalidConnectorException,
iscsi_driver.validate_connector, connector)