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 """Wrapper class for wconfd client calls from LUs.
67
68 Correctly updates the cache of the LU's owned locks
69 when leaving. Also transparently adds the context
70 for resource requests.
71
72 """
75
80
85
90
91
93 """Logical Unit base class.
94
95 Subclasses must follow these rules:
96 - implement ExpandNames
97 - implement CheckPrereq (except when tasklets are used)
98 - implement Exec (except when tasklets are used)
99 - implement BuildHooksEnv
100 - implement BuildHooksNodes
101 - redefine HPATH and HTYPE
102 - optionally redefine their run requirements:
103 REQ_BGL: the LU needs to hold the Big Ganeti Lock exclusively
104
105 Note that all commands require root permissions.
106
107 @ivar dry_run_result: the value (if any) that will be returned to the caller
108 in dry-run mode (signalled by opcode dry_run parameter)
109
110 """
111
112
113
114 HPATH = None
115 HTYPE = None
116 REQ_BGL = True
117
118 - def __init__(self, processor, op, cfg,
119 rpc_runner, wconfdcontext, wconfd):
120 """Constructor for LogicalUnit.
121
122 This needs to be overridden in derived classes in order to check op
123 validity.
124
125 @type wconfdcontext: (int, string)
126 @param wconfdcontext: the identity of the logical unit to represent itself
127 to wconfd when asking for resources; it is given as job id and livelock
128 file.
129 @param wconfd: the wconfd class to use; dependency injection to allow
130 testability.
131
132 """
133 self.proc = processor
134 self.op = op
135 self.cfg = cfg
136 self.wconfdlocks = []
137 self.wconfdcontext = wconfdcontext
138 self.rpc = rpc_runner
139 self.wconfd = wconfd
140
141
142 self.needed_locks = None
143 self.share_locks = dict.fromkeys(locking.LEVELS, 0)
144 self.opportunistic_locks = dict.fromkeys(locking.LEVELS, False)
145 self.opportunistic_locks_count = dict.fromkeys(locking.LEVELS, 1)
146 self.dont_collate_locks = dict.fromkeys(locking.LEVELS, False)
147
148 self.add_locks = {}
149
150
151 self.recalculate_locks = {}
152
153
154 self.Log = processor.Log
155 self.LogWarning = processor.LogWarning
156 self.LogInfo = processor.LogInfo
157 self.LogStep = processor.LogStep
158
159 self.dry_run_result = None
160
161 if (not hasattr(self.op, "debug_level") or
162 not isinstance(self.op.debug_level, int)):
163 self.op.debug_level = 0
164
165
166 self.tasklets = None
167
168
169 self.op.Validate(True)
170
171 self.CheckArguments()
172
175
200
202 """Return a request to release the specified locks of the given level.
203
204 Correctly break up the group lock to do so.
205
206 """
207 levelprefix = "%s/" % (locking.LEVEL_NAMES[level],)
208 release = [[levelprefix + lock, "release"] for lock in names]
209
210
211 setlock = levelprefix + locking.LOCKSET_NAME
212 if [setlock, "exclusive"] in self.wconfdlocks:
213 owned = self.owned_locks(level)
214 request = [[levelprefix + lock, "exclusive"]
215 for lock in owned
216 if lock not in names]
217 elif [setlock, "shared"] in self.wconfdlocks:
218 owned = self.owned_locks(level)
219 request = [[levelprefix + lock, "shared"]
220 for lock in owned
221 if lock not in names]
222 else:
223 request = []
224
225 return release + [[setlock, "release"]] + request
226
228 """Check syntactic validity for the opcode arguments.
229
230 This method is for doing a simple syntactic check and ensure
231 validity of opcode parameters, without any cluster-related
232 checks. While the same can be accomplished in ExpandNames and/or
233 CheckPrereq, doing these separate is better because:
234
235 - ExpandNames is left as as purely a lock-related function
236 - CheckPrereq is run after we have acquired locks (and possible
237 waited for them)
238
239 The function is allowed to change the self.op attribute so that
240 later methods can no longer worry about missing parameters.
241
242 """
243 pass
244
246 """Expand names for this LU.
247
248 This method is called before starting to execute the opcode, and it should
249 update all the parameters of the opcode to their canonical form (e.g. a
250 short node name must be fully expanded after this method has successfully
251 completed). This way locking, hooks, logging, etc. can work correctly.
252
253 LUs which implement this method must also populate the self.needed_locks
254 member, as a dict with lock levels as keys, and a list of needed lock names
255 as values. Rules:
256
257 - use an empty dict if you don't need any lock
258 - if you don't need any lock at a particular level omit that
259 level (note that in this case C{DeclareLocks} won't be called
260 at all for that level)
261 - if you need locks at a level, but you can't calculate it in
262 this function, initialise that level with an empty list and do
263 further processing in L{LogicalUnit.DeclareLocks} (see that
264 function's docstring)
265 - don't put anything for the BGL level
266 - if you want all locks at a level use L{locking.ALL_SET} as a value
267
268 If you need to share locks (rather than acquire them exclusively) at one
269 level you can modify self.share_locks, setting a true value (usually 1) for
270 that level. By default locks are not shared.
271
272 This function can also define a list of tasklets, which then will be
273 executed in order instead of the usual LU-level CheckPrereq and Exec
274 functions, if those are not defined by the LU.
275
276 Examples::
277
278 # Acquire all nodes and one instance
279 self.needed_locks = {
280 locking.LEVEL_NODE: locking.ALL_SET,
281 locking.LEVEL_INSTANCE: ['instance1.example.com'],
282 }
283 # Acquire just two nodes
284 self.needed_locks = {
285 locking.LEVEL_NODE: ['node1-uuid', 'node2-uuid'],
286 }
287 # Acquire no locks
288 self.needed_locks = {} # No, you can't leave it to the default value None
289
290 """
291
292
293
294 if self.REQ_BGL:
295 self.needed_locks = {}
296 else:
297 raise NotImplementedError
298
300 """Declare LU locking needs for a level
301
302 While most LUs can just declare their locking needs at ExpandNames time,
303 sometimes there's the need to calculate some locks after having acquired
304 the ones before. This function is called just before acquiring locks at a
305 particular level, but after acquiring the ones at lower levels, and permits
306 such calculations. It can be used to modify self.needed_locks, and by
307 default it does nothing.
308
309 This function is only called if you have something already set in
310 self.needed_locks for the level.
311
312 @param level: Locking level which is going to be locked
313 @type level: member of L{ganeti.locking.LEVELS}
314
315 """
316
318 """Check prerequisites for this LU.
319
320 This method should check that the prerequisites for the execution
321 of this LU are fulfilled. It can do internode communication, but
322 it should be idempotent - no cluster or system changes are
323 allowed.
324
325 The method should raise errors.OpPrereqError in case something is
326 not fulfilled. Its return value is ignored.
327
328 This method should also update all the parameters of the opcode to
329 their canonical form if it hasn't been done by ExpandNames before.
330
331 """
332 if self.tasklets is not None:
333 for (idx, tl) in enumerate(self.tasklets):
334 logging.debug("Checking prerequisites for tasklet %s/%s",
335 idx + 1, len(self.tasklets))
336 tl.CheckPrereq()
337 else:
338 pass
339
340 - def Exec(self, feedback_fn):
341 """Execute the LU.
342
343 This method should implement the actual work. It should raise
344 errors.OpExecError for failures that are somewhat dealt with in
345 code, or expected.
346
347 """
348 if self.tasklets is not None:
349 for (idx, tl) in enumerate(self.tasklets):
350 logging.debug("Executing tasklet %s/%s", idx + 1, len(self.tasklets))
351 tl.Exec(feedback_fn)
352 else:
353 raise NotImplementedError
354
356 """Prepare the LU to run again.
357
358 This method is called if the Exec failed for temporarily lacking resources.
359 It is expected to change the state of the LU so that it can be tried again,
360 and also change its locking policy to acquire more resources to have a
361 better chance of suceeding in the retry.
362
363 """
364
365 raise errors.OpRetryNotSupportedError()
366
368 """Build hooks environment for this LU.
369
370 @rtype: dict
371 @return: Dictionary containing the environment that will be used for
372 running the hooks for this LU. The keys of the dict must not be prefixed
373 with "GANETI_"--that'll be added by the hooks runner. The hooks runner
374 will extend the environment with additional variables. If no environment
375 should be defined, an empty dictionary should be returned (not C{None}).
376 @note: If the C{HPATH} attribute of the LU class is C{None}, this function
377 will not be called.
378
379 """
380 raise NotImplementedError
381
383 """Build list of nodes to run LU's hooks.
384
385 @rtype: tuple; (list, list)
386 @return: Tuple containing a list of node UUIDs on which the hook
387 should run before the execution and a list of node UUIDs on which the
388 hook should run after the execution.
389 No nodes should be returned as an empty list (and not None).
390 @note: If the C{HPATH} attribute of the LU class is C{None}, this function
391 will not be called.
392
393 """
394 raise NotImplementedError
395
396 - def PreparePostHookNodes(self, post_hook_node_uuids):
397 """Extend list of nodes to run the post LU hook.
398
399 This method allows LUs to change the list of node UUIDs on which the
400 post hook should run after the LU has been executed but before the post
401 hook is run.
402
403 @type post_hook_node_uuids: list
404 @param post_hook_node_uuids: The initial list of node UUIDs to run the
405 post hook on, as returned by L{BuildHooksNodes}.
406 @rtype: list
407 @return: list of node UUIDs on which the post hook should run. The default
408 implementation returns the passed in C{post_hook_node_uuids}, but
409 custom implementations can choose to alter the list.
410
411 """
412
413
414
415 return post_hook_node_uuids
416
417 - def HooksCallBack(self, phase, hook_results, feedback_fn, lu_result):
418 """Notify the LU about the results of its hooks.
419
420 This method is called every time a hooks phase is executed, and notifies
421 the Logical Unit about the hooks' result. The LU can then use it to alter
422 its result based on the hooks. By default the method does nothing and the
423 previous result is passed back unchanged but any LU can define it if it
424 wants to use the local cluster hook-scripts somehow.
425
426 @param phase: one of L{constants.HOOKS_PHASE_POST} or
427 L{constants.HOOKS_PHASE_PRE}; it denotes the hooks phase
428 @param hook_results: the results of the multi-node hooks rpc call
429 @param feedback_fn: function used send feedback back to the caller
430 @param lu_result: the previous Exec result this LU had, or None
431 in the PRE phase
432 @return: the new Exec result, based on the previous result
433 and hook results
434
435 """
436
437
438
439 return lu_result
440
442 """Called when the hooks get aborted by an exception.
443
444 This method is called everytime a hooks phase is aborted by an exception.
445 This exception is most likely of type C{errors.HooksAbort}. However, we
446 keep the design of this function broad enough to handle any kind of
447 exception.
448
449 The intended purpose of this call back is to run any action that is
450 necessary to bring the cluster back to a clean state from the point
451 in time before calling the hook.
452
453 @type phase: string
454 @param phase: one of L{constants.HOOKS_PHASE_POST} or
455 L{constants.HOOKS_PHASE_PRE}; it denotes the hooks phase
456 @type feedback_fn: callable
457 @param feedback_fn: function used send feedback back to the caller
458 @type exception: Exception
459 @param exception: The exception that was raised during the execution of
460 hooks.
461
462 """
463 pass
464
466 """Helper function to expand and lock an instance.
467
468 Many LUs that work on an instance take its name in self.op.instance_name
469 and need to expand it and then declare the expanded name for locking. This
470 function does it, and then updates self.op.instance_name to the expanded
471 name. It also initializes needed_locks as a dict, if this hasn't been done
472 before.
473
474 @param allow_forthcoming: if True, do not insist that the intsance be real;
475 the default behaviour is to raise a prerequisite error if the specified
476 instance is forthcoming.
477
478 """
479 if self.needed_locks is None:
480 self.needed_locks = {}
481 else:
482 assert locking.LEVEL_INSTANCE not in self.needed_locks, \
483 "_ExpandAndLockInstance called with instance-level locks set"
484 (self.op.instance_uuid, self.op.instance_name) = \
485 ExpandInstanceUuidAndName(self.cfg, self.op.instance_uuid,
486 self.op.instance_name)
487 self.needed_locks[locking.LEVEL_INSTANCE] = self.op.instance_name
488 if not allow_forthcoming:
489 if self.cfg.GetInstanceInfo(self.op.instance_uuid).forthcoming:
490 raise errors.OpPrereqError(
491 "forthcoming instances not supported for this operation")
492
495 """Helper function to declare instances' nodes for locking.
496
497 This function should be called after locking one or more instances to lock
498 their nodes. Its effect is populating self.needed_locks[locking.LEVEL_NODE]
499 with all primary or secondary nodes for instances already locked and
500 present in self.needed_locks[locking.LEVEL_INSTANCE].
501
502 It should be called from DeclareLocks, and for safety only works if
503 self.recalculate_locks[locking.LEVEL_NODE] is set.
504
505 In the future it may grow parameters to just lock some instance's nodes, or
506 to just lock primaries or secondary nodes, if needed.
507
508 If should be called in DeclareLocks in a way similar to::
509
510 if level == locking.LEVEL_NODE:
511 self._LockInstancesNodes()
512
513 @type primary_only: boolean
514 @param primary_only: only lock primary nodes of locked instances
515 @param level: Which lock level to use for locking nodes
516
517 """
518 assert level in self.recalculate_locks, \
519 "_LockInstancesNodes helper function called with no nodes to recalculate"
520
521
522
523
524
525
526 wanted_node_uuids = []
527 locked_i = self.owned_locks(locking.LEVEL_INSTANCE)
528 for _, instance in self.cfg.GetMultiInstanceInfoByName(locked_i):
529 wanted_node_uuids.append(instance.primary_node)
530 if not primary_only:
531 wanted_node_uuids.extend(
532 self.cfg.GetInstanceSecondaryNodes(instance.uuid))
533
534 if self.recalculate_locks[level] == constants.LOCKS_REPLACE:
535 self.needed_locks[level] = wanted_node_uuids
536 elif self.recalculate_locks[level] == constants.LOCKS_APPEND:
537 self.needed_locks[level].extend(wanted_node_uuids)
538 else:
539 raise errors.ProgrammerError("Unknown recalculation mode")
540
541 del self.recalculate_locks[level]
542
544 """Raise AssertionError if the LU holds some locks of the given level.
545
546 """
547 assert not self.owned_locks(level)
548
549
551 """Simple LU which runs no hooks.
552
553 This LU is intended as a parent for other LogicalUnits which will
554 run no hooks, in order to reduce duplicate code.
555
556 """
557 HPATH = None
558 HTYPE = None
559
561 """Empty BuildHooksEnv for NoHooksLu.
562
563 This just raises an error.
564
565 """
566 raise AssertionError("BuildHooksEnv called for NoHooksLUs")
567
569 """Empty BuildHooksNodes for NoHooksLU.
570
571 """
572 raise AssertionError("BuildHooksNodes called for NoHooksLU")
573
574 - def PreparePostHookNodes(self, post_hook_node_uuids):
575 """Empty PreparePostHookNodes for NoHooksLU.
576
577 """
578 raise AssertionError("PreparePostHookNodes called for NoHooksLU")
579
580
582 """Tasklet base class.
583
584 Tasklets are subcomponents for LUs. LUs can consist entirely of tasklets or
585 they can mix legacy code with tasklets. Locking needs to be done in the LU,
586 tasklets know nothing about locks.
587
588 Subclasses must follow these rules:
589 - Implement CheckPrereq
590 - Implement Exec
591
592 """
594 self.lu = lu
595
596
597 self.cfg = lu.cfg
598 self.rpc = lu.rpc
599
601 """Check prerequisites for this tasklets.
602
603 This method should check whether the prerequisites for the execution of
604 this tasklet are fulfilled. It can do internode communication, but it
605 should be idempotent - no cluster or system changes are allowed.
606
607 The method should raise errors.OpPrereqError in case something is not
608 fulfilled. Its return value is ignored.
609
610 This method should also update all parameters to their canonical form if it
611 hasn't been done before.
612
613 """
614 pass
615
616 - def Exec(self, feedback_fn):
617 """Execute the tasklet.
618
619 This method should implement the actual work. It should raise
620 errors.OpExecError for failures that are somewhat dealt with in code, or
621 expected.
622
623 """
624 raise NotImplementedError
625
626
628 """Base for query utility classes.
629
630 """
631
632 FIELDS = None
633
634
635 SORT_FIELD = "name"
636
637 - def __init__(self, qfilter, fields, use_locking):
638 """Initializes this class.
639
640 """
641 self.use_locking = use_locking
642
643 self.query = query.Query(self.FIELDS, fields, qfilter=qfilter,
644 namefield=self.SORT_FIELD)
645 self.requested_data = self.query.RequestedData()
646 self.names = self.query.RequestedNames()
647
648
649 self.sort_by_name = not self.names
650
651 self.do_locking = None
652 self.wanted = None
653
654 - def _GetNames(self, lu, all_names, lock_level):
655 """Helper function to determine names asked for in the query.
656
657 """
658 if self.do_locking:
659 names = lu.owned_locks(lock_level)
660 else:
661 names = all_names
662
663 if self.wanted == locking.ALL_SET:
664 assert not self.names
665
666 return utils.NiceSort(names)
667
668
669 assert self.names
670
671 missing = set(self.wanted).difference(names)
672 if missing:
673 raise errors.OpExecError("Some items were removed before retrieving"
674 " their data: %s" % missing)
675
676
677 return self.wanted
678
680 """Expand names for this query.
681
682 See L{LogicalUnit.ExpandNames}.
683
684 """
685 raise NotImplementedError()
686
688 """Declare locks for this query.
689
690 See L{LogicalUnit.DeclareLocks}.
691
692 """
693 raise NotImplementedError()
694
696 """Collects all data for this query.
697
698 @return: Query data object
699
700 """
701 raise NotImplementedError()
702
709
716