From db067b508afba97f5c3a0a3aa3db094f00533895 Mon Sep 17 00:00:00 2001 From: Nizamudeen A Date: Tue, 3 Sep 2024 17:59:47 +0530 Subject: [PATCH] mgr/dashboard: expose gw_groups list api 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 (cherry picked from commit c8434d67d08882ac88c579c6e199f59f05a31699) --- src/pybind/mgr/cephadm/services/nvmeof.py | 3 +- .../mgr/dashboard/controllers/nvmeof.py | 15 ++++ src/pybind/mgr/dashboard/openapi.yaml | 22 ++++++ .../mgr/dashboard/services/nvmeof_cli.py | 4 +- .../mgr/dashboard/services/nvmeof_conf.py | 73 +++++++++++++++---- 5 files changed, 101 insertions(+), 16 deletions(-) diff --git a/src/pybind/mgr/cephadm/services/nvmeof.py b/src/pybind/mgr/cephadm/services/nvmeof.py index 00b03ebbb4d..e2d637844e8 100644 --- a/src/pybind/mgr/cephadm/services/nvmeof.py +++ b/src/pybind/mgr/cephadm/services/nvmeof.py @@ -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 diff --git a/src/pybind/mgr/dashboard/controllers/nvmeof.py b/src/pybind/mgr/dashboard/controllers/nvmeof.py index db54ac46162..ec9c9897081 100644 --- a/src/pybind/mgr/dashboard/controllers/nvmeof.py +++ b/src/pybind/mgr/dashboard/controllers/nvmeof.py @@ -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): diff --git a/src/pybind/mgr/dashboard/openapi.yaml b/src/pybind/mgr/dashboard/openapi.yaml index 120a6eed6fb..2e82bf220ce 100644 --- a/src/pybind/mgr/dashboard/openapi.yaml +++ b/src/pybind/mgr/dashboard/openapi.yaml @@ -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: diff --git a/src/pybind/mgr/dashboard/services/nvmeof_cli.py b/src/pybind/mgr/dashboard/services/nvmeof_cli.py index 2974fa0010d..11a95237053 100644 --- a/src/pybind/mgr/dashboard/services/nvmeof_cli.py +++ b/src/pybind/mgr/dashboard/services/nvmeof_cli.py @@ -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 ''' 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) diff --git a/src/pybind/mgr/dashboard/services/nvmeof_conf.py b/src/pybind/mgr/dashboard/services/nvmeof_conf.py index 7e015ae83ac..a5a9979af25 100644 --- a/src/pybind/mgr/dashboard/services/nvmeof_conf.py +++ b/src/pybind/mgr/dashboard/services/nvmeof_conf.py @@ -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 -- 2.39.5