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 _
: hosts
,
120 get_daemons_func
=lambda _
: daemons
).place()
121 assert sorted([h
.hostname
for h
in hosts
]) == sorted(expected
)
123 class NodeAssignmentTest2(NamedTuple
):
125 placement
: PlacementSpec
127 daemons
: List
[DaemonDescription
]
131 @pytest.mark
.parametrize("service_type,placement,hosts,daemons,expected_len,in_set",
137 'host1 host2 host3'.split(),
140 ['host1', 'host2', 'host3'],
146 PlacementSpec(count
=1),
147 'host1 host2 host3'.split(),
150 ['host1', 'host2', 'host3'],
153 # hosts + (smaller) count
156 PlacementSpec(count
=1, hosts
='host1 host2'.split()),
157 'host1 host2'.split(),
162 # hosts + (smaller) count, existing
165 PlacementSpec(count
=1, hosts
='host1 host2 host3'.split()),
166 'host1 host2 host3'.split(),
167 [DaemonDescription('mon', 'mon.a', 'host1'),],
169 ['host1', 'host2', 'host3'],
171 # hosts + (smaller) count, (more) existing
174 PlacementSpec(count
=1, hosts
='host1 host2 host3'.split()),
175 'host1 host2 host3'.split(),
177 DaemonDescription('mon', 'a', 'host1'),
178 DaemonDescription('mon', 'b', 'host2'),
183 # count + partial host list
186 PlacementSpec(count
=2, hosts
=['host3']),
187 'host1 host2 host3'.split(),
190 ['host1', 'host2', 'host3']
195 PlacementSpec(count
=1, label
='foo'),
196 'host1 host2 host3'.split(),
199 ['host1', 'host2', 'host3']
202 def test_node_assignment2(service_type
, placement
, hosts
,
203 daemons
, expected_len
, in_set
):
204 hosts
= HostAssignment(
205 spec
=ServiceSpec(service_type
, placement
=placement
),
206 get_hosts_func
=lambda _
: hosts
,
207 get_daemons_func
=lambda _
: daemons
).place()
208 assert len(hosts
) == expected_len
209 for h
in [h
.hostname
for h
in hosts
]:
212 @pytest.mark
.parametrize("service_type,placement,hosts,daemons,expected_len,must_have",
214 # hosts + (smaller) count, (more) existing
217 PlacementSpec(count
=3, hosts
='host3'.split()),
218 'host1 host2 host3'.split(),
223 # count + partial host list
226 PlacementSpec(count
=2, hosts
=['host3']),
227 'host1 host2 host3'.split(),
233 def test_node_assignment3(service_type
, placement
, hosts
,
234 daemons
, expected_len
, must_have
):
235 hosts
= HostAssignment(
236 spec
=ServiceSpec(service_type
, placement
=placement
),
237 get_hosts_func
=lambda _
: hosts
,
238 get_daemons_func
=lambda _
: daemons
).place()
239 assert len(hosts
) == expected_len
241 assert h
in [h
.hostname
for h
in hosts
]
244 @pytest.mark
.parametrize("placement",
249 ('hostname12hostname12hostname12hostname12hostname12hostname12hostname12'), # > 63 chars
251 def test_bad_placements(placement
):
253 s
= PlacementSpec
.from_string(placement
.split(' '))
255 except ServiceSpecValidationError
as e
:
259 class NodeAssignmentTestBadSpec(NamedTuple
):
261 placement
: PlacementSpec
263 daemons
: List
[DaemonDescription
]
265 @pytest.mark
.parametrize("service_type,placement,hosts,daemons,expected",
268 NodeAssignmentTestBadSpec(
270 PlacementSpec(hosts
=['unknownhost']),
273 "Cannot place <ServiceSpec for service_name=mon> on {'unknownhost'}: Unknown hosts"
275 # unknown host pattern
276 NodeAssignmentTestBadSpec(
278 PlacementSpec(host_pattern
='unknownhost'),
281 "Cannot place <ServiceSpec for service_name=mon>: No matching hosts"
284 NodeAssignmentTestBadSpec(
286 PlacementSpec(label
='unknownlabel'),
289 "Cannot place <ServiceSpec for service_name=mon>: No matching hosts for label unknownlabel"
292 def test_bad_specs(service_type
, placement
, hosts
, daemons
, expected
):
293 with pytest
.raises(OrchestratorValidationError
) as e
:
294 hosts
= HostAssignment(
295 spec
=ServiceSpec(service_type
, placement
=placement
),
296 get_hosts_func
=lambda _
: hosts
,
297 get_daemons_func
=lambda _
: daemons
).place()
298 assert str(e
.value
) == expected