From: Nizamudeen A Date: Tue, 5 Dec 2023 10:11:01 +0000 (+0530) Subject: mgr/cephadm: configure the dashboard gateways X-Git-Tag: v19.3.0~104^2~4 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=a2fae7b0e267045fc6de30b21db7c43ab940f76a;p=ceph.git mgr/cephadm: configure the dashboard gateways cephadm configures the nvmeof gateways and add the gateways to a config store which dashboard will later on fetch to make the grpc calls. Fixes: https://tracker.ceph.com/issues/64201 Signed-off-by: Nizamudeen A --- diff --git a/.gitmodules b/.gitmodules index 088ae3b577ce..e47fe6495f5f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -75,3 +75,6 @@ [submodule "src/jaegertracing/opentelemetry-cpp"] path = src/jaegertracing/opentelemetry-cpp url = https://github.com/open-telemetry/opentelemetry-cpp.git +[submodule "nvmeof"] + path = src/pybind/mgr/dashboard/ceph-nvmeof + url = https://github.com/ceph/ceph-nvmeof.git diff --git a/src/pybind/mgr/cephadm/serve.py b/src/pybind/mgr/cephadm/serve.py index 116e97238691..4afe0a3b7aca 100644 --- a/src/pybind/mgr/cephadm/serve.py +++ b/src/pybind/mgr/cephadm/serve.py @@ -40,7 +40,7 @@ if TYPE_CHECKING: logger = logging.getLogger(__name__) -REQUIRES_POST_ACTIONS = ['grafana', 'iscsi', 'prometheus', 'alertmanager', 'rgw'] +REQUIRES_POST_ACTIONS = ['grafana', 'iscsi', 'prometheus', 'alertmanager', 'rgw', 'nvmeof'] class CephadmServe: diff --git a/src/pybind/mgr/cephadm/services/nvmeof.py b/src/pybind/mgr/cephadm/services/nvmeof.py index 7d2dd16cf0d6..32cb71b67447 100644 --- a/src/pybind/mgr/cephadm/services/nvmeof.py +++ b/src/pybind/mgr/cephadm/services/nvmeof.py @@ -2,6 +2,7 @@ import errno import logging import json from typing import List, cast, Optional +from ipaddress import ip_address, IPv6Address from mgr_module import HandleCommandResult from ceph.deployment.service_spec import NvmeofServiceSpec @@ -55,8 +56,39 @@ class NvmeofService(CephService): return daemon_spec def config_dashboard(self, daemon_descrs: List[DaemonDescription]) -> None: - # TODO: what integration do we need with the dashboard? - pass + def get_set_cmd_dicts(out: str) -> List[dict]: + gateways = json.loads(out)['gateways'] + cmd_dicts = [] + + spec = cast(NvmeofServiceSpec, + self.mgr.spec_store.all_specs.get(daemon_descrs[0].service_name(), None)) + + for dd in daemon_descrs: + assert dd.hostname is not None + + if not spec: + logger.warning(f'No ServiceSpec found for {dd.service_name()}') + continue + + ip = utils.resolve_ip(self.mgr.inventory.get_addr(dd.hostname)) + if type(ip_address(ip)) is IPv6Address: + ip = f'[{ip}]' + service_url = '{}:{}'.format(ip, spec.port or '5500') + gw = gateways.get(dd.hostname) + if not gw or gw['service_url'] != service_url: + logger.info(f'Adding NVMeoF gateway {service_url} to Dashboard') + cmd_dicts.append({ + 'prefix': 'dashboard nvmeof-gateway-add', + 'inbuf': service_url, + 'name': dd.hostname + }) + return cmd_dicts + + self._check_and_set_dashboard( + service_name='nvmeof', + get_cmd='dashboard nvmeof-gateway-list', + get_set_cmd_dicts=get_set_cmd_dicts + ) def ok_to_stop(self, daemon_ids: List[str], @@ -83,7 +115,14 @@ class NvmeofService(CephService): Called after the daemon is removed. """ logger.debug(f'Post remove daemon {self.TYPE}.{daemon.daemon_id}') - # TODO: remove config for dashboard nvmeof gateways if any + # remove config for dashboard nvmeof gateways if any + ret, out, err = self.mgr.mon_command({ + 'prefix': 'dashboard nvmeof-gateway-rm', + 'name': daemon.hostname, + }) + if not ret: + logger.info(f'{daemon.hostname} removed from iscsi gateways dashboard config') + # and any certificates being used for mTLS def purge(self, service_name: str) -> None: diff --git a/src/pybind/mgr/dashboard/ceph-nvmeof b/src/pybind/mgr/dashboard/ceph-nvmeof new file mode 160000 index 000000000000..c6f6ce77863f --- /dev/null +++ b/src/pybind/mgr/dashboard/ceph-nvmeof @@ -0,0 +1 @@ +Subproject commit c6f6ce77863f854444dee3d2a59d360f3b4f2255 diff --git a/src/pybind/mgr/dashboard/constraints.txt b/src/pybind/mgr/dashboard/constraints.txt index fd6141048800..590a8c1e1c42 100644 --- a/src/pybind/mgr/dashboard/constraints.txt +++ b/src/pybind/mgr/dashboard/constraints.txt @@ -4,3 +4,5 @@ bcrypt~=3.1 python3-saml~=1.4 requests~=2.26 Routes~=2.4 +grpcio~=1.48 +grpcio-tools~=1.48 diff --git a/src/pybind/mgr/dashboard/services/nvmeof_cli.py b/src/pybind/mgr/dashboard/services/nvmeof_cli.py new file mode 100644 index 000000000000..5921ab48ea8b --- /dev/null +++ b/src/pybind/mgr/dashboard/services/nvmeof_cli.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +import errno +import json + +from mgr_module import CLICheckNonemptyFileInput, CLIReadCommand, CLIWriteCommand + +from ..rest_client import RequestException +from .nvmeof_conf import NvmeofGatewaysConfig, NvmeofGatewayAlreadyExists, \ + ManagedByOrchestratorException + +@CLIReadCommand('dashboard nvmeof-gateway-list') +def list_nvmeof_gateways(_): + ''' + List NVMe-oF gateways + ''' + return 0, json.dumps(NvmeofGatewaysConfig.get_gateways_config()), '' + +@CLIWriteCommand('dashboard nvmeof-gateway-add') +@CLICheckNonemptyFileInput(desc='NVMe-oF gateway configuration') +def add_nvmeof_gateway(_, inbuf, name: str): + ''' + Add NVMe-oF gateway configuration. Gateway URL read from -i + ''' + service_url = inbuf + try: + NvmeofGatewaysConfig.add_gateway(name, service_url) + return 0, 'Success', '' + except NvmeofGatewayAlreadyExists as ex: + return -errno.EEXIST, '', str(ex) + except ManagedByOrchestratorException as ex: + return -errno.EINVAL, '', str(ex) + except RequestException as ex: + return -errno.EINVAL, '', str(ex) + +@CLIWriteCommand('dashboard nvmeof-gateway-rm') +def remove_nvmeof_gateway(_, name: str): + ''' + Remove NVMe-oF gateway configuration + ''' + try: + NvmeofGatewaysConfig.remove_gateway(name) + return 0, 'Success', '' + except ManagedByOrchestratorException as ex: + return -errno.EINVAL, '', str(ex) diff --git a/src/pybind/mgr/dashboard/services/nvmeof_conf.py b/src/pybind/mgr/dashboard/services/nvmeof_conf.py new file mode 100644 index 000000000000..709635dc7db9 --- /dev/null +++ b/src/pybind/mgr/dashboard/services/nvmeof_conf.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +import json + +from .. import mgr + +class NvmeofGatewayAlreadyExists(Exception): + def __init__(self, gateway_name): + super(NvmeofGatewayAlreadyExists, self).__init__( + "NVMe-oF gateway '{}' already exists".format(gateway_name)) + +class NvmeofGatewayDoesNotExist(Exception): + def __init__(self, hostname): + super(NvmeofGatewayDoesNotExist, self).__init__( + "NVMe-oF gateway '{}' does not exist".format(hostname)) + +class ManagedByOrchestratorException(Exception): + def __init__(self): + super(ManagedByOrchestratorException, self).__init__( + "NVMe-oF configuration is managed by the orchestrator") + +_NVMEOF_STORE_KEY = "_nvmeof_config" + +class NvmeofGatewaysConfig(object): + @classmethod + def _load_config_from_store(cls): + json_db = mgr.get_store(_NVMEOF_STORE_KEY, + '{"gateways": {}}') + config = json.loads(json_db) + cls._save_config(config) + return config + + @classmethod + def _save_config(cls, config): + mgr.set_store(_NVMEOF_STORE_KEY, json.dumps(config)) + + @classmethod + def get_gateways_config(cls): + return cls._load_config_from_store() + + @classmethod + def add_gateway(cls, name, service_url): + config = cls.get_gateways_config() + if name in config: + raise NvmeofGatewayAlreadyExists(name) + config['gateways'][name] = {'service_url': service_url} + cls._save_config(config) + + @classmethod + def remove_gateway(cls, name): + config = cls.get_gateways_config() + if name not in config['gateways']: + raise NvmeofGatewayDoesNotExist(name) + del config['gateways'][name] + cls._save_config(config)