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