how to access pillar data with variables? - jinja2

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) }}

Related

DBT - only insert\merge columns that exist in the source object

I'm new to dbt and jinja but trying my best.
We have a scenario where when using incremental merge our destination table schema is different from our Source schema so we'd like to only update/insert the common columns.
I'm loading my source columns into a variable then sending it as a configuration value like so:
{%- set src_cols = adapter.get_columns_in_relation(ref('pre_Dim_Entities_Client')) -%}
{{
config(
materialized='incremental',
unique_key='Entity_ID',
source_columns = src_cols
)
}}
SELECT *
FROM {{ ref ('pre_Dim_Entities_Client')}}
Then I've overridden merge.sql macro:
{% macro default__get_merge_sql(target, source, unique_key, dest_columns, predicates) -%}
{%- set predicates = [] if predicates is none else [] + predicates -%}
{%- set temp = config.get('source_columns') -%}
{{- print(temp) -}}
{%- set dest_cols_csv = get_quoted_csv(config.get('source_columns', default = dest_columns) | map(attribute="name"))%}
{%- set update_columns = config.get('merge_update_columns', default = dest_columns | map(attribute="quoted") | list) -%}
{%- set sql_header = config.get('sql_header', none) -%}
I've assigned the config value to a temp var and printed it to confirm what I suspected was the issue - the columns are not passed in source_columns configuration as I would have expected.
What am I doing wrong, and alternatively- Is there a better way to go about this issue?
Self-answer here -
Turns out passing variables in config block is problematic.
Instead I sent the source object name as a config value and in the merge macro retrieved the columns and updated dest_columns to the retrieved object.
{{
config(
materialized='incremental',
unique_key='Entity_ID',
source_scehma = 'pre_Dim_Entities_Client'
)
}}
SELECT *
FROM {{ ref ('pre_Dim_Entities_Client')}}
And in the merge macro:
{% macro default__get_merge_sql(target, source, unique_key, dest_columns, predicates) -%}
{%- set predicates = [] if predicates is none else [] + predicates -%}
{%- set src_name = config.get('source_scehma', none) -%}
{% if src_name %}
{%- set dest_columns = adapter.get_columns_in_relation(ref(src_name)) -%}
{% endif %}
{%- set dest_cols_csv = get_quoted_csv( dest_columns | map(attribute="name")) -%}
{%- set update_columns = config.get('merge_update_columns', default = dest_columns | map(attribute="quoted") | list) -%}
{%- set sql_header = config.get('sql_header', none) -%}
##Note:
In my solution I assume all source columns exist in the destination table.
If your need is to only take the common columns between source and destination the code should be modified accordingly.

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('-') %}

Why does this where filter not work in Jekyll?

I have a github page, whose _include directory has a file courses.html:
{% assign id = include.lessonID | split: '.' %}
{% assign courseID = id | first %}
{% assign node = site.data.courses | where: "id","1" %}
{% assign node = node[1] %}
{%- if node.id == empty -%}
<h1> EMPTY NODE Warning</h1>
{%- else -%}
<h2> DATA Found! </h2>
ID: {{ node.id }}
{%- endif -%}
<p>CourseID: {{node.id}}</p>
<p>Name: {{ node.name }}</p>
<p>Link: {{ node.permalink }}</p>
{%- for node in site.data.courses -%}
{%- if node.id == 1 -%}
<p>{{ node.name }}</p>
<p>{{ node.permalink }}</p>
{%- endif -%}
{%- endfor -%}
It is being used by a file in _layout called courses.html:
{% include courses.html post=page.lessonInfo.lessonID post=page %}
Finally, there's file lister.md that has the following contents:
---
layout: courses
title: 'Test'
lessonInfo:
lessonID : 1.1
modName: 'Installing RHEL Server'
chapterName: 'Using Essential Tools'
---
# There should be some course list around here!
The output is as follows:
DATA Found!
ID:
CourseID:
Name:
Link:
RHCSA
/rhcsa
So, apparently the node variable isn't empty, but I can't access any of the properties when I'm selecting the right element of the array using where clause.
However, this works when using the second part using if statement in for loop. How do I fix the where clause?!
Edit
The suggestions by #JJJ did solve my problem, but I have a related problem now. I can't replace the constant 1 in the expression where: "id","1" with a variable! I tried the normal where clause (both with and without quotes) which didn't work. So, I tried a where expression, which also doesn't work:
{% assign node = site.data.courses | where: "id",courseID %}
Doesn't work!
{% assign node = site.data.courses | where_exp: "selNode","selNode.id == courseID" %}
Neither does this.
What am I doing wrong and how do I fix it?
Firstly, like in most programming languages, arrays are zero-indexed. node[1] contains the second node, not the first one. You probably meant {% assign node = node[0] %} instead.
Secondly, if node.id == empty isn't how you check if a value exists. Just do unless node or node.size == 0.

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.

Remove the empty lines left by Jinja2 variable definitions

When writing template files using Jinja2 for Saltstack, I often define some variables at the beginning of the file. For example:
{% set ip = grains['ip4_interfaces']['eth1'][0] %}
{% set domain = pillar['company_domain'] %}
{% set version = pillar['site_version'] %}
{% set site_url = 'www.' + domain %}
[...]
Everything works fine but when opening the generated file, I get a block of empty lines where the jinja code was.
Am I doing something wrong ?
If not, is there any way to get rid of those empty lines when using templates ?
There is whitespace control in Jinja2. You might want:
{%- set ip = grains['ip4_interfaces']['eth1'][0] -%}
{%- set domain = pillar['company_domain'] -%}
{%- set version = pillar['site_version'] -%}
{%- set site_url = 'www.' + domain -%}
[...]
As well, the salt configuration file supports jinja_trim_blocks and jinja_lstrip_blocks (jinja_env:trim_blocks, jinja_env:lstrip_blocks, jinja_sls_env:trim_blocks, and jinja_sls_env:lstrip_blocks as of 2018.3).