using regex in jinja 2 for ansible playbooks

2019-02-09 02:32发布

问题:

HI i am new to jinja2 and trying to use regular expression as shown below

{% if ansible_hostname == 'uat' %}
   {% set server = 'thinkingmonster.com' %}

{% else %}
   {% set server = 'define yourself' %}
{% endif %}

{% if {{ server }} match('*thinking*') %}
  {% set ssl_certificate = 'akash' %}

{% elif {{ server }} match( '*sleeping*')%}
   {% set ssl_certificate = 'akashthakur' %}
{% endif %}

based on the value of "server" i would like to evaluate as which certificates to use. ie if domain contains "thinking" keyword then use these certificates and if it contains "sleeping" keyword then use that certificate.

But didn't found any jinja2 filter supporting this. Please help me.I found some python code and sure that can work but how to use python in jinja2 templates?

回答1:

Jinja2 can quite easily do substr checks with a simple 'in' comparison, e.g.

{% set server = 'www.thinkingmonster.com' %}
{% if 'thinking' in server %}
   do something...
{% endif %}

So your substring regex filter isn't required. However if you want more advanced regex matching, then there are in fact filters available in ansible - see the regex filters in http://docs.ansible.com/playbooks_filters.html#other-useful-filters - funnily enough, your match syntax above is nearly exactly right.

+1 for Bereal's answer though, it gives a nice alternative in the form of a map.



回答2:

There is a "regex_replace" filter available in Ansible>1.6

Other Useful Filters Scroll down and you'll see this:

New in version 1.6.

To replace text in a string with regex, use the “regex_replace” filter:

# convert "ansible" to "able"
{{ 'ansible' | regex_replace('^a.*i(.*)$', 'a\\1') }}

# convert "foobar" to "bar"
{{ 'foobar' | regex_replace('^f.*o(.*)$', '\\1') }}

# convert "localhost:80" to "localhost, 80" using named groups
{{ 'localhost:80' | regex_replace('^(?P<host>.+):(?P<port>\\d+)$', '\\g<host>, \\g<port>') }}

That being said, regex is overkill for finding a solution to this particular problem.



回答3:

Thanks to Steve E. hint, I've figured out a way to add regex in a template condition:

{% if server | regex_search('thinking') %}
....
{% endif %}


回答4:

So after googling for a long time and with the help of some bloggers here is the final solution to my problem:-

1. Jinja2 does not have any filter for finding sub-string or regexp, so the only solution was to create a custom filter. I followed the steps below to fix my problem.

2. Inside the root directory of my playbook, I created a directory "filter_plugins" and wrote a custom module in python and placed the file inside this directory. The name of the python file can be anything. My python code looks as follows:

 __author__ = 'akthakur'
class FilterModule(object):
    ''' Custom filters are loaded by FilterModule objects '''

    def filters(self):
        ''' Filter Module objects return a dict mapping filter names to filter functions. '''
        return {
            'substr': self.substr,
        }

        ''' def substr(self, check,checkin):
        return value1+value2'''
    def substr(self,check,checkin):
         if check in checkin:
            return True
         else:
            return False

3. Once this file is created our brand new filter "substr" is ready to use and can be used inside templates as shown below:

{% if 5==5 %}
 {% set server = 'www.thinkingmonster.com' %}
{% endif %}
{% if 'thinking' | substr(server) %}
   {% set ssl_cert = 'abc.crt'%}
{% endif %}


回答5:

There are some (currently) undocumented filters in Ansible 2.1 which may do what you need:
Ansible plugins/filter.core.py

The regex_search filter will perform a regex on the string and return the resulting match. Something similar to this would work and be contained within an Ansible role:

{% set server = 'www.thinkingmonster.com' %}
{% if regexp_search(server, 'thinking') %}
   do something...
{% endif %}

There is also a regex_findall filter which performs a Python findall search instead of regex.

Review the original pull request for further information



回答6:

To my knowledge, there is no builtin filter for that in Jinja2 neither among Ansible's extra filters, but it's not a big deal to make your own:

certs = {'.*thinking.*': 'akash', '.*sleeping.*': 'akashthakur'}
def map_regex(value, mapping=certs):
    for k, v in mapping.items():
        if re.match(k, value):
            return v 

Then you'll need to add a filter plugin to Ansible, so that it will use the function above in templates (like {{server|ssl_cert}} if you name the filter ssl_cert).

That said, a plain old function or a plain old dictionary that is passed to the templates and used there explicitly might fit this job better.



回答7:

This is rather ugly, but it works as of 1.6.

{% if server|regex_replace('.*thinking.*','matched') == 'matched' %}
  {% set ssl_certificate = 'akash' %}

{% elif server|regex_replace('.*sleeping.*','matched') == 'matched' %}
   {% set ssl_certificate = 'akashthakur' %}
{% endif %}