]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/cephadm/tests/test_spec.py
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / pybind / mgr / cephadm / tests / test_spec.py
CommitLineData
f91f0fd5
TL
1# Disable autopep8 for this file:
2
3# fmt: off
4
1911f103
TL
5import json
6
7import pytest
8
f67539c2
TL
9import yaml
10
e306af50 11from ceph.deployment.service_spec import ServiceSpec, NFSServiceSpec, RGWSpec, \
f91f0fd5 12 IscsiServiceSpec, AlertManagerSpec, HostPlacementSpec, CustomContainerSpec
e306af50 13
1911f103
TL
14from orchestrator import DaemonDescription, OrchestratorError
15
16
f6b5b4d7
TL
17@pytest.mark.parametrize(
18 "spec_json",
19 json.loads("""[
1911f103
TL
20{
21 "placement": {
22 "count": 1
23 },
24 "service_type": "alertmanager"
25},
26{
27 "placement": {
28 "host_pattern": "*"
29 },
30 "service_type": "crash"
31},
32{
33 "placement": {
34 "count": 1
35 },
36 "service_type": "grafana"
37},
38{
39 "placement": {
40 "count": 2
41 },
42 "service_type": "mgr"
43},
44{
45 "placement": {
46 "count": 5
47 },
48 "service_type": "mon"
49},
50{
51 "placement": {
52 "host_pattern": "*"
53 },
54 "service_type": "node-exporter"
55},
56{
57 "placement": {
58 "count": 1
59 },
60 "service_type": "prometheus"
61},
62{
63 "placement": {
64 "hosts": [
65 {
66 "hostname": "ceph-001",
67 "network": "",
68 "name": ""
69 }
70 ]
71 },
72 "service_type": "rgw",
73 "service_id": "default-rgw-realm.eu-central-1.1",
74 "rgw_realm": "default-rgw-realm",
f67539c2 75 "rgw_zone": "eu-central-1"
f6b5b4d7
TL
76},
77{
78 "service_type": "osd",
79 "service_id": "osd_spec_default",
80 "placement": {
81 "host_pattern": "*"
82 },
83 "data_devices": {
84 "model": "MC-55-44-XZ"
85 },
86 "db_devices": {
87 "model": "SSD-123-foo"
88 },
89 "wal_devices": {
90 "model": "NVME-QQQQ-987"
91 }
1911f103
TL
92}
93]
f6b5b4d7
TL
94""")
95)
96def test_spec_octopus(spec_json):
97 # https://tracker.ceph.com/issues/44934
98 # Those are real user data from early octopus.
99 # Please do not modify those JSON values.
100
101 spec = ServiceSpec.from_json(spec_json)
f67539c2 102
f6b5b4d7
TL
103 # just some verification that we can sill read old octopus specs
104 def convert_to_old_style_json(j):
105 j_c = dict(j.copy())
106 j_c.pop('service_name', None)
107 if 'spec' in j_c:
108 spec = j_c.pop('spec')
109 j_c.update(spec)
f91f0fd5
TL
110 if 'placement' in j_c:
111 if 'hosts' in j_c['placement']:
112 j_c['placement']['hosts'] = [
113 {
114 'hostname': HostPlacementSpec.parse(h).hostname,
115 'network': HostPlacementSpec.parse(h).network,
116 'name': HostPlacementSpec.parse(h).name
117 }
118 for h in j_c['placement']['hosts']
119 ]
f6b5b4d7
TL
120 j_c.pop('objectstore', None)
121 j_c.pop('filter_logic', None)
122 return j_c
f67539c2 123
f6b5b4d7
TL
124 assert spec_json == convert_to_old_style_json(spec.to_json())
125
126
127@pytest.mark.parametrize(
128 "dd_json",
129 json.loads("""[
1911f103
TL
130 {
131 "hostname": "ceph-001",
132 "container_id": "d94d7969094d",
133 "container_image_id": "0881eb8f169f5556a292b4e2c01d683172b12830a62a9225a98a8e206bb734f0",
134 "container_image_name": "docker.io/prom/alertmanager:latest",
135 "daemon_id": "ceph-001",
136 "daemon_type": "alertmanager",
137 "version": "0.20.0",
138 "status": 1,
139 "status_desc": "running",
140 "last_refresh": "2020-04-03T15:31:48.725856",
141 "created": "2020-04-02T19:23:08.829543",
f6b5b4d7 142 "started": "2020-04-03T07:29:16.932838",
f67539c2 143 "is_active": false
1911f103
TL
144 },
145 {
146 "hostname": "ceph-001",
147 "container_id": "c4b036202241",
148 "container_image_id": "204a01f9b0b6710dd0c0af7f37ce7139c47ff0f0105d778d7104c69282dfbbf1",
149 "container_image_name": "docker.io/ceph/ceph:v15",
150 "daemon_id": "ceph-001",
151 "daemon_type": "crash",
152 "version": "15.2.0",
153 "status": 1,
154 "status_desc": "running",
155 "last_refresh": "2020-04-03T15:31:48.725903",
156 "created": "2020-04-02T19:23:11.390694",
f6b5b4d7 157 "started": "2020-04-03T07:29:16.910897",
f67539c2 158 "is_active": false
1911f103
TL
159 },
160 {
161 "hostname": "ceph-001",
162 "container_id": "5b7b94b48f31",
163 "container_image_id": "87a51ecf0b1c9a7b187b21c1b071425dafea0d765a96d5bc371c791169b3d7f4",
164 "container_image_name": "docker.io/ceph/ceph-grafana:latest",
165 "daemon_id": "ceph-001",
166 "daemon_type": "grafana",
167 "version": "6.6.2",
168 "status": 1,
169 "status_desc": "running",
170 "last_refresh": "2020-04-03T15:31:48.725950",
171 "created": "2020-04-02T19:23:52.025088",
f6b5b4d7 172 "started": "2020-04-03T07:29:16.847972",
f67539c2 173 "is_active": false
1911f103
TL
174 },
175 {
176 "hostname": "ceph-001",
177 "container_id": "9ca007280456",
178 "container_image_id": "204a01f9b0b6710dd0c0af7f37ce7139c47ff0f0105d778d7104c69282dfbbf1",
179 "container_image_name": "docker.io/ceph/ceph:v15",
180 "daemon_id": "ceph-001.gkjwqp",
181 "daemon_type": "mgr",
182 "version": "15.2.0",
183 "status": 1,
184 "status_desc": "running",
185 "last_refresh": "2020-04-03T15:31:48.725807",
186 "created": "2020-04-02T19:22:18.648584",
f6b5b4d7 187 "started": "2020-04-03T07:29:16.856153",
f67539c2 188 "is_active": false
1911f103
TL
189 },
190 {
191 "hostname": "ceph-001",
192 "container_id": "3d1ba9a2b697",
193 "container_image_id": "204a01f9b0b6710dd0c0af7f37ce7139c47ff0f0105d778d7104c69282dfbbf1",
194 "container_image_name": "docker.io/ceph/ceph:v15",
195 "daemon_id": "ceph-001",
196 "daemon_type": "mon",
197 "version": "15.2.0",
198 "status": 1,
199 "status_desc": "running",
200 "last_refresh": "2020-04-03T15:31:48.725715",
201 "created": "2020-04-02T19:22:13.863300",
f6b5b4d7 202 "started": "2020-04-03T07:29:17.206024",
f67539c2 203 "is_active": false
1911f103
TL
204 },
205 {
206 "hostname": "ceph-001",
207 "container_id": "36d026c68ba1",
208 "container_image_id": "e5a616e4b9cf68dfcad7782b78e118be4310022e874d52da85c55923fb615f87",
209 "container_image_name": "docker.io/prom/node-exporter:latest",
210 "daemon_id": "ceph-001",
211 "daemon_type": "node-exporter",
212 "version": "0.18.1",
213 "status": 1,
214 "status_desc": "running",
215 "last_refresh": "2020-04-03T15:31:48.725996",
216 "created": "2020-04-02T19:23:53.880197",
f6b5b4d7 217 "started": "2020-04-03T07:29:16.880044",
f67539c2 218 "is_active": false
1911f103
TL
219 },
220 {
221 "hostname": "ceph-001",
222 "container_id": "faf76193cbfe",
223 "container_image_id": "204a01f9b0b6710dd0c0af7f37ce7139c47ff0f0105d778d7104c69282dfbbf1",
224 "container_image_name": "docker.io/ceph/ceph:v15",
225 "daemon_id": "0",
226 "daemon_type": "osd",
227 "version": "15.2.0",
228 "status": 1,
229 "status_desc": "running",
230 "last_refresh": "2020-04-03T15:31:48.726088",
231 "created": "2020-04-02T20:35:02.991435",
f6b5b4d7 232 "started": "2020-04-03T07:29:19.373956",
f67539c2 233 "is_active": false
1911f103
TL
234 },
235 {
236 "hostname": "ceph-001",
237 "container_id": "f82505bae0f1",
238 "container_image_id": "204a01f9b0b6710dd0c0af7f37ce7139c47ff0f0105d778d7104c69282dfbbf1",
239 "container_image_name": "docker.io/ceph/ceph:v15",
240 "daemon_id": "1",
241 "daemon_type": "osd",
242 "version": "15.2.0",
243 "status": 1,
244 "status_desc": "running",
245 "last_refresh": "2020-04-03T15:31:48.726134",
246 "created": "2020-04-02T20:35:17.142272",
f6b5b4d7 247 "started": "2020-04-03T07:29:19.374002",
f67539c2 248 "is_active": false
1911f103
TL
249 },
250 {
251 "hostname": "ceph-001",
252 "container_id": "2708d84cd484",
253 "container_image_id": "358a0d2395fe711bb8258e8fb4b2d7865c0a9a6463969bcd1452ee8869ea6653",
254 "container_image_name": "docker.io/prom/prometheus:latest",
255 "daemon_id": "ceph-001",
256 "daemon_type": "prometheus",
257 "version": "2.17.1",
258 "status": 1,
259 "status_desc": "running",
260 "last_refresh": "2020-04-03T15:31:48.726042",
261 "created": "2020-04-02T19:24:10.281163",
f6b5b4d7 262 "started": "2020-04-03T07:29:16.926292",
f67539c2 263 "is_active": false
1911f103
TL
264 },
265 {
266 "hostname": "ceph-001",
267 "daemon_id": "default-rgw-realm.eu-central-1.1.ceph-001.ytywjo",
268 "daemon_type": "rgw",
269 "status": 1,
f6b5b4d7 270 "status_desc": "starting",
f67539c2 271 "is_active": false
1911f103 272 }
f6b5b4d7
TL
273]""")
274)
275def test_dd_octopus(dd_json):
276 # https://tracker.ceph.com/issues/44934
277 # Those are real user data from early octopus.
278 # Please do not modify those JSON values.
adb31ebb
TL
279
280 # Convert datetime properties to old style.
281 # 2020-04-03T07:29:16.926292Z -> 2020-04-03T07:29:16.926292
282 def convert_to_old_style_json(j):
283 for k in ['last_refresh', 'created', 'started', 'last_deployed',
284 'last_configured']:
285 if k in j:
286 j[k] = j[k].rstrip('Z')
287 return j
288
289 assert dd_json == convert_to_old_style_json(
290 DaemonDescription.from_json(dd_json).to_json())
1911f103
TL
291
292
293@pytest.mark.parametrize("spec,dd,valid",
f67539c2 294[ # noqa: E128
e306af50 295 # https://tracker.ceph.com/issues/44934
1911f103 296 (
1911f103 297 RGWSpec(
f67539c2 298 service_id="foo",
1911f103
TL
299 rgw_realm="default-rgw-realm",
300 rgw_zone="eu-central-1",
1911f103
TL
301 ),
302 DaemonDescription(
303 daemon_type='rgw',
f67539c2 304 daemon_id="foo.ceph-001.ytywjo",
1911f103
TL
305 hostname="ceph-001",
306 ),
307 True
308 ),
309 (
f67539c2 310 # no realm
1911f103 311 RGWSpec(
f67539c2 312 service_id="foo.bar",
1911f103
TL
313 rgw_zone="eu-central-1",
314 ),
315 DaemonDescription(
316 daemon_type='rgw',
f67539c2 317 daemon_id="foo.bar.ceph-001.ytywjo",
1911f103
TL
318 hostname="ceph-001",
319 ),
320 True
321 ),
322 (
f67539c2 323 # no realm or zone
1911f103 324 RGWSpec(
f67539c2 325 service_id="bar",
1911f103
TL
326 ),
327 DaemonDescription(
328 daemon_type='rgw',
f67539c2 329 daemon_id="bar.host.domain.tld.ytywjo",
1911f103
TL
330 hostname="host.domain.tld",
331 ),
332 True
333 ),
e306af50
TL
334 (
335 # explicit naming
336 RGWSpec(
f67539c2 337 service_id="realm.zone",
e306af50
TL
338 ),
339 DaemonDescription(
340 daemon_type='rgw',
341 daemon_id="realm.zone.a",
342 hostname="smithi028",
343 ),
344 True
345 ),
1911f103
TL
346 (
347 # without host
348 RGWSpec(
349 service_type='rgw',
f67539c2
TL
350 service_id="foo",
351 ),
352 DaemonDescription(
353 daemon_type='rgw',
354 daemon_id="foo.hostname.ytywjo",
355 hostname=None,
356 ),
357 False
358 ),
359 (
360 # without host (2)
361 RGWSpec(
362 service_type='rgw',
363 service_id="default-rgw-realm.eu-central-1.1",
1911f103
TL
364 ),
365 DaemonDescription(
366 daemon_type='rgw',
367 daemon_id="default-rgw-realm.eu-central-1.1.hostname.ytywjo",
368 hostname=None,
369 ),
370 False
371 ),
e306af50 372 (
f67539c2
TL
373 # service_id contains hostname
374 # (sort of) https://tracker.ceph.com/issues/45294
e306af50 375 RGWSpec(
f67539c2 376 service_id="default.rgw.realm.ceph.001",
e306af50
TL
377 ),
378 DaemonDescription(
379 daemon_type='rgw',
f67539c2 380 daemon_id="default.rgw.realm.ceph.001.ceph.001.ytywjo",
e306af50
TL
381 hostname="ceph.001",
382 ),
383 True
384 ),
385
386 # https://tracker.ceph.com/issues/45293
387 (
388 ServiceSpec(
389 service_type='mds',
390 service_id="a",
391 ),
392 DaemonDescription(
393 daemon_type='mds',
394 daemon_id="a.host1.abc123",
395 hostname="host1",
396 ),
397 True
398 ),
399 (
400 # '.' char in service_id
401 ServiceSpec(
402 service_type='mds',
403 service_id="a.b.c",
404 ),
405 DaemonDescription(
406 daemon_type='mds',
407 daemon_id="a.b.c.host1.abc123",
408 hostname="host1",
409 ),
410 True
411 ),
412
413 # https://tracker.ceph.com/issues/45617
414 (
415 # daemon_id does not contain hostname
416 ServiceSpec(
417 service_type='mds',
418 service_id="a",
419 ),
420 DaemonDescription(
421 daemon_type='mds',
422 daemon_id="a",
423 hostname="host1",
424 ),
425 True
426 ),
427 (
428 # daemon_id only contains hostname
429 ServiceSpec(
430 service_type='mds',
431 service_id="host1",
432 ),
433 DaemonDescription(
434 daemon_type='mds',
435 daemon_id="host1",
436 hostname="host1",
437 ),
438 True
439 ),
440
441 # https://tracker.ceph.com/issues/45399
442 (
443 # daemon_id only contains hostname
444 ServiceSpec(
445 service_type='mds',
446 service_id="a",
447 ),
448 DaemonDescription(
449 daemon_type='mds',
450 daemon_id="a.host1.abc123",
451 hostname="host1.site",
452 ),
453 True
454 ),
455 (
456 NFSServiceSpec(
457 service_id="a",
458 ),
459 DaemonDescription(
460 daemon_type='nfs',
461 daemon_id="a.host1",
462 hostname="host1.site",
463 ),
464 True
465 ),
466
467 # https://tracker.ceph.com/issues/45293
468 (
469 NFSServiceSpec(
470 service_id="a",
471 ),
472 DaemonDescription(
473 daemon_type='nfs',
474 daemon_id="a.host1",
475 hostname="host1",
476 ),
477 True
478 ),
479 (
480 # service_id contains a '.' char
481 NFSServiceSpec(
482 service_id="a.b.c",
483 ),
484 DaemonDescription(
485 daemon_type='nfs',
486 daemon_id="a.b.c.host1",
487 hostname="host1",
488 ),
489 True
490 ),
491 (
492 # trailing chars after hostname
493 NFSServiceSpec(
494 service_id="a.b.c",
495 ),
496 DaemonDescription(
497 daemon_type='nfs',
498 daemon_id="a.b.c.host1.abc123",
499 hostname="host1",
500 ),
501 True
502 ),
503 (
504 # chars after hostname without '.'
505 NFSServiceSpec(
506 service_id="a",
507 ),
508 DaemonDescription(
509 daemon_type='nfs',
510 daemon_id="a.host1abc123",
511 hostname="host1",
512 ),
513 False
514 ),
515 (
516 # chars before hostname without '.'
517 NFSServiceSpec(
518 service_id="a",
519 ),
520 DaemonDescription(
521 daemon_type='nfs',
522 daemon_id="ahost1.abc123",
523 hostname="host1",
524 ),
525 False
526 ),
527
528 # https://tracker.ceph.com/issues/45293
529 (
530 IscsiServiceSpec(
531 service_type='iscsi',
532 service_id="a",
533 ),
534 DaemonDescription(
535 daemon_type='iscsi',
536 daemon_id="a.host1.abc123",
537 hostname="host1",
538 ),
539 True
540 ),
541 (
542 # '.' char in service_id
543 IscsiServiceSpec(
544 service_type='iscsi',
545 service_id="a.b.c",
546 ),
547 DaemonDescription(
548 daemon_type='iscsi',
549 daemon_id="a.b.c.host1.abc123",
550 hostname="host1",
551 ),
552 True
553 ),
f91f0fd5
TL
554 (
555 # fixed daemon id for teuthology.
556 IscsiServiceSpec(
557 service_type='iscsi',
558 service_id='iscsi',
559 ),
560 DaemonDescription(
561 daemon_type='iscsi',
562 daemon_id="iscsi.a",
563 hostname="host1",
564 ),
565 True
566 ),
567
568 (
569 CustomContainerSpec(
570 service_type='container',
571 service_id='hello-world',
572 image='docker.io/library/hello-world:latest',
573 ),
574 DaemonDescription(
575 daemon_type='container',
576 daemon_id='hello-world.mgr0',
577 hostname='mgr0',
578 ),
579 True
580 ),
f67539c2
TL
581
582 (
583 # daemon_id only contains hostname
584 ServiceSpec(
585 service_type='cephadm-exporter',
586 ),
587 DaemonDescription(
588 daemon_type='cephadm-exporter',
589 daemon_id="testhost",
590 hostname="testhost",
591 ),
592 True
593 ),
1911f103 594])
e306af50
TL
595def test_daemon_description_service_name(spec: ServiceSpec,
596 dd: DaemonDescription,
597 valid: bool):
1911f103
TL
598 if valid:
599 assert spec.service_name() == dd.service_name()
600 else:
601 with pytest.raises(OrchestratorError):
602 dd.service_name()
603
f6b5b4d7
TL
604
605def test_alertmanager_spec_1():
606 spec = AlertManagerSpec()
607 assert spec.service_type == 'alertmanager'
608 assert isinstance(spec.user_data, dict)
609 assert len(spec.user_data.keys()) == 0
610
611
612def test_alertmanager_spec_2():
613 spec = AlertManagerSpec(user_data={'default_webhook_urls': ['foo']})
614 assert isinstance(spec.user_data, dict)
615 assert 'default_webhook_urls' in spec.user_data.keys()
f91f0fd5
TL
616
617
618def test_custom_container_spec():
619 spec = CustomContainerSpec(service_id='hello-world',
620 image='docker.io/library/hello-world:latest',
621 entrypoint='/usr/bin/bash',
622 uid=1000,
623 gid=2000,
624 volume_mounts={'foo': '/foo'},
625 args=['--foo'],
626 envs=['FOO=0815'],
627 bind_mounts=[
628 [
629 'type=bind',
630 'source=lib/modules',
631 'destination=/lib/modules',
632 'ro=true'
633 ]
634 ],
635 ports=[8080, 8443],
636 dirs=['foo', 'bar'],
637 files={
638 'foo.conf': 'foo\nbar',
639 'bar.conf': ['foo', 'bar']
640 })
641 assert spec.service_type == 'container'
642 assert spec.entrypoint == '/usr/bin/bash'
643 assert spec.uid == 1000
644 assert spec.gid == 2000
645 assert spec.volume_mounts == {'foo': '/foo'}
646 assert spec.args == ['--foo']
647 assert spec.envs == ['FOO=0815']
648 assert spec.bind_mounts == [
649 [
650 'type=bind',
651 'source=lib/modules',
652 'destination=/lib/modules',
653 'ro=true'
654 ]
655 ]
656 assert spec.ports == [8080, 8443]
657 assert spec.dirs == ['foo', 'bar']
658 assert spec.files == {
659 'foo.conf': 'foo\nbar',
660 'bar.conf': ['foo', 'bar']
661 }
662
663
664def test_custom_container_spec_config_json():
665 spec = CustomContainerSpec(service_id='foo', image='foo', dirs=None)
666 config_json = spec.config_json()
667 for key in ['entrypoint', 'uid', 'gid', 'bind_mounts', 'dirs']:
668 assert key not in config_json
f67539c2
TL
669
670
671def test_ingress_spec():
672 yaml_str = """service_type: ingress
673service_id: rgw.foo
674placement:
675 hosts:
676 - host1
677 - host2
678 - host3
679spec:
680 virtual_ip: 192.168.20.1/24
681 backend_service: rgw.foo
682 frontend_port: 8080
683 monitor_port: 8081
684"""
685 yaml_file = yaml.safe_load(yaml_str)
686 spec = ServiceSpec.from_json(yaml_file)
687 assert spec.service_type == "ingress"
688 assert spec.service_id == "rgw.foo"
689 assert spec.virtual_ip == "192.168.20.1/24"
690 assert spec.frontend_port == 8080
691 assert spec.monitor_port == 8081