Proposal: More information about tags in Playbook output

Currently the output of ansible-playbook doesn’t contain any clue about the tags that are being processed.

The can lead to confusion, mostly if a user is developing a playbook which is tag-driven, and forgets to add ‘tags: xyz’ to an action, that action will simply not run and no clue will be given to the user as to why.

It would also be nice to see which tasks are associated with which tags in the output.

Current output:

$ ansible-playbook -c local -tags=“ssh,blahblah” ./setup.yml

PLAY [all] *********************

TASK: [Create directory for mark.] *********************
ok: [localhost]

PLAY RECAP *********************
localhost : ok=3 changed=0 unreachable=0 failed=0

Proposed output:

$ ansible-playbook -c local -tags=“ssh,blahblah” ./setup.yml

PLAY [all] *********************

TASK: [ssh] [Create directory for mark.] *********************
ok: [localhost]

PLAY RECAP *********************
TAGS
processed : ssh
not found : blahblah
found but skipped : ftp,www
untagged tasks skipped : 4

HOSTS
localhost : ok=3 changed=0 unreachable=0 failed=0

You can see above that the ‘ssh’ tag appears alongside the task output, so that the user knows which tag triggered that task, and if their task names are generic, like for example ‘Create directory’, they can see which tag it’s related to. Instead of naming the task ‘ssh | Create directory’ which is redundant information in the task definition.

Then, in the recap we have:

processed: The tags that had tasks associated with them, and were executed.

not found: Tags that were specified in the execution of the playbook, but no tasks were found with this tag. In the example, there were no tasks associated with ‘blahblah’.
found but skipped: There were actions associated with these tags found in the playbook, however they were skipped because the user did not give the command to run these.
untagged tasks skipped: the total number of tasks with no tags, that were skipped.

I started looking into this, but found that it’s not currently possible to pass the name of the tag down into the Runner. So this makes displaying the tag next to the task difficult without some refactoring.

I’m also looking for some hints of what kind of architecture you would like for something like this, before making it. Should I extend AggregateStats() to support this? That currently fits with per-host stats, not really with playbook stats so I was hesitant. Perhaps a new stats object?

Thanks!
Mark

Currently the output of ansible-playbook doesn't contain any clue about the
tags that are being processed.

There's the fact that you executed them with "--tags" on the command line.

The can lead to confusion, mostly if a user is developing a playbook which
is tag-driven, and forgets to add 'tags: xyz' to an action, that action will
simply not run and no clue will be given to the user as to why.

This is only true if they supply --tags to ansible-playbook.
Otherwise it runs everything.

    ...

    PLAY RECAP *********************
    TAGS
    processed : ssh
    not found : blahblah
    found but skipped : ftp,www
    untagged tasks skipped : 4

Processed would include everything given to --tags

Found but skipped? That would include everything else and could be a
ginormous list.

I don't think I find this neccessary and makes more things to skim
over when reading playbook output.

    HOSTS
    localhost : ok=3 changed=0 unreachable=0
failed=0

You can see above that the 'ssh' tag appears alongside the task output, so
that the user knows which tag triggered that task, and if their task names
are generic, like for example 'Create directory', they can see which tag
it's related to. Instead of naming the task 'ssh | Create directory' which
is redundant information in the task definition.

Then, in the recap we have:

processed: The tags that had tasks associated with them, and were
executed.

not found: Tags that were specified in the execution of the playbook, but
no tasks were found with this tag. In the example, there were no tasks
associated with 'blahblah'.
found but skipped: There were actions associated with these tags found in
the playbook, however they were skipped because the user did not give the
command to run these.
untagged tasks skipped: the total number of tasks with no tags, that were
skipped.

I started looking into this, but found that it's not currently possible to
pass the name of the tag down into the Runner. So this makes displaying the
tag next to the task difficult without some refactoring.

I'm also looking for some hints of what kind of architecture you would like
for something like this, before making it. Should I extend AggregateStats()
to support this? That currently fits with per-host stats, not really with
playbook stats so I was hesitant. Perhaps a new stats object?

I'm not interested in this feature.

Processed would include everything given to --tags

Nope, it would only include those that were given to --tags and actually had tasks associated with them. So if you make a typo in one of your tags, you will be alerted to this fact after the playbook has run. This is important when dealing with long playbooks that have many tags For example:

ansible-playbook --tags=“ssh,ftp,common,redi,postgres”

What if my playbook tags are actually named ‘redis’ and ‘postgresql’? Currently, the playbook will appear to execute perfectly fine, and you actually have to read every line looking to make sure that the tags you specified were executed.

To make the task of manually reviewing the output easier, you can name the tasks after their associated tags:

  • name: postgresql | Do some things…

action: postgresql_user …

tags: postgresql

Now I have included the tag ‘postgresql’ under both ‘tags’ and ‘name’, which is redundant information in the playbook and hurts readability.

Even after doing this however, you still have to go back through every line and make sure your major component tasks were all executed, which is why I suggested the summary lines at the bottom, too.

Found but skipped? That would include everything else and could be a

ginormous list.

I don’t think it will be ginormous. Perhaps a survey of user’s typical needs would be good, but I find my playbooks contain < 8 tags in total.

The point of this statistic is that it will find typos in your playbook. If you have a task with a tag typo, you won’t know that it’s not running.

Please, consider the example from above, imagine the output after the playbook was:

processed : ssh,ftp,common
not found : redi,postgres
found but skipped : comon

untagged tasks skipped : 2

That’s incredibly rich debugging information. Just from ‘not found’ I know I have mis-spelled ‘redi’ and ‘postgres’ in --tags. I also know that I have a task in my playbook that is misspelled too: ‘comon’ instead of ‘common’. I also know that there are 2 tasks that have no tags, also an error in my playbook.

I don’t think I find this neccessary and makes more things to skim
over when reading playbook output.

How about we only show this summary when the --tags parameter is passed to ansible-playbook? Or perhaps only in -debug or -verbose mode (although I think --tags would be better)?

  • Mark

Two thoughts:

  1. We could output this information before the play runs, by doing static analysis of the YML play structure against the --tags given. That will make it immediately apparent if something is wrong, and the user can review it while the facts gather. This should be a cheap and quick operation and it won’t display with the recap section at the bottom, meaning less information at the end.

  2. In my example below, I made a mistake. The true output would be (note the bolded parts that I left out):

processed : ssh,ftp,common
not found : redi,postgres
found but skipped : comon**,redis,postgresql**

untagged tasks skipped : 2

Which is even more informative: it tells me not only which tags were not found, but also the names of the tags I actually meant to call.

  • Mark

I do not want to see this in the output.

I will consider a patch that raises an error if you mention a tag that
is not listed in any of the included playbooks, but this must occur
*prior* to playbooks being executed.

Keep in mind both tasks and plays can be tagged.

Here is a patch that raises an error when incorrect tags are given:

https://github.com/marktheunissen/ansible/compare/tags-error-checking

I have tested using tagged playbooks, tagged tasks, and included tasks. Example of what happens when incorrect tag is given:

ansible-playbook --tags=“bash,gt,blah,ssh,mount” ./setup.yml

ERROR: tags “gt, blah” given as argument but not found in playbooks, did you mean one of “git, prompt, general”?

Ansible will tell you which tag it did not recognize, and then prompt you to choose from a list of tags that were found in the playbooks but not included in --tags.

  • Mark

This looks good and is a big improvement over having to read the
output at the bottom of the playbook run -- I haven't tested this yet,
did you account for both plays and tasks being taggable? (It may be
that this is covered already because tagging a play implies tagging
all of the tasks)?

Another thing that needs to be fixed is probably the un-removal of this part:
        if not play.should_run(self.only_tags):
            return

This keeps output concise by skipping plays Ansible knows you don't
need to run. For instance if you give --tags foo and have 37
different plays, but only two plays with tasks relating to 'foo', it
will only output those two.

This looks good and is a big improvement over having to read the
output at the bottom of the playbook run – I haven’t tested this yet,
did you account for both plays and tasks being taggable? (It may be
that this is covered already because tagging a play implies tagging
all of the tasks)?

Yes, precisely - the tasks become tagged and it works seamlessly. I have tested this, but obviously give it another run-through.

Another thing that needs to be fixed is probably the un-removal of this part:
if not play.should_run(self.only_tags):
return

This keeps output concise by skipping plays Ansible knows you don’t
need to run. For instance if you give --tags foo and have 37
different plays, but only two plays with tasks relating to ‘foo’, it
will only output those two.

Plays that don’t need to run are still skipped. The logic is just a little different to avoid iterating through all task tags twice. See:

if (len(matched_tags) > 0 or len(play.tasks()) == 0):
plays.append(play)

We only append our play to the list of plays that will be run if there are tags matched or zero tasks, this is using the same logic that was in should_run(), albeit distilled to a single statement as that’s all that’s necessary.

Pull request sent.

  • Mark