import logging
import random
-from typing import List, Optional, Callable, Iterable, Tuple, TypeVar, Set
+from typing import List, Optional, Callable, Iterable, TypeVar, Set
import orchestrator
from ceph.deployment.service_spec import PlacementSpec, HostPlacementSpec, ServiceSpec
assert spec and get_hosts_func and get_daemons_func
self.spec = spec # type: ServiceSpec
self.scheduler = scheduler if scheduler else SimpleScheduler(self.spec)
- self.get_hosts_func = get_hosts_func
+ self.hosts: List[orchestrator.HostSpec] = get_hosts_func(as_hostspec=True)
self.filter_new_host = filter_new_host
self.service_name = spec.service_name()
self.daemons = get_daemons_func(self.service_name)
+ def hosts_by_label(self, label: str) -> List[orchestrator.HostSpec]:
+ return [h for h in self.hosts if label in h.labels]
+
+ def get_hostnames(self) -> List[str]:
+ return [h.hostname for h in self.hosts]
+
def validate(self):
self.spec.validate()
if self.spec.placement.hosts:
explicit_hostnames = {h.hostname for h in self.spec.placement.hosts}
- unknown_hosts = explicit_hostnames.difference(set(self.get_hosts_func()))
+ unknown_hosts = explicit_hostnames.difference(set(self.get_hostnames()))
if unknown_hosts:
raise OrchestratorValidationError(
f'Cannot place {self.spec.one_line_str()} on {", ".join(sorted(unknown_hosts))}: Unknown hosts')
if self.spec.placement.host_pattern:
- pattern_hostnames = self.spec.placement.filter_matching_hosts(self.get_hosts_func)
+ pattern_hostnames = self.spec.placement.filter_matching_hostspecs(self.hosts)
if not pattern_hostnames:
raise OrchestratorValidationError(
f'Cannot place {self.spec.one_line_str()}: No matching hosts')
if self.spec.placement.label:
- label_hostnames = self.get_hosts_func(label=self.spec.placement.label)
- if not label_hostnames:
+ label_hosts = self.hosts_by_label(self.spec.placement.label)
+ if not label_hosts:
raise OrchestratorValidationError(
f'Cannot place {self.spec.one_line_str()}: No matching '
f'hosts for label {self.spec.placement.label}')
return self.spec.placement.hosts
elif self.spec.placement.label:
return [
- HostPlacementSpec(x, '', '')
- for x in self.get_hosts_func(label=self.spec.placement.label)
+ HostPlacementSpec(x.hostname, '', '')
+ for x in self.hosts_by_label(self.spec.placement.label)
]
elif self.spec.placement.host_pattern:
return [
HostPlacementSpec(x, '', '')
- for x in self.spec.placement.filter_matching_hosts(self.get_hosts_func)
+ for x in self.spec.placement.filter_matching_hostspecs(self.hosts)
]
# If none of the above and also no <count>
if self.spec.placement.count is None:
"placement spec is empty: no hosts, no label, no pattern, no count")
# backward compatibility: consider an empty placements to be the same pattern = *
return [
- HostPlacementSpec(x, '', '')
- for x in self.get_hosts_func()
+ HostPlacementSpec(x.hostname, '', '')
+ for x in self.hosts
]
def hosts_with_daemons(self, candidates: List[HostPlacementSpec]) -> List[HostPlacementSpec]:
hosts=explicit,
count=count,
))
- mk_hosts = lambda _: hosts
elif spec_section == 'label':
mk_spec = lambda: ServiceSpec('mon', placement=PlacementSpec(
label='mylabel',
count=count,
))
- mk_hosts = lambda l: [e for e in explicit if e in hosts] if l == 'mylabel' else hosts
elif spec_section == 'host_pattern':
pattern = {
'e': 'notfound',
host_pattern=pattern,
count=count,
))
- mk_hosts = lambda _: hosts
else:
assert False
- def _get_hosts_wrapper(label=None, as_hostspec=False):
- hosts = mk_hosts(label)
- if as_hostspec:
- return list(map(HostSpec, hosts))
- return hosts
- return mk_spec, _get_hosts_wrapper
+ def mk_hosts(*_, **__):
+ return [
+ HostSpec(h, labels=['mylabel']) if h in explicit else HostSpec(h)
+ for h in hosts
+ ]
+
+ return mk_spec, mk_hosts
def run_scheduler_test(results, mk_spec, get_hosts_func, get_daemons_func, key_elems):
def test_node_assignment(service_type, placement, hosts, daemons, expected):
def get_hosts_func(label=None, as_hostspec=False):
if as_hostspec:
- return [HostSpec(h) for h in hosts]
+ return [HostSpec(h, labels=['foo']) for h in hosts]
return hosts
service_id = None
daemons, expected_len, in_set):
def get_hosts_func(label=None, as_hostspec=False):
if as_hostspec:
- return [HostSpec(h) for h in hosts]
+ return [HostSpec(h, labels=['foo']) for h in hosts]
return hosts
hosts = HostAssignment(
import re
from collections import namedtuple, OrderedDict
from functools import wraps
-from typing import Optional, Dict, Any, List, Union, Callable, Iterator
+from typing import Optional, Dict, Any, List, Union, Callable, Iterable
import yaml
def filter_matching_hosts(self, _get_hosts_func: Callable) -> List[str]:
return self.filter_matching_hostspecs(_get_hosts_func(as_hostspec=True))
- def filter_matching_hostspecs(self, hostspecs: Iterator[HostSpec]) -> List[str]:
+ def filter_matching_hostspecs(self, hostspecs: Iterable[HostSpec]) -> List[str]:
if self.hosts:
all_hosts = [hs.hostname for hs in hostspecs]
return [h.hostname for h in self.hosts if h.hostname in all_hosts]
# get_host_selection_size
return []
- def get_host_selection_size(self, hostspecs: Iterator[HostSpec]):
+ def get_host_selection_size(self, hostspecs: Iterable[HostSpec]):
if self.count:
return self.count
return len(self.filter_matching_hostspecs(hostspecs))