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