From 1491eecfd336621ed0f6501854dabe4db5a9e25b Mon Sep 17 00:00:00 2001 From: inori Date: Wed, 27 Sep 2023 06:11:30 -0400 Subject: [PATCH] Fujitsu Driver: Update extend volume functionality Revised the 'Extend Volume' process on the RaidGroup to improve processing speed as follows: * When extending a volume created on ThinProvisionPool, the process will still use SMI-S for volume extension. * When extending a volume created on RAID group, the process has been updated to use CLI for volume extension. Change-Id: Ifcd93b9c9d94b214e890c57afd588b43b568478a --- .../unit/volume/drivers/test_fujitsu_dx.py | 83 +++++++--- .../fujitsu/eternus_dx/eternus_dx_cli.py | 5 + .../fujitsu/eternus_dx/eternus_dx_common.py | 146 +++++++++++------- .../fujitsu/eternus_dx/eternus_dx_fc.py | 8 +- .../fujitsu/eternus_dx/eternus_dx_iscsi.py | 8 +- .../drivers/fujitsu-eternus-dx-driver.rst | 4 +- ...jitsu-add-cli-extend-e94b887dac8a45b3.yaml | 14 ++ 7 files changed, 181 insertions(+), 87 deletions(-) create mode 100644 releasenotes/notes/fujitsu-add-cli-extend-e94b887dac8a45b3.yaml diff --git a/cinder/tests/unit/volume/drivers/test_fujitsu_dx.py b/cinder/tests/unit/volume/drivers/test_fujitsu_dx.py index 58d33deac1e..84b3a163ba0 100644 --- a/cinder/tests/unit/volume/drivers/test_fujitsu_dx.py +++ b/cinder/tests/unit/volume/drivers/test_fujitsu_dx.py @@ -176,7 +176,7 @@ FAKE_POOLS = [{ }] FAKE_STATS = { - 'driver_version': '1.4.0', + 'driver_version': '1.4.1', 'storage_protocol': 'iSCSI', 'vendor_name': 'FUJITSU', 'QoS_support': True, @@ -186,7 +186,7 @@ FAKE_STATS = { 'pools': FAKE_POOLS, } FAKE_STATS2 = { - 'driver_version': '1.4.0', + 'driver_version': '1.4.1', 'storage_protocol': 'FC', 'vendor_name': 'FUJITSU', 'QoS_support': True, @@ -1008,6 +1008,8 @@ class FJFCDriverTestCase(test.TestCase): '3B\r\nf.ce\tMaintainer\t01\t00' '\t00\t00\r\ntestuser\tSoftware' '\t01\t01\t00\t00\r\nCLI> ' % exec_cmdline) + elif exec_cmdline.startswith('expand volume'): + ret = '%s\r\n00\r\nCLI> ' % exec_cmdline elif exec_cmdline.startswith('set volume-qos'): ret = '%s\r\n00\r\n0001\r\nCLI> ' % exec_cmdline elif exec_cmdline.startswith('show volumes'): @@ -1176,19 +1178,27 @@ class FJFCDriverTestCase(test.TestCase): self.driver.delete_volume(TEST_VOLUME) def test_extend_volume(self): - model_info = self.driver.create_volume(TEST_VOLUME) - self.assertEqual(FAKE_MODEL_INFO1, model_info) + # Test the extension of volume created on RaidGroup and + # ThinProvisioningPool separately. + TEST_VOLUME_LIST = [TEST_VOLUME, TEST_VOLUME2] + FAKE_MODEL_INFO_LIST = [FAKE_MODEL_INFO1, FAKE_MODEL_INFO3] + model_info_list = [] + for i in range(len(TEST_VOLUME_LIST)): + model_info = self.driver.create_volume(TEST_VOLUME_LIST[i]) + self.assertEqual(FAKE_MODEL_INFO_LIST[i], model_info) + model_info_list.append(model_info) - volume_info = {} - for key in TEST_VOLUME: - if key == 'provider_location': - volume_info[key] = model_info[key] - elif key == 'metadata': - volume_info[key] = model_info[key] - else: - volume_info[key] = TEST_VOLUME[key] + for i in range(len(TEST_VOLUME_LIST)): + volume_info = {} + for key in TEST_VOLUME_LIST[i]: + if key == 'provider_location': + volume_info[key] = model_info_list[i][key] + elif key == 'metadata': + volume_info[key] = model_info_list[i][key] + else: + volume_info[key] = TEST_VOLUME_LIST[i][key] - self.driver.extend_volume(volume_info, 10) + self.driver.extend_volume(volume_info, 10) def test_create_volume_with_qos(self): self.driver.common._get_qos_specs = mock.Mock() @@ -1245,6 +1255,8 @@ class FJISCSIDriverTestCase(test.TestCase): '3B\r\nf.ce\tMaintainer\t01\t00' '\t00\t00\r\ntestuser\tSoftware' '\t01\t01\t00\t00\r\nCLI> ' % exec_cmdline) + elif exec_cmdline.startswith('expand volume'): + ret = '%s\r\n00\r\nCLI> ' % exec_cmdline elif exec_cmdline.startswith('set volume-qos'): ret = '%s\r\n00\r\n0001\r\nCLI> ' % exec_cmdline elif exec_cmdline.startswith('show volumes'): @@ -1414,19 +1426,27 @@ class FJISCSIDriverTestCase(test.TestCase): self.driver.delete_volume(TEST_VOLUME) def test_extend_volume(self): - model_info = self.driver.create_volume(TEST_VOLUME) - self.assertEqual(FAKE_MODEL_INFO1, model_info) + # Test the extension of volume created on RaidGroup and + # ThinProvisioningPool separately. + TEST_VOLUME_LIST = [TEST_VOLUME, TEST_VOLUME2] + FAKE_MODEL_INFO_LIST = [FAKE_MODEL_INFO1, FAKE_MODEL_INFO3] + model_info_list = [] + for i in range(len(TEST_VOLUME_LIST)): + model_info = self.driver.create_volume(TEST_VOLUME_LIST[i]) + self.assertEqual(FAKE_MODEL_INFO_LIST[i], model_info) + model_info_list.append(model_info) - volume_info = {} - for key in TEST_VOLUME: - if key == 'provider_location': - volume_info[key] = model_info[key] - elif key == 'metadata': - volume_info[key] = model_info[key] - else: - volume_info[key] = TEST_VOLUME[key] + for i in range(len(TEST_VOLUME_LIST)): + volume_info = {} + for key in TEST_VOLUME_LIST[i]: + if key == 'provider_location': + volume_info[key] = model_info_list[i][key] + elif key == 'metadata': + volume_info[key] = model_info_list[i][key] + else: + volume_info[key] = TEST_VOLUME_LIST[i][key] - self.driver.extend_volume(volume_info, 10) + self.driver.extend_volume(volume_info, 10) def test_create_volume_with_qos(self): self.driver.common._get_qos_specs = mock.Mock() @@ -1467,6 +1487,8 @@ class FJCLITestCase(test.TestCase): '3B\r\nf.ce\tMaintainer\t01\t00' '\t00\t00\r\ntestuser\tSoftware' '\t01\t01\t00\t00\r\nCLI> ' % exec_cmdline) + elif exec_cmdline.startswith('expand volume'): + ret = '%s\r\n00\r\nCLI> ' % exec_cmdline elif exec_cmdline.startswith('set volume-qos'): ret = '%s\r\n00\r\n0001\r\nCLI> ' % exec_cmdline elif exec_cmdline.startswith('show volumes'): @@ -1561,6 +1583,19 @@ class FJCLITestCase(test.TestCase): role = self.cli._check_user_role() self.assertEqual(FAKE_ROLE, role) + def test_expand_volume(self): + FAKE_VOLME_NAME = 'FJosv_0qJ4rpOHgFE8ipcJOMfBmg==' + FAKE_RG_NAME = 'abcd1234_RG' + FAKE_SIZE = '10gb' + FAKE_EXPAND_OPTION = self.create_fake_options( + volume_name=FAKE_VOLME_NAME, + rg_name=FAKE_RG_NAME, + size=FAKE_SIZE) + + EXPAND_OUTPUT = self.cli._expand_volume(**FAKE_EXPAND_OPTION) + FAKE_EXPAND_OUTPUT = {**FAKE_CLI_OUTPUT, 'message': []} + self.assertEqual(FAKE_EXPAND_OUTPUT, EXPAND_OUTPUT) + def test_set_volume_qos(self): FAKE_VOLUME_NAME = 'FJosv_0qJ4rpOHgFE8ipcJOMfBmg==' FAKE_BANDWIDTH_LIMIT = 2 diff --git a/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_cli.py b/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_cli.py index a9054bbb464..19ecc3d986d 100644 --- a/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_cli.py +++ b/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_cli.py @@ -47,6 +47,7 @@ class FJDXCLI(object): self.ce_support = False self.CMD_dic = { 'check_user_role': self._check_user_role, + 'expand_volume': self._expand_volume, 'show_pool_provision': self._show_pool_provision, 'show_qos_bandwidth_limit': self._show_qos_bandwidth_limit, 'set_qos_bandwidth_limit': self._set_qos_bandwidth_limit, @@ -239,6 +240,10 @@ class FJDXCLI(object): } return output + def _expand_volume(self, **option): + """Exec expand volume.""" + return self._exec_cli("expand volume", **option) + def _set_volume_qos(self, **option): """Exec set volume-qos.""" return self._exec_cli("set volume-qos", **option) diff --git a/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_common.py b/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_common.py index 4160004769d..a4cdb9ded13 100644 --- a/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_common.py +++ b/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_common.py @@ -67,10 +67,11 @@ class FJDXCommon(object): 1.0 - Initial driver 1.3.0 - Community base version 1.4.0 - Add support for QoS. + 1.4.1 - Add the method for expanding RAID volumes by CLI. """ - VERSION = "1.4.0" + VERSION = "1.4.1" stats = { 'driver_version': VERSION, 'storage_protocol': None, @@ -821,32 +822,33 @@ class FJDXCommon(object): 'size': volume['size'], 'nsize': new_size}) self.conn = self._get_eternus_connection() - volumesize = new_size * units.Gi volumename = self._get_volume_name(volume) - # Get source volume instance. - vol_instance = self._find_lun(volume) - if vol_instance is None: + # Get volume instance. + volume_instance = self._find_lun(volume) + if not volume_instance: msg = (_('extend_volume, ' 'volumename: %(volumename)s, ' - 'volume not found.') + 'not found.') % {'volumename': volumename}) LOG.error(msg) raise exception.VolumeBackendAPIException(data=msg) LOG.debug('extend_volume, volumename: %(volumename)s, ' 'volumesize: %(volumesize)u, ' - 'volume instance: %(vol_instance)s.', + 'volume instance: %(volume_instance)s.', {'volumename': volumename, - 'volumesize': volumesize, - 'vol_instance': vol_instance.path}) + 'volumesize': new_size, + 'volume_instance': volume_instance.path}) # Get poolname from driver configuration file. - pool_name, pool = self._find_pool_from_volume(vol_instance) - if pool is None: + pool_name, pool = self._find_pool_from_volume(volume_instance) + + # Check the existence of pool. + if not pool: msg = (_('extend_volume, ' 'eternus_pool: %(eternus_pool)s, ' - 'pool not found.') + 'not found.') % {'eternus_pool': pool_name}) LOG.error(msg) raise exception.VolumeBackendAPIException(data=msg) @@ -857,56 +859,84 @@ class FJDXCommon(object): else: pooltype = CONSTANTS.TPPOOL - configservice = self._find_eternus_service(CONSTANTS.STOR_CONF) - if not configservice: - msg = (_('extend_volume, volume: %(volume)s, ' - 'volumename: %(volumename)s, ' - 'eternus_pool: %(eternus_pool)s, ' - 'Storage Configuration Service not found.') - % {'volume': volume, - 'volumename': volumename, - 'eternus_pool': pool_name}) - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) + if pooltype == CONSTANTS.RAIDGROUP: + extend_size = str(new_size - volume['size']) + 'gb' + param_dict = { + 'volume-name': volumename, + 'rg-name': pool_name, + 'size': extend_size + } + rc, errordesc, data = self._exec_eternus_cli( + 'expand_volume', + **param_dict) - LOG.debug('extend_volume, ' - 'CreateOrModifyElementFromStoragePool, ' - 'ConfigService: %(service)s, ' - 'ElementName: %(volumename)s, ' - 'InPool: %(eternus_pool)s, ' - 'ElementType: %(pooltype)u, ' - 'Size: %(volumesize)u, ' - 'TheElement: %(vol_instance)s.', - {'service': configservice, - 'volumename': volumename, - 'eternus_pool': pool_name, - 'pooltype': pooltype, - 'volumesize': volumesize, - 'vol_instance': vol_instance.path}) + if rc != 0: + msg = (_('extend_volume, ' + 'volumename: %(volumename)s, ' + 'Return code: %(rc)lu, ' + 'Error: %(errordesc)s, ' + 'Message: %(job)s, ' + 'PoolType: %(pooltype)s.') + % {'volumename': volumename, + 'rc': rc, + 'errordesc': errordesc, + 'pooltype': CONSTANTS.POOL_TYPE_dic[pooltype], + 'job': data}) + LOG.error(msg) + raise exception.VolumeBackendAPIException(data=msg) - # Invoke method for extend volume - rc, errordesc, job = self._exec_eternus_service( - 'CreateOrModifyElementFromStoragePool', - configservice, - ElementName=volumename, - InPool=pool, - ElementType=self._pywbem_uint(pooltype, '16'), - Size=self._pywbem_uint(volumesize, '64'), - TheElement=vol_instance.path) + else: # Pooltype is TPPOOL. + volumesize = new_size * units.Gi + configservice = self._find_eternus_service(CONSTANTS.STOR_CONF) + if not configservice: + msg = (_('extend_volume, volume: %(volume)s, ' + 'volumename: %(volumename)s, ' + 'eternus_pool: %(eternus_pool)s, ' + 'Storage Configuration Service not found.') + % {'volume': volume, + 'volumename': volumename, + 'eternus_pool': pool_name}) + LOG.error(msg) + raise exception.VolumeBackendAPIException(data=msg) - if rc != 0: - msg = (_('extend_volume, ' - 'volumename: %(volumename)s, ' - 'Return code: %(rc)lu, ' - 'Error: %(errordesc)s, ' - 'PoolType: %(pooltype)s.') - % {'volumename': volumename, - 'rc': rc, - 'errordesc': errordesc, - 'pooltype': CONSTANTS.POOL_TYPE_dic[pooltype]}) + LOG.debug('extend_volume, ' + 'CreateOrModifyElementFromStoragePool, ' + 'ConfigService: %(service)s, ' + 'ElementName: %(volumename)s, ' + 'InPool: %(eternus_pool)s, ' + 'ElementType: %(pooltype)u, ' + 'Size: %(volumesize)u, ' + 'TheElement: %(vol_instance)s.', + {'service': configservice, + 'volumename': volumename, + 'eternus_pool': pool_name, + 'pooltype': pooltype, + 'volumesize': volumesize, + 'vol_instance': volume_instance.path}) - LOG.error(msg) - raise exception.VolumeBackendAPIException(data=msg) + # Invoke method for extend volume. + rc, errordesc, _x = self._exec_eternus_service( + 'CreateOrModifyElementFromStoragePool', + configservice, + ElementName=volumename, + InPool=pool, + ElementType=self._pywbem_uint(pooltype, '16'), + Size=self._pywbem_uint(volumesize, '64'), + TheElement=volume_instance.path) + + if rc != 0: + msg = (_('extend_volume, ' + 'volumename: %(volumename)s, ' + 'Return code: %(rc)lu, ' + 'Error: %(errordesc)s, ' + 'PoolType: %(pooltype)s.') + % {'volumename': volumename, + 'rc': rc, + 'errordesc': errordesc, + 'pooltype': CONSTANTS.POOL_TYPE_dic[pooltype]}) + + LOG.error(msg) + raise exception.VolumeBackendAPIException(data=msg) LOG.debug('extend_volume, ' 'volumename: %(volumename)s, ' diff --git a/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_fc.py b/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_fc.py index 4aacc9ac152..84f24d02185 100644 --- a/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_fc.py +++ b/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_fc.py @@ -157,7 +157,13 @@ class FJDXFCDriver(driver.FibreChannelDriver): def extend_volume(self, volume, new_size): """Extend volume.""" - self.common.extend_volume(volume, new_size) + LOG.debug('extend_volume, ' + 'volume id: %s, Enter method.', volume['id']) + + used_pool_name = self.common.extend_volume(volume, new_size) + + LOG.debug('extend_volume, ' + 'used pool name: %s, Exit method.', used_pool_name) def _get_metadata(self, volume): v_metadata = volume.get('volume_metadata') diff --git a/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_iscsi.py b/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_iscsi.py index 8f7df881643..49ef28a2982 100644 --- a/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_iscsi.py +++ b/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_iscsi.py @@ -144,4 +144,10 @@ class FJDXISCSIDriver(driver.ISCSIDriver): def extend_volume(self, volume, new_size): """Extend volume.""" - self.common.extend_volume(volume, new_size) + LOG.debug('extend_volume, ' + 'volume id: %s, Enter method.', volume['id']) + + used_pool_name = self.common.extend_volume(volume, new_size) + + LOG.debug('extend_volume, ' + 'used pool name: %s, Exit method.', used_pool_name) diff --git a/doc/source/configuration/block-storage/drivers/fujitsu-eternus-dx-driver.rst b/doc/source/configuration/block-storage/drivers/fujitsu-eternus-dx-driver.rst index 749c95e5b17..a1cb4535e92 100644 --- a/doc/source/configuration/block-storage/drivers/fujitsu-eternus-dx-driver.rst +++ b/doc/source/configuration/block-storage/drivers/fujitsu-eternus-dx-driver.rst @@ -44,11 +44,9 @@ Supported operations * Copy an image to a volume. * Copy a volume to an image. * Clone a volume. -* Extend a volume. (\*1) +* Extend a volume. * Get volume statistics. -(\*1): It is executable only when you use TPP as a storage pool. - Preparation ~~~~~~~~~~~ diff --git a/releasenotes/notes/fujitsu-add-cli-extend-e94b887dac8a45b3.yaml b/releasenotes/notes/fujitsu-add-cli-extend-e94b887dac8a45b3.yaml new file mode 100644 index 00000000000..cb94cf38f82 --- /dev/null +++ b/releasenotes/notes/fujitsu-add-cli-extend-e94b887dac8a45b3.yaml @@ -0,0 +1,14 @@ +--- +features: + - | + Fujitsu ETERNUS DX driver: Added support to extend a volume on RAID Group + using CLI. + + Revised the 'Extend Volume' process on the RAID Group to improve processing + speed as follows: + + * When extending a volume created on ThinProvisionPool, the process will + still use SMI-S for volume extension. + + * When extending a volume created on RaidGroup, the process has been + updated to use CLI for volume extension.