diff --git a/bin/ansible-playbook b/bin/ansible-playbook index 0e44ffe592..4fac717c36 100755 --- a/bin/ansible-playbook +++ b/bin/ansible-playbook @@ -35,6 +35,8 @@ def main(args): parser = utils.base_parser(constants=C, usage=usage, connect_opts=True, runas_opts=True) parser.add_option('-e', '--extra-vars', dest="extra_vars", default=None, help="set additional key=value variables from the CLI") + parser.add_option('-t', '--tags', dest='tags', default='all', + help="only run plays and tasks tagged with these values") options, args = parser.parse_args(args) @@ -53,6 +55,7 @@ def main(args): options.sudo = True options.sudo_user = options.sudo_user or C.DEFAULT_SUDO_USER extra_vars = utils.parse_kv(options.extra_vars) + only_tags = options.tags.split(",") # run all playbooks specified on the command line for playbook in args: @@ -78,7 +81,8 @@ def main(args): sudo_user=options.sudo_user, sudo_pass=sudopass, extra_vars=extra_vars, - private_key_file=options.private_key_file + private_key_file=options.private_key_file, + only_tags=only_tags, ) try: diff --git a/examples/playbooks/tags.yml b/examples/playbooks/tags.yml new file mode 100644 index 0000000000..82eaa83333 --- /dev/null +++ b/examples/playbooks/tags.yml @@ -0,0 +1,29 @@ +--- +# tags allow us to run all of a playbook or part of it. +# +# assume: ansible-playbook tags.yml --tags foo +# +# only tags with the given tags will be run when --tags is specified +# +# (an include statement will also be able to set tags on all included +# tasks at some point in the future) + + +- name: example play + hosts: all + user: root + tasks: + - name: hi + tags: foo + action: shell echo "first play ran" + +- name: example play + hosts: all + user: root + tasks: + - name: hi + tags: bar + action: shell echo "second play ran" + + + diff --git a/lib/ansible/playbook/__init__.py b/lib/ansible/playbook/__init__.py index 738e38f9ba..0c1c9ec924 100644 --- a/lib/ansible/playbook/__init__.py +++ b/lib/ansible/playbook/__init__.py @@ -61,7 +61,8 @@ class PlayBook(object): stats = None, sudo = False, sudo_user = C.DEFAULT_SUDO_USER, - extra_vars = None): + extra_vars = None, + only_tags = None): """ playbook: path to a playbook file @@ -87,7 +88,10 @@ class PlayBook(object): if extra_vars is None: extra_vars = {} - + + if only_tags is None: + only_tags = [ 'all' ] + self.module_path = module_path self.forks = forks self.timeout = timeout @@ -105,6 +109,7 @@ class PlayBook(object): self.extra_vars = extra_vars self.global_vars = {} self.private_key_file = private_key_file + self.only_tags = only_tags self.inventory = ansible.inventory.Inventory(host_list) @@ -286,9 +291,17 @@ class PlayBook(object): if play.vars_files and len(play.vars_files) > 0: rc = self._do_setup_step(play, play.vars_files) - # run all the top level tasks, these get run on every node for task in play.tasks(): - self._run_task(play, task, False) + + # 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: + self._run_task(play, task, False) # run notify actions for handler in play.handlers(): diff --git a/lib/ansible/playbook/play.py b/lib/ansible/playbook/play.py index 40274c5108..3acc76945c 100644 --- a/lib/ansible/playbook/play.py +++ b/lib/ansible/playbook/play.py @@ -26,8 +26,10 @@ import os class Play(object): __slots__ = [ - 'hosts', 'name', 'vars', 'vars_prompt', 'vars_files', 'handlers', 'remote_user', 'remote_port', - 'sudo', 'sudo_user', 'transport', 'playbook', '_ds', '_handlers', '_tasks' + 'hosts', 'name', 'vars', 'vars_prompt', 'vars_files', + 'handlers', 'remote_user', 'remote_port', + 'sudo', 'sudo_user', 'transport', 'playbook', + '_ds', '_handlers', '_tasks' ] # ************************************************* @@ -37,6 +39,7 @@ class Play(object): # TODO: more error handling + hosts = ds.get('hosts') if hosts is None: raise errors.AnsibleError('hosts declaration is required') diff --git a/lib/ansible/playbook/task.py b/lib/ansible/playbook/task.py index 304b8ef803..c65de2235c 100644 --- a/lib/ansible/playbook/task.py +++ b/lib/ansible/playbook/task.py @@ -24,7 +24,8 @@ class Task(object): __slots__ = [ 'name', 'action', 'only_if', 'async_seconds', 'async_poll_interval', - 'notify', 'module_name', 'module_args', 'module_vars', 'play', 'notified_by', + 'notify', 'module_name', 'module_args', 'module_vars', + 'play', 'notified_by', 'tags' ] def __init__(self, play, ds, module_vars=None): @@ -38,6 +39,8 @@ class Task(object): self.play = play self.name = ds.get('name', None) self.action = ds.get('action', '') + self.tags = [ 'all' ] + self.notified_by = [] if self.name is None: @@ -67,4 +70,15 @@ class Task(object): if 'first_available_file' in ds: self.module_vars['first_available_file'] = ds.get('first_available_file') + # tags allow certain parts of a playbook to be run without + # running the whole playbook + apply_tags = ds.get('tags', None) + if apply_tags is not None: + if type(apply_tags) in [ str, unicode ]: + self.tags.append(apply_tags) + elif type(apply_tags) == list: + self.tags.extend(apply_tags) + + +