1 from typing
import NamedTuple
, List
4 from ceph
.deployment
.service_spec
import ServiceSpec
, PlacementSpec
, ServiceSpecValidationError
6 from cephadm
.module
import HostAssignment
7 from orchestrator
import DaemonDescription
, OrchestratorValidationError
10 class NodeAssignmentTest(NamedTuple
):
12 placement
: PlacementSpec
14 daemons
: List
[DaemonDescription
]
17 @pytest.mark
.parametrize("service_type,placement,hosts,daemons,expected",
22 PlacementSpec(hosts
=['smithi060:[v2:172.21.15.60:3301,v1:172.21.15.60:6790]=c']),
30 PlacementSpec(host_pattern
='*'),
31 'host1 host2 host3'.split(),
33 DaemonDescription('mon', 'a', 'host1'),
34 DaemonDescription('mon', 'b', 'host2'),
36 ['host1', 'host2', 'host3']
38 # count that is bigger than the amount of hosts. Truncate to len(hosts)
39 # RGWs should not be co-located to each other.
42 PlacementSpec(count
=4),
43 'host1 host2 host3'.split(),
45 ['host1', 'host2', 'host3']
47 # count + partial host list
50 PlacementSpec(count
=3, hosts
=['host3']),
51 'host1 host2 host3'.split(),
53 DaemonDescription('mon', 'a', 'host1'),
54 DaemonDescription('mon', 'b', 'host2'),
56 ['host1', 'host2', 'host3']
58 # count 1 + partial host list
61 PlacementSpec(count
=1, hosts
=['host3']),
62 'host1 host2 host3'.split(),
64 DaemonDescription('mon', 'a', 'host1'),
65 DaemonDescription('mon', 'b', 'host2'),
69 # count + partial host list + existing
72 PlacementSpec(count
=2, hosts
=['host3']),
73 'host1 host2 host3'.split(),
75 DaemonDescription('mon', 'a', 'host1'),
79 # count + partial host list + existing (deterministic)
82 PlacementSpec(count
=2, hosts
=['host1']),
83 'host1 host2'.split(),
85 DaemonDescription('mon', 'a', 'host1'),
89 # count + partial host list + existing (deterministic)
92 PlacementSpec(count
=2, hosts
=['host1']),
93 'host1 host2'.split(),
95 DaemonDescription('mon', 'a', 'host2'),
102 PlacementSpec(label
='foo'),
103 'host1 host2 host3'.split(),
105 ['host1', 'host2', 'host3']
110 PlacementSpec(host_pattern
='mon*'),
111 'monhost1 monhost2 datahost'.split(),
113 ['monhost1', 'monhost2']
116 def test_node_assignment(service_type
, placement
, hosts
, daemons
, expected
):
117 hosts
= HostAssignment(
118 spec
=ServiceSpec(service_type
, placement
=placement
),
119 get_hosts_func
=lambda label
=None, as_hostspec
=False: hosts
,
120 get_daemons_func
=lambda _
: daemons
).place()
121 assert sorted([h
.hostname
for h
in hosts
]) == sorted(expected
)
124 class NodeAssignmentTest2(NamedTuple
):
126 placement
: PlacementSpec
128 daemons
: List
[DaemonDescription
]
132 @pytest.mark
.parametrize("service_type,placement,hosts,daemons,expected_len,in_set",
138 'host1 host2 host3'.split(),
141 ['host1', 'host2', 'host3'],
147 PlacementSpec(count
=1),
148 'host1 host2 host3'.split(),
151 ['host1', 'host2', 'host3'],
154 # hosts + (smaller) count
157 PlacementSpec(count
=1, hosts
='host1 host2'.split()),
158 'host1 host2'.split(),
163 # hosts + (smaller) count, existing
166 PlacementSpec(count
=1, hosts
='host1 host2 host3'.split()),
167 'host1 host2 host3'.split(),
168 [DaemonDescription('mon', 'mon.a', 'host1'),],
170 ['host1', 'host2', 'host3'],
172 # hosts + (smaller) count, (more) existing
175 PlacementSpec(count
=1, hosts
='host1 host2 host3'.split()),
176 'host1 host2 host3'.split(),
178 DaemonDescription('mon', 'a', 'host1'),
179 DaemonDescription('mon', 'b', 'host2'),
184 # count + partial host list
187 PlacementSpec(count
=2, hosts
=['host3']),
188 'host1 host2 host3'.split(),
191 ['host1', 'host2', 'host3']
196 PlacementSpec(count
=1, label
='foo'),
197 'host1 host2 host3'.split(),
200 ['host1', 'host2', 'host3']
203 def test_node_assignment2(service_type
, placement
, hosts
,
204 daemons
, expected_len
, in_set
):
205 hosts
= HostAssignment(
206 spec
=ServiceSpec(service_type
, placement
=placement
),
207 get_hosts_func
=lambda label
=None, as_hostspec
=False: hosts
,
208 get_daemons_func
=lambda _
: daemons
).place()
209 assert len(hosts
) == expected_len
210 for h
in [h
.hostname
for h
in hosts
]:
213 @pytest.mark
.parametrize("service_type,placement,hosts,daemons,expected_len,must_have",
215 # hosts + (smaller) count, (more) existing
218 PlacementSpec(count
=3, hosts
='host3'.split()),
219 'host1 host2 host3'.split(),
224 # count + partial host list
227 PlacementSpec(count
=2, hosts
=['host3']),
228 'host1 host2 host3'.split(),
234 def test_node_assignment3(service_type
, placement
, hosts
,
235 daemons
, expected_len
, must_have
):
236 hosts
= HostAssignment(
237 spec
=ServiceSpec(service_type
, placement
=placement
),
238 get_hosts_func
=lambda label
=None, as_hostspec
=False: hosts
,
239 get_daemons_func
=lambda _
: daemons
).place()
240 assert len(hosts
) == expected_len
242 assert h
in [h
.hostname
for h
in hosts
]
245 @pytest.mark
.parametrize("placement",
250 ('hostname12hostname12hostname12hostname12hostname12hostname12hostname12'), # > 63 chars
252 def test_bad_placements(placement
):
254 s
= PlacementSpec
.from_string(placement
.split(' '))
256 except ServiceSpecValidationError
as e
:
260 class NodeAssignmentTestBadSpec(NamedTuple
):
262 placement
: PlacementSpec
264 daemons
: List
[DaemonDescription
]
266 @pytest.mark
.parametrize("service_type,placement,hosts,daemons,expected",
269 NodeAssignmentTestBadSpec(
271 PlacementSpec(hosts
=['unknownhost']),
274 "Cannot place <ServiceSpec for service_name=mon> on {'unknownhost'}: Unknown hosts"
276 # unknown host pattern
277 NodeAssignmentTestBadSpec(
279 PlacementSpec(host_pattern
='unknownhost'),
282 "Cannot place <ServiceSpec for service_name=mon>: No matching hosts"
285 NodeAssignmentTestBadSpec(
287 PlacementSpec(label
='unknownlabel'),
290 "Cannot place <ServiceSpec for service_name=mon>: No matching hosts for label unknownlabel"
293 def test_bad_specs(service_type
, placement
, hosts
, daemons
, expected
):
294 with pytest
.raises(OrchestratorValidationError
) as e
:
295 hosts
= HostAssignment(
296 spec
=ServiceSpec(service_type
, placement
=placement
),
297 get_hosts_func
=lambda label
=None, as_hostspec
=False: hosts
,
298 get_daemons_func
=lambda _
: daemons
).place()
299 assert str(e
.value
) == expected