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
This commit is contained in:
parent
b1fd778cc8
commit
1678260539
@ -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)
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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(
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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'],
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user