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