From 1660aca7d9ba16b46dc44b299480e71af43cb16f Mon Sep 17 00:00:00 2001 From: John Mulligan Date: Mon, 16 Jun 2025 16:05:22 -0400 Subject: [PATCH] mgr/cephadm: add filter_host_candidates method to smb service class 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 (cherry picked from commit 1afb07b0c391a0c1162f6f742ae7814a5be1789e) --- src/pybind/mgr/cephadm/services/smb.py | 73 +++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/src/pybind/mgr/cephadm/services/smb.py b/src/pybind/mgr/cephadm/services/smb.py index ad63e7d94b8..f2d97c2aa5e 100644 --- a/src/pybind/mgr/cephadm/services/smb.py +++ b/src/pybind/mgr/cephadm/services/smb.py @@ -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) -- 2.39.5