]>
Commit | Line | Data |
---|---|---|
9f95a23c TL |
1 | # -*- coding: utf-8 -*- |
2 | from __future__ import absolute_import | |
3 | import os.path | |
4 | ||
5 | import time | |
6 | ||
7 | from . import ApiController, Endpoint, ReadPermission, UpdatePermission | |
8 | from . import RESTController, Task | |
9 | from .. import mgr | |
10 | from ..exceptions import DashboardException | |
11 | from ..security import Scope | |
12 | from ..services.exception import handle_orchestrator_error | |
13 | from ..services.orchestrator import OrchClient | |
14 | from ..tools import TaskManager, wraps | |
15 | ||
16 | ||
17 | def get_device_osd_map(): | |
18 | """Get mappings from inventory devices to OSD IDs. | |
19 | ||
20 | :return: Returns a dictionary containing mappings. Note one device might | |
21 | shared between multiple OSDs. | |
22 | e.g. { | |
23 | 'node1': { | |
24 | 'nvme0n1': [0, 1], | |
25 | 'vdc': [0], | |
26 | 'vdb': [1] | |
27 | }, | |
28 | 'node2': { | |
29 | 'vdc': [2] | |
30 | } | |
31 | } | |
32 | :rtype: dict | |
33 | """ | |
34 | result: dict = {} | |
35 | for osd_id, osd_metadata in mgr.get('osd_metadata').items(): | |
36 | hostname = osd_metadata.get('hostname') | |
37 | devices = osd_metadata.get('devices') | |
38 | if not hostname or not devices: | |
39 | continue | |
40 | if hostname not in result: | |
41 | result[hostname] = {} | |
42 | # for OSD contains multiple devices, devices is in `sda,sdb` | |
43 | for device in devices.split(','): | |
44 | if device not in result[hostname]: | |
45 | result[hostname][device] = [int(osd_id)] | |
46 | else: | |
47 | result[hostname][device].append(int(osd_id)) | |
48 | return result | |
49 | ||
50 | ||
51 | def orchestrator_task(name, metadata, wait_for=2.0): | |
52 | return Task("orchestrator/{}".format(name), metadata, wait_for) | |
53 | ||
54 | ||
55 | def raise_if_no_orchestrator(method): | |
56 | @wraps(method) | |
57 | def inner(self, *args, **kwargs): | |
58 | orch = OrchClient.instance() | |
59 | if not orch.available(): | |
f6b5b4d7 | 60 | raise DashboardException(code='orchestrator_status_unavailable', # pragma: no cover |
9f95a23c TL |
61 | msg='Orchestrator is unavailable', |
62 | component='orchestrator', | |
63 | http_status_code=503) | |
64 | return method(self, *args, **kwargs) | |
65 | return inner | |
66 | ||
67 | ||
68 | @ApiController('/orchestrator') | |
69 | class Orchestrator(RESTController): | |
70 | ||
71 | @Endpoint() | |
72 | @ReadPermission | |
73 | def status(self): | |
74 | return OrchClient.instance().status() | |
75 | ||
76 | @Endpoint(method='POST') | |
77 | @UpdatePermission | |
78 | @raise_if_no_orchestrator | |
79 | @handle_orchestrator_error('osd') | |
80 | @orchestrator_task('identify_device', ['{hostname}', '{device}']) | |
f6b5b4d7 | 81 | def identify_device(self, hostname, device, duration): # pragma: no cover |
9f95a23c TL |
82 | # type: (str, str, int) -> None |
83 | """ | |
84 | Identify a device by switching on the device light for N seconds. | |
85 | :param hostname: The hostname of the device to process. | |
86 | :param device: The device identifier to process, e.g. ``/dev/dm-0`` or | |
87 | ``ABC1234DEF567-1R1234_ABC8DE0Q``. | |
88 | :param duration: The duration in seconds how long the LED should flash. | |
89 | """ | |
90 | orch = OrchClient.instance() | |
91 | TaskManager.current_task().set_progress(0) | |
92 | orch.blink_device_light(hostname, device, 'ident', True) | |
93 | for i in range(int(duration)): | |
94 | percentage = int(round(i / float(duration) * 100)) | |
95 | TaskManager.current_task().set_progress(percentage) | |
96 | time.sleep(1) | |
97 | orch.blink_device_light(hostname, device, 'ident', False) | |
98 | TaskManager.current_task().set_progress(100) | |
99 | ||
100 | ||
101 | @ApiController('/orchestrator/inventory', Scope.HOSTS) | |
102 | class OrchestratorInventory(RESTController): | |
103 | ||
104 | @raise_if_no_orchestrator | |
105 | def list(self, hostname=None): | |
106 | orch = OrchClient.instance() | |
107 | hosts = [hostname] if hostname else None | |
108 | inventory_hosts = [host.to_json() for host in orch.inventory.list(hosts)] | |
109 | device_osd_map = get_device_osd_map() | |
110 | for inventory_host in inventory_hosts: | |
111 | host_osds = device_osd_map.get(inventory_host['name']) | |
112 | for device in inventory_host['devices']: | |
f6b5b4d7 | 113 | if host_osds: # pragma: no cover |
9f95a23c TL |
114 | dev_name = os.path.basename(device['path']) |
115 | device['osd_ids'] = sorted(host_osds.get(dev_name, [])) | |
116 | else: | |
117 | device['osd_ids'] = [] | |
118 | return inventory_hosts |