Reputation: 195
In my Ansible project, I have installed libraries using pip
that allow me to use additional jinja2 filters.
Among those, I have installed the netaddr
library that allows me to use the ipaddr
filter, like this: <netmask>{{ interface.IP | ipaddr('netmask') }}</netmask>
(this works in my Ansible playbook)
The problem comes when I try to test my templates without having to run my Ansible playbook by creating a quick Python script. For instance:
from jinja2 import Template
print Template('{{ ip | ipaddr("netmask") }}').render(ip='10.10.10.0/24')
Throws me the following exception:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python2.7/dist-packages/jinja2/environment.py", line 945, in __new__
return env.from_string(source, template_class=cls)
File "/usr/local/lib/python2.7/dist-packages/jinja2/environment.py", line 880, in from_string
return cls.from_code(self, self.compile(source), globals, None)
File "/usr/local/lib/python2.7/dist-packages/jinja2/environment.py", line 591, in compile
self.handle_exception(exc_info, source_hint=source_hint)
File "/usr/local/lib/python2.7/dist-packages/jinja2/environment.py", line 780, in handle_exception
reraise(exc_type, exc_value, tb)
File "<unknown>", line 1, in template
jinja2.exceptions.TemplateAssertionError: no filter named 'ipaddr'
I can see in that last line that jinja2 can't find the 'ipaddr' filter, which is
installed in /usr/lib/python2.7/dist-packages
, but I can't find how to use packages installed via pip directly in my Python scripts.
Upvotes: 1
Views: 2571
Reputation: 121
Yes, you can do this. Ansible needs to be in your python path
import jinja2
from ansible.plugins.filter import ipaddr
template_dir = 'path/to/templates'
fname = 'template.txt'
# get the filters from ansible/plugins/filter/ipaddr
f = ipaddr.FilterModule()
# for a template in your filesystem, set the loader
loader = jinja2.FileSystemLoader(template_dir)
# create the environment
env = jinja2.Environment(loader=loader)
# *** Add the ipaddr filters to the environment ***
env.filters.update(f.filters())
# Set your template
template = env.get_template(fname)
print(template.render(ip='10.10.10.0/24'))
This will make your template file (containing {{ ip | ipaddr("netmask") }}
) output
255.255.255.0
Upvotes: 2
Reputation: 490
I spent the better part of an hour trying to get this working, and it was a total nightmare. I eventually had to give up. While it may still be possible, I wasn't able to get much of anywhere, even creating Environment()
's and trying to load extensions and whatnot.
What I finally ended up doing was to use one of the solutions mentioned here: How can I test jinja2 templates in ansible?
(That solution also mentions some online Jinja2 parsers, but those seemed to suffer from the exact same problems as the interactive Python method.)
What I finally went with was a combination of the Ansible_3 and Ansible_4 solutions -
I created a testfile called nametest.yml
like this:
---
- hosts: 127.0.0.1
tasks:
- name: Test jinja2template
template: src=name.j2 dest=output.conf
Then I put my test Jinja2 stuff in the name.j2
file.
I ran tests like this:
ansible-playbook nametest.yml --check --diff --connection=local -e role=api -e level=prod -e '{"item": {"private_ip": "1.2.3.4"}}' -e debug_instance=false
Those -e
options each set a single variable's value. Mostly I just used the simple name=value
construction, but for the more complex, nested object, I used the JSON version.
Then to run with different variable values, I just twiddled the various command-line arguments to test all of the various branches of code I had set up.
Upvotes: 0