10 from textwrap
import dedent
12 from .fixtures
import (
21 from pyfakefs
import fake_filesystem
22 from pyfakefs
import fake_filesystem_unittest
24 _cephadm
= import_cephadm()
28 fsid
='00000000-0000-0000-0000-0000deadbeef',
29 mon_host
='[v2:192.168.1.1:3300/0,v1:192.168.1.1:6789/0]'):
31 # minimal ceph.conf for {fsid}
37 class TestCephAdm(object):
39 def test_docker_unit_file(self
):
40 ctx
= _cephadm
.CephadmContext()
41 ctx
.container_engine
= mock_docker()
42 r
= _cephadm
.get_unit_file(ctx
, '9b9d7609-f4d5-4aba-94c8-effa764d96c9')
43 assert 'Requires=docker.service' in r
44 ctx
.container_engine
= mock_podman()
45 r
= _cephadm
.get_unit_file(ctx
, '9b9d7609-f4d5-4aba-94c8-effa764d96c9')
46 assert 'Requires=docker.service' not in r
48 @mock.patch('cephadm.logger')
49 def test_attempt_bind(self
, _logger
):
56 _os_error
.errno
= errno
59 for side_effect
, expected_exception
in (
60 (os_error(errno
.EADDRINUSE
), _cephadm
.PortOccupiedError
),
61 (os_error(errno
.EAFNOSUPPORT
), _cephadm
.Error
),
62 (os_error(errno
.EADDRNOTAVAIL
), _cephadm
.Error
),
66 _socket
.bind
.side_effect
= side_effect
68 _cephadm
.attempt_bind(ctx
, _socket
, address
, port
)
69 except Exception as e
:
70 assert isinstance(e
, expected_exception
)
72 if expected_exception
is not None:
75 @mock.patch('cephadm.attempt_bind')
76 @mock.patch('cephadm.logger')
77 def test_port_in_use(self
, _logger
, _attempt_bind
):
80 assert _cephadm
.port_in_use(empty_ctx
, 9100) == False
82 _attempt_bind
.side_effect
= _cephadm
.PortOccupiedError('msg')
83 assert _cephadm
.port_in_use(empty_ctx
, 9100) == True
86 os_error
.errno
= errno
.EADDRNOTAVAIL
87 _attempt_bind
.side_effect
= os_error
88 assert _cephadm
.port_in_use(empty_ctx
, 9100) == False
91 os_error
.errno
= errno
.EAFNOSUPPORT
92 _attempt_bind
.side_effect
= os_error
93 assert _cephadm
.port_in_use(empty_ctx
, 9100) == False
95 @mock.patch('socket.socket')
96 @mock.patch('cephadm.logger')
97 def test_check_ip_port_success(self
, _logger
, _socket
):
98 ctx
= _cephadm
.CephadmContext()
99 ctx
.skip_ping_check
= False # enables executing port check with `check_ip_port`
101 for address
, address_family
in (
102 ('0.0.0.0', socket
.AF_INET
),
103 ('::', socket
.AF_INET6
),
106 _cephadm
.check_ip_port(ctx
, _cephadm
.EndPoint(address
, 9100))
110 assert _socket
.call_args
== mock
.call(address_family
, socket
.SOCK_STREAM
)
112 @mock.patch('socket.socket')
113 @mock.patch('cephadm.logger')
114 def test_check_ip_port_failure(self
, _logger
, _socket
):
115 ctx
= _cephadm
.CephadmContext()
116 ctx
.skip_ping_check
= False # enables executing port check with `check_ip_port`
119 _os_error
= OSError()
120 _os_error
.errno
= errno
123 for address
, address_family
in (
124 ('0.0.0.0', socket
.AF_INET
),
125 ('::', socket
.AF_INET6
),
127 for side_effect
, expected_exception
in (
128 (os_error(errno
.EADDRINUSE
), _cephadm
.PortOccupiedError
),
129 (os_error(errno
.EADDRNOTAVAIL
), _cephadm
.Error
),
130 (os_error(errno
.EAFNOSUPPORT
), _cephadm
.Error
),
133 mock_socket_obj
= mock
.Mock()
134 mock_socket_obj
.bind
.side_effect
= side_effect
135 _socket
.return_value
= mock_socket_obj
137 _cephadm
.check_ip_port(ctx
, _cephadm
.EndPoint(address
, 9100))
138 except Exception as e
:
139 assert isinstance(e
, expected_exception
)
141 if side_effect
is not None:
145 def test_is_not_fsid(self
):
146 assert not _cephadm
.is_fsid('no-uuid')
148 def test_is_fsid(self
):
149 assert _cephadm
.is_fsid('e863154d-33c7-4350-bca5-921e0467e55b')
151 def test__get_parser_image(self
):
152 args
= _cephadm
._parse
_args
(['--image', 'foo', 'version'])
153 assert args
.image
== 'foo'
155 def test_check_required_global_args(self
):
156 ctx
= _cephadm
.CephadmContext()
157 mock_fn
= mock
.Mock()
158 mock_fn
.return_value
= 0
159 require_image
= _cephadm
.require_image(mock_fn
)
161 with pytest
.raises(_cephadm
.Error
, match
='This command requires the global --image option to be set'):
164 ctx
.image
= 'sample-image'
167 @mock.patch('cephadm.logger')
168 def test_parse_mem_usage(self
, _logger
):
169 len, summary
= _cephadm
._parse
_mem
_usage
(0, 'c6290e3f1489,-- / --')
172 def test_CustomValidation(self
):
173 assert _cephadm
._parse
_args
(['deploy', '--name', 'mon.a', '--fsid', 'fsid'])
175 with pytest
.raises(SystemExit):
176 _cephadm
._parse
_args
(['deploy', '--name', 'wrong', '--fsid', 'fsid'])
178 @pytest.mark
.parametrize("test_input, expected", [
180 ("1.6.2-stable2", (1,6,2)),
182 def test_parse_podman_version(self
, test_input
, expected
):
183 assert _cephadm
._parse
_podman
_version
(test_input
) == expected
185 def test_parse_podman_version_invalid(self
):
186 with pytest
.raises(ValueError) as res
:
187 _cephadm
._parse
_podman
_version
('inval.id')
188 assert 'inval' in str(res
.value
)
190 @mock.patch('cephadm.logger')
191 def test_is_ipv6(self
, _logger
):
192 for good
in ("[::1]", "::1",
193 "fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"):
194 assert _cephadm
.is_ipv6(good
)
195 for bad
in ("127.0.0.1",
196 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffg",
197 "1:2:3:4:5:6:7:8:9", "fd00::1::1", "[fg::1]"):
198 assert not _cephadm
.is_ipv6(bad
)
200 def test_unwrap_ipv6(self
):
201 def unwrap_test(address
, expected
):
202 assert _cephadm
.unwrap_ipv6(address
) == expected
205 ('::1', '::1'), ('[::1]', '::1'),
206 ('[fde4:8dba:82e1:0:5054:ff:fe6a:357]', 'fde4:8dba:82e1:0:5054:ff:fe6a:357'),
207 ('can actually be any string', 'can actually be any string'),
208 ('[but needs to be stripped] ', '[but needs to be stripped] ')]
209 for address
, expected
in tests
:
210 unwrap_test(address
, expected
)
212 def test_wrap_ipv6(self
):
213 def wrap_test(address
, expected
):
214 assert _cephadm
.wrap_ipv6(address
) == expected
217 ('::1', '[::1]'), ('[::1]', '[::1]'),
218 ('fde4:8dba:82e1:0:5054:ff:fe6a:357',
219 '[fde4:8dba:82e1:0:5054:ff:fe6a:357]'),
220 ('myhost.example.com', 'myhost.example.com'),
221 ('192.168.0.1', '192.168.0.1'),
222 ('', ''), ('fd00::1::1', 'fd00::1::1')]
223 for address
, expected
in tests
:
224 wrap_test(address
, expected
)
226 @mock.patch('cephadm.Firewalld', mock_bad_firewalld
)
227 @mock.patch('cephadm.logger')
228 def test_skip_firewalld(self
, _logger
, cephadm_fs
):
230 test --skip-firewalld actually skips changing firewall
233 ctx
= _cephadm
.CephadmContext()
234 with pytest
.raises(Exception):
235 _cephadm
.update_firewalld(ctx
, 'mon')
237 ctx
.skip_firewalld
= True
238 _cephadm
.update_firewalld(ctx
, 'mon')
240 ctx
.skip_firewalld
= False
241 with pytest
.raises(Exception):
242 _cephadm
.update_firewalld(ctx
, 'mon')
244 ctx
= _cephadm
.CephadmContext()
245 ctx
.ssl_dashboard_port
= 8888
246 ctx
.dashboard_key
= None
247 ctx
.dashboard_password_noupdate
= True
248 ctx
.initial_dashboard_password
= 'password'
249 ctx
.initial_dashboard_user
= 'User'
250 with pytest
.raises(Exception):
251 _cephadm
.prepare_dashboard(ctx
, 0, 0, lambda _
, extra_mounts
=None, ___
=None : '5', lambda : None)
253 ctx
.skip_firewalld
= True
254 _cephadm
.prepare_dashboard(ctx
, 0, 0, lambda _
, extra_mounts
=None, ___
=None : '5', lambda : None)
256 ctx
.skip_firewalld
= False
257 with pytest
.raises(Exception):
258 _cephadm
.prepare_dashboard(ctx
, 0, 0, lambda _
, extra_mounts
=None, ___
=None : '5', lambda : None)
260 @mock.patch('cephadm.logger')
261 @mock.patch('cephadm.get_custom_config_files')
262 @mock.patch('cephadm.get_container')
263 def test_get_deployment_container(self
, _get_container
, _get_config
, _logger
):
265 test get_deployment_container properly makes use of extra container args and custom conf files
268 ctx
= _cephadm
.CephadmContext()
269 ctx
.config_json
= '-'
270 ctx
.extra_container_args
= [
271 '--pids-limit=12345',
274 ctx
.data_dir
= 'data'
275 _get_config
.return_value
= {'custom_config_files': [
277 'mount_path': '/etc/testing.str',
278 'content': 'this\nis\na\nstring',
281 _get_container
.return_value
= _cephadm
.CephContainer
.for_daemon(
283 fsid
='9b9d7609-f4d5-4aba-94c8-effa764d96c9',
284 daemon_type
='grafana',
296 c
= _cephadm
.get_deployment_container(ctx
,
297 '9b9d7609-f4d5-4aba-94c8-effa764d96c9',
301 assert '--pids-limit=12345' in c
.container_args
302 assert '--something' in c
.container_args
303 assert os
.path
.join('data', '9b9d7609-f4d5-4aba-94c8-effa764d96c9', 'custom_config_files', 'grafana.host1', 'testing.str') in c
.volume_mounts
304 assert c
.volume_mounts
[os
.path
.join('data', '9b9d7609-f4d5-4aba-94c8-effa764d96c9', 'custom_config_files', 'grafana.host1', 'testing.str')] == '/etc/testing.str'
306 @mock.patch('cephadm.logger')
307 @mock.patch('cephadm.FileLock')
308 @mock.patch('cephadm.deploy_daemon')
309 @mock.patch('cephadm.get_parm')
310 @mock.patch('cephadm.make_var_run')
311 @mock.patch('cephadm.migrate_sysctl_dir')
312 @mock.patch('cephadm.check_unit', lambda *args
, **kwargs
: (None, 'running', None))
313 @mock.patch('cephadm.get_unit_name', lambda *args
, **kwargs
: 'mon-unit-name')
314 @mock.patch('cephadm.get_deployment_container')
315 def test_mon_crush_location(self
, _get_deployment_container
, _migrate_sysctl
, _make_var_run
, _get_parm
, _deploy_daemon
, _file_lock
, _logger
):
317 test that crush location for mon is set if it is included in config_json
320 ctx
= _cephadm
.CephadmContext()
321 ctx
.name
= 'mon.test'
322 ctx
.fsid
= '9b9d7609-f4d5-4aba-94c8-effa764d96c9'
324 ctx
.container_engine
= mock_docker()
325 ctx
.allow_ptrace
= True
326 ctx
.config_json
= '-'
328 _get_parm
.return_value
= {
329 'crush_location': 'database=a'
332 _get_deployment_container
.return_value
= _cephadm
.CephContainer
.for_daemon(
334 fsid
='9b9d7609-f4d5-4aba-94c8-effa764d96c9',
348 def _crush_location_checker(ctx
, fsid
, daemon_type
, daemon_id
, container
, uid
, gid
, **kwargs
):
349 print(container
.args
)
350 raise Exception(' '.join(container
.args
))
352 _deploy_daemon
.side_effect
= _crush_location_checker
354 with pytest
.raises(Exception, match
='--set-crush-location database=a'):
355 _cephadm
.command_deploy(ctx
)
357 @mock.patch('cephadm.logger')
358 @mock.patch('cephadm.get_custom_config_files')
359 def test_write_custom_conf_files(self
, _get_config
, _logger
, cephadm_fs
):
361 test _write_custom_conf_files writes the conf files correctly
364 ctx
= _cephadm
.CephadmContext()
365 ctx
.config_json
= '-'
366 ctx
.data_dir
= _cephadm
.DATA_DIR
367 _get_config
.return_value
= {'custom_config_files': [
369 'mount_path': '/etc/testing.str',
370 'content': 'this\nis\na\nstring',
373 'mount_path': '/etc/testing.conf',
374 'content': 'very_cool_conf_setting: very_cool_conf_value\nx: y',
377 'mount_path': '/etc/no-content.conf',
380 _cephadm
._write
_custom
_conf
_files
(ctx
, 'mon', 'host1', 'fsid', 0, 0)
381 with
open(os
.path
.join(_cephadm
.DATA_DIR
, 'fsid', 'custom_config_files', 'mon.host1', 'testing.str'), 'r') as f
:
382 assert 'this\nis\na\nstring' == f
.read()
383 with
open(os
.path
.join(_cephadm
.DATA_DIR
, 'fsid', 'custom_config_files', 'mon.host1', 'testing.conf'), 'r') as f
:
384 assert 'very_cool_conf_setting: very_cool_conf_value\nx: y' == f
.read()
385 with pytest
.raises(FileNotFoundError
):
386 open(os
.path
.join(_cephadm
.DATA_DIR
, 'fsid', 'custom_config_files', 'mon.host1', 'no-content.conf'), 'r')
388 @mock.patch('cephadm.call_throws')
389 @mock.patch('cephadm.get_parm')
390 @mock.patch('cephadm.logger')
391 def test_registry_login(self
, _logger
, _get_parm
, _call_throws
):
392 # test normal valid login with url, username and password specified
393 _call_throws
.return_value
= '', '', 0
394 ctx
: _cephadm
.CephadmContext
= _cephadm
.cephadm_init_ctx(
395 ['registry-login', '--registry-url', 'sample-url',
396 '--registry-username', 'sample-user', '--registry-password',
398 ctx
.container_engine
= mock_docker()
399 retval
= _cephadm
.command_registry_login(ctx
)
402 # test bad login attempt with invalid arguments given
403 ctx
: _cephadm
.CephadmContext
= _cephadm
.cephadm_init_ctx(
404 ['registry-login', '--registry-url', 'bad-args-url'])
405 with pytest
.raises(Exception) as e
:
406 assert _cephadm
.command_registry_login(ctx
)
407 assert str(e
.value
) == ('Invalid custom registry arguments received. To login to a custom registry include '
408 '--registry-url, --registry-username and --registry-password options or --registry-json option')
410 # test normal valid login with json file
411 _get_parm
.return_value
= {"url": "sample-url", "username": "sample-username", "password": "sample-password"}
412 ctx
: _cephadm
.CephadmContext
= _cephadm
.cephadm_init_ctx(
413 ['registry-login', '--registry-json', 'sample-json'])
414 ctx
.container_engine
= mock_docker()
415 retval
= _cephadm
.command_registry_login(ctx
)
418 # test bad login attempt with bad json file
419 _get_parm
.return_value
= {"bad-json": "bad-json"}
420 ctx
: _cephadm
.CephadmContext
= _cephadm
.cephadm_init_ctx(
421 ['registry-login', '--registry-json', 'sample-json'])
422 with pytest
.raises(Exception) as e
:
423 assert _cephadm
.command_registry_login(ctx
)
424 assert str(e
.value
) == ("json provided for custom registry login did not include all necessary fields. "
425 "Please setup json file as\n"
427 " \"url\": \"REGISTRY_URL\",\n"
428 " \"username\": \"REGISTRY_USERNAME\",\n"
429 " \"password\": \"REGISTRY_PASSWORD\"\n"
432 # test login attempt with valid arguments where login command fails
433 _call_throws
.side_effect
= Exception
434 ctx
: _cephadm
.CephadmContext
= _cephadm
.cephadm_init_ctx(
435 ['registry-login', '--registry-url', 'sample-url',
436 '--registry-username', 'sample-user', '--registry-password',
438 with pytest
.raises(Exception) as e
:
439 _cephadm
.command_registry_login(ctx
)
440 assert str(e
.value
) == "Failed to login to custom registry @ sample-url as sample-user with given password"
442 def test_get_image_info_from_inspect(self
):
444 out
= """204a01f9b0b6710dd0c0af7f37ce7139c47ff0f0105d778d7104c69282dfbbf1,[docker.io/ceph/ceph@sha256:1cc9b824e1b076cdff52a9aa3f0cc8557d879fb2fbbba0cafed970aca59a3992]"""
445 r
= _cephadm
.get_image_info_from_inspect(out
, 'registry/ceph/ceph:latest')
448 'image_id': '204a01f9b0b6710dd0c0af7f37ce7139c47ff0f0105d778d7104c69282dfbbf1',
449 'repo_digests': ['docker.io/ceph/ceph@sha256:1cc9b824e1b076cdff52a9aa3f0cc8557d879fb2fbbba0cafed970aca59a3992']
453 out
= """sha256:16f4549cf7a8f112bbebf7946749e961fbbd1b0838627fe619aab16bc17ce552,[quay.ceph.io/ceph-ci/ceph@sha256:4e13da36c1bd6780b312a985410ae678984c37e6a9493a74c87e4a50b9bda41f]"""
454 r
= _cephadm
.get_image_info_from_inspect(out
, 'registry/ceph/ceph:latest')
456 'image_id': '16f4549cf7a8f112bbebf7946749e961fbbd1b0838627fe619aab16bc17ce552',
457 'repo_digests': ['quay.ceph.io/ceph-ci/ceph@sha256:4e13da36c1bd6780b312a985410ae678984c37e6a9493a74c87e4a50b9bda41f']
460 # multiple digests (podman)
461 out
= """e935122ab143a64d92ed1fbb27d030cf6e2f0258207be1baf1b509c466aeeb42,[docker.io/prom/prometheus@sha256:e4ca62c0d62f3e886e684806dfe9d4e0cda60d54986898173c1083856cfda0f4 docker.io/prom/prometheus@sha256:efd99a6be65885c07c559679a0df4ec709604bcdd8cd83f0d00a1a683b28fb6a]"""
462 r
= _cephadm
.get_image_info_from_inspect(out
, 'registry/prom/prometheus:latest')
464 'image_id': 'e935122ab143a64d92ed1fbb27d030cf6e2f0258207be1baf1b509c466aeeb42',
466 'docker.io/prom/prometheus@sha256:e4ca62c0d62f3e886e684806dfe9d4e0cda60d54986898173c1083856cfda0f4',
467 'docker.io/prom/prometheus@sha256:efd99a6be65885c07c559679a0df4ec709604bcdd8cd83f0d00a1a683b28fb6a',
472 def test_dict_get(self
):
473 result
= _cephadm
.dict_get({'a': 1}, 'a', require
=True)
475 result
= _cephadm
.dict_get({'a': 1}, 'b')
476 assert result
is None
477 result
= _cephadm
.dict_get({'a': 1}, 'b', default
=2)
480 def test_dict_get_error(self
):
481 with pytest
.raises(_cephadm
.Error
):
482 _cephadm
.dict_get({'a': 1}, 'b', require
=True)
484 def test_dict_get_join(self
):
485 result
= _cephadm
.dict_get_join({'foo': ['a', 'b']}, 'foo')
486 assert result
== 'a\nb'
487 result
= _cephadm
.dict_get_join({'foo': [1, 2]}, 'foo')
488 assert result
== '1\n2'
489 result
= _cephadm
.dict_get_join({'bar': 'a'}, 'bar')
491 result
= _cephadm
.dict_get_join({'a': 1}, 'a')
494 @mock.patch('os.listdir', return_value
=[])
495 @mock.patch('cephadm.logger')
496 def test_infer_local_ceph_image(self
, _logger
, _listdir
):
497 ctx
= _cephadm
.CephadmContext()
498 ctx
.fsid
= '00000000-0000-0000-0000-0000deadbeez'
499 ctx
.container_engine
= mock_podman()
501 # make sure the right image is selected when container is found
502 cinfo
= _cephadm
.ContainerInfo('935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972',
503 'registry.hub.docker.com/rkachach/ceph:custom-v0.5',
504 '514e6a882f6e74806a5856468489eeff8d7106095557578da96935e4d0ba4d9d',
505 '2022-04-19 13:45:20.97146228 +0000 UTC',
507 out
= '''quay.ceph.io/ceph-ci/ceph@sha256:87f200536bb887b36b959e887d5984dd7a3f008a23aa1f283ab55d48b22c6185|dad864ee21e9|main|2022-03-23 16:29:19 +0000 UTC
508 quay.ceph.io/ceph-ci/ceph@sha256:b50b130fcda2a19f8507ddde3435bb4722266956e1858ac395c838bc1dcf1c0e|514e6a882f6e|pacific|2022-03-23 15:58:34 +0000 UTC
509 docker.io/ceph/ceph@sha256:939a46c06b334e094901560c8346de33c00309e3e3968a2db240eb4897c6a508|666bbfa87e8d|v15.2.5|2020-09-16 14:15:15 +0000 UTC'''
510 with mock
.patch('cephadm.call_throws', return_value
=(out
, '', '')):
511 with mock
.patch('cephadm.get_container_info', return_value
=cinfo
):
512 image
= _cephadm
.infer_local_ceph_image(ctx
, ctx
.container_engine
)
513 assert image
== 'quay.ceph.io/ceph-ci/ceph@sha256:b50b130fcda2a19f8507ddde3435bb4722266956e1858ac395c838bc1dcf1c0e'
515 # make sure first valid image is used when no container_info is found
516 out
= '''quay.ceph.io/ceph-ci/ceph@sha256:87f200536bb887b36b959e887d5984dd7a3f008a23aa1f283ab55d48b22c6185|dad864ee21e9|main|2022-03-23 16:29:19 +0000 UTC
517 quay.ceph.io/ceph-ci/ceph@sha256:b50b130fcda2a19f8507ddde3435bb4722266956e1858ac395c838bc1dcf1c0e|514e6a882f6e|pacific|2022-03-23 15:58:34 +0000 UTC
518 docker.io/ceph/ceph@sha256:939a46c06b334e094901560c8346de33c00309e3e3968a2db240eb4897c6a508|666bbfa87e8d|v15.2.5|2020-09-16 14:15:15 +0000 UTC'''
519 with mock
.patch('cephadm.call_throws', return_value
=(out
, '', '')):
520 with mock
.patch('cephadm.get_container_info', return_value
=None):
521 image
= _cephadm
.infer_local_ceph_image(ctx
, ctx
.container_engine
)
522 assert image
== 'quay.ceph.io/ceph-ci/ceph@sha256:87f200536bb887b36b959e887d5984dd7a3f008a23aa1f283ab55d48b22c6185'
524 # make sure images without digest are discarded (no container_info is found)
525 out
= '''quay.ceph.io/ceph-ci/ceph@|||
526 docker.io/ceph/ceph@|||
527 docker.io/ceph/ceph@sha256:939a46c06b334e094901560c8346de33c00309e3e3968a2db240eb4897c6a508|666bbfa87e8d|v15.2.5|2020-09-16 14:15:15 +0000 UTC'''
528 with mock
.patch('cephadm.call_throws', return_value
=(out
, '', '')):
529 with mock
.patch('cephadm.get_container_info', return_value
=None):
530 image
= _cephadm
.infer_local_ceph_image(ctx
, ctx
.container_engine
)
531 assert image
== 'docker.io/ceph/ceph@sha256:939a46c06b334e094901560c8346de33c00309e3e3968a2db240eb4897c6a508'
535 @pytest.mark
.parametrize('daemon_filter, by_name, daemon_list, container_stats, output',
537 # get container info by type ('mon')
542 {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
543 {'name': 'mgr.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
545 ("935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972,registry.hub.docker.com/rkachach/ceph:custom-v0.5,666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4,2022-04-19 13:45:20.97146228 +0000 UTC,",
548 _cephadm
.ContainerInfo('935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972',
549 'registry.hub.docker.com/rkachach/ceph:custom-v0.5',
550 '666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4',
551 '2022-04-19 13:45:20.97146228 +0000 UTC',
554 # get container info by name ('mon.ceph-node-0')
559 {'name': 'mgr.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
560 {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
562 ("935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972,registry.hub.docker.com/rkachach/ceph:custom-v0.5,666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4,2022-04-19 13:45:20.97146228 +0000 UTC,",
565 _cephadm
.ContainerInfo('935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972',
566 'registry.hub.docker.com/rkachach/ceph:custom-v0.5',
567 '666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4',
568 '2022-04-19 13:45:20.97146228 +0000 UTC',
571 # get container info by name (same daemon but two different fsids)
576 {'name': 'mon.ceph-node-0', 'fsid': '10000000-0000-0000-0000-0000deadbeef'},
577 {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
579 ("935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972,registry.hub.docker.com/rkachach/ceph:custom-v0.5,666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4,2022-04-19 13:45:20.97146228 +0000 UTC,",
582 _cephadm
.ContainerInfo('935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972',
583 'registry.hub.docker.com/rkachach/ceph:custom-v0.5',
584 '666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4',
585 '2022-04-19 13:45:20.97146228 +0000 UTC',
588 # get container info by type (bad container stats: 127 code)
593 {'name': 'mon.ceph-node-0', 'fsid': '00000000-FFFF-0000-0000-0000deadbeef'},
594 {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
601 # get container info by name (bad container stats: 127 code)
606 {'name': 'mgr.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
607 {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
614 # get container info by invalid name (doens't contain '.')
619 {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
620 {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
622 ("935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972,registry.hub.docker.com/rkachach/ceph:custom-v0.5,666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4,2022-04-19 13:45:20.97146228 +0000 UTC,",
627 # get container info by invalid name (empty)
632 {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
633 {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
635 ("935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972,registry.hub.docker.com/rkachach/ceph:custom-v0.5,666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4,2022-04-19 13:45:20.97146228 +0000 UTC,",
640 # get container info by invalid type (empty)
645 {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
646 {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
648 ("935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972,registry.hub.docker.com/rkachach/ceph:custom-v0.5,666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4,2022-04-19 13:45:20.97146228 +0000 UTC,",
653 # get container info by name: no match (invalid fsid)
658 {'name': 'mon.ceph-node-0', 'fsid': '00000000-1111-0000-0000-0000deadbeef'},
659 {'name': 'mon.ceph-node-0', 'fsid': '00000000-2222-0000-0000-0000deadbeef'},
661 ("935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972,registry.hub.docker.com/rkachach/ceph:custom-v0.5,666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4,2022-04-19 13:45:20.97146228 +0000 UTC,",
666 # get container info by name: no match
674 # get container info by type: no match
683 @mock.patch('cephadm.logger')
684 def test_get_container_info(self
, _logger
, daemon_filter
, by_name
, daemon_list
, container_stats
, output
):
685 ctx
= _cephadm
.CephadmContext()
686 ctx
.fsid
= '00000000-0000-0000-0000-0000deadbeef'
687 ctx
.container_engine
= mock_podman()
688 with mock
.patch('cephadm.list_daemons', return_value
=daemon_list
):
689 with mock
.patch('cephadm.get_container_stats', return_value
=container_stats
):
690 assert _cephadm
.get_container_info(ctx
, daemon_filter
, by_name
) == output
692 def test_should_log_to_journald(self
):
693 ctx
= _cephadm
.CephadmContext()
695 ctx
.log_to_journald
= True
696 assert _cephadm
.should_log_to_journald(ctx
)
698 ctx
.log_to_journald
= None
699 # enable if podman support --cgroup=split
700 ctx
.container_engine
= mock_podman()
701 ctx
.container_engine
.version
= (2, 1, 0)
702 assert _cephadm
.should_log_to_journald(ctx
)
704 # disable on old podman
705 ctx
.container_engine
.version
= (2, 0, 0)
706 assert not _cephadm
.should_log_to_journald(ctx
)
709 ctx
.container_engine
= mock_docker()
710 assert not _cephadm
.should_log_to_journald(ctx
)
712 def test_normalize_image_digest(self
):
713 s
= 'myhostname:5000/ceph/ceph@sha256:753886ad9049004395ae990fbb9b096923b5a518b819283141ee8716ddf55ad1'
714 assert _cephadm
.normalize_image_digest(s
) == s
716 s
= 'ceph/ceph:latest'
717 assert _cephadm
.normalize_image_digest(s
) == f
'{_cephadm.DEFAULT_REGISTRY}/{s}'
719 @pytest.mark
.parametrize('fsid, ceph_conf, list_daemons, result, err, ',
729 '00000000-0000-0000-0000-0000deadbeef',
732 '00000000-0000-0000-0000-0000deadbeef',
736 '00000000-0000-0000-0000-0000deadbeef',
739 {'fsid': '10000000-0000-0000-0000-0000deadbeef'},
740 {'fsid': '20000000-0000-0000-0000-0000deadbeef'},
742 '00000000-0000-0000-0000-0000deadbeef',
749 {'fsid': '00000000-0000-0000-0000-0000deadbeef'},
751 '00000000-0000-0000-0000-0000deadbeef',
758 {'fsid': '10000000-0000-0000-0000-0000deadbeef'},
759 {'fsid': '20000000-0000-0000-0000-0000deadbeef'},
762 r
'Cannot infer an fsid',
766 get_ceph_conf(fsid
='00000000-0000-0000-0000-0000deadbeef'),
768 '00000000-0000-0000-0000-0000deadbeef',
773 get_ceph_conf(fsid
='00000000-0000-0000-0000-0000deadbeef'),
775 {'fsid': '00000000-0000-0000-0000-0000deadbeef'},
777 '00000000-0000-0000-0000-0000deadbeef',
782 get_ceph_conf(fsid
='00000000-0000-0000-0000-0000deadbeef'),
784 {'fsid': '10000000-0000-0000-0000-0000deadbeef'},
785 {'fsid': '20000000-0000-0000-0000-0000deadbeef'},
788 r
'Cannot infer an fsid',
791 @mock.patch('cephadm.call')
792 @mock.patch('cephadm.logger')
793 def test_infer_fsid(self
, _logger
, _call
, fsid
, ceph_conf
, list_daemons
, result
, err
, cephadm_fs
):
795 ctx
= _cephadm
.CephadmContext()
799 mock_fn
= mock
.Mock()
800 mock_fn
.return_value
= 0
801 infer_fsid
= _cephadm
.infer_fsid(mock_fn
)
803 # mock the ceph.conf file content
805 f
= cephadm_fs
.create_file('ceph.conf', contents
=ceph_conf
)
809 with mock
.patch('cephadm.list_daemons', return_value
=list_daemons
):
811 with pytest
.raises(_cephadm
.Error
, match
=err
):
815 assert ctx
.fsid
== result
817 @pytest.mark
.parametrize('fsid, other_conf_files, config, name, list_daemons, result, ',
819 # per cluster conf has more precedence than default conf
821 '00000000-0000-0000-0000-0000deadbeef',
822 [_cephadm
.CEPH_DEFAULT_CONF
],
826 '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/config/ceph.conf',
828 # mon daemon conf has more precedence than cluster conf and default conf
830 '00000000-0000-0000-0000-0000deadbeef',
831 ['/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/config/ceph.conf',
832 _cephadm
.CEPH_DEFAULT_CONF
],
835 [{'name': 'mon.a', 'fsid': '00000000-0000-0000-0000-0000deadbeef', 'style': 'cephadm:v1'}],
836 '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/mon.a/config',
838 # daemon conf (--name option) has more precedence than cluster, default and mon conf
840 '00000000-0000-0000-0000-0000deadbeef',
841 ['/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/config/ceph.conf',
842 '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/mon.a/config',
843 _cephadm
.CEPH_DEFAULT_CONF
],
846 [{'name': 'mon.a', 'fsid': '00000000-0000-0000-0000-0000deadbeef', 'style': 'cephadm:v1'},
847 {'name': 'osd.0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'}],
848 '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/osd.0/config',
850 # user provided conf ('/foo/ceph.conf') more precedence than any other conf
852 '00000000-0000-0000-0000-0000deadbeef',
853 ['/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/config/ceph.conf',
854 _cephadm
.CEPH_DEFAULT_CONF
,
855 '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/mon.a/config'],
858 [{'name': 'mon.a', 'fsid': '00000000-0000-0000-0000-0000deadbeef', 'style': 'cephadm:v1'}],
862 @mock.patch('cephadm.call')
863 @mock.patch('cephadm.logger')
864 def test_infer_config_precedence(self
, _logger
, _call
, other_conf_files
, fsid
, config
, name
, list_daemons
, result
, cephadm_fs
):
866 ctx
= _cephadm
.CephadmContext()
872 mock_fn
= mock
.Mock()
873 mock_fn
.return_value
= 0
874 infer_config
= _cephadm
.infer_config(mock_fn
)
876 # mock the config file
877 cephadm_fs
.create_file(result
)
879 # mock other potential config files
880 for f
in other_conf_files
:
881 cephadm_fs
.create_file(f
)
884 with mock
.patch('cephadm.list_daemons', return_value
=list_daemons
):
886 assert ctx
.config
== result
888 @pytest.mark
.parametrize('fsid, config, name, list_daemons, result, ',
898 '00000000-0000-0000-0000-0000deadbeef',
902 _cephadm
.CEPH_DEFAULT_CONF
,
905 '00000000-0000-0000-0000-0000deadbeef',
909 '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/config/ceph.conf',
912 '00000000-0000-0000-0000-0000deadbeef',
915 [{'name': 'mon.a', 'fsid': '00000000-0000-0000-0000-0000deadbeef', 'style': 'cephadm:v1'}],
916 '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/mon.a/config',
919 '00000000-0000-0000-0000-0000deadbeef',
922 [{'name': 'mon.a', 'fsid': 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'style': 'cephadm:v1'}],
923 _cephadm
.CEPH_DEFAULT_CONF
,
926 '00000000-0000-0000-0000-0000deadbeef',
929 [{'name': 'mon.a', 'fsid': '00000000-0000-0000-0000-0000deadbeef', 'style': 'legacy'}],
930 _cephadm
.CEPH_DEFAULT_CONF
,
933 '00000000-0000-0000-0000-0000deadbeef',
937 _cephadm
.CEPH_DEFAULT_CONF
,
940 '00000000-0000-0000-0000-0000deadbeef',
943 [{'name': 'mon.a', 'style': 'cephadm:v1'}],
947 '00000000-0000-0000-0000-0000deadbeef',
951 '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/mon.a/config',
954 '00000000-0000-0000-0000-0000deadbeef',
958 '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/osd.0/config',
965 _cephadm
.CEPH_DEFAULT_CONF
,
968 @mock.patch('cephadm.call')
969 @mock.patch('cephadm.logger')
970 def test_infer_config(self
, _logger
, _call
, fsid
, config
, name
, list_daemons
, result
, cephadm_fs
):
972 ctx
= _cephadm
.CephadmContext()
978 mock_fn
= mock
.Mock()
979 mock_fn
.return_value
= 0
980 infer_config
= _cephadm
.infer_config(mock_fn
)
982 # mock the config file
983 cephadm_fs
.create_file(result
)
986 with mock
.patch('cephadm.list_daemons', return_value
=list_daemons
):
988 assert ctx
.config
== result
990 @mock.patch('cephadm.call')
991 def test_extract_uid_gid_fail(self
, _call
):
992 err
= """Error: container_linux.go:370: starting container process caused: process_linux.go:459: container init caused: process_linux.go:422: setting cgroup config for procHooks process caused: Unit libpod-056038e1126191fba41d8a037275136f2d7aeec9710b9ee
993 ff792c06d8544b983.scope not found.: OCI runtime error"""
994 _call
.return_value
= ('', err
, 127)
995 ctx
= _cephadm
.CephadmContext()
996 ctx
.container_engine
= mock_podman()
997 with pytest
.raises(_cephadm
.Error
, match
='OCI'):
998 _cephadm
.extract_uid_gid(ctx
)
1000 @pytest.mark
.parametrize('test_input, expected', [
1001 ([_cephadm
.make_fsid(), _cephadm
.make_fsid(), _cephadm
.make_fsid()], 3),
1002 ([_cephadm
.make_fsid(), 'invalid-fsid', _cephadm
.make_fsid(), '0b87e50c-8e77-11ec-b890-'], 2),
1003 (['f6860ec2-8e76-11ec-', '0b87e50c-8e77-11ec-b890-', ''], 0),
1006 def test_get_ceph_cluster_count(self
, test_input
, expected
):
1007 ctx
= _cephadm
.CephadmContext()
1008 with mock
.patch('os.listdir', return_value
=test_input
):
1009 assert _cephadm
.get_ceph_cluster_count(ctx
) == expected
1011 def test_set_image_minimize_config(self
):
1013 raise _cephadm
.Error(' '.join(cmd
))
1014 ctx
= _cephadm
.CephadmContext()
1015 ctx
.image
= 'test_image'
1016 ctx
.no_minimize_config
= True
1017 fake_cli
= lambda cmd
, __
=None, ___
=None: throw_cmd(cmd
)
1018 with pytest
.raises(_cephadm
.Error
, match
='config set global container_image test_image'):
1019 _cephadm
.finish_bootstrap_config(
1021 fsid
=_cephadm
.make_fsid(),
1023 mon_id
='a', mon_dir
='mon_dir',
1024 mon_network
=None, ipv6
=False,
1026 cluster_network
=None,
1027 ipv6_cluster_network
=False
1031 class TestCustomContainer(unittest
.TestCase
):
1032 cc
: _cephadm
.CustomContainer
1035 self
.cc
= _cephadm
.CustomContainer(
1036 'e863154d-33c7-4350-bca5-921e0467e55b',
1039 'entrypoint': 'bash',
1045 'envs': ['SECRET=password'],
1046 'ports': [8080, 8443],
1048 '/CONFIG_DIR': '/foo/conf',
1049 'bar/config': '/bar:ro'
1054 'source=/CONFIG_DIR',
1055 'destination=/foo/conf',
1060 'source=bar/config',
1061 'destination=/bar:ro',
1066 image
='docker.io/library/hello-world:latest'
1069 def test_entrypoint(self
):
1070 self
.assertEqual(self
.cc
.entrypoint
, 'bash')
1072 def test_uid_gid(self
):
1073 self
.assertEqual(self
.cc
.uid
, 65534)
1074 self
.assertEqual(self
.cc
.gid
, 1000)
1076 def test_ports(self
):
1077 self
.assertEqual(self
.cc
.ports
, [8080, 8443])
1079 def test_get_container_args(self
):
1080 result
= self
.cc
.get_container_args()
1081 self
.assertEqual(result
, [
1086 def test_get_container_envs(self
):
1087 result
= self
.cc
.get_container_envs()
1088 self
.assertEqual(result
, ['SECRET=password'])
1090 def test_get_container_mounts(self
):
1091 result
= self
.cc
.get_container_mounts('/xyz')
1092 self
.assertDictEqual(result
, {
1093 '/CONFIG_DIR': '/foo/conf',
1094 '/xyz/bar/config': '/bar:ro'
1097 def test_get_container_binds(self
):
1098 result
= self
.cc
.get_container_binds('/xyz')
1099 self
.assertEqual(result
, [
1102 'source=/CONFIG_DIR',
1103 'destination=/foo/conf',
1108 'source=/xyz/bar/config',
1109 'destination=/bar:ro',
1115 class TestMaintenance
:
1116 systemd_target
= "ceph.00000000-0000-0000-0000-000000c0ffee.target"
1117 fsid
= '0ea8cdd0-1bbf-11ec-a9c7-5254002763fa'
1119 def test_systemd_target_OK(self
, tmp_path
):
1121 wants
= base
/ "ceph.target.wants"
1123 target
= wants
/ TestMaintenance
.systemd_target
1125 ctx
= _cephadm
.CephadmContext()
1126 ctx
.unit_dir
= str(base
)
1128 assert _cephadm
.systemd_target_state(ctx
, target
.name
)
1130 def test_systemd_target_NOTOK(self
, tmp_path
):
1132 ctx
= _cephadm
.CephadmContext()
1133 ctx
.unit_dir
= str(base
)
1134 assert not _cephadm
.systemd_target_state(ctx
, TestMaintenance
.systemd_target
)
1136 def test_parser_OK(self
):
1137 args
= _cephadm
._parse
_args
(['host-maintenance', 'enter'])
1138 assert args
.maintenance_action
== 'enter'
1140 def test_parser_BAD(self
):
1141 with pytest
.raises(SystemExit):
1142 _cephadm
._parse
_args
(['host-maintenance', 'wah'])
1144 @mock.patch('os.listdir', return_value
=[])
1145 @mock.patch('cephadm.call')
1146 @mock.patch('cephadm.logger')
1147 @mock.patch('cephadm.systemd_target_state')
1148 def test_enter_failure_1(self
, _target_state
, _logger
, _call
, _listdir
):
1149 _call
.return_value
= '', '', 999
1150 _target_state
.return_value
= True
1151 ctx
: _cephadm
.CephadmContext
= _cephadm
.cephadm_init_ctx(
1152 ['host-maintenance', 'enter', '--fsid', TestMaintenance
.fsid
])
1153 ctx
.container_engine
= mock_podman()
1154 retval
= _cephadm
.command_maintenance(ctx
)
1155 assert retval
.startswith('failed')
1157 @mock.patch('os.listdir', return_value
=[])
1158 @mock.patch('cephadm.call')
1159 @mock.patch('cephadm.logger')
1160 @mock.patch('cephadm.systemd_target_state')
1161 def test_enter_failure_2(self
, _target_state
, _logger
, _call
, _listdir
):
1162 _call
.side_effect
= [('', '', 0), ('', '', 999), ('', '', 0), ('', '', 999)]
1163 _target_state
.return_value
= True
1164 ctx
: _cephadm
.CephadmContext
= _cephadm
.cephadm_init_ctx(
1165 ['host-maintenance', 'enter', '--fsid', TestMaintenance
.fsid
])
1166 ctx
.container_engine
= mock_podman()
1167 retval
= _cephadm
.command_maintenance(ctx
)
1168 assert retval
.startswith('failed')
1170 @mock.patch('os.listdir', return_value
=[])
1171 @mock.patch('cephadm.call')
1172 @mock.patch('cephadm.logger')
1173 @mock.patch('cephadm.systemd_target_state')
1174 @mock.patch('cephadm.target_exists')
1175 def test_exit_failure_1(self
, _target_exists
, _target_state
, _logger
, _call
, _listdir
):
1176 _call
.return_value
= '', '', 999
1177 _target_state
.return_value
= False
1178 _target_exists
.return_value
= True
1179 ctx
: _cephadm
.CephadmContext
= _cephadm
.cephadm_init_ctx(
1180 ['host-maintenance', 'exit', '--fsid', TestMaintenance
.fsid
])
1181 ctx
.container_engine
= mock_podman()
1182 retval
= _cephadm
.command_maintenance(ctx
)
1183 assert retval
.startswith('failed')
1185 @mock.patch('os.listdir', return_value
=[])
1186 @mock.patch('cephadm.call')
1187 @mock.patch('cephadm.logger')
1188 @mock.patch('cephadm.systemd_target_state')
1189 @mock.patch('cephadm.target_exists')
1190 def test_exit_failure_2(self
, _target_exists
, _target_state
, _logger
, _call
, _listdir
):
1191 _call
.side_effect
= [('', '', 0), ('', '', 999), ('', '', 0), ('', '', 999)]
1192 _target_state
.return_value
= False
1193 _target_exists
.return_value
= True
1194 ctx
: _cephadm
.CephadmContext
= _cephadm
.cephadm_init_ctx(
1195 ['host-maintenance', 'exit', '--fsid', TestMaintenance
.fsid
])
1196 ctx
.container_engine
= mock_podman()
1197 retval
= _cephadm
.command_maintenance(ctx
)
1198 assert retval
.startswith('failed')
1201 class TestMonitoring(object):
1202 @mock.patch('cephadm.call')
1203 def test_get_version_alertmanager(self
, _call
):
1204 ctx
= _cephadm
.CephadmContext()
1205 ctx
.container_engine
= mock_podman()
1206 daemon_type
= 'alertmanager'
1208 # binary `prometheus`
1209 _call
.return_value
= '', '{}, version 0.16.1'.format(daemon_type
), 0
1210 version
= _cephadm
.Monitoring
.get_version(ctx
, 'container_id', daemon_type
)
1211 assert version
== '0.16.1'
1213 # binary `prometheus-alertmanager`
1214 _call
.side_effect
= (
1216 ('', '{}, version 0.16.1'.format(daemon_type
), 0),
1218 version
= _cephadm
.Monitoring
.get_version(ctx
, 'container_id', daemon_type
)
1219 assert version
== '0.16.1'
1221 @mock.patch('cephadm.call')
1222 def test_get_version_prometheus(self
, _call
):
1223 ctx
= _cephadm
.CephadmContext()
1224 ctx
.container_engine
= mock_podman()
1225 daemon_type
= 'prometheus'
1226 _call
.return_value
= '', '{}, version 0.16.1'.format(daemon_type
), 0
1227 version
= _cephadm
.Monitoring
.get_version(ctx
, 'container_id', daemon_type
)
1228 assert version
== '0.16.1'
1230 def test_prometheus_external_url(self
):
1231 ctx
= _cephadm
.CephadmContext()
1232 ctx
.config_json
= json
.dumps({'files': {}, 'retention_time': '15d'})
1233 daemon_type
= 'prometheus'
1235 fsid
= 'aaf5a720-13fe-4a3b-82b9-2d99b7fd9704'
1236 args
= _cephadm
.get_daemon_args(ctx
, fsid
, daemon_type
, daemon_id
)
1237 assert any([x
.startswith('--web.external-url=http://') for x
in args
])
1239 @mock.patch('cephadm.call')
1240 def test_get_version_node_exporter(self
, _call
):
1241 ctx
= _cephadm
.CephadmContext()
1242 ctx
.container_engine
= mock_podman()
1243 daemon_type
= 'node-exporter'
1244 _call
.return_value
= '', '{}, version 0.16.1'.format(daemon_type
.replace('-', '_')), 0
1245 version
= _cephadm
.Monitoring
.get_version(ctx
, 'container_id', daemon_type
)
1246 assert version
== '0.16.1'
1248 def test_create_daemon_dirs_prometheus(self
, cephadm_fs
):
1250 Ensures the required and optional files given in the configuration are
1251 created and mapped correctly inside the container. Tests absolute and
1252 relative file paths given in the configuration.
1255 fsid
= 'aaf5a720-13fe-4a3b-82b9-2d99b7fd9704'
1256 daemon_type
= 'prometheus'
1259 ctx
= _cephadm
.CephadmContext()
1260 ctx
.data_dir
= '/somedir'
1261 ctx
.config_json
= json
.dumps({
1263 'prometheus.yml': 'foo',
1264 '/etc/prometheus/alerting/ceph_alerts.yml': 'bar'
1268 _cephadm
.create_daemon_dirs(ctx
,
1277 prefix
= '{data_dir}/{fsid}/{daemon_type}.{daemon_id}'.format(
1278 data_dir
=ctx
.data_dir
,
1280 daemon_type
=daemon_type
,
1285 'etc/prometheus/prometheus.yml': 'foo',
1286 'etc/prometheus/alerting/ceph_alerts.yml': 'bar',
1289 for file,content
in expected
.items():
1290 file = os
.path
.join(prefix
, file)
1291 assert os
.path
.exists(file)
1292 with
open(file) as f
:
1293 assert f
.read() == content
1295 # assert uid/gid after redeploy
1298 _cephadm
.create_daemon_dirs(ctx
,
1306 for file,content
in expected
.items():
1307 file = os
.path
.join(prefix
, file)
1308 assert os
.stat(file).st_uid
== new_uid
1309 assert os
.stat(file).st_gid
== new_gid
1312 class TestBootstrap(object):
1315 def _get_cmd(*args
):
1318 '--allow-mismatched-release',
1319 '--skip-prepare-host',
1325 ###############################################3
1327 def test_config(self
, cephadm_fs
):
1329 cmd
= self
._get
_cmd
(
1330 '--mon-ip', '192.168.1.1',
1331 '--skip-mon-network',
1332 '--config', conf_file
,
1335 with
with_cephadm_ctx(cmd
) as ctx
:
1336 msg
= r
'No such file or directory'
1337 with pytest
.raises(_cephadm
.Error
, match
=msg
):
1338 _cephadm
.command_bootstrap(ctx
)
1340 cephadm_fs
.create_file(conf_file
)
1341 with
with_cephadm_ctx(cmd
) as ctx
:
1342 retval
= _cephadm
.command_bootstrap(ctx
)
1345 def test_no_mon_addr(self
, cephadm_fs
):
1346 cmd
= self
._get
_cmd
()
1347 with
with_cephadm_ctx(cmd
) as ctx
:
1348 msg
= r
'must specify --mon-ip or --mon-addrv'
1349 with pytest
.raises(_cephadm
.Error
, match
=msg
):
1350 _cephadm
.command_bootstrap(ctx
)
1352 def test_skip_mon_network(self
, cephadm_fs
):
1353 cmd
= self
._get
_cmd
('--mon-ip', '192.168.1.1')
1355 with
with_cephadm_ctx(cmd
, list_networks
={}) as ctx
:
1356 msg
= r
'--skip-mon-network'
1357 with pytest
.raises(_cephadm
.Error
, match
=msg
):
1358 _cephadm
.command_bootstrap(ctx
)
1360 cmd
+= ['--skip-mon-network']
1361 with
with_cephadm_ctx(cmd
, list_networks
={}) as ctx
:
1362 retval
= _cephadm
.command_bootstrap(ctx
)
1365 @pytest.mark
.parametrize('mon_ip, list_networks, result',
1370 {'192.168.1.0/24': {'eth0': ['192.168.1.1']}},
1375 {'192.168.1.0/24': {'eth0': ['192.168.1.1']}},
1380 {'192.168.1.0/24': {'eth0': ['192.168.1.1']}},
1385 {'192.168.1.0/24': {'eth0': ['192.168.1.1']}},
1390 {'192.168.1.0/24': {'eth0': ['192.168.1.1']}},
1395 {'192.168.1.0/24': {'eth0': ['192.168.1.1']}},
1401 {'192.168.1.0/24': {'eth0': ['192.168.1.1']}},
1405 '::ffff:192.168.1.0',
1406 {"ffff::/64": {"eth0": ["::ffff:c0a8:101"]}},
1410 '::ffff:192.168.1.1',
1411 {"ffff::/64": {"eth0": ["::ffff:c0a8:101"]}},
1416 {"ffff::/64": {"eth0": ["::ffff:c0a8:101"]}},
1420 '[::ffff:c0a8:101]:1234',
1421 {"ffff::/64": {"eth0": ["::ffff:c0a8:101"]}},
1425 '[::ffff:c0a8:101]:0123',
1426 {"ffff::/64": {"eth0": ["::ffff:c0a8:101"]}},
1430 '0000:0000:0000:0000:0000:FFFF:C0A8:0101',
1431 {"ffff::/64": {"eth0": ["::ffff:c0a8:101"]}},
1435 def test_mon_ip(self
, mon_ip
, list_networks
, result
, cephadm_fs
):
1436 cmd
= self
._get
_cmd
('--mon-ip', mon_ip
)
1438 with
with_cephadm_ctx(cmd
, list_networks
=list_networks
) as ctx
:
1439 msg
= r
'--skip-mon-network'
1440 with pytest
.raises(_cephadm
.Error
, match
=msg
):
1441 _cephadm
.command_bootstrap(ctx
)
1443 with
with_cephadm_ctx(cmd
, list_networks
=list_networks
) as ctx
:
1444 retval
= _cephadm
.command_bootstrap(ctx
)
1447 @pytest.mark
.parametrize('mon_addrv, list_networks, err',
1452 {'192.168.1.0/24': {'eth0': ['192.168.1.1']}},
1453 r
'must use square brackets',
1457 {'192.168.1.0/24': {'eth0': ['192.168.1.1']}},
1458 r
'must include port number',
1461 '[192.168.1.1:1234]',
1462 {'192.168.1.0/24': {'eth0': ['192.168.1.1']}},
1466 '[192.168.1.1:0123]',
1467 {'192.168.1.0/24': {'eth0': ['192.168.1.1']}},
1471 '[v2:192.168.1.1:3300,v1:192.168.1.1:6789]',
1472 {'192.168.1.0/24': {'eth0': ['192.168.1.1']}},
1477 '[::ffff:192.168.1.1:1234]',
1478 {'ffff::/64': {'eth0': ['::ffff:c0a8:101']}},
1482 '[::ffff:192.168.1.1:0123]',
1483 {'ffff::/64': {'eth0': ['::ffff:c0a8:101']}},
1487 '[0000:0000:0000:0000:0000:FFFF:C0A8:0101:1234]',
1488 {'ffff::/64': {'eth0': ['::ffff:c0a8:101']}},
1492 '[v2:0000:0000:0000:0000:0000:FFFF:C0A8:0101:3300,v1:0000:0000:0000:0000:0000:FFFF:C0A8:0101:6789]',
1493 {'ffff::/64': {'eth0': ['::ffff:c0a8:101']}},
1497 def test_mon_addrv(self
, mon_addrv
, list_networks
, err
, cephadm_fs
):
1498 cmd
= self
._get
_cmd
('--mon-addrv', mon_addrv
)
1500 with
with_cephadm_ctx(cmd
, list_networks
=list_networks
) as ctx
:
1501 with pytest
.raises(_cephadm
.Error
, match
=err
):
1502 _cephadm
.command_bootstrap(ctx
)
1504 with
with_cephadm_ctx(cmd
, list_networks
=list_networks
) as ctx
:
1505 retval
= _cephadm
.command_bootstrap(ctx
)
1508 def test_allow_fqdn_hostname(self
, cephadm_fs
):
1509 hostname
= 'foo.bar'
1510 cmd
= self
._get
_cmd
(
1511 '--mon-ip', '192.168.1.1',
1512 '--skip-mon-network',
1515 with
with_cephadm_ctx(cmd
, hostname
=hostname
) as ctx
:
1516 msg
= r
'--allow-fqdn-hostname'
1517 with pytest
.raises(_cephadm
.Error
, match
=msg
):
1518 _cephadm
.command_bootstrap(ctx
)
1520 cmd
+= ['--allow-fqdn-hostname']
1521 with
with_cephadm_ctx(cmd
, hostname
=hostname
) as ctx
:
1522 retval
= _cephadm
.command_bootstrap(ctx
)
1525 @pytest.mark
.parametrize('fsid, err',
1528 ('00000000-0000-0000-0000-0000deadbeef', None),
1529 ('00000000-0000-0000-0000-0000deadbeez', 'not an fsid'),
1531 def test_fsid(self
, fsid
, err
, cephadm_fs
):
1532 cmd
= self
._get
_cmd
(
1533 '--mon-ip', '192.168.1.1',
1534 '--skip-mon-network',
1538 with
with_cephadm_ctx(cmd
) as ctx
:
1540 with pytest
.raises(_cephadm
.Error
, match
=err
):
1541 _cephadm
.command_bootstrap(ctx
)
1543 retval
= _cephadm
.command_bootstrap(ctx
)
1547 class TestShell(object):
1549 def test_fsid(self
, cephadm_fs
):
1550 fsid
= '00000000-0000-0000-0000-0000deadbeef'
1552 cmd
= ['shell', '--fsid', fsid
]
1553 with
with_cephadm_ctx(cmd
) as ctx
:
1554 retval
= _cephadm
.command_shell(ctx
)
1556 assert ctx
.fsid
== fsid
1558 cmd
= ['shell', '--fsid', '00000000-0000-0000-0000-0000deadbeez']
1559 with
with_cephadm_ctx(cmd
) as ctx
:
1561 with pytest
.raises(_cephadm
.Error
, match
=err
):
1562 retval
= _cephadm
.command_shell(ctx
)
1564 assert ctx
.fsid
== None
1566 s
= get_ceph_conf(fsid
=fsid
)
1567 f
= cephadm_fs
.create_file('ceph.conf', contents
=s
)
1569 cmd
= ['shell', '--fsid', fsid
, '--config', f
.path
]
1570 with
with_cephadm_ctx(cmd
) as ctx
:
1571 retval
= _cephadm
.command_shell(ctx
)
1573 assert ctx
.fsid
== fsid
1575 cmd
= ['shell', '--fsid', '10000000-0000-0000-0000-0000deadbeef', '--config', f
.path
]
1576 with
with_cephadm_ctx(cmd
) as ctx
:
1577 err
= 'fsid does not match ceph.conf'
1578 with pytest
.raises(_cephadm
.Error
, match
=err
):
1579 retval
= _cephadm
.command_shell(ctx
)
1581 assert ctx
.fsid
== None
1583 def test_name(self
, cephadm_fs
):
1584 cmd
= ['shell', '--name', 'foo']
1585 with
with_cephadm_ctx(cmd
) as ctx
:
1586 retval
= _cephadm
.command_shell(ctx
)
1589 cmd
= ['shell', '--name', 'foo.bar']
1590 with
with_cephadm_ctx(cmd
) as ctx
:
1591 err
= r
'must pass --fsid'
1592 with pytest
.raises(_cephadm
.Error
, match
=err
):
1593 retval
= _cephadm
.command_shell(ctx
)
1596 fsid
= '00000000-0000-0000-0000-0000deadbeef'
1597 cmd
= ['shell', '--name', 'foo.bar', '--fsid', fsid
]
1598 with
with_cephadm_ctx(cmd
) as ctx
:
1599 retval
= _cephadm
.command_shell(ctx
)
1602 def test_config(self
, cephadm_fs
):
1604 with
with_cephadm_ctx(cmd
) as ctx
:
1605 retval
= _cephadm
.command_shell(ctx
)
1607 assert ctx
.config
== None
1609 cephadm_fs
.create_file(_cephadm
.CEPH_DEFAULT_CONF
)
1610 with
with_cephadm_ctx(cmd
) as ctx
:
1611 retval
= _cephadm
.command_shell(ctx
)
1613 assert ctx
.config
== _cephadm
.CEPH_DEFAULT_CONF
1615 cmd
= ['shell', '--config', 'foo']
1616 with
with_cephadm_ctx(cmd
) as ctx
:
1617 retval
= _cephadm
.command_shell(ctx
)
1619 assert ctx
.config
== 'foo'
1621 def test_keyring(self
, cephadm_fs
):
1623 with
with_cephadm_ctx(cmd
) as ctx
:
1624 retval
= _cephadm
.command_shell(ctx
)
1626 assert ctx
.keyring
== None
1628 cephadm_fs
.create_file(_cephadm
.CEPH_DEFAULT_KEYRING
)
1629 with
with_cephadm_ctx(cmd
) as ctx
:
1630 retval
= _cephadm
.command_shell(ctx
)
1632 assert ctx
.keyring
== _cephadm
.CEPH_DEFAULT_KEYRING
1634 cmd
= ['shell', '--keyring', 'foo']
1635 with
with_cephadm_ctx(cmd
) as ctx
:
1636 retval
= _cephadm
.command_shell(ctx
)
1638 assert ctx
.keyring
== 'foo'
1640 @mock.patch('cephadm.CephContainer')
1641 def test_mount_no_dst(self
, _ceph_container
, cephadm_fs
):
1642 cmd
= ['shell', '--mount', '/etc/foo']
1643 with
with_cephadm_ctx(cmd
) as ctx
:
1644 retval
= _cephadm
.command_shell(ctx
)
1646 assert _ceph_container
.call_args
.kwargs
['volume_mounts']['/etc/foo'] == '/mnt/foo'
1648 @mock.patch('cephadm.CephContainer')
1649 def test_mount_with_dst_no_opt(self
, _ceph_container
, cephadm_fs
):
1650 cmd
= ['shell', '--mount', '/etc/foo:/opt/foo/bar']
1651 with
with_cephadm_ctx(cmd
) as ctx
:
1652 retval
= _cephadm
.command_shell(ctx
)
1654 assert _ceph_container
.call_args
.kwargs
['volume_mounts']['/etc/foo'] == '/opt/foo/bar'
1656 @mock.patch('cephadm.CephContainer')
1657 def test_mount_with_dst_and_opt(self
, _ceph_container
, cephadm_fs
):
1658 cmd
= ['shell', '--mount', '/etc/foo:/opt/foo/bar:Z']
1659 with
with_cephadm_ctx(cmd
) as ctx
:
1660 retval
= _cephadm
.command_shell(ctx
)
1662 assert _ceph_container
.call_args
.kwargs
['volume_mounts']['/etc/foo'] == '/opt/foo/bar:Z'
1664 class TestCephVolume(object):
1667 def _get_cmd(*args
):
1671 '--', 'inventory', '--format', 'json'
1674 def test_noop(self
, cephadm_fs
):
1675 cmd
= self
._get
_cmd
()
1676 with
with_cephadm_ctx(cmd
) as ctx
:
1677 _cephadm
.command_ceph_volume(ctx
)
1678 assert ctx
.fsid
== None
1679 assert ctx
.config
== None
1680 assert ctx
.keyring
== None
1681 assert ctx
.config_json
== None
1683 def test_fsid(self
, cephadm_fs
):
1684 fsid
= '00000000-0000-0000-0000-0000deadbeef'
1686 cmd
= self
._get
_cmd
('--fsid', fsid
)
1687 with
with_cephadm_ctx(cmd
) as ctx
:
1688 _cephadm
.command_ceph_volume(ctx
)
1689 assert ctx
.fsid
== fsid
1691 cmd
= self
._get
_cmd
('--fsid', '00000000-0000-0000-0000-0000deadbeez')
1692 with
with_cephadm_ctx(cmd
) as ctx
:
1694 with pytest
.raises(_cephadm
.Error
, match
=err
):
1695 retval
= _cephadm
.command_shell(ctx
)
1697 assert ctx
.fsid
== None
1699 s
= get_ceph_conf(fsid
=fsid
)
1700 f
= cephadm_fs
.create_file('ceph.conf', contents
=s
)
1702 cmd
= self
._get
_cmd
('--fsid', fsid
, '--config', f
.path
)
1703 with
with_cephadm_ctx(cmd
) as ctx
:
1704 _cephadm
.command_ceph_volume(ctx
)
1705 assert ctx
.fsid
== fsid
1707 cmd
= self
._get
_cmd
('--fsid', '10000000-0000-0000-0000-0000deadbeef', '--config', f
.path
)
1708 with
with_cephadm_ctx(cmd
) as ctx
:
1709 err
= 'fsid does not match ceph.conf'
1710 with pytest
.raises(_cephadm
.Error
, match
=err
):
1711 _cephadm
.command_ceph_volume(ctx
)
1712 assert ctx
.fsid
== None
1714 def test_config(self
, cephadm_fs
):
1715 cmd
= self
._get
_cmd
('--config', 'foo')
1716 with
with_cephadm_ctx(cmd
) as ctx
:
1717 err
= r
'No such file or directory'
1718 with pytest
.raises(_cephadm
.Error
, match
=err
):
1719 _cephadm
.command_ceph_volume(ctx
)
1721 cephadm_fs
.create_file('bar')
1722 cmd
= self
._get
_cmd
('--config', 'bar')
1723 with
with_cephadm_ctx(cmd
) as ctx
:
1724 _cephadm
.command_ceph_volume(ctx
)
1725 assert ctx
.config
== 'bar'
1727 def test_keyring(self
, cephadm_fs
):
1728 cmd
= self
._get
_cmd
('--keyring', 'foo')
1729 with
with_cephadm_ctx(cmd
) as ctx
:
1730 err
= r
'No such file or directory'
1731 with pytest
.raises(_cephadm
.Error
, match
=err
):
1732 _cephadm
.command_ceph_volume(ctx
)
1734 cephadm_fs
.create_file('bar')
1735 cmd
= self
._get
_cmd
('--keyring', 'bar')
1736 with
with_cephadm_ctx(cmd
) as ctx
:
1737 _cephadm
.command_ceph_volume(ctx
)
1738 assert ctx
.keyring
== 'bar'
1742 def test_unit_run(self
, cephadm_fs
):
1743 fsid
= '9b9d7609-f4d5-4aba-94c8-effa764d96c9'
1745 'files': {'iscsi-gateway.cfg': ''}
1747 with
with_cephadm_ctx(['--image=ceph/ceph'], list_networks
={}) as ctx
:
1749 ctx
.container_engine
= mock_docker()
1750 ctx
.config_json
= json
.dumps(config_json
)
1752 _cephadm
.get_parm
.return_value
= config_json
1753 c
= _cephadm
.get_container(ctx
, fsid
, 'iscsi', 'daemon_id')
1755 _cephadm
.make_data_dir(ctx
, fsid
, 'iscsi', 'daemon_id')
1756 _cephadm
.deploy_daemon_units(
1766 with
open('/var/lib/ceph/9b9d7609-f4d5-4aba-94c8-effa764d96c9/iscsi.daemon_id/unit.run') as f
:
1767 assert f
.read() == """set -e
1768 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
1769 # iscsi tcmu-runner container
1770 ! /usr/bin/docker rm -f ceph-9b9d7609-f4d5-4aba-94c8-effa764d96c9-iscsi.daemon_id-tcmu 2> /dev/null
1771 ! /usr/bin/docker rm -f ceph-9b9d7609-f4d5-4aba-94c8-effa764d96c9-iscsi-daemon_id-tcmu 2> /dev/null
1772 /usr/bin/docker run --rm --ipc=host --stop-signal=SIGTERM --ulimit nofile=1048576 --net=host --entrypoint /usr/bin/tcmu-runner --privileged --group-add=disk --init --name ceph-9b9d7609-f4d5-4aba-94c8-effa764d96c9-iscsi-daemon_id-tcmu --pids-limit=0 -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:z -v /dev:/dev --mount type=bind,source=/lib/modules,destination=/lib/modules,ro=true ceph/ceph &
1774 ! /usr/bin/docker rm -f ceph-9b9d7609-f4d5-4aba-94c8-effa764d96c9-iscsi.daemon_id 2> /dev/null
1775 ! /usr/bin/docker rm -f ceph-9b9d7609-f4d5-4aba-94c8-effa764d96c9-iscsi-daemon_id 2> /dev/null
1776 /usr/bin/docker run --rm --ipc=host --stop-signal=SIGTERM --ulimit nofile=1048576 --net=host --entrypoint /usr/bin/rbd-target-api --privileged --group-add=disk --init --name ceph-9b9d7609-f4d5-4aba-94c8-effa764d96c9-iscsi-daemon_id --pids-limit=0 -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:z -v /dev:/dev --mount type=bind,source=/lib/modules,destination=/lib/modules,ro=true ceph/ceph
1779 def test_get_container(self
):
1781 Due to a combination of socket.getfqdn() and podman's behavior to
1782 add the container name into the /etc/hosts file, we cannot use periods
1783 in container names. But we need to be able to detect old existing containers.
1784 Assert this behaviour. I think we can remove this in Ceph R
1786 fsid
= '9b9d7609-f4d5-4aba-94c8-effa764d96c9'
1787 with
with_cephadm_ctx(['--image=ceph/ceph'], list_networks
={}) as ctx
:
1789 c
= _cephadm
.get_container(ctx
, fsid
, 'iscsi', 'something')
1790 assert c
.cname
== 'ceph-9b9d7609-f4d5-4aba-94c8-effa764d96c9-iscsi-something'
1791 assert c
.old_cname
== 'ceph-9b9d7609-f4d5-4aba-94c8-effa764d96c9-iscsi.something'
1794 class TestCheckHost
:
1796 @mock.patch('cephadm.find_executable', return_value
='foo')
1797 @mock.patch('cephadm.check_time_sync', return_value
=True)
1798 @mock.patch('cephadm.logger')
1799 def test_container_engine(self
, _logger
, _find_executable
, _check_time_sync
):
1800 ctx
= _cephadm
.CephadmContext()
1802 ctx
.container_engine
= None
1803 err
= r
'No container engine binary found'
1804 with pytest
.raises(_cephadm
.Error
, match
=err
):
1805 _cephadm
.command_check_host(ctx
)
1807 ctx
.container_engine
= mock_podman()
1808 _cephadm
.command_check_host(ctx
)
1810 ctx
.container_engine
= mock_docker()
1811 _cephadm
.command_check_host(ctx
)
1816 @pytest.mark
.parametrize('os_release',
1821 VERSION="20.04 LTS (Focal Fossa)"
1824 PRETTY_NAME="Ubuntu 20.04 LTS"
1826 HOME_URL="https://www.ubuntu.com/"
1827 SUPPORT_URL="https://help.ubuntu.com/"
1828 BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
1829 PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
1830 VERSION_CODENAME=focal
1831 UBUNTU_CODENAME=focal
1839 ID_LIKE="rhel fedora"
1841 PLATFORM_ID="platform:el8"
1842 PRETTY_NAME="CentOS Linux 8 (Core)"
1844 CPE_NAME="cpe:/o:centos:centos:8"
1845 HOME_URL="https://www.centos.org/"
1846 BUG_REPORT_URL="https://bugs.centos.org/"
1848 CENTOS_MANTISBT_PROJECT="CentOS-8"
1849 CENTOS_MANTISBT_PROJECT_VERSION="8"
1850 REDHAT_SUPPORT_PRODUCT="centos"
1851 REDHAT_SUPPORT_PRODUCT_VERSION="8"
1856 NAME="openSUSE Tumbleweed"
1857 # VERSION="20210810"
1858 ID="opensuse-tumbleweed"
1859 ID_LIKE="opensuse suse"
1860 VERSION_ID="20210810"
1861 PRETTY_NAME="openSUSE Tumbleweed"
1863 CPE_NAME="cpe:/o:opensuse:tumbleweed:20210810"
1864 BUG_REPORT_URL="https://bugs.opensuse.org"
1865 HOME_URL="https://www.opensuse.org/"
1866 DOCUMENTATION_URL="https://en.opensuse.org/Portal:Tumbleweed"
1867 LOGO="distributor-logo"
1870 @mock.patch('cephadm.find_executable', return_value
='foo')
1871 def test_container_engine(self
, _find_executable
, os_release
, cephadm_fs
):
1872 cephadm_fs
.create_file('/etc/os-release', contents
=os_release
)
1873 ctx
= _cephadm
.CephadmContext()
1875 ctx
.container_engine
= None
1876 _cephadm
.command_rm_repo(ctx
)
1878 ctx
.container_engine
= mock_podman()
1879 _cephadm
.command_rm_repo(ctx
)
1881 ctx
.container_engine
= mock_docker()
1882 _cephadm
.command_rm_repo(ctx
)
1885 class TestValidateRepo
:
1887 @pytest.mark
.parametrize('values',
1894 os_release
=dedent("""
1896 VERSION="20.04 LTS (Focal Fossa)"
1899 PRETTY_NAME="Ubuntu 20.04 LTS"
1901 HOME_URL="https://www.ubuntu.com/"
1902 SUPPORT_URL="https://help.ubuntu.com/"
1903 BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
1904 PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
1905 VERSION_CODENAME=focal
1906 UBUNTU_CODENAME=focal
1909 # YumDnf on Centos8 - OK
1914 os_release
=dedent("""
1918 ID_LIKE="rhel fedora"
1920 PLATFORM_ID="platform:el8"
1921 PRETTY_NAME="CentOS Linux 8 (Core)"
1923 CPE_NAME="cpe:/o:centos:centos:8"
1924 HOME_URL="https://www.centos.org/"
1925 BUG_REPORT_URL="https://bugs.centos.org/"
1927 CENTOS_MANTISBT_PROJECT="CentOS-8"
1928 CENTOS_MANTISBT_PROJECT_VERSION="8"
1929 REDHAT_SUPPORT_PRODUCT="centos"
1930 REDHAT_SUPPORT_PRODUCT_VERSION="8"
1933 # YumDnf on Fedora - Fedora not supported
1937 err_text
="does not build Fedora",
1938 os_release
=dedent("""
1940 VERSION="35 (Cloud Edition)"
1944 PLATFORM_ID="platform:f35"
1945 PRETTY_NAME="Fedora Linux 35 (Cloud Edition)"
1946 ANSI_COLOR="0;38;2;60;110;180"
1947 LOGO=fedora-logo-icon
1948 CPE_NAME="cpe:/o:fedoraproject:fedora:35"
1949 HOME_URL="https://fedoraproject.org/"
1950 DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f35/system-administrators-guide/"
1951 SUPPORT_URL="https://ask.fedoraproject.org/"
1952 BUG_REPORT_URL="https://bugzilla.redhat.com/"
1953 REDHAT_BUGZILLA_PRODUCT="Fedora"
1954 REDHAT_BUGZILLA_PRODUCT_VERSION=35
1955 REDHAT_SUPPORT_PRODUCT="Fedora"
1956 REDHAT_SUPPORT_PRODUCT_VERSION=35
1957 PRIVACY_POLICY_URL="https://fedoraproject.org/wiki/Legal:PrivacyPolicy"
1958 VARIANT="Cloud Edition"
1962 # YumDnf on Centos 7 - no pacific
1966 err_text
="does not support pacific",
1967 os_release
=dedent("""
1971 ID_LIKE="rhel fedora"
1973 PRETTY_NAME="CentOS Linux 7 (Core)"
1975 CPE_NAME="cpe:/o:centos:centos:7"
1976 HOME_URL="https://www.centos.org/"
1977 BUG_REPORT_URL="https://bugs.centos.org/"
1979 CENTOS_MANTISBT_PROJECT="CentOS-7"
1980 CENTOS_MANTISBT_PROJECT_VERSION="7"
1981 REDHAT_SUPPORT_PRODUCT="centos"
1982 REDHAT_SUPPORT_PRODUCT_VERSION="7"
1985 # YumDnf on Centos 7 - nothing after pacific
1989 err_text
="does not support pacific",
1990 os_release
=dedent("""
1994 ID_LIKE="rhel fedora"
1996 PRETTY_NAME="CentOS Linux 7 (Core)"
1998 CPE_NAME="cpe:/o:centos:centos:7"
1999 HOME_URL="https://www.centos.org/"
2000 BUG_REPORT_URL="https://bugs.centos.org/"
2002 CENTOS_MANTISBT_PROJECT="CentOS-7"
2003 CENTOS_MANTISBT_PROJECT_VERSION="7"
2004 REDHAT_SUPPORT_PRODUCT="centos"
2005 REDHAT_SUPPORT_PRODUCT_VERSION="7"
2008 # YumDnf on Centos 7 - nothing v16 or higher
2012 err_text
="does not support",
2013 os_release
=dedent("""
2017 ID_LIKE="rhel fedora"
2019 PRETTY_NAME="CentOS Linux 7 (Core)"
2021 CPE_NAME="cpe:/o:centos:centos:7"
2022 HOME_URL="https://www.centos.org/"
2023 BUG_REPORT_URL="https://bugs.centos.org/"
2025 CENTOS_MANTISBT_PROJECT="CentOS-7"
2026 CENTOS_MANTISBT_PROJECT_VERSION="7"
2027 REDHAT_SUPPORT_PRODUCT="centos"
2028 REDHAT_SUPPORT_PRODUCT_VERSION="7"
2031 @mock.patch('cephadm.find_executable', return_value
='foo')
2032 def test_distro_validation(self
, _find_executable
, values
, cephadm_fs
):
2033 os_release
= values
['os_release']
2034 release
= values
['release']
2035 version
= values
['version']
2036 err_text
= values
['err_text']
2038 cephadm_fs
.create_file('/etc/os-release', contents
=os_release
)
2039 ctx
= _cephadm
.CephadmContext()
2040 ctx
.repo_url
= 'http://localhost'
2041 pkg
= _cephadm
.create_packager(ctx
, stable
=release
, version
=version
)
2044 with pytest
.raises(_cephadm
.Error
, match
=err_text
):
2047 with mock
.patch('cephadm.urlopen', return_value
=None):
2050 @pytest.mark
.parametrize('values',
2057 os_release
=dedent("""
2059 VERSION="20.04 LTS (Focal Fossa)"
2062 PRETTY_NAME="Ubuntu 20.04 LTS"
2064 HOME_URL="https://www.ubuntu.com/"
2065 SUPPORT_URL="https://help.ubuntu.com/"
2066 BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
2067 PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
2068 VERSION_CODENAME=focal
2069 UBUNTU_CODENAME=focal
2072 # YumDnf on Centos8 - force failure
2076 err_text
="failed to fetch repository metadata",
2077 os_release
=dedent("""
2081 ID_LIKE="rhel fedora"
2083 PLATFORM_ID="platform:el8"
2084 PRETTY_NAME="CentOS Linux 8 (Core)"
2086 CPE_NAME="cpe:/o:centos:centos:8"
2087 HOME_URL="https://www.centos.org/"
2088 BUG_REPORT_URL="https://bugs.centos.org/"
2090 CENTOS_MANTISBT_PROJECT="CentOS-8"
2091 CENTOS_MANTISBT_PROJECT_VERSION="8"
2092 REDHAT_SUPPORT_PRODUCT="centos"
2093 REDHAT_SUPPORT_PRODUCT_VERSION="8"
2096 @mock.patch('cephadm.find_executable', return_value
='foo')
2097 @mock.patch('cephadm.logger')
2098 def test_http_validation(self
, _logger
, _find_executable
, values
, cephadm_fs
):
2099 from urllib
.error
import HTTPError
2101 os_release
= values
['os_release']
2102 release
= values
['release']
2103 version
= values
['version']
2104 err_text
= values
['err_text']
2106 cephadm_fs
.create_file('/etc/os-release', contents
=os_release
)
2107 ctx
= _cephadm
.CephadmContext()
2108 ctx
.repo_url
= 'http://localhost'
2109 pkg
= _cephadm
.create_packager(ctx
, stable
=release
, version
=version
)
2111 with mock
.patch('cephadm.urlopen') as _urlopen
:
2112 _urlopen
.side_effect
= HTTPError(ctx
.repo_url
, 404, "not found", None, fp
=None)
2114 with pytest
.raises(_cephadm
.Error
, match
=err_text
):
2122 @mock.patch('time.sleep')
2123 @mock.patch('cephadm.call', return_value
=('', '', 0))
2124 @mock.patch('cephadm.get_image_info_from_inspect', return_value
={})
2125 @mock.patch('cephadm.logger')
2126 def test_error(self
, _logger
, _get_image_info_from_inspect
, _call
, _sleep
):
2127 ctx
= _cephadm
.CephadmContext()
2128 ctx
.container_engine
= mock_podman()
2129 ctx
.insecure
= False
2131 _call
.return_value
= ('', '', 0)
2132 retval
= _cephadm
.command_pull(ctx
)
2135 err
= 'maximum retries reached'
2137 _call
.return_value
= ('', 'foobar', 1)
2138 with pytest
.raises(_cephadm
.Error
) as e
:
2139 _cephadm
.command_pull(ctx
)
2140 assert err
not in str(e
.value
)
2142 _call
.return_value
= ('', 'net/http: TLS handshake timeout', 1)
2143 with pytest
.raises(_cephadm
.Error
) as e
:
2144 _cephadm
.command_pull(ctx
)
2145 assert err
in str(e
.value
)
2147 @mock.patch('cephadm.get_image_info_from_inspect', return_value
={})
2148 @mock.patch('cephadm.infer_local_ceph_image', return_value
='last_local_ceph_image')
2149 def test_image(self
, _infer_local_ceph_image
, _get_image_info_from_inspect
):
2151 with
with_cephadm_ctx(cmd
) as ctx
:
2152 retval
= _cephadm
.command_pull(ctx
)
2154 assert ctx
.image
== _cephadm
.DEFAULT_IMAGE
2156 with mock
.patch
.dict(os
.environ
, {"CEPHADM_IMAGE": 'cephadm_image_environ'}):
2158 with
with_cephadm_ctx(cmd
) as ctx
:
2159 retval
= _cephadm
.command_pull(ctx
)
2161 assert ctx
.image
== 'cephadm_image_environ'
2163 cmd
= ['--image', 'cephadm_image_param', 'pull']
2164 with
with_cephadm_ctx(cmd
) as ctx
:
2165 retval
= _cephadm
.command_pull(ctx
)
2167 assert ctx
.image
== 'cephadm_image_param'
2170 class TestApplySpec
:
2172 def test_extract_host_info_from_applied_spec(self
, cephadm_fs
):
2176 addr: 192.168.122.44
2183 addr: 192.168.122.247
2194 rgw_frontend_ssl_certificate: |
2195 -----BEGIN PRIVATE KEY-----
2196 V2VyIGRhcyBsaWVzdCBpc3QgZG9vZi4gTG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFt
2197 ZXQsIGNvbnNldGV0dXIgc2FkaXBzY2luZyBlbGl0ciwgc2VkIGRpYW0gbm9udW15
2198 IGVpcm1vZCB0ZW1wb3IgaW52aWR1bnQgdXQgbGFib3JlIGV0IGRvbG9yZSBtYWdu
2199 YSBhbGlxdXlhbSBlcmF0LCBzZWQgZGlhbSB2b2x1cHR1YS4gQXQgdmVybyBlb3Mg
2200 ZXQgYWNjdXNhbSBldCBqdXN0byBkdW8=
2201 -----END PRIVATE KEY-----
2202 -----BEGIN CERTIFICATE-----
2203 V2VyIGRhcyBsaWVzdCBpc3QgZG9vZi4gTG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFt
2204 ZXQsIGNvbnNldGV0dXIgc2FkaXBzY2luZyBlbGl0ciwgc2VkIGRpYW0gbm9udW15
2205 IGVpcm1vZCB0ZW1wb3IgaW52aWR1bnQgdXQgbGFib3JlIGV0IGRvbG9yZSBtYWdu
2206 YSBhbGlxdXlhbSBlcmF0LCBzZWQgZGlhbSB2b2x1cHR1YS4gQXQgdmVybyBlb3Mg
2207 ZXQgYWNjdXNhbSBldCBqdXN0byBkdW8=
2208 -----END CERTIFICATE-----
2213 cephadm_fs
.create_file('spec.yml', contents
=yaml
)
2214 retdic
= [{'hostname': 'vm-00', 'addr': '192.168.122.44'},
2215 {'hostname': 'vm-01', 'addr': '192.168.122.247'},
2216 {'hostname': 'vm-02',}]
2218 with
open('spec.yml') as f
:
2219 dic
= _cephadm
._extract
_host
_info
_from
_applied
_spec
(f
)
2220 assert dic
== retdic
2222 @mock.patch('cephadm.call', return_value
=('', '', 0))
2223 @mock.patch('cephadm.logger')
2224 def test_distribute_ssh_keys(self
, _logger
, _call
):
2225 ctx
= _cephadm
.CephadmContext()
2226 ctx
.ssh_public_key
= None
2227 ctx
.ssh_user
= 'root'
2229 host_spec
= {'service_type': 'host', 'hostname': 'vm-02', 'addr': '192.168.122.165'}
2231 retval
= _cephadm
._distribute
_ssh
_keys
(ctx
, host_spec
, 'bootstrap_hostname')
2235 _call
.return_value
= ('', '', 1)
2237 retval
= _cephadm
._distribute
_ssh
_keys
(ctx
, host_spec
, 'bootstrap_hostname')
2242 class TestSNMPGateway
:
2244 'snmp_community': 'public',
2245 'destination': '192.168.1.10:162',
2246 'snmp_version': 'V2c',
2248 V3_no_priv_config
= {
2249 'destination': '192.168.1.10:162',
2250 'snmp_version': 'V3',
2251 'snmp_v3_auth_username': 'myuser',
2252 'snmp_v3_auth_password': 'mypassword',
2253 'snmp_v3_auth_protocol': 'SHA',
2254 'snmp_v3_engine_id': '8000C53F00000000',
2257 'destination': '192.168.1.10:162',
2258 'snmp_version': 'V3',
2259 'snmp_v3_auth_username': 'myuser',
2260 'snmp_v3_auth_password': 'mypassword',
2261 'snmp_v3_auth_protocol': 'SHA',
2262 'snmp_v3_priv_protocol': 'DES',
2263 'snmp_v3_priv_password': 'mysecret',
2264 'snmp_v3_engine_id': '8000C53F00000000',
2266 no_destination_config
= {
2267 'snmp_version': 'V3',
2268 'snmp_v3_auth_username': 'myuser',
2269 'snmp_v3_auth_password': 'mypassword',
2270 'snmp_v3_auth_protocol': 'SHA',
2271 'snmp_v3_priv_protocol': 'DES',
2272 'snmp_v3_priv_password': 'mysecret',
2273 'snmp_v3_engine_id': '8000C53F00000000',
2275 bad_version_config
= {
2276 'snmp_community': 'public',
2277 'destination': '192.168.1.10:162',
2278 'snmp_version': 'V1',
2281 def test_unit_run_V2c(self
, cephadm_fs
):
2282 fsid
= 'ca734440-3dc6-11ec-9b98-5254002537a6'
2283 with
with_cephadm_ctx(['--image=docker.io/maxwo/snmp-notifier:v1.2.1'], list_networks
={}) as ctx
:
2285 ctx
.config_json
= json
.dumps(self
.V2c_config
)
2287 ctx
.tcp_ports
= '9464'
2288 _cephadm
.get_parm
.return_value
= self
.V2c_config
2289 c
= _cephadm
.get_container(ctx
, fsid
, 'snmp-gateway', 'daemon_id')
2291 _cephadm
.make_data_dir(ctx
, fsid
, 'snmp-gateway', 'daemon_id')
2293 _cephadm
.create_daemon_dirs(ctx
, fsid
, 'snmp-gateway', 'daemon_id', 0, 0)
2294 with
open(f
'/var/lib/ceph/{fsid}/snmp-gateway.daemon_id/snmp-gateway.conf', 'r') as f
:
2295 conf
= f
.read().rstrip()
2296 assert conf
== 'SNMP_NOTIFIER_COMMUNITY=public'
2298 _cephadm
.deploy_daemon_units(
2307 with
open(f
'/var/lib/ceph/{fsid}/snmp-gateway.daemon_id/unit.run', 'r') as f
:
2308 run_cmd
= f
.readlines()[-1].rstrip()
2309 assert run_cmd
.endswith('docker.io/maxwo/snmp-notifier:v1.2.1 --web.listen-address=:9464 --snmp.destination=192.168.1.10:162 --snmp.version=V2c --log.level=info --snmp.trap-description-template=/etc/snmp_notifier/description-template.tpl')
2311 def test_unit_run_V3_noPriv(self
, cephadm_fs
):
2312 fsid
= 'ca734440-3dc6-11ec-9b98-5254002537a6'
2313 with
with_cephadm_ctx(['--image=docker.io/maxwo/snmp-notifier:v1.2.1'], list_networks
={}) as ctx
:
2315 ctx
.config_json
= json
.dumps(self
.V3_no_priv_config
)
2317 ctx
.tcp_ports
= '9465'
2318 _cephadm
.get_parm
.return_value
= self
.V3_no_priv_config
2319 c
= _cephadm
.get_container(ctx
, fsid
, 'snmp-gateway', 'daemon_id')
2321 _cephadm
.make_data_dir(ctx
, fsid
, 'snmp-gateway', 'daemon_id')
2323 _cephadm
.create_daemon_dirs(ctx
, fsid
, 'snmp-gateway', 'daemon_id', 0, 0)
2324 with
open(f
'/var/lib/ceph/{fsid}/snmp-gateway.daemon_id/snmp-gateway.conf', 'r') as f
:
2326 assert conf
== 'SNMP_NOTIFIER_AUTH_USERNAME=myuser\nSNMP_NOTIFIER_AUTH_PASSWORD=mypassword\n'
2328 _cephadm
.deploy_daemon_units(
2337 with
open(f
'/var/lib/ceph/{fsid}/snmp-gateway.daemon_id/unit.run', 'r') as f
:
2338 run_cmd
= f
.readlines()[-1].rstrip()
2339 assert run_cmd
.endswith('docker.io/maxwo/snmp-notifier:v1.2.1 --web.listen-address=:9465 --snmp.destination=192.168.1.10:162 --snmp.version=V3 --log.level=info --snmp.trap-description-template=/etc/snmp_notifier/description-template.tpl --snmp.authentication-enabled --snmp.authentication-protocol=SHA --snmp.security-engine-id=8000C53F00000000')
2341 def test_unit_run_V3_Priv(self
, cephadm_fs
):
2342 fsid
= 'ca734440-3dc6-11ec-9b98-5254002537a6'
2343 with
with_cephadm_ctx(['--image=docker.io/maxwo/snmp-notifier:v1.2.1'], list_networks
={}) as ctx
:
2345 ctx
.config_json
= json
.dumps(self
.V3_priv_config
)
2347 ctx
.tcp_ports
= '9464'
2348 _cephadm
.get_parm
.return_value
= self
.V3_priv_config
2349 c
= _cephadm
.get_container(ctx
, fsid
, 'snmp-gateway', 'daemon_id')
2351 _cephadm
.make_data_dir(ctx
, fsid
, 'snmp-gateway', 'daemon_id')
2353 _cephadm
.create_daemon_dirs(ctx
, fsid
, 'snmp-gateway', 'daemon_id', 0, 0)
2354 with
open(f
'/var/lib/ceph/{fsid}/snmp-gateway.daemon_id/snmp-gateway.conf', 'r') as f
:
2356 assert conf
== 'SNMP_NOTIFIER_AUTH_USERNAME=myuser\nSNMP_NOTIFIER_AUTH_PASSWORD=mypassword\nSNMP_NOTIFIER_PRIV_PASSWORD=mysecret\n'
2358 _cephadm
.deploy_daemon_units(
2367 with
open(f
'/var/lib/ceph/{fsid}/snmp-gateway.daemon_id/unit.run', 'r') as f
:
2368 run_cmd
= f
.readlines()[-1].rstrip()
2369 assert run_cmd
.endswith('docker.io/maxwo/snmp-notifier:v1.2.1 --web.listen-address=:9464 --snmp.destination=192.168.1.10:162 --snmp.version=V3 --log.level=info --snmp.trap-description-template=/etc/snmp_notifier/description-template.tpl --snmp.authentication-enabled --snmp.authentication-protocol=SHA --snmp.security-engine-id=8000C53F00000000 --snmp.private-enabled --snmp.private-protocol=DES')
2371 def test_unit_run_no_dest(self
, cephadm_fs
):
2372 fsid
= 'ca734440-3dc6-11ec-9b98-5254002537a6'
2373 with
with_cephadm_ctx(['--image=docker.io/maxwo/snmp-notifier:v1.2.1'], list_networks
={}) as ctx
:
2375 ctx
.config_json
= json
.dumps(self
.no_destination_config
)
2377 ctx
.tcp_ports
= '9464'
2378 _cephadm
.get_parm
.return_value
= self
.no_destination_config
2380 with pytest
.raises(Exception) as e
:
2381 c
= _cephadm
.get_container(ctx
, fsid
, 'snmp-gateway', 'daemon_id')
2382 assert str(e
.value
) == "config is missing destination attribute(<ip>:<port>) of the target SNMP listener"
2384 def test_unit_run_bad_version(self
, cephadm_fs
):
2385 fsid
= 'ca734440-3dc6-11ec-9b98-5254002537a6'
2386 with
with_cephadm_ctx(['--image=docker.io/maxwo/snmp-notifier:v1.2.1'], list_networks
={}) as ctx
:
2388 ctx
.config_json
= json
.dumps(self
.bad_version_config
)
2390 ctx
.tcp_ports
= '9464'
2391 _cephadm
.get_parm
.return_value
= self
.bad_version_config
2393 with pytest
.raises(Exception) as e
:
2394 c
= _cephadm
.get_container(ctx
, fsid
, 'snmp-gateway', 'daemon_id')
2395 assert str(e
.value
) == 'not a valid snmp version: V1'
2397 class TestNetworkValidation
:
2399 def test_ipv4_subnet(self
):
2400 rc
, v
, msg
= _cephadm
.check_subnet('192.168.1.0/24')
2401 assert rc
== 0 and v
[0] == 4
2403 def test_ipv4_subnet_list(self
):
2404 rc
, v
, msg
= _cephadm
.check_subnet('192.168.1.0/24,10.90.90.0/24')
2405 assert rc
== 0 and not msg
2407 def test_ipv4_subnet_list_with_spaces(self
):
2408 rc
, v
, msg
= _cephadm
.check_subnet('192.168.1.0/24, 10.90.90.0/24 ')
2409 assert rc
== 0 and not msg
2411 def test_ipv4_subnet_badlist(self
):
2412 rc
, v
, msg
= _cephadm
.check_subnet('192.168.1.0/24,192.168.1.1')
2413 assert rc
== 1 and msg
2415 def test_ipv4_subnet_mixed(self
):
2416 rc
, v
, msg
= _cephadm
.check_subnet('192.168.100.0/24,fe80::/64')
2417 assert rc
== 0 and v
== [4,6]
2419 def test_ipv6_subnet(self
):
2420 rc
, v
, msg
= _cephadm
.check_subnet('fe80::/64')
2421 assert rc
== 0 and v
[0] == 6
2423 def test_subnet_mask_missing(self
):
2424 rc
, v
, msg
= _cephadm
.check_subnet('192.168.1.58')
2425 assert rc
== 1 and msg
2427 def test_subnet_mask_junk(self
):
2428 rc
, v
, msg
= _cephadm
.check_subnet('wah')
2429 assert rc
== 1 and msg
2431 def test_ip_in_subnet(self
):
2432 # valid ip and only one valid subnet
2433 rc
= _cephadm
.ip_in_subnets('192.168.100.1', '192.168.100.0/24')
2436 # valid ip and valid subnets list without spaces
2437 rc
= _cephadm
.ip_in_subnets('192.168.100.1', '192.168.100.0/24,10.90.90.0/24')
2440 # valid ip and valid subnets list with spaces
2441 rc
= _cephadm
.ip_in_subnets('10.90.90.2', '192.168.1.0/24, 192.168.100.0/24, 10.90.90.0/24')
2444 # valid ip that doesn't belong to any subnet
2445 rc
= _cephadm
.ip_in_subnets('192.168.100.2', '192.168.50.0/24, 10.90.90.0/24')
2448 # valid ip that doesn't belong to the subnet (only 14 hosts)
2449 rc
= _cephadm
.ip_in_subnets('192.168.100.20', '192.168.100.0/28')
2452 # valid ip and valid IPV6 network
2453 rc
= _cephadm
.ip_in_subnets('fe80::5054:ff:fef4:873a', 'fe80::/64')
2456 # valid wrapped ip and valid IPV6 network
2457 rc
= _cephadm
.ip_in_subnets('[fe80::5054:ff:fef4:873a]', 'fe80::/64')
2460 # valid ip and that doesn't belong to IPV6 network
2461 rc
= _cephadm
.ip_in_subnets('fe80::5054:ff:fef4:873a', '2001:db8:85a3::/64')
2464 # invalid IPv4 and valid subnets list
2465 with pytest
.raises(Exception):
2466 rc
= _cephadm
.ip_in_sublets('10.90.200.', '192.168.1.0/24, 192.168.100.0/24, 10.90.90.0/24')
2468 # invalid IPv6 and valid subnets list
2469 with pytest
.raises(Exception):
2470 rc
= _cephadm
.ip_in_sublets('fe80:2030:31:24', 'fe80::/64')
2472 @pytest.mark
.parametrize("conf", [
2474 public_network='1.1.1.0/24,2.2.2.0/24'
2475 cluster_network="3.3.3.0/24, 4.4.4.0/24"
2478 public_network=" 1.1.1.0/24,2.2.2.0/24 "
2479 cluster_network=3.3.3.0/24, 4.4.4.0/24
2482 public_network= 1.1.1.0/24, 2.2.2.0/24
2483 cluster_network='3.3.3.0/24,4.4.4.0/24'
2485 @mock.patch('cephadm.list_networks')
2486 @mock.patch('cephadm.logger')
2487 def test_get_networks_from_conf(self
, _logger
, _list_networks
, conf
, cephadm_fs
):
2488 cephadm_fs
.create_file('ceph.conf', contents
=conf
)
2489 _list_networks
.return_value
= {'1.1.1.0/24': {'eth0': ['1.1.1.1']},
2490 '2.2.2.0/24': {'eth1': ['2.2.2.2']},
2491 '3.3.3.0/24': {'eth2': ['3.3.3.3']},
2492 '4.4.4.0/24': {'eth3': ['4.4.4.4']}}
2493 ctx
= _cephadm
.CephadmContext()
2494 ctx
.config
= 'ceph.conf'
2495 ctx
.mon_ip
= '1.1.1.1'
2496 ctx
.cluster_network
= None
2497 # what the cephadm module does with the public network string is
2498 # [x.strip() for x in out.split(',')]
2499 # so we must make sure our output, through that alteration,
2500 # generates correctly formatted networks
2501 def _str_to_networks(s
):
2502 return [x
.strip() for x
in s
.split(',')]
2503 public_network
= _cephadm
.get_public_net_from_cfg(ctx
)
2504 assert _str_to_networks(public_network
) == ['1.1.1.0/24', '2.2.2.0/24']
2505 cluster_network
, ipv6
= _cephadm
.prepare_cluster_network(ctx
)
2507 assert _str_to_networks(cluster_network
) == ['3.3.3.0/24', '4.4.4.0/24']
2510 @mock.patch('cephadm.sysctl_get')
2511 def test_filter_sysctl_settings(self
, _sysctl_get
):
2512 ctx
= _cephadm
.CephadmContext()
2514 # comment-only lines should be ignored
2516 # As should whitespace-only lines",
2519 # inline comments are stripped when querying
2520 "something = value # inline comment",
2521 "fs.aio-max-nr = 1048576",
2522 "kernel.pid_max = 4194304",
2523 "vm.lowmem_reserve_ratio = 256\t256\t32\t0\t0",
2524 " vm.max_map_count = 65530 ",
2525 " vm.max_map_count = 65530 ",
2527 _sysctl_get
.side_effect
= [
2531 "256\t256\t32\t0\t0",
2535 result
= _cephadm
.filter_sysctl_settings(ctx
, input)
2536 assert len(_sysctl_get
.call_args_list
) == 6
2537 assert _sysctl_get
.call_args_list
[0].args
[1] == "something"
2538 assert _sysctl_get
.call_args_list
[1].args
[1] == "fs.aio-max-nr"
2539 assert _sysctl_get
.call_args_list
[2].args
[1] == "kernel.pid_max"
2540 assert _sysctl_get
.call_args_list
[3].args
[1] == "vm.lowmem_reserve_ratio"
2541 assert _sysctl_get
.call_args_list
[4].args
[1] == "vm.max_map_count"
2542 assert _sysctl_get
.call_args_list
[5].args
[1] == "vm.max_map_count"
2544 "fs.aio-max-nr = 1048576",
2545 " vm.max_map_count = 65530 ",
2549 single_es_node_conf
= {
2550 'elasticsearch_nodes': 'http://192.168.0.1:9200'}
2551 multiple_es_nodes_conf
= {
2552 'elasticsearch_nodes': 'http://192.168.0.1:9200,http://192.168.0.2:9300'}
2554 'collector_nodes': 'test:14250'}
2556 def test_single_es(self
, cephadm_fs
):
2557 fsid
= 'ca734440-3dc6-11ec-9b98-5254002537a6'
2558 with
with_cephadm_ctx(['--image=quay.io/jaegertracing/jaeger-collector:1.29'], list_networks
={}) as ctx
:
2560 ctx
.config_json
= json
.dumps(self
.single_es_node_conf
)
2562 c
= _cephadm
.get_container(ctx
, fsid
, 'jaeger-collector', 'daemon_id')
2563 _cephadm
.create_daemon_dirs(ctx
, fsid
, 'jaeger-collector', 'daemon_id', 0, 0)
2564 _cephadm
.deploy_daemon_units(
2573 with
open(f
'/var/lib/ceph/{fsid}/jaeger-collector.daemon_id/unit.run', 'r') as f
:
2574 run_cmd
= f
.readlines()[-1].rstrip()
2575 assert run_cmd
.endswith('SPAN_STORAGE_TYPE=elasticsearch -e ES_SERVER_URLS=http://192.168.0.1:9200 quay.io/jaegertracing/jaeger-collector:1.29')
2577 def test_multiple_es(self
, cephadm_fs
):
2578 fsid
= 'ca734440-3dc6-11ec-9b98-5254002537a6'
2579 with
with_cephadm_ctx(['--image=quay.io/jaegertracing/jaeger-collector:1.29'], list_networks
={}) as ctx
:
2581 ctx
.config_json
= json
.dumps(self
.multiple_es_nodes_conf
)
2583 c
= _cephadm
.get_container(ctx
, fsid
, 'jaeger-collector', 'daemon_id')
2584 _cephadm
.create_daemon_dirs(ctx
, fsid
, 'jaeger-collector', 'daemon_id', 0, 0)
2585 _cephadm
.deploy_daemon_units(
2594 with
open(f
'/var/lib/ceph/{fsid}/jaeger-collector.daemon_id/unit.run', 'r') as f
:
2595 run_cmd
= f
.readlines()[-1].rstrip()
2596 assert run_cmd
.endswith('SPAN_STORAGE_TYPE=elasticsearch -e ES_SERVER_URLS=http://192.168.0.1:9200,http://192.168.0.2:9300 quay.io/jaegertracing/jaeger-collector:1.29')
2598 def test_jaeger_agent(self
, cephadm_fs
):
2599 fsid
= 'ca734440-3dc6-11ec-9b98-5254002537a6'
2600 with
with_cephadm_ctx(['--image=quay.io/jaegertracing/jaeger-agent:1.29'], list_networks
={}) as ctx
:
2602 ctx
.config_json
= json
.dumps(self
.agent_conf
)
2604 c
= _cephadm
.get_container(ctx
, fsid
, 'jaeger-agent', 'daemon_id')
2605 _cephadm
.create_daemon_dirs(ctx
, fsid
, 'jaeger-agent', 'daemon_id', 0, 0)
2606 _cephadm
.deploy_daemon_units(
2615 with
open(f
'/var/lib/ceph/{fsid}/jaeger-agent.daemon_id/unit.run', 'r') as f
:
2616 run_cmd
= f
.readlines()[-1].rstrip()
2617 assert run_cmd
.endswith('quay.io/jaegertracing/jaeger-agent:1.29 --reporter.grpc.host-port=test:14250 --processor.jaeger-compact.server-host-port=6799')
2619 class TestRescan(fake_filesystem_unittest
.TestCase
):
2622 self
.setUpPyfakefs()
2623 if not fake_filesystem
.is_root():
2624 fake_filesystem
.set_uid(0)
2626 self
.fs
.create_dir('/sys/class')
2627 self
.ctx
= _cephadm
.CephadmContext()
2628 self
.ctx
.func
= _cephadm
.command_rescan_disks
2630 @mock.patch('cephadm.logger')
2631 def test_no_hbas(self
, _logger
):
2632 out
= _cephadm
.command_rescan_disks(self
.ctx
)
2633 assert out
== 'Ok. No compatible HBAs found'
2635 @mock.patch('cephadm.logger')
2636 def test_success(self
, _logger
):
2637 self
.fs
.create_file('/sys/class/scsi_host/host0/scan')
2638 self
.fs
.create_file('/sys/class/scsi_host/host1/scan')
2639 out
= _cephadm
.command_rescan_disks(self
.ctx
)
2640 assert out
.startswith('Ok. 2 adapters detected: 2 rescanned, 0 skipped, 0 failed')
2642 @mock.patch('cephadm.logger')
2643 def test_skip_usb_adapter(self
, _logger
):
2644 self
.fs
.create_file('/sys/class/scsi_host/host0/scan')
2645 self
.fs
.create_file('/sys/class/scsi_host/host1/scan')
2646 self
.fs
.create_file('/sys/class/scsi_host/host1/proc_name', contents
='usb-storage')
2647 out
= _cephadm
.command_rescan_disks(self
.ctx
)
2648 assert out
.startswith('Ok. 2 adapters detected: 1 rescanned, 1 skipped, 0 failed')
2650 @mock.patch('cephadm.logger')
2651 def test_skip_unknown_adapter(self
, _logger
):
2652 self
.fs
.create_file('/sys/class/scsi_host/host0/scan')
2653 self
.fs
.create_file('/sys/class/scsi_host/host1/scan')
2654 self
.fs
.create_file('/sys/class/scsi_host/host1/proc_name', contents
='unknown')
2655 out
= _cephadm
.command_rescan_disks(self
.ctx
)
2656 assert out
.startswith('Ok. 2 adapters detected: 1 rescanned, 1 skipped, 0 failed')