]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/dashboard/tests/test_iscsi.py
import 15.2.5
[ceph.git] / ceph / src / pybind / mgr / dashboard / tests / test_iscsi.py
CommitLineData
11fdf7f2
TL
1# pylint: disable=too-many-public-methods
2
3import copy
4import errno
5import json
92f5a8d4 6import unittest
11fdf7f2 7
92f5a8d4
TL
8try:
9 import mock
10except ImportError:
11 import unittest.mock as mock
12
13from . import CmdException, ControllerTestCase, CLICommandTestMixin, KVStoreMockMixin
11fdf7f2
TL
14from .. import mgr
15from ..controllers.iscsi import Iscsi, IscsiTarget
16from ..services.iscsi_client import IscsiClient
17from ..services.orchestrator import OrchClient
18from ..rest_client import RequestException
19
20
92f5a8d4 21class IscsiTestCli(unittest.TestCase, CLICommandTestMixin):
11fdf7f2
TL
22
23 def setUp(self):
24 self.mock_kv_store()
25 # pylint: disable=protected-access
26 IscsiClientMock._instance = IscsiClientMock()
27 IscsiClient.instance = IscsiClientMock.instance
28
29 def test_cli_add_gateway_invalid_url(self):
30 with self.assertRaises(CmdException) as ctx:
31 self.exec_cmd('iscsi-gateway-add', name='node1',
32 service_url='http:/hello.com')
33
34 self.assertEqual(ctx.exception.retcode, -errno.EINVAL)
35 self.assertEqual(str(ctx.exception),
36 "Invalid service URL 'http:/hello.com'. Valid format: "
37 "'<scheme>://<username>:<password>@<host>[:port]'.")
38
39 def test_cli_add_gateway(self):
40 self.exec_cmd('iscsi-gateway-add', name='node1',
41 service_url='https://admin:admin@10.17.5.1:5001')
42 self.exec_cmd('iscsi-gateway-add', name='node2',
43 service_url='https://admin:admin@10.17.5.2:5001')
44 iscsi_config = json.loads(self.get_key("_iscsi_config"))
45 self.assertEqual(iscsi_config['gateways'], {
46 'node1': {
47 'service_url': 'https://admin:admin@10.17.5.1:5001'
48 },
49 'node2': {
50 'service_url': 'https://admin:admin@10.17.5.2:5001'
51 }
52 })
53
54 def test_cli_remove_gateway(self):
55 self.test_cli_add_gateway()
56 self.exec_cmd('iscsi-gateway-rm', name='node1')
57 iscsi_config = json.loads(self.get_key("_iscsi_config"))
58 self.assertEqual(iscsi_config['gateways'], {
59 'node2': {
60 'service_url': 'https://admin:admin@10.17.5.2:5001'
61 }
62 })
63
92f5a8d4
TL
64
65class IscsiTestController(ControllerTestCase, KVStoreMockMixin):
66
67 @classmethod
68 def setup_server(cls):
9f95a23c 69 OrchClient.instance().available = lambda: False
92f5a8d4
TL
70 mgr.rados.side_effect = None
71 # pylint: disable=protected-access
72 Iscsi._cp_config['tools.authenticate.on'] = False
73 IscsiTarget._cp_config['tools.authenticate.on'] = False
74 cls.setup_controllers([Iscsi, IscsiTarget])
75
76 def setUp(self):
77 self.mock_kv_store()
78 self.CONFIG_KEY_DICT['_iscsi_config'] = '''
79 {
80 "gateways": {
81 "node1": {
82 "service_url": "https://admin:admin@10.17.5.1:5001"
83 },
84 "node2": {
85 "service_url": "https://admin:admin@10.17.5.2:5001"
86 }
87 }
88 }
89 '''
90 # pylint: disable=protected-access
91 IscsiClientMock._instance = IscsiClientMock()
92 IscsiClient.instance = IscsiClientMock.instance
93
11fdf7f2
TL
94 def test_enable_discoveryauth(self):
95 discoveryauth = {
96 'user': 'myiscsiusername',
97 'password': 'myiscsipassword',
98 'mutual_user': 'myiscsiusername2',
99 'mutual_password': 'myiscsipassword2'
100 }
101 self._put('/api/iscsi/discoveryauth', discoveryauth)
102 self.assertStatus(200)
103 self.assertJsonBody(discoveryauth)
104 self._get('/api/iscsi/discoveryauth')
105 self.assertStatus(200)
106 self.assertJsonBody(discoveryauth)
107
1911f103
TL
108 def test_bad_discoveryauth(self):
109 discoveryauth = {
110 'user': 'myiscsiusername',
111 'password': 'myiscsipasswordmyiscsipasswordmyiscsipassword',
112 'mutual_user': '',
113 'mutual_password': ''
114 }
115 put_response = {
116 'detail': 'Bad authentication',
117 'code': 'target_bad_auth',
118 'component': 'iscsi'
119 }
120 get_response = {
121 'user': '',
122 'password': '',
123 'mutual_user': '',
124 'mutual_password': ''
125 }
126 self._put('/api/iscsi/discoveryauth', discoveryauth)
127 self.assertStatus(400)
128 self.assertJsonBody(put_response)
129 self._get('/api/iscsi/discoveryauth')
130 self.assertStatus(200)
131 self.assertJsonBody(get_response)
132
11fdf7f2
TL
133 def test_disable_discoveryauth(self):
134 discoveryauth = {
135 'user': '',
136 'password': '',
137 'mutual_user': '',
138 'mutual_password': ''
139 }
140 self._put('/api/iscsi/discoveryauth', discoveryauth)
141 self.assertStatus(200)
142 self.assertJsonBody(discoveryauth)
143 self._get('/api/iscsi/discoveryauth')
144 self.assertStatus(200)
145 self.assertJsonBody(discoveryauth)
146
147 def test_list_empty(self):
148 self._get('/api/iscsi/target')
149 self.assertStatus(200)
150 self.assertJsonBody([])
151
152 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
153 def test_list(self, _validate_image_mock):
154 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw1"
155 request = copy.deepcopy(iscsi_target_request)
156 request['target_iqn'] = target_iqn
eafe8130 157 self._task_post('/api/iscsi/target', request)
11fdf7f2
TL
158 self.assertStatus(201)
159 self._get('/api/iscsi/target')
160 self.assertStatus(200)
161 response = copy.deepcopy(iscsi_target_response)
162 response['target_iqn'] = target_iqn
163 self.assertJsonBody([response])
164
165 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
166 def test_create(self, _validate_image_mock):
167 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw2"
168 request = copy.deepcopy(iscsi_target_request)
169 request['target_iqn'] = target_iqn
eafe8130 170 self._task_post('/api/iscsi/target', request)
11fdf7f2
TL
171 self.assertStatus(201)
172 self._get('/api/iscsi/target/{}'.format(request['target_iqn']))
173 self.assertStatus(200)
174 response = copy.deepcopy(iscsi_target_response)
175 response['target_iqn'] = target_iqn
176 self.assertJsonBody(response)
177
178 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
179 def test_delete(self, _validate_image_mock):
180 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw3"
181 request = copy.deepcopy(iscsi_target_request)
182 request['target_iqn'] = target_iqn
eafe8130 183 self._task_post('/api/iscsi/target', request)
11fdf7f2 184 self.assertStatus(201)
eafe8130 185 self._task_delete('/api/iscsi/target/{}'.format(request['target_iqn']))
11fdf7f2
TL
186 self.assertStatus(204)
187 self._get('/api/iscsi/target')
188 self.assertStatus(200)
189 self.assertJsonBody([])
190
191 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
192 def test_add_client(self, _validate_image_mock):
193 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw4"
194 create_request = copy.deepcopy(iscsi_target_request)
195 create_request['target_iqn'] = target_iqn
196 update_request = copy.deepcopy(create_request)
197 update_request['new_target_iqn'] = target_iqn
198 update_request['clients'].append(
199 {
200 "luns": [{"image": "lun1", "pool": "rbd"}],
201 "client_iqn": "iqn.1994-05.com.redhat:rh7-client3",
202 "auth": {
203 "password": "myiscsipassword5",
204 "user": "myiscsiusername5",
205 "mutual_password": "myiscsipassword6",
206 "mutual_user": "myiscsiusername6"}
207 })
208 response = copy.deepcopy(iscsi_target_response)
209 response['target_iqn'] = target_iqn
210 response['clients'].append(
211 {
212 "luns": [{"image": "lun1", "pool": "rbd"}],
213 "client_iqn": "iqn.1994-05.com.redhat:rh7-client3",
214 "auth": {
215 "password": "myiscsipassword5",
216 "user": "myiscsiusername5",
217 "mutual_password": "myiscsipassword6",
494da23a
TL
218 "mutual_user": "myiscsiusername6"},
219 "info": {
220 "alias": "",
221 "ip_address": [],
222 "state": {}
223 }
11fdf7f2 224 })
f6b5b4d7 225 self._update_iscsi_target(create_request, update_request, 200, None, response)
11fdf7f2 226
1911f103
TL
227 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
228 def test_add_bad_client(self, _validate_image_mock):
229 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw4"
230 create_request = copy.deepcopy(iscsi_target_request)
231 create_request['target_iqn'] = target_iqn
232 update_request = copy.deepcopy(create_request)
233 update_request['new_target_iqn'] = target_iqn
234 update_request['clients'].append(
235 {
236 "luns": [{"image": "lun1", "pool": "rbd"}],
237 "client_iqn": "iqn.1994-05.com.redhat:rh7-client4",
238 "auth": {
239 "password": "myiscsipassword7myiscsipassword7myiscsipasswo",
240 "user": "myiscsiusername7",
241 "mutual_password": "myiscsipassword8",
242 "mutual_user": "myiscsiusername8"}
243 })
244 response = copy.deepcopy(iscsi_target_response)
245 response['target_iqn'] = target_iqn
246
247 self._task_post('/api/iscsi/target', create_request)
248 self.assertStatus(201)
249 self._task_put('/api/iscsi/target/{}'.format(create_request['target_iqn']), update_request)
250 self.assertStatus(400)
251 self._get('/api/iscsi/target/{}'.format(update_request['new_target_iqn']))
252 self.assertStatus(200)
253 self.assertJsonBody(response)
254
11fdf7f2
TL
255 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
256 def test_change_client_password(self, _validate_image_mock):
257 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw5"
258 create_request = copy.deepcopy(iscsi_target_request)
259 create_request['target_iqn'] = target_iqn
260 update_request = copy.deepcopy(create_request)
261 update_request['new_target_iqn'] = target_iqn
1911f103 262 update_request['clients'][0]['auth']['password'] = 'MyNewPassword'
11fdf7f2
TL
263 response = copy.deepcopy(iscsi_target_response)
264 response['target_iqn'] = target_iqn
1911f103 265 response['clients'][0]['auth']['password'] = 'MyNewPassword'
f6b5b4d7 266 self._update_iscsi_target(create_request, update_request, 200, None, response)
11fdf7f2
TL
267
268 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
269 def test_rename_client(self, _validate_image_mock):
270 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw6"
271 create_request = copy.deepcopy(iscsi_target_request)
272 create_request['target_iqn'] = target_iqn
273 update_request = copy.deepcopy(create_request)
274 update_request['new_target_iqn'] = target_iqn
275 update_request['clients'][0]['client_iqn'] = 'iqn.1994-05.com.redhat:rh7-client0'
276 response = copy.deepcopy(iscsi_target_response)
277 response['target_iqn'] = target_iqn
278 response['clients'][0]['client_iqn'] = 'iqn.1994-05.com.redhat:rh7-client0'
f6b5b4d7 279 self._update_iscsi_target(create_request, update_request, 200, None, response)
11fdf7f2
TL
280
281 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
282 def test_add_disk(self, _validate_image_mock):
283 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw7"
284 create_request = copy.deepcopy(iscsi_target_request)
285 create_request['target_iqn'] = target_iqn
286 update_request = copy.deepcopy(create_request)
287 update_request['new_target_iqn'] = target_iqn
288 update_request['disks'].append(
289 {
290 "image": "lun3",
291 "pool": "rbd",
292 "controls": {},
293 "backstore": "user:rbd"
294 })
295 update_request['clients'][0]['luns'].append({"image": "lun3", "pool": "rbd"})
296 response = copy.deepcopy(iscsi_target_response)
297 response['target_iqn'] = target_iqn
298 response['disks'].append(
299 {
300 "image": "lun3",
301 "pool": "rbd",
302 "controls": {},
eafe8130
TL
303 "backstore": "user:rbd",
304 "wwn": "64af6678-9694-4367-bacc-f8eb0baa2",
305 "lun": 2
306
11fdf7f2
TL
307 })
308 response['clients'][0]['luns'].append({"image": "lun3", "pool": "rbd"})
f6b5b4d7 309 self._update_iscsi_target(create_request, update_request, 200, None, response)
11fdf7f2
TL
310
311 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
312 def test_change_disk_image(self, _validate_image_mock):
313 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw8"
314 create_request = copy.deepcopy(iscsi_target_request)
315 create_request['target_iqn'] = target_iqn
316 update_request = copy.deepcopy(create_request)
317 update_request['new_target_iqn'] = target_iqn
318 update_request['disks'][0]['image'] = 'lun0'
319 update_request['clients'][0]['luns'][0]['image'] = 'lun0'
320 response = copy.deepcopy(iscsi_target_response)
321 response['target_iqn'] = target_iqn
322 response['disks'][0]['image'] = 'lun0'
323 response['clients'][0]['luns'][0]['image'] = 'lun0'
f6b5b4d7 324 self._update_iscsi_target(create_request, update_request, 200, None, response)
11fdf7f2
TL
325
326 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
327 def test_change_disk_controls(self, _validate_image_mock):
328 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw9"
329 create_request = copy.deepcopy(iscsi_target_request)
330 create_request['target_iqn'] = target_iqn
331 update_request = copy.deepcopy(create_request)
332 update_request['new_target_iqn'] = target_iqn
333 update_request['disks'][0]['controls'] = {"qfull_timeout": 15}
334 response = copy.deepcopy(iscsi_target_response)
335 response['target_iqn'] = target_iqn
336 response['disks'][0]['controls'] = {"qfull_timeout": 15}
f6b5b4d7 337 self._update_iscsi_target(create_request, update_request, 200, None, response)
11fdf7f2
TL
338
339 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
340 def test_rename_target(self, _validate_image_mock):
341 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw10"
342 new_target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw11"
343 create_request = copy.deepcopy(iscsi_target_request)
344 create_request['target_iqn'] = target_iqn
345 update_request = copy.deepcopy(create_request)
346 update_request['new_target_iqn'] = new_target_iqn
347 response = copy.deepcopy(iscsi_target_response)
348 response['target_iqn'] = new_target_iqn
f6b5b4d7 349 self._update_iscsi_target(create_request, update_request, 200, None, response)
11fdf7f2
TL
350
351 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
352 def test_rename_group(self, _validate_image_mock):
353 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw12"
354 create_request = copy.deepcopy(iscsi_target_request)
355 create_request['target_iqn'] = target_iqn
356 update_request = copy.deepcopy(create_request)
357 update_request['new_target_iqn'] = target_iqn
358 update_request['groups'][0]['group_id'] = 'mygroup0'
359 response = copy.deepcopy(iscsi_target_response)
360 response['target_iqn'] = target_iqn
361 response['groups'][0]['group_id'] = 'mygroup0'
f6b5b4d7 362 self._update_iscsi_target(create_request, update_request, 200, None, response)
11fdf7f2
TL
363
364 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
365 def test_add_client_to_group(self, _validate_image_mock):
366 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw13"
367 create_request = copy.deepcopy(iscsi_target_request)
368 create_request['target_iqn'] = target_iqn
369 update_request = copy.deepcopy(create_request)
370 update_request['new_target_iqn'] = target_iqn
371 update_request['clients'].append(
372 {
373 "luns": [],
374 "client_iqn": "iqn.1994-05.com.redhat:rh7-client3",
375 "auth": {
376 "password": None,
377 "user": None,
378 "mutual_password": None,
379 "mutual_user": None}
380 })
381 update_request['groups'][0]['members'].append('iqn.1994-05.com.redhat:rh7-client3')
382 response = copy.deepcopy(iscsi_target_response)
383 response['target_iqn'] = target_iqn
384 response['clients'].append(
385 {
386 "luns": [],
387 "client_iqn": "iqn.1994-05.com.redhat:rh7-client3",
388 "auth": {
389 "password": None,
390 "user": None,
391 "mutual_password": None,
494da23a
TL
392 "mutual_user": None},
393 "info": {
394 "alias": "",
395 "ip_address": [],
396 "state": {}
397 }
11fdf7f2
TL
398 })
399 response['groups'][0]['members'].append('iqn.1994-05.com.redhat:rh7-client3')
f6b5b4d7 400 self._update_iscsi_target(create_request, update_request, 200, None, response)
11fdf7f2
TL
401
402 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
403 def test_remove_client_from_group(self, _validate_image_mock):
404 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw14"
405 create_request = copy.deepcopy(iscsi_target_request)
406 create_request['target_iqn'] = target_iqn
407 update_request = copy.deepcopy(create_request)
408 update_request['new_target_iqn'] = target_iqn
409 update_request['groups'][0]['members'].remove('iqn.1994-05.com.redhat:rh7-client2')
410 response = copy.deepcopy(iscsi_target_response)
411 response['target_iqn'] = target_iqn
412 response['groups'][0]['members'].remove('iqn.1994-05.com.redhat:rh7-client2')
f6b5b4d7 413 self._update_iscsi_target(create_request, update_request, 200, None, response)
11fdf7f2
TL
414
415 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
416 def test_remove_groups(self, _validate_image_mock):
417 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw15"
418 create_request = copy.deepcopy(iscsi_target_request)
419 create_request['target_iqn'] = target_iqn
420 update_request = copy.deepcopy(create_request)
421 update_request['new_target_iqn'] = target_iqn
422 update_request['groups'] = []
423 response = copy.deepcopy(iscsi_target_response)
424 response['target_iqn'] = target_iqn
425 response['groups'] = []
f6b5b4d7 426 self._update_iscsi_target(create_request, update_request, 200, None, response)
11fdf7f2 427
81eedcae
TL
428 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
429 def test_add_client_to_multiple_groups(self, _validate_image_mock):
430 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw16"
431 create_request = copy.deepcopy(iscsi_target_request)
432 create_request['target_iqn'] = target_iqn
433 create_request['groups'].append(copy.deepcopy(create_request['groups'][0]))
434 create_request['groups'][1]['group_id'] = 'mygroup2'
eafe8130 435 self._task_post('/api/iscsi/target', create_request)
81eedcae
TL
436 self.assertStatus(400)
437 self.assertJsonBody({
438 'detail': 'Each initiator can only be part of 1 group at a time',
439 'code': 'initiator_in_multiple_groups',
440 'component': 'iscsi'
441 })
442
f6b5b4d7
TL
443 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
444 def test_remove_client_lun(self, _validate_image_mock):
445 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw17"
446 create_request = copy.deepcopy(iscsi_target_request)
447 create_request['target_iqn'] = target_iqn
448 create_request['clients'][0]['luns'] = [
449 {"image": "lun1", "pool": "rbd"},
450 {"image": "lun2", "pool": "rbd"},
451 {"image": "lun3", "pool": "rbd"}
452 ]
453 update_request = copy.deepcopy(create_request)
454 update_request['new_target_iqn'] = target_iqn
455 update_request['clients'][0]['luns'] = [
456 {"image": "lun1", "pool": "rbd"},
457 {"image": "lun3", "pool": "rbd"}
458 ]
459 response = copy.deepcopy(iscsi_target_response)
460 response['target_iqn'] = target_iqn
461 response['clients'][0]['luns'] = [
462 {"image": "lun1", "pool": "rbd"},
463 {"image": "lun3", "pool": "rbd"}
464 ]
465 self._update_iscsi_target(create_request, update_request, 200, None, response)
466
467 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
468 def test_change_client_auth(self, _validate_image_mock):
469 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw18"
470 create_request = copy.deepcopy(iscsi_target_request)
471 create_request['target_iqn'] = target_iqn
472 update_request = copy.deepcopy(create_request)
473 update_request['new_target_iqn'] = target_iqn
474 update_request['clients'][0]['auth']['password'] = 'myiscsipasswordX'
475 response = copy.deepcopy(iscsi_target_response)
476 response['target_iqn'] = target_iqn
477 response['clients'][0]['auth']['password'] = 'myiscsipasswordX'
478 self._update_iscsi_target(create_request, update_request, 200, None, response)
479
480 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
481 def test_remove_client_logged_in(self, _validate_image_mock):
482 client_info = {
483 'alias': '',
484 'ip_address': [],
485 'state': {'LOGGED_IN': ['node1']}
486 }
487 # pylint: disable=protected-access
488 IscsiClientMock._instance.clientinfo = client_info
489 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw19"
490 create_request = copy.deepcopy(iscsi_target_request)
491 create_request['target_iqn'] = target_iqn
492 update_request = copy.deepcopy(create_request)
493 update_request['new_target_iqn'] = target_iqn
494 update_request['clients'].pop(0)
495 response = copy.deepcopy(iscsi_target_response)
496 response['target_iqn'] = target_iqn
497 for client in response['clients']:
498 client['info'] = client_info
499 update_response = {
500 'detail': "Client 'iqn.1994-05.com.redhat:rh7-client' cannot be deleted until it's "
501 "logged out",
502 'code': 'client_logged_in',
503 'component': 'iscsi'
504 }
505 self._update_iscsi_target(create_request, update_request, 400, update_response, response)
506
507 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
508 def test_remove_client(self, _validate_image_mock):
509 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw20"
510 create_request = copy.deepcopy(iscsi_target_request)
511 create_request['target_iqn'] = target_iqn
512 update_request = copy.deepcopy(create_request)
513 update_request['new_target_iqn'] = target_iqn
514 update_request['clients'].pop(0)
515 response = copy.deepcopy(iscsi_target_response)
516 response['target_iqn'] = target_iqn
517 response['clients'].pop(0)
518 self._update_iscsi_target(create_request, update_request, 200, None, response)
519
520 def _update_iscsi_target(self, create_request, update_request, update_response_code,
521 update_response, response):
eafe8130 522 self._task_post('/api/iscsi/target', create_request)
11fdf7f2 523 self.assertStatus(201)
eafe8130 524 self._task_put('/api/iscsi/target/{}'.format(create_request['target_iqn']), update_request)
f6b5b4d7
TL
525 self.assertStatus(update_response_code)
526 self.assertJsonBody(update_response)
11fdf7f2
TL
527 self._get('/api/iscsi/target/{}'.format(update_request['new_target_iqn']))
528 self.assertStatus(200)
529 self.assertJsonBody(response)
530
531
532iscsi_target_request = {
533 "target_iqn": "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw",
534 "portals": [
535 {"ip": "192.168.100.202", "host": "node2"},
536 {"ip": "10.0.2.15", "host": "node2"},
537 {"ip": "192.168.100.203", "host": "node3"}
538 ],
539 "disks": [
540 {"image": "lun1", "pool": "rbd", "backstore": "user:rbd",
541 "controls": {"max_data_area_mb": 128}},
542 {"image": "lun2", "pool": "rbd", "backstore": "user:rbd",
543 "controls": {"max_data_area_mb": 128}}
544 ],
545 "clients": [
546 {
547 "luns": [{"image": "lun1", "pool": "rbd"}],
548 "client_iqn": "iqn.1994-05.com.redhat:rh7-client",
549 "auth": {
550 "password": "myiscsipassword1",
551 "user": "myiscsiusername1",
552 "mutual_password": "myiscsipassword2",
553 "mutual_user": "myiscsiusername2"}
554 },
555 {
556 "luns": [],
557 "client_iqn": "iqn.1994-05.com.redhat:rh7-client2",
558 "auth": {
559 "password": "myiscsipassword3",
560 "user": "myiscsiusername3",
561 "mutual_password": "myiscsipassword4",
562 "mutual_user": "myiscsiusername4"
563 }
564 }
565 ],
566 "acl_enabled": True,
eafe8130
TL
567 "auth": {
568 "password": "",
569 "user": "",
570 "mutual_password": "",
571 "mutual_user": ""},
11fdf7f2
TL
572 "target_controls": {},
573 "groups": [
574 {
575 "group_id": "mygroup",
576 "disks": [{"pool": "rbd", "image": "lun2"}],
577 "members": ["iqn.1994-05.com.redhat:rh7-client2"]
578 }
579 ]
580}
581
582iscsi_target_response = {
583 'target_iqn': 'iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw',
584 'portals': [
585 {'host': 'node2', 'ip': '10.0.2.15'},
586 {'host': 'node2', 'ip': '192.168.100.202'},
587 {'host': 'node3', 'ip': '192.168.100.203'}
588 ],
589 'disks': [
590 {'pool': 'rbd', 'image': 'lun1', 'backstore': 'user:rbd',
eafe8130 591 'wwn': '64af6678-9694-4367-bacc-f8eb0baa0', 'lun': 0,
11fdf7f2
TL
592 'controls': {'max_data_area_mb': 128}},
593 {'pool': 'rbd', 'image': 'lun2', 'backstore': 'user:rbd',
eafe8130 594 'wwn': '64af6678-9694-4367-bacc-f8eb0baa1', 'lun': 1,
11fdf7f2
TL
595 'controls': {'max_data_area_mb': 128}}
596 ],
597 'clients': [
598 {
599 'client_iqn': 'iqn.1994-05.com.redhat:rh7-client',
600 'luns': [{'pool': 'rbd', 'image': 'lun1'}],
601 'auth': {
602 'user': 'myiscsiusername1',
603 'password': 'myiscsipassword1',
604 'mutual_password': 'myiscsipassword2',
605 'mutual_user': 'myiscsiusername2'
494da23a
TL
606 },
607 'info': {
608 'alias': '',
609 'ip_address': [],
610 'state': {}
11fdf7f2
TL
611 }
612 },
613 {
614 'client_iqn': 'iqn.1994-05.com.redhat:rh7-client2',
615 'luns': [],
616 'auth': {
617 'user': 'myiscsiusername3',
618 'password': 'myiscsipassword3',
619 'mutual_password': 'myiscsipassword4',
620 'mutual_user': 'myiscsiusername4'
494da23a
TL
621 },
622 'info': {
623 'alias': '',
624 'ip_address': [],
625 'state': {}
11fdf7f2
TL
626 }
627 }
628 ],
629 "acl_enabled": True,
eafe8130
TL
630 "auth": {
631 "password": "",
632 "user": "",
633 "mutual_password": "",
634 "mutual_user": ""},
11fdf7f2
TL
635 'groups': [
636 {
637 'group_id': 'mygroup',
638 'disks': [{'pool': 'rbd', 'image': 'lun2'}],
639 'members': ['iqn.1994-05.com.redhat:rh7-client2']
640 }
641 ],
642 'target_controls': {},
643 'info': {
644 'num_sessions': 0
645 }
646}
647
648
649class IscsiClientMock(object):
650
651 _instance = None
652
653 def __init__(self):
654 self.gateway_name = None
655 self.service_url = None
656 self.config = {
657 "created": "2019/01/17 08:57:16",
658 "discovery_auth": {
659 "username": "",
660 "password": "",
661 "password_encryption_enabled": False,
662 "mutual_username": "",
663 "mutual_password": "",
664 "mutual_password_encryption_enabled": False
665 },
666 "disks": {},
667 "epoch": 0,
668 "gateways": {},
669 "targets": {},
670 "updated": "",
eafe8130 671 "version": 11
11fdf7f2 672 }
f6b5b4d7
TL
673 self.clientinfo = {
674 'alias': '',
675 'ip_address': [],
676 'state': {}
677 }
11fdf7f2
TL
678
679 @classmethod
680 def instance(cls, gateway_name=None, service_url=None):
681 cls._instance.gateway_name = gateway_name
682 cls._instance.service_url = service_url
683 # pylint: disable=unused-argument
684 return cls._instance
685
686 def ping(self):
687 return {
688 "message": "pong"
689 }
690
691 def get_settings(self):
692 return {
1911f103 693 "api_version": 2,
11fdf7f2
TL
694 "backstores": [
695 "user:rbd"
696 ],
697 "config": {
698 "minimum_gateways": 2
699 },
700 "default_backstore": "user:rbd",
701 "required_rbd_features": {
702 "rbd": 0,
703 "user:rbd": 4,
704 },
81eedcae
TL
705 "unsupported_rbd_features": {
706 "rbd": 88,
707 "user:rbd": 0,
11fdf7f2
TL
708 },
709 "disk_default_controls": {
710 "user:rbd": {
711 "hw_max_sectors": 1024,
712 "max_data_area_mb": 8,
713 "osd_op_timeout": 30,
714 "qfull_timeout": 5
715 }
716 },
717 "target_default_controls": {
718 "cmdsn_depth": 128,
719 "dataout_timeout": 20,
720 "first_burst_length": 262144,
721 "immediate_data": "Yes",
722 "initial_r2t": "Yes",
723 "max_burst_length": 524288,
724 "max_outstanding_r2t": 1,
725 "max_recv_data_segment_length": 262144,
726 "max_xmit_data_segment_length": 262144,
727 "nopin_response_timeout": 5,
728 "nopin_timeout": 5
729 }
730 }
731
732 def get_config(self):
eafe8130 733 return copy.deepcopy(self.config)
11fdf7f2
TL
734
735 def create_target(self, target_iqn, target_controls):
736 self.config['targets'][target_iqn] = {
737 "clients": {},
738 "acl_enabled": True,
eafe8130
TL
739 "auth": {
740 "username": "",
741 "password": "",
742 "password_encryption_enabled": False,
743 "mutual_username": "",
744 "mutual_password": "",
745 "mutual_password_encryption_enabled": False
746 },
11fdf7f2
TL
747 "controls": target_controls,
748 "created": "2019/01/17 09:22:34",
eafe8130 749 "disks": {},
11fdf7f2
TL
750 "groups": {},
751 "portals": {}
752 }
753
494da23a 754 def create_gateway(self, target_iqn, gateway_name, ip_addresses):
11fdf7f2
TL
755 target_config = self.config['targets'][target_iqn]
756 if 'ip_list' not in target_config:
757 target_config['ip_list'] = []
494da23a 758 target_config['ip_list'] += ip_addresses
11fdf7f2 759 target_config['portals'][gateway_name] = {
494da23a 760 "portal_ip_addresses": ip_addresses
11fdf7f2
TL
761 }
762
494da23a
TL
763 def delete_gateway(self, target_iqn, gateway_name):
764 target_config = self.config['targets'][target_iqn]
765 portal_config = target_config['portals'][gateway_name]
766 for ip in portal_config['portal_ip_addresses']:
767 target_config['ip_list'].remove(ip)
768 target_config['portals'].pop(gateway_name)
769
eafe8130
TL
770 def create_disk(self, pool, image, backstore, wwn):
771 if wwn is None:
772 wwn = '64af6678-9694-4367-bacc-f8eb0baa' + str(len(self.config['disks']))
11fdf7f2
TL
773 image_id = '{}/{}'.format(pool, image)
774 self.config['disks'][image_id] = {
775 "pool": pool,
776 "image": image,
777 "backstore": backstore,
eafe8130
TL
778 "controls": {},
779 "wwn": wwn
11fdf7f2
TL
780 }
781
eafe8130 782 def create_target_lun(self, target_iqn, image_id, lun):
11fdf7f2 783 target_config = self.config['targets'][target_iqn]
eafe8130
TL
784 if lun is None:
785 lun = len(target_config['disks'])
786 target_config['disks'][image_id] = {
787 "lun_id": lun
788 }
11fdf7f2
TL
789 self.config['disks'][image_id]['owner'] = list(target_config['portals'].keys())[0]
790
791 def reconfigure_disk(self, pool, image, controls):
792 image_id = '{}/{}'.format(pool, image)
eafe8130
TL
793 settings = self.get_settings()
794 backstore = self.config['disks'][image_id]['backstore']
795 disk_default_controls = settings['disk_default_controls'][backstore]
796 new_controls = {}
797 for control_k, control_v in controls.items():
798 if control_v != disk_default_controls[control_k]:
799 new_controls[control_k] = control_v
800 self.config['disks'][image_id]['controls'] = new_controls
11fdf7f2
TL
801
802 def create_client(self, target_iqn, client_iqn):
803 target_config = self.config['targets'][target_iqn]
804 target_config['clients'][client_iqn] = {
805 "auth": {
806 "username": "",
807 "password": "",
808 "password_encryption_enabled": False,
809 "mutual_username": "",
810 "mutual_password": "",
811 "mutual_password_encryption_enabled": False
812 },
813 "group_name": "",
814 "luns": {}
815 }
816
817 def create_client_lun(self, target_iqn, client_iqn, image_id):
818 target_config = self.config['targets'][target_iqn]
819 target_config['clients'][client_iqn]['luns'][image_id] = {}
820
eafe8130
TL
821 def delete_client_lun(self, target_iqn, client_iqn, image_id):
822 target_config = self.config['targets'][target_iqn]
823 del target_config['clients'][client_iqn]['luns'][image_id]
824
11fdf7f2
TL
825 def create_client_auth(self, target_iqn, client_iqn, user, password, m_user, m_password):
826 target_config = self.config['targets'][target_iqn]
827 target_config['clients'][client_iqn]['auth']['username'] = user
828 target_config['clients'][client_iqn]['auth']['password'] = password
829 target_config['clients'][client_iqn]['auth']['mutual_username'] = m_user
830 target_config['clients'][client_iqn]['auth']['mutual_password'] = m_password
831
832 def create_group(self, target_iqn, group_name, members, image_ids):
833 target_config = self.config['targets'][target_iqn]
834 target_config['groups'][group_name] = {
835 "disks": {},
836 "members": []
837 }
838 for image_id in image_ids:
839 target_config['groups'][group_name]['disks'][image_id] = {}
840 target_config['groups'][group_name]['members'] = members
841
842 def delete_group(self, target_iqn, group_name):
843 target_config = self.config['targets'][target_iqn]
844 del target_config['groups'][group_name]
845
846 def delete_client(self, target_iqn, client_iqn):
847 target_config = self.config['targets'][target_iqn]
848 del target_config['clients'][client_iqn]
849
850 def delete_target_lun(self, target_iqn, image_id):
851 target_config = self.config['targets'][target_iqn]
eafe8130 852 target_config['disks'].pop(image_id)
11fdf7f2
TL
853 del self.config['disks'][image_id]['owner']
854
855 def delete_disk(self, pool, image):
856 image_id = '{}/{}'.format(pool, image)
857 del self.config['disks'][image_id]
858
859 def delete_target(self, target_iqn):
860 del self.config['targets'][target_iqn]
861
862 def get_ip_addresses(self):
863 ips = {
864 'node1': ['192.168.100.201'],
865 'node2': ['192.168.100.202', '10.0.2.15'],
866 'node3': ['192.168.100.203']
867 }
868 return {'data': ips[self.gateway_name]}
869
870 def get_hostname(self):
871 hostnames = {
872 'https://admin:admin@10.17.5.1:5001': 'node1',
873 'https://admin:admin@10.17.5.2:5001': 'node2',
874 'https://admin:admin@10.17.5.3:5001': 'node3'
875 }
876 if self.service_url not in hostnames:
877 raise RequestException('No route to host')
878 return {'data': hostnames[self.service_url]}
879
880 def update_discoveryauth(self, user, password, mutual_user, mutual_password):
881 self.config['discovery_auth']['username'] = user
882 self.config['discovery_auth']['password'] = password
883 self.config['discovery_auth']['mutual_username'] = mutual_user
884 self.config['discovery_auth']['mutual_password'] = mutual_password
885
eafe8130 886 def update_targetacl(self, target_iqn, action):
11fdf7f2
TL
887 self.config['targets'][target_iqn]['acl_enabled'] = (action == 'enable_acl')
888
eafe8130
TL
889 def update_targetauth(self, target_iqn, user, password, mutual_user, mutual_password):
890 target_config = self.config['targets'][target_iqn]
891 target_config['auth']['username'] = user
892 target_config['auth']['password'] = password
893 target_config['auth']['mutual_username'] = mutual_user
894 target_config['auth']['mutual_password'] = mutual_password
895
494da23a
TL
896 def get_targetinfo(self, target_iqn):
897 # pylint: disable=unused-argument
11fdf7f2
TL
898 return {
899 'num_sessions': 0
900 }
494da23a
TL
901
902 def get_clientinfo(self, target_iqn, client_iqn):
903 # pylint: disable=unused-argument
f6b5b4d7 904 return self.clientinfo