12

How Do Ansible Tags Work?

 4 years ago
source link: https://www.percona.com/blog/2020/04/27/how-do-ansible-tags-work/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

QR3q2mV.png!web One of the most confusing Ansible features is the tags , and in this blog, I will try to clarify how they work.

A tag is an attribute that you can set to an Ansible structure (plays, roles, tasks), and then when you run a playbook you can use –tags or –skip-tags to execute a subset of tasks.

Let’s look at this basic playbook example:

---
- hosts: localhost
  tasks:
    - name: Install PMM2 client
      yum:
        name: pmm-client2
        state: present
      tags:
      - install
 
    - name: create pmm-admin flags source
      template:
        src: pmm-admin-flags.txt.j2
        dest: /etc/pmm/pmm-admin-flags.txt
      tags:
      - configure
 
    - name: setup pmm-agent
      command: "pmm-agent setup --force @/etc/pmm/pmm-agent-flags.txt"
      tags:
      - configure
...

Looking at the play we can see two tags – install and configure . You can also list all tags in a play using –list-tags

% ansible-playbook tags.yaml --list-tags
 
playbook: tags.yaml
  play #1 (local): local TAGS: []
      TASK TAGS: [configure, install]

You can list all tasks and tags with –list-tasks

% ansible-playbook tags.yaml --list-tasks
 
playbook: tags.yaml
  play #1 (local): local TAGS: []
    tasks:
      Install PMM2 client TAGS: [install]
      create pmm-admin flags source TAGS: [configure]
      setup pmm-agent TAGS: [configure]

Now to see the effect of tags we can –list-tasks applying tags filters to know which tasks will run. 

% ansible-playbook tags.yaml --list-tasks --tags=install
 
playbook: tags.yaml
  play #1 (local): local TAGS: []
    tasks:
      Install PMM2 client TAGS: [install]
% ansible-playbook tags.yaml --list-tasks --tags=configure
 
playbook: tags.yaml
  play #1 (local): local TAGS: []
    tasks:
      create pmm-admin flags source TAGS: [configure]
      setup pmm-agent TAGS: [configure]
% ansible-playbook tags.yaml --list-tasks --skip-tags=install
 
playbook: tags.yaml
  play #1 (local): local TAGS: []
    tasks:
      create pmm-admin flags source TAGS: [configure]
      setup pmm-agent TAGS: [configure]

Ansible Tags Inheritance

So far, so good. Now let’s check how tags inheritance works. When you add a tag to a play or a static import (role or task), the included tasks inherit the tag. Let’s see an example:

---
- hosts: local
  tags:
  - pmm
  tasks:
    - name: Install PMM2 client

This is the same play as before but I just added the pmm tag ( Percona Monitoring and Management ) to play structure. And we can see the inheritance: 

% ansible-playbook tags.yaml --list-tasks
 
playbook: tags.yaml
 
  play #1 (local): local TAGS: [pmm]
    tasks:
      Install PMM2 client TAGS: [install, pmm]
      create pmm-admin flags source TAGS: [configure, pmm]
      setup pmm-agent TAGS: [configure, pmm]

All the tasks now have the pmm tag.

Also, with roles:

- hosts: dbservers,!rds
  become: yes
  roles:
  - role: percona_repo
    tags:
    - percona_repo
    - toolkit
    - pmm
 
  - role: percona-toolkit
    tags:
    - toolkit
 
  - role: pmm
    tags:
    - pmm

When we set a tag on a role structure definition, all the tasks in those roles will inherit the tags. Which, in this case, means that all the tasks in the pmm role will have the pmm tag when we run this playbook.

As I mentioned before, inheritance only works with static imports ( import_* ). For dynamic includes ( include_* ), you would need to explicitly apply the tags.

- name: Static import pmm role
  import_role: pmm
  tags: 
  - pmm
 
- name: Dynamic include pmm role
  include_role: pmm
    apply:
      tags:
      - pmm
  tags: 
  - pmm

Be careful with this if you also have include_tasks inside your roles!Those dynamic included tasks won’t inherit the tag and then won’t run. A common example would be to include tasks based on the OS family.

% cat roles/pmm/tasks/main.yaml
 
---
- name: include {{ ansible_os_family }} tasks
  include_tasks: "{{ ansible_os_family }}.yaml"
    apply:
      tags:
      - pmm
 
% cat roles/pmm/tasks/Debian.yaml
 
---
- name: Install pmm-client with apt
  apt:
    name: pmm-client2
    state: present
 
% cat roles/pmm/tasks/RedHat.yaml
 
---
- name: Install pmm-client with yum
  yum:
    name: pmm-client2
    state: present

In this example, we had to re-apply tags for the dynamic included tasks. You could also be using static imports, but in that case, you would need to import two files and use when:   

- name: import tasks for RedHat
  import_tasks: RedHat.yaml
  when: ansible_os_family == 'RedHat'
 
- name: import tasks for Debian
  import_tasks: Debian.yaml
  when: ansible_os_family == 'Debian'

In this case, imported tasks will inherit tags from the top, but also all tasks on both files will be processed inheriting the when conditional from the import statement. This means that the when condition is not applied to the import_tasks itself, but to all the tasks imported from it. So for large roles, I don’t recommend this, as still all tasks would need to be processed and the output would become hard to analyze.

Special Ansible Tags

As you may see in the latest playbook example, the percona_repo role has three tags: one for itself (percona_repo) and one for each role that installs packages (toolkit, pmm). So what if I keep adding roles for other package management that would require the percona_repo to be present?  In that case, I will need to keep adding more tags to the percona_repo role, which is not efficient. Of course, you can use dependencies on the role’s meta, but for the context of this blog, I will just use tags. For these cases, Ansible has two special tags: always and never

always tagged tasks will run unless you skip it with –skip-tags=always .

never tagged tasks won’t run unless a tag on it is specified to run –tags=never .

Let’s extend the initial sample and add always and never tags.

---
- hosts: localhost
  tags:
  - pmm
  tasks:
  - name: Uninstall PMM2 client
      yum:
        name: pmm-client2
        state: absent
      tags:
      - never
      - uninstall
 
    - name: Install PMM2 client
      yum:
        name: pmm-client2
        state: present
      tags:
      - install
      - always 
 
    - name: create pmm-admin flags source
      template:
        src: pmm-admin-flags.txt.j2
        dest: /etc/pmm/pmm-admin-flags.txt
      tags:
      - configure
 
    - name: setup pmm-agent
      command: "pmm-agent setup --force @/etc/pmm/pmm-agent-flags.txt"
      tags:
      - configure
...

There is now a new task “Uninstall PMM2 client” with the never tag, and “Install PMM2 client” with always.

% ansible-playbook tags.yaml --list-tasks
 
playbook: tags.yaml
 
  play #1 (localhost): localhost TAGS: [pmm]
    tasks:
      Install PMM2 client TAGS: [always, install, pmm]
      create pmm-admin flags source TAGS: [configure, pmm]
      setup pmm-agent TAGS: [configure, pmm]

We can see the never task was skipped as expected.

% ansible-playbook tags.yaml --list-tasks --tags=configure
 
playbook: tags.yaml
 
  play #1 (localhost): localhost TAGS: [pmm]
    tasks:
      Install PMM2 client TAGS: [always, install, pmm]
      create pmm-admin flags source TAGS: [configure, pmm]
      setup pmm-agent TAGS: [configure, pmm]

Running with configure tag, the install task is also executed as we added the always tag.

Be careful with the inheritance effect. As we said:

  • Never tagged tasks run only if one the tags are specified to run
  • Tags are inherited from parent blocks

In this play, we see that the pmm tag is inherited from the play to the tags, so what happens if we run with the pmm tag?

% ansible-playbook tags.yaml --list-tasks --tags=pmm
 
playbook: tags.yaml
 
  play #1 (localhost): localhost TAGS: [pmm]
    tasks:
      Uninstall PMM2 client TAGS: [never, pmm, uninstall]
      Install PMM2 client TAGS: [always, install, pmm]
      create pmm-admin flags source TAGS: [configure, pmm]
      setup pmm-agent TAGS: [configure, pmm]

The task tagged as never is executed as-is, inheriting the pmm tag. So you will need to be careful when you use role tasks that are inheriting tags.

Conclusion

Tags are a powerful feature in Ansible, as it allows you to run a subset of tasks, and are very helpful for large plays. But it is really important to understand how inheritance works, and the difference between dynamic and static imports.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK