]>
git.proxmox.com Git - ceph.git/blob - ceph/src/cephadm/tests/test_cephadm.py
10 with
patch ( 'builtins.open' , create
= True ):
11 from importlib
. machinery
import SourceFileLoader
12 cd
= SourceFileLoader ( 'cephadm' , 'cephadm' ). load_module ()
14 class TestCephAdm ( object ):
16 def test_docker_unit_file ( self
):
18 cd
. container_path
= '/usr/bin/docker'
19 r
= cd
. get_unit_file ( '9b9d7609-f4d5-4aba-94c8-effa764d96c9' )
20 assert 'Requires=docker.service' in r
21 cd
. container_path
= '/usr/sbin/podman'
22 r
= cd
. get_unit_file ( '9b9d7609-f4d5-4aba-94c8-effa764d96c9' )
23 assert 'Requires=docker.service' not in r
25 def test_is_not_fsid ( self
):
26 assert not cd
. is_fsid ( 'no-uuid' )
28 def test_is_fsid ( self
):
29 assert cd
. is_fsid ( 'e863154d-33c7-4350-bca5-921e0467e55b' )
31 def test__get_parser_image ( self
):
32 args
= cd
._ parse
_ args
([ '--image' , 'foo' , 'version' ])
33 assert args
. image
== 'foo'
35 def test_CustomValidation ( self
):
36 assert cd
._ parse
_ args
([ 'deploy' , '--name' , 'mon.a' , '--fsid' , 'fsid' ])
38 with pytest
. raises ( SystemExit ):
39 cd
._ parse
_ args
([ 'deploy' , '--name' , 'wrong' , '--fsid' , 'fsid' ])
41 @pytest . mark
. parametrize ( "test_input, expected" , [
42 ( "podman version 1.6.2" , ( 1 , 6 , 2 )),
43 ( "podman version 1.6.2-stable2" , ( 1 , 6 , 2 )),
45 def test_parse_podman_version ( self
, test_input
, expected
):
46 assert cd
._ parse
_ podman
_ version
( test_input
) == expected
48 def test_parse_podman_version_invalid ( self
):
49 with pytest
. raises ( ValueError ) as res
:
50 cd
._ parse
_ podman
_ version
( 'podman version inval.id' )
51 assert 'inval' in str ( res
. value
)
53 @pytest . mark
. parametrize ( "test_input, expected" , [
56 default via 192.168.178.1 dev enxd89ef3f34260 proto dhcp metric 100
57 10.0.0.0/8 via 10.4.0.1 dev tun0 proto static metric 50
58 10.3.0.0/21 via 10.4.0.1 dev tun0 proto static metric 50
59 10.4.0.1 dev tun0 proto kernel scope link src 10.4.0.2 metric 50
60 137.1.0.0/16 via 10.4.0.1 dev tun0 proto static metric 50
61 138.1.0.0/16 via 10.4.0.1 dev tun0 proto static metric 50
62 139.1.0.0/16 via 10.4.0.1 dev tun0 proto static metric 50
63 140.1.0.0/17 via 10.4.0.1 dev tun0 proto static metric 50
64 141.1.0.0/16 via 10.4.0.1 dev tun0 proto static metric 50
65 169.254.0.0/16 dev docker0 scope link metric 1000
66 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
67 192.168.39.0/24 dev virbr1 proto kernel scope link src 192.168.39.1 linkdown
68 192.168.122.0/24 dev virbr0 proto kernel scope link src 192.168.122.1 linkdown
69 192.168.178.0/24 dev enxd89ef3f34260 proto kernel scope link src 192.168.178.28 metric 100
70 192.168.178.1 dev enxd89ef3f34260 proto static scope link metric 100
71 195.135.221.12 via 192.168.178.1 dev enxd89ef3f34260 proto static metric 100
74 '10.4.0.1' : [ '10.4.0.2' ],
75 '172.17.0.0/16' : [ '172.17.0.1' ],
76 '192.168.39.0/24' : [ '192.168.39.1' ],
77 '192.168.122.0/24' : [ '192.168.122.1' ],
78 '192.168.178.0/24' : [ '192.168.178.28' ]
82 default via 10.3.64.1 dev eno1 proto static metric 100
83 10.3.64.0/24 dev eno1 proto kernel scope link src 10.3.64.23 metric 100
84 10.3.64.0/24 dev eno1 proto kernel scope link src 10.3.64.27 metric 100
85 10.88.0.0/16 dev cni-podman0 proto kernel scope link src 10.88.0.1 linkdown
86 172.21.0.0/20 via 172.21.3.189 dev tun0
87 172.21.1.0/20 via 172.21.3.189 dev tun0
88 172.21.2.1 via 172.21.3.189 dev tun0
89 172.21.3.1 dev tun0 proto kernel scope link src 172.21.3.2
90 172.21.4.0/24 via 172.21.3.1 dev tun0
91 172.21.5.0/24 via 172.21.3.1 dev tun0
92 172.21.6.0/24 via 172.21.3.1 dev tun0
93 172.21.7.0/24 via 172.21.3.1 dev tun0
94 192.168.122.0/24 dev virbr0 proto kernel scope link src 192.168.122.1 linkdown
97 '10.3.64.0/24' : [ '10.3.64.23' , '10.3.64.27' ],
98 '10.88.0.0/16' : [ '10.88.0.1' ],
99 '172.21.3.1' : [ '172.21.3.2' ],
100 '192.168.122.0/24' : [ '192.168.122.1' ]}
103 def test_parse_ipv4_route ( self
, test_input
, expected
):
104 assert cd
._ parse
_ ipv
4_ route
( test_input
) == expected
106 @pytest . mark
. parametrize ( "test_routes, test_ips, expected" , [
109 ::1 dev lo proto kernel metric 256 pref medium
110 fdbc:7574:21fe:9200::/64 dev wlp2s0 proto ra metric 600 pref medium
111 fdd8:591e:4969:6363::/64 dev wlp2s0 proto ra metric 600 pref medium
112 fde4:8dba:82e1::/64 dev eth1 proto kernel metric 256 expires 1844sec pref medium
113 fe80::/64 dev tun0 proto kernel metric 256 pref medium
114 fe80::/64 dev wlp2s0 proto kernel metric 600 pref medium
115 default dev tun0 proto static metric 50 pref medium
116 default via fe80::2480:28ec:5097:3fe2 dev wlp2s0 proto ra metric 20600 pref medium
119 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 state UNKNOWN qlen 1000
120 inet6 ::1/128 scope host
121 valid_lft forever preferred_lft forever
122 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000
123 inet6 fdd8:591e:4969:6363:4c52:cafe:8dd4:dc4/64 scope global temporary dynamic
124 valid_lft 86394sec preferred_lft 14394sec
125 inet6 fdbc:7574:21fe:9200:4c52:cafe:8dd4:dc4/64 scope global temporary dynamic
126 valid_lft 6745sec preferred_lft 3145sec
127 inet6 fdd8:591e:4969:6363:103a:abcd:af1f:57f3/64 scope global temporary deprecated dynamic
128 valid_lft 86394sec preferred_lft 0sec
129 inet6 fdbc:7574:21fe:9200:103a:abcd:af1f:57f3/64 scope global temporary deprecated dynamic
130 valid_lft 6745sec preferred_lft 0sec
131 inet6 fdd8:591e:4969:6363:a128:1234:2bdd:1b6f/64 scope global temporary deprecated dynamic
132 valid_lft 86394sec preferred_lft 0sec
133 inet6 fdbc:7574:21fe:9200:a128:1234:2bdd:1b6f/64 scope global temporary deprecated dynamic
134 valid_lft 6745sec preferred_lft 0sec
135 inet6 fdd8:591e:4969:6363:d581:4321:380b:3905/64 scope global temporary deprecated dynamic
136 valid_lft 86394sec preferred_lft 0sec
137 inet6 fdbc:7574:21fe:9200:d581:4321:380b:3905/64 scope global temporary deprecated dynamic
138 valid_lft 6745sec preferred_lft 0sec
139 inet6 fe80::1111:2222:3333:4444/64 scope link noprefixroute
140 valid_lft forever preferred_lft forever
141 inet6 fde4:8dba:82e1:0:ec4a:e402:e9df:b357/64 scope global temporary dynamic
142 valid_lft 1074sec preferred_lft 1074sec
143 inet6 fde4:8dba:82e1:0:5054:ff:fe72:61af/64 scope global dynamic mngtmpaddr
144 valid_lft 1074sec preferred_lft 1074sec
145 12: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 state UNKNOWN qlen 100
146 inet6 fe80::cafe:cafe:cafe:cafe/64 scope link stable-privacy
147 valid_lft forever preferred_lft forever
151 "fdbc:7574:21fe:9200::/64" : [ "fdbc:7574:21fe:9200:4c52:cafe:8dd4:dc4" ,
152 "fdbc:7574:21fe:9200:103a:abcd:af1f:57f3" ,
153 "fdbc:7574:21fe:9200:a128:1234:2bdd:1b6f" ,
154 "fdbc:7574:21fe:9200:d581:4321:380b:3905" ],
155 "fdd8:591e:4969:6363::/64" : [ "fdd8:591e:4969:6363:4c52:cafe:8dd4:dc4" ,
156 "fdd8:591e:4969:6363:103a:abcd:af1f:57f3" ,
157 "fdd8:591e:4969:6363:a128:1234:2bdd:1b6f" ,
158 "fdd8:591e:4969:6363:d581:4321:380b:3905" ],
159 "fde4:8dba:82e1::/64" : [ "fde4:8dba:82e1:0:ec4a:e402:e9df:b357" ,
160 "fde4:8dba:82e1:0:5054:ff:fe72:61af" ],
161 "fe80::/64" : [ "fe80::1111:2222:3333:4444" ,
162 "fe80::cafe:cafe:cafe:cafe" ]
165 def test_parse_ipv6_route ( self
, test_routes
, test_ips
, expected
):
166 assert cd
._ parse
_ ipv
6_ route
( test_routes
, test_ips
) == expected
168 def test_is_ipv6 ( self
):
169 cd
. logger
= mock
. Mock ()
170 for good
in ( "[::1]" , "::1" ,
171 "fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" ):
172 assert cd
. is_ipv6 ( good
)
173 for bad
in ( "127.0.0.1" ,
174 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffg" ,
175 "1:2:3:4:5:6:7:8:9" , "fd00::1::1" , "[fg::1]" ):
176 assert not cd
. is_ipv6 ( bad
)
178 def test_unwrap_ipv6 ( self
):
179 def unwrap_test ( address
, expected
):
180 assert cd
. unwrap_ipv6 ( address
) == expected
183 ( '::1' , '::1' ), ( '[::1]' , '::1' ),
184 ( '[fde4:8dba:82e1:0:5054:ff:fe6a:357]' , 'fde4:8dba:82e1:0:5054:ff:fe6a:357' ),
185 ( 'can actually be any string' , 'can actually be any string' ),
186 ( '[but needs to be stripped] ' , '[but needs to be stripped] ' )]
187 for address
, expected
in tests
:
188 unwrap_test ( address
, expected
)
190 def test_wrap_ipv6 ( self
):
191 def wrap_test ( address
, expected
):
192 assert cd
. wrap_ipv6 ( address
) == expected
195 ( '::1' , '[::1]' ), ( '[::1]' , '[::1]' ),
196 ( 'fde4:8dba:82e1:0:5054:ff:fe6a:357' ,
197 '[fde4:8dba:82e1:0:5054:ff:fe6a:357]' ),
198 ( 'myhost.example.com' , 'myhost.example.com' ),
199 ( '192.168.0.1' , '192.168.0.1' ),
200 ( '' , '' ), ( 'fd00::1::1' , 'fd00::1::1' )]
201 for address
, expected
in tests
:
202 wrap_test ( address
, expected
)
204 @mock . patch ( 'cephadm.call_throws' )
205 @mock . patch ( 'cephadm.get_parm' )
206 def test_registry_login ( self
, get_parm
, call_throws
):
208 # test normal valid login with url, username and password specified
209 call_throws
. return_value
= '' , '' , 0
210 args
= cd
._ parse
_ args
([ 'registry-login' , '--registry-url' , 'sample-url' , '--registry-username' , 'sample-user' , '--registry-password' , 'sample-pass' ])
212 retval
= cd
. command_registry_login ()
215 # test bad login attempt with invalid arguments given
216 args
= cd
._ parse
_ args
([ 'registry-login' , '--registry-url' , 'bad-args-url' ])
218 with pytest
. raises ( Exception ) as e
:
219 assert cd
. command_registry_login ()
220 assert str ( e
. value
) == ( 'Invalid custom registry arguments received. To login to a custom registry include '
221 '--registry-url, --registry-username and --registry-password options or --registry-json option' )
223 # test normal valid login with json file
224 get_parm
. return_value
= { "url" : "sample-url" , "username" : "sample-username" , "password" : "sample-password" }
225 args
= cd
._ parse
_ args
([ 'registry-login' , '--registry-json' , 'sample-json' ])
227 retval
= cd
. command_registry_login ()
230 # test bad login attempt with bad json file
231 get_parm
. return_value
= { "bad-json" : "bad-json" }
232 args
= cd
._ parse
_ args
([ 'registry-login' , '--registry-json' , 'sample-json' ])
234 with pytest
. raises ( Exception ) as e
:
235 assert cd
. command_registry_login ()
236 assert str ( e
. value
) == ( "json provided for custom registry login did not include all necessary fields. "
237 "Please setup json file as \n "
239 " \" url \" : \" REGISTRY_URL \" , \n "
240 " \" username \" : \" REGISTRY_USERNAME \" , \n "
241 " \" password \" : \" REGISTRY_PASSWORD \"\n "
244 # test login attempt with valid arguments where login command fails
245 call_throws
. side_effect
= Exception
246 args
= cd
._ parse
_ args
([ 'registry-login' , '--registry-url' , 'sample-url' , '--registry-username' , 'sample-user' , '--registry-password' , 'sample-pass' ])
248 with pytest
. raises ( Exception ) as e
:
249 cd
. command_registry_login ()
250 assert str ( e
. value
) == "Failed to login to custom registry @ sample-url as sample-user with given password"
252 def test_get_image_info_from_inspect ( self
):
254 out
= """204a01f9b0b6710dd0c0af7f37ce7139c47ff0f0105d778d7104c69282dfbbf1,[docker.io/ceph/ceph@sha256:1cc9b824e1b076cdff52a9aa3f0cc8557d879fb2fbbba0cafed970aca59a3992]"""
255 r
= cd
. get_image_info_from_inspect ( out
, 'registry/ceph/ceph:latest' )
257 'image_id' : '204a01f9b0b6710dd0c0af7f37ce7139c47ff0f0105d778d7104c69282dfbbf1' ,
258 'repo_digest' : 'docker.io/ceph/ceph@sha256:1cc9b824e1b076cdff52a9aa3f0cc8557d879fb2fbbba0cafed970aca59a3992'
262 out
= """sha256:16f4549cf7a8f112bbebf7946749e961fbbd1b0838627fe619aab16bc17ce552,[quay.ceph.io/ceph-ci/ceph@sha256:4e13da36c1bd6780b312a985410ae678984c37e6a9493a74c87e4a50b9bda41f]"""
263 r
= cd
. get_image_info_from_inspect ( out
, 'registry/ceph/ceph:latest' )
265 'image_id' : '16f4549cf7a8f112bbebf7946749e961fbbd1b0838627fe619aab16bc17ce552' ,
266 'repo_digest' : 'quay.ceph.io/ceph-ci/ceph@sha256:4e13da36c1bd6780b312a985410ae678984c37e6a9493a74c87e4a50b9bda41f'
269 def test_dict_get ( self
):
270 result
= cd
. dict_get ({ 'a' : 1 }, 'a' , require
= True )
272 result
= cd
. dict_get ({ 'a' : 1 }, 'b' )
273 assert result
is None
274 result
= cd
. dict_get ({ 'a' : 1 }, 'b' , default
= 2 )
277 def test_dict_get_error ( self
):
278 with pytest
. raises ( cd
. Error
):
279 cd
. dict_get ({ 'a' : 1 }, 'b' , require
= True )
281 def test_dict_get_join ( self
):
282 result
= cd
. dict_get_join ({ 'foo' : [ 'a' , 'b' ]}, 'foo' )
283 assert result
== 'a \n b'
284 result
= cd
. dict_get_join ({ 'foo' : [ 1 , 2 ]}, 'foo' )
285 assert result
== '1 \n 2'
286 result
= cd
. dict_get_join ({ 'bar' : 'a' }, 'bar' )
288 result
= cd
. dict_get_join ({ 'a' : 1 }, 'a' )
291 def test_last_local_images ( self
):
293 docker.io/ceph/daemon-base@
294 docker.io/ceph/ceph:v15.2.5
295 docker.io/ceph/daemon-base:octopus
297 image
= cd
._ filter
_l ast
_l ocal
_ ceph
_ image
( out
)
298 assert image
== 'docker.io/ceph/ceph:v15.2.5'
301 class TestCustomContainer ( unittest
. TestCase
):
302 cc
: cd
. CustomContainer
305 self
. cc
= cd
. CustomContainer (
306 'e863154d-33c7-4350-bca5-921e0467e55b' ,
309 'entrypoint' : 'bash' ,
315 'envs' : [ 'SECRET=password' ],
316 'ports' : [ 8080 , 8443 ],
318 '/CONFIG_DIR' : '/foo/conf' ,
319 'bar/config' : '/bar:ro'
324 'source=/CONFIG_DIR' ,
325 'destination=/foo/conf' ,
331 'destination=/bar:ro' ,
336 image
= 'docker.io/library/hello-world:latest'
339 def test_entrypoint ( self
):
340 self
. assertEqual ( self
. cc
. entrypoint
, 'bash' )
342 def test_uid_gid ( self
):
343 self
. assertEqual ( self
. cc
. uid
, 65534 )
344 self
. assertEqual ( self
. cc
. gid
, 1000 )
346 def test_ports ( self
):
347 self
. assertEqual ( self
. cc
. ports
, [ 8080 , 8443 ])
349 def test_get_container_args ( self
):
350 result
= self
. cc
. get_container_args ()
351 self
. assertEqual ( result
, [
356 def test_get_container_envs ( self
):
357 result
= self
. cc
. get_container_envs ()
358 self
. assertEqual ( result
, [ 'SECRET=password' ])
360 def test_get_container_mounts ( self
):
361 result
= self
. cc
. get_container_mounts ( '/xyz' )
362 self
. assertDictEqual ( result
, {
363 '/CONFIG_DIR' : '/foo/conf' ,
364 '/xyz/bar/config' : '/bar:ro'
367 def test_get_container_binds ( self
):
368 result
= self
. cc
. get_container_binds ( '/xyz' )
369 self
. assertEqual ( result
, [
372 'source=/CONFIG_DIR' ,
373 'destination=/foo/conf' ,
378 'source=/xyz/bar/config' ,
379 'destination=/bar:ro' ,
385 class TestMonitoring ( object ):
386 @mock . patch ( 'cephadm.call' )
387 def test_get_version_alertmanager ( self
, _call
):
389 daemon_type
= 'alertmanager'
391 # binary `prometheus`
392 _call
. return_value
= '' , '{}, version 0.16.1' . format ( daemon_type
), 0
393 version
= cd
. Monitoring
. get_version ( ctx
, 'container_id' , daemon_type
)
394 assert version
== '0.16.1'
396 # binary `prometheus-alertmanager`
397 _call
. side_effect
= (
399 ( '' , '{}, version 0.16.1' . format ( daemon_type
), 0 ),
401 version
= cd
. Monitoring
. get_version ( ctx
, 'container_id' , daemon_type
)
402 assert version
== '0.16.1'
404 @mock . patch ( 'cephadm.call' )
405 def test_get_version_prometheus ( self
, _call
):
407 daemon_type
= 'prometheus'
408 _call
. return_value
= '' , '{}, version 0.16.1' . format ( daemon_type
), 0
409 version
= cd
. Monitoring
. get_version ( ctx
, 'container_id' , daemon_type
)
410 assert version
== '0.16.1'
412 @mock . patch ( 'cephadm.call' )
413 def test_get_version_node_exporter ( self
, _call
):
415 daemon_type
= 'node-exporter'
416 _call
. return_value
= '' , '{}, version 0.16.1' . format ( daemon_type
. replace ( '-' , '_' )), 0
417 version
= cd
. Monitoring
. get_version ( ctx
, 'container_id' , daemon_type
)
418 assert version
== '0.16.1'