Looks a lot better, just a few more minor things!

On Tue, Nov 24, 2015 at 2:45 PM, Oleg Ponomarev <[email protected]>
wrote:

> Interdiff for the hooksmaster:
>
> diff --git a/lib/hooksmaster.py b/lib/hooksmaster.py
> index d1b4adb..d2de4b8 100644
> --- a/lib/hooksmaster.py
> +++ b/lib/hooksmaster.py
> @@ -158,6 +158,17 @@ class HooksMaster(object):
>
>      return env
>
> +  def _CheckParamsAndExecHooks(self, node_list, hpath, phase, env):
>

While I'm ok with omitting docstring parameter descriptions for a helper
function invoked from a well-documented function, please add a one-line
description here (which will take three lines because of our style
restrictions).


> +    if node_list is None or not node_list:
> +      return
> +
> +    # Convert everything to strings
> +    env = dict([(str(key), str(val)) for key, val in env.iteritems()])
> +    assert compat.all(key == "PATH" or key.startswith("GANETI_")
> +                      for key in env)
> +
> +    return self.hooks_execution_fn(node_list, hpath, phase, env)
> +
>    def _RunWrapper(self, node_list, hpath, phase, phase_env,
> is_global=False,
>                    post_status=None):
>      """Simple wrapper over self.callfn.
> @@ -192,26 +203,18 @@ class HooksMaster(object):
>      if phase_env:
>        env = utils.algo.JoinDisjointDicts(env, phase_env)
>
> -    # Convert everything to strings
> -    env = dict([(str(key), str(val)) for key, val in env.iteritems()])
> -
> -    assert compat.all(key == "PATH" or key.startswith("GANETI_")
> -                      for key in env)
> +    if not is_global:
> +      return self._CheckParamsAndExecHooks(node_list, hpath, phase, env)
>
>

One-line comment here, on the lines of:
"For global hooks, we need to send different env values to master and the
others"


>      ret = dict()
> -    if is_global:
> -      env["GANETI_IS_MASTER"] = constants.GLOBAL_HOOKS_MASTER
> -      master_set = frozenset([self.master_uuid])
> -      ret.update(self.hooks_execution_fn(master_set, hpath, phase, env))
> -
> -      if node_list:
> -        node_list = frozenset(set(node_list) - master_set)
> -      if not node_list:
> -        return ret
> -      env["GANETI_IS_MASTER"] = constants.GLOBAL_HOOKS_NOT_MASTER
> +    env["GANETI_IS_MASTER"] = constants.GLOBAL_HOOKS_MASTER
> +    master_set = frozenset([self.master_uuid])
> +    ret.update(self._CheckParamsAndExecHooks(master_set, hpath, phase,
> env))
>
>      if node_list:
> -      ret.update(self.hooks_execution_fn(node_list, hpath, phase, env))
> +      node_list = frozenset(set(node_list) - master_set)
> +    env["GANETI_IS_MASTER"] = constants.GLOBAL_HOOKS_NOT_MASTER
> +    ret.update(self._CheckParamsAndExecHooks(node_list, hpath, phase,
> env))
>

Empty line here to match formatting of paragraph above?


>      return ret
>
>    def RunPhase(self, phase, node_uuids=None, is_global=False,
>
> On 11/23/2015 06:21 PM, Hrvoje Ribicic wrote:
>
>
>
> On Fri, Nov 20, 2015 at 5:11 PM, 'Oleg Ponomarev' via ganeti-devel <
> <[email protected]>[email protected]> wrote:
>
>> Add the *glob* argument to the RunPhase function. With *glob* set to
>> True, HooksMaster runs global hooks instead of the opcode's hooks. The
>> global hooks should be placed in the "global" subdirectory of the hooks
>> directory.
>>
>
> Thanks for the rename of "glob" - please modify the commit message as well.
>
>
>>
>> Global hooks run separately for the master node and for the others
>
> because enviromental variable IS_MASTER should be set properly.
>>
>
> As more than one global hook can be triggered by the same Ganeti job in a
> very unpredictable way, we set the IS_MASTER environmental variable to
> allow hooks to make sure that exactly one action gets executed per hook
> activation.
>
>
>>
>> Signed-off-by: Oleg Ponomarev < <[email protected]>
>> [email protected]>
>> ---
>>  lib/hooksmaster.py      | 55
>> +++++++++++++++++++++++++++++++++++++++++--------
>>  src/Ganeti/Constants.hs | 11 ++++++++++
>>  2 files changed, 57 insertions(+), 9 deletions(-)
>>
>> diff --git a/lib/hooksmaster.py b/lib/hooksmaster.py
>> index c23d857..1472ea7 100644
>> --- a/lib/hooksmaster.py
>> +++ b/lib/hooksmaster.py
>> @@ -56,7 +56,8 @@ def _RpcResultsToHooksResults(rpc_results):
>>  class HooksMaster(object):
>>    def __init__(self, opcode, hooks_path, nodes, hooks_execution_fn,
>>                 hooks_results_adapt_fn, build_env_fn,
>> prepare_post_nodes_fn,
>> -               log_fn, htype=None, cluster_name=None, master_name=None):
>> +               log_fn, htype=None, cluster_name=None, master_name=None,
>> +               master_uuid=None, job_id=None):
>>      """Base class for hooks masters.
>>
>>      This class invokes the execution of hooks according to the behaviour
>> @@ -93,6 +94,11 @@ class HooksMaster(object):
>>      @param cluster_name: name of the cluster
>>      @type master_name: string
>>      @param master_name: name of the master
>> +    @type master_uuid: string
>> +    @param master_uuid: uuid of the master (used in global hooks in order
>> +      to distinguish between the master node and the others)
>> +    @type job_id: int
>> +    @param job_id: the id of the job process (used in global post hooks)
>>
>>      """
>>      self.opcode = opcode
>> @@ -105,6 +111,8 @@ class HooksMaster(object):
>>      self.htype = htype
>>      self.cluster_name = cluster_name
>>      self.master_name = master_name
>> +    self.master_uuid = master_uuid
>> +    self.job_id = job_id
>>
>>      self.pre_env = self._BuildEnv(constants.HOOKS_PHASE_PRE)
>>      (self.pre_nodes, self.post_nodes) = nodes
>> @@ -151,7 +159,8 @@ class HooksMaster(object):
>>
>>      return env
>>
>> -  def _RunWrapper(self, node_list, hpath, phase, phase_env):
>> +  def _RunWrapper(self, node_list, hpath, phase, phase_env,
>> is_global=False,
>> +                  post_status=None):
>
>      """Simple wrapper over self.callfn.
>>
>>      This method fixes the environment before executing the hooks.
>> @@ -175,6 +184,12 @@ class HooksMaster(object):
>>      if self.master_name is not None:
>>        env["GANETI_MASTER"] = self.master_name
>>
>> +    if self.job_id and is_global:
>> +      env["GANETI_JOB_ID"] = self.job_id
>> +    if phase == constants.HOOKS_PHASE_POST and is_global:
>> +      assert post_status is not None
>> +      env["GANETI_POST_STATUS"] = post_status
>> +
>>      if phase_env:
>>        env = utils.algo.JoinDisjointDicts(env, phase_env)
>>
>> @@ -184,9 +199,26 @@ class HooksMaster(object):
>>      assert compat.all(key == "PATH" or key.startswith("GANETI_")
>>                        for key in env)
>>
>
> The assert being triggered before the env finalization is a bit messy.
>
>
>>
>> -    return self.hooks_execution_fn(node_list, hpath, phase, env)
>> -
>> -  def RunPhase(self, phase, node_names=None):
>> +    ret = dict()
>> +    if is_global:
>> +      env["GANETI_IS_MASTER"] = constants.GLOBAL_HOOKS_MASTER
>
> +      master_set = set([self.master_name])
>> +      ret.update(self.hooks_execution_fn(master_set, hpath, phase, env))
>> +
>> +      if node_list:
>> +        # Master node might be either listed by the uuid or by the name
>>
>
> How come? Where do the different value types come from?
>
> I would rather fix this at the source rather than here.
>
>
>> +        master_set.add(self.master_uuid)
>> +        node_list = frozenset(set(node_list) - master_set)
>> +      if not node_list:
>> +        return ret
>> +      env["GANETI_IS_MASTER"] = constants.GLOBAL_HOOKS_NOT_MASTER
>
>
> Urgh, a sideeffect of this code spilling over to the next invocation is a
> bit yucky.
>
>
>> +
>> +    if node_list:
>> +      ret.update(self.hooks_execution_fn(node_list, hpath, phase, env))
>
> +    return ret
>>
>
> I have to admit I do not like the way this code is structured.
>
> I would suggest using a local helper function that will operate on a set
> of nodes, set the environmental value if is_global is used, update ret, and
> optionally not invoke the execution function if the set of nodes is empty -
> we can also leave that bit to the execution function.
>
> This would allow us to separate the set calculation from the invocation,
> resulting in a more readable execution flow.
>
>
>> +
>> +  def RunPhase(self, phase, node_names=None, is_global=False,
>> +               post_status=None):
>>      """Run all the scripts for a phase.
>>
>>      This is the main function of the HookMaster.
>> @@ -198,6 +230,8 @@ class HooksMaster(object):
>>          L{constants.HOOKS_PHASE_PRE}; it denotes the hooks phase
>>      @param node_names: overrides the predefined list of nodes for the
>> given
>>          phase
>> +    @param is_global: whether global or per-opcode hooks should be
>> executed
>> +    @param post_status: the job execution status for the global post
>> hooks
>>
>
> for global post hooks
>
> Please add a @type declaration as well, as the type is not apparent from
> the code.
>
>
>>      @return: the processed results of the hooks multi-node rpc call
>>      @raise errors.HooksFailure: on communication failure to the nodes
>>      @raise errors.HooksAbort: on failure of one of the hooks
>> @@ -216,13 +250,15 @@ class HooksMaster(object):
>>      else:
>>        raise AssertionError("Unknown phase '%s'" % phase)
>>
>> -    if not node_names:
>> +    if not node_names and not is_global:
>>        # empty node list, we should not attempt to run this as either
>>        # we're in the cluster init phase and the rpc client part can't
>>        # even attempt to run, or this LU doesn't do hooks at all
>>        return
>>
>> -    results = self._RunWrapper(node_names, self.hooks_path, phase, env)
>> +    hooks_path = constants.GLOBAL_HOOKS_DIR if is_global else
>> self.hooks_path
>> +    results = self._RunWrapper(node_names, hooks_path, phase, env,
>> is_global,
>> +                               post_status)
>>      if not results:
>>        msg = "Communication Failure"
>>        if phase == constants.HOOKS_PHASE_PRE:
>> @@ -272,7 +308,7 @@ class HooksMaster(object):
>>      self._RunWrapper(nodes, hpath, phase, self.pre_env)
>>
>>    @staticmethod
>> -  def BuildFromLu(hooks_execution_fn, lu):
>> +  def BuildFromLu(hooks_execution_fn, lu, job_id=None):
>>      if lu.HPATH is None:
>>        nodes = (None, None)
>>      else:
>> @@ -285,9 +321,10 @@ class HooksMaster(object):
>>      master_name = cluster_name = None
>>      if lu.cfg:
>>        master_name = lu.cfg.GetMasterNodeName()
>> +      master_uuid = lu.cfg.GetMasterNode()
>>        cluster_name = lu.cfg.GetClusterName()
>>
>>      return HooksMaster(lu.op.OP_ID, lu.HPATH, nodes, hooks_execution_fn,
>>                         _RpcResultsToHooksResults, lu.BuildHooksEnv,
>>                         lu.PreparePostHookNodes, lu.LogWarning, lu.HTYPE,
>> -                       cluster_name, master_name)
>> +                       cluster_name, master_name, master_uuid, job_id)
>> diff --git a/src/Ganeti/Constants.hs b/src/Ganeti/Constants.hs
>> index 86c8c95..1f96c0d 100644
>> --- a/src/Ganeti/Constants.hs
>> +++ b/src/Ganeti/Constants.hs
>> @@ -724,6 +724,17 @@ hooksPhasePre = "pre"
>>  hooksVersion :: Int
>>  hooksVersion = 2
>>
>> +-- * Global hooks related constants
>> +
>> +globalHooksDir :: String
>> +globalHooksDir = "global"
>> +
>> +globalHooksMaster :: String
>> +globalHooksMaster = "master"
>> +
>> +globalHooksNotMaster :: String
>> +globalHooksNotMaster = "not_master"
>> +
>>  -- * Hooks subject type (what object type does the LU deal with)
>>
>>  htypeCluster :: String
>> --
>> 2.6.0.rc2.230.g3dd15c0
>>
>>
> Hrvoje Ribicic
> Ganeti Engineering
> Google Germany GmbH
> Dienerstr. 12, 80331, München
>
> Geschäftsführer: Matthew Scott Sucherman, Paul Terence Manicle
> Registergericht und -nummer: Hamburg, HRB 86891
> Sitz der Gesellschaft: Hamburg
>
> Diese E-Mail ist vertraulich. Wenn Sie nicht der richtige Adressat sind,
> leiten Sie diese bitte nicht weiter, informieren Sie den Absender und
> löschen Sie die E-Mail und alle Anhänge. Vielen Dank.
>
> This e-mail is confidential. If you are not the right addressee please do
> not forward it, please inform the sender, and please erase this e-mail
> including any attachments. Thanks.
>
>
>
Hrvoje Ribicic
Ganeti Engineering
Google Germany GmbH
Dienerstr. 12, 80331, München

Geschäftsführer: Matthew Scott Sucherman, Paul Terence Manicle
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg

Diese E-Mail ist vertraulich. Wenn Sie nicht der richtige Adressat sind,
leiten Sie diese bitte nicht weiter, informieren Sie den Absender und
löschen Sie die E-Mail und alle Anhänge. Vielen Dank.

This e-mail is confidential. If you are not the right addressee please do
not forward it, please inform the sender, and please erase this e-mail
including any attachments. Thanks.

Reply via email to