]>
Commit | Line | Data |
---|---|---|
e306af50 | 1 | # type: ignore |
b3b6e05e TL |
2 | |
3 | import errno | |
522d829b | 4 | import json |
9f95a23c TL |
5 | import mock |
6 | import os | |
b3b6e05e TL |
7 | import pytest |
8 | import socket | |
9f95a23c | 9 | import sys |
f67539c2 | 10 | import time |
b3b6e05e TL |
11 | import threading |
12 | import unittest | |
13 | ||
f67539c2 TL |
14 | from http.server import HTTPServer |
15 | from urllib.request import Request, urlopen | |
16 | from urllib.error import HTTPError | |
9f95a23c | 17 | |
b3b6e05e | 18 | from typing import List, Optional |
9f95a23c | 19 | |
b3b6e05e TL |
20 | from .fixtures import ( |
21 | cephadm_fs, | |
22 | exporter, | |
23 | mock_docker, | |
24 | mock_podman, | |
25 | with_cephadm_ctx, | |
26 | ) | |
f67539c2 | 27 | |
b3b6e05e TL |
28 | |
29 | with mock.patch('builtins.open', create=True): | |
9f95a23c TL |
30 | from importlib.machinery import SourceFileLoader |
31 | cd = SourceFileLoader('cephadm', 'cephadm').load_module() | |
9f95a23c | 32 | |
f67539c2 | 33 | |
522d829b TL |
34 | def get_ceph_conf( |
35 | fsid='00000000-0000-0000-0000-0000deadbeef', | |
36 | mon_host='[v2:192.168.1.1:3300/0,v1:192.168.1.1:6789/0]'): | |
37 | return f''' | |
38 | # minimal ceph.conf for {fsid} | |
39 | [global] | |
40 | fsid = {fsid} | |
41 | mon_host = {mon_host} | |
42 | ''' | |
43 | ||
b3b6e05e | 44 | class TestCephAdm(object): |
f67539c2 TL |
45 | |
46 | def test_docker_unit_file(self): | |
522d829b | 47 | ctx = cd.CephadmContext() |
b3b6e05e | 48 | ctx.container_engine = mock_docker() |
f67539c2 TL |
49 | r = cd.get_unit_file(ctx, '9b9d7609-f4d5-4aba-94c8-effa764d96c9') |
50 | assert 'Requires=docker.service' in r | |
b3b6e05e | 51 | ctx.container_engine = mock_podman() |
f67539c2 TL |
52 | r = cd.get_unit_file(ctx, '9b9d7609-f4d5-4aba-94c8-effa764d96c9') |
53 | assert 'Requires=docker.service' not in r | |
54 | ||
55 | @mock.patch('cephadm.logger') | |
56 | def test_attempt_bind(self, logger): | |
57 | ctx = None | |
58 | address = None | |
59 | port = 0 | |
60 | ||
61 | def os_error(errno): | |
62 | _os_error = OSError() | |
63 | _os_error.errno = errno | |
64 | return _os_error | |
65 | ||
66 | for side_effect, expected_exception in ( | |
67 | (os_error(errno.EADDRINUSE), cd.PortOccupiedError), | |
b3b6e05e TL |
68 | (os_error(errno.EAFNOSUPPORT), cd.Error), |
69 | (os_error(errno.EADDRNOTAVAIL), cd.Error), | |
f67539c2 TL |
70 | (None, None), |
71 | ): | |
72 | _socket = mock.Mock() | |
73 | _socket.bind.side_effect = side_effect | |
74 | try: | |
75 | cd.attempt_bind(ctx, _socket, address, port) | |
76 | except Exception as e: | |
77 | assert isinstance(e, expected_exception) | |
78 | else: | |
79 | if expected_exception is not None: | |
80 | assert False | |
81 | ||
82 | @mock.patch('cephadm.attempt_bind') | |
83 | @mock.patch('cephadm.logger') | |
84 | def test_port_in_use(self, logger, attempt_bind): | |
85 | empty_ctx = None | |
86 | ||
87 | assert cd.port_in_use(empty_ctx, 9100) == False | |
88 | ||
89 | attempt_bind.side_effect = cd.PortOccupiedError('msg') | |
90 | assert cd.port_in_use(empty_ctx, 9100) == True | |
91 | ||
92 | os_error = OSError() | |
93 | os_error.errno = errno.EADDRNOTAVAIL | |
94 | attempt_bind.side_effect = os_error | |
95 | assert cd.port_in_use(empty_ctx, 9100) == False | |
96 | ||
97 | os_error = OSError() | |
98 | os_error.errno = errno.EAFNOSUPPORT | |
99 | attempt_bind.side_effect = os_error | |
100 | assert cd.port_in_use(empty_ctx, 9100) == False | |
101 | ||
102 | @mock.patch('socket.socket') | |
103 | @mock.patch('cephadm.logger') | |
104 | def test_check_ip_port_success(self, logger, _socket): | |
522d829b | 105 | ctx = cd.CephadmContext() |
f67539c2 TL |
106 | ctx.skip_ping_check = False # enables executing port check with `check_ip_port` |
107 | ||
108 | for address, address_family in ( | |
109 | ('0.0.0.0', socket.AF_INET), | |
110 | ('::', socket.AF_INET6), | |
111 | ): | |
112 | try: | |
113 | cd.check_ip_port(ctx, address, 9100) | |
114 | except: | |
115 | assert False | |
116 | else: | |
b3b6e05e | 117 | assert _socket.call_args == mock.call(address_family, socket.SOCK_STREAM) |
f67539c2 TL |
118 | |
119 | @mock.patch('socket.socket') | |
120 | @mock.patch('cephadm.logger') | |
121 | def test_check_ip_port_failure(self, logger, _socket): | |
522d829b | 122 | ctx = cd.CephadmContext() |
f67539c2 TL |
123 | ctx.skip_ping_check = False # enables executing port check with `check_ip_port` |
124 | ||
125 | def os_error(errno): | |
126 | _os_error = OSError() | |
127 | _os_error.errno = errno | |
128 | return _os_error | |
129 | ||
130 | for address, address_family in ( | |
131 | ('0.0.0.0', socket.AF_INET), | |
132 | ('::', socket.AF_INET6), | |
133 | ): | |
134 | for side_effect, expected_exception in ( | |
135 | (os_error(errno.EADDRINUSE), cd.PortOccupiedError), | |
b3b6e05e TL |
136 | (os_error(errno.EADDRNOTAVAIL), cd.Error), |
137 | (os_error(errno.EAFNOSUPPORT), cd.Error), | |
f67539c2 TL |
138 | (None, None), |
139 | ): | |
140 | mock_socket_obj = mock.Mock() | |
141 | mock_socket_obj.bind.side_effect = side_effect | |
142 | _socket.return_value = mock_socket_obj | |
143 | try: | |
144 | cd.check_ip_port(ctx, address, 9100) | |
145 | except Exception as e: | |
146 | assert isinstance(e, expected_exception) | |
147 | else: | |
148 | if side_effect is not None: | |
149 | assert False | |
150 | ||
151 | ||
f91f0fd5 | 152 | def test_is_not_fsid(self): |
9f95a23c TL |
153 | assert not cd.is_fsid('no-uuid') |
154 | ||
f91f0fd5 TL |
155 | def test_is_fsid(self): |
156 | assert cd.is_fsid('e863154d-33c7-4350-bca5-921e0467e55b') | |
157 | ||
9f95a23c TL |
158 | def test__get_parser_image(self): |
159 | args = cd._parse_args(['--image', 'foo', 'version']) | |
160 | assert args.image == 'foo' | |
161 | ||
522d829b TL |
162 | def test_parse_mem_usage(self): |
163 | cd.logger = mock.Mock() | |
164 | len, summary = cd._parse_mem_usage(0, 'c6290e3f1489,-- / --') | |
165 | assert summary == {} | |
166 | ||
9f95a23c TL |
167 | def test_CustomValidation(self): |
168 | assert cd._parse_args(['deploy', '--name', 'mon.a', '--fsid', 'fsid']) | |
169 | ||
170 | with pytest.raises(SystemExit): | |
171 | cd._parse_args(['deploy', '--name', 'wrong', '--fsid', 'fsid']) | |
172 | ||
173 | @pytest.mark.parametrize("test_input, expected", [ | |
f67539c2 TL |
174 | ("1.6.2", (1,6,2)), |
175 | ("1.6.2-stable2", (1,6,2)), | |
9f95a23c TL |
176 | ]) |
177 | def test_parse_podman_version(self, test_input, expected): | |
178 | assert cd._parse_podman_version(test_input) == expected | |
179 | ||
180 | def test_parse_podman_version_invalid(self): | |
181 | with pytest.raises(ValueError) as res: | |
f67539c2 | 182 | cd._parse_podman_version('inval.id') |
9f95a23c TL |
183 | assert 'inval' in str(res.value) |
184 | ||
f6b5b4d7 TL |
185 | def test_is_ipv6(self): |
186 | cd.logger = mock.Mock() | |
187 | for good in ("[::1]", "::1", | |
188 | "fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"): | |
189 | assert cd.is_ipv6(good) | |
190 | for bad in ("127.0.0.1", | |
191 | "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffg", | |
192 | "1:2:3:4:5:6:7:8:9", "fd00::1::1", "[fg::1]"): | |
193 | assert not cd.is_ipv6(bad) | |
194 | ||
195 | def test_unwrap_ipv6(self): | |
196 | def unwrap_test(address, expected): | |
197 | assert cd.unwrap_ipv6(address) == expected | |
198 | ||
199 | tests = [ | |
200 | ('::1', '::1'), ('[::1]', '::1'), | |
201 | ('[fde4:8dba:82e1:0:5054:ff:fe6a:357]', 'fde4:8dba:82e1:0:5054:ff:fe6a:357'), | |
202 | ('can actually be any string', 'can actually be any string'), | |
203 | ('[but needs to be stripped] ', '[but needs to be stripped] ')] | |
204 | for address, expected in tests: | |
205 | unwrap_test(address, expected) | |
206 | ||
f91f0fd5 TL |
207 | def test_wrap_ipv6(self): |
208 | def wrap_test(address, expected): | |
209 | assert cd.wrap_ipv6(address) == expected | |
210 | ||
211 | tests = [ | |
212 | ('::1', '[::1]'), ('[::1]', '[::1]'), | |
213 | ('fde4:8dba:82e1:0:5054:ff:fe6a:357', | |
214 | '[fde4:8dba:82e1:0:5054:ff:fe6a:357]'), | |
215 | ('myhost.example.com', 'myhost.example.com'), | |
216 | ('192.168.0.1', '192.168.0.1'), | |
217 | ('', ''), ('fd00::1::1', 'fd00::1::1')] | |
218 | for address, expected in tests: | |
219 | wrap_test(address, expected) | |
220 | ||
f6b5b4d7 TL |
221 | @mock.patch('cephadm.call_throws') |
222 | @mock.patch('cephadm.get_parm') | |
223 | def test_registry_login(self, get_parm, call_throws): | |
224 | ||
225 | # test normal valid login with url, username and password specified | |
226 | call_throws.return_value = '', '', 0 | |
b3b6e05e | 227 | ctx: cd.CephadmContext = cd.cephadm_init_ctx( |
f67539c2 TL |
228 | ['registry-login', '--registry-url', 'sample-url', |
229 | '--registry-username', 'sample-user', '--registry-password', | |
230 | 'sample-pass']) | |
b3b6e05e | 231 | ctx.container_engine = mock_docker() |
f67539c2 | 232 | retval = cd.command_registry_login(ctx) |
f6b5b4d7 TL |
233 | assert retval == 0 |
234 | ||
235 | # test bad login attempt with invalid arguments given | |
b3b6e05e | 236 | ctx: cd.CephadmContext = cd.cephadm_init_ctx( |
f67539c2 | 237 | ['registry-login', '--registry-url', 'bad-args-url']) |
f6b5b4d7 | 238 | with pytest.raises(Exception) as e: |
f67539c2 | 239 | assert cd.command_registry_login(ctx) |
f6b5b4d7 TL |
240 | assert str(e.value) == ('Invalid custom registry arguments received. To login to a custom registry include ' |
241 | '--registry-url, --registry-username and --registry-password options or --registry-json option') | |
242 | ||
243 | # test normal valid login with json file | |
244 | get_parm.return_value = {"url": "sample-url", "username": "sample-username", "password": "sample-password"} | |
b3b6e05e | 245 | ctx: cd.CephadmContext = cd.cephadm_init_ctx( |
f67539c2 | 246 | ['registry-login', '--registry-json', 'sample-json']) |
b3b6e05e | 247 | ctx.container_engine = mock_docker() |
f67539c2 | 248 | retval = cd.command_registry_login(ctx) |
f6b5b4d7 TL |
249 | assert retval == 0 |
250 | ||
251 | # test bad login attempt with bad json file | |
252 | get_parm.return_value = {"bad-json": "bad-json"} | |
b3b6e05e | 253 | ctx: cd.CephadmContext = cd.cephadm_init_ctx( |
f67539c2 | 254 | ['registry-login', '--registry-json', 'sample-json']) |
f6b5b4d7 | 255 | with pytest.raises(Exception) as e: |
f67539c2 | 256 | assert cd.command_registry_login(ctx) |
f6b5b4d7 TL |
257 | assert str(e.value) == ("json provided for custom registry login did not include all necessary fields. " |
258 | "Please setup json file as\n" | |
259 | "{\n" | |
260 | " \"url\": \"REGISTRY_URL\",\n" | |
261 | " \"username\": \"REGISTRY_USERNAME\",\n" | |
262 | " \"password\": \"REGISTRY_PASSWORD\"\n" | |
263 | "}\n") | |
264 | ||
265 | # test login attempt with valid arguments where login command fails | |
266 | call_throws.side_effect = Exception | |
b3b6e05e | 267 | ctx: cd.CephadmContext = cd.cephadm_init_ctx( |
f67539c2 TL |
268 | ['registry-login', '--registry-url', 'sample-url', |
269 | '--registry-username', 'sample-user', '--registry-password', | |
270 | 'sample-pass']) | |
f6b5b4d7 | 271 | with pytest.raises(Exception) as e: |
f67539c2 | 272 | cd.command_registry_login(ctx) |
f6b5b4d7 | 273 | assert str(e.value) == "Failed to login to custom registry @ sample-url as sample-user with given password" |
f91f0fd5 TL |
274 | |
275 | def test_get_image_info_from_inspect(self): | |
276 | # podman | |
cd265ab1 | 277 | out = """204a01f9b0b6710dd0c0af7f37ce7139c47ff0f0105d778d7104c69282dfbbf1,[docker.io/ceph/ceph@sha256:1cc9b824e1b076cdff52a9aa3f0cc8557d879fb2fbbba0cafed970aca59a3992]""" |
f91f0fd5 | 278 | r = cd.get_image_info_from_inspect(out, 'registry/ceph/ceph:latest') |
f67539c2 | 279 | print(r) |
f91f0fd5 TL |
280 | assert r == { |
281 | 'image_id': '204a01f9b0b6710dd0c0af7f37ce7139c47ff0f0105d778d7104c69282dfbbf1', | |
f67539c2 | 282 | 'repo_digests': ['docker.io/ceph/ceph@sha256:1cc9b824e1b076cdff52a9aa3f0cc8557d879fb2fbbba0cafed970aca59a3992'] |
f91f0fd5 TL |
283 | } |
284 | ||
285 | # docker | |
cd265ab1 | 286 | out = """sha256:16f4549cf7a8f112bbebf7946749e961fbbd1b0838627fe619aab16bc17ce552,[quay.ceph.io/ceph-ci/ceph@sha256:4e13da36c1bd6780b312a985410ae678984c37e6a9493a74c87e4a50b9bda41f]""" |
f91f0fd5 TL |
287 | r = cd.get_image_info_from_inspect(out, 'registry/ceph/ceph:latest') |
288 | assert r == { | |
289 | 'image_id': '16f4549cf7a8f112bbebf7946749e961fbbd1b0838627fe619aab16bc17ce552', | |
f67539c2 | 290 | 'repo_digests': ['quay.ceph.io/ceph-ci/ceph@sha256:4e13da36c1bd6780b312a985410ae678984c37e6a9493a74c87e4a50b9bda41f'] |
f91f0fd5 TL |
291 | } |
292 | ||
f67539c2 TL |
293 | # multiple digests (podman) |
294 | out = """e935122ab143a64d92ed1fbb27d030cf6e2f0258207be1baf1b509c466aeeb42,[docker.io/prom/prometheus@sha256:e4ca62c0d62f3e886e684806dfe9d4e0cda60d54986898173c1083856cfda0f4 docker.io/prom/prometheus@sha256:efd99a6be65885c07c559679a0df4ec709604bcdd8cd83f0d00a1a683b28fb6a]""" | |
295 | r = cd.get_image_info_from_inspect(out, 'registry/prom/prometheus:latest') | |
296 | assert r == { | |
297 | 'image_id': 'e935122ab143a64d92ed1fbb27d030cf6e2f0258207be1baf1b509c466aeeb42', | |
298 | 'repo_digests': [ | |
299 | 'docker.io/prom/prometheus@sha256:e4ca62c0d62f3e886e684806dfe9d4e0cda60d54986898173c1083856cfda0f4', | |
300 | 'docker.io/prom/prometheus@sha256:efd99a6be65885c07c559679a0df4ec709604bcdd8cd83f0d00a1a683b28fb6a', | |
301 | ] | |
302 | } | |
303 | ||
304 | ||
f91f0fd5 TL |
305 | def test_dict_get(self): |
306 | result = cd.dict_get({'a': 1}, 'a', require=True) | |
307 | assert result == 1 | |
308 | result = cd.dict_get({'a': 1}, 'b') | |
309 | assert result is None | |
310 | result = cd.dict_get({'a': 1}, 'b', default=2) | |
311 | assert result == 2 | |
312 | ||
313 | def test_dict_get_error(self): | |
314 | with pytest.raises(cd.Error): | |
315 | cd.dict_get({'a': 1}, 'b', require=True) | |
316 | ||
317 | def test_dict_get_join(self): | |
318 | result = cd.dict_get_join({'foo': ['a', 'b']}, 'foo') | |
319 | assert result == 'a\nb' | |
320 | result = cd.dict_get_join({'foo': [1, 2]}, 'foo') | |
321 | assert result == '1\n2' | |
322 | result = cd.dict_get_join({'bar': 'a'}, 'bar') | |
323 | assert result == 'a' | |
324 | result = cd.dict_get_join({'a': 1}, 'a') | |
325 | assert result == 1 | |
326 | ||
adb31ebb TL |
327 | def test_last_local_images(self): |
328 | out = ''' | |
329 | docker.io/ceph/daemon-base@ | |
330 | docker.io/ceph/ceph:v15.2.5 | |
331 | docker.io/ceph/daemon-base:octopus | |
332 | ''' | |
333 | image = cd._filter_last_local_ceph_image(out) | |
334 | assert image == 'docker.io/ceph/ceph:v15.2.5' | |
335 | ||
b3b6e05e TL |
336 | def test_normalize_image_digest(self): |
337 | s = 'myhostname:5000/ceph/ceph@sha256:753886ad9049004395ae990fbb9b096923b5a518b819283141ee8716ddf55ad1' | |
338 | assert cd.normalize_image_digest(s) == s | |
339 | ||
340 | s = 'ceph/ceph:latest' | |
341 | assert cd.normalize_image_digest(s) == f'{cd.DEFAULT_REGISTRY}/{s}' | |
f91f0fd5 | 342 | |
522d829b TL |
343 | @pytest.mark.parametrize('fsid, ceph_conf, list_daemons, result, err, ', |
344 | [ | |
345 | ( | |
346 | None, | |
347 | None, | |
348 | [], | |
349 | None, | |
350 | None, | |
351 | ), | |
352 | ( | |
353 | '00000000-0000-0000-0000-0000deadbeef', | |
354 | None, | |
355 | [], | |
356 | '00000000-0000-0000-0000-0000deadbeef', | |
357 | None, | |
358 | ), | |
359 | ( | |
360 | '00000000-0000-0000-0000-0000deadbeef', | |
361 | None, | |
362 | [ | |
363 | {'fsid': '10000000-0000-0000-0000-0000deadbeef'}, | |
364 | {'fsid': '20000000-0000-0000-0000-0000deadbeef'}, | |
365 | ], | |
366 | '00000000-0000-0000-0000-0000deadbeef', | |
367 | None, | |
368 | ), | |
369 | ( | |
370 | None, | |
371 | None, | |
372 | [ | |
373 | {'fsid': '00000000-0000-0000-0000-0000deadbeef'}, | |
374 | ], | |
375 | '00000000-0000-0000-0000-0000deadbeef', | |
376 | None, | |
377 | ), | |
378 | ( | |
379 | None, | |
380 | None, | |
381 | [ | |
382 | {'fsid': '10000000-0000-0000-0000-0000deadbeef'}, | |
383 | {'fsid': '20000000-0000-0000-0000-0000deadbeef'}, | |
384 | ], | |
385 | None, | |
386 | r'Cannot infer an fsid', | |
387 | ), | |
388 | ( | |
389 | None, | |
390 | get_ceph_conf(fsid='00000000-0000-0000-0000-0000deadbeef'), | |
391 | [], | |
392 | '00000000-0000-0000-0000-0000deadbeef', | |
393 | None, | |
394 | ), | |
395 | ( | |
396 | None, | |
397 | get_ceph_conf(fsid='00000000-0000-0000-0000-0000deadbeef'), | |
398 | [ | |
399 | {'fsid': '00000000-0000-0000-0000-0000deadbeef'}, | |
400 | ], | |
401 | '00000000-0000-0000-0000-0000deadbeef', | |
402 | None, | |
403 | ), | |
404 | ( | |
405 | None, | |
406 | get_ceph_conf(fsid='00000000-0000-0000-0000-0000deadbeef'), | |
407 | [ | |
408 | {'fsid': '10000000-0000-0000-0000-0000deadbeef'}, | |
409 | {'fsid': '20000000-0000-0000-0000-0000deadbeef'}, | |
410 | ], | |
411 | None, | |
412 | r'Cannot infer an fsid', | |
413 | ), | |
414 | ]) | |
415 | @mock.patch('cephadm.call') | |
416 | def test_infer_fsid(self, _call, fsid, ceph_conf, list_daemons, result, err, cephadm_fs): | |
417 | # build the context | |
418 | ctx = cd.CephadmContext() | |
419 | ctx.fsid = fsid | |
420 | ||
421 | # mock the decorator | |
422 | mock_fn = mock.Mock() | |
423 | mock_fn.return_value = 0 | |
424 | infer_fsid = cd.infer_fsid(mock_fn) | |
425 | ||
426 | # mock the ceph.conf file content | |
427 | if ceph_conf: | |
428 | f = cephadm_fs.create_file('ceph.conf', contents=ceph_conf) | |
429 | ctx.config = f.path | |
430 | ||
431 | # test | |
432 | with mock.patch('cephadm.list_daemons', return_value=list_daemons): | |
433 | if err: | |
434 | with pytest.raises(cd.Error, match=err): | |
435 | infer_fsid(ctx) | |
436 | else: | |
437 | infer_fsid(ctx) | |
438 | assert ctx.fsid == result | |
439 | ||
440 | @pytest.mark.parametrize('fsid, config, name, list_daemons, result, ', | |
441 | [ | |
442 | ( | |
443 | None, | |
444 | '/foo/bar.conf', | |
445 | None, | |
446 | [], | |
447 | '/foo/bar.conf', | |
448 | ), | |
449 | ( | |
450 | '00000000-0000-0000-0000-0000deadbeef', | |
451 | None, | |
452 | None, | |
453 | [], | |
454 | cd.SHELL_DEFAULT_CONF, | |
455 | ), | |
456 | ( | |
457 | '00000000-0000-0000-0000-0000deadbeef', | |
458 | None, | |
459 | None, | |
460 | [{'name': 'mon.a'}], | |
461 | '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/mon.a/config', | |
462 | ), | |
463 | ( | |
464 | '00000000-0000-0000-0000-0000deadbeef', | |
465 | None, | |
466 | None, | |
467 | [{'name': 'osd.0'}], | |
468 | cd.SHELL_DEFAULT_CONF, | |
469 | ), | |
470 | ( | |
471 | '00000000-0000-0000-0000-0000deadbeef', | |
472 | '/foo/bar.conf', | |
473 | 'mon.a', | |
474 | [{'name': 'mon.a'}], | |
475 | '/foo/bar.conf', | |
476 | ), | |
477 | ( | |
478 | '00000000-0000-0000-0000-0000deadbeef', | |
479 | None, | |
480 | 'mon.a', | |
481 | [], | |
482 | '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/mon.a/config', | |
483 | ), | |
484 | ( | |
485 | '00000000-0000-0000-0000-0000deadbeef', | |
486 | None, | |
487 | 'osd.0', | |
488 | [], | |
489 | '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/osd.0/config', | |
490 | ), | |
491 | ( | |
492 | None, | |
493 | None, | |
494 | None, | |
495 | [], | |
496 | cd.SHELL_DEFAULT_CONF, | |
497 | ), | |
498 | ]) | |
499 | @mock.patch('cephadm.call') | |
500 | def test_infer_config(self, _call, fsid, config, name, list_daemons, result, cephadm_fs): | |
501 | # build the context | |
502 | ctx = cd.CephadmContext() | |
503 | ctx.fsid = fsid | |
504 | ctx.config = config | |
505 | ctx.name = name | |
506 | ||
507 | # mock the decorator | |
508 | mock_fn = mock.Mock() | |
509 | mock_fn.return_value = 0 | |
510 | infer_config = cd.infer_config(mock_fn) | |
511 | ||
512 | # mock the shell config | |
513 | cephadm_fs.create_file(cd.SHELL_DEFAULT_CONF) | |
514 | ||
515 | # test | |
516 | with mock.patch('cephadm.list_daemons', return_value=list_daemons): | |
517 | infer_config(ctx) | |
518 | assert ctx.config == result | |
519 | ||
520 | ||
f91f0fd5 TL |
521 | class TestCustomContainer(unittest.TestCase): |
522 | cc: cd.CustomContainer | |
523 | ||
524 | def setUp(self): | |
525 | self.cc = cd.CustomContainer( | |
526 | 'e863154d-33c7-4350-bca5-921e0467e55b', | |
527 | 'container', | |
528 | config_json={ | |
529 | 'entrypoint': 'bash', | |
530 | 'gid': 1000, | |
531 | 'args': [ | |
532 | '--no-healthcheck', | |
533 | '-p 6800:6800' | |
534 | ], | |
535 | 'envs': ['SECRET=password'], | |
536 | 'ports': [8080, 8443], | |
537 | 'volume_mounts': { | |
538 | '/CONFIG_DIR': '/foo/conf', | |
539 | 'bar/config': '/bar:ro' | |
540 | }, | |
541 | 'bind_mounts': [ | |
542 | [ | |
543 | 'type=bind', | |
544 | 'source=/CONFIG_DIR', | |
545 | 'destination=/foo/conf', | |
546 | '' | |
547 | ], | |
548 | [ | |
549 | 'type=bind', | |
550 | 'source=bar/config', | |
551 | 'destination=/bar:ro', | |
552 | 'ro=true' | |
553 | ] | |
554 | ] | |
555 | }, | |
556 | image='docker.io/library/hello-world:latest' | |
557 | ) | |
558 | ||
559 | def test_entrypoint(self): | |
560 | self.assertEqual(self.cc.entrypoint, 'bash') | |
561 | ||
562 | def test_uid_gid(self): | |
563 | self.assertEqual(self.cc.uid, 65534) | |
564 | self.assertEqual(self.cc.gid, 1000) | |
565 | ||
566 | def test_ports(self): | |
567 | self.assertEqual(self.cc.ports, [8080, 8443]) | |
568 | ||
569 | def test_get_container_args(self): | |
570 | result = self.cc.get_container_args() | |
571 | self.assertEqual(result, [ | |
572 | '--no-healthcheck', | |
573 | '-p 6800:6800' | |
574 | ]) | |
575 | ||
576 | def test_get_container_envs(self): | |
577 | result = self.cc.get_container_envs() | |
578 | self.assertEqual(result, ['SECRET=password']) | |
579 | ||
580 | def test_get_container_mounts(self): | |
581 | result = self.cc.get_container_mounts('/xyz') | |
582 | self.assertDictEqual(result, { | |
583 | '/CONFIG_DIR': '/foo/conf', | |
584 | '/xyz/bar/config': '/bar:ro' | |
585 | }) | |
586 | ||
587 | def test_get_container_binds(self): | |
588 | result = self.cc.get_container_binds('/xyz') | |
589 | self.assertEqual(result, [ | |
590 | [ | |
591 | 'type=bind', | |
592 | 'source=/CONFIG_DIR', | |
593 | 'destination=/foo/conf', | |
594 | '' | |
595 | ], | |
596 | [ | |
597 | 'type=bind', | |
598 | 'source=/xyz/bar/config', | |
599 | 'destination=/bar:ro', | |
600 | 'ro=true' | |
601 | ] | |
602 | ]) | |
f67539c2 TL |
603 | |
604 | ||
605 | class TestCephadmExporter(object): | |
606 | exporter: cd.CephadmDaemon | |
607 | files_created: List[str] = [] | |
608 | crt = """-----BEGIN CERTIFICATE----- | |
609 | MIIC1zCCAb8CEFHoZE2MfUVzo53fzzBKAT0wDQYJKoZIhvcNAQENBQAwKjENMAsG | |
610 | A1UECgwEQ2VwaDEZMBcGA1UECwwQY2VwaGFkbS1leHBvcnRlcjAeFw0yMDExMjUy | |
611 | MzEwNTVaFw0zMDExMjMyMzEwNTVaMCoxDTALBgNVBAoMBENlcGgxGTAXBgNVBAsM | |
612 | EGNlcGhhZG0tZXhwb3J0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB | |
613 | AQCsTfcJcXbREqfx1zTUuEmK+lJn9WWjk0URRF1Z+QgPkascNdkX16PnvhbGwXmF | |
614 | BTdAcNl7V0U+z4EsGJ7hJsB7qTq6Rb6wNl7r0OxjeWOmB9xbF4Q/KR5yrbM1DA9A | |
615 | B5fNswrUXViku5Y2jlOAz+ZMBhYxMx0edqhxSn297j04Z6RF4Mvkc43v0FH7Ju7k | |
616 | O5+0VbdzcOdu37DFpoE4Ll2MZ/GuAHcJ8SD06sEdzFEjRCraav976743XcUlhZGX | |
617 | ZTTG/Zf/a+wuCjtMG3od7vRFfuRrM5oTE133DuQ5deR7ybcZNDyopDjHF8xB1bAk | |
618 | IOz4SbP6Q25K99Czm1K+3kMLAgMBAAEwDQYJKoZIhvcNAQENBQADggEBACmtvZb8 | |
619 | dJGHx/WC0/JHxnEJCJM2qnn87ELzbbIQL1w1Yb/I6JQYPgq+WiQPaHaLL9eYsm0l | |
620 | dFwvrh+WC0JpXDfADnUnkTSB/WpZ2nC+2JxBptrQEuIcqNXpcJd0bKDiHunv04JI | |
621 | uEVpTAK05dBV38qNmIlu4HyB4OEnuQpyOr9xpIhdxuJ95O9K0j5BIw98ZaEwYNUP | |
622 | Rm3YlQwfS6R5xaBvL9kyfxyAD2joNj44q6w/5zj4egXVIA5VpkQm8DmMtu0Pd2NG | |
623 | dzfYRmqrDolh+rty8HiyIxzeDJQ5bj6LKbUkmABvX50nDySVyMfHmt461/n7W65R | |
624 | CHFLoOmfJJik+Uc=\n-----END CERTIFICATE----- | |
625 | """ | |
626 | key = """-----BEGIN PRIVATE KEY----- | |
627 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCsTfcJcXbREqfx | |
628 | 1zTUuEmK+lJn9WWjk0URRF1Z+QgPkascNdkX16PnvhbGwXmFBTdAcNl7V0U+z4Es | |
629 | GJ7hJsB7qTq6Rb6wNl7r0OxjeWOmB9xbF4Q/KR5yrbM1DA9AB5fNswrUXViku5Y2 | |
630 | jlOAz+ZMBhYxMx0edqhxSn297j04Z6RF4Mvkc43v0FH7Ju7kO5+0VbdzcOdu37DF | |
631 | poE4Ll2MZ/GuAHcJ8SD06sEdzFEjRCraav976743XcUlhZGXZTTG/Zf/a+wuCjtM | |
632 | G3od7vRFfuRrM5oTE133DuQ5deR7ybcZNDyopDjHF8xB1bAkIOz4SbP6Q25K99Cz | |
633 | m1K+3kMLAgMBAAECggEASnAwToMXWsGdjqxzpYasNv9oBIOO0nk4OHp5ffpJUjiT | |
634 | XM+ip1tA80g7HMjPD/mt4gge3NtaDgWlf4Bve0O7mnEE7x5cgFIs9eG/jkYOF9eD | |
635 | ilMBjivcfJywNDWujPH60iIMhqyBNEHaZl1ck+S9UJC8m6rCZLvMj40n/5riFfBy | |
636 | 1sjf2uOwcfWrjSj9Ju4wlMI6khSSz2aYC7glQQ/fo2+YArbEUcy60iloPQ6wEgZK | |
637 | okoVWZA9AehwLcnRjkwd9EVmMMtRGPE/AcP4s/kKA0tRDRicPLN727Ke/yxv+Ppo | |
638 | hbIZIcOn7soOFAENcodJ4YRSCd++QfCNaVAi7vwWWQKBgQDeBY4vvr+H0brbSjQg | |
639 | O7Fpqub/fxZY3UoHWDqWs2X4o3qhDqaTQODpuYtCm8YQE//55JoLWKAD0evq5dLS | |
640 | YLrtC1Vyxf+TA7opCUjWBe+liyndbJdB5q0zF7qdWUtQKGVSWyUWhK8gHa6M64fP | |
641 | oi83DD7F0OGusTWGtfbceErk/wKBgQDGrJLRo/5xnAH5VmPfNu+S6h0M2qM6CYwe | |
642 | Y5wHFG2uQQct73adf53SkhvZVmOzJsWQbVnlDOKMhqazcs+7VWRgO5X3naWVcctE | |
643 | Hggw9MgpbXAWFOI5sNYsCYE58E+fTHjE6O4A3MhMCsze+CIC3sKuPQBBiL9bWSOX | |
644 | 8POswqfl9QKBgDe/nVxPwTgRaaH2l/AgDQRDbY1qE+psZlJBzTRaB5jPM9ONIjaH | |
645 | a/JELLuk8a7H1tagmC2RK1zKMTriSnWY5FbxKZuQLAR2QyBavHdBNlOTBggbZD+f | |
646 | 9I2Hv8wSx95wxkBPsphc6Lxft5ya55czWjewU3LIaGK9DHuu5TWm3udxAoGBAJGP | |
647 | PsJ59KIoOwoDUYjpJv3sqPwR9CVBeXeKY3aMcQ+KdUgiejVKmsb8ZYsG0GUhsv3u | |
648 | ID7BAfsTbG9tXuVR2wjmnymcRwUHKnXtyvKTZVN06vpCsryx4zjAff2FI9ECpjke | |
649 | r8HSAK41+4QhKEoSC3C9IMLi/dBfrsRTtTSOKZVBAoGBAI2dl5HEIFpufaI4toWM | |
650 | LO5HFrlXgRDGoc/+Byr5/8ZZpYpU115Ol/q6M+l0koV2ygJ9jeJJEllFWykIDS6F | |
651 | XxazFI74swAqobHb2ZS/SLhoVxE82DdSeXrjkTvUjNtrW5zs1gIMKBR4nD6H8AqL | |
652 | iMN28C2bKGao5UHvdER1rGy7 | |
653 | -----END PRIVATE KEY----- | |
654 | """ | |
655 | token = "MyAccessToken" | |
656 | ||
657 | @classmethod | |
658 | def setup_class(cls): | |
659 | # create the ssl files | |
660 | fname = os.path.join(os.getcwd(), 'crt') | |
661 | with open(fname, 'w') as crt: | |
662 | crt.write(cls.crt) | |
663 | cls.files_created.append(fname) | |
664 | fname = os.path.join(os.getcwd(), 'key') | |
665 | with open(fname, 'w') as crt: | |
666 | crt.write(cls.key) | |
667 | cls.files_created.append(fname) | |
668 | fname = os.path.join(os.getcwd(), 'token') | |
669 | with open(fname, 'w') as crt: | |
670 | crt.write(cls.token) | |
671 | cls.files_created.append(fname) | |
672 | # start a simple http instance to test the requesthandler | |
673 | cls.server = HTTPServer(('0.0.0.0', 9443), cd.CephadmDaemonHandler) | |
674 | cls.server.cephadm_cache = cd.CephadmCache() | |
675 | cls.server.token = cls.token | |
676 | t = threading.Thread(target=cls.server.serve_forever) | |
677 | t.daemon = True | |
678 | t.start() | |
679 | ||
680 | @classmethod | |
681 | def teardown_class(cls): | |
682 | cls.server.shutdown() | |
683 | assert len(cls.files_created) > 0 | |
684 | for f in cls.files_created: | |
685 | os.remove(f) | |
686 | ||
687 | def setup_method(self): | |
688 | # re-init the cache for every test | |
689 | TestCephadmExporter.server.cephadm_cache = cd.CephadmCache() | |
690 | ||
691 | def teardown_method(self): | |
692 | pass | |
693 | ||
694 | def test_files_ready(self): | |
695 | assert os.path.exists(os.path.join(os.getcwd(), 'crt')) | |
696 | assert os.path.exists(os.path.join(os.getcwd(), 'key')) | |
697 | assert os.path.exists(os.path.join(os.getcwd(), 'token')) | |
698 | ||
699 | def test_can_run(self, exporter): | |
700 | assert exporter.can_run | |
701 | ||
702 | def test_token_valid(self, exporter): | |
703 | assert exporter.token == self.token | |
704 | ||
705 | def test_unit_name(self,exporter): | |
706 | assert exporter.unit_name | |
707 | assert exporter.unit_name == "ceph-foobar-cephadm-exporter.test.service" | |
708 | ||
709 | def test_unit_run(self,exporter): | |
710 | assert exporter.unit_run | |
711 | lines = exporter.unit_run.split('\n') | |
712 | assert len(lines) == 2 | |
713 | assert "cephadm exporter --fsid foobar --id test --port 9443 &" in lines[1] | |
714 | ||
715 | def test_binary_path(self, exporter): | |
716 | assert os.path.isfile(exporter.binary_path) | |
717 | ||
718 | def test_systemd_unit(self, exporter): | |
719 | assert exporter.unit_file | |
720 | ||
721 | def test_validate_passes(self, exporter): | |
722 | config = { | |
723 | "crt": self.crt, | |
724 | "key": self.key, | |
725 | "token": self.token, | |
726 | } | |
727 | cd.CephadmDaemon.validate_config(config) | |
728 | ||
729 | def test_validate_fails(self, exporter): | |
730 | config = { | |
731 | "key": self.key, | |
732 | "token": self.token, | |
733 | } | |
734 | with pytest.raises(cd.Error): | |
735 | cd.CephadmDaemon.validate_config(config) | |
736 | ||
737 | def test_port_active(self, exporter): | |
738 | assert exporter.port_active == True | |
739 | ||
740 | def test_rqst_health_200(self): | |
741 | hdrs={"Authorization":f"Bearer {TestCephadmExporter.token}"} | |
742 | req=Request("http://localhost:9443/v1/metadata/health",headers=hdrs) | |
743 | r = urlopen(req) | |
744 | assert r.status == 200 | |
745 | ||
746 | def test_rqst_all_inactive_500(self): | |
747 | hdrs={"Authorization":f"Bearer {TestCephadmExporter.token}"} | |
748 | req=Request("http://localhost:9443/v1/metadata",headers=hdrs) | |
749 | try: | |
750 | r = urlopen(req) | |
751 | except HTTPError as e: | |
752 | assert e.code == 500 | |
753 | ||
754 | def test_rqst_no_auth_401(self): | |
755 | req=Request("http://localhost:9443/v1/metadata") | |
756 | try: | |
757 | urlopen(req) | |
758 | except HTTPError as e: | |
759 | assert e.code == 401 | |
760 | ||
761 | def test_rqst_bad_auth_401(self): | |
762 | hdrs={"Authorization":f"Bearer BogusAuthToken"} | |
763 | req=Request("http://localhost:9443/v1/metadata",headers=hdrs) | |
764 | try: | |
765 | urlopen(req) | |
766 | except HTTPError as e: | |
767 | assert e.code == 401 | |
768 | ||
769 | def test_rqst_badURL_404(self): | |
770 | hdrs={"Authorization":f"Bearer {TestCephadmExporter.token}"} | |
771 | req=Request("http://localhost:9443/v1/metazoic",headers=hdrs) | |
772 | try: | |
773 | urlopen(req) | |
774 | except HTTPError as e: | |
775 | assert e.code == 404 | |
776 | ||
777 | def test_rqst_inactive_task_204(self): | |
778 | # all tasks initialise as inactive, and then 'go' active as their thread starts | |
779 | # so we can pick any task to check for an inactive response (no content) | |
780 | hdrs={"Authorization":f"Bearer {TestCephadmExporter.token}"} | |
781 | req=Request("http://localhost:9443/v1/metadata/disks",headers=hdrs) | |
782 | r = urlopen(req) | |
783 | assert r.status == 204 | |
784 | ||
785 | def test_rqst_active_task_200(self): | |
786 | TestCephadmExporter.server.cephadm_cache.tasks['host'] = 'active' | |
787 | hdrs={"Authorization":f"Bearer {TestCephadmExporter.token}"} | |
788 | req=Request("http://localhost:9443/v1/metadata/host",headers=hdrs) | |
789 | r = urlopen(req) | |
790 | assert r.status == 200 | |
791 | ||
792 | def test_rqst_all_206(self): | |
793 | TestCephadmExporter.server.cephadm_cache.tasks['disks'] = 'active' | |
794 | hdrs={"Authorization":f"Bearer {TestCephadmExporter.token}"} | |
795 | req=Request("http://localhost:9443/v1/metadata",headers=hdrs) | |
796 | r = urlopen(req) | |
797 | assert r.status == 206 | |
798 | ||
799 | def test_rqst_disks_200(self): | |
800 | TestCephadmExporter.server.cephadm_cache.tasks['disks'] = 'active' | |
801 | hdrs={"Authorization":f"Bearer {TestCephadmExporter.token}"} | |
802 | req=Request("http://localhost:9443/v1/metadata/disks",headers=hdrs) | |
803 | r = urlopen(req) | |
804 | assert r.status == 200 | |
805 | ||
806 | def test_thread_exception(self, exporter): | |
807 | # run is patched to invoke a mocked scrape_host thread that will raise so | |
808 | # we check here that the exception handler updates the cache object as we'd | |
809 | # expect with the error | |
810 | exporter.run() | |
811 | assert exporter.cephadm_cache.host['scrape_errors'] | |
812 | assert exporter.cephadm_cache.host['scrape_errors'] == ['ValueError exception: wah'] | |
813 | assert exporter.cephadm_cache.errors == ['host thread stopped'] | |
814 | ||
815 | # Test the requesthandler does the right thing with invalid methods... | |
816 | # ie. return a "501" - Not Implemented / Unsupported Method | |
817 | def test_invalid_method_HEAD(self): | |
818 | hdrs={"Authorization":f"Bearer {TestCephadmExporter.token}"} | |
819 | req=Request("http://localhost:9443/v1/metadata/health",headers=hdrs, method="HEAD") | |
820 | with pytest.raises(HTTPError, match=r"HTTP Error 501: .*") as e: | |
821 | urlopen(req) | |
822 | ||
823 | def test_invalid_method_DELETE(self): | |
824 | hdrs={"Authorization":f"Bearer {TestCephadmExporter.token}"} | |
825 | req=Request("http://localhost:9443/v1/metadata/health",headers=hdrs, method="DELETE") | |
826 | with pytest.raises(HTTPError, match=r"HTTP Error 501: .*") as e: | |
827 | urlopen(req) | |
828 | ||
829 | def test_invalid_method_POST(self): | |
830 | hdrs={"Authorization":f"Bearer {TestCephadmExporter.token}"} | |
831 | req=Request("http://localhost:9443/v1/metadata/health",headers=hdrs, method="POST") | |
832 | with pytest.raises(HTTPError, match=r"HTTP Error 501: .*") as e: | |
833 | urlopen(req) | |
834 | ||
835 | def test_invalid_method_PUT(self): | |
836 | hdrs={"Authorization":f"Bearer {TestCephadmExporter.token}"} | |
837 | req=Request("http://localhost:9443/v1/metadata/health",headers=hdrs, method="PUT") | |
838 | with pytest.raises(HTTPError, match=r"HTTP Error 501: .*") as e: | |
839 | urlopen(req) | |
840 | ||
841 | def test_invalid_method_CONNECT(self): | |
842 | hdrs={"Authorization":f"Bearer {TestCephadmExporter.token}"} | |
843 | req=Request("http://localhost:9443/v1/metadata/health",headers=hdrs, method="CONNECT") | |
844 | with pytest.raises(HTTPError, match=r"HTTP Error 501: .*") as e: | |
845 | urlopen(req) | |
846 | ||
847 | def test_invalid_method_TRACE(self): | |
848 | hdrs={"Authorization":f"Bearer {TestCephadmExporter.token}"} | |
849 | req=Request("http://localhost:9443/v1/metadata/health",headers=hdrs, method="TRACE") | |
850 | with pytest.raises(HTTPError, match=r"HTTP Error 501: .*") as e: | |
851 | urlopen(req) | |
852 | ||
853 | def test_invalid_method_OPTIONS(self): | |
854 | hdrs={"Authorization":f"Bearer {TestCephadmExporter.token}"} | |
855 | req=Request("http://localhost:9443/v1/metadata/health",headers=hdrs, method="OPTIONS") | |
856 | with pytest.raises(HTTPError, match=r"HTTP Error 501: .*") as e: | |
857 | urlopen(req) | |
858 | ||
859 | def test_invalid_method_PATCH(self): | |
860 | hdrs={"Authorization":f"Bearer {TestCephadmExporter.token}"} | |
861 | req=Request("http://localhost:9443/v1/metadata/health",headers=hdrs, method="PATCH") | |
862 | with pytest.raises(HTTPError, match=r"HTTP Error 501: .*") as e: | |
863 | urlopen(req) | |
864 | ||
865 | def test_ipv4_subnet(self): | |
866 | rc, v, msg = cd.check_subnet('192.168.1.0/24') | |
867 | assert rc == 0 and v[0] == 4 | |
868 | ||
869 | def test_ipv4_subnet_list(self): | |
870 | rc, v, msg = cd.check_subnet('192.168.1.0/24,10.90.90.0/24') | |
871 | assert rc == 0 and not msg | |
872 | ||
873 | def test_ipv4_subnet_badlist(self): | |
874 | rc, v, msg = cd.check_subnet('192.168.1.0/24,192.168.1.1') | |
875 | assert rc == 1 and msg | |
876 | ||
877 | def test_ipv4_subnet_mixed(self): | |
878 | rc, v, msg = cd.check_subnet('192.168.100.0/24,fe80::/64') | |
879 | assert rc == 0 and v == [4,6] | |
880 | ||
881 | def test_ipv6_subnet(self): | |
882 | rc, v, msg = cd.check_subnet('fe80::/64') | |
883 | assert rc == 0 and v[0] == 6 | |
884 | ||
885 | def test_subnet_mask_missing(self): | |
886 | rc, v, msg = cd.check_subnet('192.168.1.58') | |
887 | assert rc == 1 and msg | |
888 | ||
889 | def test_subnet_mask_junk(self): | |
890 | rc, v, msg = cd.check_subnet('wah') | |
891 | assert rc == 1 and msg | |
892 | ||
893 | ||
894 | class TestMaintenance: | |
895 | systemd_target = "ceph.00000000-0000-0000-0000-000000c0ffee.target" | |
896 | ||
897 | def test_systemd_target_OK(self, tmp_path): | |
898 | base = tmp_path | |
899 | wants = base / "ceph.target.wants" | |
900 | wants.mkdir() | |
901 | target = wants / TestMaintenance.systemd_target | |
902 | target.touch() | |
903 | cd.UNIT_DIR = str(base) | |
904 | ||
905 | assert cd.systemd_target_state(target.name) | |
906 | ||
907 | def test_systemd_target_NOTOK(self, tmp_path): | |
908 | base = tmp_path | |
909 | cd.UNIT_DIR = str(base) | |
910 | assert not cd.systemd_target_state(TestMaintenance.systemd_target) | |
911 | ||
912 | def test_parser_OK(self): | |
913 | args = cd._parse_args(['host-maintenance', 'enter']) | |
914 | assert args.maintenance_action == 'enter' | |
915 | ||
916 | def test_parser_BAD(self): | |
917 | with pytest.raises(SystemExit): | |
918 | cd._parse_args(['host-maintenance', 'wah']) | |
919 | ||
920 | ||
921 | class TestMonitoring(object): | |
922 | @mock.patch('cephadm.call') | |
923 | def test_get_version_alertmanager(self, _call): | |
522d829b TL |
924 | ctx = cd.CephadmContext() |
925 | ctx.container_engine = mock_podman() | |
f67539c2 TL |
926 | daemon_type = 'alertmanager' |
927 | ||
928 | # binary `prometheus` | |
929 | _call.return_value = '', '{}, version 0.16.1'.format(daemon_type), 0 | |
930 | version = cd.Monitoring.get_version(ctx, 'container_id', daemon_type) | |
931 | assert version == '0.16.1' | |
932 | ||
933 | # binary `prometheus-alertmanager` | |
934 | _call.side_effect = ( | |
935 | ('', '', 1), | |
936 | ('', '{}, version 0.16.1'.format(daemon_type), 0), | |
937 | ) | |
938 | version = cd.Monitoring.get_version(ctx, 'container_id', daemon_type) | |
939 | assert version == '0.16.1' | |
940 | ||
941 | @mock.patch('cephadm.call') | |
942 | def test_get_version_prometheus(self, _call): | |
522d829b TL |
943 | ctx = cd.CephadmContext() |
944 | ctx.container_engine = mock_podman() | |
f67539c2 TL |
945 | daemon_type = 'prometheus' |
946 | _call.return_value = '', '{}, version 0.16.1'.format(daemon_type), 0 | |
947 | version = cd.Monitoring.get_version(ctx, 'container_id', daemon_type) | |
948 | assert version == '0.16.1' | |
949 | ||
950 | @mock.patch('cephadm.call') | |
951 | def test_get_version_node_exporter(self, _call): | |
522d829b TL |
952 | ctx = cd.CephadmContext() |
953 | ctx.container_engine = mock_podman() | |
f67539c2 TL |
954 | daemon_type = 'node-exporter' |
955 | _call.return_value = '', '{}, version 0.16.1'.format(daemon_type.replace('-', '_')), 0 | |
956 | version = cd.Monitoring.get_version(ctx, 'container_id', daemon_type) | |
957 | assert version == '0.16.1' | |
b3b6e05e | 958 | |
522d829b | 959 | def test_create_daemon_dirs_prometheus(self, cephadm_fs): |
b3b6e05e TL |
960 | """ |
961 | Ensures the required and optional files given in the configuration are | |
962 | created and mapped correctly inside the container. Tests absolute and | |
963 | relative file paths given in the configuration. | |
964 | """ | |
965 | ||
966 | fsid = 'aaf5a720-13fe-4a3b-82b9-2d99b7fd9704' | |
967 | daemon_type = 'prometheus' | |
968 | uid, gid = 50, 50 | |
969 | daemon_id = 'home' | |
522d829b | 970 | ctx = cd.CephadmContext() |
b3b6e05e | 971 | ctx.data_dir = '/somedir' |
522d829b | 972 | ctx.config_json = json.dumps({ |
b3b6e05e TL |
973 | 'files': { |
974 | 'prometheus.yml': 'foo', | |
975 | '/etc/prometheus/alerting/ceph_alerts.yml': 'bar' | |
976 | } | |
522d829b | 977 | }) |
b3b6e05e TL |
978 | |
979 | cd.create_daemon_dirs(ctx, | |
980 | fsid, | |
981 | daemon_type, | |
982 | daemon_id, | |
983 | uid, | |
984 | gid, | |
985 | config=None, | |
986 | keyring=None) | |
987 | ||
988 | prefix = '{data_dir}/{fsid}/{daemon_type}.{daemon_id}'.format( | |
989 | data_dir=ctx.data_dir, | |
990 | fsid=fsid, | |
991 | daemon_type=daemon_type, | |
992 | daemon_id=daemon_id | |
993 | ) | |
522d829b TL |
994 | |
995 | expected = { | |
996 | 'etc/prometheus/prometheus.yml': 'foo', | |
997 | 'etc/prometheus/alerting/ceph_alerts.yml': 'bar', | |
998 | } | |
999 | ||
1000 | for file,content in expected.items(): | |
1001 | file = os.path.join(prefix, file) | |
1002 | assert os.path.exists(file) | |
1003 | with open(file) as f: | |
1004 | assert f.read() == content | |
b3b6e05e TL |
1005 | |
1006 | ||
1007 | class TestBootstrap(object): | |
1008 | ||
1009 | @staticmethod | |
1010 | def _get_cmd(*args): | |
1011 | return [ | |
1012 | 'bootstrap', | |
1013 | '--allow-mismatched-release', | |
1014 | '--skip-prepare-host', | |
1015 | '--skip-dashboard', | |
1016 | *args, | |
1017 | ] | |
1018 | ||
1019 | def test_config(self, cephadm_fs): | |
1020 | conf_file = 'foo' | |
1021 | cmd = self._get_cmd( | |
1022 | '--mon-ip', '192.168.1.1', | |
1023 | '--skip-mon-network', | |
1024 | '--config', conf_file, | |
1025 | ) | |
1026 | ||
1027 | with with_cephadm_ctx(cmd) as ctx: | |
1028 | msg = r'No such file or directory' | |
1029 | with pytest.raises(cd.Error, match=msg): | |
1030 | cd.command_bootstrap(ctx) | |
1031 | ||
1032 | cephadm_fs.create_file(conf_file) | |
1033 | with with_cephadm_ctx(cmd) as ctx: | |
1034 | retval = cd.command_bootstrap(ctx) | |
1035 | assert retval == 0 | |
1036 | ||
1037 | def test_no_mon_addr(self, cephadm_fs): | |
1038 | cmd = self._get_cmd() | |
1039 | with with_cephadm_ctx(cmd) as ctx: | |
1040 | msg = r'must specify --mon-ip or --mon-addrv' | |
1041 | with pytest.raises(cd.Error, match=msg): | |
1042 | cd.command_bootstrap(ctx) | |
1043 | ||
1044 | def test_skip_mon_network(self, cephadm_fs): | |
1045 | cmd = self._get_cmd('--mon-ip', '192.168.1.1') | |
1046 | ||
1047 | with with_cephadm_ctx(cmd, list_networks={}) as ctx: | |
1048 | msg = r'--skip-mon-network' | |
1049 | with pytest.raises(cd.Error, match=msg): | |
1050 | cd.command_bootstrap(ctx) | |
1051 | ||
1052 | cmd += ['--skip-mon-network'] | |
1053 | with with_cephadm_ctx(cmd, list_networks={}) as ctx: | |
1054 | retval = cd.command_bootstrap(ctx) | |
1055 | assert retval == 0 | |
1056 | ||
1057 | @pytest.mark.parametrize('mon_ip, list_networks, result', | |
1058 | [ | |
1059 | # IPv4 | |
1060 | ( | |
1061 | 'eth0', | |
1062 | {'192.168.1.0/24': {'eth0': ['192.168.1.1']}}, | |
1063 | False, | |
1064 | ), | |
1065 | ( | |
1066 | '0.0.0.0', | |
1067 | {'192.168.1.0/24': {'eth0': ['192.168.1.1']}}, | |
1068 | False, | |
1069 | ), | |
1070 | ( | |
1071 | '192.168.1.0', | |
1072 | {'192.168.1.0/24': {'eth0': ['192.168.1.1']}}, | |
1073 | False, | |
1074 | ), | |
1075 | ( | |
1076 | '192.168.1.1', | |
1077 | {'192.168.1.0/24': {'eth0': ['192.168.1.1']}}, | |
1078 | True, | |
1079 | ), | |
1080 | ( | |
1081 | '192.168.1.1:1234', | |
1082 | {'192.168.1.0/24': {'eth0': ['192.168.1.1']}}, | |
1083 | True, | |
1084 | ), | |
522d829b TL |
1085 | ( |
1086 | '192.168.1.1:0123', | |
1087 | {'192.168.1.0/24': {'eth0': ['192.168.1.1']}}, | |
1088 | True, | |
1089 | ), | |
b3b6e05e TL |
1090 | # IPv6 |
1091 | ( | |
1092 | '::', | |
1093 | {'192.168.1.0/24': {'eth0': ['192.168.1.1']}}, | |
1094 | False, | |
1095 | ), | |
1096 | ( | |
1097 | '::ffff:192.168.1.0', | |
1098 | {"ffff::/64": {"eth0": ["::ffff:c0a8:101"]}}, | |
1099 | False, | |
1100 | ), | |
1101 | ( | |
1102 | '::ffff:192.168.1.1', | |
1103 | {"ffff::/64": {"eth0": ["::ffff:c0a8:101"]}}, | |
1104 | True, | |
1105 | ), | |
1106 | ( | |
1107 | '::ffff:c0a8:101', | |
1108 | {"ffff::/64": {"eth0": ["::ffff:c0a8:101"]}}, | |
1109 | True, | |
1110 | ), | |
522d829b TL |
1111 | ( |
1112 | '[::ffff:c0a8:101]:1234', | |
1113 | {"ffff::/64": {"eth0": ["::ffff:c0a8:101"]}}, | |
1114 | True, | |
1115 | ), | |
1116 | ( | |
1117 | '[::ffff:c0a8:101]:0123', | |
1118 | {"ffff::/64": {"eth0": ["::ffff:c0a8:101"]}}, | |
1119 | True, | |
1120 | ), | |
b3b6e05e TL |
1121 | ( |
1122 | '0000:0000:0000:0000:0000:FFFF:C0A8:0101', | |
1123 | {"ffff::/64": {"eth0": ["::ffff:c0a8:101"]}}, | |
1124 | True, | |
1125 | ), | |
1126 | ]) | |
1127 | def test_mon_ip(self, mon_ip, list_networks, result, cephadm_fs): | |
1128 | cmd = self._get_cmd('--mon-ip', mon_ip) | |
1129 | if not result: | |
1130 | with with_cephadm_ctx(cmd, list_networks=list_networks) as ctx: | |
1131 | msg = r'--skip-mon-network' | |
1132 | with pytest.raises(cd.Error, match=msg): | |
1133 | cd.command_bootstrap(ctx) | |
1134 | else: | |
1135 | with with_cephadm_ctx(cmd, list_networks=list_networks) as ctx: | |
1136 | retval = cd.command_bootstrap(ctx) | |
1137 | assert retval == 0 | |
1138 | ||
522d829b TL |
1139 | @pytest.mark.parametrize('mon_addrv, list_networks, err', |
1140 | [ | |
1141 | # IPv4 | |
1142 | ( | |
1143 | '192.168.1.1', | |
1144 | {'192.168.1.0/24': {'eth0': ['192.168.1.1']}}, | |
1145 | r'must use square backets', | |
1146 | ), | |
1147 | ( | |
1148 | '[192.168.1.1]', | |
1149 | {'192.168.1.0/24': {'eth0': ['192.168.1.1']}}, | |
1150 | r'must include port number', | |
1151 | ), | |
1152 | ( | |
1153 | '[192.168.1.1:1234]', | |
1154 | {'192.168.1.0/24': {'eth0': ['192.168.1.1']}}, | |
1155 | None, | |
1156 | ), | |
1157 | ( | |
1158 | '[192.168.1.1:0123]', | |
1159 | {'192.168.1.0/24': {'eth0': ['192.168.1.1']}}, | |
1160 | None, | |
1161 | ), | |
1162 | ( | |
1163 | '[v2:192.168.1.1:3300,v1:192.168.1.1:6789]', | |
1164 | {'192.168.1.0/24': {'eth0': ['192.168.1.1']}}, | |
1165 | None, | |
1166 | ), | |
1167 | # IPv6 | |
1168 | ( | |
1169 | '[::ffff:192.168.1.1:1234]', | |
1170 | {'ffff::/64': {'eth0': ['::ffff:c0a8:101']}}, | |
1171 | None, | |
1172 | ), | |
1173 | ( | |
1174 | '[::ffff:192.168.1.1:0123]', | |
1175 | {'ffff::/64': {'eth0': ['::ffff:c0a8:101']}}, | |
1176 | None, | |
1177 | ), | |
1178 | ( | |
1179 | '[0000:0000:0000:0000:0000:FFFF:C0A8:0101:1234]', | |
1180 | {'ffff::/64': {'eth0': ['::ffff:c0a8:101']}}, | |
1181 | None, | |
1182 | ), | |
1183 | ( | |
1184 | '[v2:0000:0000:0000:0000:0000:FFFF:C0A8:0101:3300,v1:0000:0000:0000:0000:0000:FFFF:C0A8:0101:6789]', | |
1185 | {'ffff::/64': {'eth0': ['::ffff:c0a8:101']}}, | |
1186 | None, | |
1187 | ), | |
1188 | ]) | |
1189 | def test_mon_addrv(self, mon_addrv, list_networks, err, cephadm_fs): | |
1190 | cmd = self._get_cmd('--mon-addrv', mon_addrv) | |
1191 | if err: | |
1192 | with with_cephadm_ctx(cmd, list_networks=list_networks) as ctx: | |
1193 | with pytest.raises(cd.Error, match=err): | |
1194 | cd.command_bootstrap(ctx) | |
1195 | else: | |
1196 | with with_cephadm_ctx(cmd, list_networks=list_networks) as ctx: | |
1197 | retval = cd.command_bootstrap(ctx) | |
1198 | assert retval == 0 | |
1199 | ||
b3b6e05e TL |
1200 | def test_allow_fqdn_hostname(self, cephadm_fs): |
1201 | hostname = 'foo.bar' | |
1202 | cmd = self._get_cmd( | |
1203 | '--mon-ip', '192.168.1.1', | |
1204 | '--skip-mon-network', | |
1205 | ) | |
1206 | ||
1207 | with with_cephadm_ctx(cmd, hostname=hostname) as ctx: | |
1208 | msg = r'--allow-fqdn-hostname' | |
1209 | with pytest.raises(cd.Error, match=msg): | |
1210 | cd.command_bootstrap(ctx) | |
1211 | ||
1212 | cmd += ['--allow-fqdn-hostname'] | |
1213 | with with_cephadm_ctx(cmd, hostname=hostname) as ctx: | |
1214 | retval = cd.command_bootstrap(ctx) | |
1215 | assert retval == 0 | |
1216 | ||
1217 | @pytest.mark.parametrize('fsid, err', | |
1218 | [ | |
1219 | ('', None), | |
1220 | ('00000000-0000-0000-0000-0000deadbeef', None), | |
1221 | ('00000000-0000-0000-0000-0000deadbeez', 'not an fsid'), | |
1222 | ]) | |
1223 | def test_fsid(self, fsid, err, cephadm_fs): | |
1224 | cmd = self._get_cmd( | |
1225 | '--mon-ip', '192.168.1.1', | |
1226 | '--skip-mon-network', | |
1227 | '--fsid', fsid, | |
1228 | ) | |
1229 | ||
1230 | with with_cephadm_ctx(cmd) as ctx: | |
1231 | if err: | |
1232 | with pytest.raises(cd.Error, match=err): | |
1233 | cd.command_bootstrap(ctx) | |
1234 | else: | |
1235 | retval = cd.command_bootstrap(ctx) | |
1236 | assert retval == 0 | |
522d829b TL |
1237 | |
1238 | ||
1239 | class TestShell(object): | |
1240 | ||
1241 | def test_fsid(self, cephadm_fs): | |
1242 | fsid = '00000000-0000-0000-0000-0000deadbeef' | |
1243 | ||
1244 | cmd = ['shell', '--fsid', fsid] | |
1245 | with with_cephadm_ctx(cmd) as ctx: | |
1246 | retval = cd.command_shell(ctx) | |
1247 | assert retval == 0 | |
1248 | assert ctx.fsid == fsid | |
1249 | ||
1250 | cmd = ['shell', '--fsid', '00000000-0000-0000-0000-0000deadbeez'] | |
1251 | with with_cephadm_ctx(cmd) as ctx: | |
1252 | err = 'not an fsid' | |
1253 | with pytest.raises(cd.Error, match=err): | |
1254 | retval = cd.command_shell(ctx) | |
1255 | assert retval == 1 | |
1256 | assert ctx.fsid == None | |
1257 | ||
1258 | s = get_ceph_conf(fsid=fsid) | |
1259 | f = cephadm_fs.create_file('ceph.conf', contents=s) | |
1260 | ||
1261 | cmd = ['shell', '--fsid', fsid, '--config', f.path] | |
1262 | with with_cephadm_ctx(cmd) as ctx: | |
1263 | retval = cd.command_shell(ctx) | |
1264 | assert retval == 0 | |
1265 | assert ctx.fsid == fsid | |
1266 | ||
1267 | cmd = ['shell', '--fsid', '10000000-0000-0000-0000-0000deadbeef', '--config', f.path] | |
1268 | with with_cephadm_ctx(cmd) as ctx: | |
1269 | err = 'fsid does not match ceph.conf' | |
1270 | with pytest.raises(cd.Error, match=err): | |
1271 | retval = cd.command_shell(ctx) | |
1272 | assert retval == 1 | |
1273 | assert ctx.fsid == None | |
1274 | ||
1275 | def test_name(self, cephadm_fs): | |
1276 | cmd = ['shell', '--name', 'foo'] | |
1277 | with with_cephadm_ctx(cmd) as ctx: | |
1278 | retval = cd.command_shell(ctx) | |
1279 | assert retval == 0 | |
1280 | ||
1281 | cmd = ['shell', '--name', 'foo.bar'] | |
1282 | with with_cephadm_ctx(cmd) as ctx: | |
1283 | err = r'must pass --fsid' | |
1284 | with pytest.raises(cd.Error, match=err): | |
1285 | retval = cd.command_shell(ctx) | |
1286 | assert retval == 1 | |
1287 | ||
1288 | fsid = '00000000-0000-0000-0000-0000deadbeef' | |
1289 | cmd = ['shell', '--name', 'foo.bar', '--fsid', fsid] | |
1290 | with with_cephadm_ctx(cmd) as ctx: | |
1291 | retval = cd.command_shell(ctx) | |
1292 | assert retval == 0 | |
1293 | ||
1294 | def test_config(self, cephadm_fs): | |
1295 | cmd = ['shell'] | |
1296 | with with_cephadm_ctx(cmd) as ctx: | |
1297 | retval = cd.command_shell(ctx) | |
1298 | assert retval == 0 | |
1299 | assert ctx.config == None | |
1300 | ||
1301 | cephadm_fs.create_file(cd.SHELL_DEFAULT_CONF) | |
1302 | with with_cephadm_ctx(cmd) as ctx: | |
1303 | retval = cd.command_shell(ctx) | |
1304 | assert retval == 0 | |
1305 | assert ctx.config == cd.SHELL_DEFAULT_CONF | |
1306 | ||
1307 | cmd = ['shell', '--config', 'foo'] | |
1308 | with with_cephadm_ctx(cmd) as ctx: | |
1309 | retval = cd.command_shell(ctx) | |
1310 | assert retval == 0 | |
1311 | assert ctx.config == 'foo' | |
1312 | ||
1313 | def test_keyring(self, cephadm_fs): | |
1314 | cmd = ['shell'] | |
1315 | with with_cephadm_ctx(cmd) as ctx: | |
1316 | retval = cd.command_shell(ctx) | |
1317 | assert retval == 0 | |
1318 | assert ctx.keyring == None | |
1319 | ||
1320 | cephadm_fs.create_file(cd.SHELL_DEFAULT_KEYRING) | |
1321 | with with_cephadm_ctx(cmd) as ctx: | |
1322 | retval = cd.command_shell(ctx) | |
1323 | assert retval == 0 | |
1324 | assert ctx.keyring == cd.SHELL_DEFAULT_KEYRING | |
1325 | ||
1326 | cmd = ['shell', '--keyring', 'foo'] | |
1327 | with with_cephadm_ctx(cmd) as ctx: | |
1328 | retval = cd.command_shell(ctx) | |
1329 | assert retval == 0 | |
1330 | assert ctx.keyring == 'foo' | |
1331 | ||
1332 | ||
1333 | class TestCephVolume(object): | |
1334 | ||
1335 | @staticmethod | |
1336 | def _get_cmd(*args): | |
1337 | return [ | |
1338 | 'ceph-volume', | |
1339 | *args, | |
1340 | '--', 'inventory', '--format', 'json' | |
1341 | ] | |
1342 | ||
1343 | def test_noop(self, cephadm_fs): | |
1344 | cmd = self._get_cmd() | |
1345 | with with_cephadm_ctx(cmd) as ctx: | |
1346 | cd.command_ceph_volume(ctx) | |
1347 | assert ctx.fsid == None | |
1348 | assert ctx.config == None | |
1349 | assert ctx.keyring == None | |
1350 | assert ctx.config_json == None | |
1351 | ||
1352 | def test_fsid(self, cephadm_fs): | |
1353 | fsid = '00000000-0000-0000-0000-0000deadbeef' | |
1354 | ||
1355 | cmd = self._get_cmd('--fsid', fsid) | |
1356 | with with_cephadm_ctx(cmd) as ctx: | |
1357 | cd.command_ceph_volume(ctx) | |
1358 | assert ctx.fsid == fsid | |
1359 | ||
1360 | cmd = self._get_cmd('--fsid', '00000000-0000-0000-0000-0000deadbeez') | |
1361 | with with_cephadm_ctx(cmd) as ctx: | |
1362 | err = 'not an fsid' | |
1363 | with pytest.raises(cd.Error, match=err): | |
1364 | retval = cd.command_shell(ctx) | |
1365 | assert retval == 1 | |
1366 | assert ctx.fsid == None | |
1367 | ||
1368 | s = get_ceph_conf(fsid=fsid) | |
1369 | f = cephadm_fs.create_file('ceph.conf', contents=s) | |
1370 | ||
1371 | cmd = self._get_cmd('--fsid', fsid, '--config', f.path) | |
1372 | with with_cephadm_ctx(cmd) as ctx: | |
1373 | cd.command_ceph_volume(ctx) | |
1374 | assert ctx.fsid == fsid | |
1375 | ||
1376 | cmd = self._get_cmd('--fsid', '10000000-0000-0000-0000-0000deadbeef', '--config', f.path) | |
1377 | with with_cephadm_ctx(cmd) as ctx: | |
1378 | err = 'fsid does not match ceph.conf' | |
1379 | with pytest.raises(cd.Error, match=err): | |
1380 | cd.command_ceph_volume(ctx) | |
1381 | assert ctx.fsid == None | |
1382 | ||
1383 | def test_config(self, cephadm_fs): | |
1384 | cmd = self._get_cmd('--config', 'foo') | |
1385 | with with_cephadm_ctx(cmd) as ctx: | |
1386 | err = r'No such file or directory' | |
1387 | with pytest.raises(cd.Error, match=err): | |
1388 | cd.command_ceph_volume(ctx) | |
1389 | ||
1390 | cephadm_fs.create_file('bar') | |
1391 | cmd = self._get_cmd('--config', 'bar') | |
1392 | with with_cephadm_ctx(cmd) as ctx: | |
1393 | cd.command_ceph_volume(ctx) | |
1394 | assert ctx.config == 'bar' | |
1395 | ||
1396 | def test_keyring(self, cephadm_fs): | |
1397 | cmd = self._get_cmd('--keyring', 'foo') | |
1398 | with with_cephadm_ctx(cmd) as ctx: | |
1399 | err = r'No such file or directory' | |
1400 | with pytest.raises(cd.Error, match=err): | |
1401 | cd.command_ceph_volume(ctx) | |
1402 | ||
1403 | cephadm_fs.create_file('bar') | |
1404 | cmd = self._get_cmd('--keyring', 'bar') | |
1405 | with with_cephadm_ctx(cmd) as ctx: | |
1406 | cd.command_ceph_volume(ctx) | |
1407 | assert ctx.keyring == 'bar' | |
1408 | ||
1409 | ||
1410 | class TestIscsi: | |
1411 | def test_unit_run(self, cephadm_fs): | |
1412 | fsid = '9b9d7609-f4d5-4aba-94c8-effa764d96c9' | |
1413 | config_json = { | |
1414 | 'files': {'iscsi-gateway.cfg': ''} | |
1415 | } | |
1416 | with with_cephadm_ctx(['--image=ceph/ceph'], list_networks={}) as ctx: | |
1417 | import json | |
1418 | ctx.config_json = json.dumps(config_json) | |
1419 | ctx.fsid = fsid | |
1420 | cd.get_parm.return_value = config_json | |
1421 | iscsi = cd.CephIscsi(ctx, '9b9d7609-f4d5-4aba-94c8-effa764d96c9', 'daemon_id', config_json) | |
1422 | c = iscsi.get_tcmu_runner_container() | |
1423 | ||
1424 | cd.make_data_dir(ctx, fsid, 'iscsi', 'daemon_id') | |
1425 | cd.deploy_daemon_units( | |
1426 | ctx, | |
1427 | fsid, | |
1428 | 0, 0, | |
1429 | 'iscsi', | |
1430 | 'daemon_id', | |
1431 | c, | |
1432 | True, True | |
1433 | ) | |
1434 | ||
1435 | with open('/var/lib/ceph/9b9d7609-f4d5-4aba-94c8-effa764d96c9/iscsi.daemon_id/unit.run') as f: | |
1436 | assert f.read() == """set -e | |
1437 | if ! grep -qs /var/lib/ceph/9b9d7609-f4d5-4aba-94c8-effa764d96c9/iscsi.daemon_id/configfs /proc/mounts; then mount -t configfs none /var/lib/ceph/9b9d7609-f4d5-4aba-94c8-effa764d96c9/iscsi.daemon_id/configfs; fi | |
1438 | # iscsi tcmu-runnter container | |
1439 | ! /usr/bin/podman rm -f ceph-9b9d7609-f4d5-4aba-94c8-effa764d96c9-iscsi.daemon_id-tcmu 2> /dev/null | |
1440 | ! /usr/bin/podman rm -f ceph-9b9d7609-f4d5-4aba-94c8-effa764d96c9-iscsi-daemon_id-tcmu 2> /dev/null | |
1441 | /usr/bin/podman run --rm --ipc=host --stop-signal=SIGTERM --net=host --entrypoint /usr/bin/tcmu-runner --privileged --group-add=disk --init --name ceph-9b9d7609-f4d5-4aba-94c8-effa764d96c9-iscsi-daemon_id-tcmu -e CONTAINER_IMAGE=ceph/ceph -e NODE_NAME=host1 -e CEPH_USE_RANDOM_NONCE=1 -v /var/lib/ceph/9b9d7609-f4d5-4aba-94c8-effa764d96c9/iscsi.daemon_id/config:/etc/ceph/ceph.conf:z -v /var/lib/ceph/9b9d7609-f4d5-4aba-94c8-effa764d96c9/iscsi.daemon_id/keyring:/etc/ceph/keyring:z -v /var/lib/ceph/9b9d7609-f4d5-4aba-94c8-effa764d96c9/iscsi.daemon_id/iscsi-gateway.cfg:/etc/ceph/iscsi-gateway.cfg:z -v /var/lib/ceph/9b9d7609-f4d5-4aba-94c8-effa764d96c9/iscsi.daemon_id/configfs:/sys/kernel/config -v /var/log/ceph/9b9d7609-f4d5-4aba-94c8-effa764d96c9:/var/log/rbd-target-api:z -v /dev:/dev --mount type=bind,source=/lib/modules,destination=/lib/modules,ro=true ceph/ceph & | |
1442 | # iscsi.daemon_id | |
1443 | ! /usr/bin/podman rm -f ceph-9b9d7609-f4d5-4aba-94c8-effa764d96c9-iscsi.daemon_id-tcmu 2> /dev/null | |
1444 | ! /usr/bin/podman rm -f ceph-9b9d7609-f4d5-4aba-94c8-effa764d96c9-iscsi-daemon_id-tcmu 2> /dev/null | |
1445 | /usr/bin/podman run --rm --ipc=host --stop-signal=SIGTERM --net=host --entrypoint /usr/bin/tcmu-runner --privileged --group-add=disk --init --name ceph-9b9d7609-f4d5-4aba-94c8-effa764d96c9-iscsi-daemon_id-tcmu -e CONTAINER_IMAGE=ceph/ceph -e NODE_NAME=host1 -e CEPH_USE_RANDOM_NONCE=1 -v /var/lib/ceph/9b9d7609-f4d5-4aba-94c8-effa764d96c9/iscsi.daemon_id/config:/etc/ceph/ceph.conf:z -v /var/lib/ceph/9b9d7609-f4d5-4aba-94c8-effa764d96c9/iscsi.daemon_id/keyring:/etc/ceph/keyring:z -v /var/lib/ceph/9b9d7609-f4d5-4aba-94c8-effa764d96c9/iscsi.daemon_id/iscsi-gateway.cfg:/etc/ceph/iscsi-gateway.cfg:z -v /var/lib/ceph/9b9d7609-f4d5-4aba-94c8-effa764d96c9/iscsi.daemon_id/configfs:/sys/kernel/config -v /var/log/ceph/9b9d7609-f4d5-4aba-94c8-effa764d96c9:/var/log/rbd-target-api:z -v /dev:/dev --mount type=bind,source=/lib/modules,destination=/lib/modules,ro=true ceph/ceph | |
1446 | """ | |
1447 | ||
1448 | def test_get_container(self): | |
1449 | """ | |
1450 | Due to a combination of socket.getfqdn() and podman's behavior to | |
1451 | add the container name into the /etc/hosts file, we cannot use periods | |
1452 | in container names. But we need to be able to detect old existing containers. | |
1453 | Assert this behaviour. I think we can remove this in Ceph R | |
1454 | """ | |
1455 | fsid = '9b9d7609-f4d5-4aba-94c8-effa764d96c9' | |
1456 | with with_cephadm_ctx(['--image=ceph/ceph'], list_networks={}) as ctx: | |
1457 | ctx.fsid = fsid | |
1458 | c = cd.get_container(ctx, fsid, 'iscsi', 'something') | |
1459 | assert c.cname == 'ceph-9b9d7609-f4d5-4aba-94c8-effa764d96c9-iscsi-something' | |
1460 | assert c.old_cname == 'ceph-9b9d7609-f4d5-4aba-94c8-effa764d96c9-iscsi.something' | |
1461 | ||
1462 | ||
1463 |