1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """Miscellaneous logical units that don't fit into any category."""
23
24 import logging
25 import time
26
27 from ganeti import compat
28 from ganeti import constants
29 from ganeti import errors
30 from ganeti import locking
31 from ganeti import qlang
32 from ganeti import query
33 from ganeti import utils
34 from ganeti.cmdlib.base import NoHooksLU, QueryBase
35 from ganeti.cmdlib.common import GetWantedNodes, SupportsOob
39 """Logical unit for OOB handling.
40
41 """
42 REQ_BGL = False
43 _SKIP_MASTER = (constants.OOB_POWER_OFF, constants.OOB_POWER_CYCLE)
44
46 """Gather locks we need.
47
48 """
49 if self.op.node_names:
50 (self.op.node_uuids, self.op.node_names) = \
51 GetWantedNodes(self, self.op.node_names)
52 lock_node_uuids = self.op.node_uuids
53 else:
54 lock_node_uuids = locking.ALL_SET
55
56 self.needed_locks = {
57 locking.LEVEL_NODE: lock_node_uuids,
58 }
59
60 self.share_locks[locking.LEVEL_NODE_ALLOC] = 1
61
62 if not self.op.node_names:
63
64 self.needed_locks[locking.LEVEL_NODE_ALLOC] = locking.ALL_SET
65
67 """Check prerequisites.
68
69 This checks:
70 - the node exists in the configuration
71 - OOB is supported
72
73 Any errors are signaled by raising errors.OpPrereqError.
74
75 """
76 self.nodes = []
77 self.master_node_uuid = self.cfg.GetMasterNode()
78 master_node_obj = self.cfg.GetNodeInfo(self.master_node_uuid)
79
80 assert self.op.power_delay >= 0.0
81
82 if self.op.node_uuids:
83 if (self.op.command in self._SKIP_MASTER and
84 master_node_obj.uuid in self.op.node_uuids):
85 master_oob_handler = SupportsOob(self.cfg, master_node_obj)
86
87 if master_oob_handler:
88 additional_text = ("run '%s %s %s' if you want to operate on the"
89 " master regardless") % (master_oob_handler,
90 self.op.command,
91 master_node_obj.name)
92 else:
93 additional_text = "it does not support out-of-band operations"
94
95 raise errors.OpPrereqError(("Operating on the master node %s is not"
96 " allowed for %s; %s") %
97 (master_node_obj.name, self.op.command,
98 additional_text), errors.ECODE_INVAL)
99 else:
100 self.op.node_uuids = self.cfg.GetNodeList()
101 if self.op.command in self._SKIP_MASTER:
102 self.op.node_uuids.remove(master_node_obj.uuid)
103
104 if self.op.command in self._SKIP_MASTER:
105 assert master_node_obj.uuid not in self.op.node_uuids
106
107 for node_uuid in self.op.node_uuids:
108 node = self.cfg.GetNodeInfo(node_uuid)
109 if node is None:
110 raise errors.OpPrereqError("Node %s not found" % node_uuid,
111 errors.ECODE_NOENT)
112
113 self.nodes.append(node)
114
115 if (not self.op.ignore_status and
116 (self.op.command == constants.OOB_POWER_OFF and not node.offline)):
117 raise errors.OpPrereqError(("Cannot power off node %s because it is"
118 " not marked offline") % node.name,
119 errors.ECODE_STATE)
120
121 - def Exec(self, feedback_fn):
122 """Execute OOB and return result if we expect any.
123
124 """
125 ret = []
126
127 for idx, node in enumerate(utils.NiceSort(self.nodes,
128 key=lambda node: node.name)):
129 node_entry = [(constants.RS_NORMAL, node.name)]
130 ret.append(node_entry)
131
132 oob_program = SupportsOob(self.cfg, node)
133
134 if not oob_program:
135 node_entry.append((constants.RS_UNAVAIL, None))
136 continue
137
138 logging.info("Executing out-of-band command '%s' using '%s' on %s",
139 self.op.command, oob_program, node.name)
140 result = self.rpc.call_run_oob(self.master_node_uuid, oob_program,
141 self.op.command, node.name,
142 self.op.timeout)
143
144 if result.fail_msg:
145 self.LogWarning("Out-of-band RPC failed on node '%s': %s",
146 node.name, result.fail_msg)
147 node_entry.append((constants.RS_NODATA, None))
148 else:
149 try:
150 self._CheckPayload(result)
151 except errors.OpExecError, err:
152 self.LogWarning("Payload returned by node '%s' is not valid: %s",
153 node.name, err)
154 node_entry.append((constants.RS_NODATA, None))
155 else:
156 if self.op.command == constants.OOB_HEALTH:
157
158 for item, status in result.payload:
159 if status in [constants.OOB_STATUS_WARNING,
160 constants.OOB_STATUS_CRITICAL]:
161 self.LogWarning("Item '%s' on node '%s' has status '%s'",
162 item, node.name, status)
163
164 if self.op.command == constants.OOB_POWER_ON:
165 node.powered = True
166 elif self.op.command == constants.OOB_POWER_OFF:
167 node.powered = False
168 elif self.op.command == constants.OOB_POWER_STATUS:
169 powered = result.payload[constants.OOB_POWER_STATUS_POWERED]
170 if powered != node.powered:
171 logging.warning(("Recorded power state (%s) of node '%s' does not"
172 " match actual power state (%s)"), node.powered,
173 node.name, powered)
174
175
176 if self.op.command in (constants.OOB_POWER_ON,
177 constants.OOB_POWER_OFF):
178 self.cfg.Update(node, feedback_fn)
179
180 node_entry.append((constants.RS_NORMAL, result.payload))
181
182 if (self.op.command == constants.OOB_POWER_ON and
183 idx < len(self.nodes) - 1):
184 time.sleep(self.op.power_delay)
185
186 return ret
187
189 """Checks if the payload is valid.
190
191 @param result: RPC result
192 @raises errors.OpExecError: If payload is not valid
193
194 """
195 errs = []
196 if self.op.command == constants.OOB_HEALTH:
197 if not isinstance(result.payload, list):
198 errs.append("command 'health' is expected to return a list but got %s" %
199 type(result.payload))
200 else:
201 for item, status in result.payload:
202 if status not in constants.OOB_STATUSES:
203 errs.append("health item '%s' has invalid status '%s'" %
204 (item, status))
205
206 if self.op.command == constants.OOB_POWER_STATUS:
207 if not isinstance(result.payload, dict):
208 errs.append("power-status is expected to return a dict but got %s" %
209 type(result.payload))
210
211 if self.op.command in [
212 constants.OOB_POWER_ON,
213 constants.OOB_POWER_OFF,
214 constants.OOB_POWER_CYCLE,
215 ]:
216 if result.payload is not None:
217 errs.append("%s is expected to not return payload but got '%s'" %
218 (self.op.command, result.payload))
219
220 if errs:
221 raise errors.OpExecError("Check of out-of-band payload failed due to %s" %
222 utils.CommaJoin(errs))
223
226 FIELDS = query.EXTSTORAGE_FIELDS
227
229
230
231
232 lu.needed_locks = {}
233
234
235
236
237 if self.names:
238 self.wanted = [lu.cfg.GetNodeInfoByName(name).uuid for name in self.names]
239 else:
240 self.wanted = locking.ALL_SET
241
242 self.do_locking = self.use_locking
243
246
247 @staticmethod
249 """Remaps a per-node return list into an a per-provider per-node dictionary
250
251 @param rlist: a map with node uuids as keys and ExtStorage objects as values
252
253 @rtype: dict
254 @return: a dictionary with extstorage providers as keys and as
255 value another map, with node uuids as keys and tuples of
256 (path, status, diagnose, parameters) as values, eg::
257
258 {"provider1": {"node_uuid1": [(/usr/lib/..., True, "", [])]
259 "node_uuid2": [(/srv/..., False, "missing file")]
260 "node_uuid3": [(/srv/..., True, "", [])]
261 }
262
263 """
264 all_es = {}
265
266
267
268 good_nodes = [node_uuid for node_uuid in rlist
269 if not rlist[node_uuid].fail_msg]
270 for node_uuid, nr in rlist.items():
271 if nr.fail_msg or not nr.payload:
272 continue
273 for (name, path, status, diagnose, params) in nr.payload:
274 if name not in all_es:
275
276
277 all_es[name] = {}
278 for nuuid in good_nodes:
279 all_es[name][nuuid] = []
280
281 params = [tuple(v) for v in params]
282 all_es[name][node_uuid].append((path, status, diagnose, params))
283 return all_es
284
286 """Computes the list of nodes and their attributes.
287
288 """
289
290 assert not (compat.any(lu.glm.is_owned(level)
291 for level in locking.LEVELS
292 if level != locking.LEVEL_CLUSTER) or
293 self.do_locking or self.use_locking)
294
295 valid_nodes = [node.uuid
296 for node in lu.cfg.GetAllNodesInfo().values()
297 if not node.offline and node.vm_capable]
298 pol = self._DiagnoseByProvider(lu.rpc.call_extstorage_diagnose(valid_nodes))
299
300 data = {}
301
302 nodegroup_list = lu.cfg.GetNodeGroupList()
303
304 for (es_name, es_data) in pol.items():
305
306
307
308
309
310
311 ndgrp_data = {}
312 for nodegroup in nodegroup_list:
313 ndgrp = lu.cfg.GetNodeGroup(nodegroup)
314
315 nodegroup_nodes = ndgrp.members
316 nodegroup_name = ndgrp.name
317 node_statuses = []
318
319 for node in nodegroup_nodes:
320 if node in valid_nodes:
321 if es_data[node] != []:
322 node_status = es_data[node][0][1]
323 node_statuses.append(node_status)
324 else:
325 node_statuses.append(False)
326
327 if False in node_statuses:
328 ndgrp_data[nodegroup_name] = False
329 else:
330 ndgrp_data[nodegroup_name] = True
331
332
333 parameters = set()
334 for idx, esl in enumerate(es_data.values()):
335 valid = bool(esl and esl[0][1])
336 if not valid:
337 break
338
339 node_params = esl[0][3]
340 if idx == 0:
341
342 parameters.update(node_params)
343 else:
344
345 parameters.intersection_update(node_params)
346
347 params = list(parameters)
348
349
350 info = query.ExtStorageInfo(name=es_name, node_status=es_data,
351 nodegroup_status=ndgrp_data,
352 parameters=params)
353
354 data[es_name] = info
355
356
357 return [data[name] for name in self._GetNames(lu, pol.keys(), None)
358 if name in data]
359
362 """Logical unit for ExtStorage diagnose/query.
363
364 """
365 REQ_BGL = False
366
370
373
374 - def Exec(self, feedback_fn):
376
379 """Logical unit for executing restricted commands.
380
381 """
382 REQ_BGL = False
383
385 if self.op.nodes:
386 (self.op.node_uuids, self.op.nodes) = GetWantedNodes(self, self.op.nodes)
387
388 self.needed_locks = {
389 locking.LEVEL_NODE: self.op.node_uuids,
390 }
391 self.share_locks = {
392 locking.LEVEL_NODE: not self.op.use_locking,
393 }
394
396 """Check prerequisites.
397
398 """
399
400 - def Exec(self, feedback_fn):
401 """Execute restricted command and return output.
402
403 """
404 owned_nodes = frozenset(self.owned_locks(locking.LEVEL_NODE))
405
406
407 assert set(self.op.node_uuids).issubset(owned_nodes)
408
409 rpcres = self.rpc.call_restricted_command(self.op.node_uuids,
410 self.op.command)
411
412 result = []
413
414 for node_uuid in self.op.node_uuids:
415 nres = rpcres[node_uuid]
416 if nres.fail_msg:
417 msg = ("Command '%s' on node '%s' failed: %s" %
418 (self.op.command, self.cfg.GetNodeName(node_uuid),
419 nres.fail_msg))
420 result.append((False, msg))
421 else:
422 result.append((True, nres.payload))
423
424 return result
425