- Introduction
- Awesomeness of Include
- Filename Variable Substitution
- Static Include
- Dynamic Include
- Conclusion
Introduction
Include mechanism is one of my favorite features about Ansible because it is the key to adhere to the principle of DRY that stands for Don’t Repeat Yourself which is of great importance to write reusable and maintainable Ansible code.
This post won’t cover every piece of the include feature. Please go through the docs from Ansible docs site before continuing if you are not familiar with Ansible include. link 1, link 2.
There are 2 kinds of includes, namely dynamic and static. Essentially, at the moment. Any include in Ansible is a form of tasks expanding. When a file A includes file B, tasks defined in B will be added to file A. The difference between the two is that included tasks will be expanded during parsing time in static include while in the other case tasks will be expanded when playbooks are running.
There is a repo in GitHub intended for demonstration and readers are supposed to be familiar with that. The idea comes from OpenStack’s release convention through which each major release will have a numeric version and a associated code name such as kilo
and ocata
. The convention is similar to Ubuntu’s. Assume that the OpenStack infrastructure is managed by Ansible and sometimes the gap between different major release is so huge that you have to create dedicated tasks for each major release.
Awesomeness of Include
We know that include could be applied to tasks, plays and handlers. We also know that include could be dynamic or static. So far there are 2 features exciting me.
- Filename variable substitution
- Include with complex controls such as for loop which is impossible without dynamic include
What makes them awesome is that they result in more clean Ansible code and thus more maintainable. Instead of being shipped when Ansible was born, they were added through the evolution of Ansible.
- Filename variable substitution has been added since 1.7
- Dynamic include was introduced for the first time in 2.0
Filename Variable Substitution
In the old days, variables in names of included files were not allowed. Take the demo repo as an example, anyone who wants to switch tasks based on the codename, he will have to write follow Ansible code.
1 2 3 4 5 6 |
|
Not bad so far since there are only 2 codenames. But what if you have 10 codenames? This definitely violates the DRY principle. With filename variable substitution, the code could become much more clean.
1 2 |
|
Now every one is happy with filename variable substitution because there is much less code to maintain. And as we know, there are multiple places to define variable in Ansible such as inventory vars, playbooks’ vars
or vars_files
sections, roles’ defaults
or vars
directories, command lines’ --extra-vars
parameters and so on. Are the variables used in filename allowed to be defined anywhere? The short answer is NO to static include and YES to dynamic include. More detail will be provided in following sections, just keep in mind that variable substitution is applied to both static and dynamic include.
Static Include
Refer to the doc to check how to identify a static include. You should learn when an include will be considered as static.
Variable Substitution
As mentioned before, static include is parsed during compile time at which some variables such as those defined as inventory ones are not available, thus in static includes, variables used in names are not allowed to be defined in the places other than the following 3 places.
- Extra vars in the command line. example
vars
section in playbook. example- Files which will be included in the
vars_files
section of playbook. example
Handler Include
The doc points out that it is impossible to notify trigger handlers from dynamic includes. The correct usage of handler include would looks like this.
1 2 3 |
|
The static
option introduced in 2.1 deserves more explanation.
- Before 2.0, all includes were static, there is no need to add the
static
option. - In 2.0, all includes were considered as dynamic, the code will not works even the variables are defined following the rules described in previous section. No errors occurs but the handlers will never be triggered.
- After 2.0, since handler includes must be static, the
static
modifier has to be added because includes with filename variables are considered dynamic unless forced to be static. Of course,task_includes_static
andhandler_includes_static
could be leveraged. IMHO, it is not a good idea to override the default behavior of Ansible unless you have to.
Dynamic Include
Dynamic include is relatively trivial because it is just a form of task which means it is possible to combine include and other complex control facilities such as for loop.
There are several differences between static and dynamic include.
- The time to expand the tasks. Dynamically included tasks will not get expanded until the include task is reached.
- Play and handler includes must be static.
It is worth noting that except for the circumstances under which the includes must be static, the gap between the two is really tiny. If both forms are applicable, they should result in the same state. In other words, they are equivalent in that case. If anyone find an exception, please drop me a comment.
Conclusion
- Filename variable substitution is applicable for both static and dynamic include.
- Play and handler includes must be static.
- Static and dynamic includes are equivalent if they are applicable at the same time.
- Ansible is not 100% stable so far, pay attention to the the change log of every releases.