171 |
171 |
if op.WITH_LU)
|
172 |
172 |
|
173 |
173 |
|
|
174 |
def _RpcResultsToHooksResults(rpc_results):
|
|
175 |
"""Function to convert RPC results to the format expected by HooksMaster.
|
|
176 |
|
|
177 |
@type rpc_results: dict(node: L{rpc.RpcResult})
|
|
178 |
@param rpc_results: RPC results
|
|
179 |
@rtype: dict(node: (fail_msg, offline, hooks_results))
|
|
180 |
@return: RPC results unpacked according to the format expected by
|
|
181 |
L({mcpu.HooksMaster}
|
|
182 |
|
|
183 |
"""
|
|
184 |
return dict((node, (rpc_res.fail_msg, rpc_res.offline, rpc_res.payload))
|
|
185 |
for (node, rpc_res) in rpc_results.items())
|
|
186 |
|
|
187 |
|
174 |
188 |
class Processor(object):
|
175 |
189 |
"""Object which runs OpCodes"""
|
176 |
190 |
DISPATCH_TABLE = _ComputeDispatchTable()
|
... | ... | |
242 |
256 |
"""
|
243 |
257 |
write_count = self.context.cfg.write_count
|
244 |
258 |
lu.CheckPrereq()
|
245 |
|
hm = HooksMaster(self.rpc.call_hooks_runner, lu)
|
|
259 |
|
|
260 |
hm = self.BuildHooksManager(lu)
|
246 |
261 |
h_results = hm.RunPhase(constants.HOOKS_PHASE_PRE)
|
247 |
262 |
lu.HooksCallBack(constants.HOOKS_PHASE_PRE, h_results,
|
248 |
263 |
self.Log, None)
|
... | ... | |
267 |
282 |
|
268 |
283 |
return result
|
269 |
284 |
|
|
285 |
def BuildHooksManager(self, lu):
|
|
286 |
return self.hmclass.BuildFromLu(lu.rpc.call_hooks_runner, lu)
|
|
287 |
|
270 |
288 |
def _LockAndExecLU(self, lu, level, calc_timeout, priority):
|
271 |
289 |
"""Execute a Logical Unit, with the needed locks.
|
272 |
290 |
|
... | ... | |
444 |
462 |
|
445 |
463 |
|
446 |
464 |
class HooksMaster(object):
|
447 |
|
"""Hooks master.
|
448 |
|
|
449 |
|
This class distributes the run commands to the nodes based on the
|
450 |
|
specific LU class.
|
|
465 |
def __init__(self, opcode, hooks_path, nodes, hooks_execution_fn,
|
|
466 |
hooks_results_adapt_fn, build_env_fn, log_fn, htype=None, cluster_name=None,
|
|
467 |
master_name=None):
|
|
468 |
"""Base class for hooks masters.
|
|
469 |
|
|
470 |
This class invokes the execution of hooks according to the behaviour
|
|
471 |
specified by its parameters.
|
|
472 |
|
|
473 |
@type opcode: string
|
|
474 |
@param opcode: opcode of the operation to which the hooks are tied
|
|
475 |
@type hooks_path: string
|
|
476 |
@param hooks_path: prefix of the hooks directories
|
|
477 |
@type nodes: 2-tuple of lists
|
|
478 |
@param nodes: 2-tuple of lists containing nodes on which pre-hooks must be
|
|
479 |
run and nodes on which post-hooks must be run
|
|
480 |
@type hooks_execution_fn: function that accepts the following parameters:
|
|
481 |
(node_list, hooks_path, phase, environment)
|
|
482 |
@param hooks_execution_fn: function that will execute the hooks; can be
|
|
483 |
None, indicating that no conversion is necessary.
|
|
484 |
@type hooks_results_adapt_fn: function
|
|
485 |
@param hooks_results_adapt_fn: function that will adapt the return value of
|
|
486 |
hooks_execution_fn to the format expected by RunPhase
|
|
487 |
@type build_env_fn: function that returns a dictionary having strings as
|
|
488 |
keys
|
|
489 |
@param build_env_fn: function that builds the environment for the hooks
|
|
490 |
@type log_fn: function that accepts a string
|
|
491 |
@param log_fn: logging function
|
|
492 |
@type htype: string or None
|
|
493 |
@param htype: None or one of L{constants.HTYPE_CLUSTER},
|
|
494 |
L{constants.HTYPE_NODE}, L{constants.HTYPE_INSTANCE}
|
|
495 |
@type cluster_name: string
|
|
496 |
@param cluster_name: name of the cluster
|
|
497 |
@type master_name: string
|
|
498 |
@param master_name: name of the master
|
451 |
499 |
|
452 |
|
In order to remove the direct dependency on the rpc module, the
|
453 |
|
constructor needs a function which actually does the remote
|
454 |
|
call. This will usually be rpc.call_hooks_runner, but any function
|
455 |
|
which behaves the same works.
|
|
500 |
"""
|
|
501 |
self.opcode = opcode
|
|
502 |
self.hooks_path = hooks_path
|
|
503 |
self.hooks_execution_fn = hooks_execution_fn
|
|
504 |
self.hooks_results_adapt_fn = hooks_results_adapt_fn
|
|
505 |
self.build_env_fn = build_env_fn
|
|
506 |
self.log_fn = log_fn
|
|
507 |
self.htype = htype
|
|
508 |
self.cluster_name = cluster_name
|
|
509 |
self.master_name = master_name
|
456 |
510 |
|
457 |
|
"""
|
458 |
|
def __init__(self, callfn, lu):
|
459 |
|
self.callfn = callfn
|
460 |
|
self.lu = lu
|
461 |
|
self.op = lu.op
|
462 |
511 |
self.pre_env = self._BuildEnv(constants.HOOKS_PHASE_PRE)
|
463 |
|
|
464 |
|
if self.lu.HPATH is None:
|
465 |
|
nodes = (None, None)
|
466 |
|
else:
|
467 |
|
nodes = map(frozenset, self.lu.BuildHooksNodes())
|
468 |
|
|
469 |
512 |
(self.pre_nodes, self.post_nodes) = nodes
|
470 |
513 |
|
471 |
514 |
def _BuildEnv(self, phase):
|
... | ... | |
484 |
527 |
|
485 |
528 |
env = {}
|
486 |
529 |
|
487 |
|
if self.lu.HPATH is not None:
|
488 |
|
lu_env = self.lu.BuildHooksEnv()
|
489 |
|
if lu_env:
|
490 |
|
assert not compat.any(key.upper().startswith(prefix) for key in lu_env)
|
|
530 |
if self.hooks_path is not None:
|
|
531 |
phase_env = self.build_env_fn()
|
|
532 |
if phase_env:
|
|
533 |
assert not compat.any(key.upper().startswith(prefix)
|
|
534 |
for key in phase_env)
|
491 |
535 |
env.update(("%s%s" % (prefix, key), value)
|
492 |
|
for (key, value) in lu_env.items())
|
|
536 |
for (key, value) in phase_env.items())
|
493 |
537 |
|
494 |
538 |
if phase == constants.HOOKS_PHASE_PRE:
|
495 |
539 |
assert compat.all((key.startswith("GANETI_") and
|
... | ... | |
512 |
556 |
def _RunWrapper(self, node_list, hpath, phase, phase_env):
|
513 |
557 |
"""Simple wrapper over self.callfn.
|
514 |
558 |
|
515 |
|
This method fixes the environment before doing the rpc call.
|
|
559 |
This method fixes the environment before executing the hooks.
|
516 |
560 |
|
517 |
561 |
"""
|
518 |
|
cfg = self.lu.cfg
|
519 |
|
|
520 |
562 |
env = {
|
521 |
563 |
"PATH": "/sbin:/bin:/usr/sbin:/usr/bin",
|
522 |
564 |
"GANETI_HOOKS_VERSION": constants.HOOKS_VERSION,
|
523 |
|
"GANETI_OP_CODE": self.op.OP_ID,
|
|
565 |
"GANETI_OP_CODE": self.opcode,
|
524 |
566 |
"GANETI_DATA_DIR": constants.DATA_DIR,
|
525 |
567 |
"GANETI_HOOKS_PHASE": phase,
|
526 |
568 |
"GANETI_HOOKS_PATH": hpath,
|
527 |
569 |
}
|
528 |
570 |
|
529 |
|
if self.lu.HTYPE:
|
530 |
|
env["GANETI_OBJECT_TYPE"] = self.lu.HTYPE
|
|
571 |
if self.htype:
|
|
572 |
env["GANETI_OBJECT_TYPE"] = self.htype
|
|
573 |
|
|
574 |
if self.cluster_name is not None:
|
|
575 |
env["GANETI_CLUSTER"] = self.cluster_name
|
531 |
576 |
|
532 |
|
if cfg is not None:
|
533 |
|
env["GANETI_CLUSTER"] = cfg.GetClusterName()
|
534 |
|
env["GANETI_MASTER"] = cfg.GetMasterNode()
|
|
577 |
if self.master_name is not None:
|
|
578 |
env["GANETI_MASTER"] = self.master_name
|
535 |
579 |
|
536 |
580 |
if phase_env:
|
537 |
581 |
env = utils.algo.JoinDisjointDicts(env, phase_env)
|
... | ... | |
542 |
586 |
assert compat.all(key == "PATH" or key.startswith("GANETI_")
|
543 |
587 |
for key in env)
|
544 |
588 |
|
545 |
|
return self.callfn(node_list, hpath, phase, env)
|
|
589 |
return self.hooks_execution_fn(node_list, hpath, phase, env)
|
546 |
590 |
|
547 |
591 |
def RunPhase(self, phase, nodes=None):
|
548 |
592 |
"""Run all the scripts for a phase.
|
549 |
593 |
|
550 |
594 |
This is the main function of the HookMaster.
|
|
595 |
It executes self.hooks_execution_fn, and after running
|
|
596 |
self.hooks_results_adapt_fn on its results it expects them to be in the form
|
|
597 |
{node_name: (fail_msg, [(script, result, output), ...]}).
|
551 |
598 |
|
552 |
599 |
@param phase: one of L{constants.HOOKS_PHASE_POST} or
|
553 |
600 |
L{constants.HOOKS_PHASE_PRE}; it denotes the hooks phase
|
... | ... | |
574 |
621 |
# even attempt to run, or this LU doesn't do hooks at all
|
575 |
622 |
return
|
576 |
623 |
|
577 |
|
results = self._RunWrapper(nodes, self.lu.HPATH, phase, env)
|
|
624 |
results = self._RunWrapper(nodes, self.hooks_path, phase, env)
|
578 |
625 |
if not results:
|
579 |
626 |
msg = "Communication Failure"
|
580 |
627 |
if phase == constants.HOOKS_PHASE_PRE:
|
581 |
628 |
raise errors.HooksFailure(msg)
|
582 |
629 |
else:
|
583 |
|
self.lu.LogWarning(msg)
|
|
630 |
self.log_fn(msg)
|
584 |
631 |
return results
|
585 |
632 |
|
|
633 |
converted_res = results
|
|
634 |
if self.hooks_results_adapt_fn:
|
|
635 |
converted_res = self.hooks_results_adapt_fn(results)
|
|
636 |
|
586 |
637 |
errs = []
|
587 |
|
for node_name in results:
|
588 |
|
res = results[node_name]
|
589 |
|
if res.offline:
|
|
638 |
for node_name, (fail_msg, offline, hooks_results) in converted_res.items():
|
|
639 |
if offline:
|
590 |
640 |
continue
|
591 |
641 |
|
592 |
|
msg = res.fail_msg
|
593 |
|
if msg:
|
594 |
|
self.lu.LogWarning("Communication failure to node %s: %s",
|
595 |
|
node_name, msg)
|
|
642 |
if fail_msg:
|
|
643 |
self.log_fn("Communication failure to node %s: %s", node_name, fail_msg)
|
596 |
644 |
continue
|
597 |
645 |
|
598 |
|
for script, hkr, output in res.payload:
|
|
646 |
for script, hkr, output in hooks_results:
|
599 |
647 |
if hkr == constants.HKR_FAIL:
|
600 |
648 |
if phase == constants.HOOKS_PHASE_PRE:
|
601 |
649 |
errs.append((node_name, script, output))
|
602 |
650 |
else:
|
603 |
651 |
if not output:
|
604 |
652 |
output = "(no output)"
|
605 |
|
self.lu.LogWarning("On %s script %s failed, output: %s" %
|
606 |
|
(node_name, script, output))
|
|
653 |
self.log_fn("On %s script %s failed, output: %s" %
|
|
654 |
(node_name, script, output))
|
607 |
655 |
|
608 |
656 |
if errs and phase == constants.HOOKS_PHASE_PRE:
|
609 |
657 |
raise errors.HooksAbort(errs)
|
... | ... | |
619 |
667 |
"""
|
620 |
668 |
phase = constants.HOOKS_PHASE_POST
|
621 |
669 |
hpath = constants.HOOKS_NAME_CFGUPDATE
|
622 |
|
nodes = [self.lu.cfg.GetMasterNode()]
|
|
670 |
nodes = [self.master_name]
|
623 |
671 |
self._RunWrapper(nodes, hpath, phase, self.pre_env)
|
|
672 |
|
|
673 |
@staticmethod
|
|
674 |
def BuildFromLu(hooks_execution_fn, lu):
|
|
675 |
if lu.HPATH is None:
|
|
676 |
nodes = (None, None)
|
|
677 |
else:
|
|
678 |
nodes = map(frozenset, lu.BuildHooksNodes())
|
|
679 |
|
|
680 |
master_name = cluster_name = None
|
|
681 |
if lu.cfg:
|
|
682 |
master_name = lu.cfg.GetMasterNode()
|
|
683 |
cluster_name = lu.cfg.GetClusterName()
|
|
684 |
|
|
685 |
return HooksMaster(lu.op.OP_ID, lu.HPATH, nodes, hooks_execution_fn,
|
|
686 |
_RpcResultsToHooksResults, lu.BuildHooksEnv,
|
|
687 |
lu.LogWarning, lu.HTYPE, cluster_name, master_name)
|