Drizzt
Drizzt

Reputation: 195

How to use pip-installed jinja2 filters in Python

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

Answers (2)

Tarah
Tarah

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

Uberbrady
Uberbrady

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

Related Questions