+# Disable autopep8 for this file:
+
+# fmt: off
+
from typing import NamedTuple, List
import pytest
return [v for k, v in results
if match(k)][0]
-def mk_spec_and_host(spec_section, hosts, explicit_key, explicit, count):
+def mk_spec_and_host(spec_section, hosts, explicit_key, explicit, count):
if spec_section == 'hosts':
- mk_spec = lambda: ServiceSpec('mon', placement=PlacementSpec(
+ mk_spec = lambda: ServiceSpec('mgr', placement=PlacementSpec(
hosts=explicit,
count=count,
))
- mk_hosts = lambda _: hosts
elif spec_section == 'label':
- mk_spec = lambda: ServiceSpec('mon', placement=PlacementSpec(
+ mk_spec = lambda: ServiceSpec('mgr', 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',
'12': '[1-2]',
'123': '*',
}[explicit_key]
- mk_spec = lambda: ServiceSpec('mon', placement=PlacementSpec(
+ mk_spec = lambda: ServiceSpec('mgr', placement=PlacementSpec(
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
+ hosts = [
+ HostSpec(h, labels=['mylabel']) if h in explicit else HostSpec(h)
+ for h in hosts
+ ]
+
+ return mk_spec, hosts
-def run_scheduler_test(results, mk_spec, get_hosts_func, get_daemons_func, key_elems):
+def run_scheduler_test(results, mk_spec, hosts, get_daemons_func, key_elems):
key = ' '.join('N' if e is None else str(e) for e in key_elems)
try:
assert_res = get_result(k(key), results)
spec = mk_spec()
host_res = HostAssignment(
spec=spec,
- get_hosts_func=get_hosts_func,
+ hosts=hosts,
get_daemons_func=get_daemons_func).place()
if isinstance(host_res, list):
e = ', '.join(repr(h.hostname) for h in host_res)
spec = mk_spec()
host_res = HostAssignment(
spec=spec,
- get_hosts_func=get_hosts_func,
+ hosts=hosts,
get_daemons_func=get_daemons_func).place()
assert_res(sorted([h.hostname for h in host_res]))
# | | | | |
test_explicit_scheduler_results = [
(k("* * 0 *"), error(ServiceSpecValidationError, 'num/count must be > 1')),
- (k("* e N l"), error(OrchestratorValidationError, 'Cannot place <ServiceSpec for service_name=mon>: No matching hosts for label mylabel')),
- (k("* e N p"), error(OrchestratorValidationError, 'Cannot place <ServiceSpec for service_name=mon>: No matching hosts')),
+ (k("* e N l"), error(OrchestratorValidationError, 'Cannot place <ServiceSpec for service_name=mgr>: No matching hosts for label mylabel')),
+ (k("* e N p"), error(OrchestratorValidationError, 'Cannot place <ServiceSpec for service_name=mgr>: No matching hosts')),
(k("* e N h"), error(OrchestratorValidationError, 'placement spec is empty: no hosts, no label, no pattern, no count')),
(k("* e * *"), none),
- (k("1 12 * h"), error(OrchestratorValidationError, "Cannot place <ServiceSpec for service_name=mon> on 2: Unknown hosts")),
- (k("1 123 * h"), error(OrchestratorValidationError, "Cannot place <ServiceSpec for service_name=mon> on 2, 3: Unknown hosts")),
+ (k("1 12 * h"), error(OrchestratorValidationError, "Cannot place <ServiceSpec for service_name=mgr> on 2: Unknown hosts")),
+ (k("1 123 * h"), error(OrchestratorValidationError, "Cannot place <ServiceSpec for service_name=mgr> on 2, 3: Unknown hosts")),
(k("1 * * *"), exactly('1')),
(k("12 1 * *"), exactly('1')),
(k("12 12 1 *"), one_of('1', '2')),
(k("12 12 * *"), exactly('1', '2')),
- (k("12 123 * h"), error(OrchestratorValidationError, "Cannot place <ServiceSpec for service_name=mon> on 3: Unknown hosts")),
+ (k("12 123 * h"), error(OrchestratorValidationError, "Cannot place <ServiceSpec for service_name=mgr> on 3: Unknown hosts")),
(k("12 123 1 *"), one_of('1', '2', '3')),
(k("12 123 * *"), two_of('1', '2', '3')),
(k("123 1 * *"), exactly('1')),
count,
spec_section_key, spec_section):
- mk_spec, mk_hosts = mk_spec_and_host(spec_section, hosts, explicit_key, explicit, count)
+ mk_spec, hosts = mk_spec_and_host(spec_section, hosts, explicit_key, explicit, count)
run_scheduler_test(
results=test_explicit_scheduler_results,
mk_spec=mk_spec,
- get_hosts_func=mk_hosts,
+ hosts=hosts,
get_daemons_func=lambda _: [],
key_elems=(host_key, explicit_key, count, spec_section_key)
)
# | | | | | |
test_scheduler_daemons_results = [
(k("* 1 * * *"), exactly('1')),
- (k("1 123 * * h"), error(OrchestratorValidationError, 'Cannot place <ServiceSpec for service_name=mon> on 2, 3: Unknown hosts')),
+ (k("1 123 * * h"), error(OrchestratorValidationError, 'Cannot place <ServiceSpec for service_name=mgr> on 2, 3: Unknown hosts')),
(k("1 123 * * *"), exactly('1')),
- (k("12 123 * * h"), error(OrchestratorValidationError, 'Cannot place <ServiceSpec for service_name=mon> on 3: Unknown hosts')),
+ (k("12 123 * * h"), error(OrchestratorValidationError, 'Cannot place <ServiceSpec for service_name=mgr> on 3: Unknown hosts')),
(k("12 123 N * *"), exactly('1', '2')),
(k("12 123 1 * *"), one_of('1', '2')),
(k("12 123 2 * *"), exactly('1', '2')),
count,
daemons_key, daemons,
spec_section_key, spec_section):
- mk_spec, mk_hosts = mk_spec_and_host(spec_section, hosts, explicit_key, explicit, count)
+ mk_spec, hosts = mk_spec_and_host(spec_section, hosts, explicit_key, explicit, count)
dds = [
- DaemonDescription('mon', d, d)
+ DaemonDescription('mgr', d, d)
for d in daemons
]
run_scheduler_test(
results=test_scheduler_daemons_results,
mk_spec=mk_spec,
- get_hosts_func=mk_hosts,
+ hosts=hosts,
get_daemons_func=lambda _: dds,
key_elems=(host_key, explicit_key, count, daemons_key, spec_section_key)
)
-## =========================
+# =========================
class NodeAssignmentTest(NamedTuple):
[
# just hosts
NodeAssignmentTest(
- 'mon',
+ 'mgr',
PlacementSpec(hosts=['smithi060:[v2:172.21.15.60:3301,v1:172.21.15.60:6790]=c']),
['smithi060'],
[],
),
# all_hosts
NodeAssignmentTest(
- 'mon',
+ 'mgr',
PlacementSpec(host_pattern='*'),
'host1 host2 host3'.split(),
[
- DaemonDescription('mon', 'a', 'host1'),
- DaemonDescription('mon', 'b', 'host2'),
+ DaemonDescription('mgr', 'a', 'host1'),
+ DaemonDescription('mgr', 'b', 'host2'),
],
['host1', 'host2', 'host3']
),
),
# count + partial host list
NodeAssignmentTest(
- 'mon',
+ 'mgr',
PlacementSpec(count=3, hosts=['host3']),
'host1 host2 host3'.split(),
[
- DaemonDescription('mon', 'a', 'host1'),
- DaemonDescription('mon', 'b', 'host2'),
+ DaemonDescription('mgr', 'a', 'host1'),
+ DaemonDescription('mgr', 'b', 'host2'),
],
['host3']
),
# count 1 + partial host list
NodeAssignmentTest(
- 'mon',
+ 'mgr',
PlacementSpec(count=1, hosts=['host3']),
'host1 host2 host3'.split(),
[
- DaemonDescription('mon', 'a', 'host1'),
- DaemonDescription('mon', 'b', 'host2'),
+ DaemonDescription('mgr', 'a', 'host1'),
+ DaemonDescription('mgr', 'b', 'host2'),
],
['host3']
),
# count + partial host list + existing
NodeAssignmentTest(
- 'mon',
+ 'mgr',
PlacementSpec(count=2, hosts=['host3']),
'host1 host2 host3'.split(),
[
- DaemonDescription('mon', 'a', 'host1'),
+ DaemonDescription('mgr', 'a', 'host1'),
],
['host3']
),
# count + partial host list + existing (deterministic)
NodeAssignmentTest(
- 'mon',
+ 'mgr',
PlacementSpec(count=2, hosts=['host1']),
'host1 host2'.split(),
[
- DaemonDescription('mon', 'a', 'host1'),
+ DaemonDescription('mgr', 'a', 'host1'),
],
['host1']
),
# count + partial host list + existing (deterministic)
NodeAssignmentTest(
- 'mon',
+ 'mgr',
PlacementSpec(count=2, hosts=['host1']),
'host1 host2'.split(),
[
- DaemonDescription('mon', 'a', 'host2'),
+ DaemonDescription('mgr', 'a', 'host2'),
],
['host1']
),
# label only
NodeAssignmentTest(
- 'mon',
+ 'mgr',
PlacementSpec(label='foo'),
'host1 host2 host3'.split(),
[],
),
# host_pattern
NodeAssignmentTest(
- 'mon',
- PlacementSpec(host_pattern='mon*'),
- 'monhost1 monhost2 datahost'.split(),
+ 'mgr',
+ PlacementSpec(host_pattern='mgr*'),
+ 'mgrhost1 mgrhost2 datahost'.split(),
[],
- ['monhost1', 'monhost2']
+ ['mgrhost1', 'mgrhost2']
),
])
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 hosts
-
service_id = None
if service_type == 'rgw':
service_id = 'realm.zone'
hosts = HostAssignment(
spec=spec,
- get_hosts_func=get_hosts_func,
+ hosts=[HostSpec(h, labels=['foo']) for h in hosts],
get_daemons_func=lambda _: daemons).place()
assert sorted([h.hostname for h in hosts]) == sorted(expected)
[
# just count
NodeAssignmentTest2(
- 'mon',
+ 'mgr',
PlacementSpec(count=1),
'host1 host2 host3'.split(),
[],
# hosts + (smaller) count
NodeAssignmentTest2(
- 'mon',
+ 'mgr',
PlacementSpec(count=1, hosts='host1 host2'.split()),
'host1 host2'.split(),
[],
),
# hosts + (smaller) count, existing
NodeAssignmentTest2(
- 'mon',
+ 'mgr',
PlacementSpec(count=1, hosts='host1 host2 host3'.split()),
'host1 host2 host3'.split(),
- [DaemonDescription('mon', 'mon.a', 'host1'),],
+ [DaemonDescription('mgr', 'mgr.a', 'host1'),],
1,
['host1', 'host2', 'host3'],
),
# hosts + (smaller) count, (more) existing
NodeAssignmentTest2(
- 'mon',
+ 'mgr',
PlacementSpec(count=1, hosts='host1 host2 host3'.split()),
'host1 host2 host3'.split(),
[
- DaemonDescription('mon', 'a', 'host1'),
- DaemonDescription('mon', 'b', 'host2'),
+ DaemonDescription('mgr', 'a', 'host1'),
+ DaemonDescription('mgr', 'b', 'host2'),
],
1,
['host1', 'host2']
),
# count + partial host list
NodeAssignmentTest2(
- 'mon',
+ 'mgr',
PlacementSpec(count=2, hosts=['host3']),
'host1 host2 host3'.split(),
[],
),
# label + count
NodeAssignmentTest2(
- 'mon',
+ 'mgr',
PlacementSpec(count=1, label='foo'),
'host1 host2 host3'.split(),
[],
])
def test_node_assignment2(service_type, placement, hosts,
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 hosts
-
hosts = HostAssignment(
spec=ServiceSpec(service_type, placement=placement),
- get_hosts_func=get_hosts_func,
+ hosts=[HostSpec(h, labels=['foo']) for h in hosts],
get_daemons_func=lambda _: daemons).place()
assert len(hosts) == expected_len
for h in [h.hostname for h in hosts]:
[
# hosts + (smaller) count, (more) existing
NodeAssignmentTest2(
- 'mon',
+ 'mgr',
PlacementSpec(count=3, hosts='host3'.split()),
'host1 host2 host3'.split(),
[],
),
# count + partial host list
NodeAssignmentTest2(
- 'mon',
+ 'mgr',
PlacementSpec(count=2, hosts=['host3']),
'host1 host2 host3'.split(),
[],
])
def test_node_assignment3(service_type, placement, hosts,
daemons, expected_len, must_have):
- def get_hosts_func(label=None, as_hostspec=False):
- if as_hostspec:
- return [HostSpec(h) for h in hosts]
- return hosts
-
hosts = HostAssignment(
spec=ServiceSpec(service_type, placement=placement),
- get_hosts_func=get_hosts_func,
+ hosts=[HostSpec(h) for h in hosts],
get_daemons_func=lambda _: daemons).place()
assert len(hosts) == expected_len
for h in must_have:
[
# unknown host
NodeAssignmentTestBadSpec(
- 'mon',
+ 'mgr',
PlacementSpec(hosts=['unknownhost']),
['knownhost'],
[],
- "Cannot place <ServiceSpec for service_name=mon> on unknownhost: Unknown hosts"
+ "Cannot place <ServiceSpec for service_name=mgr> on unknownhost: Unknown hosts"
),
# unknown host pattern
NodeAssignmentTestBadSpec(
- 'mon',
+ 'mgr',
PlacementSpec(host_pattern='unknownhost'),
['knownhost'],
[],
- "Cannot place <ServiceSpec for service_name=mon>: No matching hosts"
+ "Cannot place <ServiceSpec for service_name=mgr>: No matching hosts"
),
# unknown label
NodeAssignmentTestBadSpec(
- 'mon',
+ 'mgr',
PlacementSpec(label='unknownlabel'),
[],
[],
- "Cannot place <ServiceSpec for service_name=mon>: No matching hosts for label unknownlabel"
+ "Cannot place <ServiceSpec for service_name=mgr>: No matching hosts for label unknownlabel"
),
])
def test_bad_specs(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 hosts
with pytest.raises(OrchestratorValidationError) as e:
hosts = HostAssignment(
spec=ServiceSpec(service_type, placement=placement),
- get_hosts_func=get_hosts_func,
+ hosts=[HostSpec(h) for h in hosts],
get_daemons_func=lambda _: daemons).place()
assert str(e.value) == expected
])
def test_active_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 hosts
spec = ServiceSpec(service_type=service_type,
service_id=None,
hosts = HostAssignment(
spec=spec,
- get_hosts_func=get_hosts_func,
+ hosts=[HostSpec(h) for h in hosts],
get_daemons_func=lambda _: daemons).place()
assert sorted([h.hostname for h in hosts]) in expected
+
+class OddMonsTest(NamedTuple):
+ service_type: str
+ placement: PlacementSpec
+ hosts: List[str]
+ daemons: List[DaemonDescription]
+ expected_count: int
+
+
+@pytest.mark.parametrize("service_type,placement,hosts,daemons,expected_count",
+ [
+ OddMonsTest(
+ 'mon',
+ PlacementSpec(count=5),
+ 'host1 host2 host3 host4'.split(),
+ [],
+ 3
+ ),
+ OddMonsTest(
+ 'mon',
+ PlacementSpec(count=4),
+ 'host1 host2 host3 host4 host5'.split(),
+ [],
+ 3
+ ),
+ OddMonsTest(
+ 'mon',
+ PlacementSpec(count=5),
+ 'host1 host2 host3 host4 host5'.split(),
+ [],
+ 5
+ ),
+ OddMonsTest(
+ 'mon',
+ PlacementSpec(hosts='host1 host2 host3 host4'.split()),
+ 'host1 host2 host3 host4 host5'.split(),
+ [],
+ 3
+ ),
+ OddMonsTest(
+ 'mon',
+ PlacementSpec(hosts='host1 host2 host3 host4 host5'.split()),
+ 'host1 host2 host3 host4 host5'.split(),
+ [],
+ 5
+ ),
+ OddMonsTest(
+ 'mon',
+ PlacementSpec(host_pattern='*'),
+ 'host1 host2 host3 host4'.split(),
+ [],
+ 3
+ ),
+ OddMonsTest(
+ 'mon',
+ PlacementSpec(count=5, hosts='host1 host2 host3 host4'.split()),
+ 'host1 host2 host3 host4 host5'.split(),
+ [],
+ 3
+ ),
+ OddMonsTest(
+ 'mon',
+ PlacementSpec(count=2, hosts='host1 host2 host3'.split()),
+ 'host1 host2 host3 host4 host5'.split(),
+ [],
+ 1
+ ),
+ OddMonsTest(
+ 'mon',
+ PlacementSpec(count=5),
+ 'host1 host2 host3 host4'.split(),
+ [
+ DaemonDescription('mon', 'a', 'host1'),
+ DaemonDescription('mon', 'b', 'host2'),
+ DaemonDescription('mon', 'c', 'host3'),
+ ],
+ 3
+ ),
+ OddMonsTest(
+ 'mon',
+ PlacementSpec(count=5),
+ 'host1 host2 host3 host4'.split(),
+ [
+ DaemonDescription('mon', 'a', 'host1'),
+ DaemonDescription('mon', 'b', 'host2'),
+ ],
+ 3
+ ),
+ OddMonsTest(
+ 'mon',
+ PlacementSpec(hosts='host1 host2 host3 host4'.split()),
+ 'host1 host2 host3 host4 host5'.split(),
+ [
+ DaemonDescription('mon', 'a', 'host1'),
+ DaemonDescription('mon', 'b', 'host2'),
+ DaemonDescription('mon', 'c', 'host3'),
+ ],
+ 3
+ ),
+
+ ])
+def test_odd_mons(service_type, placement, hosts, daemons, expected_count):
+
+ spec = ServiceSpec(service_type=service_type,
+ service_id=None,
+ placement=placement)
+
+ hosts = HostAssignment(
+ spec=spec,
+ hosts=[HostSpec(h) for h in hosts],
+ get_daemons_func=lambda _: daemons).place()
+ assert len(hosts) == expected_count