]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/tests/test_iscsi.py
import quincy beta 17.1.0
[ceph.git] / ceph / src / pybind / mgr / dashboard / tests / test_iscsi.py
1 # pylint: disable=too-many-public-methods, too-many-lines
2
3 import copy
4 import errno
5 import json
6 import unittest
7
8 try:
9 import mock
10 except ImportError:
11 import unittest.mock as mock
12
13 from mgr_module import ERROR_MSG_NO_INPUT_FILE
14
15 from .. import mgr
16 from ..controllers.iscsi import Iscsi, IscsiTarget, IscsiUi
17 from ..exceptions import DashboardException
18 from ..rest_client import RequestException
19 from ..services.exception import handle_request_error
20 from ..services.iscsi_client import IscsiClient
21 from ..services.orchestrator import OrchClient
22 from ..tests import CLICommandTestMixin, CmdException, ControllerTestCase, KVStoreMockMixin
23 from ..tools import NotificationQueue, TaskManager
24
25
26 class IscsiTestCli(unittest.TestCase, CLICommandTestMixin):
27
28 def setUp(self):
29 self.mock_kv_store()
30 # pylint: disable=protected-access
31 IscsiClientMock._instance = IscsiClientMock()
32 IscsiClient.instance = IscsiClientMock.instance
33
34 def test_cli_add_gateway_invalid_url(self):
35 with self.assertRaises(CmdException) as ctx:
36 self.exec_cmd('iscsi-gateway-add', name='node1',
37 inbuf='http:/hello.com')
38
39 self.assertEqual(ctx.exception.retcode, -errno.EINVAL)
40 self.assertEqual(str(ctx.exception),
41 "Invalid service URL 'http:/hello.com'. Valid format: "
42 "'<scheme>://<username>:<password>@<host>[:port]'.")
43
44 def test_cli_add_gateway_empty_url(self):
45 with self.assertRaises(CmdException) as ctx:
46 self.exec_cmd('iscsi-gateway-add', name='node1',
47 inbuf='')
48
49 self.assertEqual(ctx.exception.retcode, -errno.EINVAL)
50 self.assertIn(ERROR_MSG_NO_INPUT_FILE, str(ctx.exception))
51
52 def test_cli_add_gateway(self):
53 self.exec_cmd('iscsi-gateway-add', name='node1',
54 inbuf='https://admin:admin@10.17.5.1:5001')
55 self.exec_cmd('iscsi-gateway-add', name='node2',
56 inbuf='https://admin:admin@10.17.5.2:5001')
57 iscsi_config = json.loads(self.get_key("_iscsi_config"))
58 self.assertEqual(iscsi_config['gateways'], {
59 'node1': {
60 'service_url': 'https://admin:admin@10.17.5.1:5001'
61 },
62 'node2': {
63 'service_url': 'https://admin:admin@10.17.5.2:5001'
64 }
65 })
66
67 def test_cli_remove_gateway(self):
68 self.test_cli_add_gateway()
69 self.exec_cmd('iscsi-gateway-rm', name='node1')
70 iscsi_config = json.loads(self.get_key("_iscsi_config"))
71 self.assertEqual(iscsi_config['gateways'], {
72 'node2': {
73 'service_url': 'https://admin:admin@10.17.5.2:5001'
74 }
75 })
76
77
78 class IscsiTestController(ControllerTestCase, KVStoreMockMixin):
79
80 @classmethod
81 def setup_server(cls):
82 NotificationQueue.start_queue()
83 TaskManager.init()
84 OrchClient.instance().available = lambda: False
85 mgr.rados.side_effect = None
86 cls.setup_controllers([Iscsi, IscsiTarget])
87
88 @classmethod
89 def tearDownClass(cls):
90 NotificationQueue.stop()
91
92 def setUp(self):
93 self.mock_kv_store()
94 self.CONFIG_KEY_DICT['_iscsi_config'] = '''
95 {
96 "gateways": {
97 "node1": {
98 "service_url": "https://admin:admin@10.17.5.1:5001"
99 },
100 "node2": {
101 "service_url": "https://admin:admin@10.17.5.2:5001"
102 }
103 }
104 }
105 '''
106 # pylint: disable=protected-access
107 IscsiClientMock._instance = IscsiClientMock()
108 IscsiClient.instance = IscsiClientMock.instance
109
110 def test_enable_discoveryauth(self):
111 discoveryauth = {
112 'user': 'myiscsiusername',
113 'password': 'myiscsipassword',
114 'mutual_user': 'myiscsiusername2',
115 'mutual_password': 'myiscsipassword2'
116 }
117 self._put('/api/iscsi/discoveryauth', discoveryauth)
118 self.assertStatus(200)
119 self.assertJsonBody(discoveryauth)
120 self._get('/api/iscsi/discoveryauth')
121 self.assertStatus(200)
122 self.assertJsonBody(discoveryauth)
123
124 def test_bad_discoveryauth(self):
125 discoveryauth = {
126 'user': 'myiscsiusername',
127 'password': 'myiscsipasswordmyiscsipasswordmyiscsipassword',
128 'mutual_user': '',
129 'mutual_password': ''
130 }
131 put_response = {
132 'detail': 'Bad authentication',
133 'code': 'target_bad_auth',
134 'component': 'iscsi'
135 }
136 get_response = {
137 'user': '',
138 'password': '',
139 'mutual_user': '',
140 'mutual_password': ''
141 }
142 self._put('/api/iscsi/discoveryauth', discoveryauth)
143 self.assertStatus(400)
144 self.assertJsonBody(put_response)
145 self._get('/api/iscsi/discoveryauth')
146 self.assertStatus(200)
147 self.assertJsonBody(get_response)
148
149 def test_disable_discoveryauth(self):
150 discoveryauth = {
151 'user': '',
152 'password': '',
153 'mutual_user': '',
154 'mutual_password': ''
155 }
156 self._put('/api/iscsi/discoveryauth', discoveryauth)
157 self.assertStatus(200)
158 self.assertJsonBody(discoveryauth)
159 self._get('/api/iscsi/discoveryauth')
160 self.assertStatus(200)
161 self.assertJsonBody(discoveryauth)
162
163 def test_list_empty(self):
164 self._get('/api/iscsi/target')
165 self.assertStatus(200)
166 self.assertJsonBody([])
167
168 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
169 def test_list(self, _validate_image_mock):
170 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw1"
171 request = copy.deepcopy(iscsi_target_request)
172 request['target_iqn'] = target_iqn
173 self._task_post('/api/iscsi/target', request)
174 self.assertStatus(201)
175 self._get('/api/iscsi/target')
176 self.assertStatus(200)
177 response = copy.deepcopy(iscsi_target_response)
178 response['target_iqn'] = target_iqn
179 self.assertJsonBody([response])
180
181 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
182 def test_create(self, _validate_image_mock):
183 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw2"
184 request = copy.deepcopy(iscsi_target_request)
185 request['target_iqn'] = target_iqn
186 self._task_post('/api/iscsi/target', request)
187 self.assertStatus(201)
188 self._get('/api/iscsi/target/{}'.format(request['target_iqn']))
189 self.assertStatus(200)
190 response = copy.deepcopy(iscsi_target_response)
191 response['target_iqn'] = target_iqn
192 self.assertJsonBody(response)
193
194 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
195 def test_create_acl_enabled(self, _validate_image_mock):
196 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw2"
197 request = copy.deepcopy(iscsi_target_request)
198 request['target_iqn'] = target_iqn
199 request['acl_enabled'] = False
200 self._task_post('/api/iscsi/target', request)
201 self.assertStatus(201)
202 self._get('/api/iscsi/target/{}'.format(request['target_iqn']))
203 self.assertStatus(200)
204 response = copy.deepcopy(iscsi_target_response)
205 response['target_iqn'] = target_iqn
206 response['acl_enabled'] = False
207 self.assertJsonBody(response)
208
209 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._create')
210 def test_create_error(self, _create_mock):
211 # pylint: disable=protected-access
212 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw2"
213 request = copy.deepcopy(iscsi_target_request)
214 request['target_iqn'] = target_iqn
215 request['config'] = ""
216 request['settings'] = ""
217 request['task_progress_begin'] = 0
218 request['task_progress_end'] = 100
219 _create_mock.side_effect = RequestException("message error")
220 with self.assertRaises(DashboardException):
221 with handle_request_error('iscsi'):
222 IscsiTarget._create(**request)
223
224 def test_validate_error_iqn(self):
225 # pylint: disable=protected-access
226 with self.assertRaises(DashboardException) as ctx:
227 IscsiTarget._validate(None, None, None, None, None, None)
228 self.assertEquals(ctx.exception.__str__(),
229 "Target IQN is required")
230
231 def test_validate_error_portals(self):
232 # pylint: disable=protected-access
233 target_iqn = iscsi_target_request['target_iqn']
234 target_controls = iscsi_target_request['target_controls']
235 portals = {}
236 disks = iscsi_target_request['disks']
237 groups = iscsi_target_request['groups']
238 settings = {'config': {'minimum_gateways': 1}}
239 with self.assertRaises(DashboardException) as ctx:
240 IscsiTarget._validate(target_iqn, target_controls, portals, disks, groups, settings)
241 self.assertEquals(ctx.exception.__str__(),
242 "At least one portal is required")
243 settings = {'config': {'minimum_gateways': 2}}
244 with self.assertRaises(DashboardException) as ctx:
245 IscsiTarget._validate(target_iqn, target_controls, portals, disks, groups, settings)
246 self.assertEquals(ctx.exception.__str__(),
247 "At least 2 portals are required")
248
249 def test_validate_error_target_control(self):
250 # pylint: disable=protected-access
251 target_iqn = iscsi_target_request['target_iqn']
252 target_controls = {
253 'target_name': 0
254 }
255 portals = iscsi_target_request['portals']
256 disks = iscsi_target_request['disks']
257 groups = iscsi_target_request['groups']
258 settings = {
259 'config': {'minimum_gateways': 1},
260 'target_controls_limits': {
261 'target_name': {
262 'min': 1,
263 'max': 2,
264 }
265 }
266 }
267 with self.assertRaises(DashboardException) as ctx:
268 IscsiTarget._validate(target_iqn, target_controls, portals, disks, groups, settings)
269 self.assertEquals(ctx.exception.__str__(),
270 "Target control target_name must be >= 1")
271 target_controls = {
272 'target_name': 3
273 }
274 with self.assertRaises(DashboardException) as ctx:
275 IscsiTarget._validate(target_iqn, target_controls, portals, disks, groups, settings)
276 self.assertEquals(ctx.exception.__str__(),
277 "Target control target_name must be <= 2")
278
279 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
280 def test_validate_error_disk_control(self, _validate_image_mock):
281 # pylint: disable=protected-access
282 target_iqn = iscsi_target_request['target_iqn']
283 target_controls = {}
284 portals = iscsi_target_request['portals']
285 disks = iscsi_target_request['disks']
286 groups = iscsi_target_request['groups']
287 settings = {
288 'config': {'minimum_gateways': 1},
289 'required_rbd_features': {
290 'user:rbd': 0
291 },
292 'unsupported_rbd_features': {
293 'user:rbd': 0
294 },
295 'disk_controls_limits': {
296 'user:rbd': {'max_data_area_mb': {
297 'min': 129,
298 'max': 127,
299 }}
300 }
301 }
302 with self.assertRaises(DashboardException) as ctx:
303 IscsiTarget._validate(target_iqn, target_controls, portals, disks, groups, settings)
304 self.assertEquals(ctx.exception.__str__(),
305 "Disk control max_data_area_mb must be >= 129")
306 settings['disk_controls_limits']['user:rbd']['max_data_area_mb']['min'] = 1
307 with self.assertRaises(DashboardException) as ctx:
308 IscsiTarget._validate(target_iqn, target_controls, portals, disks, groups, settings)
309 self.assertEquals(ctx.exception.__str__(),
310 "Disk control max_data_area_mb must be <= 127")
311
312 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
313 def test_delete(self, _validate_image_mock):
314 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw3"
315 request = copy.deepcopy(iscsi_target_request)
316 request['target_iqn'] = target_iqn
317 self._task_post('/api/iscsi/target', request)
318 self.assertStatus(201)
319 self._task_delete('/api/iscsi/target/{}'.format(request['target_iqn']))
320 self.assertStatus(204)
321 self._get('/api/iscsi/target')
322 self.assertStatus(200)
323 self.assertJsonBody([])
324
325 @mock.patch('dashboard.tools.TaskManager.current_task')
326 def test_delete_raises_exception(self, _validate_image_mock):
327 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw3"
328 request = copy.deepcopy(iscsi_target_request)
329 request['target_iqn'] = target_iqn
330 configs = {'targets': {target_iqn: {'portals': {}}}}
331 with self.assertRaises(DashboardException):
332 # pylint: disable=protected-access
333 IscsiTarget._delete(target_iqn, configs, 0, 100)
334
335 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
336 def test_add_client(self, _validate_image_mock):
337 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw4"
338 create_request = copy.deepcopy(iscsi_target_request)
339 create_request['target_iqn'] = target_iqn
340 update_request = copy.deepcopy(create_request)
341 update_request['new_target_iqn'] = target_iqn
342 update_request['clients'].append(
343 {
344 "luns": [{"image": "lun1", "pool": "rbd"}],
345 "client_iqn": "iqn.1994-05.com.redhat:rh7-client3",
346 "auth": {
347 "password": "myiscsipassword5",
348 "user": "myiscsiusername5",
349 "mutual_password": "myiscsipassword6",
350 "mutual_user": "myiscsiusername6"}
351 })
352 response = copy.deepcopy(iscsi_target_response)
353 response['target_iqn'] = target_iqn
354 response['clients'].append(
355 {
356 "luns": [{"image": "lun1", "pool": "rbd"}],
357 "client_iqn": "iqn.1994-05.com.redhat:rh7-client3",
358 "auth": {
359 "password": "myiscsipassword5",
360 "user": "myiscsiusername5",
361 "mutual_password": "myiscsipassword6",
362 "mutual_user": "myiscsiusername6"},
363 "info": {
364 "alias": "",
365 "ip_address": [],
366 "state": {}
367 }
368 })
369 self._update_iscsi_target(create_request, update_request, 200, None, response)
370
371 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
372 def test_add_bad_client(self, _validate_image_mock):
373 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw4"
374 create_request = copy.deepcopy(iscsi_target_request)
375 create_request['target_iqn'] = target_iqn
376 update_request = copy.deepcopy(create_request)
377 update_request['new_target_iqn'] = target_iqn
378 update_request['clients'].append(
379 {
380 "luns": [{"image": "lun1", "pool": "rbd"}],
381 "client_iqn": "iqn.1994-05.com.redhat:rh7-client4",
382 "auth": {
383 "password": "myiscsipassword7myiscsipassword7myiscsipasswo",
384 "user": "myiscsiusername7",
385 "mutual_password": "myiscsipassword8",
386 "mutual_user": "myiscsiusername8"}
387 })
388 response = copy.deepcopy(iscsi_target_response)
389 response['target_iqn'] = target_iqn
390
391 self._task_post('/api/iscsi/target', create_request)
392 self.assertStatus(201)
393 self._task_put('/api/iscsi/target/{}'.format(create_request['target_iqn']), update_request)
394 self.assertStatus(400)
395 self._get('/api/iscsi/target/{}'.format(update_request['new_target_iqn']))
396 self.assertStatus(200)
397 self.assertJsonBody(response)
398
399 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
400 def test_change_client_password(self, _validate_image_mock):
401 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw5"
402 create_request = copy.deepcopy(iscsi_target_request)
403 create_request['target_iqn'] = target_iqn
404 update_request = copy.deepcopy(create_request)
405 update_request['new_target_iqn'] = target_iqn
406 update_request['clients'][0]['auth']['password'] = 'MyNewPassword'
407 response = copy.deepcopy(iscsi_target_response)
408 response['target_iqn'] = target_iqn
409 response['clients'][0]['auth']['password'] = 'MyNewPassword'
410 self._update_iscsi_target(create_request, update_request, 200, None, response)
411
412 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
413 def test_rename_client(self, _validate_image_mock):
414 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw6"
415 create_request = copy.deepcopy(iscsi_target_request)
416 create_request['target_iqn'] = target_iqn
417 update_request = copy.deepcopy(create_request)
418 update_request['new_target_iqn'] = target_iqn
419 update_request['clients'][0]['client_iqn'] = 'iqn.1994-05.com.redhat:rh7-client0'
420 response = copy.deepcopy(iscsi_target_response)
421 response['target_iqn'] = target_iqn
422 response['clients'][0]['client_iqn'] = 'iqn.1994-05.com.redhat:rh7-client0'
423 self._update_iscsi_target(create_request, update_request, 200, None, response)
424
425 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
426 def test_add_disk(self, _validate_image_mock):
427 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw7"
428 create_request = copy.deepcopy(iscsi_target_request)
429 create_request['target_iqn'] = target_iqn
430 update_request = copy.deepcopy(create_request)
431 update_request['new_target_iqn'] = target_iqn
432 update_request['disks'].append(
433 {
434 "image": "lun3",
435 "pool": "rbd",
436 "controls": {},
437 "backstore": "user:rbd"
438 })
439 update_request['clients'][0]['luns'].append({"image": "lun3", "pool": "rbd"})
440 response = copy.deepcopy(iscsi_target_response)
441 response['target_iqn'] = target_iqn
442 response['disks'].append(
443 {
444 "image": "lun3",
445 "pool": "rbd",
446 "controls": {},
447 "backstore": "user:rbd",
448 "wwn": "64af6678-9694-4367-bacc-f8eb0baa2",
449 "lun": 2
450
451 })
452 response['clients'][0]['luns'].append({"image": "lun3", "pool": "rbd"})
453 self._update_iscsi_target(create_request, update_request, 200, None, response)
454
455 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
456 def test_change_disk_image(self, _validate_image_mock):
457 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw8"
458 create_request = copy.deepcopy(iscsi_target_request)
459 create_request['target_iqn'] = target_iqn
460 update_request = copy.deepcopy(create_request)
461 update_request['new_target_iqn'] = target_iqn
462 update_request['disks'][0]['image'] = 'lun0'
463 update_request['clients'][0]['luns'][0]['image'] = 'lun0'
464 response = copy.deepcopy(iscsi_target_response)
465 response['target_iqn'] = target_iqn
466 response['disks'][0]['image'] = 'lun0'
467 response['clients'][0]['luns'][0]['image'] = 'lun0'
468 self._update_iscsi_target(create_request, update_request, 200, None, response)
469
470 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
471 def test_change_disk_controls(self, _validate_image_mock):
472 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw9"
473 create_request = copy.deepcopy(iscsi_target_request)
474 create_request['target_iqn'] = target_iqn
475 update_request = copy.deepcopy(create_request)
476 update_request['new_target_iqn'] = target_iqn
477 update_request['disks'][0]['controls'] = {"qfull_timeout": 15}
478 response = copy.deepcopy(iscsi_target_response)
479 response['target_iqn'] = target_iqn
480 response['disks'][0]['controls'] = {"qfull_timeout": 15}
481 self._update_iscsi_target(create_request, update_request, 200, None, response)
482
483 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
484 def test_rename_target(self, _validate_image_mock):
485 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw10"
486 new_target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw11"
487 create_request = copy.deepcopy(iscsi_target_request)
488 create_request['target_iqn'] = target_iqn
489 update_request = copy.deepcopy(create_request)
490 update_request['new_target_iqn'] = new_target_iqn
491 response = copy.deepcopy(iscsi_target_response)
492 response['target_iqn'] = new_target_iqn
493 self._update_iscsi_target(create_request, update_request, 200, None, response)
494
495 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
496 def test_rename_group(self, _validate_image_mock):
497 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw12"
498 create_request = copy.deepcopy(iscsi_target_request)
499 create_request['target_iqn'] = target_iqn
500 update_request = copy.deepcopy(create_request)
501 update_request['new_target_iqn'] = target_iqn
502 update_request['groups'][0]['group_id'] = 'mygroup0'
503 response = copy.deepcopy(iscsi_target_response)
504 response['target_iqn'] = target_iqn
505 response['groups'][0]['group_id'] = 'mygroup0'
506 self._update_iscsi_target(create_request, update_request, 200, None, response)
507
508 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
509 def test_add_client_to_group(self, _validate_image_mock):
510 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw13"
511 create_request = copy.deepcopy(iscsi_target_request)
512 create_request['target_iqn'] = target_iqn
513 update_request = copy.deepcopy(create_request)
514 update_request['new_target_iqn'] = target_iqn
515 update_request['clients'].append(
516 {
517 "luns": [],
518 "client_iqn": "iqn.1994-05.com.redhat:rh7-client3",
519 "auth": {
520 "password": None,
521 "user": None,
522 "mutual_password": None,
523 "mutual_user": None}
524 })
525 update_request['groups'][0]['members'].append('iqn.1994-05.com.redhat:rh7-client3')
526 response = copy.deepcopy(iscsi_target_response)
527 response['target_iqn'] = target_iqn
528 response['clients'].append(
529 {
530 "luns": [],
531 "client_iqn": "iqn.1994-05.com.redhat:rh7-client3",
532 "auth": {
533 "password": None,
534 "user": None,
535 "mutual_password": None,
536 "mutual_user": None},
537 "info": {
538 "alias": "",
539 "ip_address": [],
540 "state": {}
541 }
542 })
543 response['groups'][0]['members'].append('iqn.1994-05.com.redhat:rh7-client3')
544 self._update_iscsi_target(create_request, update_request, 200, None, response)
545
546 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
547 def test_remove_client_from_group(self, _validate_image_mock):
548 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw14"
549 create_request = copy.deepcopy(iscsi_target_request)
550 create_request['target_iqn'] = target_iqn
551 update_request = copy.deepcopy(create_request)
552 update_request['new_target_iqn'] = target_iqn
553 update_request['groups'][0]['members'].remove('iqn.1994-05.com.redhat:rh7-client2')
554 response = copy.deepcopy(iscsi_target_response)
555 response['target_iqn'] = target_iqn
556 response['groups'][0]['members'].remove('iqn.1994-05.com.redhat:rh7-client2')
557 self._update_iscsi_target(create_request, update_request, 200, None, response)
558
559 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
560 def test_remove_groups(self, _validate_image_mock):
561 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw15"
562 create_request = copy.deepcopy(iscsi_target_request)
563 create_request['target_iqn'] = target_iqn
564 update_request = copy.deepcopy(create_request)
565 update_request['new_target_iqn'] = target_iqn
566 update_request['groups'] = []
567 response = copy.deepcopy(iscsi_target_response)
568 response['target_iqn'] = target_iqn
569 response['groups'] = []
570 self._update_iscsi_target(create_request, update_request, 200, None, response)
571
572 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
573 def test_add_client_to_multiple_groups(self, _validate_image_mock):
574 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw16"
575 create_request = copy.deepcopy(iscsi_target_request)
576 create_request['target_iqn'] = target_iqn
577 create_request['groups'].append(copy.deepcopy(create_request['groups'][0]))
578 create_request['groups'][1]['group_id'] = 'mygroup2'
579 self._task_post('/api/iscsi/target', create_request)
580 self.assertStatus(400)
581 self.assertJsonBody({
582 'detail': 'Each initiator can only be part of 1 group at a time',
583 'code': 'initiator_in_multiple_groups',
584 'component': 'iscsi'
585 })
586
587 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
588 def test_remove_client_lun(self, _validate_image_mock):
589 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw17"
590 create_request = copy.deepcopy(iscsi_target_request)
591 create_request['target_iqn'] = target_iqn
592 create_request['clients'][0]['luns'] = [
593 {"image": "lun1", "pool": "rbd"},
594 {"image": "lun2", "pool": "rbd"},
595 {"image": "lun3", "pool": "rbd"}
596 ]
597 update_request = copy.deepcopy(create_request)
598 update_request['new_target_iqn'] = target_iqn
599 update_request['clients'][0]['luns'] = [
600 {"image": "lun1", "pool": "rbd"},
601 {"image": "lun3", "pool": "rbd"}
602 ]
603 response = copy.deepcopy(iscsi_target_response)
604 response['target_iqn'] = target_iqn
605 response['clients'][0]['luns'] = [
606 {"image": "lun1", "pool": "rbd"},
607 {"image": "lun3", "pool": "rbd"}
608 ]
609 self._update_iscsi_target(create_request, update_request, 200, None, response)
610
611 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
612 def test_change_client_auth(self, _validate_image_mock):
613 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw18"
614 create_request = copy.deepcopy(iscsi_target_request)
615 create_request['target_iqn'] = target_iqn
616 update_request = copy.deepcopy(create_request)
617 update_request['new_target_iqn'] = target_iqn
618 update_request['clients'][0]['auth']['password'] = 'myiscsipasswordX'
619 response = copy.deepcopy(iscsi_target_response)
620 response['target_iqn'] = target_iqn
621 response['clients'][0]['auth']['password'] = 'myiscsipasswordX'
622 self._update_iscsi_target(create_request, update_request, 200, None, response)
623
624 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
625 def test_remove_client_logged_in(self, _validate_image_mock):
626 client_info = {
627 'alias': '',
628 'ip_address': [],
629 'state': {'LOGGED_IN': ['node1']}
630 }
631 # pylint: disable=protected-access
632 IscsiClientMock._instance.clientinfo = client_info
633 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw19"
634 create_request = copy.deepcopy(iscsi_target_request)
635 create_request['target_iqn'] = target_iqn
636 update_request = copy.deepcopy(create_request)
637 update_request['new_target_iqn'] = target_iqn
638 update_request['clients'].pop(0)
639 response = copy.deepcopy(iscsi_target_response)
640 response['target_iqn'] = target_iqn
641 for client in response['clients']:
642 client['info'] = client_info
643 update_response = {
644 'detail': "Client 'iqn.1994-05.com.redhat:rh7-client' cannot be deleted until it's "
645 "logged out",
646 'code': 'client_logged_in',
647 'component': 'iscsi'
648 }
649 self._update_iscsi_target(create_request, update_request, 400, update_response, response)
650
651 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
652 def test_remove_client(self, _validate_image_mock):
653 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw20"
654 create_request = copy.deepcopy(iscsi_target_request)
655 create_request['target_iqn'] = target_iqn
656 update_request = copy.deepcopy(create_request)
657 update_request['new_target_iqn'] = target_iqn
658 update_request['clients'].pop(0)
659 response = copy.deepcopy(iscsi_target_response)
660 response['target_iqn'] = target_iqn
661 response['clients'].pop(0)
662 self._update_iscsi_target(create_request, update_request, 200, None, response)
663
664 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
665 def test_add_image_to_group_with_client_logged_in(self, _validate_image_mock):
666 client_info = {
667 'alias': '',
668 'ip_address': [],
669 'state': {'LOGGED_IN': ['node1']}
670 }
671 new_disk = {"pool": "rbd", "image": "lun1"}
672 # pylint: disable=protected-access
673 IscsiClientMock._instance.clientinfo = client_info
674 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw21"
675 create_request = copy.deepcopy(iscsi_target_request)
676 create_request['target_iqn'] = target_iqn
677 update_request = copy.deepcopy(create_request)
678 update_request['new_target_iqn'] = target_iqn
679 update_request['groups'][0]['disks'].append(new_disk)
680 response = copy.deepcopy(iscsi_target_response)
681 response['target_iqn'] = target_iqn
682 response['groups'][0]['disks'].insert(0, new_disk)
683 for client in response['clients']:
684 client['info'] = client_info
685 self._update_iscsi_target(create_request, update_request, 200, None, response)
686
687 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
688 def test_add_image_to_initiator_with_client_logged_in(self, _validate_image_mock):
689 client_info = {
690 'alias': '',
691 'ip_address': [],
692 'state': {'LOGGED_IN': ['node1']}
693 }
694 new_disk = {"pool": "rbd", "image": "lun2"}
695 # pylint: disable=protected-access
696 IscsiClientMock._instance.clientinfo = client_info
697 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw22"
698 create_request = copy.deepcopy(iscsi_target_request)
699 create_request['target_iqn'] = target_iqn
700 update_request = copy.deepcopy(create_request)
701 update_request['new_target_iqn'] = target_iqn
702 update_request['clients'][0]['luns'].append(new_disk)
703 response = copy.deepcopy(iscsi_target_response)
704 response['target_iqn'] = target_iqn
705 response['clients'][0]['luns'].append(new_disk)
706 for client in response['clients']:
707 client['info'] = client_info
708 self._update_iscsi_target(create_request, update_request, 200, None, response)
709
710 @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
711 def test_remove_image_from_group_with_client_logged_in(self, _validate_image_mock):
712 client_info = {
713 'alias': '',
714 'ip_address': [],
715 'state': {'LOGGED_IN': ['node1']}
716 }
717 # pylint: disable=protected-access
718 IscsiClientMock._instance.clientinfo = client_info
719 target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw23"
720 create_request = copy.deepcopy(iscsi_target_request)
721 create_request['target_iqn'] = target_iqn
722 update_request = copy.deepcopy(create_request)
723 update_request['new_target_iqn'] = target_iqn
724 update_request['groups'][0]['disks'] = []
725 response = copy.deepcopy(iscsi_target_response)
726 response['target_iqn'] = target_iqn
727 response['groups'][0]['disks'] = []
728 for client in response['clients']:
729 client['info'] = client_info
730 self._update_iscsi_target(create_request, update_request, 200, None, response)
731
732 def _update_iscsi_target(self, create_request, update_request, update_response_code,
733 update_response, response):
734 self._task_post('/api/iscsi/target', create_request)
735 self.assertStatus(201)
736 self._task_put(
737 '/api/iscsi/target/{}'.format(create_request['target_iqn']), update_request)
738 self.assertStatus(update_response_code)
739 self.assertJsonBody(update_response)
740 self._get(
741 '/api/iscsi/target/{}'.format(update_request['new_target_iqn']))
742 self.assertStatus(200)
743 self.assertJsonBody(response)
744
745
746 class TestIscsiUi(ControllerTestCase):
747 @classmethod
748 def setup_server(cls):
749 cls.setup_controllers([IscsiUi])
750
751 @mock.patch('dashboard.services.tcmu_service.TcmuService.get_image_info')
752 @mock.patch('dashboard.services.tcmu_service.TcmuService.get_iscsi_info')
753 def test_overview(self, get_iscsi_info_mock, get_image_info_mock):
754 get_iscsi_info_mock.return_value = None
755 get_image_info_mock.return_value = None
756 response = copy.deepcopy(iscsiui_response)
757 response['images'] = []
758 self._get('/ui-api/iscsi/overview')
759 self.assertStatus(200)
760 self.assertJsonBody(response)
761
762 @mock.patch('dashboard.services.iscsi_config.IscsiGatewaysConfig.get_gateways_config')
763 @mock.patch('dashboard.services.tcmu_service.TcmuService.get_image_info')
764 @mock.patch('dashboard.services.tcmu_service.TcmuService.get_iscsi_info')
765 def test_overview_config(self, get_iscsi_info_mock, get_image_info_mock,
766 get_gateways_config_mock):
767 get_iscsi_info_mock.return_value = None
768 get_image_info_mock.return_value = None
769 response = copy.deepcopy(iscsiui_response)
770 response['images'] = []
771 get_gateways_config_mock.return_value = iscsiui_gateways_config_mock
772 self._get('/ui-api/iscsi/overview')
773 self.assertStatus(200)
774 self.assertJsonBody(response)
775
776 def raise_ex(self):
777 raise RequestException('error')
778 config_method = IscsiClientMock.get_config
779 IscsiClientMock.get_config = raise_ex
780 response['gateways'][0]['num_sessions'] = 'n/a'
781 response['gateways'][1]['num_sessions'] = 'n/a'
782 response['gateways'][0]['num_targets'] = 'n/a'
783 response['gateways'][1]['num_targets'] = 'n/a'
784 self._get('/ui-api/iscsi/overview')
785 self.assertStatus(200)
786 self.assertJsonBody(response)
787 IscsiClientMock.get_config = config_method
788
789 @mock.patch('dashboard.services.iscsi_config.IscsiGatewaysConfig.get_gateways_config')
790 @mock.patch('dashboard.services.tcmu_service.TcmuService.get_image_info')
791 @mock.patch('dashboard.services.tcmu_service.TcmuService.get_iscsi_info')
792 def test_overview_ping(self, get_iscsi_info_mock, get_image_info_mock,
793 get_gateways_config_mock):
794 get_iscsi_info_mock.return_value = None
795 get_image_info_mock.return_value = None
796 get_gateways_config_mock.return_value = iscsiui_gateways_config_mock
797 response = copy.deepcopy(iscsiui_response)
798 response['gateways'][0]['num_sessions'] = 0
799 response['gateways'][1]['num_sessions'] = 0
800 response['gateways'][0]['num_targets'] = 0
801 response['gateways'][1]['num_targets'] = 0
802 self._get('/ui-api/iscsi/overview')
803 self.assertStatus(200)
804 self.assertJsonBody(response)
805
806 def raise_ex(self):
807 raise RequestException('error')
808 ping_method = IscsiClientMock.ping
809 IscsiClientMock.ping = raise_ex
810 response['gateways'][0]['num_sessions'] = 'n/a'
811 response['gateways'][1]['num_sessions'] = 'n/a'
812 response['gateways'][0]['state'] = 'down'
813 response['gateways'][1]['state'] = 'down'
814 self._get('/ui-api/iscsi/overview')
815 self.assertStatus(200)
816 self.assertJsonBody(response)
817 IscsiClientMock.ping = ping_method
818
819 @mock.patch(
820 'dashboard.services.iscsi_config.IscsiGatewaysConfig.get_gateways_config')
821 @mock.patch('dashboard.services.tcmu_service.TcmuService.get_image_info')
822 @mock.patch('dashboard.services.tcmu_service.TcmuService.get_iscsi_info')
823 def test_overview_images_info(self, get_iscsi_info_mock, get_image_info_mock,
824 get_gateways_config_mock):
825 get_iscsi_info_mock.return_value = None
826 image_info = {"optimized_since": "1616735075", "stats": {}, "stats_history": {}}
827 # pylint: disable=protected-access
828 IscsiClientMock._instance.config['disks'] = {
829 1: {"image": "lun1", "pool": "rbd", "backstore": "user:rbd",
830 "optimized_since": "1616735075", "stats": {}, "stats_history": {}},
831 2: {"image": "lun2", "pool": "rbd", "backstore": "user:rbd",
832 "optimized_since": "1616735075", "stats": {}, "stats_history": {}},
833 }
834 response = copy.deepcopy(iscsiui_response)
835 response['images'][0]['optimized_since'] = '1616735075'
836 response['images'][1]['optimized_since'] = '1616735075'
837 response['images'][0]['stats'] = {}
838 response['images'][1]['stats'] = {}
839 response['images'][0]['stats_history'] = {}
840 response['images'][1]['stats_history'] = {}
841 get_gateways_config_mock.return_value = iscsiui_gateways_config_mock
842 get_image_info_mock.return_value = image_info
843 self._get('/ui-api/iscsi/overview')
844 self.assertStatus(200)
845 self.assertJsonBody(response)
846
847
848 iscsiui_gateways_config_mock = {
849 'gateways': {
850 'node1': None,
851 'node2': None,
852 },
853 'disks': {
854 1: {"image": "lun1", "pool": "rbd", "backstore": "user:rbd",
855 "controls": {"max_data_area_mb": 128}},
856 2: {"image": "lun2", "pool": "rbd", "backstore": "user:rbd",
857 "controls": {"max_data_area_mb": 128}}
858 }
859 }
860 iscsiui_response = {
861 "gateways": [
862 {"name": "node1", "state": "up", "num_targets": 0, "num_sessions": 0},
863 {"name": "node2", "state": "up", "num_targets": 0, "num_sessions": 0}
864 ],
865 "images": [
866 {
867 'pool': 'rbd',
868 'image': 'lun1',
869 'backstore': 'user:rbd',
870 'optimized_since': None,
871 'stats': None,
872 'stats_history': None
873 },
874 {
875 'pool': 'rbd',
876 'image': 'lun2',
877 'backstore': 'user:rbd',
878 'optimized_since': None,
879 'stats': None,
880 'stats_history': None
881 }
882 ]
883 }
884 iscsi_target_request = {
885 "target_iqn": "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw",
886 "portals": [
887 {"ip": "192.168.100.202", "host": "node2"},
888 {"ip": "10.0.2.15", "host": "node2"},
889 {"ip": "192.168.100.203", "host": "node3"}
890 ],
891 "disks": [
892 {"image": "lun1", "pool": "rbd", "backstore": "user:rbd",
893 "controls": {"max_data_area_mb": 128}},
894 {"image": "lun2", "pool": "rbd", "backstore": "user:rbd",
895 "controls": {"max_data_area_mb": 128}}
896 ],
897 "clients": [
898 {
899 "luns": [{"image": "lun1", "pool": "rbd"}],
900 "client_iqn": "iqn.1994-05.com.redhat:rh7-client",
901 "auth": {
902 "password": "myiscsipassword1",
903 "user": "myiscsiusername1",
904 "mutual_password": "myiscsipassword2",
905 "mutual_user": "myiscsiusername2"}
906 },
907 {
908 "luns": [],
909 "client_iqn": "iqn.1994-05.com.redhat:rh7-client2",
910 "auth": {
911 "password": "myiscsipassword3",
912 "user": "myiscsiusername3",
913 "mutual_password": "myiscsipassword4",
914 "mutual_user": "myiscsiusername4"
915 }
916 }
917 ],
918 "acl_enabled": True,
919 "auth": {
920 "password": "",
921 "user": "",
922 "mutual_password": "",
923 "mutual_user": ""},
924 "target_controls": {},
925 "groups": [
926 {
927 "group_id": "mygroup",
928 "disks": [{"pool": "rbd", "image": "lun2"}],
929 "members": ["iqn.1994-05.com.redhat:rh7-client2"]
930 }
931 ]
932 }
933
934 iscsi_target_response = {
935 'target_iqn': 'iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw',
936 'portals': [
937 {'host': 'node2', 'ip': '10.0.2.15'},
938 {'host': 'node2', 'ip': '192.168.100.202'},
939 {'host': 'node3', 'ip': '192.168.100.203'}
940 ],
941 'disks': [
942 {'pool': 'rbd', 'image': 'lun1', 'backstore': 'user:rbd',
943 'wwn': '64af6678-9694-4367-bacc-f8eb0baa0', 'lun': 0,
944 'controls': {'max_data_area_mb': 128}},
945 {'pool': 'rbd', 'image': 'lun2', 'backstore': 'user:rbd',
946 'wwn': '64af6678-9694-4367-bacc-f8eb0baa1', 'lun': 1,
947 'controls': {'max_data_area_mb': 128}}
948 ],
949 'clients': [
950 {
951 'client_iqn': 'iqn.1994-05.com.redhat:rh7-client',
952 'luns': [{'pool': 'rbd', 'image': 'lun1'}],
953 'auth': {
954 'user': 'myiscsiusername1',
955 'password': 'myiscsipassword1',
956 'mutual_password': 'myiscsipassword2',
957 'mutual_user': 'myiscsiusername2'
958 },
959 'info': {
960 'alias': '',
961 'ip_address': [],
962 'state': {}
963 }
964 },
965 {
966 'client_iqn': 'iqn.1994-05.com.redhat:rh7-client2',
967 'luns': [],
968 'auth': {
969 'user': 'myiscsiusername3',
970 'password': 'myiscsipassword3',
971 'mutual_password': 'myiscsipassword4',
972 'mutual_user': 'myiscsiusername4'
973 },
974 'info': {
975 'alias': '',
976 'ip_address': [],
977 'state': {}
978 }
979 }
980 ],
981 "acl_enabled": True,
982 "auth": {
983 "password": "",
984 "user": "",
985 "mutual_password": "",
986 "mutual_user": ""},
987 'groups': [
988 {
989 'group_id': 'mygroup',
990 'disks': [{'pool': 'rbd', 'image': 'lun2'}],
991 'members': ['iqn.1994-05.com.redhat:rh7-client2']
992 }
993 ],
994 'target_controls': {},
995 'info': {
996 'num_sessions': 0
997 }
998 }
999
1000
1001 class IscsiClientMock(object):
1002
1003 _instance = None
1004
1005 def __init__(self):
1006 self.gateway_name = None
1007 self.service_url = None
1008 self.config = {
1009 "created": "2019/01/17 08:57:16",
1010 "discovery_auth": {
1011 "username": "",
1012 "password": "",
1013 "password_encryption_enabled": False,
1014 "mutual_username": "",
1015 "mutual_password": "",
1016 "mutual_password_encryption_enabled": False
1017 },
1018 "disks": {},
1019 "epoch": 0,
1020 "gateways": {},
1021 "targets": {},
1022 "updated": "",
1023 "version": 11
1024 }
1025 self.clientinfo = {
1026 'alias': '',
1027 'ip_address': [],
1028 'state': {}
1029 }
1030
1031 @classmethod
1032 def instance(cls, gateway_name=None, service_url=None):
1033 cls._instance.gateway_name = gateway_name
1034 cls._instance.service_url = service_url
1035 # pylint: disable=unused-argument
1036 return cls._instance
1037
1038 def ping(self):
1039 return {
1040 "message": "pong"
1041 }
1042
1043 def get_settings(self):
1044 return {
1045 "api_version": 2,
1046 "backstores": [
1047 "user:rbd"
1048 ],
1049 "config": {
1050 "minimum_gateways": 2
1051 },
1052 "default_backstore": "user:rbd",
1053 "required_rbd_features": {
1054 "rbd": 0,
1055 "user:rbd": 4,
1056 },
1057 "unsupported_rbd_features": {
1058 "rbd": 88,
1059 "user:rbd": 0,
1060 },
1061 "disk_default_controls": {
1062 "user:rbd": {
1063 "hw_max_sectors": 1024,
1064 "max_data_area_mb": 8,
1065 "osd_op_timeout": 30,
1066 "qfull_timeout": 5
1067 }
1068 },
1069 "target_default_controls": {
1070 "cmdsn_depth": 128,
1071 "dataout_timeout": 20,
1072 "first_burst_length": 262144,
1073 "immediate_data": "Yes",
1074 "initial_r2t": "Yes",
1075 "max_burst_length": 524288,
1076 "max_outstanding_r2t": 1,
1077 "max_recv_data_segment_length": 262144,
1078 "max_xmit_data_segment_length": 262144,
1079 "nopin_response_timeout": 5,
1080 "nopin_timeout": 5
1081 }
1082 }
1083
1084 def get_config(self):
1085 return copy.deepcopy(self.config)
1086
1087 def create_target(self, target_iqn, target_controls):
1088 self.config['targets'][target_iqn] = {
1089 "clients": {},
1090 "acl_enabled": True,
1091 "auth": {
1092 "username": "",
1093 "password": "",
1094 "password_encryption_enabled": False,
1095 "mutual_username": "",
1096 "mutual_password": "",
1097 "mutual_password_encryption_enabled": False
1098 },
1099 "controls": target_controls,
1100 "created": "2019/01/17 09:22:34",
1101 "disks": {},
1102 "groups": {},
1103 "portals": {}
1104 }
1105
1106 def create_gateway(self, target_iqn, gateway_name, ip_addresses):
1107 target_config = self.config['targets'][target_iqn]
1108 if 'ip_list' not in target_config:
1109 target_config['ip_list'] = []
1110 target_config['ip_list'] += ip_addresses
1111 target_config['portals'][gateway_name] = {
1112 "portal_ip_addresses": ip_addresses
1113 }
1114
1115 def delete_gateway(self, target_iqn, gateway_name):
1116 target_config = self.config['targets'][target_iqn]
1117 portal_config = target_config['portals'][gateway_name]
1118 for ip in portal_config['portal_ip_addresses']:
1119 target_config['ip_list'].remove(ip)
1120 target_config['portals'].pop(gateway_name)
1121
1122 def create_disk(self, pool, image, backstore, wwn):
1123 if wwn is None:
1124 wwn = '64af6678-9694-4367-bacc-f8eb0baa' + str(len(self.config['disks']))
1125 image_id = '{}/{}'.format(pool, image)
1126 self.config['disks'][image_id] = {
1127 "pool": pool,
1128 "image": image,
1129 "backstore": backstore,
1130 "controls": {},
1131 "wwn": wwn
1132 }
1133
1134 def create_target_lun(self, target_iqn, image_id, lun):
1135 target_config = self.config['targets'][target_iqn]
1136 if lun is None:
1137 lun = len(target_config['disks'])
1138 target_config['disks'][image_id] = {
1139 "lun_id": lun
1140 }
1141 self.config['disks'][image_id]['owner'] = list(target_config['portals'].keys())[0]
1142
1143 def reconfigure_disk(self, pool, image, controls):
1144 image_id = '{}/{}'.format(pool, image)
1145 settings = self.get_settings()
1146 backstore = self.config['disks'][image_id]['backstore']
1147 disk_default_controls = settings['disk_default_controls'][backstore]
1148 new_controls = {}
1149 for control_k, control_v in controls.items():
1150 if control_v != disk_default_controls[control_k]:
1151 new_controls[control_k] = control_v
1152 self.config['disks'][image_id]['controls'] = new_controls
1153
1154 def create_client(self, target_iqn, client_iqn):
1155 target_config = self.config['targets'][target_iqn]
1156 target_config['clients'][client_iqn] = {
1157 "auth": {
1158 "username": "",
1159 "password": "",
1160 "password_encryption_enabled": False,
1161 "mutual_username": "",
1162 "mutual_password": "",
1163 "mutual_password_encryption_enabled": False
1164 },
1165 "group_name": "",
1166 "luns": {}
1167 }
1168
1169 def create_client_lun(self, target_iqn, client_iqn, image_id):
1170 target_config = self.config['targets'][target_iqn]
1171 target_config['clients'][client_iqn]['luns'][image_id] = {}
1172
1173 def delete_client_lun(self, target_iqn, client_iqn, image_id):
1174 target_config = self.config['targets'][target_iqn]
1175 del target_config['clients'][client_iqn]['luns'][image_id]
1176
1177 def create_client_auth(self, target_iqn, client_iqn, user, password, m_user, m_password):
1178 target_config = self.config['targets'][target_iqn]
1179 target_config['clients'][client_iqn]['auth']['username'] = user
1180 target_config['clients'][client_iqn]['auth']['password'] = password
1181 target_config['clients'][client_iqn]['auth']['mutual_username'] = m_user
1182 target_config['clients'][client_iqn]['auth']['mutual_password'] = m_password
1183
1184 def create_group(self, target_iqn, group_name, members, image_ids):
1185 target_config = self.config['targets'][target_iqn]
1186 target_config['groups'][group_name] = {
1187 "disks": {},
1188 "members": []
1189 }
1190 for image_id in image_ids:
1191 target_config['groups'][group_name]['disks'][image_id] = {}
1192 target_config['groups'][group_name]['members'] = members
1193
1194 def update_group(self, target_iqn, group_name, members, image_ids):
1195 target_config = self.config['targets'][target_iqn]
1196 group = target_config['groups'][group_name]
1197 old_members = group['members']
1198 disks = group['disks']
1199 target_config['groups'][group_name] = {
1200 "disks": {},
1201 "members": []
1202 }
1203
1204 for image_id in disks.keys():
1205 if image_id not in image_ids:
1206 target_config['groups'][group_name]['disks'][image_id] = {}
1207
1208 new_members = []
1209 for member_iqn in old_members:
1210 if member_iqn not in members:
1211 new_members.append(member_iqn)
1212 target_config['groups'][group_name]['members'] = new_members
1213
1214 def delete_group(self, target_iqn, group_name):
1215 target_config = self.config['targets'][target_iqn]
1216 del target_config['groups'][group_name]
1217
1218 def delete_client(self, target_iqn, client_iqn):
1219 target_config = self.config['targets'][target_iqn]
1220 del target_config['clients'][client_iqn]
1221
1222 def delete_target_lun(self, target_iqn, image_id):
1223 target_config = self.config['targets'][target_iqn]
1224 target_config['disks'].pop(image_id)
1225 del self.config['disks'][image_id]['owner']
1226
1227 def delete_disk(self, pool, image):
1228 image_id = '{}/{}'.format(pool, image)
1229 del self.config['disks'][image_id]
1230
1231 def delete_target(self, target_iqn):
1232 del self.config['targets'][target_iqn]
1233
1234 def get_ip_addresses(self):
1235 ips = {
1236 'node1': ['192.168.100.201'],
1237 'node2': ['192.168.100.202', '10.0.2.15'],
1238 'node3': ['192.168.100.203']
1239 }
1240 return {'data': ips[self.gateway_name]}
1241
1242 def get_hostname(self):
1243 hostnames = {
1244 'https://admin:admin@10.17.5.1:5001': 'node1',
1245 'https://admin:admin@10.17.5.2:5001': 'node2',
1246 'https://admin:admin@10.17.5.3:5001': 'node3'
1247 }
1248 if self.service_url not in hostnames:
1249 raise RequestException('No route to host')
1250 return {'data': hostnames[self.service_url]}
1251
1252 def update_discoveryauth(self, user, password, mutual_user, mutual_password):
1253 self.config['discovery_auth']['username'] = user
1254 self.config['discovery_auth']['password'] = password
1255 self.config['discovery_auth']['mutual_username'] = mutual_user
1256 self.config['discovery_auth']['mutual_password'] = mutual_password
1257
1258 def update_targetacl(self, target_iqn, action):
1259 self.config['targets'][target_iqn]['acl_enabled'] = (action == 'enable_acl')
1260
1261 def update_targetauth(self, target_iqn, user, password, mutual_user, mutual_password):
1262 target_config = self.config['targets'][target_iqn]
1263 target_config['auth']['username'] = user
1264 target_config['auth']['password'] = password
1265 target_config['auth']['mutual_username'] = mutual_user
1266 target_config['auth']['mutual_password'] = mutual_password
1267
1268 def get_targetinfo(self, target_iqn):
1269 # pylint: disable=unused-argument
1270 return {
1271 'num_sessions': 0
1272 }
1273
1274 def get_clientinfo(self, target_iqn, client_iqn):
1275 # pylint: disable=unused-argument
1276 return self.clientinfo