From 32fb6c807c41dc4036bceab5e3b0799f4fd3320b Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Sat, 20 Apr 2013 18:03:03 -0400 Subject: [PATCH] Allow handlers to run in between pre_tasks, roles, tasks, and post_tasks. --- lib/ansible/playbook/__init__.py | 33 ++++++++++++++++++++++++++------ lib/ansible/playbook/play.py | 17 +++++++++++++--- lib/ansible/playbook/task.py | 13 +++++++++++-- 3 files changed, 52 insertions(+), 11 deletions(-) diff --git a/lib/ansible/playbook/__init__.py b/lib/ansible/playbook/__init__.py index 856ec632071..99e96adfcf4 100644 --- a/lib/ansible/playbook/__init__.py +++ b/lib/ansible/playbook/__init__.py @@ -496,15 +496,35 @@ class PlayBook(object): self.inventory.also_restrict_to(on_hosts) for task in play.tasks(): + + if task.meta is not None: + + # meta tasks are an internalism and are not valid for end-user playbook usage + # here a meta task is a placeholder that signals handlers should be run + + if task.meta == 'flush_handlers': + for handler in play.handlers(): + if len(handler.notified_by) > 0: + self.inventory.restrict_to(handler.notified_by) + self._run_task(play, handler, True) + self.inventory.lift_restriction() + for host in handler.notified_by: + handler.notified_by.remove(host) + + continue + hosts_count = len(self._list_available_hosts(play.hosts)) # only run the task if the requested tags match should_run = False for x in self.only_tags: + + for y in task.tags: if (x==y): should_run = True break + if should_run: if not self._run_task(play, task, False): # whether no hosts matched is fatal or not depends if it was on the initial step. @@ -523,12 +543,13 @@ class PlayBook(object): return False # run notify actions - for handler in play.handlers(): - if len(handler.notified_by) > 0: - self.inventory.restrict_to(handler.notified_by) - self._run_task(play, handler, True) - self.inventory.lift_restriction() - handler.notified_by = [] + #for handler in play.handlers(): + # if len(handler.notified_by) > 0: + # self.inventory.restrict_to(handler.notified_by) + # self._run_task(play, handler, True) + # self.inventory.lift_restriction() + # handler.notified_by = [] + # handler.notified_by = [] self.inventory.lift_also_restriction() diff --git a/lib/ansible/playbook/play.py b/lib/ansible/playbook/play.py index ad1ae4de587..6fae3fda3cc 100644 --- a/lib/ansible/playbook/play.py +++ b/lib/ansible/playbook/play.py @@ -127,7 +127,7 @@ class Play(object): # and it auto-extends tasks/handlers/vars_files as appropriate if found if roles is None: - return ds + roles = [] if type(roles) != list: raise errors.AnsibleError("value of 'roles:' must be a list") @@ -140,6 +140,7 @@ class Play(object): pre_tasks = [] for x in pre_tasks: new_tasks.append(x) + new_tasks.append(dict(meta='flush_handlers')) # variables if the role was parameterized (i.e. given as a hash) has_dict = {} @@ -200,14 +201,17 @@ class Play(object): if type(post_tasks) != list: post_tasks = [] + new_tasks.append(dict(meta='flush_handlers')) new_tasks.extend(tasks) + new_tasks.append(dict(meta='flush_handlers')) new_tasks.extend(post_tasks) + new_tasks.append(dict(meta='flush_handlers')) new_handlers.extend(handlers) new_vars_files.extend(vars_files) ds['tasks'] = new_tasks ds['handlers'] = new_handlers ds['vars_files'] = new_vars_files - + return ds # ************************************************* @@ -223,6 +227,12 @@ class Play(object): for x in tasks: if not isinstance(x, dict): raise errors.AnsibleError("expecting dict; got: %s" % x) + + if 'meta' in x: + if x['meta'] == 'flush_handlers': + results.append(Task(self,x)) + continue + task_vars = self.vars.copy() task_vars.update(vars) if original_file: @@ -369,7 +379,8 @@ class Play(object): # gather all the tags in all the tasks into one list all_tags = [] for task in self._tasks: - all_tags.extend(task.tags) + if not task.meta: + all_tags.extend(task.tags) # compare the lists of tags using sets and return the matched and unmatched all_tags_set = set(all_tags) diff --git a/lib/ansible/playbook/task.py b/lib/ansible/playbook/task.py index a1151e4f53e..b05c798f4a9 100644 --- a/lib/ansible/playbook/task.py +++ b/lib/ansible/playbook/task.py @@ -22,7 +22,7 @@ import ansible.utils.template as template class Task(object): __slots__ = [ - 'name', 'action', 'only_if', 'when', 'async_seconds', 'async_poll_interval', + 'name', 'meta', 'action', 'only_if', 'when', 'async_seconds', 'async_poll_interval', 'notify', 'module_name', 'module_args', 'module_vars', 'play', 'notified_by', 'tags', 'register', 'delegate_to', 'first_available_file', 'ignore_errors', @@ -33,7 +33,7 @@ class Task(object): # to prevent typos and such VALID_KEYS = [ - 'name', 'action', 'only_if', 'async', 'poll', 'notify', + 'name', 'meta', 'action', 'only_if', 'async', 'poll', 'notify', 'first_available_file', 'include', 'tags', 'register', 'ignore_errors', 'delegate_to', 'local_action', 'transport', 'sudo', 'sudo_user', 'sudo_pass', 'when', 'connection', 'environment', 'args', @@ -43,6 +43,15 @@ class Task(object): def __init__(self, play, ds, module_vars=None, additional_conditions=None): ''' constructor loads from a task or handler datastructure ''' + # meta directives are used to tell things like ansible/playbook to run + # operations like handler execution. Meta tasks are not executed + # normally. + if 'meta' in ds: + self.meta = ds['meta'] + return + else: + self.meta = None + for x in ds.keys(): # code to allow for saying "modulename: args" versus "action: modulename args"