10 from textwrap
import dedent
12 from .fixtures
import (
20 from pyfakefs
import fake_filesystem_unittest
22 with mock
.patch('builtins.open', create
=True):
23 from importlib
.machinery
import SourceFileLoader
24 cd
= SourceFileLoader('cephadm', 'cephadm').load_module()
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
= cd
.CephadmContext()
41 ctx
.container_engine
= mock_docker()
42 r
= cd
.get_unit_file(ctx
, '9b9d7609-f4d5-4aba-94c8-effa764d96c9')
43 assert 'Requires=docker.service' in r
44 ctx
.container_engine
= mock_podman()
45 r
= cd
.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
), cd
.PortOccupiedError
),
61 (os_error(errno
.EAFNOSUPPORT
), cd
.Error
),
62 (os_error(errno
.EADDRNOTAVAIL
), cd
.Error
),
66 _socket
.bind
.side_effect
= side_effect
68 cd
.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 cd
.port_in_use(empty_ctx
, 9100) == False
82 attempt_bind
.side_effect
= cd
.PortOccupiedError('msg')
83 assert cd
.port_in_use(empty_ctx
, 9100) == True
86 os_error
.errno
= errno
.EADDRNOTAVAIL
87 attempt_bind
.side_effect
= os_error
88 assert cd
.port_in_use(empty_ctx
, 9100) == False
91 os_error
.errno
= errno
.EAFNOSUPPORT
92 attempt_bind
.side_effect
= os_error
93 assert cd
.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
= cd
.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 cd
.check_ip_port(ctx
, cd
.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
= cd
.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
), cd
.PortOccupiedError
),
129 (os_error(errno
.EADDRNOTAVAIL
), cd
.Error
),
130 (os_error(errno
.EAFNOSUPPORT
), cd
.Error
),
133 mock_socket_obj
= mock
.Mock()
134 mock_socket_obj
.bind
.side_effect
= side_effect
135 _socket
.return_value
= mock_socket_obj
137 cd
.check_ip_port(ctx
, cd
.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 cd
.is_fsid('no-uuid')
148 def test_is_fsid(self
):
149 assert cd
.is_fsid('e863154d-33c7-4350-bca5-921e0467e55b')
151 def test__get_parser_image(self
):
152 args
= cd
._parse
_args
(['--image', 'foo', 'version'])
153 assert args
.image
== 'foo'
155 def test_parse_mem_usage(self
):
156 cd
.logger
= mock
.Mock()
157 len, summary
= cd
._parse
_mem
_usage
(0, 'c6290e3f1489,-- / --')
160 def test_CustomValidation(self
):
161 assert cd
._parse
_args
(['deploy', '--name', 'mon.a', '--fsid', 'fsid'])
163 with pytest
.raises(SystemExit):
164 cd
._parse
_args
(['deploy', '--name', 'wrong', '--fsid', 'fsid'])
166 @pytest.mark
.parametrize("test_input, expected", [
168 ("1.6.2-stable2", (1,6,2)),
170 def test_parse_podman_version(self
, test_input
, expected
):
171 assert cd
._parse
_podman
_version
(test_input
) == expected
173 def test_parse_podman_version_invalid(self
):
174 with pytest
.raises(ValueError) as res
:
175 cd
._parse
_podman
_version
('inval.id')
176 assert 'inval' in str(res
.value
)
178 def test_is_ipv6(self
):
179 cd
.logger
= mock
.Mock()
180 for good
in ("[::1]", "::1",
181 "fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"):
182 assert cd
.is_ipv6(good
)
183 for bad
in ("127.0.0.1",
184 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffg",
185 "1:2:3:4:5:6:7:8:9", "fd00::1::1", "[fg::1]"):
186 assert not cd
.is_ipv6(bad
)
188 def test_unwrap_ipv6(self
):
189 def unwrap_test(address
, expected
):
190 assert cd
.unwrap_ipv6(address
) == expected
193 ('::1', '::1'), ('[::1]', '::1'),
194 ('[fde4:8dba:82e1:0:5054:ff:fe6a:357]', 'fde4:8dba:82e1:0:5054:ff:fe6a:357'),
195 ('can actually be any string', 'can actually be any string'),
196 ('[but needs to be stripped] ', '[but needs to be stripped] ')]
197 for address
, expected
in tests
:
198 unwrap_test(address
, expected
)
200 def test_wrap_ipv6(self
):
201 def wrap_test(address
, expected
):
202 assert cd
.wrap_ipv6(address
) == expected
205 ('::1', '[::1]'), ('[::1]', '[::1]'),
206 ('fde4:8dba:82e1:0:5054:ff:fe6a:357',
207 '[fde4:8dba:82e1:0:5054:ff:fe6a:357]'),
208 ('myhost.example.com', 'myhost.example.com'),
209 ('192.168.0.1', '192.168.0.1'),
210 ('', ''), ('fd00::1::1', 'fd00::1::1')]
211 for address
, expected
in tests
:
212 wrap_test(address
, expected
)
214 @mock.patch('cephadm.Firewalld', mock_bad_firewalld
)
215 @mock.patch('cephadm.logger')
216 def test_skip_firewalld(self
, logger
, cephadm_fs
):
218 test --skip-firewalld actually skips changing firewall
221 ctx
= cd
.CephadmContext()
222 with pytest
.raises(Exception):
223 cd
.update_firewalld(ctx
, 'mon')
225 ctx
.skip_firewalld
= True
226 cd
.update_firewalld(ctx
, 'mon')
228 ctx
.skip_firewalld
= False
229 with pytest
.raises(Exception):
230 cd
.update_firewalld(ctx
, 'mon')
232 ctx
= cd
.CephadmContext()
233 ctx
.ssl_dashboard_port
= 8888
234 ctx
.dashboard_key
= None
235 ctx
.dashboard_password_noupdate
= True
236 ctx
.initial_dashboard_password
= 'password'
237 ctx
.initial_dashboard_user
= 'User'
238 with pytest
.raises(Exception):
239 cd
.prepare_dashboard(ctx
, 0, 0, lambda _
, extra_mounts
=None, ___
=None : '5', lambda : None)
241 ctx
.skip_firewalld
= True
242 cd
.prepare_dashboard(ctx
, 0, 0, lambda _
, extra_mounts
=None, ___
=None : '5', lambda : None)
244 ctx
.skip_firewalld
= False
245 with pytest
.raises(Exception):
246 cd
.prepare_dashboard(ctx
, 0, 0, lambda _
, extra_mounts
=None, ___
=None : '5', lambda : None)
248 @mock.patch('cephadm.logger')
249 @mock.patch('cephadm.get_custom_config_files')
250 @mock.patch('cephadm.get_container')
251 def test_get_deployment_container(self
, _get_container
, _get_config
, logger
):
253 test get_deployment_container properly makes use of extra container args and custom conf files
256 ctx
= cd
.CephadmContext()
257 ctx
.config_json
= '-'
258 ctx
.extra_container_args
= [
259 '--pids-limit=12345',
262 ctx
.data_dir
= 'data'
263 _get_config
.return_value
= {'custom_config_files': [
265 'mount_path': '/etc/testing.str',
266 'content': 'this\nis\na\nstring',
269 _get_container
.return_value
= cd
.CephContainer
.for_daemon(
271 fsid
='9b9d7609-f4d5-4aba-94c8-effa764d96c9',
272 daemon_type
='grafana',
284 c
= cd
.get_deployment_container(ctx
,
285 '9b9d7609-f4d5-4aba-94c8-effa764d96c9',
289 assert '--pids-limit=12345' in c
.container_args
290 assert '--something' in c
.container_args
291 assert os
.path
.join('data', '9b9d7609-f4d5-4aba-94c8-effa764d96c9', 'custom_config_files', 'grafana.host1', 'testing.str') in c
.volume_mounts
292 assert c
.volume_mounts
[os
.path
.join('data', '9b9d7609-f4d5-4aba-94c8-effa764d96c9', 'custom_config_files', 'grafana.host1', 'testing.str')] == '/etc/testing.str'
294 @mock.patch('cephadm.logger')
295 @mock.patch('cephadm.get_custom_config_files')
296 def test_write_custom_conf_files(self
, _get_config
, logger
, cephadm_fs
):
298 test _write_custom_conf_files writes the conf files correctly
301 ctx
= cd
.CephadmContext()
302 ctx
.config_json
= '-'
303 ctx
.data_dir
= cd
.DATA_DIR
304 _get_config
.return_value
= {'custom_config_files': [
306 'mount_path': '/etc/testing.str',
307 'content': 'this\nis\na\nstring',
310 'mount_path': '/etc/testing.conf',
311 'content': 'very_cool_conf_setting: very_cool_conf_value\nx: y',
314 'mount_path': '/etc/no-content.conf',
317 cd
._write
_custom
_conf
_files
(ctx
, 'mon', 'host1', 'fsid', 0, 0)
318 with
open(os
.path
.join(cd
.DATA_DIR
, 'fsid', 'custom_config_files', 'mon.host1', 'testing.str'), 'r') as f
:
319 assert 'this\nis\na\nstring' == f
.read()
320 with
open(os
.path
.join(cd
.DATA_DIR
, 'fsid', 'custom_config_files', 'mon.host1', 'testing.conf'), 'r') as f
:
321 assert 'very_cool_conf_setting: very_cool_conf_value\nx: y' == f
.read()
322 with pytest
.raises(FileNotFoundError
):
323 open(os
.path
.join(cd
.DATA_DIR
, 'fsid', 'custom_config_files', 'mon.host1', 'no-content.conf'), 'r')
325 @mock.patch('cephadm.call_throws')
326 @mock.patch('cephadm.get_parm')
327 def test_registry_login(self
, get_parm
, call_throws
):
328 # test normal valid login with url, username and password specified
329 call_throws
.return_value
= '', '', 0
330 ctx
: cd
.CephadmContext
= cd
.cephadm_init_ctx(
331 ['registry-login', '--registry-url', 'sample-url',
332 '--registry-username', 'sample-user', '--registry-password',
334 ctx
.container_engine
= mock_docker()
335 retval
= cd
.command_registry_login(ctx
)
338 # test bad login attempt with invalid arguments given
339 ctx
: cd
.CephadmContext
= cd
.cephadm_init_ctx(
340 ['registry-login', '--registry-url', 'bad-args-url'])
341 with pytest
.raises(Exception) as e
:
342 assert cd
.command_registry_login(ctx
)
343 assert str(e
.value
) == ('Invalid custom registry arguments received. To login to a custom registry include '
344 '--registry-url, --registry-username and --registry-password options or --registry-json option')
346 # test normal valid login with json file
347 get_parm
.return_value
= {"url": "sample-url", "username": "sample-username", "password": "sample-password"}
348 ctx
: cd
.CephadmContext
= cd
.cephadm_init_ctx(
349 ['registry-login', '--registry-json', 'sample-json'])
350 ctx
.container_engine
= mock_docker()
351 retval
= cd
.command_registry_login(ctx
)
354 # test bad login attempt with bad json file
355 get_parm
.return_value
= {"bad-json": "bad-json"}
356 ctx
: cd
.CephadmContext
= cd
.cephadm_init_ctx(
357 ['registry-login', '--registry-json', 'sample-json'])
358 with pytest
.raises(Exception) as e
:
359 assert cd
.command_registry_login(ctx
)
360 assert str(e
.value
) == ("json provided for custom registry login did not include all necessary fields. "
361 "Please setup json file as\n"
363 " \"url\": \"REGISTRY_URL\",\n"
364 " \"username\": \"REGISTRY_USERNAME\",\n"
365 " \"password\": \"REGISTRY_PASSWORD\"\n"
368 # test login attempt with valid arguments where login command fails
369 call_throws
.side_effect
= Exception
370 ctx
: cd
.CephadmContext
= cd
.cephadm_init_ctx(
371 ['registry-login', '--registry-url', 'sample-url',
372 '--registry-username', 'sample-user', '--registry-password',
374 with pytest
.raises(Exception) as e
:
375 cd
.command_registry_login(ctx
)
376 assert str(e
.value
) == "Failed to login to custom registry @ sample-url as sample-user with given password"
378 def test_get_image_info_from_inspect(self
):
380 out
= """204a01f9b0b6710dd0c0af7f37ce7139c47ff0f0105d778d7104c69282dfbbf1,[docker.io/ceph/ceph@sha256:1cc9b824e1b076cdff52a9aa3f0cc8557d879fb2fbbba0cafed970aca59a3992]"""
381 r
= cd
.get_image_info_from_inspect(out
, 'registry/ceph/ceph:latest')
384 'image_id': '204a01f9b0b6710dd0c0af7f37ce7139c47ff0f0105d778d7104c69282dfbbf1',
385 'repo_digests': ['docker.io/ceph/ceph@sha256:1cc9b824e1b076cdff52a9aa3f0cc8557d879fb2fbbba0cafed970aca59a3992']
389 out
= """sha256:16f4549cf7a8f112bbebf7946749e961fbbd1b0838627fe619aab16bc17ce552,[quay.ceph.io/ceph-ci/ceph@sha256:4e13da36c1bd6780b312a985410ae678984c37e6a9493a74c87e4a50b9bda41f]"""
390 r
= cd
.get_image_info_from_inspect(out
, 'registry/ceph/ceph:latest')
392 'image_id': '16f4549cf7a8f112bbebf7946749e961fbbd1b0838627fe619aab16bc17ce552',
393 'repo_digests': ['quay.ceph.io/ceph-ci/ceph@sha256:4e13da36c1bd6780b312a985410ae678984c37e6a9493a74c87e4a50b9bda41f']
396 # multiple digests (podman)
397 out
= """e935122ab143a64d92ed1fbb27d030cf6e2f0258207be1baf1b509c466aeeb42,[docker.io/prom/prometheus@sha256:e4ca62c0d62f3e886e684806dfe9d4e0cda60d54986898173c1083856cfda0f4 docker.io/prom/prometheus@sha256:efd99a6be65885c07c559679a0df4ec709604bcdd8cd83f0d00a1a683b28fb6a]"""
398 r
= cd
.get_image_info_from_inspect(out
, 'registry/prom/prometheus:latest')
400 'image_id': 'e935122ab143a64d92ed1fbb27d030cf6e2f0258207be1baf1b509c466aeeb42',
402 'docker.io/prom/prometheus@sha256:e4ca62c0d62f3e886e684806dfe9d4e0cda60d54986898173c1083856cfda0f4',
403 'docker.io/prom/prometheus@sha256:efd99a6be65885c07c559679a0df4ec709604bcdd8cd83f0d00a1a683b28fb6a',
408 def test_dict_get(self
):
409 result
= cd
.dict_get({'a': 1}, 'a', require
=True)
411 result
= cd
.dict_get({'a': 1}, 'b')
412 assert result
is None
413 result
= cd
.dict_get({'a': 1}, 'b', default
=2)
416 def test_dict_get_error(self
):
417 with pytest
.raises(cd
.Error
):
418 cd
.dict_get({'a': 1}, 'b', require
=True)
420 def test_dict_get_join(self
):
421 result
= cd
.dict_get_join({'foo': ['a', 'b']}, 'foo')
422 assert result
== 'a\nb'
423 result
= cd
.dict_get_join({'foo': [1, 2]}, 'foo')
424 assert result
== '1\n2'
425 result
= cd
.dict_get_join({'bar': 'a'}, 'bar')
427 result
= cd
.dict_get_join({'a': 1}, 'a')
430 @mock.patch('os.listdir', return_value
=[])
431 @mock.patch('cephadm.logger')
432 def test_infer_local_ceph_image(self
, _logger
, _listdir
):
433 ctx
= cd
.CephadmContext()
434 ctx
.fsid
= '00000000-0000-0000-0000-0000deadbeez'
435 ctx
.container_engine
= mock_podman()
437 # make sure the right image is selected when container is found
438 cinfo
= cd
.ContainerInfo('935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972',
439 'registry.hub.docker.com/rkachach/ceph:custom-v0.5',
440 '514e6a882f6e74806a5856468489eeff8d7106095557578da96935e4d0ba4d9d',
441 '2022-04-19 13:45:20.97146228 +0000 UTC',
443 out
= '''quay.ceph.io/ceph-ci/ceph@sha256:87f200536bb887b36b959e887d5984dd7a3f008a23aa1f283ab55d48b22c6185|dad864ee21e9|master|2022-03-23 16:29:19 +0000 UTC
444 quay.ceph.io/ceph-ci/ceph@sha256:b50b130fcda2a19f8507ddde3435bb4722266956e1858ac395c838bc1dcf1c0e|514e6a882f6e|pacific|2022-03-23 15:58:34 +0000 UTC
445 docker.io/ceph/ceph@sha256:939a46c06b334e094901560c8346de33c00309e3e3968a2db240eb4897c6a508|666bbfa87e8d|v15.2.5|2020-09-16 14:15:15 +0000 UTC'''
446 with mock
.patch('cephadm.call_throws', return_value
=(out
, '', '')):
447 with mock
.patch('cephadm.get_container_info', return_value
=cinfo
):
448 image
= cd
.infer_local_ceph_image(ctx
, ctx
.container_engine
)
449 assert image
== 'quay.ceph.io/ceph-ci/ceph@sha256:b50b130fcda2a19f8507ddde3435bb4722266956e1858ac395c838bc1dcf1c0e'
451 # make sure first valid image is used when no container_info is found
452 out
= '''quay.ceph.io/ceph-ci/ceph@sha256:87f200536bb887b36b959e887d5984dd7a3f008a23aa1f283ab55d48b22c6185|dad864ee21e9|master|2022-03-23 16:29:19 +0000 UTC
453 quay.ceph.io/ceph-ci/ceph@sha256:b50b130fcda2a19f8507ddde3435bb4722266956e1858ac395c838bc1dcf1c0e|514e6a882f6e|pacific|2022-03-23 15:58:34 +0000 UTC
454 docker.io/ceph/ceph@sha256:939a46c06b334e094901560c8346de33c00309e3e3968a2db240eb4897c6a508|666bbfa87e8d|v15.2.5|2020-09-16 14:15:15 +0000 UTC'''
455 with mock
.patch('cephadm.call_throws', return_value
=(out
, '', '')):
456 with mock
.patch('cephadm.get_container_info', return_value
=None):
457 image
= cd
.infer_local_ceph_image(ctx
, ctx
.container_engine
)
458 assert image
== 'quay.ceph.io/ceph-ci/ceph@sha256:87f200536bb887b36b959e887d5984dd7a3f008a23aa1f283ab55d48b22c6185'
460 # make sure images without digest are discarded (no container_info is found)
461 out
= '''quay.ceph.io/ceph-ci/ceph@|||
462 docker.io/ceph/ceph@|||
463 docker.io/ceph/ceph@sha256:939a46c06b334e094901560c8346de33c00309e3e3968a2db240eb4897c6a508|666bbfa87e8d|v15.2.5|2020-09-16 14:15:15 +0000 UTC'''
464 with mock
.patch('cephadm.call_throws', return_value
=(out
, '', '')):
465 with mock
.patch('cephadm.get_container_info', return_value
=None):
466 image
= cd
.infer_local_ceph_image(ctx
, ctx
.container_engine
)
467 assert image
== 'docker.io/ceph/ceph@sha256:939a46c06b334e094901560c8346de33c00309e3e3968a2db240eb4897c6a508'
471 @pytest.mark
.parametrize('daemon_filter, by_name, daemon_list, container_stats, output',
473 # get container info by type ('mon')
478 {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
479 {'name': 'mgr.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
481 ("935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972,registry.hub.docker.com/rkachach/ceph:custom-v0.5,666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4,2022-04-19 13:45:20.97146228 +0000 UTC,",
484 cd
.ContainerInfo('935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972',
485 'registry.hub.docker.com/rkachach/ceph:custom-v0.5',
486 '666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4',
487 '2022-04-19 13:45:20.97146228 +0000 UTC',
490 # get container info by name ('mon.ceph-node-0')
495 {'name': 'mgr.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
496 {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
498 ("935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972,registry.hub.docker.com/rkachach/ceph:custom-v0.5,666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4,2022-04-19 13:45:20.97146228 +0000 UTC,",
501 cd
.ContainerInfo('935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972',
502 'registry.hub.docker.com/rkachach/ceph:custom-v0.5',
503 '666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4',
504 '2022-04-19 13:45:20.97146228 +0000 UTC',
507 # get container info by name (same daemon but two different fsids)
512 {'name': 'mon.ceph-node-0', 'fsid': '10000000-0000-0000-0000-0000deadbeef'},
513 {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
515 ("935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972,registry.hub.docker.com/rkachach/ceph:custom-v0.5,666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4,2022-04-19 13:45:20.97146228 +0000 UTC,",
518 cd
.ContainerInfo('935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972',
519 'registry.hub.docker.com/rkachach/ceph:custom-v0.5',
520 '666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4',
521 '2022-04-19 13:45:20.97146228 +0000 UTC',
524 # get container info by type (bad container stats: 127 code)
529 {'name': 'mon.ceph-node-0', 'fsid': '00000000-FFFF-0000-0000-0000deadbeef'},
530 {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
537 # get container info by name (bad container stats: 127 code)
542 {'name': 'mgr.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
543 {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
550 # get container info by invalid name (doens't contain '.')
555 {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
556 {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
558 ("935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972,registry.hub.docker.com/rkachach/ceph:custom-v0.5,666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4,2022-04-19 13:45:20.97146228 +0000 UTC,",
563 # get container info by invalid name (empty)
568 {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
569 {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
571 ("935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972,registry.hub.docker.com/rkachach/ceph:custom-v0.5,666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4,2022-04-19 13:45:20.97146228 +0000 UTC,",
576 # get container info by invalid type (empty)
581 {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
582 {'name': 'mon.ceph-node-0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'},
584 ("935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972,registry.hub.docker.com/rkachach/ceph:custom-v0.5,666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4,2022-04-19 13:45:20.97146228 +0000 UTC,",
589 # get container info by name: no match (invalid fsid)
594 {'name': 'mon.ceph-node-0', 'fsid': '00000000-1111-0000-0000-0000deadbeef'},
595 {'name': 'mon.ceph-node-0', 'fsid': '00000000-2222-0000-0000-0000deadbeef'},
597 ("935b549714b8f007c6a4e29c758689cf9e8e69f2e0f51180506492974b90a972,registry.hub.docker.com/rkachach/ceph:custom-v0.5,666bbfa87e8df05702d6172cae11dd7bc48efb1d94f1b9e492952f19647199a4,2022-04-19 13:45:20.97146228 +0000 UTC,",
602 # get container info by name: no match
610 # get container info by type: no match
619 def test_get_container_info(self
, daemon_filter
, by_name
, daemon_list
, container_stats
, output
):
620 cd
.logger
= mock
.Mock()
621 ctx
= cd
.CephadmContext()
622 ctx
.fsid
= '00000000-0000-0000-0000-0000deadbeef'
623 ctx
.container_engine
= mock_podman()
624 with mock
.patch('cephadm.list_daemons', return_value
=daemon_list
):
625 with mock
.patch('cephadm.get_container_stats', return_value
=container_stats
):
626 assert cd
.get_container_info(ctx
, daemon_filter
, by_name
) == output
628 def test_should_log_to_journald(self
):
629 ctx
= cd
.CephadmContext()
631 ctx
.log_to_journald
= True
632 assert cd
.should_log_to_journald(ctx
)
634 ctx
.log_to_journald
= None
635 # enable if podman support --cgroup=split
636 ctx
.container_engine
= mock_podman()
637 ctx
.container_engine
.version
= (2, 1, 0)
638 assert cd
.should_log_to_journald(ctx
)
640 # disable on old podman
641 ctx
.container_engine
.version
= (2, 0, 0)
642 assert not cd
.should_log_to_journald(ctx
)
645 ctx
.container_engine
= mock_docker()
646 assert not cd
.should_log_to_journald(ctx
)
648 def test_normalize_image_digest(self
):
649 s
= 'myhostname:5000/ceph/ceph@sha256:753886ad9049004395ae990fbb9b096923b5a518b819283141ee8716ddf55ad1'
650 assert cd
.normalize_image_digest(s
) == s
652 s
= 'ceph/ceph:latest'
653 assert cd
.normalize_image_digest(s
) == f
'{cd.DEFAULT_REGISTRY}/{s}'
655 @pytest.mark
.parametrize('fsid, ceph_conf, list_daemons, result, err, ',
665 '00000000-0000-0000-0000-0000deadbeef',
668 '00000000-0000-0000-0000-0000deadbeef',
672 '00000000-0000-0000-0000-0000deadbeef',
675 {'fsid': '10000000-0000-0000-0000-0000deadbeef'},
676 {'fsid': '20000000-0000-0000-0000-0000deadbeef'},
678 '00000000-0000-0000-0000-0000deadbeef',
685 {'fsid': '00000000-0000-0000-0000-0000deadbeef'},
687 '00000000-0000-0000-0000-0000deadbeef',
694 {'fsid': '10000000-0000-0000-0000-0000deadbeef'},
695 {'fsid': '20000000-0000-0000-0000-0000deadbeef'},
698 r
'Cannot infer an fsid',
702 get_ceph_conf(fsid
='00000000-0000-0000-0000-0000deadbeef'),
704 '00000000-0000-0000-0000-0000deadbeef',
709 get_ceph_conf(fsid
='00000000-0000-0000-0000-0000deadbeef'),
711 {'fsid': '00000000-0000-0000-0000-0000deadbeef'},
713 '00000000-0000-0000-0000-0000deadbeef',
718 get_ceph_conf(fsid
='00000000-0000-0000-0000-0000deadbeef'),
720 {'fsid': '10000000-0000-0000-0000-0000deadbeef'},
721 {'fsid': '20000000-0000-0000-0000-0000deadbeef'},
724 r
'Cannot infer an fsid',
727 @mock.patch('cephadm.call')
728 def test_infer_fsid(self
, _call
, fsid
, ceph_conf
, list_daemons
, result
, err
, cephadm_fs
):
730 ctx
= cd
.CephadmContext()
734 mock_fn
= mock
.Mock()
735 mock_fn
.return_value
= 0
736 infer_fsid
= cd
.infer_fsid(mock_fn
)
738 # mock the ceph.conf file content
740 f
= cephadm_fs
.create_file('ceph.conf', contents
=ceph_conf
)
744 with mock
.patch('cephadm.list_daemons', return_value
=list_daemons
):
746 with pytest
.raises(cd
.Error
, match
=err
):
750 assert ctx
.fsid
== result
752 @pytest.mark
.parametrize('fsid, other_conf_files, config, name, list_daemons, result, ',
754 # per cluster conf has more precedence than default conf
756 '00000000-0000-0000-0000-0000deadbeef',
757 [cd
.CEPH_DEFAULT_CONF
],
761 '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/config/ceph.conf',
763 # mon daemon conf has more precedence than cluster conf and default conf
765 '00000000-0000-0000-0000-0000deadbeef',
766 ['/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/config/ceph.conf',
767 cd
.CEPH_DEFAULT_CONF
],
770 [{'name': 'mon.a', 'fsid': '00000000-0000-0000-0000-0000deadbeef', 'style': 'cephadm:v1'}],
771 '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/mon.a/config',
773 # daemon conf (--name option) has more precedence than cluster, default and mon conf
775 '00000000-0000-0000-0000-0000deadbeef',
776 ['/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/config/ceph.conf',
777 '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/mon.a/config',
778 cd
.CEPH_DEFAULT_CONF
],
781 [{'name': 'mon.a', 'fsid': '00000000-0000-0000-0000-0000deadbeef', 'style': 'cephadm:v1'},
782 {'name': 'osd.0', 'fsid': '00000000-0000-0000-0000-0000deadbeef'}],
783 '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/osd.0/config',
785 # user provided conf ('/foo/ceph.conf') more precedence than any other conf
787 '00000000-0000-0000-0000-0000deadbeef',
788 ['/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/config/ceph.conf',
789 cd
.CEPH_DEFAULT_CONF
,
790 '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/mon.a/config'],
793 [{'name': 'mon.a', 'fsid': '00000000-0000-0000-0000-0000deadbeef', 'style': 'cephadm:v1'}],
797 @mock.patch('cephadm.call')
798 @mock.patch('cephadm.logger')
799 def test_infer_config_precedence(self
, logger
, _call
, other_conf_files
, fsid
, config
, name
, list_daemons
, result
, cephadm_fs
):
801 ctx
= cd
.CephadmContext()
807 mock_fn
= mock
.Mock()
808 mock_fn
.return_value
= 0
809 infer_config
= cd
.infer_config(mock_fn
)
811 # mock the config file
812 cephadm_fs
.create_file(result
)
814 # mock other potential config files
815 for f
in other_conf_files
:
816 cephadm_fs
.create_file(f
)
819 with mock
.patch('cephadm.list_daemons', return_value
=list_daemons
):
821 assert ctx
.config
== result
823 @pytest.mark
.parametrize('fsid, config, name, list_daemons, result, ',
833 '00000000-0000-0000-0000-0000deadbeef',
837 cd
.CEPH_DEFAULT_CONF
,
840 '00000000-0000-0000-0000-0000deadbeef',
844 '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/config/ceph.conf',
847 '00000000-0000-0000-0000-0000deadbeef',
850 [{'name': 'mon.a', 'fsid': '00000000-0000-0000-0000-0000deadbeef', 'style': 'cephadm:v1'}],
851 '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/mon.a/config',
854 '00000000-0000-0000-0000-0000deadbeef',
857 [{'name': 'mon.a', 'fsid': 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'style': 'cephadm:v1'}],
858 cd
.CEPH_DEFAULT_CONF
,
861 '00000000-0000-0000-0000-0000deadbeef',
864 [{'name': 'mon.a', 'fsid': '00000000-0000-0000-0000-0000deadbeef', 'style': 'legacy'}],
865 cd
.CEPH_DEFAULT_CONF
,
868 '00000000-0000-0000-0000-0000deadbeef',
872 cd
.CEPH_DEFAULT_CONF
,
875 '00000000-0000-0000-0000-0000deadbeef',
878 [{'name': 'mon.a', 'style': 'cephadm:v1'}],
882 '00000000-0000-0000-0000-0000deadbeef',
886 '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/mon.a/config',
889 '00000000-0000-0000-0000-0000deadbeef',
893 '/var/lib/ceph/00000000-0000-0000-0000-0000deadbeef/osd.0/config',
900 cd
.CEPH_DEFAULT_CONF
,
903 @mock.patch('cephadm.call')
904 @mock.patch('cephadm.logger')
905 def test_infer_config(self
, logger
, _call
, fsid
, config
, name
, list_daemons
, result
, cephadm_fs
):
907 ctx
= cd
.CephadmContext()
913 mock_fn
= mock
.Mock()
914 mock_fn
.return_value
= 0
915 infer_config
= cd
.infer_config(mock_fn
)
917 # mock the config file
918 cephadm_fs
.create_file(result
)
921 with mock
.patch('cephadm.list_daemons', return_value
=list_daemons
):
923 assert ctx
.config
== result
925 @mock.patch('cephadm.call')
926 def test_extract_uid_gid_fail(self
, _call
):
927 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
928 ff792c06d8544b983.scope not found.: OCI runtime error"""
929 _call
.return_value
= ('', err
, 127)
930 ctx
= cd
.CephadmContext()
931 ctx
.container_engine
= mock_podman()
932 with pytest
.raises(cd
.Error
, match
='OCI'):
933 cd
.extract_uid_gid(ctx
)
935 @pytest.mark
.parametrize('test_input, expected', [
936 ([cd
.make_fsid(), cd
.make_fsid(), cd
.make_fsid()], 3),
937 ([cd
.make_fsid(), 'invalid-fsid', cd
.make_fsid(), '0b87e50c-8e77-11ec-b890-'], 2),
938 (['f6860ec2-8e76-11ec-', '0b87e50c-8e77-11ec-b890-', ''], 0),
941 def test_get_ceph_cluster_count(self
, test_input
, expected
):
942 ctx
= cd
.CephadmContext()
943 with mock
.patch('os.listdir', return_value
=test_input
):
944 assert cd
.get_ceph_cluster_count(ctx
) == expected
946 def test_set_image_minimize_config(self
):
948 raise cd
.Error(' '.join(cmd
))
949 ctx
= cd
.CephadmContext()
950 ctx
.image
= 'test_image'
951 ctx
.no_minimize_config
= True
952 fake_cli
= lambda cmd
, __
=None, ___
=None: throw_cmd(cmd
)
953 with pytest
.raises(cd
.Error
, match
='config set global container_image test_image'):
954 cd
.finish_bootstrap_config(
958 mon_id
='a', mon_dir
='mon_dir',
959 mon_network
=None, ipv6
=False,
961 cluster_network
=None,
962 ipv6_cluster_network
=False
966 class TestCustomContainer(unittest
.TestCase
):
967 cc
: cd
.CustomContainer
970 self
.cc
= cd
.CustomContainer(
971 'e863154d-33c7-4350-bca5-921e0467e55b',
974 'entrypoint': 'bash',
980 'envs': ['SECRET=password'],
981 'ports': [8080, 8443],
983 '/CONFIG_DIR': '/foo/conf',
984 'bar/config': '/bar:ro'
989 'source=/CONFIG_DIR',
990 'destination=/foo/conf',
996 'destination=/bar:ro',
1001 image
='docker.io/library/hello-world:latest'
1004 def test_entrypoint(self
):
1005 self
.assertEqual(self
.cc
.entrypoint
, 'bash')
1007 def test_uid_gid(self
):
1008 self
.assertEqual(self
.cc
.uid
, 65534)
1009 self
.assertEqual(self
.cc
.gid
, 1000)
1011 def test_ports(self
):
1012 self
.assertEqual(self
.cc
.ports
, [8080, 8443])
1014 def test_get_container_args(self
):
1015 result
= self
.cc
.get_container_args()
1016 self
.assertEqual(result
, [
1021 def test_get_container_envs(self
):
1022 result
= self
.cc
.get_container_envs()
1023 self
.assertEqual(result
, ['SECRET=password'])
1025 def test_get_container_mounts(self
):
1026 result
= self
.cc
.get_container_mounts('/xyz')
1027 self
.assertDictEqual(result
, {
1028 '/CONFIG_DIR': '/foo/conf',
1029 '/xyz/bar/config': '/bar:ro'
1032 def test_get_container_binds(self
):
1033 result
= self
.cc
.get_container_binds('/xyz')
1034 self
.assertEqual(result
, [
1037 'source=/CONFIG_DIR',
1038 'destination=/foo/conf',
1043 'source=/xyz/bar/config',
1044 'destination=/bar:ro',
1050 class TestMaintenance
:
1051 systemd_target
= "ceph.00000000-0000-0000-0000-000000c0ffee.target"
1052 fsid
= '0ea8cdd0-1bbf-11ec-a9c7-5254002763fa'
1054 def test_systemd_target_OK(self
, tmp_path
):
1056 wants
= base
/ "ceph.target.wants"
1058 target
= wants
/ TestMaintenance
.systemd_target
1060 ctx
= cd
.CephadmContext()
1061 ctx
.unit_dir
= str(base
)
1063 assert cd
.systemd_target_state(ctx
, target
.name
)
1065 def test_systemd_target_NOTOK(self
, tmp_path
):
1067 ctx
= cd
.CephadmContext()
1068 ctx
.unit_dir
= str(base
)
1069 assert not cd
.systemd_target_state(ctx
, TestMaintenance
.systemd_target
)
1071 def test_parser_OK(self
):
1072 args
= cd
._parse
_args
(['host-maintenance', 'enter'])
1073 assert args
.maintenance_action
== 'enter'
1075 def test_parser_BAD(self
):
1076 with pytest
.raises(SystemExit):
1077 cd
._parse
_args
(['host-maintenance', 'wah'])
1079 @mock.patch('os.listdir', return_value
=[])
1080 @mock.patch('cephadm.call')
1081 @mock.patch('cephadm.systemd_target_state')
1082 def test_enter_failure_1(self
, _target_state
, _call
, _listdir
):
1083 _call
.return_value
= '', '', 999
1084 _target_state
.return_value
= True
1085 ctx
: cd
.CephadmContext
= cd
.cephadm_init_ctx(
1086 ['host-maintenance', 'enter', '--fsid', TestMaintenance
.fsid
])
1087 ctx
.container_engine
= mock_podman()
1088 retval
= cd
.command_maintenance(ctx
)
1089 assert retval
.startswith('failed')
1091 @mock.patch('os.listdir', return_value
=[])
1092 @mock.patch('cephadm.call')
1093 @mock.patch('cephadm.systemd_target_state')
1094 def test_enter_failure_2(self
, _target_state
, _call
, _listdir
):
1095 _call
.side_effect
= [('', '', 0), ('', '', 999), ('', '', 0), ('', '', 999)]
1096 _target_state
.return_value
= True
1097 ctx
: cd
.CephadmContext
= cd
.cephadm_init_ctx(
1098 ['host-maintenance', 'enter', '--fsid', TestMaintenance
.fsid
])
1099 ctx
.container_engine
= mock_podman()
1100 retval
= cd
.command_maintenance(ctx
)
1101 assert retval
.startswith('failed')
1103 @mock.patch('os.listdir', return_value
=[])
1104 @mock.patch('cephadm.call')
1105 @mock.patch('cephadm.systemd_target_state')
1106 @mock.patch('cephadm.target_exists')
1107 def test_exit_failure_1(self
, _target_exists
, _target_state
, _call
, _listdir
):
1108 _call
.return_value
= '', '', 999
1109 _target_state
.return_value
= False
1110 _target_exists
.return_value
= True
1111 ctx
: cd
.CephadmContext
= cd
.cephadm_init_ctx(
1112 ['host-maintenance', 'exit', '--fsid', TestMaintenance
.fsid
])
1113 ctx
.container_engine
= mock_podman()
1114 retval
= cd
.command_maintenance(ctx
)
1115 assert retval
.startswith('failed')
1117 @mock.patch('os.listdir', return_value
=[])
1118 @mock.patch('cephadm.call')
1119 @mock.patch('cephadm.systemd_target_state')
1120 @mock.patch('cephadm.target_exists')
1121 def test_exit_failure_2(self
, _target_exists
, _target_state
, _call
, _listdir
):
1122 _call
.side_effect
= [('', '', 0), ('', '', 999), ('', '', 0), ('', '', 999)]
1123 _target_state
.return_value
= False
1124 _target_exists
.return_value
= True
1125 ctx
: cd
.CephadmContext
= cd
.cephadm_init_ctx(
1126 ['host-maintenance', 'exit', '--fsid', TestMaintenance
.fsid
])
1127 ctx
.container_engine
= mock_podman()
1128 retval
= cd
.command_maintenance(ctx
)
1129 assert retval
.startswith('failed')
1132 class TestMonitoring(object):
1133 @mock.patch('cephadm.call')
1134 def test_get_version_alertmanager(self
, _call
):
1135 ctx
= cd
.CephadmContext()
1136 ctx
.container_engine
= mock_podman()
1137 daemon_type
= 'alertmanager'
1139 # binary `prometheus`
1140 _call
.return_value
= '', '{}, version 0.16.1'.format(daemon_type
), 0
1141 version
= cd
.Monitoring
.get_version(ctx
, 'container_id', daemon_type
)
1142 assert version
== '0.16.1'
1144 # binary `prometheus-alertmanager`
1145 _call
.side_effect
= (
1147 ('', '{}, version 0.16.1'.format(daemon_type
), 0),
1149 version
= cd
.Monitoring
.get_version(ctx
, 'container_id', daemon_type
)
1150 assert version
== '0.16.1'
1152 @mock.patch('cephadm.call')
1153 def test_get_version_prometheus(self
, _call
):
1154 ctx
= cd
.CephadmContext()
1155 ctx
.container_engine
= mock_podman()
1156 daemon_type
= 'prometheus'
1157 _call
.return_value
= '', '{}, version 0.16.1'.format(daemon_type
), 0
1158 version
= cd
.Monitoring
.get_version(ctx
, 'container_id', daemon_type
)
1159 assert version
== '0.16.1'
1161 def test_prometheus_external_url(self
):
1162 ctx
= cd
.CephadmContext()
1163 daemon_type
= 'prometheus'
1165 fsid
= 'aaf5a720-13fe-4a3b-82b9-2d99b7fd9704'
1166 args
= cd
.get_daemon_args(ctx
, fsid
, daemon_type
, daemon_id
)
1167 assert any([x
.startswith('--web.external-url=http://') for x
in args
])
1169 @mock.patch('cephadm.call')
1170 def test_get_version_node_exporter(self
, _call
):
1171 ctx
= cd
.CephadmContext()
1172 ctx
.container_engine
= mock_podman()
1173 daemon_type
= 'node-exporter'
1174 _call
.return_value
= '', '{}, version 0.16.1'.format(daemon_type
.replace('-', '_')), 0
1175 version
= cd
.Monitoring
.get_version(ctx
, 'container_id', daemon_type
)
1176 assert version
== '0.16.1'
1178 def test_create_daemon_dirs_prometheus(self
, cephadm_fs
):
1180 Ensures the required and optional files given in the configuration are
1181 created and mapped correctly inside the container. Tests absolute and
1182 relative file paths given in the configuration.
1185 fsid
= 'aaf5a720-13fe-4a3b-82b9-2d99b7fd9704'
1186 daemon_type
= 'prometheus'
1189 ctx
= cd
.CephadmContext()
1190 ctx
.data_dir
= '/somedir'
1191 ctx
.config_json
= json
.dumps({
1193 'prometheus.yml': 'foo',
1194 '/etc/prometheus/alerting/ceph_alerts.yml': 'bar'
1198 cd
.create_daemon_dirs(ctx
,
1207 prefix
= '{data_dir}/{fsid}/{daemon_type}.{daemon_id}'.format(
1208 data_dir
=ctx
.data_dir
,
1210 daemon_type
=daemon_type
,
1215 'etc/prometheus/prometheus.yml': 'foo',
1216 'etc/prometheus/alerting/ceph_alerts.yml': 'bar',
1219 for file,content
in expected
.items():
1220 file = os
.path
.join(prefix
, file)
1221 assert os
.path
.exists(file)
1222 with
open(file) as f
:
1223 assert f
.read() == content
1225 # assert uid/gid after redeploy
1228 cd
.create_daemon_dirs(ctx
,
1236 for file,content
in expected
.items():
1237 file = os
.path
.join(prefix
, file)
1238 assert os
.stat(file).st_uid
== new_uid
1239 assert os
.stat(file).st_gid
== new_gid
1242 class TestBootstrap(object):
1245 def _get_cmd(*args
):
1248 '--allow-mismatched-release',
1249 '--skip-prepare-host',
1255 ###############################################3
1257 def test_config(self
, cephadm_fs
):
1259 cmd
= self
._get
_cmd
(
1260 '--mon-ip', '192.168.1.1',
1261 '--skip-mon-network',
1262 '--config', conf_file
,
1265 with
with_cephadm_ctx(cmd
) as ctx
:
1266 msg
= r
'No such file or directory'
1267 with pytest
.raises(cd
.Error
, match
=msg
):
1268 cd
.command_bootstrap(ctx
)
1270 cephadm_fs
.create_file(conf_file
)
1271 with
with_cephadm_ctx(cmd
) as ctx
:
1272 retval
= cd
.command_bootstrap(ctx
)
1275 def test_no_mon_addr(self
, cephadm_fs
):
1276 cmd
= self
._get
_cmd
()
1277 with
with_cephadm_ctx(cmd
) as ctx
:
1278 msg
= r
'must specify --mon-ip or --mon-addrv'
1279 with pytest
.raises(cd
.Error
, match
=msg
):
1280 cd
.command_bootstrap(ctx
)
1282 def test_skip_mon_network(self
, cephadm_fs
):
1283 cmd
= self
._get
_cmd
('--mon-ip', '192.168.1.1')
1285 with
with_cephadm_ctx(cmd
, list_networks
={}) as ctx
:
1286 msg
= r
'--skip-mon-network'
1287 with pytest
.raises(cd
.Error
, match
=msg
):
1288 cd
.command_bootstrap(ctx
)
1290 cmd
+= ['--skip-mon-network']
1291 with
with_cephadm_ctx(cmd
, list_networks
={}) as ctx
:
1292 retval
= cd
.command_bootstrap(ctx
)
1295 @pytest.mark
.parametrize('mon_ip, list_networks, result',
1300 {'192.168.1.0/24': {'eth0': ['192.168.1.1']}},
1305 {'192.168.1.0/24': {'eth0': ['192.168.1.1']}},
1310 {'192.168.1.0/24': {'eth0': ['192.168.1.1']}},
1315 {'192.168.1.0/24': {'eth0': ['192.168.1.1']}},
1320 {'192.168.1.0/24': {'eth0': ['192.168.1.1']}},
1325 {'192.168.1.0/24': {'eth0': ['192.168.1.1']}},
1331 {'192.168.1.0/24': {'eth0': ['192.168.1.1']}},
1335 '::ffff:192.168.1.0',
1336 {"ffff::/64": {"eth0": ["::ffff:c0a8:101"]}},
1340 '::ffff:192.168.1.1',
1341 {"ffff::/64": {"eth0": ["::ffff:c0a8:101"]}},
1346 {"ffff::/64": {"eth0": ["::ffff:c0a8:101"]}},
1350 '[::ffff:c0a8:101]:1234',
1351 {"ffff::/64": {"eth0": ["::ffff:c0a8:101"]}},
1355 '[::ffff:c0a8:101]:0123',
1356 {"ffff::/64": {"eth0": ["::ffff:c0a8:101"]}},
1360 '0000:0000:0000:0000:0000:FFFF:C0A8:0101',
1361 {"ffff::/64": {"eth0": ["::ffff:c0a8:101"]}},
1365 def test_mon_ip(self
, mon_ip
, list_networks
, result
, cephadm_fs
):
1366 cmd
= self
._get
_cmd
('--mon-ip', mon_ip
)
1368 with
with_cephadm_ctx(cmd
, list_networks
=list_networks
) as ctx
:
1369 msg
= r
'--skip-mon-network'
1370 with pytest
.raises(cd
.Error
, match
=msg
):
1371 cd
.command_bootstrap(ctx
)
1373 with
with_cephadm_ctx(cmd
, list_networks
=list_networks
) as ctx
:
1374 retval
= cd
.command_bootstrap(ctx
)
1377 @pytest.mark
.parametrize('mon_addrv, list_networks, err',
1382 {'192.168.1.0/24': {'eth0': ['192.168.1.1']}},
1383 r
'must use square backets',
1387 {'192.168.1.0/24': {'eth0': ['192.168.1.1']}},
1388 r
'must include port number',
1391 '[192.168.1.1:1234]',
1392 {'192.168.1.0/24': {'eth0': ['192.168.1.1']}},
1396 '[192.168.1.1:0123]',
1397 {'192.168.1.0/24': {'eth0': ['192.168.1.1']}},
1401 '[v2:192.168.1.1:3300,v1:192.168.1.1:6789]',
1402 {'192.168.1.0/24': {'eth0': ['192.168.1.1']}},
1407 '[::ffff:192.168.1.1:1234]',
1408 {'ffff::/64': {'eth0': ['::ffff:c0a8:101']}},
1412 '[::ffff:192.168.1.1:0123]',
1413 {'ffff::/64': {'eth0': ['::ffff:c0a8:101']}},
1417 '[0000:0000:0000:0000:0000:FFFF:C0A8:0101:1234]',
1418 {'ffff::/64': {'eth0': ['::ffff:c0a8:101']}},
1422 '[v2:0000:0000:0000:0000:0000:FFFF:C0A8:0101:3300,v1:0000:0000:0000:0000:0000:FFFF:C0A8:0101:6789]',
1423 {'ffff::/64': {'eth0': ['::ffff:c0a8:101']}},
1427 def test_mon_addrv(self
, mon_addrv
, list_networks
, err
, cephadm_fs
):
1428 cmd
= self
._get
_cmd
('--mon-addrv', mon_addrv
)
1430 with
with_cephadm_ctx(cmd
, list_networks
=list_networks
) as ctx
:
1431 with pytest
.raises(cd
.Error
, match
=err
):
1432 cd
.command_bootstrap(ctx
)
1434 with
with_cephadm_ctx(cmd
, list_networks
=list_networks
) as ctx
:
1435 retval
= cd
.command_bootstrap(ctx
)
1438 def test_allow_fqdn_hostname(self
, cephadm_fs
):
1439 hostname
= 'foo.bar'
1440 cmd
= self
._get
_cmd
(
1441 '--mon-ip', '192.168.1.1',
1442 '--skip-mon-network',
1445 with
with_cephadm_ctx(cmd
, hostname
=hostname
) as ctx
:
1446 msg
= r
'--allow-fqdn-hostname'
1447 with pytest
.raises(cd
.Error
, match
=msg
):
1448 cd
.command_bootstrap(ctx
)
1450 cmd
+= ['--allow-fqdn-hostname']
1451 with
with_cephadm_ctx(cmd
, hostname
=hostname
) as ctx
:
1452 retval
= cd
.command_bootstrap(ctx
)
1455 @pytest.mark
.parametrize('fsid, err',
1458 ('00000000-0000-0000-0000-0000deadbeef', None),
1459 ('00000000-0000-0000-0000-0000deadbeez', 'not an fsid'),
1461 def test_fsid(self
, fsid
, err
, cephadm_fs
):
1462 cmd
= self
._get
_cmd
(
1463 '--mon-ip', '192.168.1.1',
1464 '--skip-mon-network',
1468 with
with_cephadm_ctx(cmd
) as ctx
:
1470 with pytest
.raises(cd
.Error
, match
=err
):
1471 cd
.command_bootstrap(ctx
)
1473 retval
= cd
.command_bootstrap(ctx
)
1477 class TestShell(object):
1479 def test_fsid(self
, cephadm_fs
):
1480 fsid
= '00000000-0000-0000-0000-0000deadbeef'
1482 cmd
= ['shell', '--fsid', fsid
]
1483 with
with_cephadm_ctx(cmd
) as ctx
:
1484 retval
= cd
.command_shell(ctx
)
1486 assert ctx
.fsid
== fsid
1488 cmd
= ['shell', '--fsid', '00000000-0000-0000-0000-0000deadbeez']
1489 with
with_cephadm_ctx(cmd
) as ctx
:
1491 with pytest
.raises(cd
.Error
, match
=err
):
1492 retval
= cd
.command_shell(ctx
)
1494 assert ctx
.fsid
== None
1496 s
= get_ceph_conf(fsid
=fsid
)
1497 f
= cephadm_fs
.create_file('ceph.conf', contents
=s
)
1499 cmd
= ['shell', '--fsid', fsid
, '--config', f
.path
]
1500 with
with_cephadm_ctx(cmd
) as ctx
:
1501 retval
= cd
.command_shell(ctx
)
1503 assert ctx
.fsid
== fsid
1505 cmd
= ['shell', '--fsid', '10000000-0000-0000-0000-0000deadbeef', '--config', f
.path
]
1506 with
with_cephadm_ctx(cmd
) as ctx
:
1507 err
= 'fsid does not match ceph.conf'
1508 with pytest
.raises(cd
.Error
, match
=err
):
1509 retval
= cd
.command_shell(ctx
)
1511 assert ctx
.fsid
== None
1513 def test_name(self
, cephadm_fs
):
1514 cmd
= ['shell', '--name', 'foo']
1515 with
with_cephadm_ctx(cmd
) as ctx
:
1516 retval
= cd
.command_shell(ctx
)
1519 cmd
= ['shell', '--name', 'foo.bar']
1520 with
with_cephadm_ctx(cmd
) as ctx
:
1521 err
= r
'must pass --fsid'
1522 with pytest
.raises(cd
.Error
, match
=err
):
1523 retval
= cd
.command_shell(ctx
)
1526 fsid
= '00000000-0000-0000-0000-0000deadbeef'
1527 cmd
= ['shell', '--name', 'foo.bar', '--fsid', fsid
]
1528 with
with_cephadm_ctx(cmd
) as ctx
:
1529 retval
= cd
.command_shell(ctx
)
1532 def test_config(self
, cephadm_fs
):
1534 with
with_cephadm_ctx(cmd
) as ctx
:
1535 retval
= cd
.command_shell(ctx
)
1537 assert ctx
.config
== None
1539 cephadm_fs
.create_file(cd
.CEPH_DEFAULT_CONF
)
1540 with
with_cephadm_ctx(cmd
) as ctx
:
1541 retval
= cd
.command_shell(ctx
)
1543 assert ctx
.config
== cd
.CEPH_DEFAULT_CONF
1545 cmd
= ['shell', '--config', 'foo']
1546 with
with_cephadm_ctx(cmd
) as ctx
:
1547 retval
= cd
.command_shell(ctx
)
1549 assert ctx
.config
== 'foo'
1551 def test_keyring(self
, cephadm_fs
):
1553 with
with_cephadm_ctx(cmd
) as ctx
:
1554 retval
= cd
.command_shell(ctx
)
1556 assert ctx
.keyring
== None
1558 cephadm_fs
.create_file(cd
.CEPH_DEFAULT_KEYRING
)
1559 with
with_cephadm_ctx(cmd
) as ctx
:
1560 retval
= cd
.command_shell(ctx
)
1562 assert ctx
.keyring
== cd
.CEPH_DEFAULT_KEYRING
1564 cmd
= ['shell', '--keyring', 'foo']
1565 with
with_cephadm_ctx(cmd
) as ctx
:
1566 retval
= cd
.command_shell(ctx
)
1568 assert ctx
.keyring
== 'foo'
1570 @mock.patch('cephadm.CephContainer')
1571 def test_mount_no_dst(self
, m_ceph_container
, cephadm_fs
):
1572 cmd
= ['shell', '--mount', '/etc/foo']
1573 with
with_cephadm_ctx(cmd
) as ctx
:
1574 retval
= cd
.command_shell(ctx
)
1576 assert m_ceph_container
.call_args
.kwargs
['volume_mounts']['/etc/foo'] == '/mnt/foo'
1578 @mock.patch('cephadm.CephContainer')
1579 def test_mount_with_dst_no_opt(self
, m_ceph_container
, cephadm_fs
):
1580 cmd
= ['shell', '--mount', '/etc/foo:/opt/foo/bar']
1581 with
with_cephadm_ctx(cmd
) as ctx
:
1582 retval
= cd
.command_shell(ctx
)
1584 assert m_ceph_container
.call_args
.kwargs
['volume_mounts']['/etc/foo'] == '/opt/foo/bar'
1586 @mock.patch('cephadm.CephContainer')
1587 def test_mount_with_dst_and_opt(self
, m_ceph_container
, cephadm_fs
):
1588 cmd
= ['shell', '--mount', '/etc/foo:/opt/foo/bar:Z']
1589 with
with_cephadm_ctx(cmd
) as ctx
:
1590 retval
= cd
.command_shell(ctx
)
1592 assert m_ceph_container
.call_args
.kwargs
['volume_mounts']['/etc/foo'] == '/opt/foo/bar:Z'
1594 class TestCephVolume(object):
1597 def _get_cmd(*args
):
1601 '--', 'inventory', '--format', 'json'
1604 def test_noop(self
, cephadm_fs
):
1605 cmd
= self
._get
_cmd
()
1606 with
with_cephadm_ctx(cmd
) as ctx
:
1607 cd
.command_ceph_volume(ctx
)
1608 assert ctx
.fsid
== None
1609 assert ctx
.config
== None
1610 assert ctx
.keyring
== None
1611 assert ctx
.config_json
== None
1613 def test_fsid(self
, cephadm_fs
):
1614 fsid
= '00000000-0000-0000-0000-0000deadbeef'
1616 cmd
= self
._get
_cmd
('--fsid', fsid
)
1617 with
with_cephadm_ctx(cmd
) as ctx
:
1618 cd
.command_ceph_volume(ctx
)
1619 assert ctx
.fsid
== fsid
1621 cmd
= self
._get
_cmd
('--fsid', '00000000-0000-0000-0000-0000deadbeez')
1622 with
with_cephadm_ctx(cmd
) as ctx
:
1624 with pytest
.raises(cd
.Error
, match
=err
):
1625 retval
= cd
.command_shell(ctx
)
1627 assert ctx
.fsid
== None
1629 s
= get_ceph_conf(fsid
=fsid
)
1630 f
= cephadm_fs
.create_file('ceph.conf', contents
=s
)
1632 cmd
= self
._get
_cmd
('--fsid', fsid
, '--config', f
.path
)
1633 with
with_cephadm_ctx(cmd
) as ctx
:
1634 cd
.command_ceph_volume(ctx
)
1635 assert ctx
.fsid
== fsid
1637 cmd
= self
._get
_cmd
('--fsid', '10000000-0000-0000-0000-0000deadbeef', '--config', f
.path
)
1638 with
with_cephadm_ctx(cmd
) as ctx
:
1639 err
= 'fsid does not match ceph.conf'
1640 with pytest
.raises(cd
.Error
, match
=err
):
1641 cd
.command_ceph_volume(ctx
)
1642 assert ctx
.fsid
== None
1644 def test_config(self
, cephadm_fs
):
1645 cmd
= self
._get
_cmd
('--config', 'foo')
1646 with
with_cephadm_ctx(cmd
) as ctx
:
1647 err
= r
'No such file or directory'
1648 with pytest
.raises(cd
.Error
, match
=err
):
1649 cd
.command_ceph_volume(ctx
)
1651 cephadm_fs
.create_file('bar')
1652 cmd
= self
._get
_cmd
('--config', 'bar')
1653 with
with_cephadm_ctx(cmd
) as ctx
:
1654 cd
.command_ceph_volume(ctx
)
1655 assert ctx
.config
== 'bar'
1657 def test_keyring(self
, cephadm_fs
):
1658 cmd
= self
._get
_cmd
('--keyring', 'foo')
1659 with
with_cephadm_ctx(cmd
) as ctx
:
1660 err
= r
'No such file or directory'
1661 with pytest
.raises(cd
.Error
, match
=err
):
1662 cd
.command_ceph_volume(ctx
)
1664 cephadm_fs
.create_file('bar')
1665 cmd
= self
._get
_cmd
('--keyring', 'bar')
1666 with
with_cephadm_ctx(cmd
) as ctx
:
1667 cd
.command_ceph_volume(ctx
)
1668 assert ctx
.keyring
== 'bar'
1672 def test_unit_run(self
, cephadm_fs
):
1673 fsid
= '9b9d7609-f4d5-4aba-94c8-effa764d96c9'
1675 'files': {'iscsi-gateway.cfg': ''}
1677 with
with_cephadm_ctx(['--image=ceph/ceph'], list_networks
={}) as ctx
:
1679 ctx
.config_json
= json
.dumps(config_json
)
1681 cd
.get_parm
.return_value
= config_json
1682 c
= cd
.get_container(ctx
, fsid
, 'iscsi', 'daemon_id')
1684 cd
.make_data_dir(ctx
, fsid
, 'iscsi', 'daemon_id')
1685 cd
.deploy_daemon_units(
1695 with
open('/var/lib/ceph/9b9d7609-f4d5-4aba-94c8-effa764d96c9/iscsi.daemon_id/unit.run') as f
:
1696 assert f
.read() == """set -e
1697 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
1698 # iscsi tcmu-runner container
1699 ! /usr/bin/podman rm -f ceph-9b9d7609-f4d5-4aba-94c8-effa764d96c9-iscsi.daemon_id-tcmu 2> /dev/null
1700 ! /usr/bin/podman rm -f ceph-9b9d7609-f4d5-4aba-94c8-effa764d96c9-iscsi-daemon_id-tcmu 2> /dev/null
1701 /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:z -v /dev:/dev --mount type=bind,source=/lib/modules,destination=/lib/modules,ro=true ceph/ceph &
1703 ! /usr/bin/podman rm -f ceph-9b9d7609-f4d5-4aba-94c8-effa764d96c9-iscsi.daemon_id 2> /dev/null
1704 ! /usr/bin/podman rm -f ceph-9b9d7609-f4d5-4aba-94c8-effa764d96c9-iscsi-daemon_id 2> /dev/null
1705 /usr/bin/podman run --rm --ipc=host --stop-signal=SIGTERM --net=host --entrypoint /usr/bin/rbd-target-api --privileged --group-add=disk --init --name ceph-9b9d7609-f4d5-4aba-94c8-effa764d96c9-iscsi-daemon_id -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
1708 def test_get_container(self
):
1710 Due to a combination of socket.getfqdn() and podman's behavior to
1711 add the container name into the /etc/hosts file, we cannot use periods
1712 in container names. But we need to be able to detect old existing containers.
1713 Assert this behaviour. I think we can remove this in Ceph R
1715 fsid
= '9b9d7609-f4d5-4aba-94c8-effa764d96c9'
1716 with
with_cephadm_ctx(['--image=ceph/ceph'], list_networks
={}) as ctx
:
1718 c
= cd
.get_container(ctx
, fsid
, 'iscsi', 'something')
1719 assert c
.cname
== 'ceph-9b9d7609-f4d5-4aba-94c8-effa764d96c9-iscsi-something'
1720 assert c
.old_cname
== 'ceph-9b9d7609-f4d5-4aba-94c8-effa764d96c9-iscsi.something'
1723 class TestCheckHost
:
1725 @mock.patch('cephadm.find_executable', return_value
='foo')
1726 @mock.patch('cephadm.check_time_sync', return_value
=True)
1727 def test_container_engine(self
, find_executable
, check_time_sync
):
1728 ctx
= cd
.CephadmContext()
1730 ctx
.container_engine
= None
1731 err
= r
'No container engine binary found'
1732 with pytest
.raises(cd
.Error
, match
=err
):
1733 cd
.command_check_host(ctx
)
1735 ctx
.container_engine
= mock_podman()
1736 cd
.command_check_host(ctx
)
1738 ctx
.container_engine
= mock_docker()
1739 cd
.command_check_host(ctx
)
1744 @pytest.mark
.parametrize('os_release',
1749 VERSION="20.04 LTS (Focal Fossa)"
1752 PRETTY_NAME="Ubuntu 20.04 LTS"
1754 HOME_URL="https://www.ubuntu.com/"
1755 SUPPORT_URL="https://help.ubuntu.com/"
1756 BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
1757 PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
1758 VERSION_CODENAME=focal
1759 UBUNTU_CODENAME=focal
1767 ID_LIKE="rhel fedora"
1769 PLATFORM_ID="platform:el8"
1770 PRETTY_NAME="CentOS Linux 8 (Core)"
1772 CPE_NAME="cpe:/o:centos:centos:8"
1773 HOME_URL="https://www.centos.org/"
1774 BUG_REPORT_URL="https://bugs.centos.org/"
1776 CENTOS_MANTISBT_PROJECT="CentOS-8"
1777 CENTOS_MANTISBT_PROJECT_VERSION="8"
1778 REDHAT_SUPPORT_PRODUCT="centos"
1779 REDHAT_SUPPORT_PRODUCT_VERSION="8"
1784 NAME="openSUSE Tumbleweed"
1785 # VERSION="20210810"
1786 ID="opensuse-tumbleweed"
1787 ID_LIKE="opensuse suse"
1788 VERSION_ID="20210810"
1789 PRETTY_NAME="openSUSE Tumbleweed"
1791 CPE_NAME="cpe:/o:opensuse:tumbleweed:20210810"
1792 BUG_REPORT_URL="https://bugs.opensuse.org"
1793 HOME_URL="https://www.opensuse.org/"
1794 DOCUMENTATION_URL="https://en.opensuse.org/Portal:Tumbleweed"
1795 LOGO="distributor-logo"
1798 @mock.patch('cephadm.find_executable', return_value
='foo')
1799 def test_container_engine(self
, find_executable
, os_release
, cephadm_fs
):
1800 cephadm_fs
.create_file('/etc/os-release', contents
=os_release
)
1801 ctx
= cd
.CephadmContext()
1803 ctx
.container_engine
= None
1804 cd
.command_rm_repo(ctx
)
1806 ctx
.container_engine
= mock_podman()
1807 cd
.command_rm_repo(ctx
)
1809 ctx
.container_engine
= mock_docker()
1810 cd
.command_rm_repo(ctx
)
1813 class TestValidateRepo
:
1815 @pytest.mark
.parametrize('values',
1822 os_release
=dedent("""
1824 VERSION="20.04 LTS (Focal Fossa)"
1827 PRETTY_NAME="Ubuntu 20.04 LTS"
1829 HOME_URL="https://www.ubuntu.com/"
1830 SUPPORT_URL="https://help.ubuntu.com/"
1831 BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
1832 PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
1833 VERSION_CODENAME=focal
1834 UBUNTU_CODENAME=focal
1837 # YumDnf on Centos8 - OK
1842 os_release
=dedent("""
1846 ID_LIKE="rhel fedora"
1848 PLATFORM_ID="platform:el8"
1849 PRETTY_NAME="CentOS Linux 8 (Core)"
1851 CPE_NAME="cpe:/o:centos:centos:8"
1852 HOME_URL="https://www.centos.org/"
1853 BUG_REPORT_URL="https://bugs.centos.org/"
1855 CENTOS_MANTISBT_PROJECT="CentOS-8"
1856 CENTOS_MANTISBT_PROJECT_VERSION="8"
1857 REDHAT_SUPPORT_PRODUCT="centos"
1858 REDHAT_SUPPORT_PRODUCT_VERSION="8"
1861 # YumDnf on Fedora - Fedora not supported
1865 err_text
="does not build Fedora",
1866 os_release
=dedent("""
1868 VERSION="35 (Cloud Edition)"
1872 PLATFORM_ID="platform:f35"
1873 PRETTY_NAME="Fedora Linux 35 (Cloud Edition)"
1874 ANSI_COLOR="0;38;2;60;110;180"
1875 LOGO=fedora-logo-icon
1876 CPE_NAME="cpe:/o:fedoraproject:fedora:35"
1877 HOME_URL="https://fedoraproject.org/"
1878 DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f35/system-administrators-guide/"
1879 SUPPORT_URL="https://ask.fedoraproject.org/"
1880 BUG_REPORT_URL="https://bugzilla.redhat.com/"
1881 REDHAT_BUGZILLA_PRODUCT="Fedora"
1882 REDHAT_BUGZILLA_PRODUCT_VERSION=35
1883 REDHAT_SUPPORT_PRODUCT="Fedora"
1884 REDHAT_SUPPORT_PRODUCT_VERSION=35
1885 PRIVACY_POLICY_URL="https://fedoraproject.org/wiki/Legal:PrivacyPolicy"
1886 VARIANT="Cloud Edition"
1890 # YumDnf on Centos 7 - no pacific
1894 err_text
="does not support pacific",
1895 os_release
=dedent("""
1899 ID_LIKE="rhel fedora"
1901 PRETTY_NAME="CentOS Linux 7 (Core)"
1903 CPE_NAME="cpe:/o:centos:centos:7"
1904 HOME_URL="https://www.centos.org/"
1905 BUG_REPORT_URL="https://bugs.centos.org/"
1907 CENTOS_MANTISBT_PROJECT="CentOS-7"
1908 CENTOS_MANTISBT_PROJECT_VERSION="7"
1909 REDHAT_SUPPORT_PRODUCT="centos"
1910 REDHAT_SUPPORT_PRODUCT_VERSION="7"
1913 # YumDnf on Centos 7 - nothing after pacific
1917 err_text
="does not support pacific",
1918 os_release
=dedent("""
1922 ID_LIKE="rhel fedora"
1924 PRETTY_NAME="CentOS Linux 7 (Core)"
1926 CPE_NAME="cpe:/o:centos:centos:7"
1927 HOME_URL="https://www.centos.org/"
1928 BUG_REPORT_URL="https://bugs.centos.org/"
1930 CENTOS_MANTISBT_PROJECT="CentOS-7"
1931 CENTOS_MANTISBT_PROJECT_VERSION="7"
1932 REDHAT_SUPPORT_PRODUCT="centos"
1933 REDHAT_SUPPORT_PRODUCT_VERSION="7"
1936 # YumDnf on Centos 7 - nothing v16 or higher
1940 err_text
="does not support",
1941 os_release
=dedent("""
1945 ID_LIKE="rhel fedora"
1947 PRETTY_NAME="CentOS Linux 7 (Core)"
1949 CPE_NAME="cpe:/o:centos:centos:7"
1950 HOME_URL="https://www.centos.org/"
1951 BUG_REPORT_URL="https://bugs.centos.org/"
1953 CENTOS_MANTISBT_PROJECT="CentOS-7"
1954 CENTOS_MANTISBT_PROJECT_VERSION="7"
1955 REDHAT_SUPPORT_PRODUCT="centos"
1956 REDHAT_SUPPORT_PRODUCT_VERSION="7"
1959 @mock.patch('cephadm.find_executable', return_value
='foo')
1960 def test_distro_validation(self
, find_executable
, values
, cephadm_fs
):
1961 os_release
= values
['os_release']
1962 release
= values
['release']
1963 version
= values
['version']
1964 err_text
= values
['err_text']
1966 cephadm_fs
.create_file('/etc/os-release', contents
=os_release
)
1967 ctx
= cd
.CephadmContext()
1968 ctx
.repo_url
= 'http://localhost'
1969 pkg
= cd
.create_packager(ctx
, stable
=release
, version
=version
)
1972 with pytest
.raises(cd
.Error
, match
=err_text
):
1975 with mock
.patch('cephadm.urlopen', return_value
=None):
1978 @pytest.mark
.parametrize('values',
1985 os_release
=dedent("""
1987 VERSION="20.04 LTS (Focal Fossa)"
1990 PRETTY_NAME="Ubuntu 20.04 LTS"
1992 HOME_URL="https://www.ubuntu.com/"
1993 SUPPORT_URL="https://help.ubuntu.com/"
1994 BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
1995 PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
1996 VERSION_CODENAME=focal
1997 UBUNTU_CODENAME=focal
2000 # YumDnf on Centos8 - force failure
2004 err_text
="failed to fetch repository metadata",
2005 os_release
=dedent("""
2009 ID_LIKE="rhel fedora"
2011 PLATFORM_ID="platform:el8"
2012 PRETTY_NAME="CentOS Linux 8 (Core)"
2014 CPE_NAME="cpe:/o:centos:centos:8"
2015 HOME_URL="https://www.centos.org/"
2016 BUG_REPORT_URL="https://bugs.centos.org/"
2018 CENTOS_MANTISBT_PROJECT="CentOS-8"
2019 CENTOS_MANTISBT_PROJECT_VERSION="8"
2020 REDHAT_SUPPORT_PRODUCT="centos"
2021 REDHAT_SUPPORT_PRODUCT_VERSION="8"
2024 @mock.patch('cephadm.find_executable', return_value
='foo')
2025 def test_http_validation(self
, find_executable
, values
, cephadm_fs
):
2026 from urllib
.error
import HTTPError
2028 os_release
= values
['os_release']
2029 release
= values
['release']
2030 version
= values
['version']
2031 err_text
= values
['err_text']
2033 cephadm_fs
.create_file('/etc/os-release', contents
=os_release
)
2034 ctx
= cd
.CephadmContext()
2035 ctx
.repo_url
= 'http://localhost'
2036 pkg
= cd
.create_packager(ctx
, stable
=release
, version
=version
)
2038 with mock
.patch('cephadm.urlopen') as _urlopen
:
2039 _urlopen
.side_effect
= HTTPError(ctx
.repo_url
, 404, "not found", None, fp
=None)
2041 with pytest
.raises(cd
.Error
, match
=err_text
):
2049 @mock.patch('time.sleep')
2050 @mock.patch('cephadm.call', return_value
=('', '', 0))
2051 @mock.patch('cephadm.get_image_info_from_inspect', return_value
={})
2052 def test_error(self
, get_image_info_from_inspect
, call
, sleep
):
2053 ctx
= cd
.CephadmContext()
2054 ctx
.container_engine
= mock_podman()
2055 ctx
.insecure
= False
2057 call
.return_value
= ('', '', 0)
2058 retval
= cd
.command_pull(ctx
)
2061 err
= 'maximum retries reached'
2063 call
.return_value
= ('', 'foobar', 1)
2064 with pytest
.raises(cd
.Error
) as e
:
2065 cd
.command_pull(ctx
)
2066 assert err
not in str(e
.value
)
2068 call
.return_value
= ('', 'net/http: TLS handshake timeout', 1)
2069 with pytest
.raises(cd
.Error
) as e
:
2070 cd
.command_pull(ctx
)
2071 assert err
in str(e
.value
)
2073 @mock.patch('cephadm.logger')
2074 @mock.patch('cephadm.get_image_info_from_inspect', return_value
={})
2075 @mock.patch('cephadm.infer_local_ceph_image', return_value
='last_local_ceph_image')
2076 def test_image(self
, infer_local_ceph_image
, get_image_info_from_inspect
, logger
):
2078 with
with_cephadm_ctx(cmd
) as ctx
:
2079 retval
= cd
.command_pull(ctx
)
2081 assert ctx
.image
== cd
.DEFAULT_IMAGE
2083 with mock
.patch
.dict(os
.environ
, {"CEPHADM_IMAGE": 'cephadm_image_environ'}):
2085 with
with_cephadm_ctx(cmd
) as ctx
:
2086 retval
= cd
.command_pull(ctx
)
2088 assert ctx
.image
== 'cephadm_image_environ'
2090 cmd
= ['--image', 'cephadm_image_param', 'pull']
2091 with
with_cephadm_ctx(cmd
) as ctx
:
2092 retval
= cd
.command_pull(ctx
)
2094 assert ctx
.image
== 'cephadm_image_param'
2097 class TestApplySpec
:
2099 def test_parse_yaml(self
, cephadm_fs
):
2103 addr: 192.168.122.44
2110 addr: 192.168.122.247
2116 addr: 192.168.122.165
2122 rgw_frontend_ssl_certificate: |
2123 -----BEGIN PRIVATE KEY-----
2124 V2VyIGRhcyBsaWVzdCBpc3QgZG9vZi4gTG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFt
2125 ZXQsIGNvbnNldGV0dXIgc2FkaXBzY2luZyBlbGl0ciwgc2VkIGRpYW0gbm9udW15
2126 IGVpcm1vZCB0ZW1wb3IgaW52aWR1bnQgdXQgbGFib3JlIGV0IGRvbG9yZSBtYWdu
2127 YSBhbGlxdXlhbSBlcmF0LCBzZWQgZGlhbSB2b2x1cHR1YS4gQXQgdmVybyBlb3Mg
2128 ZXQgYWNjdXNhbSBldCBqdXN0byBkdW8=
2129 -----END PRIVATE KEY-----
2130 -----BEGIN CERTIFICATE-----
2131 V2VyIGRhcyBsaWVzdCBpc3QgZG9vZi4gTG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFt
2132 ZXQsIGNvbnNldGV0dXIgc2FkaXBzY2luZyBlbGl0ciwgc2VkIGRpYW0gbm9udW15
2133 IGVpcm1vZCB0ZW1wb3IgaW52aWR1bnQgdXQgbGFib3JlIGV0IGRvbG9yZSBtYWdu
2134 YSBhbGlxdXlhbSBlcmF0LCBzZWQgZGlhbSB2b2x1cHR1YS4gQXQgdmVybyBlb3Mg
2135 ZXQgYWNjdXNhbSBldCBqdXN0byBkdW8=
2136 -----END CERTIFICATE-----
2141 cephadm_fs
.create_file('spec.yml', contents
=yaml
)
2142 retdic
= [{'service_type': 'host', 'hostname': 'vm-00', 'addr': '192.168.122.44', 'labels': '- example1- example2'},
2143 {'service_type': 'host', 'hostname': 'vm-01', 'addr': '192.168.122.247', 'labels': '- grafana'},
2144 {'service_type': 'host', 'hostname': 'vm-02', 'addr': '192.168.122.165'},
2145 {'service_id': 'myrgw',
2146 'service_type': 'rgw',
2148 'rgw_frontend_ssl_certificate: |-----BEGIN PRIVATE '
2149 'KEY-----V2VyIGRhcyBsaWVzdCBpc3QgZG9vZi4gTG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNldGV0dXIgc2FkaXBzY2luZyBlbGl0ciwgc2VkIGRpYW0gbm9udW15IGVpcm1vZCB0ZW1wb3IgaW52aWR1bnQgdXQgbGFib3JlIGV0IGRvbG9yZSBtYWduYSBhbGlxdXlhbSBlcmF0LCBzZWQgZGlhbSB2b2x1cHR1YS4gQXQgdmVybyBlb3MgZXQgYWNjdXNhbSBldCBqdXN0byBkdW8=-----END '
2150 'PRIVATE KEY----------BEGIN '
2151 'CERTIFICATE-----V2VyIGRhcyBsaWVzdCBpc3QgZG9vZi4gTG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNldGV0dXIgc2FkaXBzY2luZyBlbGl0ciwgc2VkIGRpYW0gbm9udW15IGVpcm1vZCB0ZW1wb3IgaW52aWR1bnQgdXQgbGFib3JlIGV0IGRvbG9yZSBtYWduYSBhbGlxdXlhbSBlcmF0LCBzZWQgZGlhbSB2b2x1cHR1YS4gQXQgdmVybyBlb3MgZXQgYWNjdXNhbSBldCBqdXN0byBkdW8=-----END '
2152 'CERTIFICATE-----ssl: true'}]
2154 with
open('spec.yml') as f
:
2155 dic
= cd
.parse_yaml_objs(f
)
2156 assert dic
== retdic
2158 @mock.patch('cephadm.call', return_value
=('', '', 0))
2159 def test_distribute_ssh_keys(self
, call
):
2160 ctx
= cd
.CephadmContext()
2161 ctx
.ssh_public_key
= None
2162 ctx
.ssh_user
= 'root'
2164 host_spec
= {'service_type': 'host', 'hostname': 'vm-02', 'addr': '192.168.122.165'}
2166 retval
= cd
._distribute
_ssh
_keys
(ctx
, host_spec
, 'bootstrap_hostname')
2170 call
.return_value
= ('', '', 1)
2172 retval
= cd
._distribute
_ssh
_keys
(ctx
, host_spec
, 'bootstrap_hostname')
2177 class TestSNMPGateway
:
2179 'snmp_community': 'public',
2180 'destination': '192.168.1.10:162',
2181 'snmp_version': 'V2c',
2183 V3_no_priv_config
= {
2184 'destination': '192.168.1.10:162',
2185 'snmp_version': 'V3',
2186 'snmp_v3_auth_username': 'myuser',
2187 'snmp_v3_auth_password': 'mypassword',
2188 'snmp_v3_auth_protocol': 'SHA',
2189 'snmp_v3_engine_id': '8000C53F00000000',
2192 'destination': '192.168.1.10:162',
2193 'snmp_version': 'V3',
2194 'snmp_v3_auth_username': 'myuser',
2195 'snmp_v3_auth_password': 'mypassword',
2196 'snmp_v3_auth_protocol': 'SHA',
2197 'snmp_v3_priv_protocol': 'DES',
2198 'snmp_v3_priv_password': 'mysecret',
2199 'snmp_v3_engine_id': '8000C53F00000000',
2201 no_destination_config
= {
2202 'snmp_version': 'V3',
2203 'snmp_v3_auth_username': 'myuser',
2204 'snmp_v3_auth_password': 'mypassword',
2205 'snmp_v3_auth_protocol': 'SHA',
2206 'snmp_v3_priv_protocol': 'DES',
2207 'snmp_v3_priv_password': 'mysecret',
2208 'snmp_v3_engine_id': '8000C53F00000000',
2210 bad_version_config
= {
2211 'snmp_community': 'public',
2212 'destination': '192.168.1.10:162',
2213 'snmp_version': 'V1',
2216 def test_unit_run_V2c(self
, cephadm_fs
):
2217 fsid
= 'ca734440-3dc6-11ec-9b98-5254002537a6'
2218 with
with_cephadm_ctx(['--image=docker.io/maxwo/snmp-notifier:v1.2.1'], list_networks
={}) as ctx
:
2220 ctx
.config_json
= json
.dumps(self
.V2c_config
)
2222 ctx
.tcp_ports
= '9464'
2223 cd
.get_parm
.return_value
= self
.V2c_config
2224 c
= cd
.get_container(ctx
, fsid
, 'snmp-gateway', 'daemon_id')
2226 cd
.make_data_dir(ctx
, fsid
, 'snmp-gateway', 'daemon_id')
2228 cd
.create_daemon_dirs(ctx
, fsid
, 'snmp-gateway', 'daemon_id', 0, 0)
2229 with
open(f
'/var/lib/ceph/{fsid}/snmp-gateway.daemon_id/snmp-gateway.conf', 'r') as f
:
2230 conf
= f
.read().rstrip()
2231 assert conf
== 'SNMP_NOTIFIER_COMMUNITY=public'
2233 cd
.deploy_daemon_units(
2242 with
open(f
'/var/lib/ceph/{fsid}/snmp-gateway.daemon_id/unit.run', 'r') as f
:
2243 run_cmd
= f
.readlines()[-1].rstrip()
2244 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')
2246 def test_unit_run_V3_noPriv(self
, cephadm_fs
):
2247 fsid
= 'ca734440-3dc6-11ec-9b98-5254002537a6'
2248 with
with_cephadm_ctx(['--image=docker.io/maxwo/snmp-notifier:v1.2.1'], list_networks
={}) as ctx
:
2250 ctx
.config_json
= json
.dumps(self
.V3_no_priv_config
)
2252 ctx
.tcp_ports
= '9465'
2253 cd
.get_parm
.return_value
= self
.V3_no_priv_config
2254 c
= cd
.get_container(ctx
, fsid
, 'snmp-gateway', 'daemon_id')
2256 cd
.make_data_dir(ctx
, fsid
, 'snmp-gateway', 'daemon_id')
2258 cd
.create_daemon_dirs(ctx
, fsid
, 'snmp-gateway', 'daemon_id', 0, 0)
2259 with
open(f
'/var/lib/ceph/{fsid}/snmp-gateway.daemon_id/snmp-gateway.conf', 'r') as f
:
2261 assert conf
== 'SNMP_NOTIFIER_AUTH_USERNAME=myuser\nSNMP_NOTIFIER_AUTH_PASSWORD=mypassword\n'
2263 cd
.deploy_daemon_units(
2272 with
open(f
'/var/lib/ceph/{fsid}/snmp-gateway.daemon_id/unit.run', 'r') as f
:
2273 run_cmd
= f
.readlines()[-1].rstrip()
2274 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')
2276 def test_unit_run_V3_Priv(self
, cephadm_fs
):
2277 fsid
= 'ca734440-3dc6-11ec-9b98-5254002537a6'
2278 with
with_cephadm_ctx(['--image=docker.io/maxwo/snmp-notifier:v1.2.1'], list_networks
={}) as ctx
:
2280 ctx
.config_json
= json
.dumps(self
.V3_priv_config
)
2282 ctx
.tcp_ports
= '9464'
2283 cd
.get_parm
.return_value
= self
.V3_priv_config
2284 c
= cd
.get_container(ctx
, fsid
, 'snmp-gateway', 'daemon_id')
2286 cd
.make_data_dir(ctx
, fsid
, 'snmp-gateway', 'daemon_id')
2288 cd
.create_daemon_dirs(ctx
, fsid
, 'snmp-gateway', 'daemon_id', 0, 0)
2289 with
open(f
'/var/lib/ceph/{fsid}/snmp-gateway.daemon_id/snmp-gateway.conf', 'r') as f
:
2291 assert conf
== 'SNMP_NOTIFIER_AUTH_USERNAME=myuser\nSNMP_NOTIFIER_AUTH_PASSWORD=mypassword\nSNMP_NOTIFIER_PRIV_PASSWORD=mysecret\n'
2293 cd
.deploy_daemon_units(
2302 with
open(f
'/var/lib/ceph/{fsid}/snmp-gateway.daemon_id/unit.run', 'r') as f
:
2303 run_cmd
= f
.readlines()[-1].rstrip()
2304 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')
2306 def test_unit_run_no_dest(self
, cephadm_fs
):
2307 fsid
= 'ca734440-3dc6-11ec-9b98-5254002537a6'
2308 with
with_cephadm_ctx(['--image=docker.io/maxwo/snmp-notifier:v1.2.1'], list_networks
={}) as ctx
:
2310 ctx
.config_json
= json
.dumps(self
.no_destination_config
)
2312 ctx
.tcp_ports
= '9464'
2313 cd
.get_parm
.return_value
= self
.no_destination_config
2315 with pytest
.raises(Exception) as e
:
2316 c
= cd
.get_container(ctx
, fsid
, 'snmp-gateway', 'daemon_id')
2317 assert str(e
.value
) == "config is missing destination attribute(<ip>:<port>) of the target SNMP listener"
2319 def test_unit_run_bad_version(self
, cephadm_fs
):
2320 fsid
= 'ca734440-3dc6-11ec-9b98-5254002537a6'
2321 with
with_cephadm_ctx(['--image=docker.io/maxwo/snmp-notifier:v1.2.1'], list_networks
={}) as ctx
:
2323 ctx
.config_json
= json
.dumps(self
.bad_version_config
)
2325 ctx
.tcp_ports
= '9464'
2326 cd
.get_parm
.return_value
= self
.bad_version_config
2328 with pytest
.raises(Exception) as e
:
2329 c
= cd
.get_container(ctx
, fsid
, 'snmp-gateway', 'daemon_id')
2330 assert str(e
.value
) == 'not a valid snmp version: V1'
2332 class TestNetworkValidation
:
2334 def test_ipv4_subnet(self
):
2335 rc
, v
, msg
= cd
.check_subnet('192.168.1.0/24')
2336 assert rc
== 0 and v
[0] == 4
2338 def test_ipv4_subnet_list(self
):
2339 rc
, v
, msg
= cd
.check_subnet('192.168.1.0/24,10.90.90.0/24')
2340 assert rc
== 0 and not msg
2342 def test_ipv4_subnet_list_with_spaces(self
):
2343 rc
, v
, msg
= cd
.check_subnet('192.168.1.0/24, 10.90.90.0/24 ')
2344 assert rc
== 0 and not msg
2346 def test_ipv4_subnet_badlist(self
):
2347 rc
, v
, msg
= cd
.check_subnet('192.168.1.0/24,192.168.1.1')
2348 assert rc
== 1 and msg
2350 def test_ipv4_subnet_mixed(self
):
2351 rc
, v
, msg
= cd
.check_subnet('192.168.100.0/24,fe80::/64')
2352 assert rc
== 0 and v
== [4,6]
2354 def test_ipv6_subnet(self
):
2355 rc
, v
, msg
= cd
.check_subnet('fe80::/64')
2356 assert rc
== 0 and v
[0] == 6
2358 def test_subnet_mask_missing(self
):
2359 rc
, v
, msg
= cd
.check_subnet('192.168.1.58')
2360 assert rc
== 1 and msg
2362 def test_subnet_mask_junk(self
):
2363 rc
, v
, msg
= cd
.check_subnet('wah')
2364 assert rc
== 1 and msg
2366 def test_ip_in_subnet(self
):
2367 # valid ip and only one valid subnet
2368 rc
= cd
.ip_in_subnets('192.168.100.1', '192.168.100.0/24')
2371 # valid ip and valid subnets list without spaces
2372 rc
= cd
.ip_in_subnets('192.168.100.1', '192.168.100.0/24,10.90.90.0/24')
2375 # valid ip and valid subnets list with spaces
2376 rc
= cd
.ip_in_subnets('10.90.90.2', '192.168.1.0/24, 192.168.100.0/24, 10.90.90.0/24')
2379 # valid ip that doesn't belong to any subnet
2380 rc
= cd
.ip_in_subnets('192.168.100.2', '192.168.50.0/24, 10.90.90.0/24')
2383 # valid ip that doesn't belong to the subnet (only 14 hosts)
2384 rc
= cd
.ip_in_subnets('192.168.100.20', '192.168.100.0/28')
2387 # valid ip and valid IPV6 network
2388 rc
= cd
.ip_in_subnets('fe80::5054:ff:fef4:873a', 'fe80::/64')
2391 # valid wrapped ip and valid IPV6 network
2392 rc
= cd
.ip_in_subnets('[fe80::5054:ff:fef4:873a]', 'fe80::/64')
2395 # valid ip and that doesn't belong to IPV6 network
2396 rc
= cd
.ip_in_subnets('fe80::5054:ff:fef4:873a', '2001:db8:85a3::/64')
2399 # invalid IPv4 and valid subnets list
2400 with pytest
.raises(Exception):
2401 rc
= cd
.ip_in_sublets('10.90.200.', '192.168.1.0/24, 192.168.100.0/24, 10.90.90.0/24')
2403 # invalid IPv6 and valid subnets list
2404 with pytest
.raises(Exception):
2405 rc
= cd
.ip_in_sublets('fe80:2030:31:24', 'fe80::/64')
2407 class TestRescan(fake_filesystem_unittest
.TestCase
):
2410 self
.setUpPyfakefs()
2411 self
.fs
.create_dir('/sys/class')
2412 self
.ctx
= cd
.CephadmContext()
2413 self
.ctx
.func
= cd
.command_rescan_disks
2415 def test_no_hbas(self
):
2416 out
= cd
.command_rescan_disks(self
.ctx
)
2417 assert out
== 'Ok. No compatible HBAs found'
2419 def test_success(self
):
2420 self
.fs
.create_file('/sys/class/scsi_host/host0/scan')
2421 self
.fs
.create_file('/sys/class/scsi_host/host1/scan')
2422 out
= cd
.command_rescan_disks(self
.ctx
)
2423 assert out
.startswith('Ok. 2 adapters detected: 2 rescanned, 0 skipped, 0 failed')
2425 def test_skip_usb_adapter(self
):
2426 self
.fs
.create_file('/sys/class/scsi_host/host0/scan')
2427 self
.fs
.create_file('/sys/class/scsi_host/host1/scan')
2428 self
.fs
.create_file('/sys/class/scsi_host/host1/proc_name', contents
='usb-storage')
2429 out
= cd
.command_rescan_disks(self
.ctx
)
2430 assert out
.startswith('Ok. 2 adapters detected: 1 rescanned, 1 skipped, 0 failed')
2432 def test_skip_unknown_adapter(self
):
2433 self
.fs
.create_file('/sys/class/scsi_host/host0/scan')
2434 self
.fs
.create_file('/sys/class/scsi_host/host1/scan')
2435 self
.fs
.create_file('/sys/class/scsi_host/host1/proc_name', contents
='unknown')
2436 out
= cd
.command_rescan_disks(self
.ctx
)
2437 assert out
.startswith('Ok. 2 adapters detected: 1 rescanned, 1 skipped, 0 failed')