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.changed = {}
|
||||||
self.invocations = {}
|
self.invocations = {}
|
||||||
self.failures = {}
|
self.failures = {}
|
||||||
|
self.skipped = {}
|
||||||
|
|
||||||
# playbook file can be passed in as a path or
|
# playbook file can be passed in as a path or
|
||||||
# as file contents (to support API usage)
|
# as file contents (to support API usage)
|
||||||
|
@ -158,8 +159,13 @@ class PlayBook(object):
|
||||||
'resources' : self.invocations.get(host, 0),
|
'resources' : self.invocations.get(host, 0),
|
||||||
'changed' : self.changed.get(host, 0),
|
'changed' : self.changed.get(host, 0),
|
||||||
'dark' : self.dark.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
|
return results
|
||||||
|
|
||||||
def _prune_failed_hosts(self, host_list):
|
def _prune_failed_hosts(self, host_list):
|
||||||
|
@ -175,12 +181,12 @@ class PlayBook(object):
|
||||||
for (host, res) in results['contacted'].iteritems():
|
for (host, res) in results['contacted'].iteritems():
|
||||||
# FIXME: make polling pattern in /bin/ansible match
|
# FIXME: make polling pattern in /bin/ansible match
|
||||||
# move to common function in utils
|
# 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)
|
hosts.append(host)
|
||||||
return hosts
|
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 '''
|
''' launch an async job, if poll_interval is set, wait for completion '''
|
||||||
|
|
||||||
# TODO: refactor this function
|
# TODO: refactor this function
|
||||||
|
@ -197,6 +203,8 @@ class PlayBook(object):
|
||||||
if 'failed' in host_result:
|
if 'failed' in host_result:
|
||||||
self.callbacks.on_failed(host, host_result)
|
self.callbacks.on_failed(host, host_result)
|
||||||
self.failures[host] = 1
|
self.failures[host] = 1
|
||||||
|
if 'skipped' in host_result:
|
||||||
|
self.skipped[host] = self.skipped.get(host, 0) + 1
|
||||||
|
|
||||||
if async_poll_interval <= 0:
|
if async_poll_interval <= 0:
|
||||||
# if not polling, playbook requested fire and forget
|
# if not polling, playbook requested fire and forget
|
||||||
|
@ -248,6 +256,11 @@ class PlayBook(object):
|
||||||
if 'failed' in host_result:
|
if 'failed' in host_result:
|
||||||
self.callbacks.on_failed(host, host_result)
|
self.callbacks.on_failed(host, host_result)
|
||||||
self.failures[host] = 1
|
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():
|
for (host, host_result) in poll_results['contacted'].iteritems():
|
||||||
results['contacted'][host] = host_result
|
results['contacted'][host] = host_result
|
||||||
|
@ -267,7 +280,7 @@ class PlayBook(object):
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def _run_module(self, pattern, module, args, hosts, remote_user,
|
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 '''
|
''' run a particular module step in a playbook '''
|
||||||
runner = ansible.runner.Runner(
|
runner = ansible.runner.Runner(
|
||||||
|
@ -281,13 +294,14 @@ class PlayBook(object):
|
||||||
timeout=self.timeout,
|
timeout=self.timeout,
|
||||||
remote_user=remote_user,
|
remote_user=remote_user,
|
||||||
setup_cache=SETUP_CACHE,
|
setup_cache=SETUP_CACHE,
|
||||||
basedir=self.basedir
|
basedir=self.basedir,
|
||||||
|
conditionally_execute_if=only_if
|
||||||
)
|
)
|
||||||
|
|
||||||
if async_seconds == 0:
|
if async_seconds == 0:
|
||||||
rc = runner.run()
|
rc = runner.run()
|
||||||
else:
|
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',{})
|
dark_hosts = rc.get('dark',{})
|
||||||
for (host, error) in dark_hosts.iteritems():
|
for (host, error) in dark_hosts.iteritems():
|
||||||
|
@ -314,13 +328,12 @@ class PlayBook(object):
|
||||||
host_list = self._prune_failed_hosts(host_list)
|
host_list = self._prune_failed_hosts(host_list)
|
||||||
|
|
||||||
# load the module name and parameters from the task entry
|
# load the module name and parameters from the task entry
|
||||||
name = task['name']
|
name = task['name'] # FIXME: error if not set
|
||||||
action = task['action']
|
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_seconds = int(task.get('async', 0)) # not async by default
|
||||||
async_poll_interval = int(task.get('poll', 10)) # default poll = 10 seconds
|
async_poll_interval = int(task.get('poll', 10)) # default poll = 10 seconds
|
||||||
|
|
||||||
# comment = task.get('comment', '')
|
|
||||||
|
|
||||||
tokens = shlex.split(action)
|
tokens = shlex.split(action)
|
||||||
module_name = tokens[0]
|
module_name = tokens[0]
|
||||||
module_args = tokens[1:]
|
module_args = tokens[1:]
|
||||||
|
@ -336,7 +349,7 @@ class PlayBook(object):
|
||||||
# run the task in parallel
|
# run the task in parallel
|
||||||
results = self._run_module(pattern, module_name,
|
results = self._run_module(pattern, module_name,
|
||||||
module_args, host_list, remote_user,
|
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
|
# if no hosts are matched, carry on, unlike /bin/ansible
|
||||||
# which would warn you about this
|
# which would warn you about this
|
||||||
|
@ -357,7 +370,8 @@ class PlayBook(object):
|
||||||
self.dark[host] = 1
|
self.dark[host] = 1
|
||||||
else:
|
else:
|
||||||
self.dark[host] = self.dark[host] + 1
|
self.dark[host] = self.dark[host] + 1
|
||||||
|
|
||||||
|
# FIXME: refactor
|
||||||
for host, results in contacted.iteritems():
|
for host, results in contacted.iteritems():
|
||||||
self.processed[host] = 1
|
self.processed[host] = 1
|
||||||
|
|
||||||
|
@ -378,6 +392,12 @@ class PlayBook(object):
|
||||||
self.changed[host] = 1
|
self.changed[host] = 1
|
||||||
else:
|
else:
|
||||||
self.changed[host] = self.changed[host] + 1
|
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
|
# flag which notify handlers need to be run
|
||||||
# this will be on a SUBSET of the actual host list. For instance
|
# this will be on a SUBSET of the actual host list. For instance
|
||||||
|
|
|
@ -76,6 +76,7 @@ class Runner(object):
|
||||||
basedir=None,
|
basedir=None,
|
||||||
setup_cache=None,
|
setup_cache=None,
|
||||||
transport='paramiko',
|
transport='paramiko',
|
||||||
|
conditionally_execute_if='True',
|
||||||
verbose=False):
|
verbose=False):
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
@ -95,6 +96,7 @@ class Runner(object):
|
||||||
if setup_cache is None:
|
if setup_cache is None:
|
||||||
setup_cache = {}
|
setup_cache = {}
|
||||||
self.setup_cache = 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.host_list, self.groups = self.parse_hosts(host_list)
|
||||||
self.module_path = module_path
|
self.module_path = module_path
|
||||||
|
@ -286,12 +288,18 @@ class Runner(object):
|
||||||
modifies the command using setup_cache variables (see playbook)
|
modifies the command using setup_cache variables (see playbook)
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
args = module_args
|
args = module_args
|
||||||
if type(args) == list:
|
if type(args) == list:
|
||||||
args = " ".join([ str(x) for x in module_args ])
|
args = " ".join([ str(x) for x in module_args ])
|
||||||
|
|
||||||
# by default the args to substitute in the action line are those from the setup cache
|
# by default the args to substitute in the action line are those from the setup cache
|
||||||
inject_vars = self.setup_cache.get(conn.host,{})
|
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
|
# 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
|
# as a first parameter to get the variables to use for the host
|
||||||
|
|
|
@ -235,7 +235,8 @@
|
||||||
"changed": 2,
|
"changed": 2,
|
||||||
"dark": 0,
|
"dark": 0,
|
||||||
"failed": 0,
|
"failed": 0,
|
||||||
"resources": 9
|
"resources": 9,
|
||||||
|
"skipped": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue