]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
cephadm: fix EndPoint to handle bracketed IPv6 addresses
authorRedouane Kachach <rkachach@ibm.com>
Wed, 25 Feb 2026 14:09:11 +0000 (15:09 +0100)
committerRedouane Kachach <rkachach@ibm.com>
Thu, 23 Apr 2026 13:34:38 +0000 (15:34 +0200)
EndPoint ctr fails to correctly handle IPv6 addresses passed with
surrounding brackets. In this case, ipaddress.ip_network() raises
a ValueError, which is silently swallowed by the except block, leaving
is_ipv4 = True. This causes __str__/__repr__ method to return a malformed
address like fd19:8:5::11:3300 instead of [fd19:8:5::11]:3300.

Normalize the self.ip field by stripping brackets on construction so
we always save and use the bare ip. repr/str methods take care of
appending the [] when needed.

Fixes: https://tracker.ceph.com/issues/75165 (original ticket)
Fixes: https://tracker.ceph.com/issues/75567 (backport ticket)
Signed-off-by: Redouane Kachach <rkachach@ibm.com>
(cherry picked from commit 25319e985ade01f566f00cf0fd6a550c6f1c9764)

src/cephadm/cephadmlib/net_utils.py
src/cephadm/tests/test_networks.py

index bfa61d933ef5501ea50f015a91139d0ae86769e6..7fe48c0b36ab374acc329804a2bdabfea04c2202 100644 (file)
@@ -22,11 +22,11 @@ class EndPoint:
     """EndPoint representing an ip:port format"""
 
     def __init__(self, ip: str, port: int) -> None:
-        self.ip = ip
+        self.ip = ip.strip('[]')  # normalize: always store bare IP
         self.port = port
         self.is_ipv4 = True
         try:
-            if ip and ipaddress.ip_network(ip).version == 6:
+            if self.ip and ipaddress.ip_network(self.ip).version == 6:
                 self.is_ipv4 = False
         except Exception:
             logger.exception('Failed to check ip address version')
index 83b908c14572c2e340f011301a18ae8190a63c3a..f97dcb6a2d563fea1c9384772f8bd885867ac319 100644 (file)
@@ -8,10 +8,45 @@ from tests.fixtures import with_cephadm_ctx, cephadm_fs, import_cephadm
 
 from cephadmlib.host_facts import _parse_ipv4_route, _parse_ipv6_route
 from cephadmlib.net_utils import get_ipv6_address
+from cephadmlib.net_utils import EndPoint
 
 _cephadm = import_cephadm()
 
 
+class TestEndPoint:
+    @pytest.mark.parametrize("ip, port, expected_str, expected_is_ipv4", [
+        # Normal IPv4
+        ('192.168.1.1', 3300, '192.168.1.1:3300', True),
+        # Normal IPv6 (bare)
+        ('fd19:8:5::11', 3300, '[fd19:8:5::11]:3300', False),
+        # Bracketed IPv6
+        ('[fd19:8:5::11]', 3300, '[fd19:8:5::11]:3300', False),
+        # Bracketed IPv6 should normalize stored ip to bare
+        ('[::1]', 6789, '[::1]:6789', False),
+    ])
+    def test_endpoint_init(self, ip, port, expected_str, expected_is_ipv4):
+        ep = EndPoint(ip, port)
+        assert str(ep) == expected_str
+        assert ep.is_ipv4 == expected_is_ipv4
+        # stored ip should always be bare (no brackets)
+        assert '[' not in ep.ip
+        assert ']' not in ep.ip
+
+    def test_endpoint_ipv4(self):
+        ep = EndPoint('10.0.0.1', 6789)
+        assert ep.is_ipv4 is True
+        assert repr(ep) == '10.0.0.1:6789'
+
+    def test_endpoint_ipv6(self):
+        ep = EndPoint('[fd19:8:5::11]', 6789)
+        assert ep.is_ipv4 is False
+        assert repr(ep) == '[fd19:8:5::11]:6789'
+
+    def test_endpoint_bracketed_ipv6_normalizes_stored_ip(self):
+        ep = EndPoint('[fd19:8:5::11]', 3300)
+        assert ep.ip == 'fd19:8:5::11'  # bare ipv6, no brackets
+
+
 class TestCommandListNetworks:
     @pytest.mark.parametrize("test_input, expected", [
         (