Add "only_if" capability, which allows task steps to be skipped if they do not match a conditional.
This commit is contained in:
parent
149cc57b0f
commit
dfbe591cc0
3 changed files with 42 additions and 13 deletions
|
@ -77,6 +77,7 @@ class PlayBook(object):
|
|||
self.changed = {}
|
||||
self.invocations = {}
|
||||
self.failures = {}
|
||||
self.skipped = {}
|
||||
|
||||
# playbook file can be passed in as a path or
|
||||
# as file contents (to support API usage)
|
||||
|
@ -158,8 +159,13 @@ class PlayBook(object):
|
|||
'resources' : self.invocations.get(host, 0),
|
||||
'changed' : self.changed.get(host, 0),
|
||||
'dark' : self.dark.get(host, 0),
|
||||
'failed' : self.failures.get(host, 0)
|
||||
'failed' : self.failures.get(host, 0),
|
||||
'skipped' : self.skipped.get(host, 0)
|
||||
}
|
||||
|
||||
# FIXME: TODO: use callback to reinstate per-host summary
|
||||
# and add corresponding code in /bin/ansible-playbook
|
||||
# print results
|
||||
return results
|
||||
|
||||
def _prune_failed_hosts(self, host_list):
|
||||
|
@ -175,12 +181,12 @@ class PlayBook(object):
|
|||
for (host, res) in results['contacted'].iteritems():
|
||||
# FIXME: make polling pattern in /bin/ansible match
|
||||
# move to common function in utils
|
||||
if not 'finished' in res and 'started' in res:
|
||||
if not 'finished' in res and not 'skipped' in res and 'started' in res:
|
||||
hosts.append(host)
|
||||
return hosts
|
||||
|
||||
|
||||
def _async_poll(self, runner, async_seconds, async_poll_interval):
|
||||
def _async_poll(self, runner, async_seconds, async_poll_interval, only_if):
|
||||
''' launch an async job, if poll_interval is set, wait for completion '''
|
||||
|
||||
# TODO: refactor this function
|
||||
|
@ -197,6 +203,8 @@ class PlayBook(object):
|
|||
if 'failed' in host_result:
|
||||
self.callbacks.on_failed(host, host_result)
|
||||
self.failures[host] = 1
|
||||
if 'skipped' in host_result:
|
||||
self.skipped[host] = self.skipped.get(host, 0) + 1
|
||||
|
||||
if async_poll_interval <= 0:
|
||||
# if not polling, playbook requested fire and forget
|
||||
|
@ -248,6 +256,11 @@ class PlayBook(object):
|
|||
if 'failed' in host_result:
|
||||
self.callbacks.on_failed(host, host_result)
|
||||
self.failures[host] = 1
|
||||
if 'skipped' in host_result:
|
||||
# NOTE: callbacks on skipped? should not really
|
||||
# happen at this point in the loop
|
||||
self.skipped[host] = self.skipped.get(host, 0) + 1
|
||||
|
||||
|
||||
for (host, host_result) in poll_results['contacted'].iteritems():
|
||||
results['contacted'][host] = host_result
|
||||
|
@ -267,7 +280,7 @@ class PlayBook(object):
|
|||
return results
|
||||
|
||||
def _run_module(self, pattern, module, args, hosts, remote_user,
|
||||
async_seconds, async_poll_interval):
|
||||
async_seconds, async_poll_interval, only_if):
|
||||
|
||||
''' run a particular module step in a playbook '''
|
||||
runner = ansible.runner.Runner(
|
||||
|
@ -281,13 +294,14 @@ class PlayBook(object):
|
|||
timeout=self.timeout,
|
||||
remote_user=remote_user,
|
||||
setup_cache=SETUP_CACHE,
|
||||
basedir=self.basedir
|
||||
basedir=self.basedir,
|
||||
conditionally_execute_if=only_if
|
||||
)
|
||||
|
||||
if async_seconds == 0:
|
||||
rc = runner.run()
|
||||
else:
|
||||
rc = self._async_poll(runner, async_seconds, async_poll_interval)
|
||||
rc = self._async_poll(runner, async_seconds, async_poll_interval, only_if)
|
||||
|
||||
dark_hosts = rc.get('dark',{})
|
||||
for (host, error) in dark_hosts.iteritems():
|
||||
|
@ -314,13 +328,12 @@ class PlayBook(object):
|
|||
host_list = self._prune_failed_hosts(host_list)
|
||||
|
||||
# load the module name and parameters from the task entry
|
||||
name = task['name']
|
||||
action = task['action']
|
||||
name = task['name'] # FIXME: error if not set
|
||||
action = task['action'] # FIXME: error if not set
|
||||
only_if = task.get('only_if', 'True')
|
||||
async_seconds = int(task.get('async', 0)) # not async by default
|
||||
async_poll_interval = int(task.get('poll', 10)) # default poll = 10 seconds
|
||||
|
||||
# comment = task.get('comment', '')
|
||||
|
||||
tokens = shlex.split(action)
|
||||
module_name = tokens[0]
|
||||
module_args = tokens[1:]
|
||||
|
@ -336,7 +349,7 @@ class PlayBook(object):
|
|||
# run the task in parallel
|
||||
results = self._run_module(pattern, module_name,
|
||||
module_args, host_list, remote_user,
|
||||
async_seconds, async_poll_interval)
|
||||
async_seconds, async_poll_interval, only_if)
|
||||
|
||||
# if no hosts are matched, carry on, unlike /bin/ansible
|
||||
# which would warn you about this
|
||||
|
@ -358,6 +371,7 @@ class PlayBook(object):
|
|||
else:
|
||||
self.dark[host] = self.dark[host] + 1
|
||||
|
||||
# FIXME: refactor
|
||||
for host, results in contacted.iteritems():
|
||||
self.processed[host] = 1
|
||||
|
||||
|
@ -378,6 +392,12 @@ class PlayBook(object):
|
|||
self.changed[host] = 1
|
||||
else:
|
||||
self.changed[host] = self.changed[host] + 1
|
||||
# TODO: verify/test that async steps are skippable
|
||||
if results.get('skipped', False):
|
||||
if not host in self.changed:
|
||||
self.skipped[host] = 1
|
||||
else:
|
||||
self.skipped[host] = self.skipped[host] + 1
|
||||
|
||||
# flag which notify handlers need to be run
|
||||
# this will be on a SUBSET of the actual host list. For instance
|
||||
|
|
|
@ -76,6 +76,7 @@ class Runner(object):
|
|||
basedir=None,
|
||||
setup_cache=None,
|
||||
transport='paramiko',
|
||||
conditionally_execute_if='True',
|
||||
verbose=False):
|
||||
|
||||
'''
|
||||
|
@ -95,6 +96,7 @@ class Runner(object):
|
|||
if setup_cache is None:
|
||||
setup_cache = {}
|
||||
self.setup_cache = setup_cache
|
||||
self.conditionally_execute_if = conditionally_execute_if
|
||||
|
||||
self.host_list, self.groups = self.parse_hosts(host_list)
|
||||
self.module_path = module_path
|
||||
|
@ -286,6 +288,7 @@ class Runner(object):
|
|||
modifies the command using setup_cache variables (see playbook)
|
||||
'''
|
||||
|
||||
|
||||
args = module_args
|
||||
if type(args) == list:
|
||||
args = " ".join([ str(x) for x in module_args ])
|
||||
|
@ -293,6 +296,11 @@ class Runner(object):
|
|||
# by default the args to substitute in the action line are those from the setup cache
|
||||
inject_vars = self.setup_cache.get(conn.host,{})
|
||||
|
||||
# see if we really need to run this or not...
|
||||
conditional = utils.template(self.conditionally_execute_if, inject_vars)
|
||||
if not eval(conditional):
|
||||
return utils.smjson(dict(skipped=True))
|
||||
|
||||
# if the host file was an external script, execute it with the hostname
|
||||
# as a first parameter to get the variables to use for the host
|
||||
inject2 = {}
|
||||
|
|
|
@ -235,7 +235,8 @@
|
|||
"changed": 2,
|
||||
"dark": 0,
|
||||
"failed": 0,
|
||||
"resources": 9
|
||||
"resources": 9,
|
||||
"skipped": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue