diff --git a/cinder/tests/unit/volume/drivers/test_coprhd.py b/cinder/tests/unit/volume/drivers/test_coprhd.py index d2eaa16f861..83df077a95f 100644 --- a/cinder/tests/unit/volume/drivers/test_coprhd.py +++ b/cinder/tests/unit/volume/drivers/test_coprhd.py @@ -13,13 +13,12 @@ # License for the specific language governing permissions and limitations # under the License. -from mock import Mock +import mock from cinder import context -from cinder import exception from cinder.objects import fields from cinder import test -from cinder.tests.unit import fake_constants +from cinder.tests.unit import fake_constants as fake from cinder.volume.drivers.coprhd import common as coprhd_common from cinder.volume.drivers.coprhd import fc as coprhd_fc from cinder.volume.drivers.coprhd import iscsi as coprhd_iscsi @@ -185,60 +184,68 @@ scaleio_itl_list = {"itl": [{"hlu": -1, "target": {}}]} -def get_test_volume_data(volume_type_id): - test_volume = {'name': 'test-vol1', - 'size': 1, - 'volume_name': 'test-vol1', - 'id': '1', - 'consistencygroup_id': None, - 'provider_auth': None, - 'project_id': 'project', - 'display_name': 'test-vol1', - 'display_description': 'test volume', - 'volume_type_id': volume_type_id, - 'provider_id': '1', - } - return test_volume +class test_volume_data(object): + name = 'test-vol1' + size = 1 + volume_name = 'test-vol1' + id = fake.VOLUME_ID + group_id = None + provider_auth = None + project_id = fake.PROJECT_ID + display_name = 'test-vol1' + display_description = 'test volume', + volume_type_id = None + provider_id = fake.PROVIDER_ID + + def __init__(self, volume_type_id): + self.volume_type_id = volume_type_id -def get_source_test_volume_data(volume_type_id): - test_volume = {'name': 'source_test-vol1', - 'size': 1, - 'volume_name': 'source_test-vol1', - 'id': '1234', - 'consistencygroup_id': None, - 'provider_auth': None, - 'project_id': 'project', - 'display_name': 'source_test-vol1', - 'display_description': 'test volume', - 'volume_type_id': volume_type_id} - return test_volume +class source_test_volume_data(object): + name = 'source_test-vol1' + size = 1 + volume_name = 'source_test-vol1' + id = fake.VOLUME2_ID + group_id = None + provider_auth = None + project_id = fake.PROJECT_ID + display_name = 'source_test-vol1' + display_description = 'test volume' + volume_type_id = None + + def __init__(self, volume_type_id): + self.volume_type_id = volume_type_id -def get_clone_volume_data(volume_type_id): - clone_test_volume = {'name': 'clone-test-vol1', - 'size': 1, - 'volume_name': 'clone-test-vol1', - 'id': '2', - 'provider_auth': None, - 'project_id': 'project', - 'display_name': 'clone-test-vol1', - 'display_description': 'clone test volume', - 'volume_type_id': volume_type_id} - return clone_test_volume +class test_clone_volume_data(object): + name = 'clone-test-vol1' + size = 1 + volume_name = 'clone-test-vol1' + id = fake.VOLUME3_ID + provider_auth = None + project_id = fake.PROJECT_ID + display_name = 'clone-test-vol1' + display_description = 'clone test volume' + volume_type_id = None + + def __init__(self, volume_type_id): + self.volume_type_id = volume_type_id -def get_test_snapshot_data(src_volume): - test_snapshot = {'name': 'snapshot1', - 'display_name': 'snapshot1', - 'size': 1, - 'id': '1111', - 'volume_name': 'test-vol1', - 'volume_id': '1234', - 'volume': src_volume, - 'volume_size': 1, - 'project_id': 'project'} - return test_snapshot +class test_snapshot_data(object): + name = 'snapshot1' + display_name = 'snapshot1' + size = 1 + id = fake.SNAPSHOT_ID + volume_name = 'test-vol1' + volume_id = fake.VOLUME_ID + volume = None + volume_size = 1 + project_id = fake.PROJECT_ID + status = fields.SnapshotStatus.AVAILABLE + + def __init__(self, src_volume): + self.volume = src_volume def get_connector_data(): @@ -250,26 +257,41 @@ def get_connector_data(): return connector -def get_test_CG_data(volume_type_id): - test_CG = {'name': 'consistency_group_name', - 'id': fake_constants.CONSISTENCY_GROUP_ID, - 'volume_type_id': volume_type_id, - 'status': fields.ConsistencyGroupStatus.AVAILABLE - } - return test_CG +class test_group_data(object): + name = 'group_name' + display_name = 'group_name' + id = fake.GROUP_ID + volume_type_ids = None + volume_types = None + group_type_id = None + status = fields.GroupStatus.AVAILABLE + + def __init__(self, volume_types, group_type_id): + self.group_type_id = group_type_id + self.volume_types = volume_types -def get_test_CG_snap_data(volume_type_id): - test_CG_snapshot = {'name': 'cg_snap_name', - 'id': fake_constants.SNAPSHOT_ID, - 'consistencygroup_id': - fake_constants.CONSISTENCY_GROUP_ID, - 'status': fields.ConsistencyGroupStatus.AVAILABLE, - 'snapshots': [], - 'consistencygroup': get_test_CG_data(volume_type_id), - 'cgsnapshot_id': fake_constants.CGSNAPSHOT_ID, - } - return test_CG_snapshot +class test_group_type_data(object): + name = 'group_name' + display_name = 'group_name' + groupsnapshot_id = None + id = fake.GROUP_TYPE_ID + description = 'group' + + +class test_group_snap_data(object): + name = 'cg_snap_name' + display_name = 'cg_snap_name' + id = fake.GROUP_SNAPSHOT_ID + group_id = fake.GROUP_ID + status = fields.GroupStatus.AVAILABLE + snapshots = [] + group = None + group_type_id = None + + def __init__(self, volume_types, group_type_id): + self.group_type_id = group_type_id + self.group = test_group_data(volume_types, group_type_id) class MockedEMCCoprHDDriverCommon(coprhd_common.EMCCoprHDDriverCommon): @@ -300,22 +322,21 @@ class MockedEMCCoprHDDriverCommon(coprhd_common.EMCCoprHDDriverCommon): return "cg_uri" def init_volume_api(self): - self.volume_api = Mock() + self.volume_api = mock.Mock() self.volume_api.get.return_value = { 'name': 'source_test-vol1', 'size': 1, 'volume_name': 'source_test-vol1', - 'id': fake_constants.VOLUME_ID, - 'consistencygroup_id': fake_constants.CONSISTENCYGROUP_ID, + 'id': fake.VOLUME_ID, + 'group_id': fake.GROUP_ID, 'provider_auth': None, - 'project_id': fake_constants.PROJECT_ID, + 'project_id': fake.PROJECT_ID, 'display_name': 'source_test-vol1', 'display_description': 'test volume', - 'volume_type_id': fake_constants.VOLUME_TYPE_ID, - } + 'volume_type_id': fake.VOLUME_TYPE_ID} def init_coprhd_api_components(self): - self.volume_obj = Mock() + self.volume_obj = mock.Mock() self.volume_obj.create.return_value = "volume_created" self.volume_obj.volume_query.return_value = "volume_uri" self.volume_obj.get_storageAttributes.return_value = ( @@ -342,12 +363,12 @@ class MockedEMCCoprHDDriverCommon(coprhd_common.EMCCoprHDDriverCommon): self.volume_obj.show.return_value = {"id": "vol_id"} self.volume_obj.expand.return_value = "expanded" - self.tag_obj = Mock() + self.tag_obj = mock.Mock() self.tag_obj.list_tags.return_value = [ "Openstack-vol", "Openstack-vol1"] self.tag_obj.tag_resource.return_value = "Tagged" - self.exportgroup_obj = Mock() + self.exportgroup_obj = mock.Mock() self.exportgroup_obj.exportgroup_list.return_value = ( export_group_list) self.exportgroup_obj.exportgroup_show.return_value = ( @@ -356,7 +377,7 @@ class MockedEMCCoprHDDriverCommon(coprhd_common.EMCCoprHDDriverCommon): self.exportgroup_obj.exportgroup_add_volumes.return_value = ( "volume-added") - self.host_obj = Mock() + self.host_obj = mock.Mock() self.host_obj.list_by_tenant.return_value = [] self.host_obj.list_all.return_value = [{'id': "host1_id", 'name': "host1"}] @@ -365,11 +386,11 @@ class MockedEMCCoprHDDriverCommon(coprhd_common.EMCCoprHDDriverCommon): {'name': "12:34:56:78:90:54:32:11"}, {'name': "bfdf432500000004"}] - self.hostinitiator_obj = Mock() - self.varray_obj = Mock() + self.hostinitiator_obj = mock.Mock() + self.varray_obj = mock.Mock() self.varray_obj.varray_show.return_value = varray_detail_data - self.snapshot_obj = Mock() + self.snapshot_obj = mock.Mock() mocked_snap_obj = self.snapshot_obj.return_value mocked_snap_obj.storageResource_query.return_value = ( "resourceUri") @@ -377,10 +398,10 @@ class MockedEMCCoprHDDriverCommon(coprhd_common.EMCCoprHDDriverCommon): "snapshot_created") mocked_snap_obj.snapshot_query.return_value = "snapshot_uri" - self.consistencygroup_obj = Mock() - mocked_cg_object = self.consistencygroup_obj.return_value - mocked_cg_object.create.return_value = "CG-Created" - mocked_cg_object.consistencygroup_query.return_value = "CG-uri" + self.consistencygroup_obj = mock.Mock() + mocked_group_object = self.consistencygroup_obj.return_value + mocked_group_object.create.return_value = "CG-Created" + mocked_group_object.consistencygroup_query.return_value = "CG-uri" class EMCCoprHDISCSIDriverTest(test.TestCase): @@ -391,7 +412,7 @@ class EMCCoprHDISCSIDriverTest(test.TestCase): def create_coprhd_setup(self): - self.configuration = Mock() + self.configuration = mock.Mock() self.configuration.coprhd_hostname = "10.10.10.10" self.configuration.coprhd_port = "4443" self.configuration.volume_backend_name = "EMCCoprHDISCSIDriver" @@ -402,7 +423,10 @@ class EMCCoprHDISCSIDriverTest(test.TestCase): self.configuration.coprhd_varray = "varray" self.configuration.coprhd_emulate_snapshot = False - self.volume_type_id = self.create_coprhd_volume_type() + self.volume_type = self.create_coprhd_volume_type() + self.volume_type_id = self.volume_type.id + self.group_type = test_group_type_data() + self.group_type_id = self.group_type.id self.mock_object(coprhd_iscsi.EMCCoprHDISCSIDriver, '_get_common_driver', @@ -423,8 +447,7 @@ class EMCCoprHDISCSIDriverTest(test.TestCase): "coprhd-volume-type", {'CoprHD:VPOOL': 'vpool_coprhd'}) - volume_id = vipr_volume_type['id'] - return volume_id + return vipr_volume_type def _get_mocked_common_driver(self): return MockedEMCCoprHDDriverCommon( @@ -437,7 +460,7 @@ class EMCCoprHDISCSIDriverTest(test.TestCase): volume_types.destroy(ctx, self.volume_type_id) def test_create_destroy(self): - volume = get_test_volume_data(self.volume_type_id) + volume = test_volume_data(self.volume_type_id) self.driver.create_volume(volume) self.driver.delete_volume(volume) @@ -447,17 +470,17 @@ class EMCCoprHDISCSIDriverTest(test.TestCase): self.assertEqual('unknown', vol_stats['free_capacity_gb']) def test_create_volume_clone(self): - src_volume_data = get_test_volume_data(self.volume_type_id) - clone_volume_data = get_clone_volume_data(self.volume_type_id) + src_volume_data = test_volume_data(self.volume_type_id) + clone_volume_data = test_clone_volume_data(self.volume_type_id) self.driver.create_volume(src_volume_data) self.driver.create_cloned_volume(clone_volume_data, src_volume_data) self.driver.delete_volume(src_volume_data) self.driver.delete_volume(clone_volume_data) def test_create_destroy_snapshot(self): - volume_data = get_test_volume_data(self.volume_type_id) - snapshot_data = get_test_snapshot_data( - get_source_test_volume_data(self.volume_type_id)) + volume_data = test_volume_data(self.volume_type_id) + snapshot_data = test_snapshot_data( + source_test_volume_data(self.volume_type_id)) self.driver.create_volume(volume_data) self.driver.create_snapshot(snapshot_data) @@ -466,11 +489,11 @@ class EMCCoprHDISCSIDriverTest(test.TestCase): def test_create_volume_from_snapshot(self): - src_vol_data = get_source_test_volume_data(self.volume_type_id) + src_vol_data = source_test_volume_data(self.volume_type_id) self.driver.create_volume(src_vol_data) - volume_data = get_test_volume_data(self.volume_type_id) - snapshot_data = get_test_snapshot_data(src_vol_data) + volume_data = test_volume_data(self.volume_type_id) + snapshot_data = test_snapshot_data(src_vol_data) self.driver.create_snapshot(snapshot_data) self.driver.create_volume_from_snapshot(volume_data, snapshot_data) @@ -480,14 +503,14 @@ class EMCCoprHDISCSIDriverTest(test.TestCase): self.driver.delete_volume(volume_data) def test_extend_volume(self): - volume_data = get_test_volume_data(self.volume_type_id) + volume_data = test_volume_data(self.volume_type_id) self.driver.create_volume(volume_data) self.driver.extend_volume(volume_data, 2) self.driver.delete_volume(volume_data) def test_initialize_and_terminate_connection(self): connector_data = get_connector_data() - volume_data = get_test_volume_data(self.volume_type_id) + volume_data = test_volume_data(self.volume_type_id) self.driver.create_volume(volume_data) res_initialize = self.driver.initialize_connection( @@ -498,54 +521,63 @@ class EMCCoprHDISCSIDriverTest(test.TestCase): 'target_iqn': '50:00:09:73:00:18:95:19', 'target_discovered': False, - 'volume_id': '1'}} + 'volume_id': fake.VOLUME_ID}} self.assertEqual( expected_initialize, res_initialize, 'Unexpected return data') self.driver.terminate_connection(volume_data, connector_data) self.driver.delete_volume(volume_data) - def test_create_delete_empty_CG(self): - cg_data = get_test_CG_data(self.volume_type_id) + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_create_delete_empty_group(self, cg_ss_enabled): + cg_ss_enabled.side_effect = [True, True] + group_data = test_group_data([self.volume_type], + self.group_type_id) ctx = context.get_admin_context() - self.driver.create_consistencygroup(ctx, cg_data) + self.driver.create_group(ctx, group_data) model_update, volumes_model_update = ( - self.driver.delete_consistencygroup(ctx, cg_data, [])) + self.driver.delete_group(ctx, group_data, [])) self.assertEqual([], volumes_model_update, 'Unexpected return data') - def test_create_update_delete_CG(self): - cg_data = get_test_CG_data(self.volume_type_id) + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_create_update_delete_group(self, cg_ss_enabled): + cg_ss_enabled.side_effect = [True, True, True, True] + group_data = test_group_data([self.volume_type], + self.group_type_id) ctx = context.get_admin_context() - self.driver.create_consistencygroup(ctx, cg_data) + self.driver.create_group(ctx, group_data) - volume = get_test_volume_data(self.volume_type_id) + volume = test_volume_data(self.volume_type_id) self.driver.create_volume(volume) model_update, ret1, ret2 = ( - self.driver.update_consistencygroup(ctx, cg_data, [volume], [])) + self.driver.update_group(ctx, group_data, [volume], [])) - self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE}, + self.assertEqual({'status': fields.GroupStatus.AVAILABLE}, model_update) model_update, volumes_model_update = ( - self.driver.delete_consistencygroup(ctx, cg_data, [volume])) - self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE}, + self.driver.delete_group(ctx, group_data, [volume])) + self.assertEqual({'status': fields.GroupStatus.AVAILABLE}, model_update) - self.assertEqual([{'status': 'deleted', 'id': '1'}], + self.assertEqual([{'status': 'deleted', 'id': fake.VOLUME_ID}], volumes_model_update) - def test_create_delete_CG_snap(self): - cg_snap_data = get_test_CG_snap_data(self.volume_type_id) + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_create_delete_group_snap(self, cg_ss_enabled): + cg_ss_enabled.side_effect = [True, True] + group_snap_data = test_group_snap_data([self.volume_type], + self.group_type_id) ctx = context.get_admin_context() model_update, snapshots_model_update = ( - self.driver.create_cgsnapshot(ctx, cg_snap_data, [])) - self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE}, + self.driver.create_group_snapshot(ctx, group_snap_data, [])) + self.assertEqual({'status': fields.GroupStatus.AVAILABLE}, model_update) self.assertEqual([], snapshots_model_update, 'Unexpected return data') model_update, snapshots_model_update = ( - self.driver.delete_cgsnapshot(ctx, cg_snap_data, [])) + self.driver.delete_group_snapshot(ctx, group_snap_data, [])) self.assertEqual({}, model_update, 'Unexpected return data') self.assertEqual([], snapshots_model_update, 'Unexpected return data') @@ -558,7 +590,7 @@ class EMCCoprHDFCDriverTest(test.TestCase): def create_coprhd_setup(self): - self.configuration = Mock() + self.configuration = mock.Mock() self.configuration.coprhd_hostname = "10.10.10.10" self.configuration.coprhd_port = "4443" self.configuration.volume_backend_name = "EMCCoprHDFCDriver" @@ -569,7 +601,10 @@ class EMCCoprHDFCDriverTest(test.TestCase): self.configuration.coprhd_varray = "varray" self.configuration.coprhd_emulate_snapshot = False - self.volume_type_id = self.create_coprhd_volume_type() + self.volume_type = self.create_coprhd_volume_type() + self.volume_type_id = self.volume_type.id + self.group_type = test_group_type_data() + self.group_type_id = self.group_type.id self.mock_object(coprhd_fc.EMCCoprHDFCDriver, '_get_common_driver', @@ -589,8 +624,7 @@ class EMCCoprHDFCDriverTest(test.TestCase): vipr_volume_type = volume_types.create(ctx, "coprhd-volume-type", {'CoprHD:VPOOL': 'vpool_vipr'}) - volume_id = vipr_volume_type['id'] - return volume_id + return vipr_volume_type def _get_mocked_common_driver(self): return MockedEMCCoprHDDriverCommon( @@ -603,7 +637,7 @@ class EMCCoprHDFCDriverTest(test.TestCase): volume_types.destroy(ctx, self.volume_type_id) def test_create_destroy(self): - volume = get_test_volume_data(self.volume_type_id) + volume = test_volume_data(self.volume_type_id) self.driver.create_volume(volume) self.driver.delete_volume(volume) @@ -614,8 +648,8 @@ class EMCCoprHDFCDriverTest(test.TestCase): def test_create_volume_clone(self): - src_volume_data = get_test_volume_data(self.volume_type_id) - clone_volume_data = get_clone_volume_data(self.volume_type_id) + src_volume_data = test_volume_data(self.volume_type_id) + clone_volume_data = test_clone_volume_data(self.volume_type_id) self.driver.create_volume(src_volume_data) self.driver.create_cloned_volume(clone_volume_data, src_volume_data) self.driver.delete_volume(src_volume_data) @@ -623,9 +657,9 @@ class EMCCoprHDFCDriverTest(test.TestCase): def test_create_destroy_snapshot(self): - volume_data = get_test_volume_data(self.volume_type_id) - snapshot_data = get_test_snapshot_data( - get_source_test_volume_data(self.volume_type_id)) + volume_data = test_volume_data(self.volume_type_id) + snapshot_data = test_snapshot_data( + source_test_volume_data(self.volume_type_id)) self.driver.create_volume(volume_data) self.driver.create_snapshot(snapshot_data) @@ -633,11 +667,11 @@ class EMCCoprHDFCDriverTest(test.TestCase): self.driver.delete_volume(volume_data) def test_create_volume_from_snapshot(self): - src_vol_data = get_source_test_volume_data(self.volume_type_id) + src_vol_data = source_test_volume_data(self.volume_type_id) self.driver.create_volume(src_vol_data) - volume_data = get_test_volume_data(self.volume_type_id) - snapshot_data = get_test_snapshot_data(src_vol_data) + volume_data = test_volume_data(self.volume_type_id) + snapshot_data = test_snapshot_data(src_vol_data) self.driver.create_snapshot(snapshot_data) self.driver.create_volume_from_snapshot(volume_data, snapshot_data) @@ -646,22 +680,8 @@ class EMCCoprHDFCDriverTest(test.TestCase): self.driver.delete_volume(src_vol_data) self.driver.delete_volume(volume_data) - def test_create_volume_from_cg_snapshot(self): - ctx = context.get_admin_context() - - volume_data = get_test_volume_data(self.volume_type_id) - cg_snap_data = get_test_CG_snap_data(self.volume_type_id) - - self.driver.create_cgsnapshot(ctx, cg_snap_data, []) - self.assertRaises(exception.VolumeBackendAPIException, - self.driver.create_volume_from_snapshot, - volume_data, cg_snap_data) - - self.driver.delete_cgsnapshot(ctx, cg_snap_data, []) - self.driver.delete_volume(volume_data) - def test_extend_volume(self): - volume_data = get_test_volume_data(self.volume_type_id) + volume_data = test_volume_data(self.volume_type_id) self.driver.create_volume(volume_data) self.driver.extend_volume(volume_data, 2) self.driver.delete_volume(volume_data) @@ -669,7 +689,7 @@ class EMCCoprHDFCDriverTest(test.TestCase): def test_initialize_and_terminate_connection(self): connector_data = get_connector_data() - volume_data = get_test_volume_data(self.volume_type_id) + volume_data = test_volume_data(self.volume_type_id) self.driver.create_volume(volume_data) res_initiatlize = self.driver.initialize_connection( @@ -686,7 +706,7 @@ class EMCCoprHDFCDriverTest(test.TestCase): 'target_wwn': ['1234567890123456', '1234567890123456'], 'target_discovered': False, - 'volume_id': '1'}} + 'volume_id': fake.VOLUME_ID}} self.assertEqual( expected_initialize, res_initiatlize, 'Unexpected return data') @@ -707,47 +727,56 @@ class EMCCoprHDFCDriverTest(test.TestCase): self.driver.delete_volume(volume_data) - def test_create_delete_empty_CG(self): - cg_data = get_test_CG_data(self.volume_type_id) + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_create_delete_empty_group(self, cg_ss_enabled): + cg_ss_enabled.side_effect = [True, True] + group_data = test_group_data([self.volume_type], + self.group_type_id) ctx = context.get_admin_context() - self.driver.create_consistencygroup(ctx, cg_data) + self.driver.create_group(ctx, group_data) model_update, volumes_model_update = ( - self.driver.delete_consistencygroup(ctx, cg_data, [])) + self.driver.delete_group(ctx, group_data, [])) self.assertEqual([], volumes_model_update, 'Unexpected return data') - def test_create_update_delete_CG(self): - cg_data = get_test_CG_data(self.volume_type_id) + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_create_update_delete_group(self, cg_ss_enabled): + cg_ss_enabled.side_effect = [True, True, True] + group_data = test_group_data([self.volume_type], + self.group_type_id) ctx = context.get_admin_context() - self.driver.create_consistencygroup(ctx, cg_data) + self.driver.create_group(ctx, group_data) - volume = get_test_volume_data(self.volume_type_id) + volume = test_volume_data(self.volume_type_id) self.driver.create_volume(volume) model_update, ret1, ret2 = ( - self.driver.update_consistencygroup(ctx, cg_data, [volume], [])) + self.driver.update_group(ctx, group_data, [volume], [])) - self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE}, + self.assertEqual({'status': fields.GroupStatus.AVAILABLE}, model_update) model_update, volumes_model_update = ( - self.driver.delete_consistencygroup(ctx, cg_data, [volume])) - self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE}, + self.driver.delete_group(ctx, group_data, [volume])) + self.assertEqual({'status': fields.GroupStatus.AVAILABLE}, model_update) - self.assertEqual([{'status': 'deleted', 'id': '1'}], + self.assertEqual([{'status': 'deleted', 'id': fake.VOLUME_ID}], volumes_model_update) - def test_create_delete_CG_snap(self): - cg_snap_data = get_test_CG_snap_data(self.volume_type_id) + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_create_delete_group_snap(self, cg_ss_enabled): + cg_ss_enabled.side_effect = [True, True] + group_snap_data = test_group_snap_data([self.volume_type], + self.group_type_id) ctx = context.get_admin_context() model_update, snapshots_model_update = ( - self.driver.create_cgsnapshot(ctx, cg_snap_data, [])) - self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE}, + self.driver.create_group_snapshot(ctx, group_snap_data, [])) + self.assertEqual({'status': fields.GroupStatus.AVAILABLE}, model_update) self.assertEqual([], snapshots_model_update, 'Unexpected return data') model_update, snapshots_model_update = ( - self.driver.delete_cgsnapshot(ctx, cg_snap_data, [])) + self.driver.delete_group_snapshot(ctx, group_snap_data, [])) self.assertEqual({}, model_update, 'Unexpected return data') self.assertEqual([], snapshots_model_update, 'Unexpected return data') @@ -760,7 +789,7 @@ class EMCCoprHDScaleIODriverTest(test.TestCase): def create_coprhd_setup(self): - self.configuration = Mock() + self.configuration = mock.Mock() self.configuration.coprhd_hostname = "10.10.10.10" self.configuration.coprhd_port = "4443" self.configuration.volume_backend_name = "EMCCoprHDFCDriver" @@ -779,7 +808,10 @@ class EMCCoprHDScaleIODriverTest(test.TestCase): self.configuration.scaleio_server_certificate_path = ( "/etc/scaleio/certs") - self.volume_type_id = self.create_coprhd_volume_type() + self.volume_type = self.create_coprhd_volume_type() + self.volume_type_id = self.volume_type.id + self.group_type = test_group_type_data() + self.group_type_id = self.group_type.id self.mock_object(coprhd_scaleio.EMCCoprHDScaleIODriver, '_get_common_driver', @@ -802,8 +834,7 @@ class EMCCoprHDScaleIODriverTest(test.TestCase): vipr_volume_type = volume_types.create(ctx, "coprhd-volume-type", {'CoprHD:VPOOL': 'vpool_vipr'}) - volume_id = vipr_volume_type['id'] - return volume_id + return vipr_volume_type def _get_mocked_common_driver(self): return MockedEMCCoprHDDriverCommon( @@ -820,7 +851,7 @@ class EMCCoprHDScaleIODriverTest(test.TestCase): volume_types.destroy(ctx, self.volume_type_id) def test_create_destroy(self): - volume = get_test_volume_data(self.volume_type_id) + volume = test_volume_data(self.volume_type_id) self.driver.create_volume(volume) self.driver.delete_volume(volume) @@ -831,8 +862,8 @@ class EMCCoprHDScaleIODriverTest(test.TestCase): def test_create_volume_clone(self): - src_volume_data = get_test_volume_data(self.volume_type_id) - clone_volume_data = get_clone_volume_data(self.volume_type_id) + src_volume_data = test_volume_data(self.volume_type_id) + clone_volume_data = test_clone_volume_data(self.volume_type_id) self.driver.create_volume(src_volume_data) self.driver.create_cloned_volume(clone_volume_data, src_volume_data) self.driver.delete_volume(src_volume_data) @@ -840,9 +871,9 @@ class EMCCoprHDScaleIODriverTest(test.TestCase): def test_create_destroy_snapshot(self): - volume_data = get_test_volume_data(self.volume_type_id) - snapshot_data = get_test_snapshot_data( - get_source_test_volume_data(self.volume_type_id)) + volume_data = test_volume_data(self.volume_type_id) + snapshot_data = test_snapshot_data( + source_test_volume_data(self.volume_type_id)) self.driver.create_volume(volume_data) self.driver.create_snapshot(snapshot_data) @@ -850,11 +881,11 @@ class EMCCoprHDScaleIODriverTest(test.TestCase): self.driver.delete_volume(volume_data) def test_create_volume_from_snapshot(self): - src_vol_data = get_source_test_volume_data(self.volume_type_id) + src_vol_data = source_test_volume_data(self.volume_type_id) self.driver.create_volume(src_vol_data) - volume_data = get_test_volume_data(self.volume_type_id) - snapshot_data = get_test_snapshot_data(src_vol_data) + volume_data = test_volume_data(self.volume_type_id) + snapshot_data = test_snapshot_data(src_vol_data) self.driver.create_snapshot(snapshot_data) self.driver.create_volume_from_snapshot(volume_data, snapshot_data) @@ -864,7 +895,7 @@ class EMCCoprHDScaleIODriverTest(test.TestCase): self.driver.delete_volume(volume_data) def test_extend_volume(self): - volume_data = get_test_volume_data(self.volume_type_id) + volume_data = test_volume_data(self.volume_type_id) self.driver.create_volume(volume_data) self.driver.extend_volume(volume_data, 2) self.driver.delete_volume(volume_data) @@ -872,16 +903,17 @@ class EMCCoprHDScaleIODriverTest(test.TestCase): def test_initialize_and_terminate_connection(self): connector_data = get_connector_data() - volume_data = get_test_volume_data(self.volume_type_id) + volume_data = test_volume_data(self.volume_type_id) self.driver.create_volume(volume_data) res_initiatlize = self.driver.initialize_connection( volume_data, connector_data) + exp_name = res_initiatlize['data']['scaleIO_volname'] expected_initialize = {'data': {'bandwidthLimit': None, 'hostIP': '10.0.0.2', 'iopsLimit': None, - 'scaleIO_volname': 'test-vol1', - 'scaleIO_volume_id': '1', + 'scaleIO_volname': exp_name, + 'scaleIO_volume_id': fake.PROVIDER_ID, 'serverIP': '10.10.10.11', 'serverPassword': 'scaleio_password', 'serverPort': 443, @@ -895,46 +927,55 @@ class EMCCoprHDScaleIODriverTest(test.TestCase): volume_data, connector_data) self.driver.delete_volume(volume_data) - def test_create_delete_empty_CG(self): - cg_data = get_test_CG_data(self.volume_type_id) + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_create_delete_empty_group(self, cg_ss_enabled): + cg_ss_enabled.side_effect = [True, True] + group_data = test_group_data([self.volume_type], + self.group_type_id) ctx = context.get_admin_context() - self.driver.create_consistencygroup(ctx, cg_data) + self.driver.create_group(ctx, group_data) model_update, volumes_model_update = ( - self.driver.delete_consistencygroup(ctx, cg_data, [])) + self.driver.delete_group(ctx, group_data, [])) self.assertEqual([], volumes_model_update, 'Unexpected return data') - def test_create_update_delete_CG(self): - cg_data = get_test_CG_data(self.volume_type_id) + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_create_update_delete_group(self, cg_ss_enabled): + cg_ss_enabled.side_effect = [True, True, True, True] + group_data = test_group_data([self.volume_type], + self.group_type_id) ctx = context.get_admin_context() - self.driver.create_consistencygroup(ctx, cg_data) + self.driver.create_group(ctx, group_data) - volume = get_test_volume_data(self.volume_type_id) + volume = test_volume_data(self.volume_type_id) self.driver.create_volume(volume) model_update, ret1, ret2 = ( - self.driver.update_consistencygroup(ctx, cg_data, [volume], [])) + self.driver.update_group(ctx, group_data, [volume], [])) - self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE}, + self.assertEqual({'status': fields.GroupStatus.AVAILABLE}, model_update) model_update, volumes_model_update = ( - self.driver.delete_consistencygroup(ctx, cg_data, [volume])) - self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE}, + self.driver.delete_group(ctx, group_data, [volume])) + self.assertEqual({'status': fields.GroupStatus.AVAILABLE}, model_update) - self.assertEqual([{'status': 'deleted', 'id': '1'}], + self.assertEqual([{'status': 'deleted', 'id': fake.VOLUME_ID}], volumes_model_update) - def test_create_delete_CG_snap(self): - cg_snap_data = get_test_CG_snap_data(self.volume_type_id) + @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type') + def test_create_delete_group_snap(self, cg_ss_enabled): + cg_ss_enabled.side_effect = [True, True] + group_snap_data = test_group_snap_data([self.volume_type], + self.group_type_id) ctx = context.get_admin_context() model_update, snapshots_model_update = ( - self.driver.create_cgsnapshot(ctx, cg_snap_data, [])) - self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE}, + self.driver.create_group_snapshot(ctx, group_snap_data, [])) + self.assertEqual({'status': fields.GroupStatus.AVAILABLE}, model_update) self.assertEqual([], snapshots_model_update, 'Unexpected return data') model_update, snapshots_model_update = ( - self.driver.delete_cgsnapshot(ctx, cg_snap_data, [])) + self.driver.delete_group_snapshot(ctx, group_snap_data, [])) self.assertEqual({}, model_update, 'Unexpected return data') self.assertEqual([], snapshots_model_update, 'Unexpected return data') diff --git a/cinder/volume/drivers/coprhd/common.py b/cinder/volume/drivers/coprhd/common.py index 7c474e9936b..1c807276422 100644 --- a/cinder/volume/drivers/coprhd/common.py +++ b/cinder/volume/drivers/coprhd/common.py @@ -45,9 +45,9 @@ from cinder.volume.drivers.coprhd.helpers import tag as coprhd_tag from cinder.volume.drivers.coprhd.helpers import ( virtualarray as coprhd_varray) from cinder.volume.drivers.coprhd.helpers import volume as coprhd_vol +from cinder.volume import utils as volume_utils from cinder.volume import volume_types - LOG = logging.getLogger(__name__) MAX_RETRIES = 10 @@ -88,6 +88,10 @@ CONF.register_opts(volume_opts, group=configuration.SHARED_CONF_GROUP) URI_VPOOL_VARRAY_CAPACITY = '/block/vpools/{0}/varrays/{1}/capacity' URI_BLOCK_EXPORTS_FOR_INITIATORS = '/block/exports?initiators={0}' EXPORT_RETRY_COUNT = 5 +MAX_DEFAULT_NAME_LENGTH = 128 +MAX_SNAPSHOT_NAME_LENGTH = 63 +MAX_CONSISTENCY_GROUP_NAME_LENGTH = 64 +MAX_SIO_LEN = 31 def retry_wrapper(func): @@ -225,8 +229,9 @@ class EMCCoprHDDriverCommon(object): def create_volume(self, vol, driver, truncate_name=False): self.authenticate_user() - name = self._get_resource_name(vol, truncate_name) - size = int(vol['size']) * units.Gi + name = self._get_resource_name(vol, MAX_DEFAULT_NAME_LENGTH, + truncate_name) + size = int(vol.size) * units.Gi vpool = self._get_vpool(vol) self.vpool = vpool['CoprHD:VPOOL'] @@ -234,14 +239,17 @@ class EMCCoprHDDriverCommon(object): try: coprhd_cgid = None try: - cgid = vol['consistencygroup_id'] - if cgid: - coprhd_cgid = self._get_coprhd_cgid(cgid) + if vol.group_id: + if volume_utils.is_group_a_cg_snapshot_type(vol.group): + coprhd_cgid = self._get_coprhd_cgid(vol.group_id) except KeyError: coprhd_cgid = None + except AttributeError: + coprhd_cgid = None full_project_name = ("%s/%s" % (self.configuration.coprhd_tenant, - self.configuration.coprhd_project)) + self.configuration.coprhd_project) + ) self.volume_obj.create(full_project_name, name, size, self.configuration.coprhd_varray, self.vpool, @@ -249,6 +257,7 @@ class EMCCoprHDDriverCommon(object): sync=True, # no longer specified in volume creation consistencygroup=coprhd_cgid) + except coprhd_utils.CoprHdError as e: coprhd_err_msg = (_("Volume %(name)s: create failed\n%(err)s") % {'name': name, 'err': six.text_type(e.msg)}) @@ -260,7 +269,9 @@ class EMCCoprHDDriverCommon(object): @retry_wrapper def create_consistencygroup(self, context, group, truncate_name=False): self.authenticate_user() - name = self._get_resource_name(group, truncate_name) + name = self._get_resource_name(group, + MAX_CONSISTENCY_GROUP_NAME_LENGTH, + truncate_name) try: self.consistencygroup_obj.create( @@ -291,8 +302,8 @@ class EMCCoprHDDriverCommon(object): def update_consistencygroup(self, group, add_volumes, remove_volumes): self.authenticate_user() - model_update = {'status': fields.ConsistencyGroupStatus.AVAILABLE} - cg_uri = self._get_coprhd_cgid(group['id']) + model_update = {'status': fields.GroupStatus.AVAILABLE} + cg_uri = self._get_coprhd_cgid(group.id) add_volnames = [] remove_volnames = [] @@ -329,7 +340,9 @@ class EMCCoprHDDriverCommon(object): def delete_consistencygroup(self, context, group, volumes, truncate_name=False): self.authenticate_user() - name = self._get_resource_name(group, truncate_name) + name = self._get_resource_name(group, + MAX_CONSISTENCY_GROUP_NAME_LENGTH, + truncate_name) volumes_model_update = [] try: @@ -344,20 +357,20 @@ class EMCCoprHDDriverCommon(object): sync=True, force_delete=True) - update_item = {'id': vol['id'], + update_item = {'id': vol.id, 'status': - fields.ConsistencyGroupStatus.DELETED} + fields.GroupStatus.DELETED} volumes_model_update.append(update_item) except exception.VolumeBackendAPIException: - update_item = {'id': vol['id'], + update_item = {'id': vol.id, 'status': fields.ConsistencyGroupStatus. ERROR_DELETING} volumes_model_update.append(update_item) LOG.exception("Failed to delete the volume %s of CG.", - vol['name']) + vol.name) self.consistencygroup_obj.delete( name, @@ -365,7 +378,7 @@ class EMCCoprHDDriverCommon(object): self.configuration.coprhd_tenant) model_update = {} - model_update['status'] = group['status'] + model_update['status'] = group.status return model_update, volumes_model_update @@ -384,9 +397,19 @@ class EMCCoprHDDriverCommon(object): self.authenticate_user() snapshots_model_update = [] - cgsnapshot_name = self._get_resource_name(cgsnapshot, truncate_name) - cg_id = cgsnapshot['consistencygroup_id'] - cg_group = cgsnapshot.get('consistencygroup') + cgsnapshot_name = self._get_resource_name(cgsnapshot, + MAX_SNAPSHOT_NAME_LENGTH, + truncate_name) + + cg_id = None + cg_group = None + + try: + cg_id = cgsnapshot.group_id + cg_group = cgsnapshot.group + except AttributeError: + pass + cg_name = None coprhd_cgid = None @@ -408,7 +431,7 @@ class EMCCoprHDDriverCommon(object): True) for snapshot in snapshots: - vol_id_of_snap = snapshot['volume_id'] + vol_id_of_snap = snapshot.volume_id # Finding the volume in CoprHD for this volume id tagname = "OpenStack:id:" + vol_id_of_snap @@ -470,7 +493,7 @@ class EMCCoprHDDriverCommon(object): snapshot['status'] = fields.SnapshotStatus.AVAILABLE snapshots_model_update.append( - {'id': snapshot['id'], 'status': + {'id': snapshot.id, 'status': fields.SnapshotStatus.AVAILABLE}) model_update = {'status': fields.ConsistencyGroupStatus.AVAILABLE} @@ -493,19 +516,28 @@ class EMCCoprHDDriverCommon(object): @retry_wrapper def delete_cgsnapshot(self, cgsnapshot, snapshots, truncate_name=False): self.authenticate_user() - cgsnapshot_id = cgsnapshot['id'] - cgsnapshot_name = self._get_resource_name(cgsnapshot, truncate_name) + cgsnapshot_id = cgsnapshot.id + cgsnapshot_name = self._get_resource_name(cgsnapshot, + MAX_SNAPSHOT_NAME_LENGTH, + truncate_name) snapshots_model_update = [] - cg_id = cgsnapshot['consistencygroup_id'] - cg_group = cgsnapshot.get('consistencygroup') + + cg_id = None + cg_group = None + + try: + cg_id = cgsnapshot.group_id + cg_group = cgsnapshot.group + except AttributeError: + pass coprhd_cgid = self._get_coprhd_cgid(cg_id) cg_name = self._get_consistencygroup_name(cg_group) model_update = {} LOG.info('Delete cgsnapshot %(snap_name)s for consistency group: ' - '%(group_name)s', {'snap_name': cgsnapshot['name'], + '%(group_name)s', {'snap_name': cgsnapshot.name, 'group_name': cg_name}) try: @@ -531,7 +563,7 @@ class EMCCoprHDDriverCommon(object): for snapshot in snapshots: snapshots_model_update.append( - {'id': snapshot['id'], + {'id': snapshot.id, 'status': fields.SnapshotStatus.DELETED}) return model_update, snapshots_model_update @@ -557,7 +589,9 @@ class EMCCoprHDDriverCommon(object): exempt_tags = [] self.authenticate_user() - name = self._get_resource_name(vol, truncate_name) + name = self._get_resource_name(vol, + MAX_DEFAULT_NAME_LENGTH, + truncate_name) full_project_name = ("%s/%s" % ( self.configuration.coprhd_tenant, self.configuration.coprhd_project)) @@ -613,11 +647,16 @@ class EMCCoprHDDriverCommon(object): if ((not prop.startswith("status") and not prop.startswith("obj_status") and prop != "obj_volume") and value): - add_tags.append( - "%s:%s:%s" % (self.OPENSTACK_TAG, prop, - six.text_type(value))) + tag = ("%s:%s:%s" % + (self.OPENSTACK_TAG, prop, + six.text_type(value))) + + if len(tag) > 128: + tag = tag[0:128] + add_tags.append(tag) except TypeError: - LOG.error("Error tagging the resource property %s", prop) + LOG.error( + "Error tagging the resource property %s", prop) except TypeError: LOG.error("Error tagging the resource properties") @@ -638,17 +677,21 @@ class EMCCoprHDDriverCommon(object): def create_cloned_volume(self, vol, src_vref, truncate_name=False): """Creates a clone of the specified volume.""" self.authenticate_user() - name = self._get_resource_name(vol, truncate_name) + name = self._get_resource_name(vol, + MAX_DEFAULT_NAME_LENGTH, + truncate_name) srcname = self._get_coprhd_volume_name(src_vref) try: - if src_vref['consistencygroup_id']: + if src_vref.group_id: raise coprhd_utils.CoprHdError( coprhd_utils.CoprHdError.SOS_FAILURE_ERR, _("Clone can't be taken individually on a volume" " that is part of a Consistency Group")) except KeyError as e: pass + except AttributeError: + pass try: (storageres_type, storageres_typename) = self.volume_obj.get_storageAttributes( @@ -691,13 +734,21 @@ class EMCCoprHDDriverCommon(object): self._raise_or_log_exception(e.err_code, coprhd_err_msg, log_err_msg) - try: - src_vol_size = src_vref['size'] - except KeyError: - src_vol_size = src_vref['volume_size'] + src_vol_size = 0 + dest_vol_size = 0 - if vol['size'] > src_vol_size: - size_in_bytes = coprhd_utils.to_bytes("%sG" % vol['size']) + try: + src_vol_size = src_vref.size + except AttributeError: + src_vol_size = src_vref.volume_size + + try: + dest_vol_size = vol.size + except AttributeError: + dest_vol_size = vol.volume_size + + if dest_vol_size > src_vol_size: + size_in_bytes = coprhd_utils.to_bytes("%sG" % dest_vol_size) try: self.volume_obj.expand( ("%s/%s" % (self.configuration.coprhd_tenant, @@ -733,7 +784,8 @@ class EMCCoprHDDriverCommon(object): {'volume_name': volume_name, 'err': six.text_type(e.msg)}) - log_err_msg = "Volume : %s expand failed" % volume_name + log_err_msg = ("Volume : %s expand failed" % + volume_name) self._raise_or_log_exception(e.err_code, coprhd_err_msg, log_err_msg) @@ -747,15 +799,20 @@ class EMCCoprHDDriverCommon(object): self.create_cloned_volume(volume, snapshot, truncate_name) return - if snapshot.get('cgsnapshot_id'): - raise coprhd_utils.CoprHdError( - coprhd_utils.CoprHdError.SOS_FAILURE_ERR, - _("Volume cannot be created individually from a snapshot " - "that is part of a Consistency Group")) + try: + if snapshot.group_snapshot_id: + raise coprhd_utils.CoprHdError( + coprhd_utils.CoprHdError.SOS_FAILURE_ERR, + _("Volume cannot be created individually from a snapshot " + "that is part of a Consistency Group")) + except AttributeError: + pass src_snapshot_name = None - src_vol_ref = snapshot['volume'] - new_volume_name = self._get_resource_name(volume, truncate_name) + src_vol_ref = snapshot.volume + new_volume_name = self._get_resource_name(volume, + MAX_DEFAULT_NAME_LENGTH, + truncate_name) try: coprhd_vol_info = self._get_coprhd_volume_name( @@ -786,12 +843,13 @@ class EMCCoprHDDriverCommon(object): {'src_snapshot_name': src_snapshot_name, 'err': six.text_type(e.msg)}) - log_err_msg = "Snapshot : %s clone failed" % src_snapshot_name + log_err_msg = ("Snapshot : %s clone failed" % + src_snapshot_name) self._raise_or_log_exception(e.err_code, coprhd_err_msg, log_err_msg) - if volume['size'] > snapshot['volume_size']: - size_in_bytes = coprhd_utils.to_bytes("%sG" % volume['size']) + if volume.size > snapshot.volume_size: + size_in_bytes = coprhd_utils.to_bytes("%sG" % volume.size) try: self.volume_obj.expand( @@ -805,7 +863,8 @@ class EMCCoprHDDriverCommon(object): {'volume_name': new_volume_name, 'err': six.text_type(e.msg)}) - log_err_msg = "Volume : %s expand failed" % new_volume_name + log_err_msg = ("Volume : %s expand failed" % + new_volume_name) self._raise_or_log_exception(e.err_code, coprhd_err_msg, log_err_msg) @@ -829,7 +888,7 @@ class EMCCoprHDDriverCommon(object): "\n%(err)s") % {'name': name, 'err': six.text_type(e.msg)}) - log_err_msg = "Volume : %s delete failed" % name + log_err_msg = ("Volume : %s delete failed" % name) self._raise_or_log_exception(e.err_code, coprhd_err_msg, log_err_msg) @@ -837,10 +896,10 @@ class EMCCoprHDDriverCommon(object): def create_snapshot(self, snapshot, truncate_name=False): self.authenticate_user() - volume = snapshot['volume'] + volume = snapshot.volume try: - if volume['consistencygroup_id']: + if volume.group_id: raise coprhd_utils.CoprHdError( coprhd_utils.CoprHdError.SOS_FAILURE_ERR, _("Snapshot can't be taken individually on a volume" @@ -855,8 +914,10 @@ class EMCCoprHDDriverCommon(object): return try: - snapshotname = self._get_resource_name(snapshot, truncate_name) - vol = snapshot['volume'] + snapshotname = self._get_resource_name(snapshot, + MAX_SNAPSHOT_NAME_LENGTH, + truncate_name) + vol = snapshot.volume volumename = self._get_coprhd_volume_name(vol) projectname = self.configuration.coprhd_project @@ -894,7 +955,7 @@ class EMCCoprHDDriverCommon(object): "\n%(err)s") % {'snapshotname': snapshotname, 'err': six.text_type(e.msg)}) - log_err_msg = "Snapshot : %s create failed" % snapshotname + log_err_msg = ("Snapshot : %s create failed" % snapshotname) self._raise_or_log_exception(e.err_code, coprhd_err_msg, log_err_msg) @@ -902,10 +963,10 @@ class EMCCoprHDDriverCommon(object): def delete_snapshot(self, snapshot): self.authenticate_user() - vol = snapshot['volume'] + vol = snapshot.volume try: - if vol['consistencygroup_id']: + if vol.group_id: raise coprhd_utils.CoprHdError( coprhd_utils.CoprHdError.SOS_FAILURE_ERR, _("Snapshot delete can't be done individually on a volume" @@ -949,7 +1010,7 @@ class EMCCoprHDDriverCommon(object): coprhd_err_msg = (_("Snapshot %s : Delete Failed\n") % snapshotname) - log_err_msg = "Snapshot : %s delete failed" % snapshotname + log_err_msg = ("Snapshot : %s delete failed" % snapshotname) self._raise_or_log_exception(e.err_code, coprhd_err_msg, log_err_msg) @@ -1170,7 +1231,7 @@ class EMCCoprHDDriverCommon(object): (_("Consistency Group %s not found") % cgid)) def _get_consistencygroup_name(self, consisgrp): - return consisgrp['name'] + return consisgrp.name def _get_coprhd_snapshot_name(self, snapshot, resUri): tagname = self.OPENSTACK_TAG + ":id:" + snapshot['id'] @@ -1200,7 +1261,7 @@ class EMCCoprHDDriverCommon(object): return rslt_snap['name'] def _get_coprhd_volume_name(self, vol, verbose=False): - tagname = self.OPENSTACK_TAG + ":id:" + vol['id'] + tagname = self.OPENSTACK_TAG + ":id:" + vol.id rslt = coprhd_utils.search_by_tag( coprhd_vol.Volume.URI_SEARCH_VOLUMES_BY_TAG.format(tagname), self.configuration.coprhd_hostname, @@ -1210,7 +1271,7 @@ class EMCCoprHDDriverCommon(object): # as "OpenStack:obj_id" # as snapshots will be having the obj_id instead of just id. if len(rslt) == 0: - tagname = self.OPENSTACK_TAG + ":obj_id:" + vol['id'] + tagname = self.OPENSTACK_TAG + ":obj_id:" + vol.id rslt = coprhd_utils.search_by_tag( coprhd_vol.Volume.URI_SEARCH_VOLUMES_BY_TAG.format(tagname), self.configuration.coprhd_hostname, @@ -1228,21 +1289,36 @@ class EMCCoprHDDriverCommon(object): coprhd_utils.CoprHdError.NOT_FOUND_ERR, (_("Volume %s not found") % vol['display_name'])) - def _get_resource_name(self, resource, truncate_name=False): - name = resource.get('display_name', None) - + def _get_resource_name(self, resource, + max_name_cap=MAX_DEFAULT_NAME_LENGTH, + truncate_name=False): + # 36 refers to the length of UUID and +1 for '-' + permitted_name_length = max_name_cap - (36 + 1) + name = resource.display_name if not name: - name = resource['name'] + name = resource.name - if truncate_name and len(name) > 31: + ''' + for scaleio, truncate_name will be true. We make sure the + total name is less than or equal to 31 characters. + _id_to_base64 will return a 24 character name''' + if truncate_name: name = self._id_to_base64(resource.id) + return name - return name + elif len(name) > permitted_name_length: + ''' + The maximum length of resource name in CoprHD is 128. Hence we use + only first 91 characters of the resource name''' + return name[0:permitted_name_length] + "-" + resource.id + + else: + return name + "-" + resource.id def _get_vpool(self, volume): vpool = {} ctxt = context.get_admin_context() - type_id = volume['volume_type_id'] + type_id = volume.volume_type_id if type_id is not None: volume_type = volume_types.get_volume_type(ctxt, type_id) specs = volume_type.get('extra_specs') @@ -1363,7 +1439,8 @@ class EMCCoprHDDriverCommon(object): self.authenticate_user() try: - self.stats['consistencygroup_support'] = 'True' + self.stats['consistencygroup_support'] = True + self.stats['consistent_group_snapshot_enabled'] = True vols = self.volume_obj.list_volumes( self.configuration.coprhd_tenant + "/" + diff --git a/cinder/volume/drivers/coprhd/fc.py b/cinder/volume/drivers/coprhd/fc.py index 031f2517ad5..3018e068566 100644 --- a/cinder/volume/drivers/coprhd/fc.py +++ b/cinder/volume/drivers/coprhd/fc.py @@ -20,9 +20,13 @@ import re from oslo_log import log as logging +from cinder import exception +from cinder.i18n import _ from cinder import interface from cinder.volume import driver from cinder.volume.drivers.coprhd import common as coprhd_common +from cinder.volume import utils as volume_utils + from cinder.zonemanager import utils as fczm_utils LOG = logging.getLogger(__name__) @@ -89,30 +93,67 @@ class EMCCoprHDFCDriver(driver.FibreChannelDriver): pass def remove_export(self, context, volume): - """Driver exntry point to remove an export for a volume.""" + """Driver entry point to remove an export for a volume.""" pass - def create_consistencygroup(self, context, group): - """Creates a consistencygroup.""" - return self.common.create_consistencygroup(context, group) + def create_group(self, context, group): + """Creates a group.""" + if volume_utils.is_group_a_cg_snapshot_type(group): + return self.common.create_consistencygroup(context, group) - def update_consistencygroup(self, context, group, add_volumes=None, - remove_volumes=None): - """Updates volumes in consistency group.""" - return self.common.update_consistencygroup(group, add_volumes, - remove_volumes) + # If the group is not consistency group snapshot enabled, then + # we shall rely on generic volume group implementation + raise NotImplementedError() - def delete_consistencygroup(self, context, group, volumes): - """Deletes a consistency group.""" - return self.common.delete_consistencygroup(context, group, volumes) + def update_group(self, context, group, add_volumes=None, + remove_volumes=None): + """Updates volumes in group.""" + if volume_utils.is_group_a_cg_snapshot_type(group): + return self.common.update_consistencygroup(group, add_volumes, + remove_volumes) - def create_cgsnapshot(self, context, cgsnapshot, snapshots): - """Creates a cgsnapshot.""" - return self.common.create_cgsnapshot(cgsnapshot, snapshots) + # If the group is not consistency group snapshot enabled, then + # we shall rely on generic volume group implementation + raise NotImplementedError() - def delete_cgsnapshot(self, context, cgsnapshot, snapshots): - """Deletes a cgsnapshot.""" - return self.common.delete_cgsnapshot(cgsnapshot, snapshots) + def create_group_from_src(self, ctxt, group, volumes, + group_snapshot=None, snapshots=None, + source_group=None, source_vols=None): + """Creates a group from source.""" + if volume_utils.is_group_a_cg_snapshot_type(group): + message = _("create group from source is not supported " + "for CoprHD if the group type supports " + "consistent group snapshot.") + raise exception.VolumeBackendAPIException(data=message) + else: + raise NotImplementedError() + + def delete_group(self, context, group, volumes): + """Deletes a group.""" + if volume_utils.is_group_a_cg_snapshot_type(group): + return self.common.delete_consistencygroup(context, group, volumes) + + # If the group is not consistency group snapshot enabled, then + # we shall rely on generic volume group implementation + raise NotImplementedError() + + def create_group_snapshot(self, context, group_snapshot, snapshots): + """Creates a group snapshot.""" + if volume_utils.is_group_a_cg_snapshot_type(group_snapshot): + return self.common.create_cgsnapshot(group_snapshot, snapshots) + + # If the group is not consistency group snapshot enabled, then + # we shall rely on generic volume group implementation + raise NotImplementedError() + + def delete_group_snapshot(self, context, group_snapshot, snapshots): + """Deletes a group snapshot.""" + if volume_utils.is_group_a_cg_snapshot_type(group_snapshot): + return self.common.delete_cgsnapshot(group_snapshot, snapshots) + + # If the group is not consistency group snapshot enabled, then + # we shall rely on generic volume group implementation + raise NotImplementedError() def check_for_export(self, context, volume_id): """Make sure volume is exported.""" @@ -123,14 +164,12 @@ class EMCCoprHDFCDriver(driver.FibreChannelDriver): """Initializes the connection and returns connection info.""" properties = {} - properties['volume_id'] = volume['id'] + properties['volume_id'] = volume.id properties['target_discovered'] = False properties['target_wwn'] = [] init_ports = self._build_initport_list(connector) - itls = self.common.initialize_connection(volume, - 'FC', - init_ports, + itls = self.common.initialize_connection(volume, 'FC', init_ports, connector['host']) target_wwns = None @@ -144,7 +183,12 @@ class EMCCoprHDFCDriver(driver.FibreChannelDriver): properties['target_wwn'] = target_wwns properties['initiator_target_map'] = initiator_target_map - auth = volume['provider_auth'] + auth = None + try: + auth = volume.provider_auth + except AttributeError: + pass + if auth: (auth_method, auth_username, auth_secret) = auth.split() properties['auth_method'] = auth_method @@ -162,9 +206,7 @@ class EMCCoprHDFCDriver(driver.FibreChannelDriver): """Driver entry point to detach a volume from an instance.""" init_ports = self._build_initport_list(connector) - itls = self.common.terminate_connection(volume, - 'FC', - init_ports, + itls = self.common.terminate_connection(volume, 'FC', init_ports, connector['host']) volumes_count = self.common.get_exports_count_by_initiators(init_ports) diff --git a/cinder/volume/drivers/coprhd/iscsi.py b/cinder/volume/drivers/coprhd/iscsi.py index 0575b4da0f6..a113f536a08 100644 --- a/cinder/volume/drivers/coprhd/iscsi.py +++ b/cinder/volume/drivers/coprhd/iscsi.py @@ -18,10 +18,12 @@ from oslo_log import log as logging +from cinder import exception +from cinder.i18n import _ from cinder import interface from cinder.volume import driver from cinder.volume.drivers.coprhd import common as coprhd_common - +from cinder.volume import utils as volume_utils LOG = logging.getLogger(__name__) @@ -67,7 +69,7 @@ class EMCCoprHDISCSIDriver(driver.ISCSIDriver): self.common.expand_volume(volume, new_size) def delete_volume(self, volume): - """Deletes an volume.""" + """Deletes a volume.""" self.common.delete_volume(volume) def create_snapshot(self, snapshot): @@ -90,27 +92,65 @@ class EMCCoprHDISCSIDriver(driver.ISCSIDriver): """Driver entry point to remove an export for a volume.""" pass - def create_consistencygroup(self, context, group): - """Creates a consistencygroup.""" - return self.common.create_consistencygroup(context, group) + def create_group(self, context, group): + """Creates a group.""" + if volume_utils.is_group_a_cg_snapshot_type(group): + return self.common.create_consistencygroup(context, group) - def delete_consistencygroup(self, context, group, volumes): - """Deletes a consistency group.""" - return self.common.delete_consistencygroup(context, group, volumes) + # If the group is not consistency group snapshot enabled, then + # we shall rely on generic volume group implementation + raise NotImplementedError() - def update_consistencygroup(self, context, group, - add_volumes=None, remove_volumes=None): - """Updates volumes in consistency group.""" - return self.common.update_consistencygroup(group, add_volumes, - remove_volumes) + def create_group_from_src(self, ctxt, group, volumes, + group_snapshot=None, snapshots=None, + source_group=None, source_vols=None): + """Creates a group from source.""" + if volume_utils.is_group_a_cg_snapshot_type(group): + message = _("create group from source is not supported " + "for CoprHD if the group type supports " + "consistent group snapshot.") + raise exception.VolumeBackendAPIException(data=message) + else: + raise NotImplementedError() - def create_cgsnapshot(self, context, cgsnapshot, snapshots): - """Creates a cgsnapshot.""" - return self.common.create_cgsnapshot(cgsnapshot, snapshots) + def update_group(self, context, group, add_volumes=None, + remove_volumes=None): + """Updates volumes in group.""" + if volume_utils.is_group_a_cg_snapshot_type(group): + return self.common.update_consistencygroup(group, add_volumes, + remove_volumes) - def delete_cgsnapshot(self, context, cgsnapshot, snapshots): - """Deletes a cgsnapshot.""" - return self.common.delete_cgsnapshot(cgsnapshot, snapshots) + # If the group is not consistency group snapshot enabled, then + # we shall rely on generic volume group implementation + raise NotImplementedError() + + def delete_group(self, context, group, volumes): + """Deletes a group.""" + if volume_utils.is_group_a_cg_snapshot_type(group): + return self.common.delete_consistencygroup(context, group, volumes) + + # If the group is not consistency group snapshot enabled, then + # we shall rely on generic volume group implementation + raise NotImplementedError() + + def create_group_snapshot(self, context, group_snapshot, snapshots): + """Creates a group snapshot.""" + if volume_utils.is_group_a_cg_snapshot_type(group_snapshot): + LOG.debug("creating a group snapshot") + return self.common.create_cgsnapshot(group_snapshot, snapshots) + + # If the group is not consistency group snapshot enabled, then + # we shall rely on generic volume group implementation + raise NotImplementedError() + + def delete_group_snapshot(self, context, group_snapshot, snapshots): + """Deletes a group snapshot.""" + if volume_utils.is_group_a_cg_snapshot_type(group_snapshot): + return self.common.delete_cgsnapshot(group_snapshot, snapshots) + + # If the group is not consistency group snapshot enabled, then + # we shall rely on generic volume group implementation + raise NotImplementedError() def check_for_export(self, context, volume_id): """Make sure volume is exported.""" @@ -127,14 +167,20 @@ class EMCCoprHDISCSIDriver(driver.ISCSIDriver): connector['host']) properties = {} properties['target_discovered'] = False - properties['volume_id'] = volume['id'] + properties['volume_id'] = volume.id if itls: properties['target_iqn'] = itls[0]['target']['port'] properties['target_portal'] = '%s:%s' % ( itls[0]['target']['ip_address'], itls[0]['target']['tcp_port']) properties['target_lun'] = itls[0]['hlu'] - auth = volume['provider_auth'] + + auth = None + try: + auth = volume.provider_auth + except AttributeError: + pass + if auth: (auth_method, auth_username, auth_secret) = auth.split() properties['auth_method'] = auth_method diff --git a/cinder/volume/drivers/coprhd/scaleio.py b/cinder/volume/drivers/coprhd/scaleio.py index 23c56e168a9..c4ddc3b3171 100644 --- a/cinder/volume/drivers/coprhd/scaleio.py +++ b/cinder/volume/drivers/coprhd/scaleio.py @@ -29,6 +29,7 @@ from cinder import interface from cinder.volume import configuration from cinder.volume import driver from cinder.volume.drivers.coprhd import common as coprhd_common +from cinder.volume import utils as volume_utils LOG = logging.getLogger(__name__) @@ -92,7 +93,7 @@ class EMCCoprHDScaleIODriver(driver.VolumeDriver): """Creates a Volume.""" self.common.create_volume(volume, self, True) self.common.set_volume_tags(volume, ['_obj_volume_type'], True) - vol_size = self._update_volume_size(int(volume['size'])) + vol_size = self._update_volume_size(int(volume.size)) return {'size': vol_size} def _update_volume_size(self, vol_size): @@ -141,28 +142,68 @@ class EMCCoprHDScaleIODriver(driver.VolumeDriver): """Driver exntry point to remove an export for a volume.""" pass - def create_consistencygroup(self, context, group): - """Creates a consistencygroup.""" - return self.common.create_consistencygroup(context, group, True) + def create_group(self, context, group): + """Creates a group.""" + if volume_utils.is_group_a_cg_snapshot_type(group): + return self.common.create_consistencygroup(context, group, True) - def update_consistencygroup(self, context, group, - add_volumes=None, remove_volumes=None): - """Updates volumes in consistency group.""" - return self.common.update_consistencygroup(group, add_volumes, - remove_volumes) + # If the group is not consistency group snapshot enabled, then + # we shall rely on generic volume group implementation + raise NotImplementedError() - def delete_consistencygroup(self, context, group, volumes): - """Deletes a consistency group.""" - return self.common.delete_consistencygroup(context, group, - volumes, True) + def update_group(self, context, group, add_volumes=None, + remove_volumes=None): + """Updates volumes in group.""" + if volume_utils.is_group_a_cg_snapshot_type(group): + return self.common.update_consistencygroup(group, add_volumes, + remove_volumes) - def create_cgsnapshot(self, context, cgsnapshot, snapshots): - """Creates a cgsnapshot.""" - return self.common.create_cgsnapshot(cgsnapshot, snapshots, True) + # If the group is not consistency group snapshot enabled, then + # we shall rely on generic volume group implementation + raise NotImplementedError() - def delete_cgsnapshot(self, context, cgsnapshot, snapshots): - """Deletes a cgsnapshot.""" - return self.common.delete_cgsnapshot(cgsnapshot, snapshots, True) + def create_group_from_src(self, ctxt, group, volumes, + group_snapshot=None, snapshots=None, + source_group=None, source_vols=None): + """Creates a group from source.""" + if volume_utils.is_group_a_cg_snapshot_type(group): + message = _("create group from source is not supported " + "for CoprHD if the group type supports " + "consistent group snapshot.") + raise exception.VolumeBackendAPIException(data=message) + else: + raise NotImplementedError() + + def delete_group(self, context, group, volumes): + """Deletes a group.""" + if volume_utils.is_group_a_cg_snapshot_type(group): + return self.common.delete_consistencygroup(context, group, + volumes, True) + + # If the group is not consistency group snapshot enabled, then + # we shall rely on generic volume group implementation + raise NotImplementedError() + + def create_group_snapshot(self, context, group_snapshot, snapshots): + """Creates a group snapshot.""" + if volume_utils.is_group_a_cg_snapshot_type(group_snapshot): + LOG.debug("creating a group snapshot") + return self.common.create_cgsnapshot(group_snapshot, snapshots, + True) + + # If the group is not consistency group snapshot enabled, then + # we shall rely on generic volume group implementation + raise NotImplementedError() + + def delete_group_snapshot(self, context, group_snapshot, snapshots): + """Deletes a group snapshot.""" + if volume_utils.is_group_a_cg_snapshot_type(group_snapshot): + return self.common.delete_cgsnapshot(group_snapshot, snapshots, + True) + + # If the group is not consistency group snapshot enabled, then + # we shall rely on generic volume group implementation + raise NotImplementedError() def check_for_export(self, context, volume_id): """Make sure volume is exported.""" @@ -171,11 +212,13 @@ class EMCCoprHDScaleIODriver(driver.VolumeDriver): def initialize_connection(self, volume, connector): """Initializes the connection and returns connection info.""" - volname = self.common._get_resource_name(volume, True) + volname = self.common._get_resource_name(volume, + coprhd_common.MAX_SIO_LEN, + True) properties = {} properties['scaleIO_volname'] = volname - properties['scaleIO_volume_id'] = volume['provider_id'] + properties['scaleIO_volume_id'] = volume.provider_id properties['hostIP'] = connector['ip'] properties[ 'serverIP'] = self.configuration.coprhd_scaleio_rest_gateway_host @@ -215,10 +258,10 @@ class EMCCoprHDScaleIODriver(driver.VolumeDriver): def terminate_connection(self, volume, connector, **kwargs): """Disallow connection from connector.""" - volname = volume['display_name'] + volname = volume.display_name properties = {} properties['scaleIO_volname'] = volname - properties['scaleIO_volume_id'] = volume['provider_id'] + properties['scaleIO_volume_id'] = volume.provider_id properties['hostIP'] = connector['ip'] properties[ 'serverIP'] = self.configuration.coprhd_scaleio_rest_gateway_host diff --git a/releasenotes/notes/coprhd-generic-volume-group-a1d41d439f94ae19.yaml b/releasenotes/notes/coprhd-generic-volume-group-a1d41d439f94ae19.yaml new file mode 100644 index 00000000000..8ba22715585 --- /dev/null +++ b/releasenotes/notes/coprhd-generic-volume-group-a1d41d439f94ae19.yaml @@ -0,0 +1,3 @@ +--- +features: + - Add consistent group capability to generic volume groups in CoprHD driver.