]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/cephadm: add filter_host_candidates method to smb service class
authorJohn Mulligan <jmulligan@redhat.com>
Mon, 16 Jun 2025 20:05:22 +0000 (16:05 -0400)
committerJohn Mulligan <jmulligan@redhat.com>
Wed, 23 Jul 2025 20:25:32 +0000 (16:25 -0400)
Add a filter_host_candidates method to the smb service class allowing
that class to act as a HostSelector. The HostSelector was added in an
earlier commit to allow classes like this one to make specific host
selections based on unique to that class (or it's spec) criteria.

This method uses the newly added `bind_addrs` field of the smb service
spec to ensure only hosts that meet the desired set of
networks/addresses get used in placement.

Signed-off-by: John Mulligan <jmulligan@redhat.com>
src/pybind/mgr/cephadm/services/smb.py

index ad63e7d94b88e65b529186ad5f516e753145cf55..f2d97c2aa5e6f443b9abe7ad098ad3d14bbefbef 100644 (file)
@@ -1,6 +1,7 @@
 import errno
+import ipaddress
 import logging
-from typing import Any, Dict, List, Tuple, cast, Optional
+from typing import Any, Dict, List, Tuple, cast, Optional, Iterable, Union
 
 from mgr_module import HandleCommandResult
 
@@ -14,6 +15,7 @@ from .cephadmservice import (
     CephadmDaemonDeploySpec,
     simplified_keyring,
 )
+from ..schedule import DaemonPlacement
 
 logger = logging.getLogger(__name__)
 
@@ -59,7 +61,51 @@ class SMBService(CephService):
                         if daemon_id is not None:
                             self.fence(smb_spec.cluster_id)
                         del rank_map[rank][gen]
-                        self.mgr.spec_store.save_rank_map(spec.service_name(), rank_map)
+                        self.mgr.spec_store.save_rank_map(
+                            spec.service_name(), rank_map
+                        )
+
+    def filter_host_candidates(
+        self,
+        spec: ServiceSpec,
+        candidates: Iterable[DaemonPlacement],
+    ) -> List[DaemonPlacement]:
+        logger.debug(
+            'SMBService.filter_host_candidates with candidates: %r',
+            candidates,
+        )
+        smb_spec = cast(SMBSpec, spec)
+        if not smb_spec.bind_addrs:
+            return list(candidates)
+        addr_src = AddressPool.from_spec(smb_spec)
+        filtered = [
+            dc
+            for dc in (self._candidate_in(c, addr_src) for c in candidates)
+            if dc is not None
+        ]
+        if len(filtered) != len(list(candidates)):
+            logger.debug('Filtered host candidates to: %r', filtered)
+        return filtered
+
+    def _candidate_in(
+        self, candidate: DaemonPlacement, addr_src: 'AddressPool'
+    ) -> Optional[DaemonPlacement]:
+        hostnw = self.mgr.cache.networks.get(candidate.hostname, {})
+        if not hostnw:
+            return None
+        ips = set()
+        for net_vals in hostnw.values():
+            for if_vals in net_vals.values():
+                ips.update(if_vals)
+        logger.debug(
+            'Checking if IPs %r from %s are in bindable addrs',
+            ips,
+            candidate.hostname,
+        )
+        for ip in ips:
+            if ip in addr_src:
+                return candidate._replace(ip=ip)
+        return None
 
     def prepare_create(
         self, daemon_spec: CephadmDaemonDeploySpec
@@ -291,3 +337,26 @@ class SMBService(CephService):
         logger.debug("smb daemon map: %r", smb_dmap)
         with clustermeta.rados_object(self.mgr, uri) as cmeta:
             cmeta.sync_ranks(rank_map, smb_dmap)
+
+
+Network = Union[ipaddress.IPv4Network, ipaddress.IPv6Network]
+
+
+class AddressPool:
+    def __init__(self, nets: Iterable[Network]) -> None:
+        self._nets = set(nets)
+
+    def __contains__(self, other: Union[str, ipaddress.IPv4Address]) -> bool:
+        if isinstance(other, str):
+            addr = ipaddress.ip_address(other)
+        else:
+            addr = other
+        return any((addr in net) for net in self._nets)
+
+    @classmethod
+    def from_spec(cls, spec: SMBSpec) -> 'AddressPool':
+        nets = set()
+        for baddr in spec.bind_addrs or []:
+            for net in baddr.as_networks():
+                nets.add(net)
+        return cls(nets)