]>
git.proxmox.com Git - ceph.git/blob - ceph/src/cephadm/tests/test_cephadm.py
13 from http
. server
import HTTPServer
14 from urllib
. request
import Request
, urlopen
15 from urllib
. error
import HTTPError
17 from typing
import List
, Optional
19 from . fixtures
import (
28 with mock
. patch ( 'builtins.open' , create
= True ):
29 from importlib
. machinery
import SourceFileLoader
30 cd
= SourceFileLoader ( 'cephadm' , 'cephadm' ). load_module ()
33 class TestCephAdm ( object ):
35 def test_docker_unit_file ( self
):
37 ctx
. container_engine
= mock_docker ()
38 r
= cd
. get_unit_file ( ctx
, '9b9d7609-f4d5-4aba-94c8-effa764d96c9' )
39 assert 'Requires=docker.service' in r
40 ctx
. container_engine
= mock_podman ()
41 r
= cd
. get_unit_file ( ctx
, '9b9d7609-f4d5-4aba-94c8-effa764d96c9' )
42 assert 'Requires=docker.service' not in r
44 @mock . patch ( 'cephadm.logger' )
45 def test_attempt_bind ( self
, logger
):
52 _os_error
. errno
= errno
55 for side_effect
, expected_exception
in (
56 ( os_error ( errno
. EADDRINUSE
), cd
. PortOccupiedError
),
57 ( os_error ( errno
. EAFNOSUPPORT
), cd
. Error
),
58 ( os_error ( errno
. EADDRNOTAVAIL
), cd
. Error
),
62 _socket
. bind
. side_effect
= side_effect
64 cd
. attempt_bind ( ctx
, _socket
, address
, port
)
65 except Exception as e
:
66 assert isinstance ( e
, expected_exception
)
68 if expected_exception
is not None :
71 @mock . patch ( 'cephadm.attempt_bind' )
72 @mock . patch ( 'cephadm.logger' )
73 def test_port_in_use ( self
, logger
, attempt_bind
):
76 assert cd
. port_in_use ( empty_ctx
, 9100 ) == False
78 attempt_bind
. side_effect
= cd
. PortOccupiedError ( 'msg' )
79 assert cd
. port_in_use ( empty_ctx
, 9100 ) == True
82 os_error
. errno
= errno
. EADDRNOTAVAIL
83 attempt_bind
. side_effect
= os_error
84 assert cd
. port_in_use ( empty_ctx
, 9100 ) == False
87 os_error
. errno
= errno
. EAFNOSUPPORT
88 attempt_bind
. side_effect
= os_error
89 assert cd
. port_in_use ( empty_ctx
, 9100 ) == False
91 @mock . patch ( 'socket.socket' )
92 @mock . patch ( 'cephadm.logger' )
93 def test_check_ip_port_success ( self
, logger
, _socket
):
95 ctx
. skip_ping_check
= False # enables executing port check with `check_ip_port`
97 for address
, address_family
in (
98 ( '0.0.0.0' , socket
. AF_INET
),
99 ( '::' , socket
. AF_INET6
),
102 cd
. check_ip_port ( ctx
, address
, 9100 )
106 assert _socket
. call_args
== mock
. call ( address_family
, socket
. SOCK_STREAM
)
108 @mock . patch ( 'socket.socket' )
109 @mock . patch ( 'cephadm.logger' )
110 def test_check_ip_port_failure ( self
, logger
, _socket
):
112 ctx
. skip_ping_check
= False # enables executing port check with `check_ip_port`
115 _os_error
= OSError ()
116 _os_error
. errno
= errno
119 for address
, address_family
in (
120 ( '0.0.0.0' , socket
. AF_INET
),
121 ( '::' , socket
. AF_INET6
),
123 for side_effect
, expected_exception
in (
124 ( os_error ( errno
. EADDRINUSE
), cd
. PortOccupiedError
),
125 ( os_error ( errno
. EADDRNOTAVAIL
), cd
. Error
),
126 ( os_error ( errno
. EAFNOSUPPORT
), cd
. Error
),
129 mock_socket_obj
= mock
. Mock ()
130 mock_socket_obj
. bind
. side_effect
= side_effect
131 _socket
. return_value
= mock_socket_obj
133 cd
. check_ip_port ( ctx
, address
, 9100 )
134 except Exception as e
:
135 assert isinstance ( e
, expected_exception
)
137 if side_effect
is not None :
141 def test_is_not_fsid ( self
):
142 assert not cd
. is_fsid ( 'no-uuid' )
144 def test_is_fsid ( self
):
145 assert cd
. is_fsid ( 'e863154d-33c7-4350-bca5-921e0467e55b' )
147 def test__get_parser_image ( self
):
148 args
= cd
._ parse
_ args
([ '--image' , 'foo' , 'version' ])
149 assert args
. image
== 'foo'
151 def test_CustomValidation ( self
):
152 assert cd
._ parse
_ args
([ 'deploy' , '--name' , 'mon.a' , '--fsid' , 'fsid' ])
154 with pytest
. raises ( SystemExit ):
155 cd
._ parse
_ args
([ 'deploy' , '--name' , 'wrong' , '--fsid' , 'fsid' ])
157 @pytest . mark
. parametrize ( "test_input, expected" , [
159 ( "1.6.2-stable2" , ( 1 , 6 , 2 )),
161 def test_parse_podman_version ( self
, test_input
, expected
):
162 assert cd
._ parse
_ podman
_ version
( test_input
) == expected
164 def test_parse_podman_version_invalid ( self
):
165 with pytest
. raises ( ValueError ) as res
:
166 cd
._ parse
_ podman
_ version
( 'inval.id' )
167 assert 'inval' in str ( res
. value
)
169 @pytest . mark
. parametrize ( "test_input, expected" , [
172 default via 192.168.178.1 dev enxd89ef3f34260 proto dhcp metric 100
173 10.0.0.0/8 via 10.4.0.1 dev tun0 proto static metric 50
174 10.3.0.0/21 via 10.4.0.1 dev tun0 proto static metric 50
175 10.4.0.1 dev tun0 proto kernel scope link src 10.4.0.2 metric 50
176 137.1.0.0/16 via 10.4.0.1 dev tun0 proto static metric 50
177 138.1.0.0/16 via 10.4.0.1 dev tun0 proto static metric 50
178 139.1.0.0/16 via 10.4.0.1 dev tun0 proto static metric 50
179 140.1.0.0/17 via 10.4.0.1 dev tun0 proto static metric 50
180 141.1.0.0/16 via 10.4.0.1 dev tun0 proto static metric 50
181 169.254.0.0/16 dev docker0 scope link metric 1000
182 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
183 192.168.39.0/24 dev virbr1 proto kernel scope link src 192.168.39.1 linkdown
184 192.168.122.0/24 dev virbr0 proto kernel scope link src 192.168.122.1 linkdown
185 192.168.178.0/24 dev enxd89ef3f34260 proto kernel scope link src 192.168.178.28 metric 100
186 192.168.178.1 dev enxd89ef3f34260 proto static scope link metric 100
187 195.135.221.12 via 192.168.178.1 dev enxd89ef3f34260 proto static metric 100
190 '10.4.0.1' : { 'tun0' : [ '10.4.0.2' ]},
191 '172.17.0.0/16' : { 'docker0' : [ '172.17.0.1' ]},
192 '192.168.39.0/24' : { 'virbr1' : [ '192.168.39.1' ]},
193 '192.168.122.0/24' : { 'virbr0' : [ '192.168.122.1' ]},
194 '192.168.178.0/24' : { 'enxd89ef3f34260' : [ '192.168.178.28' ]}
198 default via 10.3.64.1 dev eno1 proto static metric 100
199 10.3.64.0/24 dev eno1 proto kernel scope link src 10.3.64.23 metric 100
200 10.3.64.0/24 dev eno1 proto kernel scope link src 10.3.64.27 metric 100
201 10.88.0.0/16 dev cni-podman0 proto kernel scope link src 10.88.0.1 linkdown
202 172.21.0.0/20 via 172.21.3.189 dev tun0
203 172.21.1.0/20 via 172.21.3.189 dev tun0
204 172.21.2.1 via 172.21.3.189 dev tun0
205 172.21.3.1 dev tun0 proto kernel scope link src 172.21.3.2
206 172.21.4.0/24 via 172.21.3.1 dev tun0
207 172.21.5.0/24 via 172.21.3.1 dev tun0
208 172.21.6.0/24 via 172.21.3.1 dev tun0
209 172.21.7.0/24 via 172.21.3.1 dev tun0
210 192.168.122.0/24 dev virbr0 proto kernel scope link src 192.168.122.1 linkdown
213 '10.3.64.0/24' : { 'eno1' : [ '10.3.64.23' , '10.3.64.27' ]},
214 '10.88.0.0/16' : { 'cni-podman0' : [ '10.88.0.1' ]},
215 '172.21.3.1' : { 'tun0' : [ '172.21.3.2' ]},
216 '192.168.122.0/24' : { 'virbr0' : [ '192.168.122.1' ]}}
219 def test_parse_ipv4_route ( self
, test_input
, expected
):
220 assert cd
._ parse
_ ipv
4_ route
( test_input
) == expected
222 @pytest . mark
. parametrize ( "test_routes, test_ips, expected" , [
225 ::1 dev lo proto kernel metric 256 pref medium
226 fe80::/64 dev eno1 proto kernel metric 100 pref medium
227 fe80::/64 dev br-3d443496454c proto kernel metric 256 linkdown pref medium
228 fe80::/64 dev tun0 proto kernel metric 256 pref medium
229 fe80::/64 dev br-4355f5dbb528 proto kernel metric 256 pref medium
230 fe80::/64 dev docker0 proto kernel metric 256 linkdown pref medium
231 fe80::/64 dev cni-podman0 proto kernel metric 256 linkdown pref medium
232 fe80::/64 dev veth88ba1e8 proto kernel metric 256 pref medium
233 fe80::/64 dev vethb6e5fc7 proto kernel metric 256 pref medium
234 fe80::/64 dev vethaddb245 proto kernel metric 256 pref medium
235 fe80::/64 dev vethbd14d6b proto kernel metric 256 pref medium
236 fe80::/64 dev veth13e8fd2 proto kernel metric 256 pref medium
237 fe80::/64 dev veth1d3aa9e proto kernel metric 256 pref medium
238 fe80::/64 dev vethe485ca9 proto kernel metric 256 pref medium
241 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 state UNKNOWN qlen 1000
242 inet6 ::1/128 scope host
243 valid_lft forever preferred_lft forever
244 2: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000
245 inet6 fe80::225:90ff:fee5:26e8/64 scope link noprefixroute
246 valid_lft forever preferred_lft forever
247 6: br-3d443496454c: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 state DOWN
248 inet6 fe80::42:23ff:fe9d:ee4/64 scope link
249 valid_lft forever preferred_lft forever
250 7: br-4355f5dbb528: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP
251 inet6 fe80::42:6eff:fe35:41fe/64 scope link
252 valid_lft forever preferred_lft forever
253 8: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 state DOWN
254 inet6 fe80::42:faff:fee6:40a0/64 scope link
255 valid_lft forever preferred_lft forever
256 11: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 state UNKNOWN qlen 100
257 inet6 fe80::98a6:733e:dafd:350/64 scope link stable-privacy
258 valid_lft forever preferred_lft forever
259 28: cni-podman0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 state DOWN qlen 1000
260 inet6 fe80::3449:cbff:fe89:b87e/64 scope link
261 valid_lft forever preferred_lft forever
262 31: vethaddb245@if30: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP
263 inet6 fe80::90f7:3eff:feed:a6bb/64 scope link
264 valid_lft forever preferred_lft forever
265 33: veth88ba1e8@if32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP
266 inet6 fe80::d:f5ff:fe73:8c82/64 scope link
267 valid_lft forever preferred_lft forever
268 35: vethbd14d6b@if34: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP
269 inet6 fe80::b44f:8ff:fe6f:813d/64 scope link
270 valid_lft forever preferred_lft forever
271 37: vethb6e5fc7@if36: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP
272 inet6 fe80::4869:c6ff:feaa:8afe/64 scope link
273 valid_lft forever preferred_lft forever
274 39: veth13e8fd2@if38: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP
275 inet6 fe80::78f4:71ff:fefe:eb40/64 scope link
276 valid_lft forever preferred_lft forever
277 41: veth1d3aa9e@if40: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP
278 inet6 fe80::24bd:88ff:fe28:5b18/64 scope link
279 valid_lft forever preferred_lft forever
280 43: vethe485ca9@if42: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP
281 inet6 fe80::6425:87ff:fe42:b9f0/64 scope link
282 valid_lft forever preferred_lft forever
287 "fe80::225:90ff:fee5:26e8"
290 "fe80::42:23ff:fe9d:ee4"
293 "fe80::98a6:733e:dafd:350"
296 "fe80::42:6eff:fe35:41fe"
299 "fe80::42:faff:fee6:40a0"
302 "fe80::3449:cbff:fe89:b87e"
305 "fe80::d:f5ff:fe73:8c82"
308 "fe80::4869:c6ff:feaa:8afe"
311 "fe80::90f7:3eff:feed:a6bb"
314 "fe80::b44f:8ff:fe6f:813d"
317 "fe80::78f4:71ff:fefe:eb40"
320 "fe80::24bd:88ff:fe28:5b18"
323 "fe80::6425:87ff:fe42:b9f0"
330 ::1 dev lo proto kernel metric 256 pref medium
331 2001:1458:301:eb::100:1a dev ens20f0 proto kernel metric 100 pref medium
332 2001:1458:301:eb::/64 dev ens20f0 proto ra metric 100 pref medium
333 fd01:1458:304:5e::/64 dev ens20f0 proto ra metric 100 pref medium
334 fe80::/64 dev ens20f0 proto kernel metric 100 pref medium
335 default proto ra metric 100
336 nexthop via fe80::46ec:ce00:b8a0:d3c8 dev ens20f0 weight 1
337 nexthop via fe80::46ec:ce00:b8a2:33c8 dev ens20f0 weight 1 pref medium
340 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 state UNKNOWN qlen 1000
341 inet6 ::1/128 scope host
342 valid_lft forever preferred_lft forever
343 2: ens20f0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000
344 inet6 2001:1458:301:eb::100:1a/128 scope global dynamic noprefixroute
345 valid_lft 590879sec preferred_lft 590879sec
346 inet6 fe80::2e60:cff:fef8:da41/64 scope link noprefixroute
347 valid_lft forever preferred_lft forever
350 '2001:1458:301:eb::/64' : {
352 '2001:1458:301:eb::100:1a'
356 'ens20f0' : [ 'fe80::2e60:cff:fef8:da41' ],
358 'fd01:1458:304:5e::/64' : {
364 def test_parse_ipv6_route ( self
, test_routes
, test_ips
, expected
):
365 assert cd
._ parse
_ ipv
6_ route
( test_routes
, test_ips
) == expected
367 def test_is_ipv6 ( self
):
368 cd
. logger
= mock
. Mock ()
369 for good
in ( "[::1]" , "::1" ,
370 "fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" ):
371 assert cd
. is_ipv6 ( good
)
372 for bad
in ( "127.0.0.1" ,
373 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffg" ,
374 "1:2:3:4:5:6:7:8:9" , "fd00::1::1" , "[fg::1]" ):
375 assert not cd
. is_ipv6 ( bad
)
377 def test_unwrap_ipv6 ( self
):
378 def unwrap_test ( address
, expected
):
379 assert cd
. unwrap_ipv6 ( address
) == expected
382 ( '::1' , '::1' ), ( '[::1]' , '::1' ),
383 ( '[fde4:8dba:82e1:0:5054:ff:fe6a:357]' , 'fde4:8dba:82e1:0:5054:ff:fe6a:357' ),
384 ( 'can actually be any string' , 'can actually be any string' ),
385 ( '[but needs to be stripped] ' , '[but needs to be stripped] ' )]
386 for address
, expected
in tests
:
387 unwrap_test ( address
, expected
)
389 def test_wrap_ipv6 ( self
):
390 def wrap_test ( address
, expected
):
391 assert cd
. wrap_ipv6 ( address
) == expected
394 ( '::1' , '[::1]' ), ( '[::1]' , '[::1]' ),
395 ( 'fde4:8dba:82e1:0:5054:ff:fe6a:357' ,
396 '[fde4:8dba:82e1:0:5054:ff:fe6a:357]' ),
397 ( 'myhost.example.com' , 'myhost.example.com' ),
398 ( '192.168.0.1' , '192.168.0.1' ),
399 ( '' , '' ), ( 'fd00::1::1' , 'fd00::1::1' )]
400 for address
, expected
in tests
:
401 wrap_test ( address
, expected
)
403 @mock . patch ( 'cephadm.call_throws' )
404 @mock . patch ( 'cephadm.get_parm' )
405 def test_registry_login ( self
, get_parm
, call_throws
):
407 # test normal valid login with url, username and password specified
408 call_throws
. return_value
= '' , '' , 0
409 ctx
: cd
. CephadmContext
= cd
. cephadm_init_ctx (
410 [ 'registry-login' , '--registry-url' , 'sample-url' ,
411 '--registry-username' , 'sample-user' , '--registry-password' ,
413 ctx
. container_engine
= mock_docker ()
414 retval
= cd
. command_registry_login ( ctx
)
417 # test bad login attempt with invalid arguments given
418 ctx
: cd
. CephadmContext
= cd
. cephadm_init_ctx (
419 [ 'registry-login' , '--registry-url' , 'bad-args-url' ])
420 with pytest
. raises ( Exception ) as e
:
421 assert cd
. command_registry_login ( ctx
)
422 assert str ( e
. value
) == ( 'Invalid custom registry arguments received. To login to a custom registry include '
423 '--registry-url, --registry-username and --registry-password options or --registry-json option' )
425 # test normal valid login with json file
426 get_parm
. return_value
= { "url" : "sample-url" , "username" : "sample-username" , "password" : "sample-password" }
427 ctx
: cd
. CephadmContext
= cd
. cephadm_init_ctx (
428 [ 'registry-login' , '--registry-json' , 'sample-json' ])
429 ctx
. container_engine
= mock_docker ()
430 retval
= cd
. command_registry_login ( ctx
)
433 # test bad login attempt with bad json file
434 get_parm
. return_value
= { "bad-json" : "bad-json" }
435 ctx
: cd
. CephadmContext
= cd
. cephadm_init_ctx (
436 [ 'registry-login' , '--registry-json' , 'sample-json' ])
437 with pytest
. raises ( Exception ) as e
:
438 assert cd
. command_registry_login ( ctx
)
439 assert str ( e
. value
) == ( "json provided for custom registry login did not include all necessary fields. "
440 "Please setup json file as \n "
442 " \" url \" : \" REGISTRY_URL \" , \n "
443 " \" username \" : \" REGISTRY_USERNAME \" , \n "
444 " \" password \" : \" REGISTRY_PASSWORD \"\n "
447 # test login attempt with valid arguments where login command fails
448 call_throws
. side_effect
= Exception
449 ctx
: cd
. CephadmContext
= cd
. cephadm_init_ctx (
450 [ 'registry-login' , '--registry-url' , 'sample-url' ,
451 '--registry-username' , 'sample-user' , '--registry-password' ,
453 with pytest
. raises ( Exception ) as e
:
454 cd
. command_registry_login ( ctx
)
455 assert str ( e
. value
) == "Failed to login to custom registry @ sample-url as sample-user with given password"
457 def test_get_image_info_from_inspect ( self
):
459 out
= """204a01f9b0b6710dd0c0af7f37ce7139c47ff0f0105d778d7104c69282dfbbf1,[docker.io/ceph/ceph@sha256:1cc9b824e1b076cdff52a9aa3f0cc8557d879fb2fbbba0cafed970aca59a3992]"""
460 r
= cd
. get_image_info_from_inspect ( out
, 'registry/ceph/ceph:latest' )
463 'image_id' : '204a01f9b0b6710dd0c0af7f37ce7139c47ff0f0105d778d7104c69282dfbbf1' ,
464 'repo_digests' : [ 'docker.io/ceph/ceph@sha256:1cc9b824e1b076cdff52a9aa3f0cc8557d879fb2fbbba0cafed970aca59a3992' ]
468 out
= """sha256:16f4549cf7a8f112bbebf7946749e961fbbd1b0838627fe619aab16bc17ce552,[quay.ceph.io/ceph-ci/ceph@sha256:4e13da36c1bd6780b312a985410ae678984c37e6a9493a74c87e4a50b9bda41f]"""
469 r
= cd
. get_image_info_from_inspect ( out
, 'registry/ceph/ceph:latest' )
471 'image_id' : '16f4549cf7a8f112bbebf7946749e961fbbd1b0838627fe619aab16bc17ce552' ,
472 'repo_digests' : [ 'quay.ceph.io/ceph-ci/ceph@sha256:4e13da36c1bd6780b312a985410ae678984c37e6a9493a74c87e4a50b9bda41f' ]
475 # multiple digests (podman)
476 out
= """e935122ab143a64d92ed1fbb27d030cf6e2f0258207be1baf1b509c466aeeb42,[docker.io/prom/prometheus@sha256:e4ca62c0d62f3e886e684806dfe9d4e0cda60d54986898173c1083856cfda0f4 docker.io/prom/prometheus@sha256:efd99a6be65885c07c559679a0df4ec709604bcdd8cd83f0d00a1a683b28fb6a]"""
477 r
= cd
. get_image_info_from_inspect ( out
, 'registry/prom/prometheus:latest' )
479 'image_id' : 'e935122ab143a64d92ed1fbb27d030cf6e2f0258207be1baf1b509c466aeeb42' ,
481 'docker.io/prom/prometheus@sha256:e4ca62c0d62f3e886e684806dfe9d4e0cda60d54986898173c1083856cfda0f4' ,
482 'docker.io/prom/prometheus@sha256:efd99a6be65885c07c559679a0df4ec709604bcdd8cd83f0d00a1a683b28fb6a' ,
487 def test_dict_get ( self
):
488 result
= cd
. dict_get ({ 'a' : 1 }, 'a' , require
= True )
490 result
= cd
. dict_get ({ 'a' : 1 }, 'b' )
491 assert result
is None
492 result
= cd
. dict_get ({ 'a' : 1 }, 'b' , default
= 2 )
495 def test_dict_get_error ( self
):
496 with pytest
. raises ( cd
. Error
):
497 cd
. dict_get ({ 'a' : 1 }, 'b' , require
= True )
499 def test_dict_get_join ( self
):
500 result
= cd
. dict_get_join ({ 'foo' : [ 'a' , 'b' ]}, 'foo' )
501 assert result
== 'a \n b'
502 result
= cd
. dict_get_join ({ 'foo' : [ 1 , 2 ]}, 'foo' )
503 assert result
== '1 \n 2'
504 result
= cd
. dict_get_join ({ 'bar' : 'a' }, 'bar' )
506 result
= cd
. dict_get_join ({ 'a' : 1 }, 'a' )
509 def test_last_local_images ( self
):
511 docker.io/ceph/daemon-base@
512 docker.io/ceph/ceph:v15.2.5
513 docker.io/ceph/daemon-base:octopus
515 image
= cd
._ filter
_l ast
_l ocal
_ ceph
_ image
( out
)
516 assert image
== 'docker.io/ceph/ceph:v15.2.5'
518 def test_normalize_image_digest ( self
):
519 s
= 'myhostname:5000/ceph/ceph@sha256:753886ad9049004395ae990fbb9b096923b5a518b819283141ee8716ddf55ad1'
520 assert cd
. normalize_image_digest ( s
) == s
522 s
= 'ceph/ceph:latest'
523 assert cd
. normalize_image_digest ( s
) == f
'{cd.DEFAULT_REGISTRY}/ {s} '
525 class TestCustomContainer ( unittest
. TestCase
):
526 cc
: cd
. CustomContainer
529 self
. cc
= cd
. CustomContainer (
530 'e863154d-33c7-4350-bca5-921e0467e55b' ,
533 'entrypoint' : 'bash' ,
539 'envs' : [ 'SECRET=password' ],
540 'ports' : [ 8080 , 8443 ],
542 '/CONFIG_DIR' : '/foo/conf' ,
543 'bar/config' : '/bar:ro'
548 'source=/CONFIG_DIR' ,
549 'destination=/foo/conf' ,
555 'destination=/bar:ro' ,
560 image
= 'docker.io/library/hello-world:latest'
563 def test_entrypoint ( self
):
564 self
. assertEqual ( self
. cc
. entrypoint
, 'bash' )
566 def test_uid_gid ( self
):
567 self
. assertEqual ( self
. cc
. uid
, 65534 )
568 self
. assertEqual ( self
. cc
. gid
, 1000 )
570 def test_ports ( self
):
571 self
. assertEqual ( self
. cc
. ports
, [ 8080 , 8443 ])
573 def test_get_container_args ( self
):
574 result
= self
. cc
. get_container_args ()
575 self
. assertEqual ( result
, [
580 def test_get_container_envs ( self
):
581 result
= self
. cc
. get_container_envs ()
582 self
. assertEqual ( result
, [ 'SECRET=password' ])
584 def test_get_container_mounts ( self
):
585 result
= self
. cc
. get_container_mounts ( '/xyz' )
586 self
. assertDictEqual ( result
, {
587 '/CONFIG_DIR' : '/foo/conf' ,
588 '/xyz/bar/config' : '/bar:ro'
591 def test_get_container_binds ( self
):
592 result
= self
. cc
. get_container_binds ( '/xyz' )
593 self
. assertEqual ( result
, [
596 'source=/CONFIG_DIR' ,
597 'destination=/foo/conf' ,
602 'source=/xyz/bar/config' ,
603 'destination=/bar:ro' ,
609 class TestCephadmExporter ( object ):
610 exporter
: cd
. CephadmDaemon
611 files_created
: List
[ str ] = []
612 crt
= """-----BEGIN CERTIFICATE-----
613 MIIC1zCCAb8CEFHoZE2MfUVzo53fzzBKAT0wDQYJKoZIhvcNAQENBQAwKjENMAsG
614 A1UECgwEQ2VwaDEZMBcGA1UECwwQY2VwaGFkbS1leHBvcnRlcjAeFw0yMDExMjUy
615 MzEwNTVaFw0zMDExMjMyMzEwNTVaMCoxDTALBgNVBAoMBENlcGgxGTAXBgNVBAsM
616 EGNlcGhhZG0tZXhwb3J0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
617 AQCsTfcJcXbREqfx1zTUuEmK+lJn9WWjk0URRF1Z+QgPkascNdkX16PnvhbGwXmF
618 BTdAcNl7V0U+z4EsGJ7hJsB7qTq6Rb6wNl7r0OxjeWOmB9xbF4Q/KR5yrbM1DA9A
619 B5fNswrUXViku5Y2jlOAz+ZMBhYxMx0edqhxSn297j04Z6RF4Mvkc43v0FH7Ju7k
620 O5+0VbdzcOdu37DFpoE4Ll2MZ/GuAHcJ8SD06sEdzFEjRCraav976743XcUlhZGX
621 ZTTG/Zf/a+wuCjtMG3od7vRFfuRrM5oTE133DuQ5deR7ybcZNDyopDjHF8xB1bAk
622 IOz4SbP6Q25K99Czm1K+3kMLAgMBAAEwDQYJKoZIhvcNAQENBQADggEBACmtvZb8
623 dJGHx/WC0/JHxnEJCJM2qnn87ELzbbIQL1w1Yb/I6JQYPgq+WiQPaHaLL9eYsm0l
624 dFwvrh+WC0JpXDfADnUnkTSB/WpZ2nC+2JxBptrQEuIcqNXpcJd0bKDiHunv04JI
625 uEVpTAK05dBV38qNmIlu4HyB4OEnuQpyOr9xpIhdxuJ95O9K0j5BIw98ZaEwYNUP
626 Rm3YlQwfS6R5xaBvL9kyfxyAD2joNj44q6w/5zj4egXVIA5VpkQm8DmMtu0Pd2NG
627 dzfYRmqrDolh+rty8HiyIxzeDJQ5bj6LKbUkmABvX50nDySVyMfHmt461/n7W65R
628 CHFLoOmfJJik+Uc= \n -----END CERTIFICATE-----
630 key
= """-----BEGIN PRIVATE KEY-----
631 MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCsTfcJcXbREqfx
632 1zTUuEmK+lJn9WWjk0URRF1Z+QgPkascNdkX16PnvhbGwXmFBTdAcNl7V0U+z4Es
633 GJ7hJsB7qTq6Rb6wNl7r0OxjeWOmB9xbF4Q/KR5yrbM1DA9AB5fNswrUXViku5Y2
634 jlOAz+ZMBhYxMx0edqhxSn297j04Z6RF4Mvkc43v0FH7Ju7kO5+0VbdzcOdu37DF
635 poE4Ll2MZ/GuAHcJ8SD06sEdzFEjRCraav976743XcUlhZGXZTTG/Zf/a+wuCjtM
636 G3od7vRFfuRrM5oTE133DuQ5deR7ybcZNDyopDjHF8xB1bAkIOz4SbP6Q25K99Cz
637 m1K+3kMLAgMBAAECggEASnAwToMXWsGdjqxzpYasNv9oBIOO0nk4OHp5ffpJUjiT
638 XM+ip1tA80g7HMjPD/mt4gge3NtaDgWlf4Bve0O7mnEE7x5cgFIs9eG/jkYOF9eD
639 ilMBjivcfJywNDWujPH60iIMhqyBNEHaZl1ck+S9UJC8m6rCZLvMj40n/5riFfBy
640 1sjf2uOwcfWrjSj9Ju4wlMI6khSSz2aYC7glQQ/fo2+YArbEUcy60iloPQ6wEgZK
641 okoVWZA9AehwLcnRjkwd9EVmMMtRGPE/AcP4s/kKA0tRDRicPLN727Ke/yxv+Ppo
642 hbIZIcOn7soOFAENcodJ4YRSCd++QfCNaVAi7vwWWQKBgQDeBY4vvr+H0brbSjQg
643 O7Fpqub/fxZY3UoHWDqWs2X4o3qhDqaTQODpuYtCm8YQE//55JoLWKAD0evq5dLS
644 YLrtC1Vyxf+TA7opCUjWBe+liyndbJdB5q0zF7qdWUtQKGVSWyUWhK8gHa6M64fP
645 oi83DD7F0OGusTWGtfbceErk/wKBgQDGrJLRo/5xnAH5VmPfNu+S6h0M2qM6CYwe
646 Y5wHFG2uQQct73adf53SkhvZVmOzJsWQbVnlDOKMhqazcs+7VWRgO5X3naWVcctE
647 Hggw9MgpbXAWFOI5sNYsCYE58E+fTHjE6O4A3MhMCsze+CIC3sKuPQBBiL9bWSOX
648 8POswqfl9QKBgDe/nVxPwTgRaaH2l/AgDQRDbY1qE+psZlJBzTRaB5jPM9ONIjaH
649 a/JELLuk8a7H1tagmC2RK1zKMTriSnWY5FbxKZuQLAR2QyBavHdBNlOTBggbZD+f
650 9I2Hv8wSx95wxkBPsphc6Lxft5ya55czWjewU3LIaGK9DHuu5TWm3udxAoGBAJGP
651 PsJ59KIoOwoDUYjpJv3sqPwR9CVBeXeKY3aMcQ+KdUgiejVKmsb8ZYsG0GUhsv3u
652 ID7BAfsTbG9tXuVR2wjmnymcRwUHKnXtyvKTZVN06vpCsryx4zjAff2FI9ECpjke
653 r8HSAK41+4QhKEoSC3C9IMLi/dBfrsRTtTSOKZVBAoGBAI2dl5HEIFpufaI4toWM
654 LO5HFrlXgRDGoc/+Byr5/8ZZpYpU115Ol/q6M+l0koV2ygJ9jeJJEllFWykIDS6F
655 XxazFI74swAqobHb2ZS/SLhoVxE82DdSeXrjkTvUjNtrW5zs1gIMKBR4nD6H8AqL
656 iMN28C2bKGao5UHvdER1rGy7
657 -----END PRIVATE KEY-----
659 token
= "MyAccessToken"
662 def setup_class ( cls
):
663 # create the ssl files
664 fname
= os
. path
. join ( os
. getcwd (), 'crt' )
665 with
open ( fname
, 'w' ) as crt
:
667 cls
. files_created
. append ( fname
)
668 fname
= os
. path
. join ( os
. getcwd (), 'key' )
669 with
open ( fname
, 'w' ) as crt
:
671 cls
. files_created
. append ( fname
)
672 fname
= os
. path
. join ( os
. getcwd (), 'token' )
673 with
open ( fname
, 'w' ) as crt
:
675 cls
. files_created
. append ( fname
)
676 # start a simple http instance to test the requesthandler
677 cls
. server
= HTTPServer (( '0.0.0.0' , 9443 ), cd
. CephadmDaemonHandler
)
678 cls
. server
. cephadm_cache
= cd
. CephadmCache ()
679 cls
. server
. token
= cls
. token
680 t
= threading
. Thread ( target
= cls
. server
. serve_forever
)
685 def teardown_class ( cls
):
686 cls
. server
. shutdown ()
687 assert len ( cls
. files_created
) > 0
688 for f
in cls
. files_created
:
691 def setup_method ( self
):
692 # re-init the cache for every test
693 TestCephadmExporter
. server
. cephadm_cache
= cd
. CephadmCache ()
695 def teardown_method ( self
):
698 def test_files_ready ( self
):
699 assert os
. path
. exists ( os
. path
. join ( os
. getcwd (), 'crt' ))
700 assert os
. path
. exists ( os
. path
. join ( os
. getcwd (), 'key' ))
701 assert os
. path
. exists ( os
. path
. join ( os
. getcwd (), 'token' ))
703 def test_can_run ( self
, exporter
):
704 assert exporter
. can_run
706 def test_token_valid ( self
, exporter
):
707 assert exporter
. token
== self
. token
709 def test_unit_name ( self
, exporter
):
710 assert exporter
. unit_name
711 assert exporter
. unit_name
== "ceph-foobar-cephadm-exporter.test.service"
713 def test_unit_run ( self
, exporter
):
714 assert exporter
. unit_run
715 lines
= exporter
. unit_run
. split ( ' \n ' )
716 assert len ( lines
) == 2
717 assert "cephadm exporter --fsid foobar --id test --port 9443 &" in lines
[ 1 ]
719 def test_binary_path ( self
, exporter
):
720 assert os
. path
. isfile ( exporter
. binary_path
)
722 def test_systemd_unit ( self
, exporter
):
723 assert exporter
. unit_file
725 def test_validate_passes ( self
, exporter
):
731 cd
. CephadmDaemon
. validate_config ( config
)
733 def test_validate_fails ( self
, exporter
):
738 with pytest
. raises ( cd
. Error
):
739 cd
. CephadmDaemon
. validate_config ( config
)
741 def test_port_active ( self
, exporter
):
742 assert exporter
. port_active
== True
744 def test_rqst_health_200 ( self
):
745 hdrs
={ "Authorization" : f
"Bearer {TestCephadmExporter.token}" }
746 req
= Request ( "http://localhost:9443/v1/metadata/health" , headers
= hdrs
)
748 assert r
. status
== 200
750 def test_rqst_all_inactive_500 ( self
):
751 hdrs
={ "Authorization" : f
"Bearer {TestCephadmExporter.token}" }
752 req
= Request ( "http://localhost:9443/v1/metadata" , headers
= hdrs
)
755 except HTTPError
as e
:
758 def test_rqst_no_auth_401 ( self
):
759 req
= Request ( "http://localhost:9443/v1/metadata" )
762 except HTTPError
as e
:
765 def test_rqst_bad_auth_401 ( self
):
766 hdrs
={ "Authorization" : f
"Bearer BogusAuthToken" }
767 req
= Request ( "http://localhost:9443/v1/metadata" , headers
= hdrs
)
770 except HTTPError
as e
:
773 def test_rqst_badURL_404 ( self
):
774 hdrs
={ "Authorization" : f
"Bearer {TestCephadmExporter.token}" }
775 req
= Request ( "http://localhost:9443/v1/metazoic" , headers
= hdrs
)
778 except HTTPError
as e
:
781 def test_rqst_inactive_task_204 ( self
):
782 # all tasks initialise as inactive, and then 'go' active as their thread starts
783 # so we can pick any task to check for an inactive response (no content)
784 hdrs
={ "Authorization" : f
"Bearer {TestCephadmExporter.token}" }
785 req
= Request ( "http://localhost:9443/v1/metadata/disks" , headers
= hdrs
)
787 assert r
. status
== 204
789 def test_rqst_active_task_200 ( self
):
790 TestCephadmExporter
. server
. cephadm_cache
. tasks
[ 'host' ] = 'active'
791 hdrs
={ "Authorization" : f
"Bearer {TestCephadmExporter.token}" }
792 req
= Request ( "http://localhost:9443/v1/metadata/host" , headers
= hdrs
)
794 assert r
. status
== 200
796 def test_rqst_all_206 ( self
):
797 TestCephadmExporter
. server
. cephadm_cache
. tasks
[ 'disks' ] = 'active'
798 hdrs
={ "Authorization" : f
"Bearer {TestCephadmExporter.token}" }
799 req
= Request ( "http://localhost:9443/v1/metadata" , headers
= hdrs
)
801 assert r
. status
== 206
803 def test_rqst_disks_200 ( self
):
804 TestCephadmExporter
. server
. cephadm_cache
. tasks
[ 'disks' ] = 'active'
805 hdrs
={ "Authorization" : f
"Bearer {TestCephadmExporter.token}" }
806 req
= Request ( "http://localhost:9443/v1/metadata/disks" , headers
= hdrs
)
808 assert r
. status
== 200
810 def test_thread_exception ( self
, exporter
):
811 # run is patched to invoke a mocked scrape_host thread that will raise so
812 # we check here that the exception handler updates the cache object as we'd
813 # expect with the error
815 assert exporter
. cephadm_cache
. host
[ 'scrape_errors' ]
816 assert exporter
. cephadm_cache
. host
[ 'scrape_errors' ] == [ 'ValueError exception: wah' ]
817 assert exporter
. cephadm_cache
. errors
== [ 'host thread stopped' ]
819 # Test the requesthandler does the right thing with invalid methods...
820 # ie. return a "501" - Not Implemented / Unsupported Method
821 def test_invalid_method_HEAD ( self
):
822 hdrs
={ "Authorization" : f
"Bearer {TestCephadmExporter.token}" }
823 req
= Request ( "http://localhost:9443/v1/metadata/health" , headers
= hdrs
, method
= "HEAD" )
824 with pytest
. raises ( HTTPError
, match
= r
"HTTP Error 501: .*" ) as e
:
827 def test_invalid_method_DELETE ( self
):
828 hdrs
={ "Authorization" : f
"Bearer {TestCephadmExporter.token}" }
829 req
= Request ( "http://localhost:9443/v1/metadata/health" , headers
= hdrs
, method
= "DELETE" )
830 with pytest
. raises ( HTTPError
, match
= r
"HTTP Error 501: .*" ) as e
:
833 def test_invalid_method_POST ( self
):
834 hdrs
={ "Authorization" : f
"Bearer {TestCephadmExporter.token}" }
835 req
= Request ( "http://localhost:9443/v1/metadata/health" , headers
= hdrs
, method
= "POST" )
836 with pytest
. raises ( HTTPError
, match
= r
"HTTP Error 501: .*" ) as e
:
839 def test_invalid_method_PUT ( self
):
840 hdrs
={ "Authorization" : f
"Bearer {TestCephadmExporter.token}" }
841 req
= Request ( "http://localhost:9443/v1/metadata/health" , headers
= hdrs
, method
= "PUT" )
842 with pytest
. raises ( HTTPError
, match
= r
"HTTP Error 501: .*" ) as e
:
845 def test_invalid_method_CONNECT ( self
):
846 hdrs
={ "Authorization" : f
"Bearer {TestCephadmExporter.token}" }
847 req
= Request ( "http://localhost:9443/v1/metadata/health" , headers
= hdrs
, method
= "CONNECT" )
848 with pytest
. raises ( HTTPError
, match
= r
"HTTP Error 501: .*" ) as e
:
851 def test_invalid_method_TRACE ( self
):
852 hdrs
={ "Authorization" : f
"Bearer {TestCephadmExporter.token}" }
853 req
= Request ( "http://localhost:9443/v1/metadata/health" , headers
= hdrs
, method
= "TRACE" )
854 with pytest
. raises ( HTTPError
, match
= r
"HTTP Error 501: .*" ) as e
:
857 def test_invalid_method_OPTIONS ( self
):
858 hdrs
={ "Authorization" : f
"Bearer {TestCephadmExporter.token}" }
859 req
= Request ( "http://localhost:9443/v1/metadata/health" , headers
= hdrs
, method
= "OPTIONS" )
860 with pytest
. raises ( HTTPError
, match
= r
"HTTP Error 501: .*" ) as e
:
863 def test_invalid_method_PATCH ( self
):
864 hdrs
={ "Authorization" : f
"Bearer {TestCephadmExporter.token}" }
865 req
= Request ( "http://localhost:9443/v1/metadata/health" , headers
= hdrs
, method
= "PATCH" )
866 with pytest
. raises ( HTTPError
, match
= r
"HTTP Error 501: .*" ) as e
:
869 def test_ipv4_subnet ( self
):
870 rc
, v
, msg
= cd
. check_subnet ( '192.168.1.0/24' )
871 assert rc
== 0 and v
[ 0 ] == 4
873 def test_ipv4_subnet_list ( self
):
874 rc
, v
, msg
= cd
. check_subnet ( '192.168.1.0/24,10.90.90.0/24' )
875 assert rc
== 0 and not msg
877 def test_ipv4_subnet_badlist ( self
):
878 rc
, v
, msg
= cd
. check_subnet ( '192.168.1.0/24,192.168.1.1' )
879 assert rc
== 1 and msg
881 def test_ipv4_subnet_mixed ( self
):
882 rc
, v
, msg
= cd
. check_subnet ( '192.168.100.0/24,fe80::/64' )
883 assert rc
== 0 and v
== [ 4 , 6 ]
885 def test_ipv6_subnet ( self
):
886 rc
, v
, msg
= cd
. check_subnet ( 'fe80::/64' )
887 assert rc
== 0 and v
[ 0 ] == 6
889 def test_subnet_mask_missing ( self
):
890 rc
, v
, msg
= cd
. check_subnet ( '192.168.1.58' )
891 assert rc
== 1 and msg
893 def test_subnet_mask_junk ( self
):
894 rc
, v
, msg
= cd
. check_subnet ( 'wah' )
895 assert rc
== 1 and msg
898 class TestMaintenance
:
899 systemd_target
= "ceph.00000000-0000-0000-0000-000000c0ffee.target"
901 def test_systemd_target_OK ( self
, tmp_path
):
903 wants
= base
/ "ceph.target.wants"
905 target
= wants
/ TestMaintenance
. systemd_target
907 cd
. UNIT_DIR
= str ( base
)
909 assert cd
. systemd_target_state ( target
. name
)
911 def test_systemd_target_NOTOK ( self
, tmp_path
):
913 cd
. UNIT_DIR
= str ( base
)
914 assert not cd
. systemd_target_state ( TestMaintenance
. systemd_target
)
916 def test_parser_OK ( self
):
917 args
= cd
._ parse
_ args
([ 'host-maintenance' , 'enter' ])
918 assert args
. maintenance_action
== 'enter'
920 def test_parser_BAD ( self
):
921 with pytest
. raises ( SystemExit ):
922 cd
._ parse
_ args
([ 'host-maintenance' , 'wah' ])
925 class TestMonitoring ( object ):
926 @mock . patch ( 'cephadm.call' )
927 def test_get_version_alertmanager ( self
, _call
):
929 daemon_type
= 'alertmanager'
931 # binary `prometheus`
932 _call
. return_value
= '' , '{}, version 0.16.1' . format ( daemon_type
), 0
933 version
= cd
. Monitoring
. get_version ( ctx
, 'container_id' , daemon_type
)
934 assert version
== '0.16.1'
936 # binary `prometheus-alertmanager`
937 _call
. side_effect
= (
939 ( '' , '{}, version 0.16.1' . format ( daemon_type
), 0 ),
941 version
= cd
. Monitoring
. get_version ( ctx
, 'container_id' , daemon_type
)
942 assert version
== '0.16.1'
944 @mock . patch ( 'cephadm.call' )
945 def test_get_version_prometheus ( self
, _call
):
947 daemon_type
= 'prometheus'
948 _call
. return_value
= '' , '{}, version 0.16.1' . format ( daemon_type
), 0
949 version
= cd
. Monitoring
. get_version ( ctx
, 'container_id' , daemon_type
)
950 assert version
== '0.16.1'
952 @mock . patch ( 'cephadm.call' )
953 def test_get_version_node_exporter ( self
, _call
):
955 daemon_type
= 'node-exporter'
956 _call
. return_value
= '' , '{}, version 0.16.1' . format ( daemon_type
. replace ( '-' , '_' )), 0
957 version
= cd
. Monitoring
. get_version ( ctx
, 'container_id' , daemon_type
)
958 assert version
== '0.16.1'
960 @mock . patch ( 'cephadm.os.fchown' )
961 @mock . patch ( 'cephadm.get_parm' )
962 @mock . patch ( 'cephadm.makedirs' )
963 @mock . patch ( 'cephadm.open' )
964 @mock . patch ( 'cephadm.make_log_dir' )
965 @mock . patch ( 'cephadm.make_data_dir' )
966 def test_create_daemon_dirs_prometheus ( self
, make_data_dir
, make_log_dir
, _open
, makedirs
,
969 Ensures the required and optional files given in the configuration are
970 created and mapped correctly inside the container. Tests absolute and
971 relative file paths given in the configuration.
974 fsid
= 'aaf5a720-13fe-4a3b-82b9-2d99b7fd9704'
975 daemon_type
= 'prometheus'
979 ctx
. data_dir
= '/somedir'
982 'prometheus.yml' : 'foo' ,
983 '/etc/prometheus/alerting/ceph_alerts.yml' : 'bar'
986 get_parm
. return_value
= files
988 cd
. create_daemon_dirs ( ctx
,
997 prefix
= ' {data_dir} / {fsid} / {daemon_type} . {daemon_id} ' . format (
998 data_dir
= ctx
. data_dir
,
1000 daemon_type
= daemon_type
,
1003 assert _open
. call_args_list
== [
1004 mock
. call ( '{}/etc/prometheus/prometheus.yml' . format ( prefix
), 'w' ,
1006 mock
. call ( '{}/etc/prometheus/alerting/ceph_alerts.yml' . format ( prefix
), 'w' ,
1009 assert mock
. call () .__ enter
__ (). write ( 'foo' ) in _open
. mock_calls
1010 assert mock
. call () .__ enter
__ (). write ( 'bar' ) in _open
. mock_calls
1013 class TestBootstrap ( object ):
1016 def _get_cmd (* args
):
1019 '--allow-mismatched-release' ,
1020 '--skip-prepare-host' ,
1025 def test_config ( self
, cephadm_fs
):
1027 cmd
= self
._ get
_ cmd
(
1028 '--mon-ip' , '192.168.1.1' ,
1029 '--skip-mon-network' ,
1030 '--config' , conf_file
,
1033 with
with_cephadm_ctx ( cmd
) as ctx
:
1034 msg
= r
'No such file or directory'
1035 with pytest
. raises ( cd
. Error
, match
= msg
):
1036 cd
. command_bootstrap ( ctx
)
1038 cephadm_fs
. create_file ( conf_file
)
1039 with
with_cephadm_ctx ( cmd
) as ctx
:
1040 retval
= cd
. command_bootstrap ( ctx
)
1043 def test_no_mon_addr ( self
, cephadm_fs
):
1044 cmd
= self
._ get
_ cmd
()
1045 with
with_cephadm_ctx ( cmd
) as ctx
:
1046 msg
= r
'must specify --mon-ip or --mon-addrv'
1047 with pytest
. raises ( cd
. Error
, match
= msg
):
1048 cd
. command_bootstrap ( ctx
)
1050 def test_skip_mon_network ( self
, cephadm_fs
):
1051 cmd
= self
._ get
_ cmd
( '--mon-ip' , '192.168.1.1' )
1053 with
with_cephadm_ctx ( cmd
, list_networks
={}) as ctx
:
1054 msg
= r
'--skip-mon-network'
1055 with pytest
. raises ( cd
. Error
, match
= msg
):
1056 cd
. command_bootstrap ( ctx
)
1058 cmd
+= [ '--skip-mon-network' ]
1059 with
with_cephadm_ctx ( cmd
, list_networks
={}) as ctx
:
1060 retval
= cd
. command_bootstrap ( ctx
)
1063 @pytest . mark
. parametrize ( 'mon_ip, list_networks, result' ,
1068 { '192.168.1.0/24' : { 'eth0' : [ '192.168.1.1' ]}},
1073 { '192.168.1.0/24' : { 'eth0' : [ '192.168.1.1' ]}},
1078 { '192.168.1.0/24' : { 'eth0' : [ '192.168.1.1' ]}},
1083 { '192.168.1.0/24' : { 'eth0' : [ '192.168.1.1' ]}},
1088 { '192.168.1.0/24' : { 'eth0' : [ '192.168.1.1' ]}},
1094 { '192.168.1.0/24' : { 'eth0' : [ '192.168.1.1' ]}},
1098 '::ffff:192.168.1.0' ,
1099 { "ffff::/64" : { "eth0" : [ "::ffff:c0a8:101" ]}},
1103 '::ffff:192.168.1.1' ,
1104 { "ffff::/64" : { "eth0" : [ "::ffff:c0a8:101" ]}},
1109 { "ffff::/64" : { "eth0" : [ "::ffff:c0a8:101" ]}},
1113 '0000:0000:0000:0000:0000:FFFF:C0A8:0101' ,
1114 { "ffff::/64" : { "eth0" : [ "::ffff:c0a8:101" ]}},
1118 def test_mon_ip ( self
, mon_ip
, list_networks
, result
, cephadm_fs
):
1119 cmd
= self
._ get
_ cmd
( '--mon-ip' , mon_ip
)
1121 with
with_cephadm_ctx ( cmd
, list_networks
= list_networks
) as ctx
:
1122 msg
= r
'--skip-mon-network'
1123 with pytest
. raises ( cd
. Error
, match
= msg
):
1124 cd
. command_bootstrap ( ctx
)
1126 with
with_cephadm_ctx ( cmd
, list_networks
= list_networks
) as ctx
:
1127 retval
= cd
. command_bootstrap ( ctx
)
1130 def test_allow_fqdn_hostname ( self
, cephadm_fs
):
1131 hostname
= 'foo.bar'
1132 cmd
= self
._ get
_ cmd
(
1133 '--mon-ip' , '192.168.1.1' ,
1134 '--skip-mon-network' ,
1137 with
with_cephadm_ctx ( cmd
, hostname
= hostname
) as ctx
:
1138 msg
= r
'--allow-fqdn-hostname'
1139 with pytest
. raises ( cd
. Error
, match
= msg
):
1140 cd
. command_bootstrap ( ctx
)
1142 cmd
+= [ '--allow-fqdn-hostname' ]
1143 with
with_cephadm_ctx ( cmd
, hostname
= hostname
) as ctx
:
1144 retval
= cd
. command_bootstrap ( ctx
)
1147 @pytest . mark
. parametrize ( 'fsid, err' ,
1150 ( '00000000-0000-0000-0000-0000deadbeef' , None ),
1151 ( '00000000-0000-0000-0000-0000deadbeez' , 'not an fsid' ),
1153 def test_fsid ( self
, fsid
, err
, cephadm_fs
):
1154 cmd
= self
._ get
_ cmd
(
1155 '--mon-ip' , '192.168.1.1' ,
1156 '--skip-mon-network' ,
1160 with
with_cephadm_ctx ( cmd
) as ctx
:
1162 with pytest
. raises ( cd
. Error
, match
= err
):
1163 cd
. command_bootstrap ( ctx
)
1165 retval
= cd
. command_bootstrap ( ctx
)