One of the first roadblocks you’ll hit in your “let’s master Ansible” journey will be a weird error deep inside a Jinja2 template. Can we manage that complexity somehow… or as one of the participants in our Building Network Automation Solutions online course asked:
Is there any recommendation/best practices on Jinja templates size and/or complexity, when is it time to split single template into function portions, what do you guys do? And what is better in terms of where to put logic - into jinja or playbooks
One of my friends described the challenge as “Debugging Ansible is one of the most terrible experiences one can endure…” and debugging Jinja2 errors within Ansible playbooks is even worse, but there are still a few things you can do.
To start with, I would strongly recommend keeping Jinja2 templates (and Ansible playbooks) as simple as possible, and I found two ways to manage large Jinja2 templates:
- Split them into smaller templates and include those, or use multiple templates and Ansible assemble module. That helps readability (and your sanity) but does nothing to abstract the complexity;
- Extract common code into Jinja2 macros, potentially building a library of those macros that can be included into other templates.
Also, don’t debug Jinja2 templates in Ansible, because Ansible passes templates into Jinja2 engine as strings, resulting in error messages along the lines of “there’s something wrong with this template”. It’s much better to write a simple Python renderer that pushes YAML data through a Jinja2 file template. See also: debugging Jinja2 templates (part of our Ansible and Network Automation online courses)
Finally, anything reasonably complex deserves a custom Python plugin - you’ll find simple examples in our Ansible course and the corresponding Github repo. If you’re not fluent in Python start with macros, and when you know what needs to be done pass the specs to a decent Python programmer (“decent” just to make sure you don’t get spaghetti code back).
Mat Wood described exactly the dilemma described in this blog post in his Managed Configurations presentation. They decided to go for a custom Python filter instead of a Jinja2 macro every time they found something that had to be done several times in multiple places, or something that was too complex for Jinja2 (like my horrific write-only code that generated don’t-care subnet bits).