Skip to content

Commit 1b6e452

Browse files
committed
CA-386479: ensure we login to all iSCSI Target Portal Groups
Some arrays may have multiple, entirely independent, Target Portal Groups within a single controller IQN space. When performing discovery to portals within these portal groups they respond only with the portal addresses within that TQG and do not include the portal addresses in other TPGs in the controller or information about TPGs in any other controller IQN spaces. In order to ensure that all the expected iSCSI paths and sessions are activated it is necessary to make the checks for IQN connectivity also check for the specific target portal address and not just check that the target IQN is served by an active session Signed-off-by: Mark Syms <[email protected]>
1 parent be5b691 commit 1b6e452

File tree

5 files changed

+189
-85
lines changed

5 files changed

+189
-85
lines changed

drivers/BaseISCSI.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def force_tapdisk(self):
5252
def attached(self):
5353
if not self._attached:
5454
self._attached = False
55-
self._attached = iscsilib._checkTGT(self.targetIQN)
55+
self._attached = iscsilib._checkTGT(self.targetIQN, self.target)
5656
return self._attached
5757

5858
@attached.setter
@@ -331,7 +331,7 @@ def attach(self, sr_uuid):
331331
iscsilib.ensure_daemon_running_ok(self.localIQN)
332332

333333
# Check to see if auto attach was set
334-
if not iscsilib._checkTGT(self.targetIQN) or multiTargets:
334+
if not iscsilib._checkTGT(self.targetIQN, tgt=self.target) or multiTargets:
335335
try:
336336
iqn_map = []
337337
if 'any' != self.targetIQN:
@@ -341,7 +341,10 @@ def attach(self, sr_uuid):
341341
# Pass the exception that is thrown, when there
342342
# are no nodes
343343
pass
344-
if len(iqn_map) == 0:
344+
345+
# Check our current target is in the map
346+
portal = '%s:%d' % (self.target, self.port)
347+
if len(iqn_map) == 0 or not any([x[0] for x in iqn_map if x[0] == portal]):
345348
iqn_map = iscsilib.discovery(self.target, self.port,
346349
self.chapuser, self.chappassword,
347350
self.targetIQN,
@@ -373,7 +376,7 @@ def attach(self, sr_uuid):
373376
else:
374377
pass
375378

376-
if not iscsilib._checkTGT(self.targetIQN):
379+
if not iscsilib._checkTGT(self.targetIQN, tgt=self.target):
377380
raise xs_errors.XenError('ISCSIDevice', \
378381
opterr='during login')
379382

@@ -394,6 +397,7 @@ def attach(self, sr_uuid):
394397
IQNs += iqn.split(',')[2]
395398
else:
396399
IQNs.append(self.targetIQN)
400+
397401
sessions = 0
398402
paths = iscsilib.get_IQN_paths()
399403
for path in paths:

drivers/LVHDoISCSISR.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ def create_iscsi_sessions(self, sr_uuid):
228228
util.SMlog("path %s" % self.iscsi.path)
229229
util.SMlog("iscsci data: targetIQN %s, portal %s" % (self.iscsi.targetIQN, self.iscsi.target))
230230
iscsilib.ensure_daemon_running_ok(self.iscsi.localIQN)
231-
if not iscsilib._checkTGT(self.iscsi.targetIQN):
231+
if not iscsilib._checkTGT(self.iscsi.targetIQN, self.iscsi.target):
232232
attempt_discovery = True
233233
try:
234234
# Ensure iscsi db has been populated

tests/shared_iscsi_test_base.py

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import unittest
2+
from unittest import mock
3+
4+
import iscsilib
5+
from SRCommand import SRCommand
6+
7+
8+
class ISCSITestCase(unittest.TestCase):
9+
10+
def setUp(self):
11+
iscsilib_patcher = mock.patch(f'{self.TEST_CLASS}.iscsilib',
12+
autospec=True)
13+
self.mock_iscsilib = iscsilib_patcher.start()
14+
self.mock_iscsilib.discovery.side_effect = self.discovery
15+
self.mock_iscsilib._checkTGT.side_effect = self._checkTGT
16+
self.mock_iscsilib.login.side_effect = self.iscsi_login
17+
self.mock_iscsilib.parse_IP_port = iscsilib.parse_IP_port
18+
self.discovery_data = {}
19+
self.sessions = []
20+
21+
sleep_patcher = mock.patch(f'{self.TEST_CLASS}.time.sleep',
22+
autospec=True)
23+
self.mock_sleep = sleep_patcher.start()
24+
25+
def _checkTGT(self, tgtIQN, tgt=''):
26+
all_sessions = '\n'.join(self.sessions)
27+
matched = iscsilib._compare_sessions_to_tgt(all_sessions, tgtIQN, tgt)
28+
return matched
29+
30+
def discovery(self, target, port, chapuser, chappassword,
31+
targetIQN="any", interfaceArray=["default"]):
32+
return self.discovery_data.get(target, [])
33+
34+
def iscsi_login(self, target, target_iqn, chauser, chappassword,
35+
incoming_user, incoming_password, mpath):
36+
session_count = len(self.sessions)
37+
self.sessions.append(f'tcp: [{session_count}] {target},1 {target_iqn}')
38+
39+
def create_sr_command(
40+
self, additional_dconf=None, cmd=None,
41+
target_iqn='iqn.2009-01.example.test:iscsi085e938a',
42+
multihomelist='tgt1:3260,tgt2:3260', target="10.70.89.34"):
43+
44+
sr_cmd = mock.create_autospec(SRCommand)
45+
sr_cmd.dconf = {
46+
'SCSIid': '3600a098038313577792450384a4a6275',
47+
'multihomelist': multihomelist,
48+
'target': target,
49+
'targetIQN': target_iqn,
50+
'localIQN': 'iqn.2018-05.com.example:0d312804'
51+
}
52+
if additional_dconf:
53+
sr_cmd.dconf.update(additional_dconf)
54+
55+
sr_cmd.params = {
56+
'command': 'nop',
57+
'session_ref': 'test_session',
58+
'host_ref': 'test_host',
59+
'sr_ref': 'sr_ref'
60+
}
61+
sr_cmd.cmd = cmd
62+
return sr_cmd
63+

tests/test_BaseISCSI.py

+112-32
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,36 @@
66
import unittest
77
from uuid import uuid4
88

9+
import util
910
from BaseISCSI import BaseISCSISR
1011
import SR
1112
import SRCommand
13+
from shared_iscsi_test_base import ISCSITestCase
1214
from util import CommandException
1315

1416

15-
class TestBaseISCSI(unittest.TestCase):
17+
class TestBaseISCSI(ISCSITestCase):
18+
19+
TEST_CLASS = 'BaseISCSI'
1620

1721
def setUp(self):
1822
self.addCleanup(mock.patch.stopall)
1923

2024
util_patcher = mock.patch('BaseISCSI.util', autospec=True)
2125
self.mock_util = util_patcher.start()
2226
self.mock_util.CommandException = CommandException
27+
self.mock_util.sessions_less_than_targets = util.sessions_less_than_targets
28+
self.mock_util._convertDNS.side_effect = lambda x: x
29+
# self.mock_util.SMlog.side_effect = print
30+
31+
scsi_util_patcher = mock.patch('BaseISCSI.scsiutil', autospec=True)
32+
self.mock_scsiutil = scsi_util_patcher.start()
2333

2434
self.mock_session = mock.MagicMock()
2535
xenapi_patcher = mock.patch('SR.XenAPI')
2636
mock_xenapi = xenapi_patcher.start()
2737
mock_xenapi.xapi_local.return_value = self.mock_session
2838

29-
iscsilib_patcher = mock.patch('BaseISCSI.iscsilib', autospec=True)
30-
self.mock_iscsilib = iscsilib_patcher.start()
31-
3239
copy_patcher = mock.patch('LVHDoISCSISR.SR.copy.deepcopy')
3340
self.mock_copy = copy_patcher.start()
3441

@@ -37,25 +44,9 @@ def deepcopy(to_copy):
3744

3845
self.mock_copy.side_effect = deepcopy
3946

40-
dummy_cmd = mock.create_autospec(SRCommand)
41-
dummy_cmd.dconf = {
42-
'SCSIid': '3600a098038313577792450384a4a6275',
43-
'target': "10.70.89.34",
44-
'targetIQN': 'iqn.2009-01.example.test:iscsi085e938a'
45-
}
46-
dummy_cmd.params = {
47-
'command': 'nop',
48-
'session_ref': 'test_session',
49-
'host_ref': 'test_host',
50-
'sr_ref': 'sr_ref'
51-
}
52-
dummy_cmd.cmd = None
53-
5447
self.sr_uuid = str(uuid4())
5548

56-
self.subject = BaseISCSISR(
57-
dummy_cmd, self.sr_uuid
58-
)
49+
super().setUp()
5950

6051
def setup_path_mocks(self):
6152
self.path_contents = {}
@@ -67,24 +58,31 @@ def setup_path_mocks(self):
6758
mock_listdir.side_effect = self.listdir
6859

6960
def exists(self, path):
70-
print(f'checking existance of {path}')
7161
return path in self.path_contents
7262

7363
def listdir(self, path):
7464
return self.path_contents[path]
7565

66+
def create_test_sr(self, sr_cmd):
67+
self.sr_uuid = str(uuid4())
68+
self.subject = BaseISCSISR(
69+
sr_cmd, self.sr_uuid)
70+
7671
@mock.patch('BaseISCSI.BaseISCSISR._initPaths', autospec=True)
7772
def test_attach_tgt_present_path_found(self, mock_init_paths):
7873
# Arrange
7974
self.setup_path_mocks()
8075
self.path_contents.update(
8176
{'/dev/disk/by-scsid/3600a098038313577792450384a4a6275': ['sdb']})
8277
self.mock_util._testHost.return_value = None
83-
self.mock_util.sessions_less_than_targets.return_value = False
84-
self.mock_iscsilib._checkTGT.return_value = True
85-
self.mock_iscsilib.parse_IP_port.side_effect = [
86-
('tgt1', '3260')
87-
]
78+
self.discovery_data = {
79+
'tgt1': [
80+
('tgt1:3260', 1, 'iqn.2009-11.com.infinidat:storage:infinibox-sn-3393')],
81+
}
82+
83+
self.create_test_sr(self.create_sr_command(
84+
cmd='sr_attach',
85+
target_iqn='iqn.2009-11.com.infinidat:storage:infinibox-sn-3393'))
8886

8987
# Act
9088
self.subject.attach(self.sr_uuid)
@@ -94,15 +92,97 @@ def test_attach_tgt_present_path_found(self, mock_init_paths):
9492
def test_attach_tgt_present_path_not_found(self, mock_init_paths):
9593
# Arrange
9694
self.mock_util._testHost.return_value = None
97-
self.mock_util.sessions_less_than_targets.return_value = False
98-
self.mock_iscsilib._checkTGT.return_value = True
99-
self.mock_iscsilib.parse_IP_port.side_effect = [
100-
('tgt1', '3260')
101-
]
95+
self.discovery_data = {
96+
'tgt1': [
97+
('tgt1:3260', 1, 'iqn.2009-11.com.infinidat:storage:infinibox-sn-3393')],
98+
}
99+
100+
self.create_test_sr(self.create_sr_command(
101+
cmd='sr_attach',
102+
target_iqn='iqn.2009-11.com.infinidat:storage:infinibox-sn-3393'))
102103

103104
# Act
104105
with self.assertRaises(SR.SROSError) as srose:
105106
self.subject.attach(self.sr_uuid)
106107

107108
# Assert
108109
self.assertEqual(107, srose.exception.errno)
110+
111+
def test_sr_attach_multi_session(self):
112+
# Arrange
113+
self.mock_util.find_my_pbd.return_value = 'my_pbd'
114+
additional_dconf = {
115+
'multiSession': '10.207.6.60,3260,iqn.2009-11.com.infinidat:storage:infinibox-sn-3393|'
116+
'10.207.3.65,3260,iqn.2009-11.com.infinidat:storage:infinibox-sn-3394|'
117+
'10.207.3.61,3260,iqn.2009-11.com.infinidat:storage:infinibox-sn-3393|'
118+
'10.207.6.61,3260,iqn.2009-11.com.infinidat:storage:infinibox-sn-3393|'
119+
'10.207.3.63,3260,iqn.2009-11.com.infinidat:storage:infinibox-sn-3394|'
120+
'10.207.6.62,3260,iqn.2009-11.com.infinidat:storage:infinibox-sn-3393|'
121+
'10.207.3.62,3260,iqn.2009-11.com.infinidat:storage:infinibox-sn-3393|'
122+
'10.207.3.60,3260,iqn.2009-11.com.infinidat:storage:infinibox-sn-3393|'
123+
'10.207.6.64,3260,iqn.2009-11.com.infinidat:storage:infinibox-sn-3394|'
124+
'10.207.6.65,3260,iqn.2009-11.com.infinidat:storage:infinibox-sn-3394|'
125+
'10.207.3.64,3260,iqn.2009-11.com.infinidat:storage:infinibox-sn-3394|'
126+
'10.207.6.63,3260,iqn.2009-11.com.infinidat:storage:infinibox-sn-3394|'
127+
}
128+
129+
tpg_data = [
130+
[
131+
('10.207.3.60:3260', 1, 'iqn.2009-11.com.infinidat:storage:infinibox-sn-3393'),
132+
('10.207.3.61:3260', 1, 'iqn.2009-11.com.infinidat:storage:infinibox-sn-3393'),
133+
('10.207.3.62:3260', 1, 'iqn.2009-11.com.infinidat:storage:infinibox-sn-3393')],
134+
[
135+
('10.207.3.63:3260', 1, 'iqn.2009-11.com.infinidat:storage:infinibox-sn-3394'),
136+
('10.207.3.64:3260', 1, 'iqn.2009-11.com.infinidat:storage:infinibox-sn-3394'),
137+
('10.207.3.65:3260', 1, 'iqn.2009-11.com.infinidat:storage:infinibox-sn-3394')],
138+
[
139+
('10.207.6.60:3260', 2, 'iqn.2009-11.com.infinidat:storage:infinibox-sn-3393'),
140+
('10.207.6.61:3260', 2, 'iqn.2009-11.com.infinidat:storage:infinibox-sn-3393'),
141+
('10.207.6.62:3260', 2, 'iqn.2009-11.com.infinidat:storage:infinibox-sn-3393')
142+
],
143+
[
144+
('10.207.6.63:3260', 2, 'iqn.2009-11.com.infinidat:storage:infinibox-sn-3394'),
145+
('10.207.6.64:3260', 2, 'iqn.2009-11.com.infinidat:storage:infinibox-sn-3394'),
146+
('10.207.6.65:3260', 2, 'iqn.2009-11.com.infinidat:storage:infinibox-sn-3394')
147+
]
148+
]
149+
150+
self.discovery_data = {
151+
'10.207.3.60': tpg_data[0],
152+
'10.207.3.61': tpg_data[0],
153+
'10.207.3.62': tpg_data[0],
154+
'10.207.3.63': tpg_data[1],
155+
'10.207.3.64': tpg_data[1],
156+
'10.207.3.65': tpg_data[1],
157+
'10.207.6.60': tpg_data[2],
158+
'10.207.6.61': tpg_data[2],
159+
'10.207.6.62': tpg_data[2],
160+
'10.207.6.63': tpg_data[3],
161+
'10.207.6.64': tpg_data[3],
162+
'10.207.6.65': tpg_data[3]
163+
}
164+
165+
self.mock_scsiutil._genHostList.return_value = [1, 2]
166+
self.mock_iscsilib.get_targetIQN.return_value = 'iqn.2009-11.com.infinidat:storage:infinibox-sn-3393'
167+
self.mock_scsiutil.cacheSCSIidentifiers.return_value = [
168+
['NONE', '0', '0', '0', '0', '0', '/dev/sdb']
169+
]
170+
self.setup_path_mocks()
171+
self.path_contents.update(
172+
{'/dev/iscsi/iqn.2009-11.com.infinidat:storage:infinibox-sn-3393/10.207.3.60:3260': ['LUN0'],
173+
'/dev/disk/by-scsid/3600a098038313577792450384a4a6275': []})
174+
175+
# Create SR
176+
self.create_test_sr(self.create_sr_command(
177+
additional_dconf=additional_dconf,
178+
cmd='sr_attach',
179+
multihomelist="10.207.3.62:3260,10.207.6.61:3260,10.207.6.62:3260,10.207.6.60:3260",
180+
target='10.207.3.60',
181+
target_iqn='iqn.2009-11.com.infinidat:storage:infinibox-sn-3393'))
182+
183+
# Act
184+
self.subject.attach(self.sr_uuid)
185+
186+
# Assert
187+
self.assertEqual(1, self.mock_iscsilib.discovery.call_count)
188+
self.assertEqual(1, self.mock_iscsilib.login.call_count)

0 commit comments

Comments
 (0)