1 # -*- coding: utf-8 -*-
2 from __future__
import absolute_import
6 from typing
import List
, Dict
10 from mgr_util
import merge_dicts
11 from orchestrator
import HostSpec
12 from . import ApiController
, RESTController
, Task
, Endpoint
, ReadPermission
, \
13 UiApiController
, BaseController
14 from .orchestrator
import raise_if_no_orchestrator
16 from ..exceptions
import DashboardException
17 from ..security
import Scope
18 from ..services
.orchestrator
import OrchClient
19 from ..services
.ceph_service
import CephService
20 from ..services
.exception
import handle_orchestrator_error
23 def host_task(name
, metadata
, wait_for
=10.0):
24 return Task("host/{}".format(name
), metadata
, wait_for
)
27 def merge_hosts_by_hostname(ceph_hosts
, orch_hosts
):
28 # type: (List[dict], List[HostSpec]) -> List[dict]
30 Merge Ceph hosts with orchestrator hosts by hostnames.
32 :param ceph_hosts: hosts returned from mgr
33 :type ceph_hosts: list of dict
34 :param orch_hosts: hosts returned from ochestrator
35 :type orch_hosts: list of HostSpec
38 hosts
= copy
.deepcopy(ceph_hosts
)
39 orch_hosts_map
= {host
.hostname
: host
.to_json() for host
in orch_hosts
}
42 for hostname
in orch_hosts_map
:
43 orch_hosts_map
[hostname
]['labels'].sort()
45 # Hosts in both Ceph and Orchestrator.
47 hostname
= host
['hostname']
48 if hostname
in orch_hosts_map
:
49 host
.update(orch_hosts_map
[hostname
])
50 host
['sources']['orchestrator'] = True
51 orch_hosts_map
.pop(hostname
)
53 # Hosts only in Orchestrator.
63 }, orch_hosts_map
[hostname
]) for hostname
in orch_hosts_map
65 hosts
.extend(orch_hosts_only
)
69 def get_hosts(from_ceph
=True, from_orchestrator
=True):
71 Get hosts from various sources.
86 }) for server
in mgr
.list_servers()
89 orch
= OrchClient
.instance()
91 return merge_hosts_by_hostname(ceph_hosts
, orch
.hosts
.list())
95 def get_host(hostname
: str) -> Dict
:
97 Get a specific host from Ceph or Orchestrator (if available).
98 :param hostname: The name of the host to fetch.
99 :raises: cherrypy.HTTPError: If host not found.
101 for host
in get_hosts():
102 if host
['hostname'] == hostname
:
104 raise cherrypy
.HTTPError(404)
107 @ApiController('/host', Scope
.HOSTS
)
108 class Host(RESTController
):
109 def list(self
, sources
=None):
112 _sources
= sources
.split(',')
113 from_ceph
= 'ceph' in _sources
114 from_orchestrator
= 'orchestrator' in _sources
115 return get_hosts(from_ceph
, from_orchestrator
)
117 @raise_if_no_orchestrator
118 @handle_orchestrator_error('host')
119 @host_task('create', {'hostname': '{hostname}'})
120 def create(self
, hostname
): # pragma: no cover - requires realtime env
121 orch_client
= OrchClient
.instance()
122 self
._check
_orchestrator
_host
_op
(orch_client
, hostname
, True)
123 orch_client
.hosts
.add(hostname
)
125 @raise_if_no_orchestrator
126 @handle_orchestrator_error('host')
127 @host_task('delete', {'hostname': '{hostname}'})
128 def delete(self
, hostname
): # pragma: no cover - requires realtime env
129 orch_client
= OrchClient
.instance()
130 self
._check
_orchestrator
_host
_op
(orch_client
, hostname
, False)
131 orch_client
.hosts
.remove(hostname
)
133 def _check_orchestrator_host_op(self
, orch_client
, hostname
, add_host
=True): # pragma:no cover
134 """Check if we can adding or removing a host with orchestrator
136 :param orch_client: Orchestrator client
137 :param add: True for adding host operation, False for removing host
138 :raise DashboardException
140 host
= orch_client
.hosts
.get(hostname
)
141 if add_host
and host
:
142 raise DashboardException(
143 code
='orchestrator_add_existed_host',
144 msg
='{} is already in orchestrator'.format(hostname
),
145 component
='orchestrator')
146 if not add_host
and not host
:
147 raise DashboardException(
148 code
='orchestrator_remove_nonexistent_host',
149 msg
='Remove a non-existent host {} from orchestrator'.format(
151 component
='orchestrator')
153 @RESTController.Resource('GET')
154 def devices(self
, hostname
):
156 return CephService
.get_devices_by_host(hostname
)
158 @RESTController.Resource('GET')
159 def smart(self
, hostname
):
160 # type: (str) -> dict
161 return CephService
.get_smart_data_by_host(hostname
)
163 @RESTController.Resource('GET')
164 @raise_if_no_orchestrator
165 def daemons(self
, hostname
: str) -> List
[dict]:
166 orch
= OrchClient
.instance()
167 daemons
= orch
.services
.list_daemons(None, hostname
)
168 return [d
.to_json() for d
in daemons
]
170 @handle_orchestrator_error('host')
171 def get(self
, hostname
: str) -> Dict
:
173 Get the specified host.
174 :raises: cherrypy.HTTPError: If host not found.
176 return get_host(hostname
)
178 @raise_if_no_orchestrator
179 @handle_orchestrator_error('host')
180 def set(self
, hostname
: str, labels
: List
[str]):
182 Update the specified host.
183 Note, this is only supported when Ceph Orchestrator is enabled.
184 :param hostname: The name of the host to be processed.
185 :param labels: List of labels.
187 orch
= OrchClient
.instance()
188 host
= get_host(hostname
)
189 current_labels
= set(host
['labels'])
191 remove_labels
= list(current_labels
.difference(set(labels
)))
192 for label
in remove_labels
:
193 orch
.hosts
.remove_label(hostname
, label
)
195 add_labels
= list(set(labels
).difference(current_labels
))
196 for label
in add_labels
:
197 orch
.hosts
.add_label(hostname
, label
)
200 @UiApiController('/host', Scope
.HOSTS
)
201 class HostUi(BaseController
):
204 @handle_orchestrator_error('host')
205 def labels(self
) -> List
[str]:
208 Note, host labels are only supported when Ceph Orchestrator is enabled.
209 If Ceph Orchestrator is not enabled, an empty list is returned.
210 :return: A list of all host labels.
213 orch
= OrchClient
.instance()
215 for host
in orch
.hosts
.list():
216 labels
.extend(host
.labels
)
218 return list(set(labels
)) # Filter duplicate labels.