]>
git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/tests/test_host.py
0f55ce52247c117fea36972700a8daace03f60be
2 from unittest
import mock
4 from orchestrator
import HostSpec
7 from ..controllers
._version
import APIVersion
8 from ..controllers
.host
import Host
, HostUi
, get_device_osd_map
, get_hosts
, get_inventories
9 from ..tests
import ControllerTestCase
, patch_orch
10 from ..tools
import NotificationQueue
, TaskManager
13 class HostControllerTest(ControllerTestCase
):
14 URL_HOST
= '/api/host'
17 def setup_server(cls
):
18 NotificationQueue
.start_queue()
20 cls
.setup_controllers([Host
])
23 def tearDownClass(cls
):
24 NotificationQueue
.stop()
26 @mock.patch('dashboard.controllers.host.get_hosts')
27 def test_host_list_with_sources(self
, mock_get_hosts
):
48 def _get_hosts(sources
=None):
51 if sources
== 'orchestrator':
53 if sources
== 'ceph, orchestrator':
57 mock_get_hosts
.side_effect
= _get_hosts
59 self
._get
(self
.URL_HOST
, version
=APIVersion(1, 1))
60 self
.assertStatus(200)
61 self
.assertJsonBody(hosts
)
63 self
._get
('{}?sources=ceph'.format(self
.URL_HOST
), version
=APIVersion(1, 1))
64 self
.assertStatus(200)
65 self
.assertJsonBody(hosts
[0])
67 self
._get
('{}?sources=orchestrator'.format(self
.URL_HOST
), version
=APIVersion(1, 1))
68 self
.assertStatus(200)
69 self
.assertJsonBody(hosts
[1:])
71 self
._get
('{}?sources=ceph,orchestrator'.format(self
.URL_HOST
), version
=APIVersion(1, 1))
72 self
.assertStatus(200)
73 self
.assertJsonBody(hosts
)
75 @mock.patch('dashboard.controllers.host.get_hosts')
76 def test_host_list_with_facts(self
, mock_get_hosts
):
77 hosts_without_facts
= [{
94 'memory_total_kb': 1024
98 'memory_total_kb': 1024
101 hosts_with_facts
= [{
102 'hostname': 'host-0',
105 'orchestrator': False
108 'memory_total_kb': 1024
110 'hostname': 'host-1',
116 'memory_total_kb': 1024
118 # test with orchestrator available
119 with
patch_orch(True, hosts
=hosts_without_facts
) as fake_client
:
120 mock_get_hosts
.return_value
= hosts_without_facts
121 fake_client
.hosts
.get_facts
.return_value
= hosts_facts
122 # test with ?facts=true
123 self
._get
('{}?facts=true'.format(self
.URL_HOST
), version
=APIVersion(1, 1))
124 self
.assertStatus(200)
125 self
.assertHeader('Content-Type',
126 'application/vnd.ceph.api.v1.1+json')
127 self
.assertJsonBody(hosts_with_facts
)
129 # test with ?facts=false
130 self
._get
('{}?facts=false'.format(self
.URL_HOST
), version
=APIVersion(1, 1))
131 self
.assertStatus(200)
132 self
.assertHeader('Content-Type',
133 'application/vnd.ceph.api.v1.1+json')
134 self
.assertJsonBody(hosts_without_facts
)
136 # test with orchestrator available but orch backend!=cephadm
137 with
patch_orch(True, missing_features
=['get_facts']) as fake_client
:
138 mock_get_hosts
.return_value
= hosts_without_facts
139 # test with ?facts=true
140 self
._get
('{}?facts=true'.format(self
.URL_HOST
), version
=APIVersion(1, 1))
141 self
.assertStatus(400)
143 # test with no orchestrator available
144 with
patch_orch(False):
145 mock_get_hosts
.return_value
= hosts_without_facts
147 # test with ?facts=true
148 self
._get
('{}?facts=true'.format(self
.URL_HOST
), version
=APIVersion(1, 1))
149 self
.assertStatus(400)
151 # test with ?facts=false
152 self
._get
('{}?facts=false'.format(self
.URL_HOST
), version
=APIVersion(1, 1))
153 self
.assertStatus(200)
154 self
.assertHeader('Content-Type',
155 'application/vnd.ceph.api.v1.1+json')
156 self
.assertJsonBody(hosts_without_facts
)
158 def test_get_1(self
):
159 mgr
.list_servers
.return_value
= []
161 with
patch_orch(False):
162 self
._get
('{}/node1'.format(self
.URL_HOST
))
163 self
.assertStatus(404)
165 def test_get_2(self
):
166 mgr
.list_servers
.return_value
= [{'hostname': 'node1'}]
168 with
patch_orch(False):
169 self
._get
('{}/node1'.format(self
.URL_HOST
))
170 self
.assertStatus(200)
171 self
.assertIn('labels', self
.json_body())
172 self
.assertIn('status', self
.json_body())
173 self
.assertIn('addr', self
.json_body())
175 def test_get_3(self
):
176 mgr
.list_servers
.return_value
= []
178 with
patch_orch(True, hosts
=[HostSpec('node1')]):
179 self
._get
('{}/node1'.format(self
.URL_HOST
))
180 self
.assertStatus(200)
181 self
.assertIn('labels', self
.json_body())
182 self
.assertIn('status', self
.json_body())
183 self
.assertIn('addr', self
.json_body())
185 @mock.patch('dashboard.controllers.host.add_host')
186 def test_add_host(self
, mock_add_host
):
187 with
patch_orch(True):
192 'status': 'maintenance'
194 self
._post
(self
.URL_HOST
, payload
, version
=APIVersion(0, 1))
195 self
.assertStatus(201)
196 mock_add_host
.assert_called()
198 def test_set_labels(self
):
199 mgr
.list_servers
.return_value
= []
201 HostSpec('node0', labels
=['aaa', 'bbb'])
203 with
patch_orch(True, hosts
=orch_hosts
) as fake_client
:
204 fake_client
.hosts
.remove_label
= mock
.Mock()
205 fake_client
.hosts
.add_label
= mock
.Mock()
207 payload
= {'update_labels': True, 'labels': ['bbb', 'ccc']}
208 self
._put
('{}/node0'.format(self
.URL_HOST
), payload
, version
=APIVersion(0, 1))
209 self
.assertStatus(200)
210 self
.assertHeader('Content-Type',
211 'application/vnd.ceph.api.v0.1+json')
212 fake_client
.hosts
.remove_label
.assert_called_once_with('node0', 'aaa')
213 fake_client
.hosts
.add_label
.assert_called_once_with('node0', 'ccc')
215 # return 400 if type other than List[str]
216 self
._put
('{}/node0'.format(self
.URL_HOST
),
217 {'update_labels': True, 'labels': 'ddd'},
218 version
=APIVersion(0, 1))
219 self
.assertStatus(400)
221 def test_host_maintenance(self
):
222 mgr
.list_servers
.return_value
= []
227 with
patch_orch(True, hosts
=orch_hosts
):
228 # enter maintenance mode
229 self
._put
('{}/node0'.format(self
.URL_HOST
), {'maintenance': True},
230 version
=APIVersion(0, 1))
231 self
.assertStatus(200)
232 self
.assertHeader('Content-Type',
233 'application/vnd.ceph.api.v0.1+json')
235 # force enter maintenance mode
236 self
._put
('{}/node1'.format(self
.URL_HOST
), {'maintenance': True, 'force': True},
237 version
=APIVersion(0, 1))
238 self
.assertStatus(200)
240 # exit maintenance mode
241 self
._put
('{}/node0'.format(self
.URL_HOST
), {'maintenance': True},
242 version
=APIVersion(0, 1))
243 self
.assertStatus(200)
244 self
._put
('{}/node1'.format(self
.URL_HOST
), {'maintenance': True},
245 version
=APIVersion(0, 1))
246 self
.assertStatus(200)
248 # maintenance without orchestrator service
249 with
patch_orch(False):
250 self
._put
('{}/node0'.format(self
.URL_HOST
), {'maintenance': True},
251 version
=APIVersion(0, 1))
252 self
.assertStatus(503)
254 @mock.patch('dashboard.controllers.host.time')
255 def test_identify_device(self
, mock_time
):
256 url
= '{}/host-0/identify_device'.format(self
.URL_HOST
)
257 with
patch_orch(True) as fake_client
:
259 'device': '/dev/sdz',
262 self
._task
_post
(url
, payload
)
263 self
.assertStatus(200)
264 mock_time
.sleep
.assert_called()
266 mock
.call('host-0', '/dev/sdz', 'ident', True),
267 mock
.call('host-0', '/dev/sdz', 'ident', False),
269 fake_client
.blink_device_light
.assert_has_calls(calls
)
271 @mock.patch('dashboard.controllers.host.get_inventories')
272 def test_inventory(self
, mock_get_inventories
):
273 inventory_url
= '{}/host-0/inventory'.format(self
.URL_HOST
)
274 with
patch_orch(True):
277 'url': inventory_url
,
278 'inventories': [{'a': 'b'}],
283 'url': '{}?refresh=true'.format(inventory_url
),
284 'inventories': [{'a': 'b'}],
289 'url': inventory_url
,
296 mock_get_inventories
.reset_mock()
297 mock_get_inventories
.return_value
= test
['inventories']
298 self
._get
(test
['url'])
299 mock_get_inventories
.assert_called_once_with(['host-0'], test
['refresh'])
300 self
.assertEqual(self
.json_body(), test
['resp'])
301 self
.assertStatus(200)
303 # list without orchestrator service
304 with
patch_orch(False):
305 self
._get
(inventory_url
)
306 self
.assertStatus(503)
308 def test_host_drain(self
):
309 mgr
.list_servers
.return_value
= []
313 with
patch_orch(True, hosts
=orch_hosts
):
314 self
._put
('{}/node0'.format(self
.URL_HOST
), {'drain': True},
315 version
=APIVersion(0, 1))
316 self
.assertStatus(200)
317 self
.assertHeader('Content-Type',
318 'application/vnd.ceph.api.v0.1+json')
320 # maintenance without orchestrator service
321 with
patch_orch(False):
322 self
._put
('{}/node0'.format(self
.URL_HOST
), {'drain': True},
323 version
=APIVersion(0, 1))
324 self
.assertStatus(503)
327 class HostUiControllerTest(ControllerTestCase
):
328 URL_HOST
= '/ui-api/host'
331 def setup_server(cls
):
332 cls
.setup_controllers([HostUi
])
334 def test_labels(self
):
336 HostSpec('node1', labels
=['foo']),
337 HostSpec('node2', labels
=['foo', 'bar'])
340 with
patch_orch(True, hosts
=orch_hosts
):
341 self
._get
('{}/labels'.format(self
.URL_HOST
))
342 self
.assertStatus(200)
343 labels
= self
.json_body()
345 self
.assertListEqual(labels
, ['bar', 'foo'])
347 @mock.patch('dashboard.controllers.host.get_inventories')
348 def test_inventory(self
, mock_get_inventories
):
349 inventory_url
= '{}/inventory'.format(self
.URL_HOST
)
350 with
patch_orch(True):
353 'url': inventory_url
,
357 'url': '{}?refresh=true'.format(inventory_url
),
362 mock_get_inventories
.reset_mock()
363 mock_get_inventories
.return_value
= [{'a': 'b'}]
364 self
._get
(test
['url'])
365 mock_get_inventories
.assert_called_once_with(None, test
['refresh'])
366 self
.assertEqual(self
.json_body(), [{'a': 'b'}])
367 self
.assertStatus(200)
369 # list without orchestrator service
370 with
patch_orch(False):
371 self
._get
(inventory_url
)
372 self
.assertStatus(503)
375 class TestHosts(unittest
.TestCase
):
376 def test_get_hosts(self
):
377 mgr
.list_servers
.return_value
= [{
380 'hostname': 'localhost'
383 HostSpec('node1', labels
=['foo', 'bar']),
384 HostSpec('node2', labels
=['bar'])
387 with
patch_orch(True, hosts
=orch_hosts
):
389 self
.assertEqual(len(hosts
), 3)
394 'orchestrator': False
403 'labels': ['bar', 'foo']
414 hostname
= host
['hostname']
415 self
.assertDictEqual(host
['sources'], checks
[hostname
]['sources'])
416 self
.assertListEqual(host
['labels'], checks
[hostname
]['labels'])
418 @mock.patch('dashboard.controllers.host.mgr.get')
419 def test_get_device_osd_map(self
, mgr_get
):
420 mgr_get
.side_effect
= lambda key
: {
424 'devices': 'nvme0n1,sdb',
428 'devices': 'nvme0n1,sdc',
441 device_osd_map
= get_device_osd_map()
442 mgr
.get
.assert_called_with('osd_metadata')
443 # sort OSD IDs to make assertDictEqual work
444 for devices
in device_osd_map
.values():
445 for host
in devices
.keys():
446 devices
[host
] = sorted(devices
[host
])
447 self
.assertDictEqual(device_osd_map
, {
458 @mock.patch('dashboard.controllers.host.str_to_bool')
459 @mock.patch('dashboard.controllers.host.get_device_osd_map')
460 def test_get_inventories(self
, mock_get_device_osd_map
, mock_str_to_bool
):
461 mock_get_device_osd_map
.return_value
= {
477 {'path': '/dev/sdb'},
478 {'path': '/dev/sdc'},
485 {'path': '/dev/sda'},
491 with
patch_orch(True, inventory
=inventory
) as orch_client
:
492 mock_str_to_bool
.return_value
= True
494 hosts
= ['host-0', 'host-1']
495 inventories
= get_inventories(hosts
, 'true')
496 mock_str_to_bool
.assert_called_with('true')
497 orch_client
.inventory
.list.assert_called_once_with(hosts
=hosts
, refresh
=True)
498 self
.assertEqual(len(inventories
), 2)
499 host0
= inventories
[0]
500 self
.assertEqual(host0
['name'], 'host-0')
501 self
.assertEqual(host0
['addr'], '1.2.3.4')
502 self
.assertEqual(host0
['devices'][0]['osd_ids'], [1, 2])
503 self
.assertEqual(host0
['devices'][1]['osd_ids'], [1])
504 self
.assertEqual(host0
['devices'][2]['osd_ids'], [2])
505 host1
= inventories
[1]
506 self
.assertEqual(host1
['name'], 'host-1')
507 self
.assertEqual(host1
['addr'], '1.2.3.5')
508 self
.assertEqual(host1
['devices'][0]['osd_ids'], [])
509 self
.assertEqual(host1
['devices'][1]['osd_ids'], [3])