]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/cephadm: prepare schedule.py for per-service-type host filtering
authorJohn Mulligan <jmulligan@redhat.com>
Mon, 16 Jun 2025 20:05:01 +0000 (16:05 -0400)
committerAdam King <adking@redhat.com>
Fri, 25 Jul 2025 17:55:22 +0000 (13:55 -0400)
Prepare schedule.py for per-service-type host filtering based on allowed
host addresses/networks. Add a new HostSelector protocol type to the
module defining what the filtering interface looks like.

This interface is intended allows CephService classes to "take over" the
network based filtering of nodes prior to placement and customize the
behavior of this step in cephamd's placement algorithm.

Note that the type must be passed in to the HostAssignment class as an
optional argument. If nothing is passed the class behaves as it did
before.

Signed-off-by: John Mulligan <jmulligan@redhat.com>
(cherry picked from commit e3a3334f2ccf46c436ee61db14378629b1e72df8)

Conflicts:
src/pybind/mgr/cephadm/schedule.py

src/pybind/mgr/cephadm/schedule.py

index e71c0345f75604a56fa2d7b63d2051b5e4068b44..448fc20bdad26100a1ea9ea1d7d13e154e5920d5 100644 (file)
@@ -2,7 +2,17 @@ import ipaddress
 import hashlib
 import logging
 import random
-from typing import List, Optional, Callable, TypeVar, Tuple, NamedTuple, Dict
+from typing import (
+    Callable,
+    Dict,
+    Iterable,
+    List,
+    NamedTuple,
+    Optional,
+    Protocol,
+    Tuple,
+    TypeVar,
+)
 
 import orchestrator
 from ceph.deployment.service_spec import ServiceSpec
@@ -143,6 +153,15 @@ class DaemonPlacement(NamedTuple):
         return rank_map[dd.rank][dd.rank_generation] == dd.daemon_id
 
 
+class HostSelector(Protocol):
+    def filter_host_candidates(
+        self,
+        spec: ServiceSpec,
+        candidates: Iterable[DaemonPlacement],
+    ) -> List[DaemonPlacement]:
+        "Filter candidates for host/ip matching."
+
+
 class HostAssignment(object):
 
     def __init__(self,
@@ -159,7 +178,8 @@ class HostAssignment(object):
                  per_host_daemon_type: Optional[str] = None,
                  rank_map: Optional[Dict[int, Dict[int, Optional[str]]]] = None,
                  blocking_daemon_hosts: Optional[List[orchestrator.HostSpec]] = None,
-                 upgrade_in_progress: bool = False
+                 upgrade_in_progress: bool = False,
+                 host_selector: Optional[HostSelector] = None,
                  ):
         assert spec
         self.spec = spec  # type: ServiceSpec
@@ -178,6 +198,7 @@ class HostAssignment(object):
         self.ports_start = spec.get_port_start()
         self.rank_map = rank_map
         self.upgrade_in_progress = upgrade_in_progress
+        self.host_selector = host_selector
 
     def hosts_by_label(self, label: str) -> List[orchestrator.HostSpec]:
         return [h for h in self.hosts if label in h.labels]
@@ -467,7 +488,9 @@ class HostAssignment(object):
                 "placement spec is empty: no hosts, no label, no pattern, no count")
 
         # allocate an IP?
-        if self.spec.networks:
+        if self.host_selector:
+            ls = self.host_selector.filter_host_candidates(self.spec, ls)
+        elif self.spec.networks:
             orig = ls.copy()
             ls = []
             for p in orig: