Notifable handlers only run when something is changed. Awesome.

This commit is contained in:
Michael DeHaan 2012-02-25 14:42:41 -05:00
parent de80166b6d
commit 79fdc1b6f4
3 changed files with 46 additions and 65 deletions

36
TODO.md
View file

@ -1,36 +0,0 @@
TODO list and plans
===================
Playbook TODO:
* error codes and failure summaries
* handle 'changed' attributes
* fail nodes on errors, i.e. remove from host list, rather than continuing to pound them
* further improve output
* more conditional capability (if statement) (?)
* very good logging
Command module:
* magic to pull async & timeout options off of the command line and not feed them
to the app we're executing
General:
* better logging
* async options
* modules for users, groups, and files, using puppet style ensure mechanics
* think about how to build idempotency (aka Puppet-style 'creates') around command module?
Bonus utilities:
* ansible-inventory - gathering fact/hw info, storing in git, adding RSS
* ansible-slurp - recursively rsync file trees for each host
* maybe it's own fact engine, not required, that also feeds from facter
Not so interested really, but maybe:
* list available modules from command line
* add/remove/list hosts from the command line
* filter exclusion (run this only if fact is true/false)
-- should be doable with playbooks (i.e. not neccessary)

View file

@ -6,17 +6,18 @@
- do:
- copy a file
- copy /srv/a /srv/b
notify:
- restart apache
- do:
- template from local file template.j2 to remote location /srv/file.out
- template /srv/template.j2 /srv/file.out
- do:
- update apache
- command /usr/bin/yum update apache
onchange:
notify:
- restart apache
- quack like a duck
handlers:
- do:
- restart apache
- command /sbin/service apache restart
- command /sbin/service httpd restart
- do:
- run bin false
- command /bin/false
- quack like a duck
- command /bin/true

View file

@ -101,7 +101,7 @@ class PlayBook(object):
timeout=self.timeout
)
def _run_task(self, pattern, task, host_list=None, conditional=False):
def _run_task(self, pattern=None, task=None, host_list=None, handlers=None, conditional=False):
'''
run a single task in the playbook and
recursively run any subtasks.
@ -118,7 +118,7 @@ class PlayBook(object):
namestr = "%s/%s" % (pattern, comment)
if conditional:
namestr = "subset/%s" % namestr
namestr = "notified/%s" % namestr
print "TASK [%s]" % namestr
runner = self._get_task_runner(
@ -130,15 +130,7 @@ class PlayBook(object):
results = runner.run()
dark = results.get("dark", [])
contacted = results.get("contacted", [])
# TODO: filter based on values that indicate
# they have changed events to emulate Puppet
# 'notify' behavior, super easy -- just
# a list comprehension -- but we need complaint
# modules first
ok_hosts = contacted.keys()
for host, msg in dark.items():
@ -152,16 +144,30 @@ class PlayBook(object):
print "FAIL: [%s/%s]" % (host, comment, results)
subtasks = task.get('onchange', [])
# flag which notify handlers need to be run
subtasks = task.get('notify', [])
if len(subtasks) > 0:
for host, results in contacted.items():
if results.get('changed', False):
for subtask in subtasks:
self._run_task(pattern, subtask, ok_hosts, conditional=True)
self._flag_handler(handlers, subtask, host)
# TODO: if a host fails in task 1, add it to an excludes
# list such that no other tasks in the list ever execute
# unlike Puppet, do not allow partial failure of the tree
# and continuing as far as possible. Fail fast.
# TODO: if a host fails in any task, remove it from
# the host list immediately
def _flag_handler(self, handlers, match_name, host):
'''
if a task has any notify elements, flag handlers for run
at end of execution cycle for hosts that have indicated
changes have been made
'''
for x in handlers:
attribs = x["do"]
name = attribs[0]
if match_name == name:
if not x.has_key("run"):
x['run'] = []
x['run'].append(host)
def _run_pattern(self, pg):
'''
@ -170,9 +176,19 @@ class PlayBook(object):
pattern = pg['pattern']
tasks = pg['tasks']
handlers = pg['handlers']
for task in tasks:
self._run_task(pattern, task)
self._run_task(pattern=pattern, task=task, handlers=handlers)
for task in handlers:
if type(task.get("run", None)) == list:
self._run_task(
pattern=pattern,
task=task,
handlers=handlers,
host_list=task.get('run',[]),
conditional=True
)