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