From 822fb701de48d30e662b5f16270b3c38e8703151 Mon Sep 17 00:00:00 2001 From: "yixuan.zhang" Date: Mon, 21 May 2018 14:27:39 +0800 Subject: [PATCH] Storwize: add data reduction pool support Data reduction pool is a new style pool on Storwize/SVC storage. Thin provisioned/compressed vdisk copies created in a data_reduction pool are quite different from regular pool. This patch adds thin-provision and compressed volumes support on data reduction pool. Change-Id: Icb09cbacc3cfe63017d17847799c0904e06cf8a7 Implements: blueprint svc-drpool-support --- .../volume/drivers/ibm/test_storwize_svc.py | 379 +++++++++++++++++- .../drivers/ibm/storwize_svc/replication.py | 2 +- .../ibm/storwize_svc/storwize_svc_common.py | 309 +++++++++++--- ...wize-dr-pool-support-52db3a95e54aef88.yaml | 5 + 4 files changed, 631 insertions(+), 64 deletions(-) create mode 100644 releasenotes/notes/storwize-dr-pool-support-52db3a95e54aef88.yaml diff --git a/cinder/tests/unit/volume/drivers/ibm/test_storwize_svc.py b/cinder/tests/unit/volume/drivers/ibm/test_storwize_svc.py index 783a53dec64..4b634ecabbb 100644 --- a/cinder/tests/unit/volume/drivers/ibm/test_storwize_svc.py +++ b/cinder/tests/unit/volume/drivers/ibm/test_storwize_svc.py @@ -538,7 +538,8 @@ class StorwizeSVCManagementSimulator(object): 'vdisk_count', 'capacity', 'extent_size', 'free_capacity', 'virtual_capacity', 'used_capacity', 'real_capacity', 'overallocation', 'warning', - 'easy_tier', 'easy_tier_status', 'site_id']) + 'easy_tier', 'easy_tier_status', 'site_id', + 'data_reduction']) for i in range(pool_num): row_data = [str(i + 1), self._flags['storwize_svc_volpool_name'][i], 'online', @@ -546,24 +547,32 @@ class StorwizeSVCManagementSimulator(object): '3573412790272', '256', '3529926246400', '1693247906775', '26843545600', '38203734097', '47', '80', 'auto', - 'inactive', ''] + 'inactive', '', 'no'] rows.append(row_data) rows.append([str(pool_num + 1), 'openstack2', 'online', '1', '0', '3573412790272', '256', '3529432325160', '1693247906775', '26843545600', - '38203734097', '47', '80', 'auto', 'inactive', '']) + '38203734097', '47', '80', 'auto', 'inactive', '', 'no']) rows.append([str(pool_num + 2), 'openstack3', 'offline', '1', '0', '3573412790272', '128', '3529432325160', '1693247906775', '26843545600', - '38203734097', '47', '80', 'auto', 'inactive', '']) + '38203734097', '47', '80', 'auto', 'inactive', '', 'yes']) rows.append([str(pool_num + 3), 'hyperswap1', 'online', '1', '0', '3573412790272', '256', '3529432325160', '1693247906775', '26843545600', - '38203734097', '47', '80', 'auto', 'inactive', '1']) + '38203734097', '47', '80', 'auto', 'inactive', '1', 'no']) rows.append([str(pool_num + 4), 'hyperswap2', 'online', '1', '0', '3573412790272', '128', '3529432325160', '1693247906775', '26843545600', - '38203734097', '47', '80', 'auto', 'inactive', '2']) + '38203734097', '47', '80', 'auto', 'inactive', '2', 'no']) + rows.append([str(pool_num + 5), 'dr_pool1', 'online', + '1', '0', '3573412790272', '128', '3529432325160', + '1693247906775', '26843545600', '38203734097', '47', '80', + 'auto', 'inactive', '1', 'yes']) + rows.append([str(pool_num + 6), 'dr_pool2', 'online', + '1', '0', '3573412790272', '128', '3529432325160', + '1693247906775', '26843545600', '38203734097', '47', '80', + 'auto', 'inactive', '2', 'yes']) if 'obj' not in kwargs: return self._print_info_cmd(rows=rows, **kwargs) else: @@ -577,12 +586,16 @@ class StorwizeSVCManagementSimulator(object): row = each_row break elif pool_name == 'openstack2': - row = rows[-4] + row = rows[-6] elif pool_name == 'openstack3': - row = rows[-3] + row = rows[-5] elif pool_name == 'hyperswap1': - row = rows[-2] + row = rows[-4] elif pool_name == 'hyperswap2': + row = rows[-3] + elif pool_name == 'dr_pool1': + row = rows[-2] + elif pool_name == 'dr_pool2': row = rows[-1] else: return self._errors['CMMVC5754E'] @@ -961,7 +974,8 @@ port_speed!N/A 'primary': 'yes', 'mdisk_grp_id': str(mdiskgrp_id), 'mdisk_grp_name': mdiskgrp, - 'easy_tier': volume_info['easy_tier'], + 'easy_tier': (volume_info[ + 'easy_tier'] if 'easy_tier' in volume_info else 'on'), 'compressed_copy': volume_info['compressed_copy']} volume_info['copies'] = {'0': vol_cp} if is_mirror_vol: @@ -971,7 +985,8 @@ port_speed!N/A 'primary': 'no', 'mdisk_grp_id': str(sec_pool_id), 'mdisk_grp_name': sec_pool, - 'easy_tier': volume_info['easy_tier'], + 'easy_tier': (volume_info['easy_tier'] + if 'easy_tier' in volume_info else 'on'), 'compressed_copy': volume_info['compressed_copy']} volume_info['copies']['1'] = vol_cp1 @@ -2716,7 +2731,7 @@ port_speed!N/A site_volume_info['RC_name'] = '' site_volume_info['RC_id'] = '' - if 'buffersize' in kwargs: + if 'thin' in kwargs or 'compressed' in kwargs: site_volume_info['formatted'] = 'no' # Fake numbers site_volume_info['used_capacity'] = '786432' @@ -4816,6 +4831,15 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase): vol = testutils.create_volume(self.ctxt, **prop) return vol + def _generate_vol_info_on_dr_pool(self, vol_type=None, size=10): + pool = 'dr_pool1' + prop = {'size': size, + 'host': 'openstack@svc#%s' % pool} + if vol_type: + prop['volume_type_id'] = vol_type.id + vol = testutils.create_volume(self.ctxt, **prop) + return vol + def _generate_snap_info(self, vol_id, size=10): prop = {'volume_id': vol_id, 'volume_size': size} @@ -5853,6 +5877,24 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase): 'failed') self.driver.delete_volume(volume) + # retype a volume in dr_pool + loc = ('StorwizeSVCDriver:' + self.driver._state['system_id'] + + ':openstack3') + cap = {'location_info': loc, 'extent_size': '128'} + self.driver._stats = {'location_info': loc} + host = {'host': 'openstack@svc#openstack3', 'capabilities': cap} + volume = testutils.create_volume( + self.ctxt, volume_type_id=old_type.id, + host='openstack@svc#hyperswap3') + volume['host'] = host['host'] + new_type = objects.VolumeType.get_by_id(ctxt, + new_type_ref['id']) + + self.driver.create_volume(volume) + self.assertRaises(exception.VolumeDriverException, + self.driver.retype, ctxt, volume, + new_type, diff, host) + @mock.patch.object(storwize_svc_common.StorwizeHelpers, 'disable_vdisk_qos') @mock.patch.object(storwize_svc_common.StorwizeHelpers, @@ -7164,7 +7206,7 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase): easytier_type = self._create_volume_type(spec, 'easytier_type') vol = self._generate_vol_info(easytier_type) - self.assertRaises(exception.InvalidInput, + self.assertRaises(exception.VolumeDriverException, self.driver.create_volume, vol) # create hyperswap volume without peer_pool @@ -7553,6 +7595,7 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase): # retype from hyperswap volume to replication volume spec3 = {'replication_enabled': ' True', 'replication_type': ' metro'} + self.driver._replica_target['pool_name'] = 'openstack2' replication_type = self._create_volume_type(spec3, 'test_replication_type') diff, _equal = volume_types.volume_types_diff( @@ -7740,6 +7783,239 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase): add_volumes_update) self.assertEqual([], remove_volumes_update) + @ddt.data({'spec': {'rsize': -1}}, + {'spec': {'mirror_pool': 'dr_pool2'}}, + {'spec': {'drivers:volume_topology': 'hyperswap', + 'peer_pool': 'dr_pool2'}}) + @ddt.unpack + def test_storwize_volumes_on_dr_pool_success_case(self, spec): + with mock.patch.object(storwize_svc_common.StorwizeHelpers, + 'get_system_info') as get_system_info: + fake_system_info = {'code_level': (7, 7, 0, 0), + 'topology': 'hyperswap', + 'system_name': 'storwize-svc-sim', + 'system_id': '0123456789ABCDEF'} + get_system_info.return_value = fake_system_info + self.driver.do_setup(None) + + dr_type = self._create_volume_type(spec, 'type_dr') + vol = testutils.create_volume(self.ctxt, volume_type_id=dr_type.id, + host='openstack@svc#hyperswap1') + self.driver.create_volume(vol) + + vol2 = testutils.create_volume(self.ctxt, volume_type_id=dr_type.id, + host='openstack@svc#hyperswap1') + ref = {'source-name': vol.name} + self.driver.manage_existing(vol2, ref) + + @ddt.data({'spec': {'warning': 30}}, + {'spec': {'rsize': 5}}, + {'spec': {'easytier': False}}, + {'spec': {'autoexpand': False}}, + {'spec': {'grainsize': 128}}) + @ddt.unpack + def test_storwize_create_thin_volume_on_dr_pool_failure_case(self, spec): + # create basic thin volume on dr_pool + with mock.patch.object(storwize_svc_common.StorwizeHelpers, + 'get_system_info') as get_system_info: + fake_system_info = {'code_level': (7, 7, 0, 0), + 'topology': 'hyperswap', + 'system_name': 'storwize-svc-sim', + 'system_id': '0123456789ABCDEF'} + get_system_info.return_value = fake_system_info + self.driver.do_setup(None) + + thin_dr_type = self._create_volume_type(spec, 'type_thin') + vol = self._generate_vol_info_on_dr_pool(thin_dr_type) + self.assertRaises(exception.VolumeDriverException, + self.driver.create_volume, vol) + + # create mirror volume on dr_pool + self._set_flag('storwize_svc_mirror_pool', 'dr_pool1') + mirror_dr_type = self._create_volume_type(spec, 'type_mirror') + vol = self._generate_vol_info(mirror_dr_type) + self.assertRaises(exception.VolumeDriverException, + self.driver.create_volume, vol) + self._reset_flags() + + # create hyperswap volume on dr_pool + spec.update({'drivers:volume_topology': 'hyperswap', + 'peer_pool': 'dr_pool2'}) + hyper_dr_type = self._create_volume_type(spec, 'hyper_dr_type') + self.assertRaises(exception.VolumeDriverException, + self._create_hyperswap_volume, hyper_dr_type) + + @ddt.data({'spec': {'warning': 30}}, + {'spec': {'rsize': 5}}, + {'spec': {'easytier': False}}, + {'spec': {'autoexpand': False}}, + {'spec': {'grainsize': 128}}) + @ddt.unpack + def test_storwize_manage_volume_on_dr_pool_failure_case(self, spec): + with mock.patch.object(storwize_svc_common.StorwizeHelpers, + 'get_system_info') as get_system_info: + fake_system_info = {'code_level': (7, 7, 0, 0), + 'topology': 'hyperswap', + 'system_name': 'storwize-svc-sim', + 'system_id': '0123456789ABCDEF'} + get_system_info.return_value = fake_system_info + self.driver.do_setup(None) + + extra_spec = {} + thin_type = self._create_volume_type(extra_spec, 'thin_type') + vol_type1 = self._create_volume_type(spec, 'vol_type1') + thin_volume = self._generate_vol_info_on_dr_pool(thin_type) + self.driver.create_volume(thin_volume) + vol1 = self._generate_vol_info_on_dr_pool(vol_type1) + ref1 = {'source-name': thin_volume.name} + self.assertRaises(exception.ManageExistingVolumeTypeMismatch, + self.driver.manage_existing, vol1, ref1) + + extra_spec = {'mirror_pool': 'dr_pool1'} + mirror_type = self._create_volume_type(extra_spec, 'type_mirror') + mirror_volume = self._generate_vol_info(mirror_type) + self.driver.create_volume(mirror_volume) + spec.update({'mirror_pool': 'dr_pool1'}) + vol_type2 = self._create_volume_type(spec, 'vol_type2') + vol2 = self._generate_vol_info(vol_type2) + ref2 = {'source-name': mirror_volume.name} + self.assertRaises(exception.ManageExistingVolumeTypeMismatch, + self.driver.manage_existing, vol2, ref2) + spec.pop('mirror_pool') + + extra_spec = {'drivers:volume_topology': 'hyperswap', + 'peer_pool': 'dr_pool2'} + hyper_type = self._create_volume_type(extra_spec, 'type_hyper') + hyper_volume = testutils.create_volume( + self.ctxt, volume_type_id=hyper_type.id, + host='openstack@svc#hyperswap1') + self.driver.create_volume(hyper_volume) + spec.update(extra_spec) + vol_type3 = self._create_volume_type(spec, 'vol_type3') + vol3 = testutils.create_volume( + self.ctxt, volume_type_id=vol_type3.id, + host='openstack@svc#hyperswap1') + ref3 = {'source-name': hyper_volume.name} + self.assertRaises(exception.ManageExistingVolumeTypeMismatch, + self.driver.manage_existing, vol3, ref3) + + def test_storwize_migrate_volume_between_regular_dr_pool(self): + spec = {'mirror_pool': 'openstack1'} + mirror_vol_type = self._create_volume_type(spec, 'test_mirror_type') + vol = self._generate_vol_info(mirror_vol_type) + self.driver.create_volume(vol) + loc = ('StorwizeSVCDriver:' + self.driver._state['system_id'] + + ':dr_pool2') + cap = {'location_info': loc, 'extent_size': '256'} + host = {'host': 'openstack@svc#dr_pool2', 'capabilities': cap} + ctxt = context.get_admin_context() + self.assertRaises(exception.VolumeDriverException, + self.driver.migrate_volume, ctxt, vol, host) + + vol2 = self._generate_vol_info_on_dr_pool(mirror_vol_type) + self.driver.create_volume(vol2) + self.assertRaises(exception.VolumeDriverException, + self.driver.migrate_volume, ctxt, vol2, host) + + spec = {'mirror_pool': 'dr_pool1'} + mirror_vol_type1 = self._create_volume_type(spec, 'test_mirror_type1') + vol3 = self._generate_vol_info(mirror_vol_type1) + self.driver.create_volume(vol3) + self.assertRaises(exception.VolumeDriverException, + self.driver.migrate_volume, ctxt, vol3, host) + + spec.update({'rsize': -1}) + thick_vol_type = self._create_volume_type(spec, 'thick_mirror_type') + vol3 = self._generate_vol_info_on_dr_pool(thick_vol_type) + self.driver.create_volume(vol3) + self.driver.migrate_volume(ctxt, vol3, host) + + vol4 = self._create_volume() + self.driver.migrate_volume(ctxt, vol4, host) + + spec = {'rsize': '10'} + rsize_type = self._create_volume_type(spec, 'rsize_type') + vol5 = self._generate_vol_info(rsize_type) + self.driver.create_volume(vol5) + self.assertRaises(exception.VolumeDriverException, + self.driver.migrate_volume, ctxt, vol5, host) + + @ddt.data(({}, {'easytier': True, 'warning': 5, 'autoexpand': False}), + ({}, {'grainsize': 128}), + ({'mirror_pool': 'dr_pool2'}, {'mirror_pool': 'hyperswap1'})) + @ddt.unpack + def test_storwize_svc_retype_old_type_dr_pool(self, key_specs_old, + key_specs_new): + self.driver.do_setup(None) + loc = ('StorwizeSVCDriver:' + self.driver._state['system_id'] + + ':dr_pool1') + cap = {'location_info': loc, 'extent_size': '128'} + self.driver._stats = {'location_info': loc} + host = {'host': 'openstack@svc#dr_pool1', 'capabilities': cap} + ctxt = context.get_admin_context() + + old_type_ref = volume_types.create(ctxt, 'old', key_specs_old) + new_type_ref = volume_types.create(ctxt, 'new', key_specs_new) + + diff, _equal = volume_types.volume_types_diff(ctxt, old_type_ref['id'], + new_type_ref['id']) + + old_type = objects.VolumeType.get_by_id(ctxt, + old_type_ref['id']) + + volume = self._generate_vol_info_on_dr_pool(old_type) + volume['host'] = host['host'] + new_type = objects.VolumeType.get_by_id(ctxt, + new_type_ref['id']) + + self.driver.create_volume(volume) + self.assertRaises(exception.VolumeDriverException, + self.driver.retype, ctxt, volume, + new_type, diff, host) + + @ddt.data(({}, {'mirror_pool': 'dr_pool2', 'warning': 5}), + ({'mirror_pool': 'openstack2'}, {'mirror_pool': 'dr_pool2'}), + ({'mirror_pool': 'dr_pool2'}, {'mirror_pool': 'hyperswap1'}), + ({'autoexpand': False}, {'drivers:volume_topology': 'hyperswap', + 'peer_pool': 'dr_pool2', + 'autoexpand': False})) + @ddt.unpack + def test_storwize_svc_retype_new_type_dr_pool(self, key_specs_old, + key_specs_new): + with mock.patch.object(storwize_svc_common.StorwizeHelpers, + 'get_system_info') as get_system_info: + fake_system_info = {'code_level': (7, 7, 0, 0), + 'topology': 'hyperswap', + 'system_name': 'storwize-svc-sim', + 'system_id': '0123456789ABCDEF'} + get_system_info.return_value = fake_system_info + self.driver.do_setup(None) + loc = ('StorwizeSVCDriver:' + self.driver._state['system_id'] + + ':openstack') + cap = {'location_info': loc, 'extent_size': '128'} + self.driver._stats = {'location_info': loc} + host = {'host': 'openstack@svc#openstack', 'capabilities': cap} + ctxt = context.get_admin_context() + + old_type_ref = volume_types.create(ctxt, 'old', key_specs_old) + new_type_ref = volume_types.create(ctxt, 'new', key_specs_new) + + diff, _equal = volume_types.volume_types_diff(ctxt, old_type_ref['id'], + new_type_ref['id']) + + old_type = objects.VolumeType.get_by_id(ctxt, + old_type_ref['id']) + + volume = self._generate_vol_info(old_type) + volume['host'] = host['host'] + new_type = objects.VolumeType.get_by_id(ctxt, + new_type_ref['id']) + + self.driver.create_volume(volume) + self.assertRaises(exception.VolumeDriverException, + self.driver.retype, ctxt, volume, + new_type, diff, host) + class CLIResponseTestCase(test.TestCase): def test_empty(self): @@ -8283,6 +8559,83 @@ class StorwizeSVCReplicationTestCase(test.TestCase): self._create_test_volume, self.gmcv_with_cps86401_type) + @ddt.data(({"backend_id": "svc_aux_target_1", + "san_ip": "192.168.10.22", + "san_login": "admin", + "san_password": "admin", + "pool_name": "openstack"}, 'openstack@svc#dr_pool1'), + ({"backend_id": "svc_aux_target_1", + "san_ip": "192.168.10.22", + "san_login": "admin", + "san_password": "admin", + "pool_name": "dr_pool1"}, 'openstack@svc#openstack')) + @ddt.unpack + def test_storwize_replication_volume_with_dr_pools(self, target, vol_host): + # Set replication target + self.driver.configuration.set_override('replication_device', + [target]) + + self.driver.do_setup(self.ctxt) + + # Create metro mirror replication volume on dr_pool. + volume = testutils.create_volume( + self.ctxt, volume_type_id=self.mm_type.id, + host=vol_host) + model_update = self.driver.create_volume(volume) + self.assertEqual(fields.ReplicationStatus.ENABLED, + model_update['replication_status']) + volume1 = testutils.create_volume( + self.ctxt, volume_type_id=self.mm_type.id, + host=vol_host) + ref = {'source-name': volume.name} + self.driver.manage_existing(volume1, ref) + + spec = {'replication_enabled': ' True', + 'replication_type': ' metro', + 'easytier': 'False'} + type_ref = volume_types.create(self.ctxt, 'type_dr', spec) + dr_type = objects.VolumeType.get_by_id(self.ctxt, type_ref['id']) + volume2 = testutils.create_volume( + self.ctxt, volume_type_id=dr_type.id, + host=vol_host) + self.assertRaises(exception.VolumeDriverException, + self.driver.create_volume, volume2) + + volume3 = testutils.create_volume( + self.ctxt, volume_type_id=self.mm_type.id, + host=vol_host) + model_update = self.driver.create_volume(volume3) + ref2 = {'source-name': volume3.name} + self.assertRaises(exception.ManageExistingVolumeTypeMismatch, + self.driver.manage_existing, volume2, ref2) + + volume4 = testutils.create_volume( + self.ctxt, volume_type_id=self.non_replica_type.id, + host=vol_host) + self.driver.create_volume(volume4) + # Retype to mm replica + host = {'host': vol_host} + diff, _equal = volume_types.volume_types_diff( + self.ctxt, self.non_replica_type['id'], self.mm_type['id']) + retyped, model_update = self.driver.retype( + self.ctxt, volume4, self.mm_type, diff, host) + volume4['volume_type_id'] = self.mm_type['id'] + volume4['volume_type'] = self.mm_type + self.assertEqual(fields.ReplicationStatus.ENABLED, + model_update['replication_status']) + self._validate_replic_vol_creation(volume4) + + volume5 = testutils.create_volume( + self.ctxt, volume_type_id=self.non_replica_type.id, + host=vol_host) + self.driver.create_volume(volume5) + # retype with check dr_pool params failure + diff, _equal = volume_types.volume_types_diff( + self.ctxt, self.non_replica_type['id'], dr_type['id']) + self.assertRaises(exception.VolumeDriverException, + self.driver.retype, self.ctxt, volume5, + dr_type, diff, host) + def _validate_replic_vol_creation(self, volume, isGMCV=False): self._assert_vol_exists(volume['name'], True) self._assert_vol_exists( diff --git a/cinder/volume/drivers/ibm/storwize_svc/replication.py b/cinder/volume/drivers/ibm/storwize_svc/replication.py index 561e55630b2..b15ba18964d 100644 --- a/cinder/volume/drivers/ibm/storwize_svc/replication.py +++ b/cinder/volume/drivers/ibm/storwize_svc/replication.py @@ -195,7 +195,7 @@ class StorwizeSVCReplicationGMCV(StorwizeSVCReplicationGlobalMirror): self.driver._helpers.create_vdisk(source_change_vol_name, six.text_type(vref['size']), 'gb', - src_attr['mdisk_grp_id'], + src_attr['mdisk_grp_name'], src_change_opts) # Create target volume if it doesn't exist target_attr = self.target_helpers.get_vdisk_attributes( diff --git a/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_common.py b/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_common.py index 0a9b22d6870..6bc67815537 100644 --- a/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_common.py +++ b/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_common.py @@ -78,7 +78,7 @@ storwize_svc_opts = [ cfg.IntOpt('storwize_svc_vol_grainsize', default=256, help='Storage system grain size parameter for volumes ' - '(32/64/128/256)'), + '(8/32/64/128/256)'), cfg.BoolOpt('storwize_svc_vol_compression', default=False, help='Storage system compression option for volumes'), @@ -802,6 +802,14 @@ class StorwizeHelpers(object): attrs = self.get_pool_attrs(pool_name) return attrs is not None + def is_data_reduction_pool(self, pool_name): + """Check if pool is data reduction pool.""" + pool_data = self.get_pool_attrs(pool_name) + if (pool_data and 'data_reduction' in pool_data and + pool_data['data_reduction'] == 'yes'): + return True + return False + def get_available_io_groups(self): """Return list of available IO groups.""" iogrps = [] @@ -1286,7 +1294,7 @@ class StorwizeHelpers(object): @staticmethod def check_vdisk_opts(state, opts): # Check that grainsize is 32/64/128/256 - if opts['grainsize'] not in [32, 64, 128, 256]: + if opts['grainsize'] not in [8, 32, 64, 128, 256]: raise exception.InvalidInput( reason=_('Illegal value specified for ' 'storwize_svc_vol_grainsize: set to either ' @@ -1481,29 +1489,110 @@ class StorwizeHelpers(object): self.check_vdisk_opts(state, opts) return opts + def check_data_reduction_pool_params(self, opts): + """Check the configured parameters if vol in data reduction pool.""" + if opts['warning'] != 0: + msg = (_('You cannot specify -warning for thin-provisioned or ' + 'compressed volumes that are in data reduction ' + 'pools. The configured warning is ' + '%s.') % opts['warning']) + raise exception.VolumeDriverException(message=msg) + if not opts['easytier']: + msg = (_('You cannot specify -easytier for thin-provisioned ' + 'or compressed volumes that are in data reduction ' + 'pools. The configured easytier is ' + '%s') % opts['easytier']) + raise exception.VolumeDriverException(message=msg) + if opts['grainsize'] != 256 and opts['grainsize'] != 8: + msg = (_('You cannot specify -grainsize for thin-provisioned ' + 'or compressed volumes that are in data reduction ' + 'pools. This type of volume will be created with a ' + 'grainsize of 8 KB. The configured grainsize is ' + '%s.') % opts['grainsize']) + raise exception.VolumeDriverException(message=msg) + if opts['rsize'] != 2: + if opts['volume_topology'] == 'hyperswap': + msg = (_('You cannot specify -buffersize for Hyperswap volumes' + ' that are in data reduction pools, The configured ' + 'buffersize is %s.') % opts['rsize']) + raise exception.VolumeDriverException(message=msg) + else: + msg = (_('You cannot specify -rsize for thin-provisioned ' + 'or compressed volumes that are in data reduction ' + 'pools. The -rsize parameter will be ignored in ' + 'mkvdisk. Only its presence or absence is used to ' + 'determine if the disk is a data reduction volume ' + 'copy or a thick volume copy. The ' + 'configured rsize is %s.') % opts['rsize']) + raise exception.VolumeDriverException(message=msg) + if not opts['autoexpand']: + msg = (_('You cannot set the autoexpand to disable for ' + 'thin-provisioned or compressed volumes that are in data ' + 'reduction pool. The configured' + ' autoexpand is %s.') % opts['autoexpand']) + raise exception.VolumeDriverException(message=msg) + else: + LOG.info('You cannot specify warning, grainsize and ' + 'easytier for thin-provisioned or compressed' + ' volumes that are in data reduction pools. ' + 'The rsize parameter will be ignored, the ' + 'autoexpand must be enabled.') + + def is_volume_type_dr_pools(self, pool, opts, rep_type=None, + rep_target_pool=None): + """Check every configured pools is data reduction pool.""" + if self.is_data_reduction_pool(pool): + LOG.debug('The configured pool %s is a data reduction pool.', pool) + return True + + if opts['mirror_pool'] and self.is_data_reduction_pool( + opts['mirror_pool']): + LOG.debug('The mirror_pool %s is a data reduction pool.', + opts['mirror_pool']) + return True + + if (opts['volume_topology'] == 'hyperswap' and + self.is_data_reduction_pool(opts['peer_pool'])): + LOG.debug('The peer_pool %s is a data reduction pool.', + opts['peer_pool']) + return True + + if rep_type and self.is_data_reduction_pool(rep_target_pool): + LOG.debug('The replica target pool %s is a data reduction pool.', + rep_target_pool) + return True + + return False + @staticmethod - def _get_vdisk_create_params(opts, add_copies=False): + def _get_vdisk_create_params(opts, is_dr_pool, add_copies=False): easytier = 'on' if opts['easytier'] else 'off' if opts['rsize'] == -1: params = [] if opts['nofmtdisk']: params.append('-nofmtdisk') else: - params = ['-rsize', '%s%%' % str(opts['rsize']), - '-autoexpand', '-warning', - '%s%%' % str(opts['warning'])] - if not opts['autoexpand']: - params.remove('-autoexpand') - - if opts['compression']: - params.append('-compressed') + if is_dr_pool: + params = ['-rsize', '%s%%' % str(opts['rsize']), '-autoexpand'] + if opts['compression']: + params.append('-compressed') else: - params.extend(['-grainsize', str(opts['grainsize'])]) + params = ['-rsize', '%s%%' % str(opts['rsize']), + '-autoexpand', '-warning', + '%s%%' % str(opts['warning'])] + if not opts['autoexpand']: + params.remove('-autoexpand') + + if opts['compression']: + params.append('-compressed') + else: + params.extend(['-grainsize', str(opts['grainsize'])]) if add_copies and opts['mirror_pool']: params.extend(['-copies', '2']) - params.extend(['-easytier', easytier]) + if not is_dr_pool: + params.extend(['-easytier', easytier]) return params def create_vdisk(self, name, size, units, pool, opts): @@ -1517,19 +1606,31 @@ class StorwizeHelpers(object): # The syntax of pool SVC expects is pool:mirror_pool in # mdiskgrp for mirror volume mdiskgrp = '%s:%s' % (pool, opts['mirror_pool']) + + is_dr_pool = False + if opts['rsize'] != -1: + is_dr_pool = self.is_volume_type_dr_pools(pool, opts) + if is_dr_pool: + self.check_data_reduction_pool_params(opts) params = self._get_vdisk_create_params( - opts, add_copies=True if opts['mirror_pool'] else False) + opts, is_dr_pool, + add_copies=True if opts['mirror_pool'] else False) self.ssh.mkvdisk(name, size, units, mdiskgrp, opts, params) LOG.debug('Leave: _create_vdisk: volume %s.', name) - def _get_hyperswap_volume_create_params(self, opts): + def _get_hyperswap_volume_create_params(self, opts, is_dr_pool): # Storwize/svc use cli command mkvolume to create hyperswap volume. # You must specify -thin with grainsize. # You must specify either -thin or -compressed with warning. params = [] LOG.debug('The I/O groups of a hyperswap volume will be selected by ' 'storage.') - if opts['rsize'] != -1: + if is_dr_pool: + if opts['compression']: + params.append('-compressed') + else: + params.append('-thin') + else: params.extend(['-buffersize', '%s%%' % str(opts['rsize']), '-warning', '%s%%' % six.text_type(opts['warning'])]) @@ -1544,16 +1645,23 @@ class StorwizeHelpers(object): def create_hyperswap_volume(self, vol_name, size, units, pool, opts): vol_name = '"%s"' % vol_name - params = self._get_hyperswap_volume_create_params(opts) - self.ssh.mkvolume(vol_name, six.text_type(size), units, pool, params) + params = [] + if opts['rsize'] != -1: + is_dr_pool = self.is_volume_type_dr_pools(pool, opts) + if is_dr_pool: + self.check_data_reduction_pool_params(opts) + params = self._get_hyperswap_volume_create_params(opts, is_dr_pool) + hyperpool = '%s:%s' % (pool, opts['peer_pool']) + self.ssh.mkvolume(vol_name, six.text_type(size), units, + hyperpool, params) def convert_volume_to_hyperswap(self, vol_name, opts, state): vol_name = '%s' % vol_name if not self.is_system_topology_hyperswap(state): - reason = _('Convert volume to hyperswap failed, the system is ' - 'below release 7.6.0.0 or it is not hyperswap ' - 'topology.') - raise exception.VolumeDriverException(reason=reason) + msg = _('Convert volume to hyperswap failed, the system is ' + 'below release 7.6.0.0 or it is not hyperswap ' + 'topology.') + raise exception.VolumeDriverException(message=msg) else: attr = self.get_vdisk_attributes(vol_name) if attr is None: @@ -1564,7 +1672,10 @@ class StorwizeHelpers(object): pool = attr['mdisk_grp_name'] self.check_hyperswap_pool(pool, opts['peer_pool']) hyper_pool = '%s' % opts['peer_pool'] - params = self._get_hyperswap_volume_create_params(opts) + is_dr_pool = self.is_volume_type_dr_pools(pool, opts) + if is_dr_pool and opts['rsize'] != -1: + self.check_data_reduction_pool_params(opts) + params = self._get_hyperswap_volume_create_params(opts, is_dr_pool) self.ssh.addvolumecopy(vol_name, hyper_pool, params) def convert_hyperswap_volume_to_normal(self, vol_name, peer_pool): @@ -2213,7 +2324,10 @@ class StorwizeHelpers(object): else: opts = self.get_vdisk_params(config, state, volume_type['id'], volume_type=volume_type) - params = self._get_vdisk_create_params(opts) + is_dr_pool = self.is_data_reduction_pool(dest_pool) + if is_dr_pool and opts['rsize'] != -1: + self.check_data_reduction_pool_params(opts) + params = self._get_vdisk_create_params(opts, is_dr_pool) try: new_copy_id = self.ssh.addvdiskcopy(vdisk, dest_pool, params, auto_delete) @@ -2904,14 +3018,12 @@ class StorwizeSVCCommonDriver(san.SanDriver, 'replication enabled is not supported.') raise exception.InvalidInput(reason=reason) if not opts['easytier']: - raise exception.InvalidInput( - reason=_('The default easytier of hyperswap volume is ' - 'on, it does not support easytier off.')) + msg = _('The default easytier of hyperswap volume is ' + 'on, it does not support easytier off.') + raise exception.VolumeDriverException(message=msg) self._helpers.check_hyperswap_pool(pool, opts['peer_pool']) - hyperpool = '%s:%s' % (pool, opts['peer_pool']) - self._helpers.create_hyperswap_volume(volume.name, - volume.size, 'gb', - hyperpool, opts) + self._helpers.create_hyperswap_volume(volume.name, volume.size, + 'gb', pool, opts) else: if opts['mirror_pool'] and rep_type: reason = _('Create mirror volume with replication enabled is ' @@ -4293,6 +4405,21 @@ class StorwizeSVCCommonDriver(san.SanDriver, resp = self._helpers.lsvdiskcopy(volume.name) if len(resp) > 1: copies = self._helpers.get_vdisk_copies(volume.name) + src_pool = copies['primary']['mdisk_grp_name'] + mirror_pool = copies['secondary']['mdisk_grp_name'] + opts = self._get_vdisk_params(volume.volume_type_id) + if opts['rsize'] != -1: + if (self._helpers.is_data_reduction_pool(src_pool) or + self._helpers.is_data_reduction_pool(mirror_pool)): + msg = _('Unable to migrate: the thin-provisioned or ' + 'compressed volume can not be migrated from a data' + ' reduction pool. ') + raise exception.VolumeDriverException(message=msg) + elif self._helpers.is_data_reduction_pool(dest_pool): + msg = _('Unable to migrate: the thin-provisioned or ' + 'compressed volume can not be migrated to a data ' + 'reduction pool.') + raise exception.VolumeDriverException(message=msg) self._helpers.migratevdisk(volume.name, dest_pool, copies['primary']['copy_id']) else: @@ -4309,8 +4436,17 @@ class StorwizeSVCCommonDriver(san.SanDriver, {'id': volume.id, 'host': host['host']}) return (True, None) + def _verify_iogrp(self, rsize, pool, opts, rep_type, status): + if rsize != -1 and self._helpers.is_volume_type_dr_pools( + pool, opts, rep_type, rep_target_pool=self._replica_target[ + 'pool_name'] if rep_type else None): + msg = _('Unable to retype: the thin-provisioned or compressed ' + 'vol in data reduction pool can not modify iogrp.') + raise exception.VolumeDriverException(message=msg) + def _verify_retype_params(self, volume, new_opts, old_opts, need_copy, - change_mirror, new_rep_type, old_rep_type): + change_mirror, new_rep_type, old_rep_type, + vdisk_changes, old_pool, new_pool): # Some volume parameters can not be changed or changed at the same # time during volume retype operation. This function checks the # retype parameters. @@ -4320,6 +4456,16 @@ class StorwizeSVCCommonDriver(san.SanDriver, 'has only one copy in storage.') % volume.name) raise exception.VolumeDriverException(message=msg) + is_old_type_dr_pool = self._helpers.is_volume_type_dr_pools( + old_pool, old_opts, old_rep_type, + rep_target_pool=self._replica_target[ + 'pool_name'] if old_rep_type else None) + is_new_type_dr_pool = self._helpers.is_volume_type_dr_pools( + new_pool, new_opts, new_rep_type, + rep_target_pool=self._replica_target[ + 'pool_name'] if new_rep_type else None) + need_check_dr_pool_param = False + if need_copy: # mirror volume can not add volume-copy again. if len(resp) > 1: @@ -4332,6 +4478,7 @@ class StorwizeSVCCommonDriver(san.SanDriver, 'it is not allowed for mirror volume ' '%s.') % volume.name) raise exception.VolumeDriverException(message=msg) + need_check_dr_pool_param = True if change_mirror: if (new_opts['mirror_pool'] and @@ -4340,6 +4487,16 @@ class StorwizeSVCCommonDriver(san.SanDriver, msg = (_('Unable to retype: The pool %s in which mirror copy ' 'is stored is not valid') % new_opts['mirror_pool']) raise exception.VolumeDriverException(message=msg) + # migrate second copy to a dr pool or from a dr pool is not allowed + if (old_opts['mirror_pool'] and new_opts[ + 'mirror_pool'] and old_opts['rsize'] != -1): + if is_old_type_dr_pool or is_new_type_dr_pool: + msg = _('Unable to retype: the thin-provisioned or ' + 'compressed vol can not be migrated from a dr pool' + ' or to a dr pool.') + raise exception.VolumeDriverException(message=msg) + if not old_opts['mirror_pool'] and new_opts['mirror_pool']: + need_check_dr_pool_param = True # There are four options for rep_type: None, metro, global, gmcv if new_rep_type or old_rep_type: @@ -4367,6 +4524,15 @@ class StorwizeSVCCommonDriver(san.SanDriver, 'new_rep_type': new_rep_type}) LOG.error(msg) raise exception.VolumeDriverException(message=msg) + if not old_rep_type and new_rep_type: + if new_opts['rsize'] != -1 and is_new_type_dr_pool: + try: + self._helpers.check_data_reduction_pool_params( + new_opts) + except Exception as err: + msg = (_("Failed to retype volume, the error is " + "%s") % err) + raise exception.VolumeDriverException(message=msg) elif storwize_const.GMCV == new_rep_type: # To gmcv, we may change cycle_period_seconds if needed previous_cps = old_opts.get('cycle_period_seconds') @@ -4375,6 +4541,22 @@ class StorwizeSVCCommonDriver(san.SanDriver, self._helpers.change_relationship_cycleperiod(volume.name, new_cps) + if (is_new_type_dr_pool and new_opts[ + 'rsize'] != -1 and need_check_dr_pool_param == 1): + try: + self._helpers.check_data_reduction_pool_params(new_opts) + except Exception as err: + msg = (_("Failed to retype volume, the error is " + "%s") % err) + raise exception.VolumeDriverException(message=msg) + + if vdisk_changes and not need_copy: + if is_old_type_dr_pool or is_new_type_dr_pool: + msg = _('The volume specified is a thin or compressed volume ' + 'in a data reduction pool. The autoexpand and warning' + ' and easytier can not be changed.') + raise exception.VolumeDriverException(message=msg) + def _check_hyperswap_retype_params(self, volume, new_opts, old_opts, change_mirror, new_rep_type, old_rep_type, old_pool, @@ -4413,13 +4595,23 @@ class StorwizeSVCCommonDriver(san.SanDriver, raise exception.InvalidInput( reason=_('The default easytier of hyperswap volume is ' 'on, it does not support easytier off.')) - if (old_opts['volume_topology'] != 'hyperswap' and - self._helpers._get_vdisk_fc_mappings(volume.name)): - msg = _('Unable to retype: it is not allowed to change a ' - 'normal volume with snapshot to a hyperswap ' - 'volume.') - LOG.error(msg) - raise exception.InvalidInput(message=msg) + if old_opts['volume_topology'] != 'hyperswap': + is_new_type_dr_pool = self._helpers.is_volume_type_dr_pools( + new_pool, new_opts) + if is_new_type_dr_pool and new_opts['rsize'] != -1: + try: + self._helpers.check_data_reduction_pool_params( + new_opts) + except Exception as err: + msg = (_("Failed to retype volume, the error is " + "%s") % err) + raise exception.VolumeDriverException(reason=msg) + if self._helpers._get_vdisk_fc_mappings(volume.name): + msg = _('Unable to retype: it is not allowed to change a ' + 'normal volume with snapshot to a hyperswap ' + 'volume.') + LOG.error(msg) + raise exception.InvalidInput(message=msg) if (old_opts['volume_topology'] == 'hyperswap' and old_opts['peer_pool'] != new_opts['peer_pool']): msg = _('Unable to retype: it is not allowed to change a ' @@ -4529,7 +4721,8 @@ class StorwizeSVCCommonDriver(san.SanDriver, new_opts, new_pool) self._verify_retype_params(volume, new_opts, old_opts, need_copy, - change_mirror, new_rep_type, old_rep_type) + change_mirror, new_rep_type, old_rep_type, + vdisk_changes, old_pool, new_pool) if old_opts['volume_topology'] or new_opts['volume_topology']: self._check_hyperswap_retype_params(volume, new_opts, old_opts, @@ -4540,6 +4733,11 @@ class StorwizeSVCCommonDriver(san.SanDriver, old_pool, new_pool, vdisk_changes, need_copy, new_type) else: + # hyperswap volume will select iogrp by storage. ignore iogrp here. + if old_io_grp != new_io_grp: + self._verify_iogrp(old_opts['rsize'], old_pool, old_opts, + old_rep_type, + volume.previous_status) if need_copy: self._check_volume_copy_ops() dest_pool = self._helpers.can_migrate_to_host(host, @@ -4732,6 +4930,17 @@ class StorwizeSVCCommonDriver(san.SanDriver, 'type_cps': rep_cps}) raise exception.ManageExistingVolumeTypeMismatch(reason=msg) + pool = utils.extract_host(volume['host'], 'pool') + if copies['primary']['mdisk_grp_name'] != pool: + msg = (_("Failed to manage existing volume due to the " + "pool of the volume to be managed does not " + "match the backend pool. Pool of the " + "volume to be managed is %(vdisk_pool)s. Pool " + "of the backend is %(backend_pool)s.") % + {'vdisk_pool': copies['primary']['mdisk_grp_name'], + 'backend_pool': pool}) + raise exception.ManageExistingVolumeTypeMismatch(reason=msg) + if volume['volume_type_id']: opts = self._get_vdisk_params(volume['volume_type_id'], volume_metadata= @@ -4822,17 +5031,17 @@ class StorwizeSVCCommonDriver(san.SanDriver, {'vdisk_iogrp': vdisk['IO_group_name'], 'opt_iogrp': opts['iogrp']}) raise exception.ManageExistingVolumeTypeMismatch(reason=msg) - pool = utils.extract_host(volume['host'], 'pool') - if copies['primary']['mdisk_grp_name'] != pool: - msg = (_("Failed to manage existing volume due to the " - "pool of the volume to be managed does not " - "match the backend pool. Pool of the " - "volume to be managed is %(vdisk_pool)s. Pool " - "of the backend is %(backend_pool)s.") % - {'vdisk_pool': copies['primary']['mdisk_grp_name'], - 'backend_pool': pool}) - raise exception.ManageExistingVolumeTypeMismatch(reason=msg) + if opts['rsize'] != -1 and self._helpers.is_volume_type_dr_pools( + pool, opts, rep_type, rep_target_pool=self._replica_target[ + 'pool_name'] if rep_type else None): + try: + self._helpers.check_data_reduction_pool_params(opts) + except Exception as err: + msg = (_("Failed to manage existing volume, the error is " + "%s") % err) + raise exception.ManageExistingVolumeTypeMismatch( + reason=msg) model_update = {'replication_status': fields.ReplicationStatus.NOT_CAPABLE} self._helpers.rename_vdisk(vdisk['name'], volume['name']) diff --git a/releasenotes/notes/storwize-dr-pool-support-52db3a95e54aef88.yaml b/releasenotes/notes/storwize-dr-pool-support-52db3a95e54aef88.yaml new file mode 100644 index 00000000000..4aa186291ba --- /dev/null +++ b/releasenotes/notes/storwize-dr-pool-support-52db3a95e54aef88.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Added data reduction pool support for thin-provisoned and compressed + volume in Storwize cinder driver.