Merge "Add CG capability to generic groups in VNX driver"

This commit is contained in:
Jenkins 2017-01-13 03:04:38 +00:00 committed by Gerrit Code Review
commit de813e7a74
9 changed files with 436 additions and 62 deletions

View File

@ -175,12 +175,12 @@ test_create_snapshot_adapter:
test_delete_snapshot_adapter: test_delete_snapshot_adapter:
snapshot: *snapshot_base snapshot: *snapshot_base
test_create_cgsnapshot: &cg_snap_and_snaps test_do_create_cgsnap: &cg_snap_and_snaps
cg_snap: *cg_snapshot_base cg_snap: *cg_snapshot_base
snap1: *snapshot_base snap1: *snapshot_base
snap2: *snapshot_base snap2: *snapshot_base
test_delete_cgsnapshot: *cg_snap_and_snaps test_do_delete_cgsnap: *cg_snap_and_snaps
test_manage_existing_lun_no_exist: test_manage_existing_lun_no_exist:
volume: *volume_base volume: *volume_base
@ -244,7 +244,7 @@ test_create_volume_from_snapshot_snapcopy:
test_get_base_lun_name: test_get_base_lun_name:
volume: *volume_base volume: *volume_base
test_create_cg_from_cgsnapshot: test_do_create_cg_from_cgsnap:
vol1: vol1:
_type: 'volume' _type: 'volume'
_properties: _properties:
@ -257,8 +257,6 @@ test_create_cg_from_cgsnapshot:
<<: *volume_base_properties <<: *volume_base_properties
id: id:
_uuid: volume2_id _uuid: volume2_id
cg: *cg_base
cg_snap: *cg_snapshot_base
snap1: snap1:
_type: 'snapshot' _type: 'snapshot'
_properties: _properties:
@ -272,21 +270,13 @@ test_create_cg_from_cgsnapshot:
id: id:
_uuid: snapshot2_id _uuid: snapshot2_id
test_create_cloned_cg: test_do_clone_cg:
vol1: vol1:
_type: 'volume' _type: 'volume'
_properties: _properties:
<<: *volume_base_properties <<: *volume_base_properties
id: id:
_uuid: consistency_group_id _uuid: consistency_group_id
cg: *cg_base
src_cg:
_type: 'cg'
_properties:
<<: *cg_base_properties
id:
_uuid: consistency_group2_id
name: 'src_cg_name'
src_vol1: src_vol1:
_type: 'volume' _type: 'volume'
@ -322,7 +312,7 @@ test_remove_host_access_sg_absent:
test_remove_host_access_volume_not_in_sg: test_remove_host_access_volume_not_in_sg:
volume: *volume_base volume: *volume_base
test_update_consistencygroup: test_do_update_cg:
cg: *cg_base cg: *cg_base
volume_add: volume_add:
<<: *volume_base <<: *volume_base
@ -421,13 +411,36 @@ test_update_migrated_volume_smp:
<<: *provider_location_dict <<: *provider_location_dict
type: smp type: smp
test_create_group_snap:
test_create_cgsnapshot:
test_create_cloned_cg:
test_create_cloned_group:
test_create_cg_from_cgsnapshot:
test_create_group_from_group_snapshot:
test_create_cgsnapshot:
test_create_group_snapshot:
test_delete_group_snapshot:
test_delete_cgsnapshot:
########################################################### ###########################################################
# TestUtils # TestUtils
########################################################### ###########################################################
test_validate_cg_type: test_validate_cg_type:
cg: *cg_base_with_type cg:
_properties:
id:
_uuid: GROUP_ID
volume_type_ids: ['type1']
########################################################### ###########################################################

View File

@ -150,6 +150,7 @@ mirror_base: &mirror_base
_type: VNXMirrorImageState _type: VNXMirrorImageState
value: 'SYNCHRONIZED' value: 'SYNCHRONIZED'
########################################################### ###########################################################
# TestClient # TestClient
########################################################### ###########################################################
@ -1365,7 +1366,9 @@ test_delete_snapshot_adapter: *test_delete_snapshot
test_create_cgsnapshot: *test_create_cg_snapshot test_create_cgsnapshot: *test_create_cg_snapshot
test_delete_cgsnapshot: test_do_create_cgsnap: *test_create_cg_snapshot
test_do_delete_cgsnap:
cg_snap: &cg_snap_delete cg_snap: &cg_snap_delete
_methods: _methods:
delete: delete:
@ -1373,7 +1376,7 @@ test_delete_cgsnapshot:
_methods: _methods:
get_snap: *cg_snap_delete get_snap: *cg_snap_delete
test_create_cg_from_cgsnapshot: test_do_create_cg_from_cgsnap:
snap: &copied_cg_snap snap: &copied_cg_snap
_methods: _methods:
copy: copy:
@ -1400,7 +1403,7 @@ test_create_cg_from_cgsnapshot:
get_migration_session: *session_verify get_migration_session: *session_verify
create_cg: *cg_for_create create_cg: *cg_for_create
test_create_cloned_cg: test_do_clone_cg:
vnx: vnx:
_properties: _properties:
_methods: _methods:
@ -1773,6 +1776,8 @@ test_terminate_connection_cleanup_sg_is_not_empty:
test_update_consistencygroup: test_update_consistencygroup:
test_do_update_cg:
test_update_migrated_volume: test_update_migrated_volume:
test_update_migrated_volume_smp: test_update_migrated_volume_smp:
@ -1785,6 +1790,26 @@ test_normalize_config_iscsi_initiators_empty_str:
test_normalize_config_iscsi_initiators_not_dict: test_normalize_config_iscsi_initiators_not_dict:
test_create_group_snap:
test_create_cgsnapshot:
test_create_cloned_cg:
test_create_cloned_group:
test_create_cg_from_cgsnapshot:
test_create_group_from_group_snapshot:
test_create_cgsnapshot:
test_create_group_snapshot:
test_delete_group_snapshot:
test_delete_cgsnapshot:
########################################################### ###########################################################
# TestISCSIAdapter # TestISCSIAdapter

View File

@ -15,9 +15,12 @@
import mock import mock
import re import re
from cinder import context
from cinder import exception from cinder import exception
from cinder.objects import fields from cinder.objects import fields
from cinder import test from cinder import test
from cinder.tests.unit import fake_constants
from cinder.tests.unit import utils as test_utils
from cinder.tests.unit.volume.drivers.dell_emc.vnx import fake_exception \ from cinder.tests.unit.volume.drivers.dell_emc.vnx import fake_exception \
as storops_ex as storops_ex
from cinder.tests.unit.volume.drivers.dell_emc.vnx import fake_storops \ from cinder.tests.unit.volume.drivers.dell_emc.vnx import fake_storops \
@ -39,6 +42,7 @@ class TestCommonAdapter(test.TestCase):
vnx_utils.init_ops(self.configuration) vnx_utils.init_ops(self.configuration)
self.configuration.san_ip = '192.168.1.1' self.configuration.san_ip = '192.168.1.1'
self.configuration.storage_vnx_authentication_type = 'global' self.configuration.storage_vnx_authentication_type = 'global'
self.ctxt = context.get_admin_context()
def tearDown(self): def tearDown(self):
super(TestCommonAdapter, self).tearDown() super(TestCommonAdapter, self).tearDown()
@ -136,32 +140,124 @@ class TestCommonAdapter(test.TestCase):
update = vnx_common.create_volume_from_snapshot(volume, snapshot) update = vnx_common.create_volume_from_snapshot(volume, snapshot)
self.assertEqual('True', update['metadata']['snapcopy']) self.assertEqual('True', update['metadata']['snapcopy'])
@res_mock.patch_common_adapter
def test_create_cg_from_cgsnapshot(self, common, _):
common.do_create_cg_from_cgsnap = mock.Mock(
return_value='fake_return')
new_cg = test_utils.create_consistencygroup(
self.ctxt,
id=fake_constants.CONSISTENCY_GROUP_ID,
host='host@backend#unit_test_pool',
group_type_id=fake_constants.VOLUME_TYPE_ID)
cg_snapshot = test_utils.create_cgsnapshot(
self.ctxt,
fake_constants.CONSISTENCY_GROUP2_ID)
vol = test_utils.create_volume(self.ctxt)
snaps = [
test_utils.create_snapshot(self.ctxt, vol.id)]
vol_new = test_utils.create_volume(self.ctxt)
ret = common.create_cg_from_cgsnapshot(
None, new_cg, [vol_new], cg_snapshot, snaps)
self.assertEqual('fake_return', ret)
common.do_create_cg_from_cgsnap.assert_called_once_with(
new_cg.id, new_cg.host, [vol_new], cg_snapshot.id, snaps)
@res_mock.patch_common_adapter
def test_create_group_from_group_snapshot(self, common, _):
common.do_create_cg_from_cgsnap = mock.Mock(
return_value='fake_return')
group = test_utils.create_group(
self.ctxt,
id=fake_constants.CONSISTENCY_GROUP_ID,
host='host@backend#unit_test_pool',
group_type_id=fake_constants.VOLUME_TYPE_ID)
group_snapshot = test_utils.create_group_snapshot(
self.ctxt,
fake_constants.CGSNAPSHOT_ID,
host='host@backend#unit_test_pool',
group_type_id=fake_constants.VOLUME_TYPE_ID)
vol = test_utils.create_volume(self.ctxt)
snaps = [
test_utils.create_snapshot(self.ctxt, vol.id)]
vol_new = test_utils.create_volume(self.ctxt)
ret = common.create_group_from_group_snapshot(
None, group, [vol_new], group_snapshot, snaps)
self.assertEqual('fake_return', ret)
common.do_create_cg_from_cgsnap.assert_called_once_with(
group.id, group.host, [vol_new], group_snapshot.id, snaps)
@res_mock.mock_driver_input @res_mock.mock_driver_input
@res_mock.patch_common_adapter @res_mock.patch_common_adapter
def test_create_cg_from_cgsnapshot(self, vnx_common, mocked, def test_do_create_cg_from_cgsnap(
cinder_input): self, vnx_common, mocked, cinder_input):
group = cinder_input['cg'] cg_id = fake_constants.CONSISTENCY_GROUP_ID
cg_host = 'host@backend#unit_test_pool'
volumes = [cinder_input['vol1']] volumes = [cinder_input['vol1']]
cg_snap = cinder_input['cg_snap'] cgsnap_id = fake_constants.CGSNAPSHOT_ID
snaps = [cinder_input['snap1']] snaps = [cinder_input['snap1']]
model_update, volume_updates = vnx_common.create_cg_from_cgsnapshot( model_update, volume_updates = (
None, group, volumes, cg_snap, snaps) vnx_common.do_create_cg_from_cgsnap(
cg_id, cg_host, volumes, cgsnap_id, snaps))
self.assertIsNone(model_update) self.assertIsNone(model_update)
self.assertIsNotNone( self.assertIsNotNone(
re.findall('id^12', re.findall('id^12',
volume_updates[0]['provider_location'])) volume_updates[0]['provider_location']))
@res_mock.patch_common_adapter
def test_create_cloned_cg(self, common, _):
common.do_clone_cg = mock.Mock(
return_value='fake_return')
group = test_utils.create_consistencygroup(
self.ctxt,
id=fake_constants.CONSISTENCY_GROUP_ID,
host='host@backend#unit_test_pool',
group_type_id=fake_constants.VOLUME_TYPE_ID)
src_group = test_utils.create_consistencygroup(
self.ctxt,
id=fake_constants.CONSISTENCY_GROUP2_ID,
host='host@backend#unit_test_pool2',
group_type_id=fake_constants.VOLUME_TYPE_ID)
vol = test_utils.create_volume(self.ctxt)
src_vol = test_utils.create_volume(self.ctxt)
ret = common.create_cloned_group(
None, group, [vol], src_group, [src_vol])
self.assertEqual('fake_return', ret)
common.do_clone_cg.assert_called_once_with(
group.id, group.host, [vol], src_group.id, [src_vol])
@res_mock.patch_common_adapter
def test_create_cloned_group(self, common, _):
common.do_clone_cg = mock.Mock(
return_value='fake_return')
group = test_utils.create_group(
self.ctxt,
id=fake_constants.GROUP_ID,
host='host@backend#unit_test_pool',
group_type_id=fake_constants.VOLUME_TYPE_ID)
src_group = test_utils.create_group(
self.ctxt,
id=fake_constants.GROUP2_ID,
host='host@backend#unit_test_pool2',
group_type_id=fake_constants.VOLUME_TYPE_ID)
vol = test_utils.create_volume(self.ctxt)
src_vol = test_utils.create_volume(self.ctxt)
ret = common.create_cloned_group(
None, group, [vol], src_group, [src_vol])
self.assertEqual('fake_return', ret)
common.do_clone_cg.assert_called_once_with(
group.id, group.host, [vol], src_group.id, [src_vol])
@res_mock.mock_driver_input @res_mock.mock_driver_input
@res_mock.patch_common_adapter @res_mock.patch_common_adapter
def test_create_cloned_cg(self, vnx_common, mocked, def test_do_clone_cg(self, vnx_common, _, cinder_input):
cinder_input): cg_id = fake_constants.CONSISTENCY_GROUP_ID
group = cinder_input['cg'] cg_host = 'host@backend#unit_test_pool'
src_group = cinder_input['src_cg']
volumes = [cinder_input['vol1']] volumes = [cinder_input['vol1']]
src_cg_id = fake_constants.CONSISTENCY_GROUP2_ID
src_volumes = [cinder_input['src_vol1']] src_volumes = [cinder_input['src_vol1']]
model_update, volume_updates = vnx_common.create_cloned_cg( model_update, volume_updates = vnx_common.do_clone_cg(
None, group, volumes, src_group, src_volumes) cg_id, cg_host, volumes, src_cg_id, src_volumes)
self.assertIsNone(model_update) self.assertIsNone(model_update)
self.assertIsNotNone( self.assertIsNotNone(
re.findall('id^12', re.findall('id^12',
@ -474,24 +570,105 @@ class TestCommonAdapter(test.TestCase):
mocked_input): mocked_input):
common_adapter.delete_snapshot(mocked_input['snapshot']) common_adapter.delete_snapshot(mocked_input['snapshot'])
@res_mock.patch_common_adapter
def test_create_cgsnapshot(self, common_adapter, _):
common_adapter.do_create_cgsnap = mock.Mock(
return_value='fake_return')
cg_snapshot = test_utils.create_cgsnapshot(
self.ctxt,
fake_constants.CONSISTENCY_GROUP_ID)
vol = test_utils.create_volume(self.ctxt)
snaps = [
test_utils.create_snapshot(self.ctxt, vol.id)]
ret = common_adapter.create_cgsnapshot(
None, cg_snapshot, snaps)
self.assertEqual('fake_return', ret)
common_adapter.do_create_cgsnap.assert_called_once_with(
cg_snapshot.consistencygroup_id,
cg_snapshot.id,
snaps)
@res_mock.patch_common_adapter
def test_create_group_snap(self, common_adapter, _):
common_adapter.do_create_cgsnap = mock.Mock(
return_value='fake_return')
group_snapshot = test_utils.create_group_snapshot(
self.ctxt,
fake_constants.GROUP_ID,
host='host@backend#unit_test_pool',
group_type_id=fake_constants.VOLUME_TYPE_ID)
vol = test_utils.create_volume(self.ctxt)
snaps = [
test_utils.create_snapshot(self.ctxt, vol.id)]
ret = common_adapter.create_group_snapshot(
None, group_snapshot, snaps)
self.assertEqual('fake_return', ret)
common_adapter.do_create_cgsnap.assert_called_once_with(
group_snapshot.group_id,
group_snapshot.id,
snaps)
@res_mock.mock_driver_input @res_mock.mock_driver_input
@res_mock.patch_common_adapter @res_mock.patch_common_adapter
def test_create_cgsnapshot(self, common_adapter, mocked, mocked_input): def test_do_create_cgsnap(self, common_adapter, _, mocked_input):
cg_snap = mocked_input['cg_snap'] group_name = fake_constants.CONSISTENCY_GROUP_ID
snap_name = fake_constants.CGSNAPSHOT_ID
snap1 = mocked_input['snap1'] snap1 = mocked_input['snap1']
snap2 = mocked_input['snap2'] snap2 = mocked_input['snap2']
model_update, snapshots_model_update = ( model_update, snapshots_model_update = (
common_adapter.create_cgsnapshot(None, cg_snap, [snap1, snap2])) common_adapter.do_create_cgsnap(group_name, snap_name,
[snap1, snap2]))
self.assertEqual('available', model_update['status']) self.assertEqual('available', model_update['status'])
for update in snapshots_model_update: for update in snapshots_model_update:
self.assertEqual(fields.SnapshotStatus.AVAILABLE, update['status']) self.assertEqual(fields.SnapshotStatus.AVAILABLE, update['status'])
@res_mock.patch_common_adapter
def test_delete_group_snapshot(self, common_adapter, _):
common_adapter.do_delete_cgsnap = mock.Mock(
return_value='fake_return')
group_snapshot = test_utils.create_group_snapshot(
self.ctxt,
fake_constants.GROUP_ID,
host='host@backend#unit_test_pool',
group_type_id=fake_constants.VOLUME_TYPE_ID)
vol = test_utils.create_volume(self.ctxt)
snaps = [
test_utils.create_snapshot(self.ctxt, vol.id)]
ret = common_adapter.delete_group_snapshot(
None, group_snapshot, snaps)
self.assertEqual('fake_return', ret)
common_adapter.do_delete_cgsnap.assert_called_once_with(
group_snapshot.group_id,
group_snapshot.id,
group_snapshot.status,
snaps)
@res_mock.patch_common_adapter
def test_delete_cgsnapshot(self, common_adapter, _):
common_adapter.do_delete_cgsnap = mock.Mock(
return_value='fake_return')
cg_snapshot = test_utils.create_cgsnapshot(
self.ctxt,
fake_constants.CONSISTENCY_GROUP_ID)
vol = test_utils.create_volume(self.ctxt)
snaps = [
test_utils.create_snapshot(self.ctxt, vol.id)]
ret = common_adapter.delete_cgsnapshot(None, cg_snapshot, snaps)
self.assertEqual('fake_return', ret)
common_adapter.do_delete_cgsnap.assert_called_once_with(
cg_snapshot.consistencygroup_id,
cg_snapshot.id,
cg_snapshot.status,
snaps)
@res_mock.mock_driver_input @res_mock.mock_driver_input
@res_mock.patch_common_adapter @res_mock.patch_common_adapter
def test_delete_cgsnapshot(self, common_adapter, mocked, mocked_input): def test_do_delete_cgsnap(self, common_adapter, _, mocked_input):
group_name = fake_constants.CGSNAPSHOT_ID
snap_name = fake_constants.CGSNAPSHOT_ID
model_update, snapshot_updates = ( model_update, snapshot_updates = (
common_adapter.delete_cgsnapshot( common_adapter.do_delete_cgsnap(
None, mocked_input['cg_snap'], group_name, snap_name, 'available',
[mocked_input['snap1'], mocked_input['snap2']])) [mocked_input['snap1'], mocked_input['snap2']]))
self.assertEqual('deleted', model_update['status']) self.assertEqual('deleted', model_update['status'])
for snap in snapshot_updates: for snap in snapshot_updates:
@ -792,15 +969,13 @@ class TestCommonAdapter(test.TestCase):
@res_mock.mock_driver_input @res_mock.mock_driver_input
@res_mock.patch_common_adapter @res_mock.patch_common_adapter
def test_update_consistencygroup(self, common_adapter, mocked_res, def test_do_update_cg(self, common_adapter, _, mocked_input):
mocked_input):
common_adapter.client.update_consistencygroup = mock.Mock() common_adapter.client.update_consistencygroup = mock.Mock()
cg = mocked_input['cg'] cg = mocked_input['cg']
common_adapter.client.get_cg = mock.Mock(return_value=cg) common_adapter.client.get_cg = mock.Mock(return_value=cg)
common_adapter.do_update_cg(cg.id,
common_adapter.update_consistencygroup(None, cg, [mocked_input['volume_add']],
[mocked_input['volume_add']], [mocked_input['volume_remove']])
[mocked_input['volume_remove']])
common_adapter.client.update_consistencygroup.assert_called_once_with( common_adapter.client.update_consistencygroup.assert_called_once_with(
cg, [1], [2]) cg, [1], [2])

View File

@ -71,3 +71,11 @@ class TestVNXDriver(test.TestCase):
_driver.terminate_connection('fake_volume', {'host': 'fake_host'}) _driver.terminate_connection('fake_volume', {'host': 'fake_host'})
_driver.adapter.terminate_connection.assert_called_once_with( _driver.adapter.terminate_connection.assert_called_once_with(
'fake_volume', {'host': 'fake_host'}) 'fake_volume', {'host': 'fake_host'})
def test_is_consistent_group_snapshot_enabled(self):
_driver = self._get_driver('iscsi')
_driver._stats = {'consistent_group_snapshot_enabled': True}
self.assertTrue(_driver.is_consistent_group_snapshot_enabled())
_driver._stats = {'consistent_group_snapshot_enabled': False}
self.assertFalse(_driver.is_consistent_group_snapshot_enabled())
self.assertFalse(_driver.is_consistent_group_snapshot_enabled())

View File

@ -27,6 +27,18 @@ from cinder.volume.drivers.dell_emc.vnx import common
from cinder.volume.drivers.dell_emc.vnx import utils from cinder.volume.drivers.dell_emc.vnx import utils
class FakeDriver(object):
def __init__(self, support):
self.support = support
def is_consistent_group_snapshot_enabled(self):
return self.support
@utils.require_consistent_group_snapshot_enabled
def fake_method(self):
return 'called'
class TestUtils(test.TestCase): class TestUtils(test.TestCase):
def setUp(self): def setUp(self):
super(TestUtils, self).setUp() super(TestUtils, self).setUp()
@ -173,3 +185,14 @@ class TestUtils(test.TestCase):
'wwn2_1': ['wwnt_1', 'wwnt_3'], 'wwn2_1': ['wwnt_1', 'wwnt_3'],
'wwn2_2': ['wwnt_1', 'wwnt_3']}, 'wwn2_2': ['wwnt_1', 'wwnt_3']},
itor_tgt_map) itor_tgt_map)
def test_cg_snapshot_is_not_enabled(self):
def do():
driver = FakeDriver(False)
driver.fake_method()
self.assertRaises(NotImplementedError, do)
def test_cg_snapshot_is_enabled(self):
driver = FakeDriver(True)
ret = driver.fake_method()
self.assertEqual('called', ret)

View File

@ -222,9 +222,10 @@ class CommonAdapter(object):
'provision': provision, 'provision': provision,
'tier': tier}) 'tier': tier})
cg_id = volume.group_id or volume.consistencygroup_id
lun = self.client.create_lun( lun = self.client.create_lun(
pool, volume_name, volume_size, pool, volume_name, volume_size,
provision, tier, volume.consistencygroup_id, provision, tier, cg_id,
ignore_thresholds=self.config.ignore_pool_full_threshold) ignore_thresholds=self.config.ignore_pool_full_threshold)
location = self._build_provider_location( location = self._build_provider_location(
lun_type='lun', lun_type='lun',
@ -464,15 +465,21 @@ class CommonAdapter(object):
return model_update, volumes_model_update return model_update, volumes_model_update
def create_cgsnapshot(self, context, cgsnapshot, snapshots): def create_cgsnapshot(self, context, cgsnapshot, snapshots):
"""Creates a CG snapshot(snap group).""" """Creates a CG snapshot(snap group)."""
return self.do_create_cgsnap(cgsnapshot.consistencygroup_id,
cgsnapshot.id,
snapshots)
def do_create_cgsnap(self, group_name, snap_name, snapshots):
model_update = {} model_update = {}
snapshots_model_update = [] snapshots_model_update = []
LOG.info(_LI('Creating CG snapshot for consistency group' LOG.info(_LI('Creating consistency snapshot for group'
': %(group_name)s'), ': %(group_name)s'),
{'group_name': cgsnapshot.consistencygroup_id}) {'group_name': group_name})
self.client.create_cg_snapshot(cgsnapshot.id, self.client.create_cg_snapshot(snap_name,
cgsnapshot.consistencygroup_id) group_name)
for snapshot in snapshots: for snapshot in snapshots:
snapshots_model_update.append( snapshots_model_update.append(
{'id': snapshot.id, 'status': 'available'}) {'id': snapshot.id, 'status': 'available'})
@ -482,15 +489,22 @@ class CommonAdapter(object):
def delete_cgsnapshot(self, context, cgsnapshot, snapshots): def delete_cgsnapshot(self, context, cgsnapshot, snapshots):
"""Deletes a CG snapshot(snap group).""" """Deletes a CG snapshot(snap group)."""
return self.do_delete_cgsnap(cgsnapshot.consistencygroup_id,
cgsnapshot.id,
cgsnapshot.status,
snapshots)
def do_delete_cgsnap(self, group_name, snap_name,
snap_status, snapshots):
model_update = {} model_update = {}
snapshots_model_update = [] snapshots_model_update = []
model_update['status'] = cgsnapshot.status model_update['status'] = snap_status
LOG.info(_LI('Deleting CG snapshot %(snap_name)s for consistency ' LOG.info(_LI('Deleting consistency snapshot %(snap_name)s for '
'group: %(group_name)s'), 'group: %(group_name)s'),
{'snap_name': cgsnapshot.id, {'snap_name': snap_name,
'group_name': cgsnapshot.consistencygroup_id}) 'group_name': group_name})
self.client.delete_cg_snapshot(cgsnapshot.id) self.client.delete_cg_snapshot(snap_name)
for snapshot in snapshots: for snapshot in snapshots:
snapshots_model_update.append( snapshots_model_update.append(
{'id': snapshot.id, 'status': 'deleted'}) {'id': snapshot.id, 'status': 'deleted'})
@ -500,6 +514,11 @@ class CommonAdapter(object):
def create_cg_from_cgsnapshot(self, context, group, def create_cg_from_cgsnapshot(self, context, group,
volumes, cgsnapshot, snapshots): volumes, cgsnapshot, snapshots):
return self.do_create_cg_from_cgsnap(
group.id, group.host, volumes, cgsnapshot.id, snapshots)
def do_create_cg_from_cgsnap(self, cg_id, cg_host, volumes,
cgsnap_id, snapshots):
# 1. Copy a temp CG snapshot from CG snapshot # 1. Copy a temp CG snapshot from CG snapshot
# and allow RW for it # and allow RW for it
# 2. Create SMPs from source volumes # 2. Create SMPs from source volumes
@ -509,9 +528,9 @@ class CommonAdapter(object):
# 6. Wait completion of migration # 6. Wait completion of migration
# 7. Create a new CG, add all LUNs to it # 7. Create a new CG, add all LUNs to it
# 8. Delete the temp CG snapshot # 8. Delete the temp CG snapshot
cg_name = group.id cg_name = cg_id
src_cg_snap_name = cgsnapshot.id src_cg_snap_name = cgsnap_id
pool_name = utils.get_pool_from_host(group.host) pool_name = utils.get_pool_from_host(cg_host)
lun_sizes = [] lun_sizes = []
lun_names = [] lun_names = []
src_lun_names = [] src_lun_names = []
@ -549,9 +568,14 @@ class CommonAdapter(object):
def create_cloned_cg(self, context, group, def create_cloned_cg(self, context, group,
volumes, source_cg, source_vols): volumes, source_cg, source_vols):
self.do_clone_cg(group.id, group.host, volumes,
source_cg.id, source_vols)
def do_clone_cg(self, cg_id, cg_host, volumes,
source_cg_id, source_vols):
# 1. Create temp CG snapshot from source_cg # 1. Create temp CG snapshot from source_cg
# Same with steps 2-8 of create_cg_from_cgsnapshot # Same with steps 2-8 of create_cg_from_cgsnapshot
pool_name = utils.get_pool_from_host(group.host) pool_name = utils.get_pool_from_host(cg_host)
lun_sizes = [] lun_sizes = []
lun_names = [] lun_names = []
src_lun_names = [] src_lun_names = []
@ -564,8 +588,8 @@ class CommonAdapter(object):
lun_id_list = emc_taskflow.create_cloned_cg( lun_id_list = emc_taskflow.create_cloned_cg(
client=self.client, client=self.client,
cg_name=group.id, cg_name=cg_id,
src_cg_name=source_cg.id, src_cg_name=source_cg_id,
pool_name=pool_name, pool_name=pool_name,
lun_sizes=lun_sizes, lun_sizes=lun_sizes,
lun_names=lun_names, lun_names=lun_names,
@ -623,6 +647,8 @@ class CommonAdapter(object):
stats['thin_provisioning_support'] = self.client.is_thin_enabled() stats['thin_provisioning_support'] = self.client.is_thin_enabled()
stats['consistencygroup_support'] = self.client.is_snap_enabled() stats['consistencygroup_support'] = self.client.is_snap_enabled()
stats['replication_enabled'] = True if self.mirror_view else False stats['replication_enabled'] = True if self.mirror_view else False
stats['consistent_group_snapshot_enabled'] = (
self.client.is_snap_enabled())
return stats return stats
def get_pool_stats(self, enabler_stats=None): def get_pool_stats(self, enabler_stats=None):
@ -1017,7 +1043,12 @@ class CommonAdapter(object):
def update_consistencygroup(self, context, group, add_volumes, def update_consistencygroup(self, context, group, add_volumes,
remove_volumes): remove_volumes):
cg = self.client.get_cg(name=group.id) return self.do_update_cg(group.id, add_volumes,
remove_volumes)
def do_update_cg(self, cg_name, add_volumes,
remove_volumes):
cg = self.client.get_cg(name=cg_name)
lun_ids_to_add = [self.client.get_lun_id(volume) lun_ids_to_add = [self.client.get_lun_id(volume)
for volume in add_volumes] for volume in add_volumes]
lun_ids_to_remove = [self.client.get_lun_id(volume) lun_ids_to_remove = [self.client.get_lun_id(volume)
@ -1204,6 +1235,46 @@ class CommonAdapter(object):
return {'provider_location': new_volume.provider_location, return {'provider_location': new_volume.provider_location,
'metadata': metadata} 'metadata': metadata}
def create_group(self, context, group):
return self.create_consistencygroup(context, group)
def delete_group(self, context, group, volumes):
return self.delete_consistencygroup(context, group, volumes)
def create_group_snapshot(self, context, group_snapshot, snapshots):
"""Creates a group_snapshot."""
return self.do_create_cgsnap(group_snapshot.group_id,
group_snapshot.id,
snapshots)
def delete_group_snapshot(self, context, group_snapshot, snapshots):
"""Deletes a group snapshot."""
return self.do_delete_cgsnap(
group_snapshot.group_id,
group_snapshot.id,
group_snapshot.status,
snapshots)
def create_group_from_group_snapshot(self,
context, group, volumes,
group_snapshot, snapshots):
"""Creates a group from a group snapshot."""
return self.do_create_cg_from_cgsnap(group.id, group.host, volumes,
group_snapshot.id, snapshots)
def update_group(self, context, group,
add_volumes=None, remove_volumes=None):
"""Updates a group."""
return self.do_update_cg(group.id,
add_volumes,
remove_volumes)
def create_cloned_group(self, context, group, volumes,
source_group, source_vols):
"""Clones a group"""
return self.do_clone_cg(group.id, group.host, volumes,
source_group.id, source_vols)
class ISCSIAdapter(CommonAdapter): class ISCSIAdapter(CommonAdapter):
def __init__(self, configuration, active_backend_id): def __init__(self, configuration, active_backend_id):

View File

@ -86,6 +86,7 @@ class VNXDriver(driver.TransferVD,
self.protocol = self.configuration.storage_protocol.lower() self.protocol = self.configuration.storage_protocol.lower()
self.active_backend_id = kwargs.get('active_backend_id', None) self.active_backend_id = kwargs.get('active_backend_id', None)
self.adapter = None self.adapter = None
self._stats = {}
def do_setup(self, context): def do_setup(self, context):
if self.protocol == common.PROTOCOL_FC: if self.protocol == common.PROTOCOL_FC:
@ -331,3 +332,49 @@ class VNXDriver(driver.TransferVD,
def failover_host(self, context, volumes, secondary_id=None): def failover_host(self, context, volumes, secondary_id=None):
"""Fail-overs volumes from primary device to secondary.""" """Fail-overs volumes from primary device to secondary."""
return self.adapter.failover_host(context, volumes, secondary_id) return self.adapter.failover_host(context, volumes, secondary_id)
@utils.require_consistent_group_snapshot_enabled
def create_group(self, context, group):
"""Creates a group."""
return self.adapter.create_group(context, group)
@utils.require_consistent_group_snapshot_enabled
def delete_group(self, context, group, volumes):
"""Deletes a group."""
return self.adapter.delete_group(
context, group, volumes)
@utils.require_consistent_group_snapshot_enabled
def update_group(self, context, group,
add_volumes=None, remove_volumes=None):
"""Updates a group."""
return self.adapter.update_group(context, group,
add_volumes,
remove_volumes)
@utils.require_consistent_group_snapshot_enabled
def create_group_from_src(self, context, group, volumes,
group_snapshot=None, snapshots=None,
source_group=None, source_vols=None):
"""Creates a group from source."""
if group_snapshot:
return self.adapter.create_group_from_group_snapshot(
context, group, volumes, group_snapshot, snapshots)
elif source_group:
return self.adapter.create_cloned_group(
context, group, volumes, source_group, source_vols)
@utils.require_consistent_group_snapshot_enabled
def create_group_snapshot(self, context, group_snapshot, snapshots):
"""Creates a group_snapshot."""
return self.adapter.create_group_snapshot(
context, group_snapshot, snapshots)
@utils.require_consistent_group_snapshot_enabled
def delete_group_snapshot(self, context, group_snapshot, snapshots):
"""Deletes a group_snapshot."""
return self.adapter.delete_group_snapshot(
context, group_snapshot, snapshots)
def is_consistent_group_snapshot_enabled(self):
return self._stats.get('consistent_group_snapshot_enabled')

View File

@ -246,9 +246,9 @@ def get_migration_rate(volume):
def validate_cg_type(group): def validate_cg_type(group):
if group.get('volume_type_id') is None: if not group.get('volume_type_ids'):
return return
for type_id in group['volume_type_id'].split(","): for type_id in group.get('volume_type_ids'):
if type_id: if type_id:
specs = volume_types.get_volume_type_extra_specs(type_id) specs = volume_types.get_volume_type_extra_specs(type_id)
extra_specs = common.ExtraSpecs(specs) extra_specs = common.ExtraSpecs(specs)
@ -337,3 +337,12 @@ def truncate_fc_port_wwn(wwn):
def is_volume_smp(volume): def is_volume_smp(volume):
return 'smp' == extract_provider_location(volume.provider_location, 'type') return 'smp' == extract_provider_location(volume.provider_location, 'type')
def require_consistent_group_snapshot_enabled(func):
@six.wraps(func)
def inner(self, *args, **kwargs):
if not self.is_consistent_group_snapshot_enabled():
raise NotImplementedError
return func(self, *args, **kwargs)
return inner

View File

@ -0,0 +1,3 @@
---
features:
- Add consistent group capability to generic volume groups in VNX driver.