Revision 949dcb1d lib/mcpu.py
b/lib/mcpu.py | ||
---|---|---|
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) |
Also available in: Unified diff