]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/dashboard: expose gw_groups list api
authorNizamudeen A <nia@redhat.com>
Tue, 3 Sep 2024 12:29:47 +0000 (17:59 +0530)
committerNizamudeen A <nia@redhat.com>
Thu, 12 Sep 2024 06:02:03 +0000 (11:32 +0530)
also if the cephadm is available, make the api smart enough to
understand the running daemons and get its gateway_addr instead of
taking the first one in the list so that the HA will be there for the UI
and API as well

Fixes: https://tracker.ceph.com/issues/67774
Signed-off-by: Nizamudeen A <nia@redhat.com>
(cherry picked from commit c8434d67d08882ac88c579c6e199f59f05a31699)

src/pybind/mgr/cephadm/services/nvmeof.py
src/pybind/mgr/dashboard/controllers/nvmeof.py
src/pybind/mgr/dashboard/openapi.yaml
src/pybind/mgr/dashboard/services/nvmeof_cli.py
src/pybind/mgr/dashboard/services/nvmeof_conf.py

index 00b03ebbb4d61e53946d2e827ccdeeb86d70de27..e2d637844e8c075743e3b32fb624f6d6f4072df1 100644 (file)
@@ -106,7 +106,8 @@ class NvmeofService(CephService):
                         'prefix': 'dashboard nvmeof-gateway-add',
                         'inbuf': service_url,
                         'name': service_name,
-                        'group': spec.group
+                        'group': spec.group,
+                        'daemon_name': dd.name()
                     })
             return cmd_dicts
 
index db54ac46162212c32e97690005bee09d5c8232e9..ec9c9897081737b8b0874291af1f7c6de34604c9 100644 (file)
@@ -2,6 +2,8 @@
 import logging
 from typing import Any, Dict, Optional
 
+from orchestrator import OrchestratorError
+
 from .. import mgr
 from ..model import nvmeof as model
 from ..security import Scope
@@ -35,6 +37,19 @@ else:
                 NVMeoFClient.pb2.get_gateway_info_req()
             )
 
+        @ReadPermission
+        @Endpoint('GET')
+        def group(self):
+            try:
+                orch = OrchClient.instance()
+                return orch.services.list(service_type='nvmeof')
+            except OrchestratorError as e:
+                # just return none instead of raising an exception
+                # since we need this to work regardless of the status
+                # of orchestrator in UI
+                logger.error('Failed to fetch the gateway groups: %s', e)
+                return None
+
     @APIRouter("/nvmeof/subsystem", Scope.NVME_OF)
     @APIDoc("NVMe-oF Subsystem Management API", "NVMe-oF Subsystem")
     class NVMeoFSubsystem(RESTController):
index 120a6eed6fb25a0812ee59f47afd20d87ecd21a6..2e82bf220ce5bfcf51d7fcc252776e57ff6be60b 100644 (file)
@@ -7756,6 +7756,28 @@ paths:
       summary: Get information about the NVMeoF gateway
       tags:
       - NVMe-oF Gateway
+  /api/nvmeof/gateway/group:
+    get:
+      parameters: []
+      responses:
+        '200':
+          content:
+            application/vnd.ceph.api.v1.0+json:
+              type: object
+          description: OK
+        '400':
+          description: Operation exception. Please check the response body for details.
+        '401':
+          description: Unauthenticated access. Please login first.
+        '403':
+          description: Unauthorized access. Please check your permissions.
+        '500':
+          description: Unexpected error. Please check the response body for the stack
+            trace.
+      security:
+      - jwt: []
+      tags:
+      - NVMe-oF Gateway
   /api/nvmeof/subsystem:
     get:
       parameters:
index 2974fa0010db2ad32c2e6f9fb7fee6575bbcde64..11a95237053dbf0214fea94605a8855d0bdfd361 100644 (file)
@@ -19,13 +19,13 @@ def list_nvmeof_gateways(_):
 
 @CLIWriteCommand('dashboard nvmeof-gateway-add')
 @CLICheckNonemptyFileInput(desc='NVMe-oF gateway configuration')
-def add_nvmeof_gateway(_, inbuf, name: str, group: str):
+def add_nvmeof_gateway(_, inbuf, name: str, group: str, daemon_name: str):
     '''
     Add NVMe-oF gateway configuration. Gateway URL read from -i <file>
     '''
     service_url = inbuf
     try:
-        NvmeofGatewaysConfig.add_gateway(name, service_url, group)
+        NvmeofGatewaysConfig.add_gateway(name, service_url, group, daemon_name)
         return 0, 'Success', ''
     except NvmeofGatewayAlreadyExists as ex:
         return -errno.EEXIST, '', str(ex)
index 7e015ae83acf47f96b850e21bbdec323ae67bd24..a5a9979af25720e1a980ec50f6843b611bc8c416 100644 (file)
@@ -51,18 +51,28 @@ class NvmeofGatewaysConfig(object):
         return cls._load_config_from_store()
 
     @classmethod
-    def add_gateway(cls, name, service_url, group):
+    def add_gateway(cls, name, service_url, group, daemon_name):
         config = cls.get_gateways_config()
 
         if name in config.get('gateways', {}):
             existing_gateways = config['gateways'][name]
-            if any(gateway['service_url'] == service_url for gateway in existing_gateways):
-                return
+            for gateway in existing_gateways:
+                if 'daemon_name' not in gateway:
+                    gateway['daemon_name'] = daemon_name
+                    break
+                if gateway['service_url'] == service_url:
+                    return
+
+        new_gateway = {
+            'service_url': service_url,
+            'group': group,
+            'daemon_name': daemon_name
+        }
 
         if name in config.get('gateways', {}):
-            config['gateways'][name].append({'service_url': service_url, 'group': group})
+            config['gateways'][name].append(new_gateway)
         else:
-            config['gateways'][name] = [{'service_url': service_url, 'group': group}]
+            config['gateways'][name] = [new_gateway]
 
         cls._save_config(config)
 
@@ -83,15 +93,10 @@ class NvmeofGatewaysConfig(object):
                 return None
 
             if group:
-                for service_name, entries in gateways.items():
-                    if group in service_name:
-                        entry = next((entry for entry in entries if entry['group'] == group), None)
-                        if entry['group'] == group:  # type: ignore
-                            return service_name, entry['service_url']  # type: ignore
-                return None
+                return cls._get_name_url_for_group(gateways, group)
+
+            return cls._get_default_service(gateways)
 
-            service_name = list(gateways.keys())[0]
-            return service_name, config['gateways'][service_name][0]['service_url']
         except (KeyError, IndexError) as e:
             raise DashboardException(
                 msg=f'NVMe-oF configuration is not set: {e}',
@@ -131,3 +136,45 @@ class NvmeofGatewaysConfig(object):
             # just return None if any orchestrator error is raised
             # otherwise nvmeof api will raise this error and doesn't proceed.
             return None
+
+    @classmethod
+    def _get_name_url_for_group(cls, gateways, group):
+        try:
+            orch = OrchClient.instance()
+            for service_name, svc_config in gateways.items():
+                # get the group name of the service and match it against the
+                # group name provided
+                group_name_from_svc = orch.services.get(service_name)[0].spec.group
+                if group == group_name_from_svc:
+                    running_daemons = cls._get_running_daemons(orch, service_name)
+                    config = cls._get_running_daemon_svc_config(svc_config, running_daemons)
+
+                    if config:
+                        return service_name, config['service_url']
+            return None
+
+        except OrchestratorError:
+            return cls._get_default_service(gateways)
+
+    @classmethod
+    def _get_running_daemons(cls, orch, service_name):
+        # get the running nvmeof daemons
+        daemons = [d.to_dict()
+                   for d in orch.services.list_daemons(service_name=service_name)]
+        return [d['daemon_name'] for d in daemons
+                if d['status_desc'] == 'running']
+
+    @classmethod
+    def _get_running_daemon_svc_config(cls, svc_config, running_daemons):
+        try:
+            return next(config for config in svc_config
+                        if config['daemon_name'] in running_daemons)
+        except StopIteration:
+            return None
+
+    @classmethod
+    def _get_default_service(cls, gateways):
+        if gateways:
+            service_name = list(gateways.keys())[0]
+            return service_name, gateways[service_name][0]['service_url']
+        return None