Return iSCSI Initiator for VIOSes
Adding the get_iscsi_initiators method in iscsi volume adapter to discover all iSCSI initiators for active VIOSes and return the first initiator from the sorted collection of initiators. Change-Id: Ic842bd49e0a3dc6e4f4a3bf14409ca2f70369a26
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
# Copyright 2014, 2017 IBM Corp.
|
# Copyright 2014, 2018 IBM Corp.
|
||||||
#
|
#
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
import collections
|
||||||
import fixtures
|
import fixtures
|
||||||
import logging
|
import logging
|
||||||
import mock
|
import mock
|
||||||
@@ -206,21 +206,21 @@ class TestPowerVMDriver(test.NoDBTestCase):
|
|||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
self.drv.session.get_event_listener.return_value.shutdown.called)
|
self.drv.session.get_event_listener.return_value.shutdown.called)
|
||||||
|
|
||||||
@mock.patch('nova_powervm.virt.powervm.volume.get_iscsi_initiator',
|
@mock.patch('nova_powervm.virt.powervm.volume.iscsi.get_iscsi_initiators')
|
||||||
autospec=True)
|
def test_get_volume_connector(self, mock_initiators):
|
||||||
def test_get_volume_connector(self, mock_initiator):
|
|
||||||
"""Tests that a volume connector can be built."""
|
"""Tests that a volume connector can be built."""
|
||||||
mock_initiator.return_value = 'iscsi_initiator'
|
|
||||||
|
initiators = [('1300C76F-9814-4A4D-B1F0-5B69352A7DEA', 'fake_iqn1'),
|
||||||
|
('7DBBE705-E4C4-4458-8223-3EBE07015CA9', 'fake_iqn2')]
|
||||||
|
initiators = collections.OrderedDict(initiators)
|
||||||
|
|
||||||
|
mock_initiators.return_value = initiators
|
||||||
|
|
||||||
self.flags(volume_adapter='fibre_channel', group='powervm')
|
self.flags(volume_adapter='fibre_channel', group='powervm')
|
||||||
vol_connector = self.drv.get_volume_connector(mock.Mock())
|
vol_connector = self.drv.get_volume_connector(mock.Mock())
|
||||||
self.assertIsNotNone(vol_connector['wwpns'])
|
self.assertIsNotNone(vol_connector['wwpns'])
|
||||||
self.assertIsNotNone(vol_connector['host'])
|
self.assertIsNotNone(vol_connector['host'])
|
||||||
self.assertEqual('iscsi_initiator', vol_connector['initiator'])
|
self.assertEqual('fake_iqn1', vol_connector['initiator'])
|
||||||
|
|
||||||
self.flags(volume_adapter='iscsi', group='powervm')
|
|
||||||
vol_connector = self.drv.get_volume_connector(mock.Mock())
|
|
||||||
self.assertEqual('iscsi_initiator', vol_connector['initiator'])
|
|
||||||
|
|
||||||
def test_setup_disk_adapter(self):
|
def test_setup_disk_adapter(self):
|
||||||
# Ensure we can handle upper case option and we instantiate the class
|
# Ensure we can handle upper case option and we instantiate the class
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright 2015, 2017 IBM Corp.
|
# Copyright 2015, 2018 IBM Corp.
|
||||||
#
|
#
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
@@ -15,7 +15,6 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
from pypowervm.wrappers import virtual_io_server as pvm_vios
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from nova import test
|
from nova import test
|
||||||
@@ -54,34 +53,6 @@ class TestInitMethods(test.NoDBTestCase):
|
|||||||
'gpfs': gpfs.GPFSVolumeAdapter,
|
'gpfs': gpfs.GPFSVolumeAdapter,
|
||||||
}
|
}
|
||||||
|
|
||||||
@mock.patch('pypowervm.tasks.hdisk.discover_iscsi_initiator')
|
|
||||||
@mock.patch('pypowervm.tasks.partition.get_mgmt_partition')
|
|
||||||
def test_get_iscsi_initiator(self, mock_mgmt, mock_iscsi_init):
|
|
||||||
# Set up mocks and clear out data that may have been set by other
|
|
||||||
# tests
|
|
||||||
mock_adpt = mock.Mock()
|
|
||||||
mock_mgmt.return_value = mock.Mock(spec=pvm_vios.VIOS)
|
|
||||||
mock_iscsi_init.return_value = 'test_initiator'
|
|
||||||
|
|
||||||
self.assertEqual('test_initiator',
|
|
||||||
volume.get_iscsi_initiator(mock_adpt))
|
|
||||||
|
|
||||||
# Make sure it gets set properly in the backend
|
|
||||||
self.assertEqual('test_initiator', volume._ISCSI_INITIATOR)
|
|
||||||
self.assertTrue(volume._ISCSI_LOOKUP_COMPLETE)
|
|
||||||
mock_mgmt.assert_called_once_with(mock_adpt)
|
|
||||||
self.assertEqual(1, mock_mgmt.call_count)
|
|
||||||
|
|
||||||
# Invoke again, make sure it doesn't call down to the mgmt part again
|
|
||||||
self.assertEqual('test_initiator',
|
|
||||||
volume.get_iscsi_initiator(mock_adpt))
|
|
||||||
self.assertEqual(1, mock_mgmt.call_count)
|
|
||||||
|
|
||||||
# Check if initiator returned does not have newline character
|
|
||||||
mock_iscsi_init.return_value = 'test_initiator\n'
|
|
||||||
self.assertEqual('test_initiator',
|
|
||||||
volume.get_iscsi_initiator(mock_adpt))
|
|
||||||
|
|
||||||
def test_get_volume_class(self):
|
def test_get_volume_class(self):
|
||||||
for vol_type, class_type in six.iteritems(self.volume_drivers):
|
for vol_type, class_type in six.iteritems(self.volume_drivers):
|
||||||
self.assertEqual(class_type, volume.get_volume_class(vol_type))
|
self.assertEqual(class_type, volume.get_volume_class(vol_type))
|
||||||
|
|||||||
@@ -392,3 +392,64 @@ class TestISCSIAdapter(test_vol.TestVolumeAdapter):
|
|||||||
retrieved_devname = self.vol_drv._get_devname()
|
retrieved_devname = self.vol_drv._get_devname()
|
||||||
# Check key not found
|
# Check key not found
|
||||||
self.assertIsNone(retrieved_devname)
|
self.assertIsNone(retrieved_devname)
|
||||||
|
|
||||||
|
@mock.patch('pypowervm.tasks.partition.get_active_vioses')
|
||||||
|
@mock.patch('pypowervm.tasks.hdisk.discover_iscsi_initiator')
|
||||||
|
def test_get_iscsi_initiators(self, mock_iscsi_init, mock_active_vioses):
|
||||||
|
# Set up mocks and clear out data that may have been set by other
|
||||||
|
# tests
|
||||||
|
iscsi._ISCSI_INITIATORS = dict()
|
||||||
|
mock_iscsi_init.return_value = 'test_initiator'
|
||||||
|
|
||||||
|
vios_ids = ['1300C76F-9814-4A4D-B1F0-5B69352A7DEA',
|
||||||
|
'7DBBE705-E4C4-4458-8223-3EBE07015CA9']
|
||||||
|
|
||||||
|
mock_active_vioses.return_value = vios_ids
|
||||||
|
|
||||||
|
expected_output = {
|
||||||
|
'1300C76F-9814-4A4D-B1F0-5B69352A7DEA': 'test_initiator',
|
||||||
|
'7DBBE705-E4C4-4458-8223-3EBE07015CA9': 'test_initiator'
|
||||||
|
}
|
||||||
|
|
||||||
|
self.assertEqual(expected_output,
|
||||||
|
iscsi.get_iscsi_initiators(self.adpt, vios_ids))
|
||||||
|
|
||||||
|
# Make sure it gets set properly in the backend
|
||||||
|
self.assertEqual(expected_output, iscsi._ISCSI_INITIATORS)
|
||||||
|
self.assertEqual(mock_active_vioses.call_count, 0)
|
||||||
|
self.assertEqual(mock_iscsi_init.call_count, 2)
|
||||||
|
|
||||||
|
# Invoke again, make sure it doesn't call down to the mgmt part again
|
||||||
|
mock_iscsi_init.reset_mock()
|
||||||
|
self.assertEqual(expected_output,
|
||||||
|
iscsi.get_iscsi_initiators(self.adpt, vios_ids))
|
||||||
|
self.assertEqual(mock_active_vioses.call_count, 0)
|
||||||
|
self.assertEqual(mock_iscsi_init.call_count, 0)
|
||||||
|
|
||||||
|
# Invoke iscsi.get_iscsi_initiators with vios_id=None
|
||||||
|
iscsi._ISCSI_INITIATORS = dict()
|
||||||
|
mock_iscsi_init.reset_mock()
|
||||||
|
self.assertEqual(expected_output,
|
||||||
|
iscsi.get_iscsi_initiators(self.adpt, None))
|
||||||
|
self.assertEqual(expected_output, iscsi._ISCSI_INITIATORS)
|
||||||
|
self.assertEqual(mock_active_vioses.call_count, 1)
|
||||||
|
self.assertEqual(mock_iscsi_init.call_count, 2)
|
||||||
|
|
||||||
|
# Invoke again with vios_id=None to ensure get_active_vioses,
|
||||||
|
# discover_iscsi_initiator is not called
|
||||||
|
mock_iscsi_init.reset_mock()
|
||||||
|
mock_active_vioses.reset_mock()
|
||||||
|
self.assertEqual(expected_output,
|
||||||
|
iscsi.get_iscsi_initiators(self.adpt, None))
|
||||||
|
self.assertEqual(mock_active_vioses.call_count, 0)
|
||||||
|
self.assertEqual(mock_iscsi_init.call_count, 0)
|
||||||
|
|
||||||
|
# Invoke iscsi.get_iscsi_initiators with discover_iscsi_initiator()
|
||||||
|
# raises ISCSIDiscoveryFailed exception
|
||||||
|
iscsi._ISCSI_INITIATORS = dict()
|
||||||
|
mock_iscsi_init.reset_mock()
|
||||||
|
mock_iscsi_init.side_effect = pvm_exc.ISCSIDiscoveryFailed(
|
||||||
|
vios_uuid='fake_vios_uid', status="fake_status")
|
||||||
|
self.assertEqual(dict(),
|
||||||
|
iscsi.get_iscsi_initiators(self.adpt, vios_ids))
|
||||||
|
self.assertEqual(dict(), iscsi._ISCSI_INITIATORS)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright 2014, 2017 IBM Corp.
|
# Copyright 2014, 2018 IBM Corp.
|
||||||
#
|
#
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
@@ -66,7 +66,7 @@ from nova_powervm.virt.powervm.tasks import storage as tf_stg
|
|||||||
from nova_powervm.virt.powervm.tasks import vm as tf_vm
|
from nova_powervm.virt.powervm.tasks import vm as tf_vm
|
||||||
from nova_powervm.virt.powervm import vm
|
from nova_powervm.virt.powervm import vm
|
||||||
from nova_powervm.virt.powervm import volume as vol_attach
|
from nova_powervm.virt.powervm import volume as vol_attach
|
||||||
|
from nova_powervm.virt.powervm.volume import iscsi
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
@@ -1103,7 +1103,9 @@ class PowerVMDriver(driver.ComputeDriver):
|
|||||||
connector["wwpns"] = wwpn_list
|
connector["wwpns"] = wwpn_list
|
||||||
connector["multipath"] = CONF.powervm.volume_use_multipath
|
connector["multipath"] = CONF.powervm.volume_use_multipath
|
||||||
connector['host'] = vol_attach.get_hostname_for_volume(instance)
|
connector['host'] = vol_attach.get_hostname_for_volume(instance)
|
||||||
connector['initiator'] = vol_attach.get_iscsi_initiator(self.adapter)
|
initiator_dict = iscsi.get_iscsi_initiators(self.adapter)
|
||||||
|
if initiator_dict:
|
||||||
|
connector['initiator'] = list(initiator_dict.values())[0]
|
||||||
return connector
|
return connector
|
||||||
|
|
||||||
def migrate_disk_and_power_off(self, context, instance, dest,
|
def migrate_disk_and_power_off(self, context, instance, dest,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright 2015, 2017 IBM Corp.
|
# Copyright 2015, 2018 IBM Corp.
|
||||||
#
|
#
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
@@ -14,11 +14,6 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from oslo_concurrency import lockutils
|
|
||||||
from pypowervm.tasks import hdisk
|
|
||||||
from pypowervm.tasks import partition
|
|
||||||
from pypowervm.wrappers import virtual_io_server as pvm_vios
|
|
||||||
|
|
||||||
|
|
||||||
# Defines the various volume connectors that can be used.
|
# Defines the various volume connectors that can be used.
|
||||||
from nova import exception
|
from nova import exception
|
||||||
@@ -85,28 +80,3 @@ def get_wwpns_for_volume_connector(adapter, host_uuid, instance):
|
|||||||
fc_vol_drv = build_volume_driver(adapter, host_uuid, instance,
|
fc_vol_drv = build_volume_driver(adapter, host_uuid, instance,
|
||||||
fake_fc_conn_info)
|
fake_fc_conn_info)
|
||||||
return fc_vol_drv.wwpns()
|
return fc_vol_drv.wwpns()
|
||||||
|
|
||||||
|
|
||||||
_ISCSI_INITIATOR = None
|
|
||||||
_ISCSI_LOOKUP_COMPLETE = False
|
|
||||||
|
|
||||||
|
|
||||||
@lockutils.synchronized("PowerVM_iSCSI_Initiator_Lookup")
|
|
||||||
def get_iscsi_initiator(adapter):
|
|
||||||
"""Gets the iSCSI initiator.
|
|
||||||
|
|
||||||
This is looked up once at process start up. Stored in memory thereafter.
|
|
||||||
|
|
||||||
:param adapter: The pypowervm adapter.
|
|
||||||
:return: The initiator name. If the NovaLink is not capable of supporting
|
|
||||||
iSCSI, None will be returned.
|
|
||||||
"""
|
|
||||||
global _ISCSI_INITIATOR, _ISCSI_LOOKUP_COMPLETE
|
|
||||||
if not _ISCSI_LOOKUP_COMPLETE:
|
|
||||||
mgmt_w = partition.get_mgmt_partition(adapter)
|
|
||||||
if isinstance(mgmt_w, pvm_vios.VIOS):
|
|
||||||
_ISCSI_INITIATOR = hdisk.discover_iscsi_initiator(
|
|
||||||
adapter, mgmt_w.uuid).strip()
|
|
||||||
|
|
||||||
_ISCSI_LOOKUP_COMPLETE = True
|
|
||||||
return _ISCSI_INITIATOR
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
import collections
|
||||||
import copy
|
import copy
|
||||||
from oslo_concurrency import lockutils
|
from oslo_concurrency import lockutils
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
@@ -26,6 +27,7 @@ from nova_powervm.virt.powervm.volume import volume
|
|||||||
from pypowervm import const as pvm_const
|
from pypowervm import const as pvm_const
|
||||||
from pypowervm import exceptions as pvm_exc
|
from pypowervm import exceptions as pvm_exc
|
||||||
from pypowervm.tasks import hdisk
|
from pypowervm.tasks import hdisk
|
||||||
|
from pypowervm.tasks import partition as pvm_partition
|
||||||
from pypowervm.utils import transaction as tx
|
from pypowervm.utils import transaction as tx
|
||||||
from pypowervm.wrappers import virtual_io_server as pvm_vios
|
from pypowervm.wrappers import virtual_io_server as pvm_vios
|
||||||
|
|
||||||
@@ -37,6 +39,56 @@ import six
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
DEVNAME_KEY = 'target_devname'
|
DEVNAME_KEY = 'target_devname'
|
||||||
|
_ISCSI_INITIATORS = collections.OrderedDict()
|
||||||
|
|
||||||
|
|
||||||
|
def get_iscsi_initiators(adapter, vios_ids=None):
|
||||||
|
"""Gets the VIOS iSCSI initiators.
|
||||||
|
|
||||||
|
For the first time invocation of this method after process start up,
|
||||||
|
it populates initiators data for VIOSes (if specified, otherwise it
|
||||||
|
gets active VIOSes from the host) and stores in memory for futher
|
||||||
|
lookup.
|
||||||
|
|
||||||
|
:param adapter: The pypowervm adapter
|
||||||
|
:param vios_ids: List of VIOS ids to get the initiators. If not
|
||||||
|
specified, a list of active VIOSes for the
|
||||||
|
host is fetched (but only for the first time)
|
||||||
|
through the pypowervm adapter.
|
||||||
|
:return: A dict of the form
|
||||||
|
{<vios_id>: <list of initiators>}
|
||||||
|
"""
|
||||||
|
|
||||||
|
global _ISCSI_INITIATORS
|
||||||
|
|
||||||
|
def discover_initiator(vios_id):
|
||||||
|
|
||||||
|
# Get the VIOS id lock for initiator lookup
|
||||||
|
@lockutils.synchronized('inititator-lookup-' + vios_id)
|
||||||
|
def _discover_initiator():
|
||||||
|
if (vios_id in _ISCSI_INITIATORS and
|
||||||
|
_ISCSI_INITIATORS[vios_id]):
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
initiator = hdisk.discover_iscsi_initiator(
|
||||||
|
adapter, vios_id)
|
||||||
|
_ISCSI_INITIATORS[vios_id] = initiator
|
||||||
|
except pvm_exc.ISCSIDiscoveryFailed as e:
|
||||||
|
# TODO(chhagarw): handle differently based on
|
||||||
|
# error codes
|
||||||
|
LOG.error(e)
|
||||||
|
|
||||||
|
_discover_initiator()
|
||||||
|
|
||||||
|
if vios_ids is None and not _ISCSI_INITIATORS:
|
||||||
|
vios_ids = pvm_partition.get_active_vioses(adapter)
|
||||||
|
|
||||||
|
for vios_id in vios_ids or []:
|
||||||
|
discover_initiator(vios_id)
|
||||||
|
|
||||||
|
LOG.debug("iSCSI initiator info: %s" % _ISCSI_INITIATORS)
|
||||||
|
return _ISCSI_INITIATORS
|
||||||
|
|
||||||
|
|
||||||
class IscsiVolumeAdapter(volume.VscsiVolumeAdapter,
|
class IscsiVolumeAdapter(volume.VscsiVolumeAdapter,
|
||||||
|
|||||||
Reference in New Issue
Block a user