Jinja and Skillets¶
Jinja is a templating language for Python and used within the skillet framework to:
Content objects are collected from the skillet and passed through the rendering engine.
The video tutorial covers Jinja variables, if conditionals, and for loops.
Jinja Variable¶
Variables are used in the Snippets sections of the .meta-cnc.yaml file and externally referenced text files to:
provide simple substitutions for values in an XML or set command configuration file
define variable-based XPaths such as Panorama templates and device-groups
create contextual output pass/fail messages in validation skillets
Variables are added using a {{ variable }}
syntax.
The example below uses three variables: tag_name, tag_color, and tag_description
snippets:
- name: object_tag
xpath: /config/devices/entry[@name="localhost.localdomain"]/vsys/entry[@name="vsys1"]/tag
element: |-
<entry name="{{ tag_name }}">
<color>{{ tag_color }}</color>
<comments>{{ tag_description }}</comments>
</entry>
The skillet player captures the variable values with output as a rendered XML element.
entry name="block_rule">
<color>color1</color>
<comments>block rules based on EDL destinations</comments>
</entry>
Ensuring All Variables Are Defined¶
When working with a large amount of configuration templates, it’s easy to miss a variable definition. Use this one-liner to find them all.
Change directories into a skillet directory and run this to find all configured variables:
grep -r '{{' . | cut -d'{' -f3 | awk '{ print $1 }' | sort -u
Or, if you have perl available, the following may also catch any configuration commands that may have more than one variable defined:
grep -r '{{' . | perl -pne 'chomp(); s/.*?{{ (.*?) }}/$1\n/g;' | sort -u
Jinja If Conditional¶
If conditionals can be used in a skillet to decide if part of an element is rendered based on a variable value.
In this example a dropdown is used in the metadata file to input dhcp or static IP addressing.
variables:
- name: MGMT_TYPE
description: firewall management IP type
default: dhcp-client
type_hint: dropdown
dd_list:
- key: dhcp-client
value: dhcp-client
- key: static
value: static
- name: MGMT_IP
description: NGFW management IP
default: 192.0.2.6
type_hint: ip_address
- name: MGMT_MASK
description: NGFW management netmask
type_hint: ip_address
default: 255.255.255.0
- name: MGMT_DG
description: NGFW management default gateway
default: 192.0.2.7
type_hint: ip_address
Below is the XML element with the if conditional embedded. The conditionals are placed within {% if content %}
using built-in Jinja expressions.
Choosing MGMT_TYPE = static will include the IP address, netmask, and gateway elements while ignoring the DHCP configuration. If the selection is DHCP the inverse is true with only the DHCP settings rendered.
<type>
{%- if MGMT_TYPE == "static" %}
<static/>
{% elif MGMT_TYPE == "dhcp-client" %}
<dhcp-client>
<send-hostname>yes</send-hostname>
<send-client-id>no</send-client-id>
<accept-dhcp-hostname>no</accept-dhcp-hostname>
<accept-dhcp-domain>no</accept-dhcp-domain>
</dhcp-client>
{% else %}
<dhcp-client>
<send-hostname>yes</send-hostname>
<send-client-id>no</send-client-id>
<accept-dhcp-hostname>no</accept-dhcp-hostname>
<accept-dhcp-domain>no</accept-dhcp-domain>
</dhcp-client>
{% endif %}
</type>
{%- if MGMT_TYPE == "static" %}
<ip-address>{{ MGMT_IP }}</ip-address>
<netmask>{{ MGMT_MASK }}</netmask>
<default-gateway>{{ MGMT_DG }}</default-gateway>
{% endif %}
Here is the output if static is selected:
<type>
<static/>
</type>
<ip-address>192.0.2.6</ip-address>
<netmask>255.255.255.0</netmask>
<default-gateway>192.0.2.7</default-gateway>
And the output if dhcp-client is selected:
<type>
<dhcp-client>
<send-hostname>yes</send-hostname>
<send-client-id>no</send-client-id>
<accept-dhcp-hostname>no</accept-dhcp-hostname>
<accept-dhcp-domain>no</accept-dhcp-domain>
</dhcp-client>
</type>
Jinja For Loop¶
For loops can be used in a skillet to capture a list of information and iterate over the list as multiple entries.
In this example a list of serial numbers are onboarded to Panorama.
variables:
- name: serial_number
description: Device serial number
default: 12345
type_hint: list
help_text: basic onboarding to panorama; click + to add additional devices
Below is the XML element with the for loop embedded. The conditionals are placed within {% for content %}
using Jinja built-in for loop logic.
{% for item in serial_number %}
<entry name="{{ item }}"/>
{% endfor %}
The rendered output element is:
<entry name="1234567890"/>
<entry name="1234567891"/>
<entry name="1234567892"/>
adding in each serial number in the variable list.
Jinja Filter¶
Jinja filters have a few roles in skillets:
reformat data
boolean logic for validation tests
output passwords as hashes
These filters can be built-in filters or custom skillet filters.
Filters are used by including `` | filter `` after a variable:
Variable | Filter example |
Filter action |
---|---|
var | length |
True if a list var has values |
var | length == 0 |
True if a list var is empty |
var | md5_hash |
convert a password to a phash |
var | element_value(‘config_value’) == ‘yes’ |
True if the XML config_value = yes |
var | tag_present(‘config_tag’) |
True if the XML tag exists |
var | replace (“old”, “new”) |
replace a string or substring with a new value |
var | int |
convert a string to an integer |
Jinja Whitespace Control¶
Care must usually be taken to ensure no extra whitespace creeps into your templates due to Jinja looping constructs or control characters. For example, consider the following fragment:
<dns-servers>
{% for member in CLIENT_DNS_SUFFIX %}
<member>{{ member }}</member>
{% endfor %}
</dns-servers>
This fragment will result in blank lines being inserted where the for and endfor control tags are placed. To ensure this does not happen and to prevent any unintentioal whitespace, you can use Jinja whitespace control like so:
<dns-servers>
{%- for member in CLIENT_DNS_SUFFIX %}
<member>{{ member }}</member>
{%- endfor %}
</dns-servers>
Note
Note the ‘-‘ after the leading ‘{%’. This instructs jinja to remove these blank lines in the resulting parsed output template.