]>
Commit | Line | Data |
---|---|---|
9f95a23c | 1 | import unittest |
f67539c2 | 2 | from unittest import mock |
9f95a23c | 3 | |
39ae355f | 4 | from orchestrator import DaemonDescription, HostSpec |
9f95a23c | 5 | |
9f95a23c | 6 | from .. import mgr |
a4b75251 | 7 | from ..controllers._version import APIVersion |
f67539c2 | 8 | from ..controllers.host import Host, HostUi, get_device_osd_map, get_hosts, get_inventories |
20effc67 | 9 | from ..tests import ControllerTestCase, patch_orch |
f67539c2 | 10 | from ..tools import NotificationQueue, TaskManager |
f67539c2 TL |
11 | |
12 | ||
9f95a23c TL |
13 | class HostControllerTest(ControllerTestCase): |
14 | URL_HOST = '/api/host' | |
15 | ||
16 | @classmethod | |
17 | def setup_server(cls): | |
f67539c2 TL |
18 | NotificationQueue.start_queue() |
19 | TaskManager.init() | |
9f95a23c TL |
20 | cls.setup_controllers([Host]) |
21 | ||
f67539c2 TL |
22 | @classmethod |
23 | def tearDownClass(cls): | |
24 | NotificationQueue.stop() | |
25 | ||
9f95a23c | 26 | @mock.patch('dashboard.controllers.host.get_hosts') |
a4b75251 | 27 | def test_host_list_with_sources(self, mock_get_hosts): |
f6b5b4d7 TL |
28 | hosts = [{ |
29 | 'hostname': 'host-0', | |
30 | 'sources': { | |
31 | 'ceph': True, | |
32 | 'orchestrator': False | |
9f95a23c | 33 | } |
f6b5b4d7 TL |
34 | }, { |
35 | 'hostname': 'host-1', | |
36 | 'sources': { | |
37 | 'ceph': False, | |
38 | 'orchestrator': True | |
39 | } | |
40 | }, { | |
41 | 'hostname': 'host-2', | |
42 | 'sources': { | |
43 | 'ceph': True, | |
44 | 'orchestrator': True | |
45 | } | |
46 | }] | |
9f95a23c | 47 | |
a4b75251 TL |
48 | def _get_hosts(sources=None): |
49 | if sources == 'ceph': | |
aee94f69 | 50 | return [hosts[0]] |
a4b75251 TL |
51 | if sources == 'orchestrator': |
52 | return hosts[1:] | |
53 | if sources == 'ceph, orchestrator': | |
aee94f69 | 54 | return [hosts[2]] |
a4b75251 | 55 | return hosts |
f6b5b4d7 | 56 | |
aee94f69 TL |
57 | with patch_orch(True, hosts=hosts): |
58 | 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) | |
9f95a23c | 62 | |
aee94f69 TL |
63 | self._get('{}?sources=ceph'.format(self.URL_HOST), version=APIVersion(1, 1)) |
64 | self.assertStatus(200) | |
65 | self.assertJsonBody([hosts[0]]) | |
9f95a23c | 66 | |
aee94f69 TL |
67 | self._get('{}?sources=orchestrator'.format(self.URL_HOST), version=APIVersion(1, 1)) |
68 | self.assertStatus(200) | |
69 | self.assertJsonBody(hosts[1:]) | |
9f95a23c | 70 | |
aee94f69 TL |
71 | self._get('{}?sources=ceph,orchestrator'.format(self.URL_HOST), |
72 | version=APIVersion(1, 1)) | |
73 | self.assertStatus(200) | |
74 | self.assertJsonBody(hosts) | |
9f95a23c | 75 | |
a4b75251 TL |
76 | @mock.patch('dashboard.controllers.host.get_hosts') |
77 | def test_host_list_with_facts(self, mock_get_hosts): | |
78 | hosts_without_facts = [{ | |
79 | 'hostname': 'host-0', | |
80 | 'sources': { | |
81 | 'ceph': True, | |
82 | 'orchestrator': False | |
83 | } | |
84 | }, { | |
85 | 'hostname': 'host-1', | |
86 | 'sources': { | |
87 | 'ceph': False, | |
88 | 'orchestrator': True | |
89 | } | |
90 | }] | |
91 | ||
92 | hosts_facts = [{ | |
93 | 'hostname': 'host-0', | |
94 | 'cpu_count': 1, | |
95 | 'memory_total_kb': 1024 | |
96 | }, { | |
97 | 'hostname': 'host-1', | |
98 | 'cpu_count': 2, | |
99 | 'memory_total_kb': 1024 | |
100 | }] | |
101 | ||
102 | hosts_with_facts = [{ | |
103 | 'hostname': 'host-0', | |
104 | 'sources': { | |
105 | 'ceph': True, | |
106 | 'orchestrator': False | |
107 | }, | |
108 | 'cpu_count': 1, | |
aee94f69 TL |
109 | 'memory_total_kb': 1024, |
110 | 'services': [], | |
111 | 'service_instances': [{'type': 'mon', 'count': 1}] | |
a4b75251 TL |
112 | }, { |
113 | 'hostname': 'host-1', | |
114 | 'sources': { | |
115 | 'ceph': False, | |
116 | 'orchestrator': True | |
117 | }, | |
118 | 'cpu_count': 2, | |
aee94f69 TL |
119 | 'memory_total_kb': 1024, |
120 | 'services': [], | |
121 | 'service_instances': [{'type': 'mon', 'count': 1}] | |
a4b75251 TL |
122 | }] |
123 | # test with orchestrator available | |
124 | with patch_orch(True, hosts=hosts_without_facts) as fake_client: | |
125 | mock_get_hosts.return_value = hosts_without_facts | |
aee94f69 TL |
126 | |
127 | def get_facts_mock(hostname: str): | |
128 | if hostname == 'host-0': | |
129 | return [hosts_facts[0]] | |
130 | return [hosts_facts[1]] | |
131 | fake_client.hosts.get_facts.side_effect = get_facts_mock | |
a4b75251 | 132 | # test with ?facts=true |
aee94f69 | 133 | self._get('{}?facts=true'.format(self.URL_HOST), version=APIVersion(1, 3)) |
a4b75251 TL |
134 | self.assertStatus(200) |
135 | self.assertHeader('Content-Type', | |
aee94f69 | 136 | APIVersion(1, 3).to_mime_type()) |
a4b75251 TL |
137 | self.assertJsonBody(hosts_with_facts) |
138 | ||
139 | # test with ?facts=false | |
aee94f69 | 140 | self._get('{}?facts=false'.format(self.URL_HOST), version=APIVersion(1, 3)) |
a4b75251 TL |
141 | self.assertStatus(200) |
142 | self.assertHeader('Content-Type', | |
aee94f69 | 143 | APIVersion(1, 3).to_mime_type()) |
a4b75251 TL |
144 | self.assertJsonBody(hosts_without_facts) |
145 | ||
146 | # test with orchestrator available but orch backend!=cephadm | |
147 | with patch_orch(True, missing_features=['get_facts']) as fake_client: | |
148 | mock_get_hosts.return_value = hosts_without_facts | |
149 | # test with ?facts=true | |
aee94f69 | 150 | self._get('{}?facts=true'.format(self.URL_HOST), version=APIVersion(1, 3)) |
a4b75251 TL |
151 | self.assertStatus(400) |
152 | ||
153 | # test with no orchestrator available | |
154 | with patch_orch(False): | |
155 | mock_get_hosts.return_value = hosts_without_facts | |
156 | ||
157 | # test with ?facts=true | |
aee94f69 | 158 | self._get('{}?facts=true'.format(self.URL_HOST), version=APIVersion(1, 3)) |
a4b75251 TL |
159 | self.assertStatus(400) |
160 | ||
161 | # test with ?facts=false | |
aee94f69 | 162 | self._get('{}?facts=false'.format(self.URL_HOST), version=APIVersion(1, 3)) |
a4b75251 TL |
163 | self.assertStatus(200) |
164 | self.assertHeader('Content-Type', | |
aee94f69 | 165 | APIVersion(1, 3).to_mime_type()) |
a4b75251 TL |
166 | self.assertJsonBody(hosts_without_facts) |
167 | ||
f67539c2 | 168 | def test_get_1(self): |
f6b5b4d7 | 169 | mgr.list_servers.return_value = [] |
9f95a23c | 170 | |
f67539c2 TL |
171 | with patch_orch(False): |
172 | self._get('{}/node1'.format(self.URL_HOST)) | |
173 | self.assertStatus(404) | |
f6b5b4d7 | 174 | |
f67539c2 | 175 | def test_get_2(self): |
39ae355f TL |
176 | mgr.list_servers.return_value = [{ |
177 | 'hostname': 'node1', | |
178 | 'services': [] | |
179 | }] | |
f6b5b4d7 | 180 | |
f67539c2 TL |
181 | with patch_orch(False): |
182 | self._get('{}/node1'.format(self.URL_HOST)) | |
183 | self.assertStatus(200) | |
184 | self.assertIn('labels', self.json_body()) | |
b3b6e05e TL |
185 | self.assertIn('status', self.json_body()) |
186 | self.assertIn('addr', self.json_body()) | |
f6b5b4d7 | 187 | |
f67539c2 | 188 | def test_get_3(self): |
f6b5b4d7 TL |
189 | mgr.list_servers.return_value = [] |
190 | ||
f67539c2 TL |
191 | with patch_orch(True, hosts=[HostSpec('node1')]): |
192 | self._get('{}/node1'.format(self.URL_HOST)) | |
193 | self.assertStatus(200) | |
194 | self.assertIn('labels', self.json_body()) | |
b3b6e05e TL |
195 | self.assertIn('status', self.json_body()) |
196 | self.assertIn('addr', self.json_body()) | |
197 | ||
39ae355f TL |
198 | def test_populate_service_instances(self): |
199 | mgr.list_servers.return_value = [] | |
200 | ||
201 | node1_daemons = [ | |
202 | DaemonDescription( | |
203 | hostname='node1', | |
204 | daemon_type='mon', | |
205 | daemon_id='a' | |
206 | ), | |
207 | DaemonDescription( | |
208 | hostname='node1', | |
209 | daemon_type='mon', | |
210 | daemon_id='b' | |
211 | ) | |
212 | ] | |
213 | ||
214 | node2_daemons = [ | |
215 | DaemonDescription( | |
216 | hostname='node2', | |
217 | daemon_type='mgr', | |
218 | daemon_id='x' | |
219 | ), | |
220 | DaemonDescription( | |
221 | hostname='node2', | |
222 | daemon_type='mon', | |
223 | daemon_id='c' | |
224 | ) | |
225 | ] | |
226 | ||
227 | node1_instances = [{ | |
228 | 'type': 'mon', | |
229 | 'count': 2 | |
230 | }] | |
231 | ||
232 | node2_instances = [{ | |
233 | 'type': 'mgr', | |
234 | 'count': 1 | |
235 | }, { | |
236 | 'type': 'mon', | |
237 | 'count': 1 | |
238 | }] | |
239 | ||
240 | # test with orchestrator available | |
241 | with patch_orch(True, | |
242 | hosts=[HostSpec('node1'), HostSpec('node2')]) as fake_client: | |
243 | fake_client.services.list_daemons.return_value = node1_daemons | |
244 | self._get('{}/node1'.format(self.URL_HOST)) | |
245 | self.assertStatus(200) | |
246 | self.assertIn('service_instances', self.json_body()) | |
247 | self.assertEqual(self.json_body()['service_instances'], node1_instances) | |
248 | ||
249 | fake_client.services.list_daemons.return_value = node2_daemons | |
250 | self._get('{}/node2'.format(self.URL_HOST)) | |
251 | self.assertStatus(200) | |
252 | self.assertIn('service_instances', self.json_body()) | |
253 | self.assertEqual(self.json_body()['service_instances'], node2_instances) | |
254 | ||
255 | # test with no orchestrator available | |
256 | with patch_orch(False): | |
257 | mgr.list_servers.return_value = [{ | |
258 | 'hostname': 'node1', | |
259 | 'services': [{ | |
260 | 'type': 'mon', | |
261 | 'id': 'a' | |
262 | }, { | |
263 | 'type': 'mgr', | |
264 | 'id': 'b' | |
265 | }] | |
266 | }] | |
267 | self._get('{}/node1'.format(self.URL_HOST)) | |
268 | self.assertStatus(200) | |
269 | self.assertIn('service_instances', self.json_body()) | |
270 | self.assertEqual(self.json_body()['service_instances'], | |
271 | [{ | |
272 | 'type': 'mon', | |
273 | 'count': 1 | |
274 | }, { | |
275 | 'type': 'mgr', | |
276 | 'count': 1 | |
277 | }]) | |
278 | ||
b3b6e05e TL |
279 | @mock.patch('dashboard.controllers.host.add_host') |
280 | def test_add_host(self, mock_add_host): | |
281 | with patch_orch(True): | |
282 | payload = { | |
283 | 'hostname': 'node0', | |
284 | 'addr': '192.0.2.0', | |
285 | 'labels': 'mon', | |
286 | 'status': 'maintenance' | |
287 | } | |
a4b75251 | 288 | self._post(self.URL_HOST, payload, version=APIVersion(0, 1)) |
b3b6e05e TL |
289 | self.assertStatus(201) |
290 | mock_add_host.assert_called() | |
9f95a23c | 291 | |
f67539c2 | 292 | def test_set_labels(self): |
f6b5b4d7 | 293 | mgr.list_servers.return_value = [] |
f67539c2 | 294 | orch_hosts = [ |
f6b5b4d7 TL |
295 | HostSpec('node0', labels=['aaa', 'bbb']) |
296 | ] | |
f67539c2 TL |
297 | with patch_orch(True, hosts=orch_hosts) as fake_client: |
298 | fake_client.hosts.remove_label = mock.Mock() | |
299 | fake_client.hosts.add_label = mock.Mock() | |
300 | ||
301 | payload = {'update_labels': True, 'labels': ['bbb', 'ccc']} | |
a4b75251 | 302 | self._put('{}/node0'.format(self.URL_HOST), payload, version=APIVersion(0, 1)) |
f67539c2 | 303 | self.assertStatus(200) |
b3b6e05e TL |
304 | self.assertHeader('Content-Type', |
305 | 'application/vnd.ceph.api.v0.1+json') | |
f67539c2 TL |
306 | fake_client.hosts.remove_label.assert_called_once_with('node0', 'aaa') |
307 | fake_client.hosts.add_label.assert_called_once_with('node0', 'ccc') | |
308 | ||
309 | # return 400 if type other than List[str] | |
a4b75251 TL |
310 | self._put('{}/node0'.format(self.URL_HOST), |
311 | {'update_labels': True, 'labels': 'ddd'}, | |
312 | version=APIVersion(0, 1)) | |
f67539c2 TL |
313 | self.assertStatus(400) |
314 | ||
315 | def test_host_maintenance(self): | |
316 | mgr.list_servers.return_value = [] | |
317 | orch_hosts = [ | |
318 | HostSpec('node0'), | |
319 | HostSpec('node1') | |
320 | ] | |
321 | with patch_orch(True, hosts=orch_hosts): | |
322 | # enter maintenance mode | |
a4b75251 TL |
323 | self._put('{}/node0'.format(self.URL_HOST), {'maintenance': True}, |
324 | version=APIVersion(0, 1)) | |
f67539c2 | 325 | self.assertStatus(200) |
b3b6e05e TL |
326 | self.assertHeader('Content-Type', |
327 | 'application/vnd.ceph.api.v0.1+json') | |
f67539c2 TL |
328 | |
329 | # force enter maintenance mode | |
b3b6e05e | 330 | self._put('{}/node1'.format(self.URL_HOST), {'maintenance': True, 'force': True}, |
a4b75251 | 331 | version=APIVersion(0, 1)) |
f67539c2 TL |
332 | self.assertStatus(200) |
333 | ||
334 | # exit maintenance mode | |
a4b75251 TL |
335 | self._put('{}/node0'.format(self.URL_HOST), {'maintenance': True}, |
336 | version=APIVersion(0, 1)) | |
f67539c2 | 337 | self.assertStatus(200) |
a4b75251 TL |
338 | self._put('{}/node1'.format(self.URL_HOST), {'maintenance': True}, |
339 | version=APIVersion(0, 1)) | |
f67539c2 TL |
340 | self.assertStatus(200) |
341 | ||
342 | # maintenance without orchestrator service | |
343 | with patch_orch(False): | |
a4b75251 TL |
344 | self._put('{}/node0'.format(self.URL_HOST), {'maintenance': True}, |
345 | version=APIVersion(0, 1)) | |
f67539c2 TL |
346 | self.assertStatus(503) |
347 | ||
348 | @mock.patch('dashboard.controllers.host.time') | |
349 | def test_identify_device(self, mock_time): | |
350 | url = '{}/host-0/identify_device'.format(self.URL_HOST) | |
351 | with patch_orch(True) as fake_client: | |
352 | payload = { | |
353 | 'device': '/dev/sdz', | |
354 | 'duration': '1' | |
355 | } | |
356 | self._task_post(url, payload) | |
357 | self.assertStatus(200) | |
358 | mock_time.sleep.assert_called() | |
359 | calls = [ | |
360 | mock.call('host-0', '/dev/sdz', 'ident', True), | |
361 | mock.call('host-0', '/dev/sdz', 'ident', False), | |
362 | ] | |
363 | fake_client.blink_device_light.assert_has_calls(calls) | |
364 | ||
365 | @mock.patch('dashboard.controllers.host.get_inventories') | |
366 | def test_inventory(self, mock_get_inventories): | |
367 | inventory_url = '{}/host-0/inventory'.format(self.URL_HOST) | |
368 | with patch_orch(True): | |
369 | tests = [ | |
370 | { | |
371 | 'url': inventory_url, | |
372 | 'inventories': [{'a': 'b'}], | |
373 | 'refresh': None, | |
374 | 'resp': {'a': 'b'} | |
375 | }, | |
376 | { | |
377 | 'url': '{}?refresh=true'.format(inventory_url), | |
378 | 'inventories': [{'a': 'b'}], | |
379 | 'refresh': "true", | |
380 | 'resp': {'a': 'b'} | |
381 | }, | |
382 | { | |
383 | 'url': inventory_url, | |
384 | 'inventories': [], | |
385 | 'refresh': None, | |
386 | 'resp': {} | |
387 | }, | |
388 | ] | |
389 | for test in tests: | |
390 | mock_get_inventories.reset_mock() | |
391 | mock_get_inventories.return_value = test['inventories'] | |
392 | self._get(test['url']) | |
393 | mock_get_inventories.assert_called_once_with(['host-0'], test['refresh']) | |
394 | self.assertEqual(self.json_body(), test['resp']) | |
395 | self.assertStatus(200) | |
f6b5b4d7 | 396 | |
f67539c2 TL |
397 | # list without orchestrator service |
398 | with patch_orch(False): | |
399 | self._get(inventory_url) | |
400 | self.assertStatus(503) | |
f6b5b4d7 | 401 | |
20effc67 TL |
402 | def test_host_drain(self): |
403 | mgr.list_servers.return_value = [] | |
404 | orch_hosts = [ | |
405 | HostSpec('node0') | |
406 | ] | |
407 | with patch_orch(True, hosts=orch_hosts): | |
408 | self._put('{}/node0'.format(self.URL_HOST), {'drain': True}, | |
409 | version=APIVersion(0, 1)) | |
410 | self.assertStatus(200) | |
411 | self.assertHeader('Content-Type', | |
412 | 'application/vnd.ceph.api.v0.1+json') | |
413 | ||
414 | # maintenance without orchestrator service | |
415 | with patch_orch(False): | |
416 | self._put('{}/node0'.format(self.URL_HOST), {'drain': True}, | |
417 | version=APIVersion(0, 1)) | |
418 | self.assertStatus(503) | |
419 | ||
f6b5b4d7 TL |
420 | |
421 | class HostUiControllerTest(ControllerTestCase): | |
422 | URL_HOST = '/ui-api/host' | |
423 | ||
424 | @classmethod | |
425 | def setup_server(cls): | |
f6b5b4d7 TL |
426 | cls.setup_controllers([HostUi]) |
427 | ||
f67539c2 TL |
428 | def test_labels(self): |
429 | orch_hosts = [ | |
f6b5b4d7 TL |
430 | HostSpec('node1', labels=['foo']), |
431 | HostSpec('node2', labels=['foo', 'bar']) | |
432 | ] | |
f6b5b4d7 | 433 | |
f67539c2 TL |
434 | with patch_orch(True, hosts=orch_hosts): |
435 | self._get('{}/labels'.format(self.URL_HOST)) | |
436 | self.assertStatus(200) | |
437 | labels = self.json_body() | |
438 | labels.sort() | |
439 | self.assertListEqual(labels, ['bar', 'foo']) | |
440 | ||
441 | @mock.patch('dashboard.controllers.host.get_inventories') | |
442 | def test_inventory(self, mock_get_inventories): | |
443 | inventory_url = '{}/inventory'.format(self.URL_HOST) | |
444 | with patch_orch(True): | |
445 | tests = [ | |
446 | { | |
447 | 'url': inventory_url, | |
448 | 'refresh': None | |
449 | }, | |
450 | { | |
451 | 'url': '{}?refresh=true'.format(inventory_url), | |
452 | 'refresh': "true" | |
453 | }, | |
454 | ] | |
455 | for test in tests: | |
456 | mock_get_inventories.reset_mock() | |
457 | mock_get_inventories.return_value = [{'a': 'b'}] | |
458 | self._get(test['url']) | |
459 | mock_get_inventories.assert_called_once_with(None, test['refresh']) | |
460 | self.assertEqual(self.json_body(), [{'a': 'b'}]) | |
461 | self.assertStatus(200) | |
462 | ||
463 | # list without orchestrator service | |
464 | with patch_orch(False): | |
465 | self._get(inventory_url) | |
466 | self.assertStatus(503) | |
f6b5b4d7 TL |
467 | |
468 | ||
469 | class TestHosts(unittest.TestCase): | |
f67539c2 | 470 | def test_get_hosts(self): |
f6b5b4d7 | 471 | mgr.list_servers.return_value = [{ |
39ae355f TL |
472 | 'hostname': 'node1', |
473 | 'services': [] | |
f6b5b4d7 | 474 | }, { |
39ae355f TL |
475 | 'hostname': 'localhost', |
476 | 'services': [] | |
f6b5b4d7 | 477 | }] |
f67539c2 | 478 | orch_hosts = [ |
f6b5b4d7 TL |
479 | HostSpec('node1', labels=['foo', 'bar']), |
480 | HostSpec('node2', labels=['bar']) | |
481 | ] | |
9f95a23c | 482 | |
f67539c2 TL |
483 | with patch_orch(True, hosts=orch_hosts): |
484 | hosts = get_hosts() | |
aee94f69 | 485 | self.assertEqual(len(hosts), 2) |
f67539c2 | 486 | checks = { |
f67539c2 TL |
487 | 'node1': { |
488 | 'sources': { | |
aee94f69 | 489 | 'ceph': False, |
f67539c2 TL |
490 | 'orchestrator': True |
491 | }, | |
aee94f69 | 492 | 'labels': ['foo', 'bar'] |
f67539c2 TL |
493 | }, |
494 | 'node2': { | |
495 | 'sources': { | |
496 | 'ceph': False, | |
497 | 'orchestrator': True | |
498 | }, | |
499 | 'labels': ['bar'] | |
500 | } | |
501 | } | |
502 | for host in hosts: | |
503 | hostname = host['hostname'] | |
504 | self.assertDictEqual(host['sources'], checks[hostname]['sources']) | |
505 | self.assertListEqual(host['labels'], checks[hostname]['labels']) | |
506 | ||
507 | @mock.patch('dashboard.controllers.host.mgr.get') | |
508 | def test_get_device_osd_map(self, mgr_get): | |
509 | mgr_get.side_effect = lambda key: { | |
510 | 'osd_metadata': { | |
511 | '0': { | |
512 | 'hostname': 'node0', | |
513 | 'devices': 'nvme0n1,sdb', | |
514 | }, | |
515 | '1': { | |
516 | 'hostname': 'node0', | |
517 | 'devices': 'nvme0n1,sdc', | |
518 | }, | |
519 | '2': { | |
520 | 'hostname': 'node1', | |
521 | 'devices': 'sda', | |
522 | }, | |
523 | '3': { | |
524 | 'hostname': 'node2', | |
525 | 'devices': '', | |
526 | } | |
527 | } | |
528 | }[key] | |
529 | ||
530 | device_osd_map = get_device_osd_map() | |
531 | mgr.get.assert_called_with('osd_metadata') | |
532 | # sort OSD IDs to make assertDictEqual work | |
533 | for devices in device_osd_map.values(): | |
534 | for host in devices.keys(): | |
535 | devices[host] = sorted(devices[host]) | |
536 | self.assertDictEqual(device_osd_map, { | |
537 | 'node0': { | |
538 | 'nvme0n1': [0, 1], | |
539 | 'sdb': [0], | |
540 | 'sdc': [1], | |
f6b5b4d7 TL |
541 | }, |
542 | 'node1': { | |
f67539c2 TL |
543 | 'sda': [2] |
544 | } | |
545 | }) | |
546 | ||
547 | @mock.patch('dashboard.controllers.host.str_to_bool') | |
548 | @mock.patch('dashboard.controllers.host.get_device_osd_map') | |
549 | def test_get_inventories(self, mock_get_device_osd_map, mock_str_to_bool): | |
550 | mock_get_device_osd_map.return_value = { | |
551 | 'host-0': { | |
552 | 'nvme0n1': [1, 2], | |
553 | 'sdb': [1], | |
554 | 'sdc': [2] | |
f6b5b4d7 | 555 | }, |
f67539c2 TL |
556 | 'host-1': { |
557 | 'sdb': [3] | |
f6b5b4d7 | 558 | } |
9f95a23c | 559 | } |
f67539c2 TL |
560 | inventory = [ |
561 | { | |
562 | 'name': 'host-0', | |
563 | 'addr': '1.2.3.4', | |
564 | 'devices': [ | |
565 | {'path': 'nvme0n1'}, | |
566 | {'path': '/dev/sdb'}, | |
567 | {'path': '/dev/sdc'}, | |
568 | ] | |
569 | }, | |
570 | { | |
571 | 'name': 'host-1', | |
572 | 'addr': '1.2.3.5', | |
573 | 'devices': [ | |
574 | {'path': '/dev/sda'}, | |
575 | {'path': 'sdb'}, | |
576 | ] | |
577 | } | |
578 | ] | |
579 | ||
580 | with patch_orch(True, inventory=inventory) as orch_client: | |
581 | mock_str_to_bool.return_value = True | |
582 | ||
583 | hosts = ['host-0', 'host-1'] | |
584 | inventories = get_inventories(hosts, 'true') | |
585 | mock_str_to_bool.assert_called_with('true') | |
586 | orch_client.inventory.list.assert_called_once_with(hosts=hosts, refresh=True) | |
587 | self.assertEqual(len(inventories), 2) | |
588 | host0 = inventories[0] | |
589 | self.assertEqual(host0['name'], 'host-0') | |
590 | self.assertEqual(host0['addr'], '1.2.3.4') | |
39ae355f TL |
591 | # devices should be sorted by path name, so |
592 | # /dev/sdb, /dev/sdc, nvme0n1 | |
593 | self.assertEqual(host0['devices'][0]['osd_ids'], [1]) | |
594 | self.assertEqual(host0['devices'][1]['osd_ids'], [2]) | |
595 | self.assertEqual(host0['devices'][2]['osd_ids'], [1, 2]) | |
f67539c2 TL |
596 | host1 = inventories[1] |
597 | self.assertEqual(host1['name'], 'host-1') | |
598 | self.assertEqual(host1['addr'], '1.2.3.5') | |
39ae355f TL |
599 | # devices should be sorted by path name, so |
600 | # /dev/sda, sdb | |
f67539c2 TL |
601 | self.assertEqual(host1['devices'][0]['osd_ids'], []) |
602 | self.assertEqual(host1['devices'][1]['osd_ids'], [3]) |