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:
Chhavi Agarwal 2018-03-29 14:45:08 -04:00 committed by prashkre
parent ec454b725d
commit 390347afc6
6 changed files with 131 additions and 75 deletions

View File

@ -1,4 +1,4 @@
# Copyright 2014, 2017 IBM Corp.
# Copyright 2014, 2018 IBM Corp.
#
# All Rights Reserved.
#
@ -15,7 +15,7 @@
# under the License.
from __future__ import absolute_import
import collections
import fixtures
import logging
import mock
@ -206,21 +206,21 @@ class TestPowerVMDriver(test.NoDBTestCase):
self.assertTrue(
self.drv.session.get_event_listener.return_value.shutdown.called)
@mock.patch('nova_powervm.virt.powervm.volume.get_iscsi_initiator',
autospec=True)
def test_get_volume_connector(self, mock_initiator):
@mock.patch('nova_powervm.virt.powervm.volume.iscsi.get_iscsi_initiators')
def test_get_volume_connector(self, mock_initiators):
"""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')
vol_connector = self.drv.get_volume_connector(mock.Mock())
self.assertIsNotNone(vol_connector['wwpns'])
self.assertIsNotNone(vol_connector['host'])
self.assertEqual('iscsi_initiator', 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'])
self.assertEqual('fake_iqn1', vol_connector['initiator'])
def test_setup_disk_adapter(self):
# Ensure we can handle upper case option and we instantiate the class

View File

@ -1,4 +1,4 @@
# Copyright 2015, 2017 IBM Corp.
# Copyright 2015, 2018 IBM Corp.
#
# All Rights Reserved.
#
@ -15,7 +15,6 @@
# under the License.
import mock
from pypowervm.wrappers import virtual_io_server as pvm_vios
import six
from nova import test
@ -54,34 +53,6 @@ class TestInitMethods(test.NoDBTestCase):
'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):
for vol_type, class_type in six.iteritems(self.volume_drivers):
self.assertEqual(class_type, volume.get_volume_class(vol_type))

View File

@ -392,3 +392,64 @@ class TestISCSIAdapter(test_vol.TestVolumeAdapter):
retrieved_devname = self.vol_drv._get_devname()
# Check key not found
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)

View File

@ -1,4 +1,4 @@
# Copyright 2014, 2017 IBM Corp.
# Copyright 2014, 2018 IBM Corp.
#
# 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 import vm
from nova_powervm.virt.powervm import volume as vol_attach
from nova_powervm.virt.powervm.volume import iscsi
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
@ -1103,7 +1103,9 @@ class PowerVMDriver(driver.ComputeDriver):
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)
initiator_dict = iscsi.get_iscsi_initiators(self.adapter)
if initiator_dict:
connector['initiator'] = list(initiator_dict.values())[0]
return connector
def migrate_disk_and_power_off(self, context, instance, dest,

View File

@ -1,4 +1,4 @@
# Copyright 2015, 2017 IBM Corp.
# Copyright 2015, 2018 IBM Corp.
#
# All Rights Reserved.
#
@ -14,11 +14,6 @@
# License for the specific language governing permissions and limitations
# 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.
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,
fake_fc_conn_info)
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

View File

@ -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 collections
import copy
from oslo_concurrency import lockutils
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 exceptions as pvm_exc
from pypowervm.tasks import hdisk
from pypowervm.tasks import partition as pvm_partition
from pypowervm.utils import transaction as tx
from pypowervm.wrappers import virtual_io_server as pvm_vios
@ -37,6 +39,56 @@ import six
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
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,