Merge "ISCSI volume attachment fixes"
This commit is contained in:
commit
d0155cb0dc
|
@ -144,7 +144,16 @@ vol_adapter_opts = [
|
|||
default=1, min=1,
|
||||
help='Indicates a minimum number of Virtual I/O Servers that '
|
||||
'are required to support a Cinder volume attach with the '
|
||||
'vSCSI volume connector.')
|
||||
'vSCSI volume connector.'),
|
||||
cfg.BoolOpt('volume_use_multipath',
|
||||
default=False,
|
||||
help="Use multipath connections when attaching iSCSI or FC"),
|
||||
cfg.StrOpt('iscsi_iface',
|
||||
default='default',
|
||||
help="The iSCSI transport iface to use to connect to target in "
|
||||
"case offload support is desired. Do not confuse the "
|
||||
"iscsi_iface parameter to be provided here with the "
|
||||
"actual transport name.")
|
||||
]
|
||||
|
||||
# NPIV Options. Only applicable if the 'fc_attach_strategy' is set to 'npiv'.
|
||||
|
|
|
@ -108,7 +108,8 @@ class TestSwiftSlotManager(test.NoDBTestCase):
|
|||
self.slot_mgr.init_recreate_map(mock.Mock(), self._vol_drv_iter())
|
||||
self.assertEqual(1, mock_ftsk.call_count)
|
||||
mock_rebuild_slot.assert_called_once_with(
|
||||
self.slot_mgr, mock.ANY, {'udid': ['uuid2']}, ['a', 'b'])
|
||||
self.slot_mgr, mock.ANY, {'udid': ['uuid2'], 'iscsi': ['uuid1']},
|
||||
['a', 'b'])
|
||||
|
||||
@mock.patch('pypowervm.tasks.slot_map.RebuildSlotMap')
|
||||
@mock.patch('pypowervm.tasks.storage.ComprehensiveScrub')
|
||||
|
@ -152,6 +153,10 @@ class TestSwiftSlotManager(test.NoDBTestCase):
|
|||
mock_scsi.vol_type.return_value = 'vscsi'
|
||||
mock_scsi.is_volume_on_vios.side_effect = ((False, None),
|
||||
(True, 'udid'))
|
||||
mock_iscsi = mock.Mock()
|
||||
mock_iscsi.vol_type.return_value = 'iscsi'
|
||||
mock_iscsi.is_volume_on_vios.side_effect = ((True, 'iscsi'),
|
||||
(False, None))
|
||||
|
||||
mock_npiv1 = mock.Mock()
|
||||
mock_npiv1.vol_type.return_value = 'npiv'
|
||||
|
@ -161,6 +166,6 @@ class TestSwiftSlotManager(test.NoDBTestCase):
|
|||
mock_npiv2.vol_type.return_value = 'npiv'
|
||||
mock_npiv2._fabric_names.return_value = ['a', 'b', 'c']
|
||||
|
||||
vol_drv = [mock_scsi, mock_npiv1, mock_npiv2]
|
||||
vol_drv = [mock_scsi, mock_npiv1, mock_npiv2, mock_iscsi]
|
||||
for type in vol_drv:
|
||||
yield mock.Mock(), type
|
||||
|
|
|
@ -33,7 +33,7 @@ from pypowervm.wrappers import virtual_io_server as pvm_vios
|
|||
|
||||
CONF = cfg.CONF
|
||||
|
||||
VIOS_FEED = 'fake_vios_feed2.txt'
|
||||
VIOS_FEED = 'fake_vios_feed.txt'
|
||||
|
||||
|
||||
class TestISCSIAdapter(test_vol.TestVolumeAdapter):
|
||||
|
@ -59,16 +59,16 @@ class TestISCSIAdapter(test_vol.TestVolumeAdapter):
|
|||
autospec=True)
|
||||
def init_vol_adpt(mock_initiator, mock_mgmt_part, mock_pvm_uuid,
|
||||
mock_vios):
|
||||
self.trans_type = 'iscsi'
|
||||
self.iqn = 'iqn.2016-08.bar.foo:target'
|
||||
self.lun = '1'
|
||||
self.iqn = 'iqn.2016-08.com.foo:bar'
|
||||
self.lun = 1
|
||||
self.host_ip = '10.0.0.1'
|
||||
self.user = 'user'
|
||||
self.password = 'password'
|
||||
self.serial = 'f042c68a-c5a5-476a-ba34-2f6d43f4226c'
|
||||
con_info = {
|
||||
'serial': self.serial,
|
||||
'driver_volume_type': self.trans_type,
|
||||
'driver_volume_type': 'iscsi',
|
||||
'connector': {},
|
||||
'data': {
|
||||
'target_iqn': self.iqn,
|
||||
'target_lun': self.lun,
|
||||
|
@ -77,15 +77,38 @@ class TestISCSIAdapter(test_vol.TestVolumeAdapter):
|
|||
'auth_password': self.password
|
||||
},
|
||||
}
|
||||
self.auth_method = 'CHAP'
|
||||
multi_con_info = {
|
||||
'serial': self.serial,
|
||||
'driver_volume_type': 'iser',
|
||||
'connector': {'multipath': True},
|
||||
'data': {
|
||||
'target_iqn': self.iqn,
|
||||
'target_lun': self.lun,
|
||||
'target_portal': self.host_ip,
|
||||
'auth_method': self.auth_method,
|
||||
'auth_username': self.user,
|
||||
'auth_password': self.password,
|
||||
'discovery_auth_method': self.auth_method,
|
||||
'discovery_auth_username': self.user,
|
||||
'discovery_auth_password': self.password,
|
||||
'target_iqns': [self.iqn],
|
||||
'target_luns': [self.lun],
|
||||
'target_portals': [self.host_ip]
|
||||
},
|
||||
}
|
||||
mock_inst = mock.MagicMock()
|
||||
mock_pvm_uuid.return_value = '1234'
|
||||
mock_initiator.return_value = 'initiatior iqn'
|
||||
mock_initiator.return_value = 'initiator iqn'
|
||||
# The getter can just return the VIOS values (to remove a read
|
||||
# that would otherwise need to be mocked).
|
||||
mock_vios.getter.return_value = self.feed
|
||||
return iscsi.IscsiVolumeAdapter(self.adpt, 'host_uuid', mock_inst,
|
||||
con_info)
|
||||
self.vol_drv = init_vol_adpt()
|
||||
single_path = iscsi.IscsiVolumeAdapter(self.adpt, 'host_uuid',
|
||||
mock_inst, con_info)
|
||||
multi_path = iscsi.IscsiVolumeAdapter(self.adpt, 'host_uuid',
|
||||
mock_inst, multi_con_info)
|
||||
return single_path, multi_path
|
||||
self.vol_drv, self.multi_vol_drv = init_vol_adpt()
|
||||
|
||||
# setup system_metadata tests
|
||||
self.devname = "/dev/fake"
|
||||
|
@ -114,21 +137,66 @@ class TestISCSIAdapter(test_vol.TestVolumeAdapter):
|
|||
self.assertIsInstance(pv, pvm_stor.PV)
|
||||
self.assertEqual(62, lpar_slot_num)
|
||||
self.assertEqual('the_lua', lua)
|
||||
self.assertEqual('ISCSI-target', target_name)
|
||||
self.assertEqual('ISCSI-bar_%s' % self.lun, target_name)
|
||||
return 'fake_map'
|
||||
|
||||
mock_build_map.side_effect = build_map_func
|
||||
|
||||
# Run the method
|
||||
self.vol_drv.connect_volume(self.slot_mgr)
|
||||
|
||||
# As initialized above, remove_maps returns True to trigger update.
|
||||
self.assertEqual(1, mock_add_map.call_count)
|
||||
self.assertEqual(1, self.ft_fx.patchers['update'].mock.call_count)
|
||||
self.assertEqual(1, mock_build_map.call_count)
|
||||
mock_discover.assert_called_with(
|
||||
self.adpt, self.host_ip, self.user, self.password, self.iqn,
|
||||
self.feed[0].uuid, transport_type=self.trans_type)
|
||||
self.assertEqual(2, mock_add_map.call_count)
|
||||
self.assertEqual(2, self.ft_fx.patchers['update'].mock.call_count)
|
||||
self.assertEqual(2, mock_build_map.call_count)
|
||||
|
||||
calls = [mock.call(self.adpt, self.host_ip, self.user, self.password,
|
||||
self.iqn, self.feed[0].uuid, lunid=self.lun,
|
||||
multipath=False, iface_name='default',
|
||||
discovery_auth=None, discovery_username=None,
|
||||
auth=None, discovery_password=None),
|
||||
mock.call(self.adpt, self.host_ip, self.user, self.password,
|
||||
self.iqn, self.feed[1].uuid, lunid=self.lun,
|
||||
multipath=False, iface_name='default',
|
||||
discovery_auth=None, discovery_username=None,
|
||||
auth=None, discovery_password=None)]
|
||||
multi_calls = [
|
||||
mock.call(self.adpt, [self.host_ip], self.user, self.password,
|
||||
[self.iqn], self.feed[0].uuid, lunid=[self.lun],
|
||||
iface_name='iser', multipath=True,
|
||||
auth=self.auth_method, discovery_auth=self.auth_method,
|
||||
discovery_username=self.user,
|
||||
discovery_password=self.password),
|
||||
mock.call(self.adpt, [self.host_ip], self.user, self.password,
|
||||
[self.iqn], self.feed[1].uuid, lunid=[self.lun],
|
||||
iface_name='iser', multipath=True,
|
||||
auth=self.auth_method, discovery_auth=self.auth_method,
|
||||
discovery_username=self.user,
|
||||
discovery_password=self.password)]
|
||||
mock_discover.assert_has_calls(calls, any_order=True)
|
||||
self.multi_vol_drv.connect_volume(self.slot_mgr)
|
||||
mock_discover.assert_has_calls(multi_calls, any_order=True)
|
||||
|
||||
@mock.patch('pypowervm.tasks.hdisk.discover_iscsi', autospec=True)
|
||||
@mock.patch('nova_powervm.virt.powervm.vm.get_vm_id')
|
||||
def test_connect_volume_discover_fail(self, mock_get_vm_id, mock_discover):
|
||||
mock_get_vm_id.return_value = '2'
|
||||
mock_discover.side_effect = pvm_exc.ISCSIDiscoveryFailed(
|
||||
vios_uuid='fake_vios', status='fake_status')
|
||||
|
||||
# Run the method
|
||||
self.assertRaises(pvm_exc.MultipleExceptionsInFeedTask,
|
||||
self.vol_drv.connect_volume, self.slot_mgr)
|
||||
|
||||
@mock.patch('pypowervm.tasks.hdisk.discover_iscsi', autospec=True)
|
||||
@mock.patch('nova_powervm.virt.powervm.vm.get_vm_id')
|
||||
def test_connect_volume_job_fail(self, mock_get_vm_id, mock_discover):
|
||||
mock_get_vm_id.return_value = '2'
|
||||
mock_discover.side_effect = pvm_exc.JobRequestFailed(
|
||||
operation_name='ISCSIDiscovery', error='fake_err')
|
||||
|
||||
# Run the method
|
||||
self.assertRaises(pvm_exc.MultipleExceptionsInFeedTask,
|
||||
self.vol_drv.connect_volume, self.slot_mgr)
|
||||
|
||||
@mock.patch('pypowervm.tasks.partition.get_active_vioses', autospec=True)
|
||||
@mock.patch('pypowervm.tasks.storage.rescan_vstor', autospec=True)
|
||||
|
@ -172,12 +240,13 @@ class TestISCSIAdapter(test_vol.TestVolumeAdapter):
|
|||
mock_hdisk_from_uuid, mock_remove_iscsi):
|
||||
# The mock return values
|
||||
mock_hdisk_from_uuid.return_value = 'device_name'
|
||||
mock_get_vm_id.return_value = 'partition_id'
|
||||
mock_get_vm_id.return_value = '2'
|
||||
self.vol_drv._set_devname('/dev/fake')
|
||||
self.multi_vol_drv._set_devname('/dev/fake')
|
||||
|
||||
def validate_remove_maps(vios_w, vm_uuid, match_func):
|
||||
self.assertIsInstance(vios_w, pvm_vios.VIOS)
|
||||
self.assertEqual('partition_id', vm_uuid)
|
||||
self.assertEqual('2', vm_uuid)
|
||||
return 'removed'
|
||||
mock_remove_maps.side_effect = validate_remove_maps
|
||||
|
||||
|
@ -185,11 +254,24 @@ class TestISCSIAdapter(test_vol.TestVolumeAdapter):
|
|||
self.vol_drv.disconnect_volume(self.slot_mgr)
|
||||
|
||||
# As initialized above, remove_maps returns True to trigger update.
|
||||
self.assertEqual(1, mock_remove_maps.call_count)
|
||||
fake_iqn = self.vol_drv.connection_info['data']['target_iqn']
|
||||
mock_remove_iscsi.assert_called_once_with(
|
||||
self.adpt, fake_iqn, '3443DB77-AED1-47ED-9AA5-3DB9C6CF7089')
|
||||
self.assertEqual(1, self.ft_fx.patchers['update'].mock.call_count)
|
||||
self.assertEqual(2, mock_remove_maps.call_count)
|
||||
self.assertEqual(2, self.ft_fx.patchers['update'].mock.call_count)
|
||||
calls = [mock.call(self.adpt, self.iqn, self.feed[0].uuid,
|
||||
lun=self.lun, iface_name='default',
|
||||
portal=self.host_ip, multipath=False),
|
||||
mock.call(self.adpt, self.iqn, self.feed[1].uuid,
|
||||
lun=self.lun, iface_name='default',
|
||||
portal=self.host_ip, multipath=False)]
|
||||
multi_calls = [mock.call(self.adpt, [self.iqn], self.feed[0].uuid,
|
||||
lun=[self.lun], iface_name='iser',
|
||||
portal=[self.host_ip], multipath=True),
|
||||
mock.call(self.adpt, [self.iqn], self.feed[1].uuid,
|
||||
lun=[self.lun], iface_name='iser',
|
||||
portal=[self.host_ip], multipath=True)]
|
||||
mock_remove_iscsi.assert_has_calls(calls, any_order=True)
|
||||
mock_remove_iscsi.reset_mock()
|
||||
self.multi_vol_drv.disconnect_volume(self.slot_mgr)
|
||||
mock_remove_iscsi.assert_has_calls(multi_calls, any_order=True)
|
||||
|
||||
def test_min_xags(self):
|
||||
xags = self.vol_drv.min_xags()
|
||||
|
|
|
@ -1106,6 +1106,7 @@ class PowerVMDriver(driver.ComputeDriver):
|
|||
self.adapter, self.host_uuid, instance)
|
||||
if wwpn_list is not None:
|
||||
connector["wwpns"] = wwpn_list
|
||||
connector["multipath"] = CONF.powervm.volume_use_multipath
|
||||
connector['host'] = vol_attach.get_hostname_for_volume(instance)
|
||||
connector['initiator'] = vol_attach.get_iscsi_initiator(self.adapter)
|
||||
return connector
|
||||
|
|
|
@ -28,6 +28,7 @@ from nova_powervm.virt.powervm import exception as p_exc
|
|||
LOG = logging.getLogger(__name__)
|
||||
|
||||
_SLOT_KEY = "CLIENT_SLOT_DATA"
|
||||
_SLOT_VOLUME_TYPES = ['vscsi', 'fileio', 'rbd', 'iscsi']
|
||||
|
||||
|
||||
def build_slot_mgr(instance, store_api, adapter=None, vol_drv_iter=None):
|
||||
|
@ -124,7 +125,7 @@ class NovaSlotManager(slot_map.SlotMapStore):
|
|||
pv_vscsi_vol_to_vio = {}
|
||||
fabric_names = []
|
||||
for bdm, vol_drv in vol_drv_iter:
|
||||
if vol_drv.vol_type() in ['vscsi', 'fileio', 'rbd']:
|
||||
if vol_drv.vol_type() in _SLOT_VOLUME_TYPES:
|
||||
self._pv_vscsi_vol_to_vio(vol_drv, pv_vscsi_vol_to_vio)
|
||||
elif len(fabric_names) == 0 and vol_drv.vol_type() == 'npiv':
|
||||
fabric_names = vol_drv._fabric_names()
|
||||
|
|
|
@ -37,6 +37,8 @@ FC_STRATEGY_MAPPING = {
|
|||
_STATIC_VOLUME_MAPPINGS = {
|
||||
'iscsi': 'nova_powervm.virt.powervm.volume.iscsi.'
|
||||
'IscsiVolumeAdapter',
|
||||
'iser': 'nova_powervm.virt.powervm.volume.iscsi.'
|
||||
'IscsiVolumeAdapter',
|
||||
'local': 'nova_powervm.virt.powervm.volume.local.'
|
||||
'LocalVolumeAdapter',
|
||||
'nfs': 'nova_powervm.virt.powervm.volume.nfs.NFSVolumeAdapter',
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import copy
|
||||
from oslo_concurrency import lockutils
|
||||
from oslo_log import log as logging
|
||||
|
||||
|
@ -23,6 +24,7 @@ from nova_powervm.virt.powervm import vm
|
|||
from nova_powervm.virt.powervm.volume import driver as v_driver
|
||||
from nova_powervm.virt.powervm.volume import volume as volume
|
||||
from pypowervm import const as pvm_const
|
||||
from pypowervm import exceptions as pvm_exc
|
||||
from pypowervm.tasks import hdisk
|
||||
from pypowervm.utils import transaction as tx
|
||||
from pypowervm.wrappers import virtual_io_server as pvm_vios
|
||||
|
@ -49,6 +51,10 @@ class IscsiVolumeAdapter(volume.VscsiVolumeAdapter,
|
|||
stg_ftsk=None):
|
||||
super(IscsiVolumeAdapter, self).__init__(
|
||||
adapter, host_uuid, instance, connection_info, stg_ftsk=stg_ftsk)
|
||||
if connection_info['driver_volume_type'] == 'iser':
|
||||
self.iface_name = 'iser'
|
||||
else:
|
||||
self.iface_name = CONF.powervm.iscsi_iface
|
||||
|
||||
@classmethod
|
||||
def vol_type(cls):
|
||||
|
@ -80,6 +86,58 @@ class IscsiVolumeAdapter(volume.VscsiVolumeAdapter,
|
|||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def is_volume_on_vios(self, vios_w):
|
||||
"""Returns whether or not the volume is on a VIOS.
|
||||
|
||||
:param vios_w: The Virtual I/O Server wrapper.
|
||||
:return: True if the volume driver's volume is on the VIOS. False
|
||||
otherwise.
|
||||
:return: The udid of the device.
|
||||
"""
|
||||
device_name, udid = self._discover_volume_on_vios(vios_w)
|
||||
return (device_name and udid) is not None, udid
|
||||
|
||||
def _is_multipath(self):
|
||||
return self.connection_info["connector"].get("multipath", False)
|
||||
|
||||
def _discover_vol(self, vios_w, props):
|
||||
portal = props.get("target_portals", props.get("target_portal"))
|
||||
iqn = props.get("target_iqns", props.get("target_iqn"))
|
||||
lun = props.get("target_luns", props.get("target_lun"))
|
||||
auth = props.get("auth_method")
|
||||
user = props.get("auth_username")
|
||||
password = props.get("auth_password")
|
||||
discovery_auth = props.get("discovery_auth_method")
|
||||
discovery_username = props.get("discovery_auth_username")
|
||||
discovery_password = props.get("discovery_auth_password")
|
||||
try:
|
||||
return hdisk.discover_iscsi(
|
||||
self.adapter, portal, user, password, iqn, vios_w.uuid,
|
||||
lunid=lun, iface_name=self.iface_name, auth=auth,
|
||||
discovery_auth=discovery_auth,
|
||||
discovery_username=discovery_username,
|
||||
discovery_password=discovery_password,
|
||||
multipath=self._is_multipath())
|
||||
except (pvm_exc.ISCSIDiscoveryFailed, pvm_exc.JobRequestFailed) as e:
|
||||
LOG.warning(e)
|
||||
|
||||
def _discover_volume_on_vios(self, vios_w):
|
||||
"""Discovers an hdisk on a single vios for the volume.
|
||||
|
||||
:param vios_w: VIOS wrapper to process
|
||||
:returns: Device name or None
|
||||
:returns: LUN or None
|
||||
"""
|
||||
device_name = udid = None
|
||||
if self._is_multipath():
|
||||
device_name, udid = self._discover_vol(
|
||||
vios_w, self.connection_info["data"])
|
||||
else:
|
||||
for props in self._iterate_all_targets(
|
||||
self.connection_info["data"]):
|
||||
device_name, udid = self._discover_vol(vios_w, props)
|
||||
return device_name, udid
|
||||
|
||||
def _connect_volume_to_vio(self, vios_w, slot_mgr):
|
||||
"""Attempts to connect a volume to a given VIO.
|
||||
|
||||
|
@ -91,18 +149,13 @@ class IscsiVolumeAdapter(volume.VscsiVolumeAdapter,
|
|||
not (could be the Virtual I/O Server does not have
|
||||
connectivity to the hdisk).
|
||||
"""
|
||||
transport_type = self.connection_info["driver_volume_type"]
|
||||
host_ip = self.connection_info["data"]["target_portal"]
|
||||
iqn = self.connection_info["data"]["target_iqn"]
|
||||
password = self.connection_info["data"]["auth_password"]
|
||||
user = self.connection_info["data"]["auth_username"]
|
||||
target_name = "ISCSI-" + iqn.split(":")[1]
|
||||
device_name, udid = hdisk.discover_iscsi(
|
||||
self.adapter, host_ip, user, password, iqn, vios_w.uuid,
|
||||
transport_type=transport_type)
|
||||
slot, lua = slot_mgr.build_map.get_vscsi_slot(vios_w, device_name)
|
||||
device_name, udid = self._discover_volume_on_vios(vios_w)
|
||||
if device_name is not None and udid is not None:
|
||||
slot, lua = slot_mgr.build_map.get_vscsi_slot(vios_w, device_name)
|
||||
device_name = '/dev/' + device_name
|
||||
iqn = self.connection_info["data"]["target_iqn"]
|
||||
lun = self.connection_info["data"]["target_lun"]
|
||||
target_name = "ISCSI-%s_%s" % (iqn.split(":")[1], str(lun))
|
||||
# Found a hdisk on this Virtual I/O Server. Add the action to
|
||||
# map it to the VM when the stg_ftsk is executed.
|
||||
with lockutils.lock(hash(self)):
|
||||
|
@ -191,14 +244,30 @@ class IscsiVolumeAdapter(volume.VscsiVolumeAdapter,
|
|||
with lockutils.lock(hash(self)):
|
||||
self._add_remove_mapping(partition_id, vios_w.uuid,
|
||||
device_name, slot_mgr)
|
||||
target_iqn = self.connection_info["data"]["target_iqn"]
|
||||
conn_data = self.connection_info["data"]
|
||||
iqn = conn_data.get("target_iqns", conn_data.get("target_iqn"))
|
||||
portal = conn_data.get("target_portals",
|
||||
conn_data.get("target_portal"))
|
||||
lun = conn_data.get("target_luns",
|
||||
conn_data.get("target_lun"))
|
||||
|
||||
def remove():
|
||||
try:
|
||||
hdisk.remove_iscsi(
|
||||
self.adapter, iqn, vios_w.uuid, lun=lun,
|
||||
iface_name=self.iface_name, portal=portal,
|
||||
multipath=self._is_multipath())
|
||||
except (pvm_exc.ISCSIRemoveFailed,
|
||||
pvm_exc.JobRequestFailed) as e:
|
||||
LOG.warning(e)
|
||||
|
||||
def logout():
|
||||
hdisk.remove_iscsi(self.adapter, target_iqn, vios_w.uuid)
|
||||
self.stg_ftsk.add_post_execute(task.FunctorTask(
|
||||
logout, name='remove_iSCSI_%s' % target_iqn))
|
||||
remove, name='remove_%s_from_vios_%s' % (device_name,
|
||||
vios_w.uuid)))
|
||||
|
||||
# Found a valid element to remove
|
||||
return True
|
||||
|
||||
try:
|
||||
# See logic in _connect_volume for why this new FeedTask is here.
|
||||
discon_ftsk = tx.FeedTask(
|
||||
|
@ -223,3 +292,26 @@ class IscsiVolumeAdapter(volume.VscsiVolumeAdapter,
|
|||
ex_args = {'volume_id': self.volume_id, 'reason': six.text_type(e),
|
||||
'instance_name': self.instance.name}
|
||||
raise p_exc.VolumeDetachFailed(**ex_args)
|
||||
|
||||
# Taken from os_brick.initiator.connectors.base_iscsi.py
|
||||
def _iterate_all_targets(self, connection_properties):
|
||||
for portal, iqn, lun in self._get_all_targets(connection_properties):
|
||||
props = copy.deepcopy(connection_properties)
|
||||
props['target_portal'] = portal
|
||||
props['target_iqn'] = iqn
|
||||
props['target_lun'] = lun
|
||||
for key in ('target_portals', 'target_iqns', 'target_luns'):
|
||||
props.pop(key, None)
|
||||
yield props
|
||||
|
||||
def _get_all_targets(self, connection_properties):
|
||||
if all([key in connection_properties for key in ('target_portals',
|
||||
'target_iqns',
|
||||
'target_luns')]):
|
||||
return zip(connection_properties['target_portals'],
|
||||
connection_properties['target_iqns'],
|
||||
connection_properties['target_luns'])
|
||||
|
||||
return [(connection_properties['target_portal'],
|
||||
connection_properties['target_iqn'],
|
||||
connection_properties.get('target_lun', 0))]
|
||||
|
|
Loading…
Reference in New Issue