Ensure that lun_id is an int for NetApp Drivers

Various NetApp drivers were treating the lun_id as a string
rather than an int.  This was causing attempts to mount NetApp
volumes to Hyper-V nodes to fail as they were checking an integer
type against a unicode type which would fail.

This change casts result_lun to an integer after the value has
gone through the ssh injection attack check.  This way Hyper-V
is able to verify if the found LUN is the target LUN, enabling
mounting of NetApp volumes to Hyper-V.

This change also refactors initialize_connection into some helper
methods so as to enable simpler unit testing of the patchset.

Closes-bug: 1372808

Change-Id: I308b3b2dff315ec33451fb45a30ecd53d5d4c353
This commit is contained in:
Rushil Chugh
2014-11-18 14:37:17 -05:00
parent 5259bd7f60
commit 2d0204ff3c
6 changed files with 97 additions and 40 deletions

View File

@@ -58,7 +58,7 @@ FC_FABRIC_MAP = {'fabricB':
'initiator_port_wwn_list': ['21000024ff406cc3']}} 'initiator_port_wwn_list': ['21000024ff406cc3']}}
FC_TARGET_INFO = {'driver_volume_type': 'fibre_channel', FC_TARGET_INFO = {'driver_volume_type': 'fibre_channel',
'data': {'target_lun': '1', 'data': {'target_lun': 1,
'initiator_target_map': FC_I_T_MAP, 'initiator_target_map': FC_I_T_MAP,
'access_mode': 'rw', 'access_mode': 'rw',
'target_wwn': FC_TARGET_WWPNS, 'target_wwn': FC_TARGET_WWPNS,

View File

@@ -1,4 +1,5 @@
# Copyright (c) - 2014, Clinton Knight. All rights reserved. # Copyright (c) - 2014, Clinton Knight All rights reserved.
# Copyright (c) - 2014, Rushil Chugh All rights reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain # not use this file except in compliance with the License. You may obtain
@@ -42,3 +43,27 @@ def create_configuration_eseries():
config = create_configuration() config = create_configuration()
config.append_config_values(na_opts.netapp_eseries_opts) config.append_config_values(na_opts.netapp_eseries_opts)
return config return config
ISCSI_FAKE_LUN_ID = 1
ISCSI_FAKE_IQN = 'iqn.1993-08.org.debian:01:10'
ISCSI_FAKE_ADDRESS = '10.63.165.216'
ISCSI_FAKE_PORT = '2232'
ISCSI_FAKE_VOLUME = {'id': 'fake_id'}
ISCSI_FAKE_TARGET = {}
ISCSI_FAKE_TARGET['address'] = ISCSI_FAKE_ADDRESS
ISCSI_FAKE_TARGET['port'] = ISCSI_FAKE_PORT
ISCSI_FAKE_VOLUME = {'id': 'fake_id', 'provider_auth': 'None stack password'}
FC_ISCSI_TARGET_INFO_DICT = {'target_discovered': False,
'target_portal': '10.63.165.216:2232',
'target_iqn': ISCSI_FAKE_IQN,
'target_lun': ISCSI_FAKE_LUN_ID,
'volume_id': ISCSI_FAKE_VOLUME['id'],
'auth_method': 'None', 'auth_username': 'stack',
'auth_password': 'password'}

View File

@@ -24,6 +24,7 @@ from oslo.concurrency import processutils as putils
from cinder import exception from cinder import exception
from cinder import test from cinder import test
import cinder.tests.volume.drivers.netapp.fakes as fake
from cinder import version from cinder import version
import cinder.volume.drivers.netapp.utils as na_utils import cinder.volume.drivers.netapp.utils as na_utils
@@ -350,3 +351,34 @@ class OpenStackInfoTestCase(test.TestCase):
info._update_openstack_info() info._update_openstack_info()
self.assertTrue(mock_updt_from_dpkg.called) self.assertTrue(mock_updt_from_dpkg.called)
def test_iscsi_connection_properties(self):
actual_properties = na_utils.get_iscsi_connection_properties(
fake.ISCSI_FAKE_LUN_ID, fake.ISCSI_FAKE_VOLUME,
fake.ISCSI_FAKE_IQN, fake.ISCSI_FAKE_ADDRESS,
fake.ISCSI_FAKE_PORT)
actual_properties_mapped = actual_properties['data']
self.assertDictEqual(actual_properties_mapped,
fake.FC_ISCSI_TARGET_INFO_DICT)
def test_iscsi_connection_lun_id_type_str(self):
FAKE_LUN_ID = '1'
actual_properties = na_utils.get_iscsi_connection_properties(
FAKE_LUN_ID, fake.ISCSI_FAKE_VOLUME, fake.ISCSI_FAKE_IQN,
fake.ISCSI_FAKE_ADDRESS, fake.ISCSI_FAKE_PORT)
actual_properties_mapped = actual_properties['data']
self.assertIs(type(actual_properties_mapped['target_lun']), int)
def test_iscsi_connection_lun_id_type_dict(self):
FAKE_LUN_ID = {'id': 'fake_id'}
self.assertRaises(TypeError, na_utils.get_iscsi_connection_properties,
FAKE_LUN_ID, fake.ISCSI_FAKE_VOLUME,
fake.ISCSI_FAKE_IQN, fake.ISCSI_FAKE_ADDRESS,
fake.ISCSI_FAKE_PORT)

View File

@@ -519,7 +519,6 @@ class NetAppBlockStorageLibrary(object):
msg = _("Mapped LUN %(name)s to the initiator %(initiator_name)s") msg = _("Mapped LUN %(name)s to the initiator %(initiator_name)s")
msg_fmt = {'name': name, 'initiator_name': initiator_name} msg_fmt = {'name': name, 'initiator_name': initiator_name}
LOG.debug(msg % msg_fmt) LOG.debug(msg % msg_fmt)
iqn = self.zapi_client.get_iscsi_service_details()
target_details_list = self.zapi_client.get_iscsi_target_details() target_details_list = self.zapi_client.get_iscsi_target_details()
msg = _("Successfully fetched target details for LUN %(name)s and " msg = _("Successfully fetched target details for LUN %(name)s and "
"initiator %(initiator_name)s") "initiator %(initiator_name)s")
@@ -537,32 +536,22 @@ class NetAppBlockStorageLibrary(object):
if not target_details: if not target_details:
target_details = target_details_list[0] target_details = target_details_list[0]
(address, port) = (target_details['address'], target_details['port'])
if not target_details['address'] and target_details['port']: if not target_details['address'] and target_details['port']:
msg = _('Failed to get target portal for the LUN %s') msg = _('Failed to get target portal for the LUN %s')
raise exception.VolumeBackendAPIException(data=msg % name) raise exception.VolumeBackendAPIException(data=msg % name)
iqn = self.zapi_client.get_iscsi_service_details()
if not iqn: if not iqn:
msg = _('Failed to get target IQN for the LUN %s') msg = _('Failed to get target IQN for the LUN %s')
raise exception.VolumeBackendAPIException(data=msg % name) raise exception.VolumeBackendAPIException(data=msg % name)
properties = {} properties = na_utils.get_iscsi_connection_properties(lun_id, volume,
properties['target_discovered'] = False iqn, address,
(address, port) = (target_details['address'], target_details['port']) port)
properties['target_portal'] = '%s:%s' % (address, port)
properties['target_iqn'] = iqn
properties['target_lun'] = lun_id
properties['volume_id'] = volume['id']
auth = volume['provider_auth'] return properties
if auth:
(auth_method, auth_username, auth_secret) = auth.split()
properties['auth_method'] = auth_method
properties['auth_username'] = auth_username
properties['auth_password'] = auth_secret
return {
'driver_volume_type': 'iscsi',
'data': properties,
}
def terminate_connection_iscsi(self, volume, connector, **kwargs): def terminate_connection_iscsi(self, volume, connector, **kwargs):
"""Driver entry point to unattach a volume from an instance. """Driver entry point to unattach a volume from an instance.
@@ -648,7 +637,7 @@ class NetAppBlockStorageLibrary(object):
target_info = {'driver_volume_type': 'fibre_channel', target_info = {'driver_volume_type': 'fibre_channel',
'data': {'target_discovered': True, 'data': {'target_discovered': True,
'target_lun': lun_id, 'target_lun': int(lun_id),
'target_wwn': target_wwpns, 'target_wwn': target_wwpns,
'access_mode': 'rw', 'access_mode': 'rw',
'initiator_target_map': initiator_target_map}} 'initiator_target_map': initiator_target_map}}

View File

@@ -556,7 +556,7 @@ class NetAppEseriesISCSIDriver(driver.ISCSIDriver):
initiator_name = connector['initiator'] initiator_name = connector['initiator']
vol = self._get_latest_volume(volume['name_id']) vol = self._get_latest_volume(volume['name_id'])
iscsi_details = self._get_iscsi_service_details() iscsi_details = self._get_iscsi_service_details()
iscsi_det = self._get_iscsi_portal_for_vol(vol, iscsi_details) iscsi_portal = self._get_iscsi_portal_for_vol(vol, iscsi_details)
mapping = self._map_volume_to_host(vol, initiator_name) mapping = self._map_volume_to_host(vol, initiator_name)
lun_id = mapping['lun'] lun_id = mapping['lun']
self._cache_vol_mapping(mapping) self._cache_vol_mapping(mapping)
@@ -566,23 +566,13 @@ class NetAppEseriesISCSIDriver(driver.ISCSIDriver):
msg = _("Successfully fetched target details for volume %(id)s and " msg = _("Successfully fetched target details for volume %(id)s and "
"initiator %(initiator_name)s.") "initiator %(initiator_name)s.")
LOG.debug(msg % msg_fmt) LOG.debug(msg % msg_fmt)
properties = {} iqn = iscsi_portal['iqn']
properties['target_discovered'] = False address = iscsi_portal['ip']
properties['target_portal'] = '%s:%s' % (iscsi_det['ip'], port = iscsi_portal['tcp_port']
iscsi_det['tcp_port']) properties = na_utils.get_iscsi_connection_properties(lun_id, volume,
properties['target_iqn'] = iscsi_det['iqn'] iqn, address,
properties['target_lun'] = lun_id port)
properties['volume_id'] = volume['id'] return properties
auth = volume['provider_auth']
if auth:
(auth_method, auth_username, auth_secret) = auth.split()
properties['auth_method'] = auth_method
properties['auth_username'] = auth_username
properties['auth_password'] = auth_secret
return {
'driver_volume_type': 'iscsi',
'data': properties,
}
def _get_iscsi_service_details(self): def _get_iscsi_service_details(self):
"""Gets iscsi iqn, ip and port information.""" """Gets iscsi iqn, ip and port information."""

View File

@@ -139,6 +139,27 @@ def log_extra_spec_warnings(extra_specs):
LOG.warning(msg % args) LOG.warning(msg % args)
def get_iscsi_connection_properties(lun_id, volume, iqn,
address, port):
properties = {}
properties['target_discovered'] = False
properties['target_portal'] = '%s:%s' % (address, port)
properties['target_iqn'] = iqn
properties['target_lun'] = int(lun_id)
properties['volume_id'] = volume['id']
auth = volume['provider_auth']
if auth:
(auth_method, auth_username, auth_secret) = auth.split()
properties['auth_method'] = auth_method
properties['auth_username'] = auth_username
properties['auth_password'] = auth_secret
return {
'driver_volume_type': 'iscsi',
'data': properties,
}
class hashabledict(dict): class hashabledict(dict):
"""A hashable dictionary that is comparable (i.e. in unit tests, etc.)""" """A hashable dictionary that is comparable (i.e. in unit tests, etc.)"""
def __hash__(self): def __hash__(self):