1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

Merge pull request #10183 from bcoca/tag_control

adds complex tag management
This commit is contained in:
Brian Coca 2015-02-25 10:36:11 -05:00
commit c81d981164
7 changed files with 138 additions and 67 deletions

View file

@ -227,22 +227,6 @@ def main(args):
label = play.name label = play.name
hosts = pb.inventory.list_hosts(play.hosts) hosts = pb.inventory.list_hosts(play.hosts)
# Filter all tasks by given tags
if pb.only_tags != 'all':
if options.subset and not hosts:
continue
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')
unknown_tags = ((set(pb.only_tags) | set(pb.skip_tags)) -
(matched_tags | unmatched_tags))
if unknown_tags:
continue
if options.listhosts: if options.listhosts:
print ' play #%d (%s): host count=%d' % (playnum, label, len(hosts)) print ' play #%d (%s): host count=%d' % (playnum, label, len(hosts))
for host in hosts: for host in hosts:
@ -251,9 +235,7 @@ def main(args):
if options.listtasks: if options.listtasks:
print ' play #%d (%s):' % (playnum, label) print ' play #%d (%s):' % (playnum, label)
for task in play.tasks(): for task in pb.tasks_to_run_in_play(play):
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

View file

@ -40,6 +40,28 @@ And you may also tag basic include statements::
Both of these have the function of tagging every single task inside the include statement. Both of these have the function of tagging every single task inside the include statement.
Special Tags
````````````
There is a special 'always' tag that will always run a task, unless specifically skipped (--skip-tags always)
Example::
tasks:
- debug: msg="Always runs"
tags:
- always
- debug: msg="runs when you use tag1"
tags:
- tag1
There are another 3 special keywords for tags, 'tagged', 'untagged' and 'all', which run only tagged, only untagged
and all tasks respectively. By default ansible runs as if --tags all had been specified.
.. seealso:: .. seealso::
:doc:`playbooks` :doc:`playbooks`

View file

@ -36,6 +36,7 @@ import pipes
# holds all other variables about a host # holds all other variables about a host
SETUP_CACHE = ansible.cache.FactCache() SETUP_CACHE = ansible.cache.FactCache()
VARS_CACHE = collections.defaultdict(dict) VARS_CACHE = collections.defaultdict(dict)
RESERVED_TAGS = ['all','tagged','untagged','always']
class PlayBook(object): class PlayBook(object):
@ -314,6 +315,7 @@ class PlayBook(object):
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
@ -332,10 +334,13 @@ class PlayBook(object):
# the user can correct the arguments. # the user can correct the arguments.
unknown_tags = ((set(self.only_tags) | set(self.skip_tags)) - unknown_tags = ((set(self.only_tags) | set(self.skip_tags)) -
(matched_tags_all | unmatched_tags_all)) (matched_tags_all | unmatched_tags_all))
unknown_tags.discard('all')
for t in RESERVED_TAGS:
unknown_tags.discard(t)
if len(unknown_tags) > 0: if len(unknown_tags) > 0:
unmatched_tags_all.discard('all') for t in RESERVED_TAGS:
unmatched_tags_all.discard(t)
msg = 'tag(s) not found in playbook: %s. possible values: %s' msg = 'tag(s) not found in playbook: %s. possible values: %s'
unknown = ','.join(sorted(unknown_tags)) unknown = ','.join(sorted(unknown_tags))
unmatched = ','.join(sorted(unmatched_tags_all)) unmatched = ','.join(sorted(unmatched_tags_all))
@ -667,7 +672,53 @@ class PlayBook(object):
return filename return filename
# ***************************************************** # *****************************************************
def tasks_to_run_in_play(self, play):
tasks = []
for task in play.tasks():
# only run the task if the requested tags match or has 'always' tag
u = set(['untagged'])
task_set = set(task.tags)
if 'always' in task.tags:
should_run = True
else:
if 'all' in self.only_tags:
should_run = True
else:
should_run = False
if 'tagged' in self.only_tags:
if task_set != u:
should_run = True
elif 'untagged' in self.only_tags:
if task_set == u:
should_run = True
else:
if task_set.intersection(self.only_tags):
should_run = True
# Check for tags that we need to skip
if 'all' in self.skip_tags:
should_run = False
else:
if 'tagged' in self.skip_tags:
if task_set != u:
should_run = False
elif 'untagged' in self.skip_tags:
if task_set == u:
should_run = False
else:
if should_run:
if task_set.intersection(self.skip_tags):
should_run = False
if should_run:
tasks.append(task)
return tasks
# *****************************************************
def _run_play(self, play): def _run_play(self, play):
''' run a list of tasks for a given pattern, in order ''' ''' run a list of tasks for a given pattern, in order '''
@ -720,7 +771,7 @@ class PlayBook(object):
play._play_hosts = self._trim_unavailable_hosts(on_hosts) play._play_hosts = self._trim_unavailable_hosts(on_hosts)
self.inventory.also_restrict_to(on_hosts) self.inventory.also_restrict_to(on_hosts)
for task in play.tasks(): for task in self.tasks_to_run_in_play(play):
if task.meta is not None: if task.meta is not None:
# meta tasks can force handlers to run mid-play # meta tasks can force handlers to run mid-play
@ -730,22 +781,6 @@ class PlayBook(object):
# skip calling the handler till the play is finished # skip calling the handler till the play is finished
continue continue
# 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
# Check for tags that we need to skip
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

View file

@ -669,20 +669,6 @@ class Play(object):
# ************************************************* # *************************************************
def _is_valid_tag(self, tag_list):
"""
Check to see if the list of tags passed in is in the list of tags
we only want (playbook.only_tags), or if it is not in the list of
tags we don't want (playbook.skip_tags).
"""
matched_skip_tags = set(tag_list) & set(self.playbook.skip_tags)
matched_only_tags = set(tag_list) & set(self.playbook.only_tags)
if len(matched_skip_tags) > 0 or (self.playbook.only_tags != ['all'] and len(matched_only_tags) == 0):
return False
return True
# *************************************************
def tasks(self): def tasks(self):
''' return task objects for this play ''' ''' return task objects for this play '''
return self._tasks return self._tasks
@ -783,8 +769,27 @@ class Play(object):
# compare the lists of tags using sets and return the matched and unmatched # compare the lists of tags using sets and return the matched and unmatched
all_tags_set = set(all_tags) all_tags_set = set(all_tags)
tags_set = set(tags) tags_set = set(tags)
matched_tags = all_tags_set & tags_set
unmatched_tags = all_tags_set - tags_set matched_tags = all_tags_set.intersection(tags_set)
unmatched_tags = all_tags_set.difference(tags_set)
a = set(['always'])
u = set(['untagged'])
if 'always' in all_tags_set:
matched_tags = matched_tags.union(a)
unmatched_tags = all_tags_set.difference(a)
if 'all' in tags_set:
matched_tags = matched_tags.union(all_tags_set)
unmatched_tags = set()
if 'tagged' in tags_set:
matched_tags = all_tags_set.difference(u)
unmatched_tags = u
if 'untagged' in tags_set and 'untagged' in all_tags_set:
matched_tags = matched_tags.union(u)
unmatched_tags = unmatched_tags.difference(u)
return matched_tags, unmatched_tags return matched_tags, unmatched_tags

View file

@ -129,7 +129,7 @@ class Task(object):
# load various attributes # load various attributes
self.name = ds.get('name', None) self.name = ds.get('name', None)
self.tags = [ 'all' ] self.tags = [ 'untagged' ]
self.register = ds.get('register', None) self.register = ds.get('register', None)
self.sudo = utils.boolean(ds.get('sudo', play.sudo)) self.sudo = utils.boolean(ds.get('sudo', play.sudo))
self.su = utils.boolean(ds.get('su', play.su)) self.su = utils.boolean(ds.get('su', play.su))
@ -316,6 +316,9 @@ class Task(object):
self.tags.extend(apply_tags) self.tags.extend(apply_tags)
self.tags.extend(import_tags) self.tags.extend(import_tags)
if len(self.tags) > 1:
self.tags.remove('untagged')
if additional_conditions: if additional_conditions:
new_conditions = additional_conditions[:] new_conditions = additional_conditions[:]
if self.when: if self.when:

View file

@ -21,7 +21,7 @@ VAULT_PASSWORD_FILE = vault-password
CONSUL_RUNNING := $(shell python consul_running.py) CONSUL_RUNNING := $(shell python consul_running.py)
all: parsing test_var_precedence unicode non_destructive destructive includes check_mode test_hash test_handlers test_group_by test_vault all: parsing test_var_precedence unicode non_destructive destructive includes check_mode test_hash test_handlers test_group_by test_vault test_tags
parsing: parsing:
ansible-playbook bad_parsing.yml -i $(INVENTORY) -e @$(VARS_FILE) $(CREDENTIALS_ARG) -vvv $(TEST_FLAGS) --tags prepare,common,scenario1; [ $$? -eq 3 ] ansible-playbook bad_parsing.yml -i $(INVENTORY) -e @$(VARS_FILE) $(CREDENTIALS_ARG) -vvv $(TEST_FLAGS) --tags prepare,common,scenario1; [ $$? -eq 3 ]
@ -82,6 +82,15 @@ test_delegate_to:
test_winrm: test_winrm:
ansible-playbook test_winrm.yml -i inventory.winrm -e @$(VARS_FILE) $(CREDENTIALS_ARG) -v $(TEST_FLAGS) ansible-playbook test_winrm.yml -i inventory.winrm -e @$(VARS_FILE) $(CREDENTIALS_ARG) -v $(TEST_FLAGS)
test_tags:
# Run everything by default
[ "$$(ansible-playbook --list-tasks test_tags.yml -i $(INVENTORY) -e @$(VARS_FILE) $(CREDENTIALS_ARG) -v $(TEST_FLAGS) | fgrep Task_with | xargs)" = "Task_with_tag Task_with_always_tag Task_without_tag" ]
# Run the exact tags, and always
[ "$$(ansible-playbook --list-tasks --tags tag test_tags.yml -i $(INVENTORY) -e @$(VARS_FILE) $(CREDENTIALS_ARG) -v $(TEST_FLAGS) | fgrep Task_with | xargs)" = "Task_with_tag Task_with_always_tag" ]
# Skip one tag
[ "$$(ansible-playbook --list-tasks --skip-tags tag test_tags.yml -i $(INVENTORY) -e @$(VARS_FILE) $(CREDENTIALS_ARG) -v $(TEST_FLAGS) | fgrep Task_with | xargs)" = "Task_with_always_tag Task_without_tag" ]
cloud: amazon rackspace cloud: amazon rackspace
cloud_cleanup: amazon_cleanup rackspace_cleanup cloud_cleanup: amazon_cleanup rackspace_cleanup

View file

@ -0,0 +1,15 @@
---
- name: verify tags work as expected
hosts: localhost
gather_facts: False
connection: local
tasks:
- name: Task_with_tag
debug: msg=
tags: tag
- name: Task_with_always_tag
debug: msg=
tags: always
- name: Task_without_tag
debug: msg=