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 """Base classes and functions for cmdlib."""
32
33 import logging
34
35 from ganeti import errors
36 from ganeti import constants
37 from ganeti import locking
38 from ganeti import query
39 from ganeti import utils
40 from ganeti.cmdlib.common import ExpandInstanceUuidAndName
41
42
44 """Data container for LU results with jobs.
45
46 Instances of this class returned from L{LogicalUnit.Exec} will be recognized
47 by L{mcpu._ProcessResult}. The latter will then submit the jobs
48 contained in the C{jobs} attribute and include the job IDs in the opcode
49 result.
50
51 """
53 """Initializes this class.
54
55 Additional return values can be specified as keyword arguments.
56
57 @type jobs: list of lists of L{opcode.OpCode}
58 @param jobs: A list of lists of opcode objects
59
60 """
61 self.jobs = jobs
62 self.other = kwargs
63
64
66 """Logical Unit base class.
67
68 Subclasses must follow these rules:
69 - implement ExpandNames
70 - implement CheckPrereq (except when tasklets are used)
71 - implement Exec (except when tasklets are used)
72 - implement BuildHooksEnv
73 - implement BuildHooksNodes
74 - redefine HPATH and HTYPE
75 - optionally redefine their run requirements:
76 REQ_BGL: the LU needs to hold the Big Ganeti Lock exclusively
77
78 Note that all commands require root permissions.
79
80 @ivar dry_run_result: the value (if any) that will be returned to the caller
81 in dry-run mode (signalled by opcode dry_run parameter)
82
83 """
84 HPATH = None
85 HTYPE = None
86 REQ_BGL = True
87
88 - def __init__(self, processor, op, context, rpc_runner):
89 """Constructor for LogicalUnit.
90
91 This needs to be overridden in derived classes in order to check op
92 validity.
93
94 """
95 self.proc = processor
96 self.op = op
97 self.cfg = context.cfg
98 self.glm = context.glm
99
100 self.owned_locks = context.glm.list_owned
101 self.context = context
102 self.rpc = rpc_runner
103
104
105 self.needed_locks = None
106 self.share_locks = dict.fromkeys(locking.LEVELS, 0)
107 self.opportunistic_locks = dict.fromkeys(locking.LEVELS, False)
108
109 self.add_locks = {}
110 self.remove_locks = {}
111
112
113 self.recalculate_locks = {}
114
115
116 self.Log = processor.Log
117 self.LogWarning = processor.LogWarning
118 self.LogInfo = processor.LogInfo
119 self.LogStep = processor.LogStep
120
121 self.dry_run_result = None
122
123 if (not hasattr(self.op, "debug_level") or
124 not isinstance(self.op.debug_level, int)):
125 self.op.debug_level = 0
126
127
128 self.tasklets = None
129
130
131 self.op.Validate(True)
132
133 self.CheckArguments()
134
136 """Check syntactic validity for the opcode arguments.
137
138 This method is for doing a simple syntactic check and ensure
139 validity of opcode parameters, without any cluster-related
140 checks. While the same can be accomplished in ExpandNames and/or
141 CheckPrereq, doing these separate is better because:
142
143 - ExpandNames is left as as purely a lock-related function
144 - CheckPrereq is run after we have acquired locks (and possible
145 waited for them)
146
147 The function is allowed to change the self.op attribute so that
148 later methods can no longer worry about missing parameters.
149
150 """
151 pass
152
154 """Expand names for this LU.
155
156 This method is called before starting to execute the opcode, and it should
157 update all the parameters of the opcode to their canonical form (e.g. a
158 short node name must be fully expanded after this method has successfully
159 completed). This way locking, hooks, logging, etc. can work correctly.
160
161 LUs which implement this method must also populate the self.needed_locks
162 member, as a dict with lock levels as keys, and a list of needed lock names
163 as values. Rules:
164
165 - use an empty dict if you don't need any lock
166 - if you don't need any lock at a particular level omit that
167 level (note that in this case C{DeclareLocks} won't be called
168 at all for that level)
169 - if you need locks at a level, but you can't calculate it in
170 this function, initialise that level with an empty list and do
171 further processing in L{LogicalUnit.DeclareLocks} (see that
172 function's docstring)
173 - don't put anything for the BGL level
174 - if you want all locks at a level use L{locking.ALL_SET} as a value
175
176 If you need to share locks (rather than acquire them exclusively) at one
177 level you can modify self.share_locks, setting a true value (usually 1) for
178 that level. By default locks are not shared.
179
180 This function can also define a list of tasklets, which then will be
181 executed in order instead of the usual LU-level CheckPrereq and Exec
182 functions, if those are not defined by the LU.
183
184 Examples::
185
186 # Acquire all nodes and one instance
187 self.needed_locks = {
188 locking.LEVEL_NODE: locking.ALL_SET,
189 locking.LEVEL_INSTANCE: ['instance1.example.com'],
190 }
191 # Acquire just two nodes
192 self.needed_locks = {
193 locking.LEVEL_NODE: ['node1-uuid', 'node2-uuid'],
194 }
195 # Acquire no locks
196 self.needed_locks = {} # No, you can't leave it to the default value None
197
198 """
199
200
201
202 if self.REQ_BGL:
203 self.needed_locks = {}
204 else:
205 raise NotImplementedError
206
208 """Declare LU locking needs for a level
209
210 While most LUs can just declare their locking needs at ExpandNames time,
211 sometimes there's the need to calculate some locks after having acquired
212 the ones before. This function is called just before acquiring locks at a
213 particular level, but after acquiring the ones at lower levels, and permits
214 such calculations. It can be used to modify self.needed_locks, and by
215 default it does nothing.
216
217 This function is only called if you have something already set in
218 self.needed_locks for the level.
219
220 @param level: Locking level which is going to be locked
221 @type level: member of L{ganeti.locking.LEVELS}
222
223 """
224
226 """Check prerequisites for this LU.
227
228 This method should check that the prerequisites for the execution
229 of this LU are fulfilled. It can do internode communication, but
230 it should be idempotent - no cluster or system changes are
231 allowed.
232
233 The method should raise errors.OpPrereqError in case something is
234 not fulfilled. Its return value is ignored.
235
236 This method should also update all the parameters of the opcode to
237 their canonical form if it hasn't been done by ExpandNames before.
238
239 """
240 if self.tasklets is not None:
241 for (idx, tl) in enumerate(self.tasklets):
242 logging.debug("Checking prerequisites for tasklet %s/%s",
243 idx + 1, len(self.tasklets))
244 tl.CheckPrereq()
245 else:
246 pass
247
248 - def Exec(self, feedback_fn):
249 """Execute the LU.
250
251 This method should implement the actual work. It should raise
252 errors.OpExecError for failures that are somewhat dealt with in
253 code, or expected.
254
255 """
256 if self.tasklets is not None:
257 for (idx, tl) in enumerate(self.tasklets):
258 logging.debug("Executing tasklet %s/%s", idx + 1, len(self.tasklets))
259 tl.Exec(feedback_fn)
260 else:
261 raise NotImplementedError
262
264 """Build hooks environment for this LU.
265
266 @rtype: dict
267 @return: Dictionary containing the environment that will be used for
268 running the hooks for this LU. The keys of the dict must not be prefixed
269 with "GANETI_"--that'll be added by the hooks runner. The hooks runner
270 will extend the environment with additional variables. If no environment
271 should be defined, an empty dictionary should be returned (not C{None}).
272 @note: If the C{HPATH} attribute of the LU class is C{None}, this function
273 will not be called.
274
275 """
276 raise NotImplementedError
277
279 """Build list of nodes to run LU's hooks.
280
281 @rtype: tuple; (list, list)
282 @return: Tuple containing a list of node UUIDs on which the hook
283 should run before the execution and a list of node UUIDs on which the
284 hook should run after the execution.
285 No nodes should be returned as an empty list (and not None).
286 @note: If the C{HPATH} attribute of the LU class is C{None}, this function
287 will not be called.
288
289 """
290 raise NotImplementedError
291
292 - def PreparePostHookNodes(self, post_hook_node_uuids):
293 """Extend list of nodes to run the post LU hook.
294
295 This method allows LUs to change the list of node UUIDs on which the
296 post hook should run after the LU has been executed but before the post
297 hook is run.
298
299 @type post_hook_node_uuids: list
300 @param post_hook_node_uuids: The initial list of node UUIDs to run the
301 post hook on, as returned by L{BuildHooksNodes}.
302 @rtype: list
303 @return: list of node UUIDs on which the post hook should run. The default
304 implementation returns the passed in C{post_hook_node_uuids}, but
305 custom implementations can choose to alter the list.
306
307 """
308
309
310
311 return post_hook_node_uuids
312
313 - def HooksCallBack(self, phase, hook_results, feedback_fn, lu_result):
314 """Notify the LU about the results of its hooks.
315
316 This method is called every time a hooks phase is executed, and notifies
317 the Logical Unit about the hooks' result. The LU can then use it to alter
318 its result based on the hooks. By default the method does nothing and the
319 previous result is passed back unchanged but any LU can define it if it
320 wants to use the local cluster hook-scripts somehow.
321
322 @param phase: one of L{constants.HOOKS_PHASE_POST} or
323 L{constants.HOOKS_PHASE_PRE}; it denotes the hooks phase
324 @param hook_results: the results of the multi-node hooks rpc call
325 @param feedback_fn: function used send feedback back to the caller
326 @param lu_result: the previous Exec result this LU had, or None
327 in the PRE phase
328 @return: the new Exec result, based on the previous result
329 and hook results
330
331 """
332
333
334
335 return lu_result
336
338 """Helper function to expand and lock an instance.
339
340 Many LUs that work on an instance take its name in self.op.instance_name
341 and need to expand it and then declare the expanded name for locking. This
342 function does it, and then updates self.op.instance_name to the expanded
343 name. It also initializes needed_locks as a dict, if this hasn't been done
344 before.
345
346 """
347 if self.needed_locks is None:
348 self.needed_locks = {}
349 else:
350 assert locking.LEVEL_INSTANCE not in self.needed_locks, \
351 "_ExpandAndLockInstance called with instance-level locks set"
352 (self.op.instance_uuid, self.op.instance_name) = \
353 ExpandInstanceUuidAndName(self.cfg, self.op.instance_uuid,
354 self.op.instance_name)
355 self.needed_locks[locking.LEVEL_INSTANCE] = self.op.instance_name
356
359 """Helper function to declare instances' nodes for locking.
360
361 This function should be called after locking one or more instances to lock
362 their nodes. Its effect is populating self.needed_locks[locking.LEVEL_NODE]
363 with all primary or secondary nodes for instances already locked and
364 present in self.needed_locks[locking.LEVEL_INSTANCE].
365
366 It should be called from DeclareLocks, and for safety only works if
367 self.recalculate_locks[locking.LEVEL_NODE] is set.
368
369 In the future it may grow parameters to just lock some instance's nodes, or
370 to just lock primaries or secondary nodes, if needed.
371
372 If should be called in DeclareLocks in a way similar to::
373
374 if level == locking.LEVEL_NODE:
375 self._LockInstancesNodes()
376
377 @type primary_only: boolean
378 @param primary_only: only lock primary nodes of locked instances
379 @param level: Which lock level to use for locking nodes
380
381 """
382 assert level in self.recalculate_locks, \
383 "_LockInstancesNodes helper function called with no nodes to recalculate"
384
385
386
387
388
389
390 wanted_node_uuids = []
391 locked_i = self.owned_locks(locking.LEVEL_INSTANCE)
392 for _, instance in self.cfg.GetMultiInstanceInfoByName(locked_i):
393 wanted_node_uuids.append(instance.primary_node)
394 if not primary_only:
395 wanted_node_uuids.extend(instance.secondary_nodes)
396
397 if self.recalculate_locks[level] == constants.LOCKS_REPLACE:
398 self.needed_locks[level] = wanted_node_uuids
399 elif self.recalculate_locks[level] == constants.LOCKS_APPEND:
400 self.needed_locks[level].extend(wanted_node_uuids)
401 else:
402 raise errors.ProgrammerError("Unknown recalculation mode")
403
404 del self.recalculate_locks[level]
405
406
408 """Simple LU which runs no hooks.
409
410 This LU is intended as a parent for other LogicalUnits which will
411 run no hooks, in order to reduce duplicate code.
412
413 """
414 HPATH = None
415 HTYPE = None
416
418 """Empty BuildHooksEnv for NoHooksLu.
419
420 This just raises an error.
421
422 """
423 raise AssertionError("BuildHooksEnv called for NoHooksLUs")
424
426 """Empty BuildHooksNodes for NoHooksLU.
427
428 """
429 raise AssertionError("BuildHooksNodes called for NoHooksLU")
430
431 - def PreparePostHookNodes(self, post_hook_node_uuids):
432 """Empty PreparePostHookNodes for NoHooksLU.
433
434 """
435 raise AssertionError("PreparePostHookNodes called for NoHooksLU")
436
437
439 """Tasklet base class.
440
441 Tasklets are subcomponents for LUs. LUs can consist entirely of tasklets or
442 they can mix legacy code with tasklets. Locking needs to be done in the LU,
443 tasklets know nothing about locks.
444
445 Subclasses must follow these rules:
446 - Implement CheckPrereq
447 - Implement Exec
448
449 """
451 self.lu = lu
452
453
454 self.cfg = lu.cfg
455 self.rpc = lu.rpc
456
458 """Check prerequisites for this tasklets.
459
460 This method should check whether the prerequisites for the execution of
461 this tasklet are fulfilled. It can do internode communication, but it
462 should be idempotent - no cluster or system changes are allowed.
463
464 The method should raise errors.OpPrereqError in case something is not
465 fulfilled. Its return value is ignored.
466
467 This method should also update all parameters to their canonical form if it
468 hasn't been done before.
469
470 """
471 pass
472
473 - def Exec(self, feedback_fn):
474 """Execute the tasklet.
475
476 This method should implement the actual work. It should raise
477 errors.OpExecError for failures that are somewhat dealt with in code, or
478 expected.
479
480 """
481 raise NotImplementedError
482
483
485 """Base for query utility classes.
486
487 """
488
489 FIELDS = None
490
491
492 SORT_FIELD = "name"
493
494 - def __init__(self, qfilter, fields, use_locking):
495 """Initializes this class.
496
497 """
498 self.use_locking = use_locking
499
500 self.query = query.Query(self.FIELDS, fields, qfilter=qfilter,
501 namefield=self.SORT_FIELD)
502 self.requested_data = self.query.RequestedData()
503 self.names = self.query.RequestedNames()
504
505
506 self.sort_by_name = not self.names
507
508 self.do_locking = None
509 self.wanted = None
510
511 - def _GetNames(self, lu, all_names, lock_level):
512 """Helper function to determine names asked for in the query.
513
514 """
515 if self.do_locking:
516 names = lu.owned_locks(lock_level)
517 else:
518 names = all_names
519
520 if self.wanted == locking.ALL_SET:
521 assert not self.names
522
523 return utils.NiceSort(names)
524
525
526 assert self.names
527 assert not self.do_locking or lu.glm.is_owned(lock_level)
528
529 missing = set(self.wanted).difference(names)
530 if missing:
531 raise errors.OpExecError("Some items were removed before retrieving"
532 " their data: %s" % missing)
533
534
535 return self.wanted
536
538 """Expand names for this query.
539
540 See L{LogicalUnit.ExpandNames}.
541
542 """
543 raise NotImplementedError()
544
546 """Declare locks for this query.
547
548 See L{LogicalUnit.DeclareLocks}.
549
550 """
551 raise NotImplementedError()
552
554 """Collects all data for this query.
555
556 @return: Query data object
557
558 """
559 raise NotImplementedError()
560
567
574