From 1678260539898762dd955bf05241e16fc41769ff Mon Sep 17 00:00:00 2001 From: Atsushi Kawai Date: Tue, 8 Mar 2022 03:59:37 +0000 Subject: [PATCH] Hitachi: Add Target Port Assignment for VSP Driver Defining particular ports in extra spec "hbsd:target_ports" determines which of the ports specified by the hitachi_target_ports or the hitachi_compute_target_ports parameters are used to create LUN paths during volume attach operations for each volume type. Implements: blueprint hitachi-vsp-tgt-port-asgn Change-Id: I2e8d80261fa797c8f3c83c078c29cf1173d46c3c --- .../hitachi/test_hitachi_hbsd_rest_fc.py | 23 ++++++++-- .../hitachi/test_hitachi_hbsd_rest_iscsi.py | 16 ++++++- cinder/volume/drivers/hitachi/hbsd_common.py | 43 +++++++++++++++++-- cinder/volume/drivers/hitachi/hbsd_fc.py | 5 ++- cinder/volume/drivers/hitachi/hbsd_iscsi.py | 5 ++- cinder/volume/drivers/hitachi/hbsd_rest.py | 4 +- cinder/volume/drivers/hitachi/hbsd_rest_fc.py | 4 +- cinder/volume/drivers/hitachi/hbsd_utils.py | 14 +++++- ...hi-vsp-tgt-port-asgn-7536da008990824a.yaml | 9 ++++ 9 files changed, 109 insertions(+), 14 deletions(-) create mode 100644 releasenotes/notes/hitachi-vsp-tgt-port-asgn-7536da008990824a.yaml diff --git a/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hbsd_rest_fc.py b/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hbsd_rest_fc.py index 356401c8810..7b685c2e433 100644 --- a/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hbsd_rest_fc.py +++ b/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hbsd_rest_fc.py @@ -88,6 +88,7 @@ for i in range(4): volume = {} volume['id'] = '00000000-0000-0000-0000-{0:012d}'.format(i) volume['name'] = 'test-volume{0:d}'.format(i) + volume['volume_type_id'] = '00000000-0000-0000-0000-{0:012d}'.format(i) if i == 3: volume['provider_location'] = None else: @@ -98,6 +99,7 @@ for i in range(4): else: volume['status'] = 'available' volume = fake_volume.fake_volume_obj(CTXT, **volume) + volume.volume_type = fake_volume.fake_volume_type_obj(CTXT) TEST_VOLUME.append(volume) @@ -785,9 +787,13 @@ class HBSDRESTFCDriverTest(test.TestCase): @mock.patch.object(fczm_utils, "add_fc_zone") @mock.patch.object(requests.Session, "request") - def test_initialize_connection(self, request, add_fc_zone): + @mock.patch.object(volume_types, 'get_volume_type_extra_specs') + def test_initialize_connection( + self, get_volume_type_extra_specs, request, add_fc_zone): self.driver.common.conf.hitachi_zoning_request = True self.driver.common._lookup_service = FakeLookupService() + extra_specs = {"hbsd:target_ports": "CL1-A"} + get_volume_type_extra_specs.return_value = extra_specs request.side_effect = [FakeResponse(200, GET_HOST_WWNS_RESULT), FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)] ret = self.driver.initialize_connection( @@ -795,15 +801,20 @@ class HBSDRESTFCDriverTest(test.TestCase): self.assertEqual('fibre_channel', ret['driver_volume_type']) self.assertEqual([CONFIG_MAP['target_wwn']], ret['data']['target_wwn']) self.assertEqual(1, ret['data']['target_lun']) + self.assertEqual(1, get_volume_type_extra_specs.call_count) self.assertEqual(2, request.call_count) self.assertEqual(1, add_fc_zone.call_count) @mock.patch.object(fczm_utils, "add_fc_zone") @mock.patch.object(requests.Session, "request") - def test_initialize_connection_already_mapped(self, request, add_fc_zone): + @mock.patch.object(volume_types, 'get_volume_type_extra_specs') + def test_initialize_connection_already_mapped( + self, get_volume_type_extra_specs, request, add_fc_zone): """Normal case: ldev have already mapped.""" self.driver.common.conf.hitachi_zoning_request = True self.driver.common._lookup_service = FakeLookupService() + extra_specs = {"hbsd:target_ports": "CL1-A"} + get_volume_type_extra_specs.return_value = extra_specs request.side_effect = [ FakeResponse(200, GET_HOST_WWNS_RESULT), FakeResponse(202, COMPLETED_FAILED_RESULT_LU_DEFINED), @@ -814,15 +825,20 @@ class HBSDRESTFCDriverTest(test.TestCase): self.assertEqual('fibre_channel', ret['driver_volume_type']) self.assertEqual([CONFIG_MAP['target_wwn']], ret['data']['target_wwn']) self.assertEqual(1, ret['data']['target_lun']) + self.assertEqual(1, get_volume_type_extra_specs.call_count) self.assertEqual(3, request.call_count) self.assertEqual(1, add_fc_zone.call_count) @mock.patch.object(fczm_utils, "add_fc_zone") @mock.patch.object(requests.Session, "request") - def test_initialize_connection_shared_target(self, request, add_fc_zone): + @mock.patch.object(volume_types, 'get_volume_type_extra_specs') + def test_initialize_connection_shared_target( + self, get_volume_type_extra_specs, request, add_fc_zone): """Normal case: A target shared with other systems.""" self.driver.common.conf.hitachi_zoning_request = True self.driver.common._lookup_service = FakeLookupService() + extra_specs = {"hbsd:target_ports": "CL1-A"} + get_volume_type_extra_specs.return_value = extra_specs request.side_effect = [FakeResponse(200, NOTFOUND_RESULT), FakeResponse(200, NOTFOUND_RESULT), FakeResponse(200, GET_HOST_GROUPS_RESULT), @@ -833,6 +849,7 @@ class HBSDRESTFCDriverTest(test.TestCase): self.assertEqual('fibre_channel', ret['driver_volume_type']) self.assertEqual([CONFIG_MAP['target_wwn']], ret['data']['target_wwn']) self.assertEqual(1, ret['data']['target_lun']) + self.assertEqual(1, get_volume_type_extra_specs.call_count) self.assertEqual(5, request.call_count) self.assertEqual(1, add_fc_zone.call_count) diff --git a/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hbsd_rest_iscsi.py b/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hbsd_rest_iscsi.py index 867a888b3cd..dde0ef939e4 100644 --- a/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hbsd_rest_iscsi.py +++ b/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hbsd_rest_iscsi.py @@ -79,6 +79,7 @@ for i in range(4): volume = {} volume['id'] = '00000000-0000-0000-0000-{0:012d}'.format(i) volume['name'] = 'test-volume{0:d}'.format(i) + volume['volume_type_id'] = '00000000-0000-0000-0000-{0:012d}'.format(i) if i == 3: volume['provider_location'] = None else: @@ -89,6 +90,7 @@ for i in range(4): else: volume['status'] = 'available' volume = fake_volume.fake_volume_obj(CTXT, **volume) + volume.volume_type = fake_volume.fake_volume_type_obj(CTXT) TEST_VOLUME.append(volume) @@ -616,7 +618,11 @@ class HBSDRESTISCSIDriverTest(test.TestCase): self.assertEqual(5, request.call_count) @mock.patch.object(requests.Session, "request") - def test_initialize_connection(self, request): + @mock.patch.object(volume_types, 'get_volume_type_extra_specs') + def test_initialize_connection( + self, get_volume_type_extra_specs, request): + extra_specs = {"hbsd:target_ports": "CL1-A"} + get_volume_type_extra_specs.return_value = extra_specs request.side_effect = [FakeResponse(200, GET_HOST_ISCSIS_RESULT), FakeResponse(200, GET_HOST_GROUP_RESULT), FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)] @@ -636,11 +642,16 @@ class HBSDRESTISCSIDriverTest(test.TestCase): self.assertEqual( CONFIG_MAP['auth_password'], ret['data']['auth_password']) self.assertEqual(1, ret['data']['target_lun']) + self.assertEqual(1, get_volume_type_extra_specs.call_count) self.assertEqual(3, request.call_count) @mock.patch.object(requests.Session, "request") - def test_initialize_connection_shared_target(self, request): + @mock.patch.object(volume_types, 'get_volume_type_extra_specs') + def test_initialize_connection_shared_target( + self, get_volume_type_extra_specs, request): """Normal case: A target shared with other systems.""" + extra_specs = {"hbsd:target_ports": "CL1-A"} + get_volume_type_extra_specs.return_value = extra_specs request.side_effect = [FakeResponse(200, NOTFOUND_RESULT), FakeResponse(200, GET_HOST_GROUPS_RESULT), FakeResponse(200, GET_HOST_ISCSIS_RESULT), @@ -661,6 +672,7 @@ class HBSDRESTISCSIDriverTest(test.TestCase): self.assertEqual( CONFIG_MAP['auth_password'], ret['data']['auth_password']) self.assertEqual(1, ret['data']['target_lun']) + self.assertEqual(1, get_volume_type_extra_specs.call_count) self.assertEqual(4, request.call_count) @mock.patch.object(requests.Session, "request") diff --git a/cinder/volume/drivers/hitachi/hbsd_common.py b/cinder/volume/drivers/hitachi/hbsd_common.py index 6de294a3e8c..5a8a02a2a98 100644 --- a/cinder/volume/drivers/hitachi/hbsd_common.py +++ b/cinder/volume/drivers/hitachi/hbsd_common.py @@ -25,6 +25,7 @@ from cinder import exception from cinder.i18n import _ from cinder.volume import configuration from cinder.volume.drivers.hitachi import hbsd_utils as utils +from cinder.volume import volume_types from cinder.volume import volume_utils _STR_VOLUME = 'volume' @@ -645,7 +646,7 @@ class HBSDCommon(): object='compute target port list', value=self.storage_info['compute_ports']) - def attach_ldev(self, volume, ldev, connector, targets): + def attach_ldev(self, volume, ldev, connector, is_snapshot, targets): """Initialize connection between the server and the volume.""" raise NotImplementedError() @@ -703,7 +704,7 @@ class HBSDCommon(): @coordination.synchronized( '{self.driver_info[driver_file_prefix]}-host-' '{self.conf.hitachi_storage_id}-{connector[host]}') - def initialize_connection(self, volume, connector): + def initialize_connection(self, volume, connector, is_snapshot=False): """Initialize connection between the server and the volume.""" targets = { 'info': {}, @@ -718,7 +719,8 @@ class HBSDCommon(): volume_id=volume['id']) self.raise_error(msg) - target_lun = self.attach_ldev(volume, ldev, connector, targets) + target_lun = self.attach_ldev( + volume, ldev, connector, is_snapshot, targets) return { 'driver_volume_type': self.driver_info['volume_type'], @@ -781,6 +783,41 @@ class HBSDCommon(): 'data': {'target_wwn': target_wwn}} return inner(self, volume, connector) + def get_volume_extra_specs(self, volume): + if volume is None: + return {} + type_id = volume.get('volume_type_id') + if type_id is None: + return {} + + return volume_types.get_volume_type_extra_specs(type_id) + + def filter_target_ports(self, target_ports, volume, is_snapshot=False): + specs = self.get_volume_extra_specs(volume) if volume else None + if not specs: + return target_ports + if self.driver_info.get('driver_dir_name'): + tps_name = self.driver_info['driver_dir_name'] + ':target_ports' + else: + return target_ports + + tps = specs.get(tps_name) + if tps is None: + return target_ports + + tpsset = set([s.strip() for s in tps.split(',')]) + filtered_tps = list(tpsset.intersection(target_ports)) + if is_snapshot: + volume = volume['volume'] + for port in tpsset: + if port not in target_ports: + utils.output_log( + MSG.INVALID_EXTRA_SPEC_KEY_PORT, + port=port, target_ports_param=tps_name, + volume_type=volume['volume_type']['name']) + + return filtered_tps + def unmanage_snapshot(self, snapshot): """Output error message and raise NotImplementedError.""" utils.output_log( diff --git a/cinder/volume/drivers/hitachi/hbsd_fc.py b/cinder/volume/drivers/hitachi/hbsd_fc.py index e21ad1acc30..2a30bd4e34c 100644 --- a/cinder/volume/drivers/hitachi/hbsd_fc.py +++ b/cinder/volume/drivers/hitachi/hbsd_fc.py @@ -42,6 +42,7 @@ _DRIVER_INFO = { 'volume_type': 'fibre_channel', 'param_prefix': utils.PARAM_PREFIX, 'vendor_name': utils.VENDOR_NAME, + 'driver_dir_name': utils.DRIVER_DIR_NAME, 'driver_prefix': utils.DRIVER_PREFIX, 'driver_file_prefix': utils.DRIVER_FILE_PREFIX, 'target_prefix': utils.TARGET_PREFIX, @@ -68,6 +69,7 @@ class HBSDFCDriver(driver.FibreChannelDriver): 2.1.0 - Add Cinder generic volume groups. 2.2.0 - Add maintenance parameters. 2.2.1 - Make the parameters name variable for supporting OEM storages. + 2.2.2 - Add Target Port Assignment. """ @@ -223,7 +225,8 @@ class HBSDFCDriver(driver.FibreChannelDriver): @volume_utils.trace def initialize_connection_snapshot(self, snapshot, connector, **kwargs): """Initialize connection between the server and the snapshot.""" - return self.common.initialize_connection(snapshot, connector) + return self.common.initialize_connection( + snapshot, connector, is_snapshot=True) @volume_utils.trace def terminate_connection_snapshot(self, snapshot, connector, **kwargs): diff --git a/cinder/volume/drivers/hitachi/hbsd_iscsi.py b/cinder/volume/drivers/hitachi/hbsd_iscsi.py index a34f5423e2f..cb58b0de16a 100644 --- a/cinder/volume/drivers/hitachi/hbsd_iscsi.py +++ b/cinder/volume/drivers/hitachi/hbsd_iscsi.py @@ -42,6 +42,7 @@ _DRIVER_INFO = { 'volume_type': 'iscsi', 'param_prefix': utils.PARAM_PREFIX, 'vendor_name': utils.VENDOR_NAME, + 'driver_dir_name': utils.DRIVER_DIR_NAME, 'driver_prefix': utils.DRIVER_PREFIX, 'driver_file_prefix': utils.DRIVER_FILE_PREFIX, 'target_prefix': utils.TARGET_PREFIX, @@ -68,6 +69,7 @@ class HBSDISCSIDriver(driver.ISCSIDriver): 2.1.0 - Add Cinder generic volume groups. 2.2.0 - Add maintenance parameters. 2.2.1 - Make the parameters name variable for supporting OEM storages. + 2.2.2 - Add Target Port Assignment. """ @@ -221,7 +223,8 @@ class HBSDISCSIDriver(driver.ISCSIDriver): @volume_utils.trace def initialize_connection_snapshot(self, snapshot, connector, **kwargs): """Initialize connection between the server and the snapshot.""" - return self.common.initialize_connection(snapshot, connector) + return self.common.initialize_connection( + snapshot, connector, is_snapshot=True) @volume_utils.trace def terminate_connection_snapshot(self, snapshot, connector, **kwargs): diff --git a/cinder/volume/drivers/hitachi/hbsd_rest.py b/cinder/volume/drivers/hitachi/hbsd_rest.py index e96d03c82d0..b7b4865b55e 100644 --- a/cinder/volume/drivers/hitachi/hbsd_rest.py +++ b/cinder/volume/drivers/hitachi/hbsd_rest.py @@ -560,9 +560,11 @@ class HBSDREST(common.HBSDCommon): port=port, id=gid, lun=lun) return lun - def attach_ldev(self, volume, ldev, connector, targets): + def attach_ldev(self, volume, ldev, connector, is_snapshot, targets): """Initialize connection between the server and the volume.""" target_ports = self.get_target_ports(connector) + target_ports = self.filter_target_ports(target_ports, volume, + is_snapshot) if (self.find_targets_from_storage( targets, connector, target_ports) and self.conf.hitachi_group_create): diff --git a/cinder/volume/drivers/hitachi/hbsd_rest_fc.py b/cinder/volume/drivers/hitachi/hbsd_rest_fc.py index 4f5e9cf79a1..330eaa75fb2 100644 --- a/cinder/volume/drivers/hitachi/hbsd_rest_fc.py +++ b/cinder/volume/drivers/hitachi/hbsd_rest_fc.py @@ -242,10 +242,10 @@ class HBSDRESTFC(rest.HBSDREST): not_found_count += 1 return not_found_count - def initialize_connection(self, volume, connector): + def initialize_connection(self, volume, connector, is_snapshot=False): """Initialize connection between the server and the volume.""" conn_info = super(HBSDRESTFC, self).initialize_connection( - volume, connector) + volume, connector, is_snapshot) if self.conf.hitachi_zoning_request: init_targ_map = utils.build_initiator_target_map( connector, conn_info['data']['target_wwn'], diff --git a/cinder/volume/drivers/hitachi/hbsd_utils.py b/cinder/volume/drivers/hitachi/hbsd_utils.py index 76009c6efd9..0d0f95ebb04 100644 --- a/cinder/volume/drivers/hitachi/hbsd_utils.py +++ b/cinder/volume/drivers/hitachi/hbsd_utils.py @@ -25,10 +25,11 @@ from oslo_utils import units from cinder import exception -VERSION = '2.2.1' +VERSION = '2.2.2' CI_WIKI_NAME = 'Hitachi_VSP_CI' PARAM_PREFIX = 'hitachi' VENDOR_NAME = 'Hitachi' +DRIVER_DIR_NAME = 'hbsd' DRIVER_PREFIX = 'HBSD' DRIVER_FILE_PREFIX = 'hbsd' TARGET_PREFIX = 'HBSD-' @@ -172,6 +173,17 @@ class HBSDMsg(enum.Enum): 'reason: %(reason)s)', 'suffix': WARNING_SUFFIX, } + INVALID_EXTRA_SPEC_KEY_PORT = { + 'msg_id': 330, + 'loglevel': base_logging.WARNING, + 'msg': 'The port name specified for the extra spec key ' + '"%(target_ports_param)s" ' + 'of the volume type is not specified for the ' + 'target_ports or compute_target_ports ' + 'parameter in cinder.conf. (port: %(port)s, volume type: ' + '%(volume_type)s)', + 'suffix': WARNING_SUFFIX, + } INVALID_PORT = { 'msg_id': 339, 'loglevel': base_logging.WARNING, diff --git a/releasenotes/notes/hitachi-vsp-tgt-port-asgn-7536da008990824a.yaml b/releasenotes/notes/hitachi-vsp-tgt-port-asgn-7536da008990824a.yaml new file mode 100644 index 00000000000..f077481db27 --- /dev/null +++ b/releasenotes/notes/hitachi-vsp-tgt-port-asgn-7536da008990824a.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + Hitachi driver: Add target port assignment. Defining particular + ports in extra spec ``hbsd:target_ports`` determines which of + the ports specified by the ``hitachi_target_ports`` or the + ``hitachi_compute_target_ports`` parameters are used to create LUN + paths during volume attach operations for each volume type. +