Added --skip-tags option to ansible-playbook.

This commit is contained in:
Alan Descoins 2013-06-25 02:33:29 -03:00
parent fb869e58ee
commit 04349ec362
2 changed files with 51 additions and 23 deletions

View file

@ -23,7 +23,7 @@ import os
import ansible.playbook import ansible.playbook
import ansible.constants as C import ansible.constants as C
import ansible.utils.template import ansible.utils.template
from ansible import errors from ansible import errors
from ansible import callbacks from ansible import callbacks
from ansible import utils from ansible import utils
@ -53,11 +53,11 @@ def main(args):
# create parser for CLI options # create parser for CLI options
parser = utils.base_parser( parser = utils.base_parser(
constants=C, constants=C,
usage = "%prog playbook.yml", usage = "%prog playbook.yml",
connect_opts=True, connect_opts=True,
runas_opts=True, runas_opts=True,
subset_opts=True, subset_opts=True,
check_opts=True, check_opts=True,
diff_opts=True diff_opts=True
) )
@ -65,13 +65,15 @@ def main(args):
help="set additional key=value variables from the CLI") help="set additional key=value variables from the CLI")
parser.add_option('-t', '--tags', dest='tags', default='all', parser.add_option('-t', '--tags', dest='tags', default='all',
help="only run plays and tasks tagged with these values") help="only run plays and tasks tagged with these values")
parser.add_option('--skip-tags', dest='skip_tags',
help="only run plays and tasks whose tags do not match these values")
parser.add_option('--syntax-check', dest='syntax', action='store_true', parser.add_option('--syntax-check', dest='syntax', action='store_true',
help="perform a syntax check on the playbook, but do not execute it") help="perform a syntax check on the playbook, but do not execute it")
parser.add_option('--list-tasks', dest='listtasks', action='store_true', parser.add_option('--list-tasks', dest='listtasks', action='store_true',
help="list all tasks that would be executed") help="list all tasks that would be executed")
parser.add_option('--step', dest='step', action='store_true', parser.add_option('--step', dest='step', action='store_true',
help="one-step-at-a-time: confirm each task before running") help="one-step-at-a-time: confirm each task before running")
parser.add_option('--start-at-task', dest='start_at', parser.add_option('--start-at-task', dest='start_at',
help="start the playbook at the task matching this name") help="start the playbook at the task matching this name")
options, args = parser.parse_args(args) options, args = parser.parse_args(args)
@ -97,6 +99,9 @@ def main(args):
else: else:
extra_vars = utils.parse_kv(options.extra_vars) extra_vars = utils.parse_kv(options.extra_vars)
only_tags = options.tags.split(",") only_tags = options.tags.split(",")
skip_tags = options.skip_tags
if options.skip_tags is not None:
skip_tags = options.skip_tags.split(",")
for playbook in args: for playbook in args:
if not os.path.exists(playbook): if not os.path.exists(playbook):
@ -136,6 +141,7 @@ def main(args):
extra_vars=extra_vars, extra_vars=extra_vars,
private_key_file=options.private_key_file, private_key_file=options.private_key_file,
only_tags=only_tags, only_tags=only_tags,
skip_tags=skip_tags,
check=options.check, check=options.check,
diff=options.diff diff=options.diff
) )
@ -156,13 +162,20 @@ def main(args):
print ' %s' % host print ' %s' % host
if options.listtasks: if options.listtasks:
matched_tags, unmatched_tags = play.compare_tags(pb.only_tags) matched_tags, unmatched_tags = play.compare_tags(pb.only_tags)
# Remove skipped tasks
matched_tags = matched_tags - set(pb.skip_tags)
unmatched_tags.discard('all') unmatched_tags.discard('all')
unknown_tags = set(pb.only_tags) - (matched_tags | unmatched_tags) unknown_tags = ((set(pb.only_tags) | set(pb.skip_tags)) -
(matched_tags | unmatched_tags))
if unknown_tags: if unknown_tags:
continue continue
print ' play #%d (%s): task count=%d' % (playnum, label, len(play.tasks())) print ' play #%d (%s): task count=%d' % (playnum, label, len(play.tasks()))
for task in play.tasks(): for task in play.tasks():
if set(task.tags).intersection(pb.only_tags): if (set(task.tags).intersection(pb.only_tags) and not
set(task.tags).intersection(pb.skip_tags)):
if getattr(task, 'name', None) is not None: if getattr(task, 'name', None) is not None:
# meta tasks have no names # meta tasks have no names
print ' %s' % task.name print ' %s' % task.name
@ -173,7 +186,7 @@ def main(args):
# if we've not exited by now then we are fine. # if we've not exited by now then we are fine.
print 'Playbook Syntax is fine' print 'Playbook Syntax is fine'
return 0 return 0
failed_hosts = [] failed_hosts = []
try: try:
@ -202,7 +215,7 @@ def main(args):
colorize('ok', t['ok'], 'green'), colorize('ok', t['ok'], 'green'),
colorize('changed', t['changed'], 'yellow'), colorize('changed', t['changed'], 'yellow'),
colorize('unreachable', t['unreachable'], 'red'), colorize('unreachable', t['unreachable'], 'red'),
colorize('failed', t['failures'], 'red')), colorize('failed', t['failures'], 'red')),
screen_only=True screen_only=True
) )
@ -211,11 +224,11 @@ def main(args):
colorize('ok', t['ok'], None), colorize('ok', t['ok'], None),
colorize('changed', t['changed'], None), colorize('changed', t['changed'], None),
colorize('unreachable', t['unreachable'], None), colorize('unreachable', t['unreachable'], None),
colorize('failed', t['failures'], None)), colorize('failed', t['failures'], None)),
log_only=True log_only=True
) )
print "" print ""
if len(failed_hosts) > 0: if len(failed_hosts) > 0:
return 2 return 2

View file

@ -63,6 +63,7 @@ class PlayBook(object):
sudo_user = C.DEFAULT_SUDO_USER, sudo_user = C.DEFAULT_SUDO_USER,
extra_vars = None, extra_vars = None,
only_tags = None, only_tags = None,
skip_tags = None,
subset = C.DEFAULT_SUBSET, subset = C.DEFAULT_SUBSET,
inventory = None, inventory = None,
check = False, check = False,
@ -97,6 +98,8 @@ class PlayBook(object):
extra_vars = {} extra_vars = {}
if only_tags is None: if only_tags is None:
only_tags = [ 'all' ] only_tags = [ 'all' ]
if skip_tags is None:
skip_tags = []
self.check = check self.check = check
self.diff = diff self.diff = diff
@ -117,6 +120,7 @@ class PlayBook(object):
self.global_vars = {} self.global_vars = {}
self.private_key_file = private_key_file self.private_key_file = private_key_file
self.only_tags = only_tags self.only_tags = only_tags
self.skip_tags = skip_tags
self.any_errors_fatal = any_errors_fatal self.any_errors_fatal = any_errors_fatal
self.callbacks.playbook = self self.callbacks.playbook = self
@ -216,11 +220,14 @@ class PlayBook(object):
for (play_ds, play_basedir) in zip(self.playbook, self.play_basedirs): for (play_ds, play_basedir) in zip(self.playbook, self.play_basedirs):
play = Play(self, play_ds, play_basedir) play = Play(self, play_ds, play_basedir)
assert play is not None assert play is not None
matched_tags, unmatched_tags = play.compare_tags(self.only_tags) matched_tags, unmatched_tags = play.compare_tags(self.only_tags)
matched_tags_all = matched_tags_all | matched_tags matched_tags_all = matched_tags_all | matched_tags
unmatched_tags_all = unmatched_tags_all | unmatched_tags unmatched_tags_all = unmatched_tags_all | unmatched_tags
# Remove tasks we wish to skip
matched_tags = matched_tags - set(self.skip_tags)
# if we have matched_tags, the play must be run. # if we have matched_tags, the play must be run.
# if the play contains no tasks, assume we just want to gather facts # if the play contains no tasks, assume we just want to gather facts
# in this case there are actually 3 meta tasks (handler flushes) not 0 # in this case there are actually 3 meta tasks (handler flushes) not 0
@ -228,9 +235,11 @@ class PlayBook(object):
if (len(matched_tags) > 0 or len(play.tasks()) == 3): if (len(matched_tags) > 0 or len(play.tasks()) == 3):
plays.append(play) plays.append(play)
# if the playbook is invoked with --tags that don't exist at all in the playbooks # if the playbook is invoked with --tags or --skip-tags that don't
# then we need to raise an error so that the user can correct the arguments. # exist at all in the playbooks then we need to raise an error so that
unknown_tags = set(self.only_tags) - (matched_tags_all | unmatched_tags_all) # the user can correct the arguments.
unknown_tags = ((set(self.only_tags) | set(self.skip_tags)) -
(matched_tags_all | unmatched_tags_all))
unknown_tags.discard('all') unknown_tags.discard('all')
if len(unknown_tags) > 0: if len(unknown_tags) > 0:
@ -331,7 +340,7 @@ class PlayBook(object):
ansible.callbacks.set_task(self.callbacks, None) ansible.callbacks.set_task(self.callbacks, None)
ansible.callbacks.set_task(self.runner_callbacks, None) ansible.callbacks.set_task(self.runner_callbacks, None)
return True return True
# load up an appropriate ansible runner to run the task in parallel # load up an appropriate ansible runner to run the task in parallel
results = self._run_task_internal(task) results = self._run_task_internal(task)
@ -340,7 +349,7 @@ class PlayBook(object):
if results is None: if results is None:
hosts_remaining = False hosts_remaining = False
results = {} results = {}
contacted = results.get('contacted', {}) contacted = results.get('contacted', {})
self.stats.compute(results, ignore_errors=task.ignore_errors) self.stats.compute(results, ignore_errors=task.ignore_errors)
@ -408,7 +417,7 @@ class PlayBook(object):
self.callbacks.on_setup() self.callbacks.on_setup()
self.inventory.restrict_to(host_list) self.inventory.restrict_to(host_list)
ansible.callbacks.set_task(self.callbacks, None) ansible.callbacks.set_task(self.callbacks, None)
ansible.callbacks.set_task(self.runner_callbacks, None) ansible.callbacks.set_task(self.runner_callbacks, None)
@ -438,10 +447,10 @@ class PlayBook(object):
def generate_retry_inventory(self, replay_hosts): def generate_retry_inventory(self, replay_hosts):
''' '''
called by /usr/bin/ansible when a playbook run fails. It generates a inventory called by /usr/bin/ansible when a playbook run fails. It generates a inventory
that allows re-running on ONLY the failed hosts. This may duplicate some that allows re-running on ONLY the failed hosts. This may duplicate some
variable information in group_vars/host_vars but that is ok, and expected. variable information in group_vars/host_vars but that is ok, and expected.
''' '''
buf = StringIO.StringIO() buf = StringIO.StringIO()
for x in replay_hosts: for x in replay_hosts:
@ -507,7 +516,7 @@ class PlayBook(object):
# meta tasks are an internalism and are not valid for end-user playbook usage # 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 # here a meta task is a placeholder that signals handlers should be run
if task.meta == 'flush_handlers': if task.meta == 'flush_handlers':
for handler in play.handlers(): for handler in play.handlers():
if len(handler.notified_by) > 0: if len(handler.notified_by) > 0:
@ -530,11 +539,17 @@ class PlayBook(object):
for x in self.only_tags: for x in self.only_tags:
for y in task.tags: for y in task.tags:
if (x==y): if x == y:
should_run = True should_run = True
break break
# Check for tags that we need to skip
if should_run: if should_run:
if any(x in task.tags for x in self.skip_tags):
should_run = False
if should_run:
if not self._run_task(play, task, False): if not self._run_task(play, task, False):
# whether no hosts matched is fatal or not depends if it was on the initial step. # whether no hosts matched is fatal or not depends if it was on the initial step.
# if we got exactly no hosts on the first step (setup!) then the host group # if we got exactly no hosts on the first step (setup!) then the host group