From 2e06995ad5153f5d76ad9ba0f0ca0e2134fea43c Mon Sep 17 00:00:00 2001 From: huyang Date: Thu, 23 Mar 2017 17:52:58 +0800 Subject: [PATCH] Add CG capability to generic groups in Huawei driver This patch adds the consistency group capability to generic group in Huawei driver. Consistency groups will be used as generic groups. DocImpact: Implements: blueprint huawei-generic-group Change-Id: I16937c0ccd9f5a4ffad79f35c49ab32ee6092620 --- .../drivers/huawei/test_huawei_drivers.py | 176 +++++++++--------- cinder/volume/drivers/huawei/huawei_driver.py | 131 +++++++------ cinder/volume/drivers/huawei/huawei_utils.py | 12 ++ cinder/volume/drivers/huawei/hypermetro.py | 5 +- ...huawei-generic-group-bc3fb7236efc58e7.yaml | 3 + 5 files changed, 182 insertions(+), 145 deletions(-) create mode 100644 releasenotes/notes/huawei-generic-group-bc3fb7236efc58e7.yaml diff --git a/cinder/tests/unit/volume/drivers/huawei/test_huawei_drivers.py b/cinder/tests/unit/volume/drivers/huawei/test_huawei_drivers.py index f71a9a81693..b3f0ebdca84 100644 --- a/cinder/tests/unit/volume/drivers/huawei/test_huawei_drivers.py +++ b/cinder/tests/unit/volume/drivers/huawei/test_huawei_drivers.py @@ -25,9 +25,10 @@ from xml.dom import minidom from cinder import context from cinder import exception +from cinder.objects import fields from cinder import test -from cinder.tests.unit.consistencygroup import fake_cgsnapshot -from cinder.tests.unit.consistencygroup import fake_consistencygroup +from cinder.tests.unit import fake_group +from cinder.tests.unit import fake_group_snapshot from cinder.tests.unit import fake_snapshot from cinder.tests.unit import fake_volume from cinder.tests.unit import utils @@ -42,6 +43,7 @@ from cinder.volume.drivers.huawei import replication from cinder.volume.drivers.huawei import rest_client from cinder.volume.drivers.huawei import smartx from cinder.volume import qos_specs +from cinder.volume import utils as volume_utils from cinder.volume import volume_types admin_contex = context.get_admin_context() @@ -382,7 +384,7 @@ FAKE_POOLS_SUPPORT_REPORT = { 'max_over_subscription_ratio': 20.0, 'luncopy': True, 'hypermetro': True, - 'consistencygroup_support': True + 'consistent_group_snapshot_enabled': True } FAKE_LUN_GET_SUCCESS_RESPONSE = """ @@ -2020,6 +2022,15 @@ MAP_COMMAND_TO_FAKE_RESPONSE['/mappingview/associate/portgroup?TYPE=245&ASSOC' REPLICA_BACKEND_ID = 'huawei-replica-1' +def cg_or_cg_snapshot(func): + def wrapper(self, *args, **kwargs): + self.mock_object(volume_utils, + 'is_group_a_cg_snapshot_type', + return_value=True) + return func(self, *args, **kwargs) + return wrapper + + class FakeHuaweiConf(huawei_conf.HuaweiConf): def __init__(self, conf, protocol): self.conf = conf @@ -2241,10 +2252,10 @@ class HuaweiTestBase(test.TestCase): admin_contex, id=ID, provider_location=PROVIDER_LOCATION, name_id=ID) - self.cgsnapshot = fake_cgsnapshot.fake_cgsnapshot_obj( - admin_contex, id=ID, consistencygroup_id=ID, status='available') + self.group_snapshot = fake_group_snapshot.fake_group_snapshot_obj( + admin_contex, id=ID, group_id=ID, status='available') - self.cg = fake_consistencygroup.fake_consistencyobject_obj( + self.group = fake_group.fake_group_obj( admin_contex, id=ID, status='available') def test_encode_name(self): @@ -4286,49 +4297,52 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase): iqn = self.driver.client._get_tgt_iqn_from_rest(ip) self.assertIsNone(iqn) - def test_create_cgsnapshot(self): + @cg_or_cg_snapshot + def test_create_group_snapshot(self): test_snapshots = [self.snapshot] ctxt = context.get_admin_context() - model, snapshots = self.driver.create_cgsnapshot(ctxt, - self.cgsnapshot, - test_snapshots) + model, snapshots = ( + self.driver.create_group_snapshot(ctxt, self.group_snapshot, + test_snapshots)) snapshots_model_update = [{'id': '21ec7341-9256-497b-97d9' '-ef48edcf0635', 'status': 'available', 'provider_location': 11}] self.assertEqual(snapshots_model_update, snapshots) - self.assertEqual('available', model['status']) + self.assertEqual(fields.GroupSnapshotStatus.AVAILABLE, model['status']) - def test_create_cgsnapshot_create_snapshot_fail(self): + @cg_or_cg_snapshot + def test_create_group_snapshot_with_create_snapshot_fail(self): test_snapshots = [self.snapshot] ctxt = context.get_admin_context() self.mock_object(rest_client.RestClient, 'create_snapshot', side_effect=( exception.VolumeBackendAPIException(data='err'))) self.assertRaises(exception.VolumeBackendAPIException, - self.driver.create_cgsnapshot, + self.driver.create_group_snapshot, ctxt, - self.cgsnapshot, + self.group_snapshot, test_snapshots) - def test_create_cgsnapshot_active_snapshot_fail(self): + @cg_or_cg_snapshot + def test_create_group_snapshot_with_active_snapshot_fail(self): test_snapshots = [self.snapshot] ctxt = context.get_admin_context() self.mock_object(rest_client.RestClient, 'activate_snapshot', side_effect=( exception.VolumeBackendAPIException(data='err'))) self.assertRaises(exception.VolumeBackendAPIException, - self.driver.create_cgsnapshot, + self.driver.create_group_snapshot, ctxt, - self.cgsnapshot, + self.group_snapshot, test_snapshots) - def test_delete_cgsnapshot(self): + @cg_or_cg_snapshot + def test_delete_group_snapshot(self): test_snapshots = [self.snapshot] ctxt = context.get_admin_context() - self.driver.delete_cgsnapshot(ctxt, - self.cgsnapshot, - test_snapshots) + self.driver.delete_group_snapshot(ctxt, self.group_snapshot, + test_snapshots) class FCSanLookupService(object): @@ -5109,95 +5123,81 @@ class HuaweiFCDriverTestCase(HuaweiTestBase): self.assertFalse(res) @mock.patch.object(huawei_driver.HuaweiBaseDriver, - '_get_consistencygroup_type', - return_value={"hypermetro": "true"}) - def test_create_hypermetro_consistencygroup_success(self, mock_grouptype): - """Test that create_consistencygroup return successfully.""" + '_get_group_type', + return_value=[{"hypermetro": "true"}]) + @cg_or_cg_snapshot + def test_create_hypermetro_group_success(self, mock_grouptype): + """Test that create_group return successfully.""" ctxt = context.get_admin_context() - # Create consistency group - model_update = self.driver.create_consistencygroup(ctxt, self.cg) + # Create group + model_update = self.driver.create_group(ctxt, self.group) - self.assertEqual('available', + self.assertEqual(fields.GroupStatus.AVAILABLE, model_update['status'], - "Consistency Group created failed") + "Group created failed") @mock.patch.object(huawei_driver.HuaweiBaseDriver, - '_get_consistencygroup_type', - return_value={"hypermetro": "false"}) - def test_create_normal_consistencygroup_success(self, - mock_grouptype): - """Test that create_consistencygroup return successfully.""" + '_get_group_type', + return_value=[{"hypermetro": "false"}]) + @cg_or_cg_snapshot + def test_create_normal_group_success(self, mock_grouptype): + """Test that create_group return successfully.""" ctxt = context.get_admin_context() - # Create consistency group - model_update = self.driver.create_consistencygroup(ctxt, self.cg) + # Create group + model_update = self.driver.create_group(ctxt, self.group) - self.assertEqual('available', + self.assertEqual(fields.GroupStatus.AVAILABLE, model_update['status'], - "Consistency Group created failed") + "Group created failed") @mock.patch.object(huawei_driver.HuaweiBaseDriver, - '_get_consistencygroup_type', - return_value={"hypermetro": "true"}) - def test_delete_hypermetro_consistencygroup_success(self, mock_grouptype): - """Test that create_consistencygroup return successfully.""" + '_get_group_type', + return_value=[{"hypermetro": "true"}]) + @cg_or_cg_snapshot + def test_delete_hypermetro_group_success(self, mock_grouptype): + """Test that delete_group return successfully.""" test_volumes = [self.volume] ctxt = context.get_admin_context() - # Create consistency group - model, volumes = self.driver.delete_consistencygroup(ctxt, - self.cg, - test_volumes) - self.assertEqual('available', + # Delete group + model, volumes = self.driver.delete_group(ctxt, self.group, + test_volumes) + self.assertEqual(fields.GroupStatus.DELETED, model['status'], - "Consistency Group created failed") - - def test_delete_normal_consistencygroup_success(self): - ctxt = context.get_admin_context() - test_volumes = [self.volume] - self.mock_object(huawei_driver.HuaweiBaseDriver, - '_get_consistencygroup_type', - return_value={"hypermetro": "false"}) - - model, volumes = self.driver.delete_consistencygroup(ctxt, - self.cg, - test_volumes) - self.assertEqual('available', - model['status'], - "Consistency Group created failed") + "Group deleted failed") @mock.patch.object(huawei_driver.HuaweiBaseDriver, - '_get_consistencygroup_type', - return_value={"hypermetro": "true"}) + '_get_group_type', + return_value=[{"hypermetro": "false"}]) + @cg_or_cg_snapshot + def test_delete_normal_group_success(self, mock_grouptype): + """Test that delete_group return successfully.""" + ctxt = context.get_admin_context() + test_volumes = [self.volume] + # Delete group + model, volumes = self.driver.delete_group(ctxt, self.group, + test_volumes) + self.assertEqual(fields.GroupStatus.DELETED, + model['status'], + "Group deleted failed") + + @mock.patch.object(huawei_driver.HuaweiBaseDriver, + '_get_group_type', + return_value=[{"hypermetro": "true"}]) @mock.patch.object(huawei_driver.huawei_utils, 'get_volume_metadata', return_value={'hypermetro_id': '3400a30d844d0007', 'remote_lun_id': '59'}) - def test_update_consistencygroup_success(self, - mock_grouptype, - mock_metadata): - """Test that create_consistencygroup return successfully.""" + @cg_or_cg_snapshot + def test_update_group_success(self, mock_grouptype, mock_metadata): + """Test that update_group return successfully.""" ctxt = context.get_admin_context() add_volumes = [self.volume] remove_volumes = [self.volume] - # Create consistency group - model_update = self.driver.update_consistencygroup(ctxt, - self.cg, - add_volumes, - remove_volumes) - self.assertEqual('available', + # Update group + model_update = self.driver.update_group(ctxt, self.group, + add_volumes, remove_volumes) + self.assertEqual(fields.GroupStatus.AVAILABLE, model_update[0]['status'], - "Consistency Group update failed") - - def test_create_hypermetro_consistencygroup_success_2(self): - ctxt = context.get_admin_context() - # Create consistency group - temp_cg = copy.deepcopy(self.cg) - temp_cg['volume_type_id'] = '550c089b-bfdd-4f7f-86e1-3ba88125555c,' - self.mock_object(volume_types, 'get_volume_type', - return_value=test_hypermetro_type) - model_update = self.driver.create_consistencygroup(ctxt, temp_cg) - - self.assertEqual('available', - model_update['status'], - "Consistency Group created failed") + "Group update failed") def test_is_initiator_associated_to_host_raise(self): self.assertRaises(exception.VolumeBackendAPIException, diff --git a/cinder/volume/drivers/huawei/huawei_driver.py b/cinder/volume/drivers/huawei/huawei_driver.py index f1f43fa6f91..73103503da5 100644 --- a/cinder/volume/drivers/huawei/huawei_driver.py +++ b/cinder/volume/drivers/huawei/huawei_driver.py @@ -29,6 +29,7 @@ from cinder import context from cinder import exception from cinder.i18n import _ from cinder import interface +from cinder.objects import fields from cinder import utils from cinder.volume import driver from cinder.volume.drivers.huawei import constants @@ -191,6 +192,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): pool['thick_provisioning_support'] = True pool['thin_provisioning_support'] = True pool['smarttier'] = True + pool['consistent_group_snapshot_enabled'] = True if self.configuration.san_product == "Dorado": pool['smarttier'] = False @@ -198,8 +200,6 @@ class HuaweiBaseDriver(driver.VolumeDriver): if self.metro_flag: pool['hypermetro'] = self.check_func_support("HyperMetroPair") - pool['consistencygroup_support'] = ( - self.check_func_support("HyperMetro_ConsistentGroup")) # Asign the support function to global paramenter. self.support_func = pool @@ -224,17 +224,28 @@ class HuaweiBaseDriver(driver.VolumeDriver): opts = self._get_volume_params_from_specs(specs) return opts - def _get_consistencygroup_type(self, group): - specs = {} - opts = {} - type_id = group.volume_type_id.split(",") - if type_id[0] and len(type_id) == 2: - ctxt = context.get_admin_context() - volume_type = volume_types.get_volume_type(ctxt, type_id[0]) - specs = dict(volume_type).get('extra_specs') - opts = self._get_volume_params_from_specs(specs) + def _get_group_type(self, group): + opts = [] + vol_types = group.volume_types + + for vol_type in vol_types: + specs = vol_type.extra_specs + opts.append(self._get_volume_params_from_specs(specs)) + return opts + def _check_volume_type_support(self, opts, vol_type): + if not opts: + return False + + support = True + for opt in opts: + if opt.get(vol_type) != 'true': + support = False + break + + return support + def _get_volume_params_from_specs(self, specs): """Return the volume parameters from extra specs.""" opts_capability = { @@ -1599,24 +1610,26 @@ class HuaweiBaseDriver(driver.VolumeDriver): self.client.is_host_associated_to_hostgroup(host_id)): self.client.remove_host(host_id) - def create_consistencygroup(self, context, group): - """Creates a consistencygroup.""" - model_update = {'status': 'available'} - opts = self._get_consistencygroup_type(group) - if (opts.get('hypermetro') == 'true'): + @huawei_utils.check_whether_operate_consistency_group + def create_group(self, context, group): + """Creates a group.""" + model_update = {'status': fields.GroupStatus.AVAILABLE} + opts = self._get_group_type(group) + if self._check_volume_type_support(opts, 'hypermetro'): metro = hypermetro.HuaweiHyperMetro(self.client, self.rmt_client, self.configuration) metro.create_consistencygroup(group) return model_update - # Array will create CG at create_cgsnapshot time. Cinder will - # maintain the CG and volumes relationship in the db. + # Array will create group at create_group_snapshot time. Cinder will + # maintain the group and volumes relationship in the db. return model_update - def delete_consistencygroup(self, context, group, volumes): - opts = self._get_consistencygroup_type(group) - if opts.get('hypermetro') == 'true': + @huawei_utils.check_whether_operate_consistency_group + def delete_group(self, context, group, volumes): + opts = self._get_group_type(group) + if self._check_volume_type_support(opts, 'hypermetro'): metro = hypermetro.HuaweiHyperMetro(self.client, self.rmt_client, self.configuration) @@ -1624,7 +1637,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): model_update = {} volumes_model_update = [] - model_update.update({'status': group.status}) + model_update.update({'status': fields.GroupStatus.DELETED}) for volume_ref in volumes: try: @@ -1637,12 +1650,12 @@ class HuaweiBaseDriver(driver.VolumeDriver): return model_update, volumes_model_update - def update_consistencygroup(self, context, group, - add_volumes, - remove_volumes): - model_update = {'status': 'available'} - opts = self._get_consistencygroup_type(group) - if opts.get('hypermetro') == 'true': + @huawei_utils.check_whether_operate_consistency_group + def update_group(self, context, group, + add_volumes=None, remove_volumes=None): + model_update = {'status': fields.GroupStatus.AVAILABLE} + opts = self._get_group_type(group) + if self._check_volume_type_support(opts, 'hypermetro'): metro = hypermetro.HuaweiHyperMetro(self.client, self.rmt_client, self.configuration) @@ -1651,15 +1664,23 @@ class HuaweiBaseDriver(driver.VolumeDriver): remove_volumes) return model_update, None, None - # Array will create CG at create_cgsnapshot time. Cinder will - # maintain the CG and volumes relationship in the db. + # Array will create group at create_group_snapshot time. Cinder will + # maintain the group and volumes relationship in the db. return model_update, None, None - def create_cgsnapshot(self, context, cgsnapshot, snapshots): - """Create cgsnapshot.""" - LOG.info('Create cgsnapshot for consistency group' - ': %(group_id)s', - {'group_id': cgsnapshot.consistencygroup_id}) + @huawei_utils.check_whether_operate_consistency_group + def create_group_from_src(self, context, group, volumes, + group_snapshot=None, snapshots=None, + source_group=None, source_vols=None): + err_msg = _("Huawei Storage doesn't support create_group_from_src.") + LOG.error(err_msg) + raise exception.VolumeBackendAPIException(data=err_msg) + + @huawei_utils.check_whether_operate_consistency_group + def create_group_snapshot(self, context, group_snapshot, snapshots): + """Create group snapshot.""" + LOG.info('Create group snapshot for group' + ': %(group_id)s', {'group_id': group_snapshot.group_id}) model_update = {} snapshots_model_update = [] @@ -1682,48 +1703,50 @@ class HuaweiBaseDriver(driver.VolumeDriver): info = self.client.create_snapshot(lun_id, snapshot_name, snapshot_description) - snapshot_model_update = {'id': snapshot.id, - 'status': 'available', - 'provider_location': info['ID']} - snapshots_model_update.append(snapshot_model_update) + snap_model_update = {'id': snapshot.id, + 'status': fields.SnapshotStatus.AVAILABLE, + 'provider_location': info['ID']} + snapshots_model_update.append(snap_model_update) added_snapshots_info.append(info) except Exception: with excutils.save_and_reraise_exception(): - LOG.error("Create cgsnapshots failed. " - "Cgsnapshot id: %s.", cgsnapshot.id) + LOG.error("Create group snapshots failed. " + "Group snapshot id: %s.", group_snapshot.id) snapshot_ids = [added_snapshot['ID'] for added_snapshot in added_snapshots_info] try: self.client.activate_snapshot(snapshot_ids) except Exception: with excutils.save_and_reraise_exception(): - LOG.error("Active cgsnapshots failed. " - "Cgsnapshot id: %s.", cgsnapshot.id) + LOG.error("Active group snapshots failed. " + "Group snapshot id: %s.", group_snapshot.id) - model_update['status'] = 'available' + model_update['status'] = fields.GroupSnapshotStatus.AVAILABLE return model_update, snapshots_model_update - def delete_cgsnapshot(self, context, cgsnapshot, snapshots): - """Delete consistency group snapshot.""" - LOG.info('Delete cgsnapshot %(snap_id)s for consistency group: ' + @huawei_utils.check_whether_operate_consistency_group + def delete_group_snapshot(self, context, group_snapshot, snapshots): + """Delete group snapshot.""" + LOG.info('Delete group snapshot %(snap_id)s for group: ' '%(group_id)s', - {'snap_id': cgsnapshot.id, - 'group_id': cgsnapshot.consistencygroup_id}) + {'snap_id': group_snapshot.id, + 'group_id': group_snapshot.group_id}) model_update = {} snapshots_model_update = [] - model_update['status'] = cgsnapshot.status + model_update['status'] = fields.GroupSnapshotStatus.DELETED for snapshot in snapshots: try: self.delete_snapshot(snapshot) - snapshots_model_update.append({'id': snapshot.id, - 'status': 'deleted'}) + snapshot_model = {'id': snapshot.id, + 'status': fields.SnapshotStatus.DELETED} + snapshots_model_update.append(snapshot_model) except Exception: with excutils.save_and_reraise_exception(): - LOG.error("Delete cg snapshots failed. " - "Cgsnapshot id: %s", cgsnapshot.id) + LOG.error("Delete group snapshot failed. " + "Group snapshot id: %s", group_snapshot.id) return model_update, snapshots_model_update diff --git a/cinder/volume/drivers/huawei/huawei_utils.py b/cinder/volume/drivers/huawei/huawei_utils.py index 7166a3112d8..012f1e7be13 100644 --- a/cinder/volume/drivers/huawei/huawei_utils.py +++ b/cinder/volume/drivers/huawei/huawei_utils.py @@ -24,6 +24,7 @@ from cinder import exception from cinder.i18n import _ from cinder import objects from cinder.volume.drivers.huawei import constants +from cinder.volume import utils LOG = logging.getLogger(__name__) @@ -112,3 +113,14 @@ def get_snapshot_metadata_value(snapshot): return {item['key']: item['value'] for item in metadata} return {} + + +def check_whether_operate_consistency_group(func): + def wrapper(self, context, group, *args, **kwargs): + if not utils.is_group_a_cg_snapshot_type(group): + msg = _("%s, the group or group snapshot is not cg or " + "cg_snapshot") % func.__name__ + LOG.debug(msg) + raise NotImplementedError(msg) + return func(self, context, group, *args, **kwargs) + return wrapper diff --git a/cinder/volume/drivers/huawei/hypermetro.py b/cinder/volume/drivers/huawei/hypermetro.py index 9608ef53d94..0ccefc84194 100644 --- a/cinder/volume/drivers/huawei/hypermetro.py +++ b/cinder/volume/drivers/huawei/hypermetro.py @@ -18,6 +18,7 @@ from oslo_log import log as logging from cinder import exception from cinder.i18n import _ +from cinder.objects import fields from cinder.volume.drivers.huawei import constants from cinder.volume.drivers.huawei import huawei_utils @@ -286,7 +287,7 @@ class HuaweiHyperMetro(object): {'group': group.id}) model_update = {} volumes_model_update = [] - model_update['status'] = group.status + model_update['status'] = fields.GroupStatus.DELETED metrogroup_id = self.check_consistencygroup_need_to_stop(group) if metrogroup_id: self.client.delete_metrogroup(metrogroup_id) @@ -304,8 +305,6 @@ class HuaweiHyperMetro(object): LOG.info("Update Consistency Group: %(group)s. " "This adds or removes volumes from a CG.", {'group': group.id}) - model_update = {} - model_update['status'] = group.status metrogroup_id = self.check_consistencygroup_need_to_stop(group) if metrogroup_id: # Deal with add volumes to CG diff --git a/releasenotes/notes/huawei-generic-group-bc3fb7236efc58e7.yaml b/releasenotes/notes/huawei-generic-group-bc3fb7236efc58e7.yaml new file mode 100644 index 00000000000..d9c7fc3a771 --- /dev/null +++ b/releasenotes/notes/huawei-generic-group-bc3fb7236efc58e7.yaml @@ -0,0 +1,3 @@ +--- +features: + - Add CG capability to generic volume groups in Huawei driver.