]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/dashboard/controllers/orchestrator.py
import 15.2.5
[ceph.git] / ceph / src / pybind / mgr / dashboard / controllers / orchestrator.py
CommitLineData
9f95a23c
TL
1# -*- coding: utf-8 -*-
2from __future__ import absolute_import
3import os.path
4
5import time
6
7from . import ApiController, Endpoint, ReadPermission, UpdatePermission
8from . import RESTController, Task
9from .. import mgr
10from ..exceptions import DashboardException
11from ..security import Scope
12from ..services.exception import handle_orchestrator_error
13from ..services.orchestrator import OrchClient
14from ..tools import TaskManager, wraps
15
16
17def 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
51def orchestrator_task(name, metadata, wait_for=2.0):
52 return Task("orchestrator/{}".format(name), metadata, wait_for)
53
54
55def 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')
69class 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)
102class 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