summaryrefslogtreecommitdiffstats
path: root/src/pybind/mgr/dashboard/tests/test_iscsi.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/pybind/mgr/dashboard/tests/test_iscsi.py')
-rw-r--r--src/pybind/mgr/dashboard/tests/test_iscsi.py1008
1 files changed, 1008 insertions, 0 deletions
diff --git a/src/pybind/mgr/dashboard/tests/test_iscsi.py b/src/pybind/mgr/dashboard/tests/test_iscsi.py
new file mode 100644
index 000000000..7728a496b
--- /dev/null
+++ b/src/pybind/mgr/dashboard/tests/test_iscsi.py
@@ -0,0 +1,1008 @@
+# pylint: disable=too-many-public-methods, too-many-lines
+
+import copy
+import errno
+import json
+import unittest
+
+try:
+ import mock
+except ImportError:
+ import unittest.mock as mock
+
+from mgr_module import ERROR_MSG_NO_INPUT_FILE
+
+from .. import mgr
+from ..controllers.iscsi import Iscsi, IscsiTarget
+from ..rest_client import RequestException
+from ..services.iscsi_client import IscsiClient
+from ..services.orchestrator import OrchClient
+from ..tests import CLICommandTestMixin, CmdException, ControllerTestCase, KVStoreMockMixin
+from ..tools import NotificationQueue, TaskManager
+
+
+class IscsiTestCli(unittest.TestCase, CLICommandTestMixin):
+
+ def setUp(self):
+ self.mock_kv_store()
+ # pylint: disable=protected-access
+ IscsiClientMock._instance = IscsiClientMock()
+ IscsiClient.instance = IscsiClientMock.instance
+
+ def test_cli_add_gateway_invalid_url(self):
+ with self.assertRaises(CmdException) as ctx:
+ self.exec_cmd('iscsi-gateway-add', name='node1',
+ inbuf='http:/hello.com')
+
+ self.assertEqual(ctx.exception.retcode, -errno.EINVAL)
+ self.assertEqual(str(ctx.exception),
+ "Invalid service URL 'http:/hello.com'. Valid format: "
+ "'<scheme>://<username>:<password>@<host>[:port]'.")
+
+ def test_cli_add_gateway_empty_url(self):
+ with self.assertRaises(CmdException) as ctx:
+ self.exec_cmd('iscsi-gateway-add', name='node1',
+ inbuf='')
+
+ self.assertEqual(ctx.exception.retcode, -errno.EINVAL)
+ self.assertIn(ERROR_MSG_NO_INPUT_FILE, str(ctx.exception))
+
+ def test_cli_add_gateway(self):
+ self.exec_cmd('iscsi-gateway-add', name='node1',
+ inbuf='https://admin:admin@10.17.5.1:5001')
+ self.exec_cmd('iscsi-gateway-add', name='node2',
+ inbuf='https://admin:admin@10.17.5.2:5001')
+ iscsi_config = json.loads(self.get_key("_iscsi_config"))
+ self.assertEqual(iscsi_config['gateways'], {
+ 'node1': {
+ 'service_url': 'https://admin:admin@10.17.5.1:5001'
+ },
+ 'node2': {
+ 'service_url': 'https://admin:admin@10.17.5.2:5001'
+ }
+ })
+
+ def test_cli_remove_gateway(self):
+ self.test_cli_add_gateway()
+ self.exec_cmd('iscsi-gateway-rm', name='node1')
+ iscsi_config = json.loads(self.get_key("_iscsi_config"))
+ self.assertEqual(iscsi_config['gateways'], {
+ 'node2': {
+ 'service_url': 'https://admin:admin@10.17.5.2:5001'
+ }
+ })
+
+
+class IscsiTestController(ControllerTestCase, KVStoreMockMixin):
+
+ @classmethod
+ def setup_server(cls):
+ NotificationQueue.start_queue()
+ TaskManager.init()
+ OrchClient.instance().available = lambda: False
+ mgr.rados.side_effect = None
+ cls.setup_controllers([Iscsi, IscsiTarget])
+
+ @classmethod
+ def tearDownClass(cls):
+ NotificationQueue.stop()
+
+ def setUp(self):
+ self.mock_kv_store()
+ self.CONFIG_KEY_DICT['_iscsi_config'] = '''
+ {
+ "gateways": {
+ "node1": {
+ "service_url": "https://admin:admin@10.17.5.1:5001"
+ },
+ "node2": {
+ "service_url": "https://admin:admin@10.17.5.2:5001"
+ }
+ }
+ }
+ '''
+ # pylint: disable=protected-access
+ IscsiClientMock._instance = IscsiClientMock()
+ IscsiClient.instance = IscsiClientMock.instance
+
+ def test_enable_discoveryauth(self):
+ discoveryauth = {
+ 'user': 'myiscsiusername',
+ 'password': 'myiscsipassword',
+ 'mutual_user': 'myiscsiusername2',
+ 'mutual_password': 'myiscsipassword2'
+ }
+ self._put('/api/iscsi/discoveryauth', discoveryauth)
+ self.assertStatus(200)
+ self.assertJsonBody(discoveryauth)
+ self._get('/api/iscsi/discoveryauth')
+ self.assertStatus(200)
+ self.assertJsonBody(discoveryauth)
+
+ def test_bad_discoveryauth(self):
+ discoveryauth = {
+ 'user': 'myiscsiusername',
+ 'password': 'myiscsipasswordmyiscsipasswordmyiscsipassword',
+ 'mutual_user': '',
+ 'mutual_password': ''
+ }
+ put_response = {
+ 'detail': 'Bad authentication',
+ 'code': 'target_bad_auth',
+ 'component': 'iscsi'
+ }
+ get_response = {
+ 'user': '',
+ 'password': '',
+ 'mutual_user': '',
+ 'mutual_password': ''
+ }
+ self._put('/api/iscsi/discoveryauth', discoveryauth)
+ self.assertStatus(400)
+ self.assertJsonBody(put_response)
+ self._get('/api/iscsi/discoveryauth')
+ self.assertStatus(200)
+ self.assertJsonBody(get_response)
+
+ def test_disable_discoveryauth(self):
+ discoveryauth = {
+ 'user': '',
+ 'password': '',
+ 'mutual_user': '',
+ 'mutual_password': ''
+ }
+ self._put('/api/iscsi/discoveryauth', discoveryauth)
+ self.assertStatus(200)
+ self.assertJsonBody(discoveryauth)
+ self._get('/api/iscsi/discoveryauth')
+ self.assertStatus(200)
+ self.assertJsonBody(discoveryauth)
+
+ def test_list_empty(self):
+ self._get('/api/iscsi/target')
+ self.assertStatus(200)
+ self.assertJsonBody([])
+
+ @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
+ def test_list(self, _validate_image_mock):
+ target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw1"
+ request = copy.deepcopy(iscsi_target_request)
+ request['target_iqn'] = target_iqn
+ self._task_post('/api/iscsi/target', request)
+ self.assertStatus(201)
+ self._get('/api/iscsi/target')
+ self.assertStatus(200)
+ response = copy.deepcopy(iscsi_target_response)
+ response['target_iqn'] = target_iqn
+ self.assertJsonBody([response])
+
+ @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
+ def test_create(self, _validate_image_mock):
+ target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw2"
+ request = copy.deepcopy(iscsi_target_request)
+ request['target_iqn'] = target_iqn
+ self._task_post('/api/iscsi/target', request)
+ self.assertStatus(201)
+ self._get('/api/iscsi/target/{}'.format(request['target_iqn']))
+ self.assertStatus(200)
+ response = copy.deepcopy(iscsi_target_response)
+ response['target_iqn'] = target_iqn
+ self.assertJsonBody(response)
+
+ @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
+ def test_delete(self, _validate_image_mock):
+ target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw3"
+ request = copy.deepcopy(iscsi_target_request)
+ request['target_iqn'] = target_iqn
+ self._task_post('/api/iscsi/target', request)
+ self.assertStatus(201)
+ self._task_delete('/api/iscsi/target/{}'.format(request['target_iqn']))
+ self.assertStatus(204)
+ self._get('/api/iscsi/target')
+ self.assertStatus(200)
+ self.assertJsonBody([])
+
+ @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
+ def test_add_client(self, _validate_image_mock):
+ target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw4"
+ create_request = copy.deepcopy(iscsi_target_request)
+ create_request['target_iqn'] = target_iqn
+ update_request = copy.deepcopy(create_request)
+ update_request['new_target_iqn'] = target_iqn
+ update_request['clients'].append(
+ {
+ "luns": [{"image": "lun1", "pool": "rbd"}],
+ "client_iqn": "iqn.1994-05.com.redhat:rh7-client3",
+ "auth": {
+ "password": "myiscsipassword5",
+ "user": "myiscsiusername5",
+ "mutual_password": "myiscsipassword6",
+ "mutual_user": "myiscsiusername6"}
+ })
+ response = copy.deepcopy(iscsi_target_response)
+ response['target_iqn'] = target_iqn
+ response['clients'].append(
+ {
+ "luns": [{"image": "lun1", "pool": "rbd"}],
+ "client_iqn": "iqn.1994-05.com.redhat:rh7-client3",
+ "auth": {
+ "password": "myiscsipassword5",
+ "user": "myiscsiusername5",
+ "mutual_password": "myiscsipassword6",
+ "mutual_user": "myiscsiusername6"},
+ "info": {
+ "alias": "",
+ "ip_address": [],
+ "state": {}
+ }
+ })
+ self._update_iscsi_target(create_request, update_request, 200, None, response)
+
+ @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
+ def test_add_bad_client(self, _validate_image_mock):
+ target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw4"
+ create_request = copy.deepcopy(iscsi_target_request)
+ create_request['target_iqn'] = target_iqn
+ update_request = copy.deepcopy(create_request)
+ update_request['new_target_iqn'] = target_iqn
+ update_request['clients'].append(
+ {
+ "luns": [{"image": "lun1", "pool": "rbd"}],
+ "client_iqn": "iqn.1994-05.com.redhat:rh7-client4",
+ "auth": {
+ "password": "myiscsipassword7myiscsipassword7myiscsipasswo",
+ "user": "myiscsiusername7",
+ "mutual_password": "myiscsipassword8",
+ "mutual_user": "myiscsiusername8"}
+ })
+ response = copy.deepcopy(iscsi_target_response)
+ response['target_iqn'] = target_iqn
+
+ self._task_post('/api/iscsi/target', create_request)
+ self.assertStatus(201)
+ self._task_put('/api/iscsi/target/{}'.format(create_request['target_iqn']), update_request)
+ self.assertStatus(400)
+ self._get('/api/iscsi/target/{}'.format(update_request['new_target_iqn']))
+ self.assertStatus(200)
+ self.assertJsonBody(response)
+
+ @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
+ def test_change_client_password(self, _validate_image_mock):
+ target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw5"
+ create_request = copy.deepcopy(iscsi_target_request)
+ create_request['target_iqn'] = target_iqn
+ update_request = copy.deepcopy(create_request)
+ update_request['new_target_iqn'] = target_iqn
+ update_request['clients'][0]['auth']['password'] = 'MyNewPassword'
+ response = copy.deepcopy(iscsi_target_response)
+ response['target_iqn'] = target_iqn
+ response['clients'][0]['auth']['password'] = 'MyNewPassword'
+ self._update_iscsi_target(create_request, update_request, 200, None, response)
+
+ @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
+ def test_rename_client(self, _validate_image_mock):
+ target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw6"
+ create_request = copy.deepcopy(iscsi_target_request)
+ create_request['target_iqn'] = target_iqn
+ update_request = copy.deepcopy(create_request)
+ update_request['new_target_iqn'] = target_iqn
+ update_request['clients'][0]['client_iqn'] = 'iqn.1994-05.com.redhat:rh7-client0'
+ response = copy.deepcopy(iscsi_target_response)
+ response['target_iqn'] = target_iqn
+ response['clients'][0]['client_iqn'] = 'iqn.1994-05.com.redhat:rh7-client0'
+ self._update_iscsi_target(create_request, update_request, 200, None, response)
+
+ @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
+ def test_add_disk(self, _validate_image_mock):
+ target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw7"
+ create_request = copy.deepcopy(iscsi_target_request)
+ create_request['target_iqn'] = target_iqn
+ update_request = copy.deepcopy(create_request)
+ update_request['new_target_iqn'] = target_iqn
+ update_request['disks'].append(
+ {
+ "image": "lun3",
+ "pool": "rbd",
+ "controls": {},
+ "backstore": "user:rbd"
+ })
+ update_request['clients'][0]['luns'].append({"image": "lun3", "pool": "rbd"})
+ response = copy.deepcopy(iscsi_target_response)
+ response['target_iqn'] = target_iqn
+ response['disks'].append(
+ {
+ "image": "lun3",
+ "pool": "rbd",
+ "controls": {},
+ "backstore": "user:rbd",
+ "wwn": "64af6678-9694-4367-bacc-f8eb0baa2",
+ "lun": 2
+
+ })
+ response['clients'][0]['luns'].append({"image": "lun3", "pool": "rbd"})
+ self._update_iscsi_target(create_request, update_request, 200, None, response)
+
+ @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
+ def test_change_disk_image(self, _validate_image_mock):
+ target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw8"
+ create_request = copy.deepcopy(iscsi_target_request)
+ create_request['target_iqn'] = target_iqn
+ update_request = copy.deepcopy(create_request)
+ update_request['new_target_iqn'] = target_iqn
+ update_request['disks'][0]['image'] = 'lun0'
+ update_request['clients'][0]['luns'][0]['image'] = 'lun0'
+ response = copy.deepcopy(iscsi_target_response)
+ response['target_iqn'] = target_iqn
+ response['disks'][0]['image'] = 'lun0'
+ response['clients'][0]['luns'][0]['image'] = 'lun0'
+ self._update_iscsi_target(create_request, update_request, 200, None, response)
+
+ @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
+ def test_change_disk_controls(self, _validate_image_mock):
+ target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw9"
+ create_request = copy.deepcopy(iscsi_target_request)
+ create_request['target_iqn'] = target_iqn
+ update_request = copy.deepcopy(create_request)
+ update_request['new_target_iqn'] = target_iqn
+ update_request['disks'][0]['controls'] = {"qfull_timeout": 15}
+ response = copy.deepcopy(iscsi_target_response)
+ response['target_iqn'] = target_iqn
+ response['disks'][0]['controls'] = {"qfull_timeout": 15}
+ self._update_iscsi_target(create_request, update_request, 200, None, response)
+
+ @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
+ def test_rename_target(self, _validate_image_mock):
+ target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw10"
+ new_target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw11"
+ create_request = copy.deepcopy(iscsi_target_request)
+ create_request['target_iqn'] = target_iqn
+ update_request = copy.deepcopy(create_request)
+ update_request['new_target_iqn'] = new_target_iqn
+ response = copy.deepcopy(iscsi_target_response)
+ response['target_iqn'] = new_target_iqn
+ self._update_iscsi_target(create_request, update_request, 200, None, response)
+
+ @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
+ def test_rename_group(self, _validate_image_mock):
+ target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw12"
+ create_request = copy.deepcopy(iscsi_target_request)
+ create_request['target_iqn'] = target_iqn
+ update_request = copy.deepcopy(create_request)
+ update_request['new_target_iqn'] = target_iqn
+ update_request['groups'][0]['group_id'] = 'mygroup0'
+ response = copy.deepcopy(iscsi_target_response)
+ response['target_iqn'] = target_iqn
+ response['groups'][0]['group_id'] = 'mygroup0'
+ self._update_iscsi_target(create_request, update_request, 200, None, response)
+
+ @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
+ def test_add_client_to_group(self, _validate_image_mock):
+ target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw13"
+ create_request = copy.deepcopy(iscsi_target_request)
+ create_request['target_iqn'] = target_iqn
+ update_request = copy.deepcopy(create_request)
+ update_request['new_target_iqn'] = target_iqn
+ update_request['clients'].append(
+ {
+ "luns": [],
+ "client_iqn": "iqn.1994-05.com.redhat:rh7-client3",
+ "auth": {
+ "password": None,
+ "user": None,
+ "mutual_password": None,
+ "mutual_user": None}
+ })
+ update_request['groups'][0]['members'].append('iqn.1994-05.com.redhat:rh7-client3')
+ response = copy.deepcopy(iscsi_target_response)
+ response['target_iqn'] = target_iqn
+ response['clients'].append(
+ {
+ "luns": [],
+ "client_iqn": "iqn.1994-05.com.redhat:rh7-client3",
+ "auth": {
+ "password": None,
+ "user": None,
+ "mutual_password": None,
+ "mutual_user": None},
+ "info": {
+ "alias": "",
+ "ip_address": [],
+ "state": {}
+ }
+ })
+ response['groups'][0]['members'].append('iqn.1994-05.com.redhat:rh7-client3')
+ self._update_iscsi_target(create_request, update_request, 200, None, response)
+
+ @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
+ def test_remove_client_from_group(self, _validate_image_mock):
+ target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw14"
+ create_request = copy.deepcopy(iscsi_target_request)
+ create_request['target_iqn'] = target_iqn
+ update_request = copy.deepcopy(create_request)
+ update_request['new_target_iqn'] = target_iqn
+ update_request['groups'][0]['members'].remove('iqn.1994-05.com.redhat:rh7-client2')
+ response = copy.deepcopy(iscsi_target_response)
+ response['target_iqn'] = target_iqn
+ response['groups'][0]['members'].remove('iqn.1994-05.com.redhat:rh7-client2')
+ self._update_iscsi_target(create_request, update_request, 200, None, response)
+
+ @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
+ def test_remove_groups(self, _validate_image_mock):
+ target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw15"
+ create_request = copy.deepcopy(iscsi_target_request)
+ create_request['target_iqn'] = target_iqn
+ update_request = copy.deepcopy(create_request)
+ update_request['new_target_iqn'] = target_iqn
+ update_request['groups'] = []
+ response = copy.deepcopy(iscsi_target_response)
+ response['target_iqn'] = target_iqn
+ response['groups'] = []
+ self._update_iscsi_target(create_request, update_request, 200, None, response)
+
+ @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
+ def test_add_client_to_multiple_groups(self, _validate_image_mock):
+ target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw16"
+ create_request = copy.deepcopy(iscsi_target_request)
+ create_request['target_iqn'] = target_iqn
+ create_request['groups'].append(copy.deepcopy(create_request['groups'][0]))
+ create_request['groups'][1]['group_id'] = 'mygroup2'
+ self._task_post('/api/iscsi/target', create_request)
+ self.assertStatus(400)
+ self.assertJsonBody({
+ 'detail': 'Each initiator can only be part of 1 group at a time',
+ 'code': 'initiator_in_multiple_groups',
+ 'component': 'iscsi'
+ })
+
+ @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
+ def test_remove_client_lun(self, _validate_image_mock):
+ target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw17"
+ create_request = copy.deepcopy(iscsi_target_request)
+ create_request['target_iqn'] = target_iqn
+ create_request['clients'][0]['luns'] = [
+ {"image": "lun1", "pool": "rbd"},
+ {"image": "lun2", "pool": "rbd"},
+ {"image": "lun3", "pool": "rbd"}
+ ]
+ update_request = copy.deepcopy(create_request)
+ update_request['new_target_iqn'] = target_iqn
+ update_request['clients'][0]['luns'] = [
+ {"image": "lun1", "pool": "rbd"},
+ {"image": "lun3", "pool": "rbd"}
+ ]
+ response = copy.deepcopy(iscsi_target_response)
+ response['target_iqn'] = target_iqn
+ response['clients'][0]['luns'] = [
+ {"image": "lun1", "pool": "rbd"},
+ {"image": "lun3", "pool": "rbd"}
+ ]
+ self._update_iscsi_target(create_request, update_request, 200, None, response)
+
+ @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
+ def test_change_client_auth(self, _validate_image_mock):
+ target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw18"
+ create_request = copy.deepcopy(iscsi_target_request)
+ create_request['target_iqn'] = target_iqn
+ update_request = copy.deepcopy(create_request)
+ update_request['new_target_iqn'] = target_iqn
+ update_request['clients'][0]['auth']['password'] = 'myiscsipasswordX'
+ response = copy.deepcopy(iscsi_target_response)
+ response['target_iqn'] = target_iqn
+ response['clients'][0]['auth']['password'] = 'myiscsipasswordX'
+ self._update_iscsi_target(create_request, update_request, 200, None, response)
+
+ @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
+ def test_remove_client_logged_in(self, _validate_image_mock):
+ client_info = {
+ 'alias': '',
+ 'ip_address': [],
+ 'state': {'LOGGED_IN': ['node1']}
+ }
+ # pylint: disable=protected-access
+ IscsiClientMock._instance.clientinfo = client_info
+ target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw19"
+ create_request = copy.deepcopy(iscsi_target_request)
+ create_request['target_iqn'] = target_iqn
+ update_request = copy.deepcopy(create_request)
+ update_request['new_target_iqn'] = target_iqn
+ update_request['clients'].pop(0)
+ response = copy.deepcopy(iscsi_target_response)
+ response['target_iqn'] = target_iqn
+ for client in response['clients']:
+ client['info'] = client_info
+ update_response = {
+ 'detail': "Client 'iqn.1994-05.com.redhat:rh7-client' cannot be deleted until it's "
+ "logged out",
+ 'code': 'client_logged_in',
+ 'component': 'iscsi'
+ }
+ self._update_iscsi_target(create_request, update_request, 400, update_response, response)
+
+ @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
+ def test_remove_client(self, _validate_image_mock):
+ target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw20"
+ create_request = copy.deepcopy(iscsi_target_request)
+ create_request['target_iqn'] = target_iqn
+ update_request = copy.deepcopy(create_request)
+ update_request['new_target_iqn'] = target_iqn
+ update_request['clients'].pop(0)
+ response = copy.deepcopy(iscsi_target_response)
+ response['target_iqn'] = target_iqn
+ response['clients'].pop(0)
+ self._update_iscsi_target(create_request, update_request, 200, None, response)
+
+ @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
+ def test_add_image_to_group_with_client_logged_in(self, _validate_image_mock):
+ client_info = {
+ 'alias': '',
+ 'ip_address': [],
+ 'state': {'LOGGED_IN': ['node1']}
+ }
+ new_disk = {"pool": "rbd", "image": "lun1"}
+ # pylint: disable=protected-access
+ IscsiClientMock._instance.clientinfo = client_info
+ target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw21"
+ create_request = copy.deepcopy(iscsi_target_request)
+ create_request['target_iqn'] = target_iqn
+ update_request = copy.deepcopy(create_request)
+ update_request['new_target_iqn'] = target_iqn
+ update_request['groups'][0]['disks'].append(new_disk)
+ response = copy.deepcopy(iscsi_target_response)
+ response['target_iqn'] = target_iqn
+ response['groups'][0]['disks'].insert(0, new_disk)
+ for client in response['clients']:
+ client['info'] = client_info
+ self._update_iscsi_target(create_request, update_request, 200, None, response)
+
+ @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
+ def test_add_image_to_initiator_with_client_logged_in(self, _validate_image_mock):
+ client_info = {
+ 'alias': '',
+ 'ip_address': [],
+ 'state': {'LOGGED_IN': ['node1']}
+ }
+ new_disk = {"pool": "rbd", "image": "lun2"}
+ # pylint: disable=protected-access
+ IscsiClientMock._instance.clientinfo = client_info
+ target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw22"
+ create_request = copy.deepcopy(iscsi_target_request)
+ create_request['target_iqn'] = target_iqn
+ update_request = copy.deepcopy(create_request)
+ update_request['new_target_iqn'] = target_iqn
+ update_request['clients'][0]['luns'].append(new_disk)
+ response = copy.deepcopy(iscsi_target_response)
+ response['target_iqn'] = target_iqn
+ response['clients'][0]['luns'].append(new_disk)
+ for client in response['clients']:
+ client['info'] = client_info
+ self._update_iscsi_target(create_request, update_request, 200, None, response)
+
+ @mock.patch('dashboard.controllers.iscsi.IscsiTarget._validate_image')
+ def test_remove_image_from_group_with_client_logged_in(self, _validate_image_mock):
+ client_info = {
+ 'alias': '',
+ 'ip_address': [],
+ 'state': {'LOGGED_IN': ['node1']}
+ }
+ # pylint: disable=protected-access
+ IscsiClientMock._instance.clientinfo = client_info
+ target_iqn = "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw23"
+ create_request = copy.deepcopy(iscsi_target_request)
+ create_request['target_iqn'] = target_iqn
+ update_request = copy.deepcopy(create_request)
+ update_request['new_target_iqn'] = target_iqn
+ update_request['groups'][0]['disks'] = []
+ response = copy.deepcopy(iscsi_target_response)
+ response['target_iqn'] = target_iqn
+ response['groups'][0]['disks'] = []
+ for client in response['clients']:
+ client['info'] = client_info
+ self._update_iscsi_target(create_request, update_request, 200, None, response)
+
+ def _update_iscsi_target(self, create_request, update_request, update_response_code,
+ update_response, response):
+ self._task_post('/api/iscsi/target', create_request)
+ self.assertStatus(201)
+ self._task_put(
+ '/api/iscsi/target/{}'.format(create_request['target_iqn']), update_request)
+ self.assertStatus(update_response_code)
+ self.assertJsonBody(update_response)
+ self._get(
+ '/api/iscsi/target/{}'.format(update_request['new_target_iqn']))
+ self.assertStatus(200)
+ self.assertJsonBody(response)
+
+
+iscsi_target_request = {
+ "target_iqn": "iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw",
+ "portals": [
+ {"ip": "192.168.100.202", "host": "node2"},
+ {"ip": "10.0.2.15", "host": "node2"},
+ {"ip": "192.168.100.203", "host": "node3"}
+ ],
+ "disks": [
+ {"image": "lun1", "pool": "rbd", "backstore": "user:rbd",
+ "controls": {"max_data_area_mb": 128}},
+ {"image": "lun2", "pool": "rbd", "backstore": "user:rbd",
+ "controls": {"max_data_area_mb": 128}}
+ ],
+ "clients": [
+ {
+ "luns": [{"image": "lun1", "pool": "rbd"}],
+ "client_iqn": "iqn.1994-05.com.redhat:rh7-client",
+ "auth": {
+ "password": "myiscsipassword1",
+ "user": "myiscsiusername1",
+ "mutual_password": "myiscsipassword2",
+ "mutual_user": "myiscsiusername2"}
+ },
+ {
+ "luns": [],
+ "client_iqn": "iqn.1994-05.com.redhat:rh7-client2",
+ "auth": {
+ "password": "myiscsipassword3",
+ "user": "myiscsiusername3",
+ "mutual_password": "myiscsipassword4",
+ "mutual_user": "myiscsiusername4"
+ }
+ }
+ ],
+ "acl_enabled": True,
+ "auth": {
+ "password": "",
+ "user": "",
+ "mutual_password": "",
+ "mutual_user": ""},
+ "target_controls": {},
+ "groups": [
+ {
+ "group_id": "mygroup",
+ "disks": [{"pool": "rbd", "image": "lun2"}],
+ "members": ["iqn.1994-05.com.redhat:rh7-client2"]
+ }
+ ]
+}
+
+iscsi_target_response = {
+ 'target_iqn': 'iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw',
+ 'portals': [
+ {'host': 'node2', 'ip': '10.0.2.15'},
+ {'host': 'node2', 'ip': '192.168.100.202'},
+ {'host': 'node3', 'ip': '192.168.100.203'}
+ ],
+ 'disks': [
+ {'pool': 'rbd', 'image': 'lun1', 'backstore': 'user:rbd',
+ 'wwn': '64af6678-9694-4367-bacc-f8eb0baa0', 'lun': 0,
+ 'controls': {'max_data_area_mb': 128}},
+ {'pool': 'rbd', 'image': 'lun2', 'backstore': 'user:rbd',
+ 'wwn': '64af6678-9694-4367-bacc-f8eb0baa1', 'lun': 1,
+ 'controls': {'max_data_area_mb': 128}}
+ ],
+ 'clients': [
+ {
+ 'client_iqn': 'iqn.1994-05.com.redhat:rh7-client',
+ 'luns': [{'pool': 'rbd', 'image': 'lun1'}],
+ 'auth': {
+ 'user': 'myiscsiusername1',
+ 'password': 'myiscsipassword1',
+ 'mutual_password': 'myiscsipassword2',
+ 'mutual_user': 'myiscsiusername2'
+ },
+ 'info': {
+ 'alias': '',
+ 'ip_address': [],
+ 'state': {}
+ }
+ },
+ {
+ 'client_iqn': 'iqn.1994-05.com.redhat:rh7-client2',
+ 'luns': [],
+ 'auth': {
+ 'user': 'myiscsiusername3',
+ 'password': 'myiscsipassword3',
+ 'mutual_password': 'myiscsipassword4',
+ 'mutual_user': 'myiscsiusername4'
+ },
+ 'info': {
+ 'alias': '',
+ 'ip_address': [],
+ 'state': {}
+ }
+ }
+ ],
+ "acl_enabled": True,
+ "auth": {
+ "password": "",
+ "user": "",
+ "mutual_password": "",
+ "mutual_user": ""},
+ 'groups': [
+ {
+ 'group_id': 'mygroup',
+ 'disks': [{'pool': 'rbd', 'image': 'lun2'}],
+ 'members': ['iqn.1994-05.com.redhat:rh7-client2']
+ }
+ ],
+ 'target_controls': {},
+ 'info': {
+ 'num_sessions': 0
+ }
+}
+
+
+class IscsiClientMock(object):
+
+ _instance = None
+
+ def __init__(self):
+ self.gateway_name = None
+ self.service_url = None
+ self.config = {
+ "created": "2019/01/17 08:57:16",
+ "discovery_auth": {
+ "username": "",
+ "password": "",
+ "password_encryption_enabled": False,
+ "mutual_username": "",
+ "mutual_password": "",
+ "mutual_password_encryption_enabled": False
+ },
+ "disks": {},
+ "epoch": 0,
+ "gateways": {},
+ "targets": {},
+ "updated": "",
+ "version": 11
+ }
+ self.clientinfo = {
+ 'alias': '',
+ 'ip_address': [],
+ 'state': {}
+ }
+
+ @classmethod
+ def instance(cls, gateway_name=None, service_url=None):
+ cls._instance.gateway_name = gateway_name
+ cls._instance.service_url = service_url
+ # pylint: disable=unused-argument
+ return cls._instance
+
+ def ping(self):
+ return {
+ "message": "pong"
+ }
+
+ def get_settings(self):
+ return {
+ "api_version": 2,
+ "backstores": [
+ "user:rbd"
+ ],
+ "config": {
+ "minimum_gateways": 2
+ },
+ "default_backstore": "user:rbd",
+ "required_rbd_features": {
+ "rbd": 0,
+ "user:rbd": 4,
+ },
+ "unsupported_rbd_features": {
+ "rbd": 88,
+ "user:rbd": 0,
+ },
+ "disk_default_controls": {
+ "user:rbd": {
+ "hw_max_sectors": 1024,
+ "max_data_area_mb": 8,
+ "osd_op_timeout": 30,
+ "qfull_timeout": 5
+ }
+ },
+ "target_default_controls": {
+ "cmdsn_depth": 128,
+ "dataout_timeout": 20,
+ "first_burst_length": 262144,
+ "immediate_data": "Yes",
+ "initial_r2t": "Yes",
+ "max_burst_length": 524288,
+ "max_outstanding_r2t": 1,
+ "max_recv_data_segment_length": 262144,
+ "max_xmit_data_segment_length": 262144,
+ "nopin_response_timeout": 5,
+ "nopin_timeout": 5
+ }
+ }
+
+ def get_config(self):
+ return copy.deepcopy(self.config)
+
+ def create_target(self, target_iqn, target_controls):
+ self.config['targets'][target_iqn] = {
+ "clients": {},
+ "acl_enabled": True,
+ "auth": {
+ "username": "",
+ "password": "",
+ "password_encryption_enabled": False,
+ "mutual_username": "",
+ "mutual_password": "",
+ "mutual_password_encryption_enabled": False
+ },
+ "controls": target_controls,
+ "created": "2019/01/17 09:22:34",
+ "disks": {},
+ "groups": {},
+ "portals": {}
+ }
+
+ def create_gateway(self, target_iqn, gateway_name, ip_addresses):
+ target_config = self.config['targets'][target_iqn]
+ if 'ip_list' not in target_config:
+ target_config['ip_list'] = []
+ target_config['ip_list'] += ip_addresses
+ target_config['portals'][gateway_name] = {
+ "portal_ip_addresses": ip_addresses
+ }
+
+ def delete_gateway(self, target_iqn, gateway_name):
+ target_config = self.config['targets'][target_iqn]
+ portal_config = target_config['portals'][gateway_name]
+ for ip in portal_config['portal_ip_addresses']:
+ target_config['ip_list'].remove(ip)
+ target_config['portals'].pop(gateway_name)
+
+ def create_disk(self, pool, image, backstore, wwn):
+ if wwn is None:
+ wwn = '64af6678-9694-4367-bacc-f8eb0baa' + str(len(self.config['disks']))
+ image_id = '{}/{}'.format(pool, image)
+ self.config['disks'][image_id] = {
+ "pool": pool,
+ "image": image,
+ "backstore": backstore,
+ "controls": {},
+ "wwn": wwn
+ }
+
+ def create_target_lun(self, target_iqn, image_id, lun):
+ target_config = self.config['targets'][target_iqn]
+ if lun is None:
+ lun = len(target_config['disks'])
+ target_config['disks'][image_id] = {
+ "lun_id": lun
+ }
+ self.config['disks'][image_id]['owner'] = list(target_config['portals'].keys())[0]
+
+ def reconfigure_disk(self, pool, image, controls):
+ image_id = '{}/{}'.format(pool, image)
+ settings = self.get_settings()
+ backstore = self.config['disks'][image_id]['backstore']
+ disk_default_controls = settings['disk_default_controls'][backstore]
+ new_controls = {}
+ for control_k, control_v in controls.items():
+ if control_v != disk_default_controls[control_k]:
+ new_controls[control_k] = control_v
+ self.config['disks'][image_id]['controls'] = new_controls
+
+ def create_client(self, target_iqn, client_iqn):
+ target_config = self.config['targets'][target_iqn]
+ target_config['clients'][client_iqn] = {
+ "auth": {
+ "username": "",
+ "password": "",
+ "password_encryption_enabled": False,
+ "mutual_username": "",
+ "mutual_password": "",
+ "mutual_password_encryption_enabled": False
+ },
+ "group_name": "",
+ "luns": {}
+ }
+
+ def create_client_lun(self, target_iqn, client_iqn, image_id):
+ target_config = self.config['targets'][target_iqn]
+ target_config['clients'][client_iqn]['luns'][image_id] = {}
+
+ def delete_client_lun(self, target_iqn, client_iqn, image_id):
+ target_config = self.config['targets'][target_iqn]
+ del target_config['clients'][client_iqn]['luns'][image_id]
+
+ def create_client_auth(self, target_iqn, client_iqn, user, password, m_user, m_password):
+ target_config = self.config['targets'][target_iqn]
+ target_config['clients'][client_iqn]['auth']['username'] = user
+ target_config['clients'][client_iqn]['auth']['password'] = password
+ target_config['clients'][client_iqn]['auth']['mutual_username'] = m_user
+ target_config['clients'][client_iqn]['auth']['mutual_password'] = m_password
+
+ def create_group(self, target_iqn, group_name, members, image_ids):
+ target_config = self.config['targets'][target_iqn]
+ target_config['groups'][group_name] = {
+ "disks": {},
+ "members": []
+ }
+ for image_id in image_ids:
+ target_config['groups'][group_name]['disks'][image_id] = {}
+ target_config['groups'][group_name]['members'] = members
+
+ def update_group(self, target_iqn, group_name, members, image_ids):
+ target_config = self.config['targets'][target_iqn]
+ group = target_config['groups'][group_name]
+ old_members = group['members']
+ disks = group['disks']
+ target_config['groups'][group_name] = {
+ "disks": {},
+ "members": []
+ }
+
+ for image_id in disks.keys():
+ if image_id not in image_ids:
+ target_config['groups'][group_name]['disks'][image_id] = {}
+
+ new_members = []
+ for member_iqn in old_members:
+ if member_iqn not in members:
+ new_members.append(member_iqn)
+ target_config['groups'][group_name]['members'] = new_members
+
+ def delete_group(self, target_iqn, group_name):
+ target_config = self.config['targets'][target_iqn]
+ del target_config['groups'][group_name]
+
+ def delete_client(self, target_iqn, client_iqn):
+ target_config = self.config['targets'][target_iqn]
+ del target_config['clients'][client_iqn]
+
+ def delete_target_lun(self, target_iqn, image_id):
+ target_config = self.config['targets'][target_iqn]
+ target_config['disks'].pop(image_id)
+ del self.config['disks'][image_id]['owner']
+
+ def delete_disk(self, pool, image):
+ image_id = '{}/{}'.format(pool, image)
+ del self.config['disks'][image_id]
+
+ def delete_target(self, target_iqn):
+ del self.config['targets'][target_iqn]
+
+ def get_ip_addresses(self):
+ ips = {
+ 'node1': ['192.168.100.201'],
+ 'node2': ['192.168.100.202', '10.0.2.15'],
+ 'node3': ['192.168.100.203']
+ }
+ return {'data': ips[self.gateway_name]}
+
+ def get_hostname(self):
+ hostnames = {
+ 'https://admin:admin@10.17.5.1:5001': 'node1',
+ 'https://admin:admin@10.17.5.2:5001': 'node2',
+ 'https://admin:admin@10.17.5.3:5001': 'node3'
+ }
+ if self.service_url not in hostnames:
+ raise RequestException('No route to host')
+ return {'data': hostnames[self.service_url]}
+
+ def update_discoveryauth(self, user, password, mutual_user, mutual_password):
+ self.config['discovery_auth']['username'] = user
+ self.config['discovery_auth']['password'] = password
+ self.config['discovery_auth']['mutual_username'] = mutual_user
+ self.config['discovery_auth']['mutual_password'] = mutual_password
+
+ def update_targetacl(self, target_iqn, action):
+ self.config['targets'][target_iqn]['acl_enabled'] = (action == 'enable_acl')
+
+ def update_targetauth(self, target_iqn, user, password, mutual_user, mutual_password):
+ target_config = self.config['targets'][target_iqn]
+ target_config['auth']['username'] = user
+ target_config['auth']['password'] = password
+ target_config['auth']['mutual_username'] = mutual_user
+ target_config['auth']['mutual_password'] = mutual_password
+
+ def get_targetinfo(self, target_iqn):
+ # pylint: disable=unused-argument
+ return {
+ 'num_sessions': 0
+ }
+
+ def get_clientinfo(self, target_iqn, client_iqn):
+ # pylint: disable=unused-argument
+ return self.clientinfo