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