]>
Commit | Line | Data |
---|---|---|
11fdf7f2 | 1 | # -*- coding: utf-8 -*- |
11fdf7f2 | 2 | |
f67539c2 | 3 | import logging |
adb31ebb | 4 | from functools import wraps |
f67539c2 | 5 | from typing import Any, Dict, List, Optional |
9f95a23c | 6 | |
adb31ebb | 7 | from ceph.deployment.service_spec import ServiceSpec |
f67539c2 TL |
8 | from orchestrator import DaemonDescription, DeviceLightLoc, HostSpec, \ |
9 | InventoryFilter, OrchestratorClientMixin, OrchestratorError, OrchResult, \ | |
10 | ServiceDescription, raise_if_exception | |
11 | ||
9f95a23c | 12 | from .. import mgr |
9f95a23c TL |
13 | |
14 | logger = logging.getLogger('orchestrator') | |
11fdf7f2 TL |
15 | |
16 | ||
81eedcae | 17 | # pylint: disable=abstract-method |
9f95a23c | 18 | class OrchestratorAPI(OrchestratorClientMixin): |
81eedcae | 19 | def __init__(self): |
9f95a23c TL |
20 | super(OrchestratorAPI, self).__init__() |
21 | self.set_mgr(mgr) # type: ignore | |
22 | ||
23 | def status(self): | |
24 | try: | |
f67539c2 TL |
25 | status, message, _module_details = super().available() |
26 | logger.info("is orchestrator available: %s, %s", status, message) | |
27 | return dict(available=status, message=message) | |
28 | except (RuntimeError, OrchestratorError, ImportError) as e: | |
9f95a23c TL |
29 | return dict( |
30 | available=False, | |
f67539c2 | 31 | message='Orchestrator is unavailable: {}'.format(str(e))) |
9f95a23c TL |
32 | |
33 | ||
34 | def wait_api_result(method): | |
35 | @wraps(method) | |
36 | def inner(self, *args, **kwargs): | |
37 | completion = method(self, *args, **kwargs) | |
81eedcae | 38 | raise_if_exception(completion) |
11fdf7f2 | 39 | return completion.result |
9f95a23c TL |
40 | return inner |
41 | ||
42 | ||
43 | class ResourceManager(object): | |
44 | def __init__(self, api): | |
45 | self.api = api | |
46 | ||
47 | ||
48 | class HostManger(ResourceManager): | |
49 | @wait_api_result | |
50 | def list(self) -> List[HostSpec]: | |
51 | return self.api.get_hosts() | |
52 | ||
f67539c2 TL |
53 | @wait_api_result |
54 | def enter_maintenance(self, hostname: str, force: bool = False): | |
55 | return self.api.enter_host_maintenance(hostname, force) | |
56 | ||
57 | @wait_api_result | |
58 | def exit_maintenance(self, hostname: str): | |
59 | return self.api.exit_host_maintenance(hostname) | |
60 | ||
9f95a23c TL |
61 | def get(self, hostname: str) -> Optional[HostSpec]: |
62 | hosts = [host for host in self.list() if host.hostname == hostname] | |
63 | return hosts[0] if hosts else None | |
64 | ||
65 | @wait_api_result | |
b3b6e05e TL |
66 | def add(self, hostname: str, addr: str, labels: List[str]): |
67 | return self.api.add_host(HostSpec(hostname, addr=addr, labels=labels)) | |
9f95a23c | 68 | |
a4b75251 TL |
69 | @wait_api_result |
70 | def get_facts(self, hostname: Optional[str] = None) -> List[Dict[str, Any]]: | |
71 | return self.api.get_facts(hostname) | |
72 | ||
9f95a23c TL |
73 | @wait_api_result |
74 | def remove(self, hostname: str): | |
75 | return self.api.remove_host(hostname) | |
76 | ||
f6b5b4d7 | 77 | @wait_api_result |
f67539c2 | 78 | def add_label(self, host: str, label: str) -> OrchResult[str]: |
f6b5b4d7 TL |
79 | return self.api.add_host_label(host, label) |
80 | ||
81 | @wait_api_result | |
f67539c2 | 82 | def remove_label(self, host: str, label: str) -> OrchResult[str]: |
f6b5b4d7 TL |
83 | return self.api.remove_host_label(host, label) |
84 | ||
20effc67 TL |
85 | @wait_api_result |
86 | def drain(self, hostname: str): | |
87 | return self.api.drain_host(hostname) | |
88 | ||
9f95a23c TL |
89 | |
90 | class InventoryManager(ResourceManager): | |
91 | @wait_api_result | |
92 | def list(self, hosts=None, refresh=False): | |
93 | host_filter = InventoryFilter(hosts=hosts) if hosts else None | |
94 | return self.api.get_inventory(host_filter=host_filter, refresh=refresh) | |
11fdf7f2 | 95 | |
11fdf7f2 | 96 | |
9f95a23c TL |
97 | class ServiceManager(ResourceManager): |
98 | @wait_api_result | |
f91f0fd5 TL |
99 | def list(self, |
100 | service_type: Optional[str] = None, | |
101 | service_name: Optional[str] = None) -> List[ServiceDescription]: | |
102 | return self.api.describe_service(service_type, service_name) | |
9f95a23c TL |
103 | |
104 | @wait_api_result | |
105 | def get(self, service_name: str) -> ServiceDescription: | |
106 | return self.api.describe_service(None, service_name) | |
107 | ||
108 | @wait_api_result | |
109 | def list_daemons(self, | |
110 | service_name: Optional[str] = None, | |
f91f0fd5 | 111 | daemon_type: Optional[str] = None, |
9f95a23c | 112 | hostname: Optional[str] = None) -> List[DaemonDescription]: |
f91f0fd5 TL |
113 | return self.api.list_daemons(service_name=service_name, |
114 | daemon_type=daemon_type, | |
115 | host=hostname) | |
9f95a23c TL |
116 | |
117 | def reload(self, service_type, service_ids): | |
11fdf7f2 TL |
118 | if not isinstance(service_ids, list): |
119 | service_ids = [service_ids] | |
120 | ||
9f95a23c TL |
121 | completion_list = [ |
122 | self.api.service_action('reload', service_type, service_name, | |
123 | service_id) | |
124 | for service_name, service_id in service_ids | |
125 | ] | |
126 | self.api.orchestrator_wait(completion_list) | |
81eedcae TL |
127 | for c in completion_list: |
128 | raise_if_exception(c) | |
9f95a23c | 129 | |
adb31ebb | 130 | @wait_api_result |
f67539c2 | 131 | def apply(self, service_spec: Dict) -> OrchResult[List[str]]: |
adb31ebb TL |
132 | spec = ServiceSpec.from_json(service_spec) |
133 | return self.api.apply([spec]) | |
134 | ||
135 | @wait_api_result | |
136 | def remove(self, service_name: str) -> List[str]: | |
137 | return self.api.remove_service(service_name) | |
138 | ||
9f95a23c TL |
139 | |
140 | class OsdManager(ResourceManager): | |
141 | @wait_api_result | |
142 | def create(self, drive_group_specs): | |
143 | return self.api.apply_drivegroups(drive_group_specs) | |
144 | ||
145 | @wait_api_result | |
f6b5b4d7 TL |
146 | def remove(self, osd_ids, replace=False, force=False): |
147 | return self.api.remove_osds(osd_ids, replace, force) | |
9f95a23c TL |
148 | |
149 | @wait_api_result | |
150 | def removing_status(self): | |
151 | return self.api.remove_osds_status() | |
152 | ||
153 | ||
20effc67 TL |
154 | class DaemonManager(ResourceManager): |
155 | @wait_api_result | |
156 | def action(self, daemon_name='', action='', image=None): | |
157 | return self.api.daemon_action(daemon_name=daemon_name, action=action, image=image) | |
158 | ||
159 | ||
9f95a23c TL |
160 | class OrchClient(object): |
161 | ||
162 | _instance = None | |
163 | ||
164 | @classmethod | |
165 | def instance(cls): | |
f67539c2 | 166 | # type: () -> OrchClient |
9f95a23c TL |
167 | if cls._instance is None: |
168 | cls._instance = cls() | |
169 | return cls._instance | |
170 | ||
171 | def __init__(self): | |
172 | self.api = OrchestratorAPI() | |
173 | ||
174 | self.hosts = HostManger(self.api) | |
175 | self.inventory = InventoryManager(self.api) | |
176 | self.services = ServiceManager(self.api) | |
177 | self.osds = OsdManager(self.api) | |
20effc67 | 178 | self.daemons = DaemonManager(self.api) |
9f95a23c | 179 | |
f67539c2 TL |
180 | def available(self, features: Optional[List[str]] = None) -> bool: |
181 | available = self.status()['available'] | |
182 | if available and features is not None: | |
183 | return not self.get_missing_features(features) | |
184 | return available | |
9f95a23c | 185 | |
f67539c2 TL |
186 | def status(self) -> Dict[str, Any]: |
187 | status = self.api.status() | |
188 | status['features'] = {} | |
189 | if status['available']: | |
190 | status['features'] = self.api.get_feature_set() | |
191 | return status | |
192 | ||
193 | def get_missing_features(self, features: List[str]) -> List[str]: | |
194 | supported_features = {k for k, v in self.api.get_feature_set().items() if v['available']} | |
195 | return list(set(features) - supported_features) | |
9f95a23c TL |
196 | |
197 | @wait_api_result | |
198 | def blink_device_light(self, hostname, device, ident_fault, on): | |
f67539c2 | 199 | # type: (str, str, str, bool) -> OrchResult[List[str]] |
9f95a23c TL |
200 | return self.api.blink_device_light( |
201 | ident_fault, on, [DeviceLightLoc(hostname, device, device)]) | |
f67539c2 TL |
202 | |
203 | ||
204 | class OrchFeature(object): | |
205 | HOST_LIST = 'get_hosts' | |
a4b75251 TL |
206 | HOST_ADD = 'add_host' |
207 | HOST_REMOVE = 'remove_host' | |
f67539c2 TL |
208 | HOST_LABEL_ADD = 'add_host_label' |
209 | HOST_LABEL_REMOVE = 'remove_host_label' | |
210 | HOST_MAINTENANCE_ENTER = 'enter_host_maintenance' | |
211 | HOST_MAINTENANCE_EXIT = 'exit_host_maintenance' | |
20effc67 | 212 | HOST_DRAIN = 'drain_host' |
f67539c2 TL |
213 | |
214 | SERVICE_LIST = 'describe_service' | |
215 | SERVICE_CREATE = 'apply' | |
a4b75251 | 216 | SERVICE_EDIT = 'apply' |
f67539c2 TL |
217 | SERVICE_DELETE = 'remove_service' |
218 | SERVICE_RELOAD = 'service_action' | |
219 | DAEMON_LIST = 'list_daemons' | |
220 | ||
221 | OSD_GET_REMOVE_STATUS = 'remove_osds_status' | |
222 | ||
223 | OSD_CREATE = 'apply_drivegroups' | |
224 | OSD_DELETE = 'remove_osds' | |
225 | ||
226 | DEVICE_LIST = 'get_inventory' | |
227 | DEVICE_BLINK_LIGHT = 'blink_device_light' | |
20effc67 TL |
228 | |
229 | DAEMON_ACTION = 'daemon_action' |