Jinja2 whitespace controls doesn't work as expected with IF block - jinja2

First of all: I read thoroughly documentation about whitespace control in Jinja documentation!
Have simple Jinja template:
New client has been created.
{% if not client.address %}!! Client does not have address{% endif %}
{% if not client.first_name or not client.last_name %}!! Client does not have first/last names{% endif %}
`C{{ client.id }} {{ client.full_name or "##MISSING##" }}
{{ client.status }}
When client hasn't address and first name it is rendering correct:
New client has been created.
!! Client does not have address
!! Client does not have first/last names
`C123 Some Name
pending
But when client has address or name:
New client has been created.
`C123 Some Name
pending
if expectedly produces empty lines.
Reason why I cannot user {%- if -%}: in this case second example rendering correctly BUT first example joins together both lines with "ifs".
Question: how to keep lines separate in the case then if conditions are true AND remove newlines when it falsy?
UPD: This is how I instantiate my Jinja environment:
from jinja2 import Environment, PackageLoader, select_autoescape
loader = PackageLoader("tally2bot", "templates")
env = Environment(
loader=loader,
autoescape=select_autoescape(["html", "xml", "jinja2"])
)

try this snippet below:
New client has been created.
{%- if not client.address %}
!! Client does not have address
{%- endif %}
{%- if not client.first_name or not client.last_name %}
!! Client does not have first/last names
{%- endif %}
`C{{ client.id }} {{ client.full_name or "##MISSING##" }}
{{ client.status }}
__ init__.py
from jinja2 import Environment, PackageLoader, select_autoescape
env = Environment(
loader=PackageLoader("app", "templates"),
autoescape=select_autoescape(["html", "xml", "jinja2"])
)
template = env.get_template('index.html')
print(template.render({'client': None}))
print('--------------------------------------------------------------------------------')
client = {
'id': 123,
'first_name': 'John',
'last_name': 'DOE',
'full_name': 'John DOE',
'address': 'CA',
'status': 'pending'
}
print(template.render({'client': client}))
result:
(venv) PS D:\dev\python\app> py .\__init__.py
New client has been created.
!! Client does not have address
!! Client does not have first/last names
`C ##MISSING##
pending
--------------------------------------------------------------------------------
New client has been created.
`C123 John DOE
pending

Related

SaltStack check if grain exists in Jinja file

I'm using SaltStack to manage my infra. Machines are hosted in different DCs, so they also have slightly different network setup.
Currently, I'm running into the following issue:
Comment: Unable to manage file: Jinja variable 'dict object' has no attribute 'macaddress'; line 9
---
[...]
ethernets:
{{ grains['interface_context'] }}:
dhcp4: {{ grains['dhcp4'] }}
dhcp6: {{ grains['dhcp6'] }}
addresses: [{{ grains['ipv4'] }}, "{{ grains['ipv6'] }}"]
{% if grains['macaddress'] %} <======================
match:
macaddress: {{ grains['macaddress'] }}
{% endif %}
routes:
- to: default
[...]
---
As the message indicates, the grain "macaddress" is missing, which I can confirm, it's not set for this minion. But What I do not understand is how I can simply check if this variable/grain exists at all within a jinja template?
I wouldn't expect this error to come up, as I actually wanted to catch it with the if statement.
Can somebody help?
Use get to return None instead of raising:
{% if grains.get('macaddress') is not none %}
Or if you want to treat "empty" values the same:
{% if not grains.get('macaddress') %}

Configuring a Jinja template in Cisco DNAC Template editor

I am configuring a Jinja template in Cisco DNAC Template editor to provision new switches. The problem I am having is getting Jinja to split the string variable within the Cisco Template tool. I tried using this split command.
{% set list1 = variable1.split(';') %}
This works fine if I hard code in the {{ HOSTNAME }} variable, for example using ABC-DEF-111-AS-10-11-12-13
{# {% set hname = '{{ HOSTNAME }}' %} #}
{% set hname = 'ABC-DEF-111-AS-10-11-12-13' %}
Device name is {{ hname }}
{% set list1 = hname.split('-') %}
{% for list in list1 %}
<p>{{ list }}</p>
{% endfor %}
The campus is {{ list1[0] }}
The building is {{ list1[1] }}
Room number is {{ list1[2] }}
Switch type is {{ list1[3] }}
IP address is {{ list1[4] }}.{{ list1[5] }}.{{ list1[6] }}.{{ list1[7] }}
However, when I use the {{ HOSTNAME }} variable to input the hostname, the split command will not split the hostname, instead it passes the hostname through in its entirity within {{ list1[0] }}. This happens even if I use the variable directly within the split command, for example.
{% set list1 = '{{ HOSTNAME }}'.split('-') %}
I have also tried splitting the resultant {{ list1[0] }} but it too just passes the hostname on through in its entirety.
I am not sure if this is a problem with the version of Jinja used in the Template Editor because I get a systax error when ever I tried using this split command:
{% set item1, item2 = variable1.split(';') %}
Is there away to get {{ HOSTNAME }} split? What am I missing?
The solution is that I should change the variable into a statement like this:
{% set list1 = **HOSTNAME**.split('-') %}

Looping through a single _data file in Jekyll and grouping the results

Setup: Currently I have a script that reads a google spreadsheet and writes the results to a *.yaml file in my _data folder on my GitHub pages site.
A typical results looks like this:
- ip: 192.168.0.1
mac: 'a4:13:4e:5e:2c:c0'
hostname: XWR-3100V1 Wireless Router
vendor: Luxul
dns_name: XWR-3100.lan
mdns_name: test
smb_name: test
smb_domain: test
comments: test
type: Router
- ip: 192.168.0.4
mac: 'b0:6e:bf:9c:11:00'
hostname: Toshiba eStudio 4505ac
vendor: ''
dns_name: ''
mdns_name: ''
smb_name: ''
smb_domain: ''
comments: ''
type: Printer
From there, I've created a page that displays a table that loops through the information and displays all of the devices.
I can't figure out the syntax for the following:
Look through the data and identify all of the different Types (Router, Media Player, Printer) etc.
Create the table with Type as the group header, and then list the records that match that type below the header, then move on to the next type, and so on.
Is this possible with a single yaml file, or do I need to some-how generate another file that contains ONLY the list of types found in the first file?
Thanks!
{% assign devices = site.data.network %}
{% for ip in devices %} //not sure what this really means
{{ip.ip}} | {{ip.mac}} ...etc.
{% endfor %}
I excluded the table tags for brevity.
Use group_by filter :
{% assign net = site.data.network | group_by:"type" %}
{% for type in net %}
<h1>{{ type.name }}</h1>
<ul>
{% for item in type.items %}
<li>{{ item.ip }} ...</li>
{% endfor %}
</ul>
{% endfor %}

how to access pillar data with variables?

I have a pillar data set like this;
vlan_tag_id:
nginx: 1
apache: 2
mp: 3
redis: 4
in the formula sls file I do this;
{% set tag = pillar.get('vlan_tag_id', 'u') %}
so now I have a variable tag which is a dictionary {'apache': 2, 'nginx': 1, 'redis': 4, 'mp': 3}
At run time I pass a pillar data app whose value will be either
1. apache
2. nginx
3. redis
4. mp
so if at run time I pass apache I want to something which will get me the value 2
I cant do {{ salt['pillar.get']('vlan_tag_id:app', '')}} because app itself is a variable.
I tried doing {{ salt'pillar.get'}}, but it throws error.
how can I do this ?
Since tag is just another dictionary, you can do a get on that as well:
{%- set tag = pillar.get('vlan_tag_id', 'u') %}
{%- set app = pillar.get('app') %}
{{ tag.get(app) }} # Note lack of quotes
If you want to use the colon syntax, you can append the contents of app to the key string:
{%- set app = pillar.get('app') %}
{{ salt['pillar.get']('vlan_tab_id:' + app) }}
I find it simpler to follow if I alias pillar.get and break it up a bit:
{%- set pget = salt['pillar.get'] %}
{%- set app = pget('app') %}
{%- set tag = pget('vlan_tag_id') %}
{{ tag.get(app) }}

including grain data when querying pillar in saltstack managed file

I have a state using file.managed, which generates a config file via a jinja for loop from a key in pillar.
My pillar looks like this:
configuration:
server01:
key1: value1
key2: value2
server02:
key03: value03
key04: value04
and the managed file:
{% set kv = pillar['configuration']['server01'] %}
{% for key, value in kv.iteritems() %}
{{ key }}:{ value }};
{% endfor %}
The way I differentiate between different servers right now in my state file is
config:
file.managed:
- name: /etc/config.conf
- source: salt://files/{{ grains['id'] }}.conf.jinja
- template: jinja
but this is less than ideal, since I have to create an almost identical file for every server.
Is there a way to dynamically replace server01 with the ID of the actual server, something like
{% set kv = pillar['configuration']['{{ grains[id''] }}'] %}
The goal is to generally limit the necessary changes only to the corresponding pillar file, when adding a new server, so other suggestions are also welcome too.
i think you should use pillar info in your state file.
your state file like bellow :
{% if grains['id'] in pillar['configuration'] %}
{% set nodeinfo = pillar['configuration'][grains['id']] %}
config:
file.managed:
- name: /etc/config.conf
- source: salt://conf.jinja
- template: jinja
- defaults :
nodeinfo: {{nodeinfo}}
{% endif %}
then, conf.jinja:
{% for key, value in nodeinfo.iteritems() -%}
{{ key }}:{{ value }};
{% endfor -%}
i hope that will solve your problem, thanks.