]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Drop iSCSI gateway name parameter 26984/head
authorRicardo Marques <rimarques@suse.com>
Fri, 15 Mar 2019 13:50:09 +0000 (13:50 +0000)
committerRicardo Marques <rimarques@suse.com>
Fri, 15 Mar 2019 14:05:49 +0000 (14:05 +0000)
iSCSI gateway name will now be obtained from the
ceph-iscsi `/api/sysinfo/hostname` endpoint

Signed-off-by: Ricardo Marques <rimarques@suse.com>
doc/mgr/dashboard.rst
src/pybind/mgr/dashboard/services/iscsi_cli.py
src/pybind/mgr/dashboard/services/iscsi_client.py
src/pybind/mgr/dashboard/services/iscsi_config.py [new file with mode: 0644]
src/pybind/mgr/dashboard/tests/test_iscsi.py

index b2918c90cd3d3040ca31bcbee3ddcebe1d674136..a557d43eecf42ead985b8992afcc5ab5d64d1f8c 100644 (file)
@@ -318,19 +318,19 @@ The Ceph Dashboard can manage iSCSI targets using the REST API provided
 by the `rbd-target-api` service of the `ceph-iscsi <https://github.com/ceph/ceph-iscsi>`_
 project. Please make sure that it's installed and enabled on the iSCSI gateways.
 
-The available iSCSI gateways must be defined using the following commands::
-
-    $ ceph dashboard iscsi-gateway-list
-    $ ceph dashboard iscsi-gateway-add <gateway_name> <scheme>://<username>:<password>@<host>[:port]
-    $ ceph dashboard iscsi-gateway-rm <gateway_name>
-
 If ceph-iscsi REST API is configured in HTTPS mode and its using a self-signed
 certificate, then we need to configure the dashboard to avoid SSL certificate
 verification when accessing ceph-iscsi API.
 
 To disable API SSL verification run the following commmand::
 
-    $ ceph dashboard iscsi-set-api-ssl-verification false
+    $ ceph dashboard set-iscsi-api-ssl-verification false
+
+The available iSCSI gateways must be defined using the following commands::
+
+    $ ceph dashboard iscsi-gateway-list
+    $ ceph dashboard iscsi-gateway-add <scheme>://<username>:<password>@<host>[:port]
+    $ ceph dashboard iscsi-gateway-rm <gateway_name>
 
 
 .. _dashboard-grafana:
index c03d69f78e07c919dd64df9c7a18e1b9b71639b0..fca1f61b3cc14db1a24a176f935bad97ba016bfb 100644 (file)
@@ -4,97 +4,12 @@ from __future__ import absolute_import
 import errno
 import json
 
-try:
-    from urlparse import urlparse
-except ImportError:
-    from urllib.parse import urlparse
-
 from mgr_module import CLIReadCommand, CLIWriteCommand
 
-from .orchestrator import OrchClient
-from .. import mgr
-
-
-class IscsiGatewayAlreadyExists(Exception):
-    def __init__(self, gateway_name):
-        super(IscsiGatewayAlreadyExists, self).__init__(
-            "iSCSI gateway '{}' already exists".format(gateway_name))
-
-
-class IscsiGatewayDoesNotExist(Exception):
-    def __init__(self, hostname):
-        super(IscsiGatewayDoesNotExist, self).__init__(
-            "iSCSI gateway '{}' does not exist".format(hostname))
-
-
-class InvalidServiceUrl(Exception):
-    def __init__(self, service_url):
-        super(InvalidServiceUrl, self).__init__(
-            "Invalid service URL '{}'. "
-            "Valid format: '<scheme>://<username>:<password>@<host>[:port]'.".format(service_url))
-
-
-class ManagedByOrchestratorException(Exception):
-    def __init__(self):
-        super(ManagedByOrchestratorException, self).__init__(
-            "iSCSI configuration is managed by the orchestrator")
-
-
-_ISCSI_STORE_KEY = "_iscsi_config"
-
-
-class IscsiGatewaysConfig(object):
-    @classmethod
-    def _load_config(cls):
-        if OrchClient.instance().available():
-            raise ManagedByOrchestratorException()
-        json_db = mgr.get_store(_ISCSI_STORE_KEY,
-                                '{"gateways": {}}')
-        return json.loads(json_db)
-
-    @classmethod
-    def _save_config(cls, config):
-        mgr.set_store(_ISCSI_STORE_KEY, json.dumps(config))
-
-    @classmethod
-    def add_gateway(cls, name, service_url):
-        config = cls._load_config()
-        if name in config:
-            raise IscsiGatewayAlreadyExists(name)
-        url = urlparse(service_url)
-        if not url.scheme or not url.hostname or not url.username or not url.password:
-            raise InvalidServiceUrl(service_url)
-        config['gateways'][name] = {'service_url': service_url}
-        cls._save_config(config)
-
-    @classmethod
-    def remove_gateway(cls, name):
-        config = cls._load_config()
-        if name not in config['gateways']:
-            raise IscsiGatewayDoesNotExist(name)
-
-        del config['gateways'][name]
-        cls._save_config(config)
-
-    @classmethod
-    def get_gateways_config(cls):
-        try:
-            config = cls._load_config()
-        except ManagedByOrchestratorException:
-            config = {'gateways': {}}
-            instances = OrchClient.instance().list_service_info("iscsi")
-            for instance in instances:
-                config['gateways'][instance.nodename] = {
-                    'service_url': instance.service_url
-                }
-        return config
-
-    @classmethod
-    def get_gateway_config(cls, name):
-        config = IscsiGatewaysConfig.get_gateways_config()
-        if name not in config['gateways']:
-            raise IscsiGatewayDoesNotExist(name)
-        return config['gateways'][name]
+from .iscsi_client import IscsiClient
+from .iscsi_config import IscsiGatewaysConfig, IscsiGatewayAlreadyExists, InvalidServiceUrl, \
+    ManagedByOrchestratorException, IscsiGatewayDoesNotExist
+from ..rest_client import RequestException
 
 
 @CLIReadCommand('dashboard iscsi-gateway-list', desc='List iSCSI gateways')
@@ -103,11 +18,12 @@ def list_iscsi_gateways(_):
 
 
 @CLIWriteCommand('dashboard iscsi-gateway-add',
-                 'name=name,type=CephString '
                  'name=service_url,type=CephString',
                  'Add iSCSI gateway configuration')
-def add_iscsi_gateway(_, name, service_url):
+def add_iscsi_gateway(_, service_url):
     try:
+        IscsiGatewaysConfig.validate_service_url(service_url)
+        name = IscsiClient.instance(service_url=service_url).get_hostname()['data']
         IscsiGatewaysConfig.add_gateway(name, service_url)
         return 0, 'Success', ''
     except IscsiGatewayAlreadyExists as ex:
@@ -116,6 +32,8 @@ def add_iscsi_gateway(_, name, service_url):
         return -errno.EINVAL, '', str(ex)
     except ManagedByOrchestratorException as ex:
         return -errno.EINVAL, '', str(ex)
+    except RequestException as ex:
+        return -errno.EINVAL, '', str(ex)
 
 
 @CLIWriteCommand('dashboard iscsi-gateway-rm',
index 92d3a16e52a0fd7d5f5c018c8ba3bdad56d58b78..e69c621b5586f2024bc3f0ef45887c0b42492dfd 100644 (file)
@@ -11,7 +11,7 @@ try:
 except ImportError:
     from urllib.parse import urlparse
 
-from .iscsi_cli import IscsiGatewaysConfig
+from .iscsi_config import IscsiGatewaysConfig
 from .. import logger
 from ..settings import Settings
 from ..rest_client import RestClient
@@ -24,11 +24,12 @@ class IscsiClient(RestClient):
     service_url = None
 
     @classmethod
-    def instance(cls, gateway_name=None):
-        if not gateway_name:
-            gateway_name = list(IscsiGatewaysConfig.get_gateways_config()['gateways'].keys())[0]
-        gateways_config = IscsiGatewaysConfig.get_gateway_config(gateway_name)
-        service_url = gateways_config['service_url']
+    def instance(cls, gateway_name=None, service_url=None):
+        if not service_url:
+            if not gateway_name:
+                gateway_name = list(IscsiGatewaysConfig.get_gateways_config()['gateways'].keys())[0]
+            gateways_config = IscsiGatewaysConfig.get_gateway_config(gateway_name)
+            service_url = gateways_config['service_url']
 
         instance = cls._instances.get(gateway_name)
         if not instance or service_url != instance.service_url or \
@@ -46,7 +47,8 @@ class IscsiClient(RestClient):
             instance = IscsiClient(host, port, IscsiClient._CLIENT_NAME, ssl,
                                    auth, Settings.ISCSI_API_SSL_VERIFICATION)
             instance.service_url = service_url
-            cls._instances[gateway_name] = instance
+            if gateway_name:
+                cls._instances[gateway_name] = instance
 
         return instance
 
@@ -62,6 +64,10 @@ class IscsiClient(RestClient):
     def get_ip_addresses(self, request=None):
         return request()
 
+    @RestClient.api_get('/api/sysinfo/hostname')
+    def get_hostname(self, request=None):
+        return request()
+
     @RestClient.api_get('/api/config')
     def get_config(self, request=None):
         return request({
diff --git a/src/pybind/mgr/dashboard/services/iscsi_config.py b/src/pybind/mgr/dashboard/services/iscsi_config.py
new file mode 100644 (file)
index 0000000..df6537c
--- /dev/null
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import
+
+import json
+
+try:
+    from urlparse import urlparse
+except ImportError:
+    from urllib.parse import urlparse
+
+from .orchestrator import OrchClient
+from .. import mgr
+
+
+class IscsiGatewayAlreadyExists(Exception):
+    def __init__(self, gateway_name):
+        super(IscsiGatewayAlreadyExists, self).__init__(
+            "iSCSI gateway '{}' already exists".format(gateway_name))
+
+
+class IscsiGatewayDoesNotExist(Exception):
+    def __init__(self, hostname):
+        super(IscsiGatewayDoesNotExist, self).__init__(
+            "iSCSI gateway '{}' does not exist".format(hostname))
+
+
+class InvalidServiceUrl(Exception):
+    def __init__(self, service_url):
+        super(InvalidServiceUrl, self).__init__(
+            "Invalid service URL '{}'. "
+            "Valid format: '<scheme>://<username>:<password>@<host>[:port]'.".format(service_url))
+
+
+class ManagedByOrchestratorException(Exception):
+    def __init__(self):
+        super(ManagedByOrchestratorException, self).__init__(
+            "iSCSI configuration is managed by the orchestrator")
+
+
+_ISCSI_STORE_KEY = "_iscsi_config"
+
+
+class IscsiGatewaysConfig(object):
+    @classmethod
+    def _load_config(cls):
+        if OrchClient.instance().available():
+            raise ManagedByOrchestratorException()
+        json_db = mgr.get_store(_ISCSI_STORE_KEY,
+                                '{"gateways": {}}')
+        return json.loads(json_db)
+
+    @classmethod
+    def _save_config(cls, config):
+        mgr.set_store(_ISCSI_STORE_KEY, json.dumps(config))
+
+    @classmethod
+    def validate_service_url(cls, service_url):
+        url = urlparse(service_url)
+        if not url.scheme or not url.hostname or not url.username or not url.password:
+            raise InvalidServiceUrl(service_url)
+
+    @classmethod
+    def add_gateway(cls, name, service_url):
+        config = cls._load_config()
+        if name in config:
+            raise IscsiGatewayAlreadyExists(name)
+        IscsiGatewaysConfig.validate_service_url(service_url)
+        config['gateways'][name] = {'service_url': service_url}
+        cls._save_config(config)
+
+    @classmethod
+    def remove_gateway(cls, name):
+        config = cls._load_config()
+        if name not in config['gateways']:
+            raise IscsiGatewayDoesNotExist(name)
+
+        del config['gateways'][name]
+        cls._save_config(config)
+
+    @classmethod
+    def get_gateways_config(cls):
+        try:
+            config = cls._load_config()
+        except ManagedByOrchestratorException:
+            config = {'gateways': {}}
+            instances = OrchClient.instance().list_service_info("iscsi")
+            for instance in instances:
+                config['gateways'][instance.nodename] = {
+                    'service_url': instance.service_url
+                }
+        return config
+
+    @classmethod
+    def get_gateway_config(cls, name):
+        config = IscsiGatewaysConfig.get_gateways_config()
+        if name not in config['gateways']:
+            raise IscsiGatewayDoesNotExist(name)
+        return config['gateways'][name]
index f86ac2a9e101449eab692b82cec2fa0201aabf7b..c8d99eea280ab5df7456f8631dc52a0899610860 100644 (file)
@@ -10,6 +10,7 @@ from .. import mgr
 from ..controllers.iscsi import Iscsi, IscsiTarget
 from ..services.iscsi_client import IscsiClient
 from ..services.orchestrator import OrchClient
+from ..rest_client import RequestException
 
 
 class IscsiTest(ControllerTestCase, CLICommandTestMixin):
@@ -440,6 +441,7 @@ class IscsiClientMock(object):
 
     def __init__(self):
         self.gateway_name = None
+        self.service_url = None
         self.config = {
             "created": "2019/01/17 08:57:16",
             "discovery_auth": {
@@ -459,8 +461,9 @@ class IscsiClientMock(object):
         }
 
     @classmethod
-    def instance(cls, gateway_name=None):
+    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
 
@@ -606,6 +609,16 @@ class IscsiClientMock(object):
         }
         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