]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/cephadm: fix mgmt-gateway startup on IPv6 VIP 68387/head
authorkginon <kobi.ginon.ext@nokia.com>
Mon, 2 Mar 2026 14:44:05 +0000 (16:44 +0200)
committerRedouane Kachach <rkachach@ibm.com>
Wed, 15 Apr 2026 13:52:11 +0000 (15:52 +0200)
Ensure mgmt-gateway service starts correctly when it is configured
to listen on an IPv6 VIP address.

Fixes: https://tracker.ceph.com/issues/75267 (original PR)
Fixes: https://tracker.ceph.com/issues/75914 (Tentacle ticket)
Signed-off-by: Kobi Ginon <kginon@redhat.com>
(cherry picked from commit e68e302a7389c59ed92fbebc1684b9b5d252d854)

src/pybind/mgr/cephadm/services/cephadmservice.py
src/pybind/mgr/cephadm/services/mgmt_gateway.py
src/pybind/mgr/cephadm/tests/test_services.py

index 9e618d42866d73a520d486b098f85a7d3ba819ca..53ad6d9166aeca064fb40b49e4146c7827888805 100644 (file)
@@ -21,7 +21,7 @@ from ceph.deployment.service_spec import (
     RGWSpec,
     ServiceSpec,
 )
-from ceph.deployment.utils import is_ipv6, unwrap_ipv6
+from ceph.deployment.utils import is_ipv6, unwrap_ipv6, wrap_ipv6
 from mgr_util import build_url, merge_dicts
 from orchestrator import (
     OrchestratorError,
@@ -96,8 +96,10 @@ def get_dashboard_endpoints(svc: 'CephadmService') -> Tuple[List[str], Optional[
             if not port:
                 continue
             assert dd.hostname is not None
+            # fqdn may already be a name or numeric address; ensure IPv6
+            # literals are bracketed.
             addr = svc.mgr.get_fqdn(dd.hostname)
-            dashboard_endpoints.append(f'{addr}:{port}')
+            dashboard_endpoints.append(f'{wrap_ipv6(addr)}:{port}')
 
     return dashboard_endpoints, protocol
 
index 5a89c96187f861c0b02c60a78a3f7aee4aa46b98..bcabf60f30b54f1d3584f40bcfd258ccd4565055 100644 (file)
@@ -1,6 +1,8 @@
 import logging
 from typing import List, Any, Tuple, Dict, cast, Optional, TYPE_CHECKING
 
+from ceph.deployment.utils import wrap_ipv6
+
 from orchestrator import DaemonDescription
 from ceph.deployment.service_spec import MgmtGatewaySpec, GrafanaSpec, ServiceSpec
 from cephadm.services.cephadmservice import CephadmService, CephadmDaemonDeploySpec, get_dashboard_endpoints
@@ -26,12 +28,14 @@ class MgmtGatewayService(CephadmService):
         return daemon_spec
 
     def get_service_endpoints(self, service_name: str) -> List[str]:
+        # return host:port strings for every daemon of the given service
+        # wrap IPv6 addresses in square brackets so a port can be added later
         srv_entries = []
         for dd in self.mgr.cache.get_daemons_by_service(service_name):
             assert dd.hostname is not None
             addr = dd.ip if dd.ip else self.mgr.inventory.get_addr(dd.hostname)
             port = dd.ports[0] if dd.ports else None
-            srv_entries.append(f'{addr}:{port}')
+            srv_entries.append(f'{wrap_ipv6(addr)}:{port}')
         return srv_entries
 
     def get_active_daemon(self, daemon_descrs: List[DaemonDescription]) -> DaemonDescription:
@@ -83,11 +87,14 @@ class MgmtGatewayService(CephadmService):
         return self.mgr.cert_mgr.generate_cert(host_fqdn, ip)
 
     def get_service_discovery_endpoints(self) -> List[str]:
+        # the mgmt gateway uses this internally when generating its nginx
+        # configuration and the URL prefixes that we publish to the world.
+        # A literal IPv6 address needs to be wrapped in brackets.
         sd_endpoints = []
         for dd in self.mgr.cache.get_daemons_by_service('mgr'):
             assert dd.hostname is not None
             addr = dd.ip if dd.ip else self.mgr.inventory.get_addr(dd.hostname)
-            sd_endpoints.append(f"{addr}:{self.mgr.service_discovery_port}")
+            sd_endpoints.append(f"{wrap_ipv6(addr)}:{self.mgr.service_discovery_port}")
         return sd_endpoints
 
     @classmethod
index 7e3b1f2a383716853105f5470ef2098ba8949f90..f84445fe0ffda8effaecda8c28c1622be59cf93b 100644 (file)
@@ -4035,6 +4035,27 @@ class TestSMB:
 
 
 class TestMgmtGateway:
+    def test_ipv6_formatting_helpers(self, cephadm_module: CephadmOrchestrator):
+        # verify that endpoints generated by the mgmt-gateway helper methods
+        # correctly bracket IPv6 addresses before the port portion is added.
+        svc = service_registry.get_service('mgmt-gateway')
+
+        # service discovery endpoints use a fixed port from the orchestrator
+        port = cephadm_module.service_discovery_port
+        mgr_daemons = [
+            DaemonDescription(daemon_type='mgr', hostname='h1', ip='fe80::1', ports=[port]),
+            DaemonDescription(daemon_type='mgr', hostname='h2', ip='192.0.2.1', ports=[port]),
+        ]
+        cephadm_module.cache.get_daemons_by_service = lambda name: mgr_daemons if name == 'mgr' else []
+
+        sd = svc.get_service_discovery_endpoints()
+        assert sd == [f'[fe80::1]:{port}', f'192.0.2.1:{port}']
+
+        # generic service endpoints also need the same treatment
+        foo_daemons = [DaemonDescription(daemon_type='foo', hostname='f1', ip='fe80::2', ports=[8080])]
+        cephadm_module.cache.get_daemons_by_service = lambda name: foo_daemons if name == 'foo' else []
+        assert svc.get_service_endpoints('foo') == ['[fe80::2]:8080']
+
     @patch("cephadm.serve.CephadmServe._run_cephadm")
     @patch("cephadm.services.mgmt_gateway.MgmtGatewayService.get_service_endpoints")
     @patch("cephadm.services.mgmt_gateway.MgmtGatewayService.get_service_discovery_endpoints")