Merge "Kaminario K2: Add non discovery iSCSI multipath"
This commit is contained in:
commit
cbf2c57e02
@ -15,6 +15,7 @@
|
|||||||
"""Unit tests for kaminario driver."""
|
"""Unit tests for kaminario driver."""
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
import ddt
|
||||||
import mock
|
import mock
|
||||||
from oslo_utils import units
|
from oslo_utils import units
|
||||||
import time
|
import time
|
||||||
@ -47,12 +48,13 @@ class FakeK2Obj(object):
|
|||||||
|
|
||||||
class FakeSaveObject(FakeK2Obj):
|
class FakeSaveObject(FakeK2Obj):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
item = kwargs.pop('item', 1)
|
||||||
self.ntype = kwargs.get('ntype')
|
self.ntype = kwargs.get('ntype')
|
||||||
self.ip_address = '10.0.0.1'
|
self.ip_address = '10.0.0.%s' % item
|
||||||
self.iscsi_qualified_target_name = "xyztlnxyz"
|
self.iscsi_qualified_target_name = "xyztlnxyz"
|
||||||
self.snapshot = FakeK2Obj()
|
self.snapshot = FakeK2Obj()
|
||||||
self.name = 'test'
|
self.name = 'test'
|
||||||
self.pwwn = '50024f4053300300'
|
self.pwwn = '50024f405330030%s' % item
|
||||||
self.volume_group = self
|
self.volume_group = self
|
||||||
self.is_dedup = True
|
self.is_dedup = True
|
||||||
self.size = units.Mi
|
self.size = units.Mi
|
||||||
@ -83,8 +85,8 @@ class FakeSaveObjectExp(FakeSaveObject):
|
|||||||
|
|
||||||
|
|
||||||
class FakeSearchObject(object):
|
class FakeSearchObject(object):
|
||||||
hits = [FakeSaveObject()]
|
hits = [FakeSaveObject(item=1), FakeSaveObject(item=2)]
|
||||||
total = 1
|
total = 2
|
||||||
|
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
if args and "mappings" in args[0]:
|
if args and "mappings" in args[0]:
|
||||||
@ -119,14 +121,14 @@ class Replication(object):
|
|||||||
rpo = 500
|
rpo = 500
|
||||||
|
|
||||||
|
|
||||||
class TestKaminarioISCSI(test.TestCase):
|
class TestKaminarioCommon(test.TestCase):
|
||||||
driver = None
|
driver = None
|
||||||
conf = None
|
conf = None
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self._setup_config()
|
self._setup_config()
|
||||||
self._setup_driver()
|
self._setup_driver()
|
||||||
super(TestKaminarioISCSI, self).setUp()
|
super(TestKaminarioCommon, self).setUp()
|
||||||
self.context = context.get_admin_context()
|
self.context = context.get_admin_context()
|
||||||
self.vol = fake_volume.fake_volume_obj(self.context)
|
self.vol = fake_volume.fake_volume_obj(self.context)
|
||||||
self.vol.volume_type = fake_volume.fake_volume_type_obj(self.context)
|
self.vol.volume_type = fake_volume.fake_volume_type_obj(self.context)
|
||||||
@ -140,6 +142,7 @@ class TestKaminarioISCSI(test.TestCase):
|
|||||||
self.conf.kaminario_dedup_type_name = "dedup"
|
self.conf.kaminario_dedup_type_name = "dedup"
|
||||||
self.conf.volume_dd_blocksize = 2
|
self.conf.volume_dd_blocksize = 2
|
||||||
self.conf.unique_fqdn_network = True
|
self.conf.unique_fqdn_network = True
|
||||||
|
self.conf.disable_discovery = False
|
||||||
|
|
||||||
def _setup_driver(self):
|
def _setup_driver(self):
|
||||||
self.driver = (kaminario_iscsi.
|
self.driver = (kaminario_iscsi.
|
||||||
@ -258,12 +261,6 @@ class TestKaminarioISCSI(test.TestCase):
|
|||||||
self.assertRaises(exception.KaminarioCinderDriverException,
|
self.assertRaises(exception.KaminarioCinderDriverException,
|
||||||
self.driver.extend_volume, self.vol, new_size)
|
self.driver.extend_volume, self.vol, new_size)
|
||||||
|
|
||||||
def test_initialize_connection(self):
|
|
||||||
"""Test initialize_connection."""
|
|
||||||
conn_info = self.driver.initialize_connection(self.vol, CONNECTOR)
|
|
||||||
self.assertIn('data', conn_info)
|
|
||||||
self.assertIn('target_iqn', conn_info['data'])
|
|
||||||
|
|
||||||
def test_initialize_connection_with_exception(self):
|
def test_initialize_connection_with_exception(self):
|
||||||
"""Test initialize_connection_with_exception."""
|
"""Test initialize_connection_with_exception."""
|
||||||
self.driver.client = FakeKrestException()
|
self.driver.client = FakeKrestException()
|
||||||
@ -271,11 +268,6 @@ class TestKaminarioISCSI(test.TestCase):
|
|||||||
self.driver.initialize_connection, self.vol,
|
self.driver.initialize_connection, self.vol,
|
||||||
CONNECTOR)
|
CONNECTOR)
|
||||||
|
|
||||||
def test_terminate_connection(self):
|
|
||||||
"""Test terminate_connection."""
|
|
||||||
result = self.driver.terminate_connection(self.vol, CONNECTOR)
|
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
def test_get_lun_number(self):
|
def test_get_lun_number(self):
|
||||||
"""Test _get_lun_number."""
|
"""Test _get_lun_number."""
|
||||||
host, host_rs, host_name = self.driver._get_host_object(CONNECTOR)
|
host, host_rs, host_name = self.driver._get_host_object(CONNECTOR)
|
||||||
@ -291,20 +283,15 @@ class TestKaminarioISCSI(test.TestCase):
|
|||||||
"""Test _get_host_object."""
|
"""Test _get_host_object."""
|
||||||
host, host_rs, host_name = self.driver._get_host_object(CONNECTOR)
|
host, host_rs, host_name = self.driver._get_host_object(CONNECTOR)
|
||||||
self.assertEqual(548, host.id)
|
self.assertEqual(548, host.id)
|
||||||
self.assertEqual(1, host_rs.total)
|
self.assertEqual(2, host_rs.total)
|
||||||
self.assertEqual('test-k2', host_name)
|
self.assertEqual('test-k2', host_name)
|
||||||
|
|
||||||
def test_get_target_info(self):
|
|
||||||
"""Test get_target_info."""
|
|
||||||
iscsi_portal, target_iqn = self.driver.get_target_info(self.vol)
|
|
||||||
self.assertEqual('10.0.0.1:3260', iscsi_portal)
|
|
||||||
self.assertEqual('xyztlnxyz', target_iqn)
|
|
||||||
|
|
||||||
def test_k2_initialize_connection(self):
|
def test_k2_initialize_connection(self):
|
||||||
"""Test k2_initialize_connection."""
|
"""Test k2_initialize_connection."""
|
||||||
result = self.driver.k2_initialize_connection(self.vol, CONNECTOR)
|
result = self.driver.k2_initialize_connection(self.vol, CONNECTOR)
|
||||||
self.assertEqual(548, result)
|
self.assertEqual(548, result)
|
||||||
|
|
||||||
|
@mock.patch.object(FakeSearchObject, 'total', 1)
|
||||||
def test_manage_existing(self):
|
def test_manage_existing(self):
|
||||||
"""Test manage_existing."""
|
"""Test manage_existing."""
|
||||||
self.driver._get_replica_status = mock.Mock(return_value=False)
|
self.driver._get_replica_status = mock.Mock(return_value=False)
|
||||||
@ -543,7 +530,64 @@ class TestKaminarioISCSI(test.TestCase):
|
|||||||
self.assertEqual(expected, result)
|
self.assertEqual(expected, result)
|
||||||
|
|
||||||
|
|
||||||
class TestKaminarioFC(TestKaminarioISCSI):
|
@ddt.ddt
|
||||||
|
class TestKaminarioISCSI(TestKaminarioCommon):
|
||||||
|
def test_get_target_info(self):
|
||||||
|
"""Test get_target_info."""
|
||||||
|
iscsi_portals, target_iqns = self.driver.get_target_info(self.vol)
|
||||||
|
self.assertEqual(['10.0.0.1:3260', '10.0.0.2:3260'],
|
||||||
|
iscsi_portals)
|
||||||
|
self.assertEqual(['xyztlnxyz', 'xyztlnxyz'],
|
||||||
|
target_iqns)
|
||||||
|
|
||||||
|
@ddt.data(True, False)
|
||||||
|
def test_initialize_connection(self, multipath):
|
||||||
|
"""Test initialize_connection."""
|
||||||
|
connector = CONNECTOR.copy()
|
||||||
|
connector['multipath'] = multipath
|
||||||
|
self.driver.configuration.disable_discovery = False
|
||||||
|
|
||||||
|
conn_info = self.driver.initialize_connection(self.vol, CONNECTOR)
|
||||||
|
expected = {
|
||||||
|
'data': {
|
||||||
|
'target_discovered': True,
|
||||||
|
'target_iqn': 'xyztlnxyz',
|
||||||
|
'target_lun': 548,
|
||||||
|
'target_portal': '10.0.0.1:3260',
|
||||||
|
},
|
||||||
|
'driver_volume_type': 'iscsi',
|
||||||
|
}
|
||||||
|
self.assertEqual(expected, conn_info)
|
||||||
|
|
||||||
|
def test_initialize_connection_multipath(self):
|
||||||
|
"""Test initialize_connection with multipath."""
|
||||||
|
connector = CONNECTOR.copy()
|
||||||
|
connector['multipath'] = True
|
||||||
|
self.driver.configuration.disable_discovery = True
|
||||||
|
|
||||||
|
conn_info = self.driver.initialize_connection(self.vol, connector)
|
||||||
|
|
||||||
|
expected = {
|
||||||
|
'data': {
|
||||||
|
'target_discovered': True,
|
||||||
|
'target_iqn': 'xyztlnxyz',
|
||||||
|
'target_iqns': ['xyztlnxyz', 'xyztlnxyz'],
|
||||||
|
'target_lun': 548,
|
||||||
|
'target_luns': [548, 548],
|
||||||
|
'target_portal': '10.0.0.1:3260',
|
||||||
|
'target_portals': ['10.0.0.1:3260', '10.0.0.2:3260'],
|
||||||
|
},
|
||||||
|
'driver_volume_type': 'iscsi',
|
||||||
|
}
|
||||||
|
self.assertEqual(expected, conn_info)
|
||||||
|
|
||||||
|
def test_terminate_connection(self):
|
||||||
|
"""Test terminate_connection."""
|
||||||
|
result = self.driver.terminate_connection(self.vol, CONNECTOR)
|
||||||
|
self.assertIsNone(result)
|
||||||
|
|
||||||
|
|
||||||
|
class TestKaminarioFC(TestKaminarioCommon):
|
||||||
|
|
||||||
def _setup_driver(self):
|
def _setup_driver(self):
|
||||||
self.driver = (kaminario_fc.
|
self.driver = (kaminario_fc.
|
||||||
@ -562,7 +606,8 @@ class TestKaminarioFC(TestKaminarioISCSI):
|
|||||||
def test_get_target_info(self):
|
def test_get_target_info(self):
|
||||||
"""Test get_target_info."""
|
"""Test get_target_info."""
|
||||||
target_wwpn = self.driver.get_target_info(self.vol)
|
target_wwpn = self.driver.get_target_info(self.vol)
|
||||||
self.assertEqual(['50024f4053300300'], target_wwpn)
|
self.assertEqual(['50024f4053300301', '50024f4053300302'],
|
||||||
|
target_wwpn)
|
||||||
|
|
||||||
def test_terminate_connection(self):
|
def test_terminate_connection(self):
|
||||||
"""Test terminate_connection."""
|
"""Test terminate_connection."""
|
||||||
|
@ -59,6 +59,10 @@ kaminario_opts = [
|
|||||||
"FQDN. When true this will create host entries on K2 "
|
"FQDN. When true this will create host entries on K2 "
|
||||||
"using the FQDN, when false it will use the reversed "
|
"using the FQDN, when false it will use the reversed "
|
||||||
"IQN/WWNN."),
|
"IQN/WWNN."),
|
||||||
|
cfg.BoolOpt('disable_discovery',
|
||||||
|
default=False,
|
||||||
|
help="Disabling iSCSI discovery (sendtargets) for multipath "
|
||||||
|
"connections on K2 driver."),
|
||||||
]
|
]
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
@ -61,19 +61,25 @@ class KaminarioISCSIDriver(common.KaminarioCinderDriver):
|
|||||||
temp_client = self.client
|
temp_client = self.client
|
||||||
self.client = self.target
|
self.client = self.target
|
||||||
# Get target_portal and target iqn.
|
# Get target_portal and target iqn.
|
||||||
iscsi_portal, target_iqn = self.get_target_info(volume)
|
iscsi_portals, target_iqns = self.get_target_info(volume)
|
||||||
# Map volume.
|
# Map volume.
|
||||||
lun = self.k2_initialize_connection(volume, connector)
|
lun = self.k2_initialize_connection(volume, connector)
|
||||||
# To support replication failback
|
# To support replication failback
|
||||||
if temp_client:
|
if temp_client:
|
||||||
self.client = temp_client
|
self.client = temp_client
|
||||||
# Return target volume information.
|
# Return target volume information.
|
||||||
return {"driver_volume_type": "iscsi",
|
result = {"driver_volume_type": "iscsi",
|
||||||
"data": {"target_iqn": target_iqn,
|
"data": {"target_iqn": target_iqns[0],
|
||||||
"target_portal": iscsi_portal,
|
"target_portal": iscsi_portals[0],
|
||||||
"target_lun": lun,
|
"target_lun": lun,
|
||||||
"target_discovered": True}}
|
"target_discovered": True}}
|
||||||
|
|
||||||
|
if self.configuration.disable_discovery and connector.get('multipath'):
|
||||||
|
result['data'].update(target_iqns=target_iqns,
|
||||||
|
target_portals=iscsi_portals,
|
||||||
|
target_luns=[lun] * len(target_iqns))
|
||||||
|
return result
|
||||||
|
|
||||||
@kaminario_logger
|
@kaminario_logger
|
||||||
@coordination.synchronized('{self.k2_lock_name}')
|
@coordination.synchronized('{self.k2_lock_name}')
|
||||||
def terminate_connection(self, volume, connector, **kwargs):
|
def terminate_connection(self, volume, connector, **kwargs):
|
||||||
@ -92,28 +98,26 @@ class KaminarioISCSIDriver(common.KaminarioCinderDriver):
|
|||||||
def get_target_info(self, volume):
|
def get_target_info(self, volume):
|
||||||
LOG.debug("Searching first iscsi port ip without wan in K2.")
|
LOG.debug("Searching first iscsi port ip without wan in K2.")
|
||||||
iscsi_ip_rs = self.client.search("system/net_ips")
|
iscsi_ip_rs = self.client.search("system/net_ips")
|
||||||
iscsi_ip = target_iqn = None
|
iscsi_portals = target_iqns = None
|
||||||
if hasattr(iscsi_ip_rs, 'hits') and iscsi_ip_rs.total != 0:
|
if hasattr(iscsi_ip_rs, 'hits') and iscsi_ip_rs.total != 0:
|
||||||
for ip in iscsi_ip_rs.hits:
|
iscsi_portals = ['%s:%s' % (ip.ip_address, ISCSI_TCP_PORT)
|
||||||
if not ip.wan_port:
|
for ip in iscsi_ip_rs.hits if not ip.wan_port]
|
||||||
iscsi_ip = ip.ip_address
|
if not iscsi_portals:
|
||||||
break
|
|
||||||
if not iscsi_ip:
|
|
||||||
msg = _("Unable to get ISCSI IP address from K2.")
|
msg = _("Unable to get ISCSI IP address from K2.")
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise exception.KaminarioCinderDriverException(reason=msg)
|
raise exception.KaminarioCinderDriverException(reason=msg)
|
||||||
iscsi_portal = "{0}:{1}".format(iscsi_ip, ISCSI_TCP_PORT)
|
|
||||||
LOG.debug("Searching system state for target iqn in K2.")
|
LOG.debug("Searching system state for target iqn in K2.")
|
||||||
sys_state_rs = self.client.search("system/state")
|
sys_state_rs = self.client.search("system/state")
|
||||||
|
|
||||||
if hasattr(sys_state_rs, 'hits') and sys_state_rs.total != 0:
|
if hasattr(sys_state_rs, 'hits') and sys_state_rs.total != 0:
|
||||||
target_iqn = sys_state_rs.hits[0].iscsi_qualified_target_name
|
iqn = sys_state_rs.hits[0].iscsi_qualified_target_name
|
||||||
|
target_iqns = [iqn] * len(iscsi_portals)
|
||||||
|
|
||||||
if not target_iqn:
|
if not target_iqns:
|
||||||
msg = _("Unable to get target iqn from K2.")
|
msg = _("Unable to get target iqn from K2.")
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise exception.KaminarioCinderDriverException(reason=msg)
|
raise exception.KaminarioCinderDriverException(reason=msg)
|
||||||
return iscsi_portal, target_iqn
|
return iscsi_portals, target_iqns
|
||||||
|
|
||||||
@kaminario_logger
|
@kaminario_logger
|
||||||
def _get_host_object(self, connector):
|
def _get_host_object(self, connector):
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Kaminario K2 iSCSI driver now supports non discovery multipathing (Nova and
|
||||||
|
Cinder won't use iSCSI sendtargets) which can be enabled by setting
|
||||||
|
`disable_discovery` to `true` in the configuration.
|
Loading…
Reference in New Issue
Block a user