Creating your own jinja filter for Ansible

If there is one thing I don't like in Ansible, or automation tools in general, is that, as a developer, I quickly hit the limit of things you can do out of the box.  One of those things is the limitation of Jinja.  It has a bunch of filters and functions, and each time I want something, it's not there.

That's why I quickly went looking to create my own filter plugins.  And it's so simple and so powerfull, so I have started to create my own filter plugin libraries, which make my life so much easier.

So lets create one together and see what we can do with it.


What is a filter

A filter in jinja is a function, a piece of code, where you "pipe" variables to, to filter or transform that input.

A few examples : (https://ansible-docs.readthedocs.io/zh/stable-2.0/rst/playbooks_filters.html)

  • Filtering arrays
    • {{ some_list | max }}
    • {{ some_list | unique }}
  • Splitting strings or joining arrays 
    • {{ some_variable | split(',') }}
    • {{ some_list | join(',')) }}
  • Setting Default values in case of null/undefined 
    • {{ some_variable | default(5) }}
  • Convert data 
    • {{ some_data | to_json }}
    • {{ some_date | to_yaml }}

How to create your own

In your folder structure, start with creating a folder called "filter_plugins".
And in that folder create a new file with a name that suits your filter(s).  Don't be shy to think big and make your own customer library.

So let's create one and call ours "ansibleguy_filter_lib.py"

Inside the file we start with this template, the basic structure for a filterplugin

class FilterModule(object):

  def filters(self):
    return {

    }
Next we make a function that actually does something.  
Let's create something simple but still usefull.  Let's create a function that creates a timestamp based on a pattern.  So the pattern is the input.  The timestamp is the output.
def timestamp(pattern):
  from datetime import datetime
  now = datetime.now()
  timestamp = now.strftime(pattern)
  return timestamp
      
print(timestamp("%Y%m%d"))

you can easily test this at https://www.programiz.com/python-programming/online-compiler/

Since we will add this function as a method in the filtermodule class, we will have to add the "self" parameter to the function, like this :

def timestamp(self,pattern):
  from datetime import datetime
  now = datetime.now()
  timestamp = now.strftime(pattern)
  return timestamp
Now we add the function to our class and register it as a filter
class FilterModule(object):
  def timestamp(self,pattern):
    from datetime import datetime
    now = datetime.now()
    timestamp = now.strftime(pattern)
    return timestamp
    
  def filters(self):
    return {
      'timestamp':self.timestamp
    }
You can now use this filter like this :

"{{ '%Y%m%d' | timestamp }}"

Note that we don't actually run the filter like "timestamp(pattern)", but rather "pipe" the pattern into the filter.

Let us add a second filter.  Last week I just happened to need a filter that could convert ascii to hex (it was actually to convert an ascii based lun serial number to a hex based lun serial number)
class FilterModule(object):
  '''
  creates a timestamp
  '''
  def timestamp(self,pattern):
    from datetime import datetime
    now = datetime.now()
    timestamp = now.strftime(pattern)
    return timestamp
    
  '''
  converts a string from ascii to hex
  '''
  def ascii_to_hex(self,ascii):
    return ascii.encode("ascii").hex()    
    
  def filters(self):
    return {
      'timestamp':self.timestamp,
      'ascii_to_hex':self.ascii_to_hex
    }

What about multiple parameters ?

If you want to use multiple parameters, you can just add them, but note that the piped input will go into the first parameter.  

For example the existing join filter :

{{ some_list | join(',') }}

the internal function will look like :

def join(self,list,delimeter):
      return list.join(delimeter)
So this was my first post of this blog. I hope you find it interesting. I will soon add more advanced filters, which will make your life so much easier.

Post a Comment

0 Comments