Search code examples
escapingjinja2salt-project

How to escape special characters in a Jinja2 macro?


I'm writing a macro that returns a cronjob syntax, something like this:

{%- macro passive_check(state, service) -%}
{%- set state_checks = salt['monitoring.discover_checks_passive'](state) %}
{% for host in pillar['shinken_pollers'] %}
*/{{ state_checks[service]['passive_interval'] }} * * * * nagios output=$({{ state_checks[service]['check_command'] }}); return_code=$?; printf "%s\t%s\t%s\t%s\n" "{{ grains['id'] }}" "{{ service }}" "$return_code" "$output" | /usr/local/nagios/bin/py_send_nsca -H {{ host }} -c /etc/send_nsca.conf
{%- endfor -%}
{%- endmacro %}

then in a .sls file it is called as:

{% from 'nrpe/passive.sls' import passive_check with context %}

{%- for state in pillar['monitoring']['states'] -%}
{%- for name in salt['monitoring.discover_checks_passive'](state) %}
/etc/cron.d/passive-checks:
  file:
    - append
    - text: |
      {{ passive_check(state, name)|safe }}
{%- endfor -%}
{%- endfor %}

but I got the below error when running:

    Rendering SLS rsyslog.nrpe failed, render error: while scanning an alias
  in "<unicode string>", line 29, column 1:
    */5 * * * * nagios output=$(/usr ... 
    ^
expected alphabetic or numeric character, but found '/'
  in "<unicode string>", line 29, column 2:
    */5 * * * * nagios output=$(/usr/ ... 
     ^

Manual escaping with |e also returns the same error.

So the question is: how to escape these characters: *, /, ... in a Jinja2 macro?


Solution

  • You may be surprised but no need to escape these characters.

    The culprit that causes the error expected alphabetic or numeric character, but found '/' is... whitespace control. Pay attention to the macro:

    {%- set state_checks = salt['monitoring.discover_checks_passive'](state) %}
    {% for host in pillar['shinken_pollers'] %}
    */{{ state_checks[service]['passive_interval'] }} * * * * nagios output=$({{ state_checks[service]['check_command'] }}); return_code=$?; printf "%s\t%s\t%s\t%s\n" "{{ grains['id'] }}" "{{ service }}" "$return_code" "$output" | /usr/local/nagios/bin/py_send_nsca -H {{ host }} -c /etc/send_nsca.conf
    

    Without minus sign (-) at the end of the set and for block, it will be rendered to something like:

    /etc/cron.d/passive-checks:
      file:
        - append
        - text: |
    
    
    */5 * * * * nagios output=$/usr/lib/nagios/plugins/check_procs -c 1:1 -C rsyslogd -u syslog; return_code=$?; printf "%s\t%s\t%s\t%s\n" "q-mail" "rsyslogd_proc
    s" "$return_code" "$output" | /usr/local/nagios/bin/py_send_nsca -H host1 -c /etc/send_nsca.conf
    */5 * * * * nagios output=$/usr/lib/nagios/plugins/check_procs -c 1:1 -C rsyslogd -u syslog; return_code=$?; printf "%s\t%s\t%s\t%s\n" "q-mail" "rsyslogd_proc
    s" "$return_code" "$output" | /usr/local/nagios/bin/py_send_nsca -H host2 -c /etc/send_nsca.conf
    

    So you have to remove the blank lines:

    {%- macro passive_check(state, service) -%}
    {%- set state_checks = salt['monitoring.discover_checks_passive'](state) -%}
    {%- for host in pillar['shinken_pollers'] -%}
    */{{ state_checks[service]['passive_interval'] }} * * * * nagios output=$({{ state_checks[service]['check_command'] }}); return_code=$?; printf "\%s\t\%s\t\%s\t\%s\n" "{{ grains['id'] }}" "{{ service }}" "$return_code" "$output" | /usr/local/nagios/bin/py_send_nsca -H {{ host }} -c /etc/send_nsca.conf
    {% endfor -%}
    {%- endmacro %}
    

    (Notice that I have to escape the percent sign to make it work in crontab)

    and put an indentation when calling in file.append state:

    {%- for state in pillar['monitoring']['states'] -%}
    {%- for name in salt['monitoring.discover_checks_passive'](state) %}
    /etc/cron.d/passive-checks:
      file:
        - append
        - text: |
            {{ passive_check(state, name)|indent(8) }}
        - require:
          - file: touch_cron
    {%- endfor -%}
    {%- endfor %}