Keep YAML list structure in Ansible templates - templates

Say I have a variable file
---
app1:
environments:
- test
- demo
paths:
- /home/someuser/_env_/*.log
- /var/log/something/*.log
id: _env_
that's included in my playbook like this
---
- hosts: all
become: yes
vars:
apps:
- app1
vars_files:
- ~/vars/app1_vars.yml
tasks:
- name: Update config
template:
src: foo.j2
dest: /home/configurer/test.conf
owner: configurer
group: configurer
mode: '0644'
lstrip_blocks: yes
and the template itself is
{% for app in apps %}
{% for env in vars[app]['environments'] %}
- type: log
enabled: true
paths:
{% for path in vars[app]['paths'] %}
- {{ path | replace("_env_", env) }}
{% endfor %}
fields:
app_id: {{ vars[app]['id'] | replace("_env_", env) }}
{% endfor %}
{% endfor %}
That gives me as output
- type: log
enabled: true
paths:
- /home/someuser/test/*.log
- /var/log/something/*.log
fields:
app_id: test
- type: log
enabled: true
paths:
- /home/someuser/demo/*.log
- /var/log/something/*.log
fields:
app_id: demo
Is there a more compact and nicer way to iterate over the paths variable list and keep the YAML list structure with the -s?

After some experimenting I did the following as an alternative:
Changed the variable file:
---
app1:
environments:
- test
- demo
filebeat_properties:
paths:
- /home/someuser/_env_/*.log
- /var/log/something/*.log
fields:
app_id: _env_
Used to_nice_yaml to pretty-print the config:
{% for app in apps %}
{% for env in vars[app]['environments'] %}
- type: log
enabled: true
{{ vars[app]["filebeat_properties"] | to_nice_yaml(indent=2, width=9999) | indent(2) | replace("_env_", env) }}
{% endfor %}
{% endfor %}

Related

I have a Jinja template that only prints the first match of a regex match but I would like it to print all matches

I run a playbook that executes a Cisco command show interfaces and I store that into a variable.
I then jump into a template for further processing to only find Ethernet interfaces and errors.
I find the first match of both interface and error which is fine but I do not find the second match of interface and error in the control environment. The lab device has two interfaces Ethernet0/0 and Ethernet0/1.
Can someone make a suggestion so that I can match on both interfaces and respective errors?
Here is the task:
Blockquote
tasks:
- name: gather interface stats and store in register
ios_command:
commands:
- show interface | section include is up
register: interface_errors
- name: store interface_errors_file
copy:
content: {{interface_errors}}
dest: ./{{inventory_hostname}}_interface_full.txt
- name: deploy template
copy:
content: "{{lookup('template', 'errors.j2') }}"
dest: ./{{inventory_hostname}}_errors.txt
Template:
Blockquote
{% for int in interface_errors.stdout %}
{% set int_name = int | regex_search('Ethernet\d{1,3}\/\d{1,3} is up') %}
{% if 'Ethernet' in int_name | string %}
- Interface: {{int_name}}
{% endif %}
{% endfor %}
{% for int_err in interface_errors.stdout %}
{% set int_errors = int_err | regex_search('\d{1,10000}.input.errors|output.errors') %}
{% if 'errors' in int_errors %}
- Interface Errors: {{int_errors}}
{% endif %}
{% endfor %}

Jinja2 For Loop over a YAML List of Dictionaries

I am fairly new to Jinja2 and I have an issue I can't seem to resolve no matter what I try. I am trying to create a config file for a device using a Jinja2 template and some variable files I have created. I cannot seem to get it working at all when I am specifying a list of dictionaries for my YAML variable file.
template:
{% for id in VLANS %}
vlan {{ id.id }}
name {{ id.name }}
vn-segment {{ id.vni }}
{% endfor %}
variable file:
VLANS:
- id: 9
name: "VLAN9"
vni: 109
- id: 10
name: "VLAN10"
vni: 110
- id: 11
name: "VLAN11"
vni: 111
- id: 12
name: "VLAN12"
vni: 112
Looks fine. The play below
- name: Template
template:
src: template.j2
dest: test.txt
gives
shell> cat test.txt
vlan 9
name VLAN9
vn-segment 109
vlan 10
name VLAN10
vn-segment 110
vlan 11
name VLAN11
vn-segment 111
vlan 12
name VLAN12
vn-segment 112
with template
shell> cat template.j2
{% for item in VLANS %}
vlan {{ item.id }}
name {{ item.name }}
vn-segment {{ item.vni }}
{% endfor %}

ansible set_fact if-else failure

I am trying to set_fact value based on condition with if-else ,Below is syntax which I use for the if-else condition .
- name: set the server details
set_fact:
ad_server: "{{ '{{ server.westeurope }}' if ('{{ ansible_local.vdc.location }}' == 'westeurope') else '{{ server.eastus2 }}' }}"
ad_server_ip: "{% if'{{ ansible_local.vdc.location }}'=='westeurope' %}{{ server_ip.westeurope }}{% else %}{{ server_ip.eastus2 }}{% endif %}"
- debug:
msg: "{{ ansible_local.vdc.location }},{{ ansible_local.vdc.binaryrepo_url }},{{ ad_server }},{{ ad_server_ip }}"
I have already declared my variable section with the details as mentioned below :
server:
westeurope: WIN030123
eastus2: WIN100270
server_ip:
westeurope: 10.x.x.x
eastus2: 10.x.x.x
my if-else condition fails to get the value of if statement and always gets the value of else condition ..Can someone provide me the correct syntax for this situation.
Using Jinja2 there are different types of markup to define logic and variable expansion and : more information on the Jinja2 Ansible page.
So if you want to do logic (branching) and variable expansion on the same line, like on you first fact setting that will be :
ad_server: "{% if ansible_local.vdc.location == 'westeurope' %}{{ server.westeurope }}{% else %}{{ server.eastus2 }}{% endif %}"
Please try it.
Use Jinja2 if expression:
- name: set the server details
set_fact:
ad_server: "{{ server.westeurope if ansible_local.vdc.location == 'westeurope' else server.eastus2 }}"
ad_server_ip: "{{ server_ip.westeurope if ansible_local.vdc.location == 'westeurope' else server_ip.eastus2 }}"

Create directories with Ansible using two separate lists

I want to create a series of directory trees dependent on two seperate lists.
Example:
---
# variable file ...
datacenters:
- london
- paris
types:
- databases
- baremetal
- vms
So I want my trees to be like so ...
dest: "/{{ datacenter.0 }}/{{ types.0 }}"
dest: "/{{ datacenter.0 }}/{{ types.1 }}"
dest: "/{{ datacenter.0 }}/{{ types.2 }}"
dest: "/{{ datacenter.1 }}/{{ types.0 }}"
dest: "/{{ datacenter.1 }}/{{ types.1 }}"
dest: "/{{ datacenter.1 }}/{{ types.2 }}"
dest: "/{{ datacenter.N }}/{{ types.N }} .... etc
I'm not exactly sure how to do this without using an include file ....
You can use Nested Loops
- name: Test with_nested
hosts: localhost
vars:
datacenters:
- london
- paris
types:
- databases
- baremetal
- vms
tasks:
- name: Do it
debug: msg="{{item[0]}}/{{item[1]}}"
with_nested:
- datacenters
- types

Golang template (helm) iterating over a list of maps

I'm using helm to generate kubernetes yamls.
My values.yaml looks like this:
...
jobs:
- nme: job1
command: [sh, -c, "/app/deployment/start.sh job1"]
activeDeadlineSeconds: 600
- name: job2
command: [sh, -c, "/app/deployment/start.sh job2"]
activeDeadlineSeconds: 600
...
templates/jobs.yaml
{{ range $i, $job := .Values.jobs -}}
apiVersion: batch/v1
kind: Job
metadata:
name: {{ template "name" . }}-{{ $job.name }}
labels:
chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}"
spec:
activeDeadlineSeconds: {{ $job.activeDeadlineSeconds }}
template:
metadata:
labels:
app: {{ template "name" . }}-{{ $job.name }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
command: {{ $job.command }}
env:
{{ toYaml .Values.service.env | indent 10 }}
ports:
- containerPort: {{ .Values.service.internalPort }}
{{- end }}
Helm is failing with this error:
Error: UPGRADE FAILED: render error in "app1/templates/jobs.yaml": template: app1/templates/_helpers.tpl:6:18: executing "name" at <.Chart.Name>: can't evaluate field Name in type interface {}
When I look at _helpers.tpl:
{{- define "name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
If I remove the range loop and references to $job in my jobs.yaml, the _helpers.tpl name template works fine. When I add in the loop, it fails.
It seems like within the loop, all dot . pipeline, which contains the scope for .Chart and .Values, is reassigned to something else.
What am I doing wrong?
Inside the loop the value of the . is set to the current element and you have to use $.Chart.Name to access your data.
I asked a similar question and I think the answer https://stackoverflow.com/a/44734585/8131948 will answer your question too.
I ended up saving the global context and then updating all of my references like this:
{{ $global := . }}
{{ range $i, $job := .Values.jobs -}}
apiVersion: batch/v1
kind: Job
metadata:
name: {{ template "name" $global }}-{{ $job.name }}
labels:
chart: "{{ $global.Chart.Name }}-{{ $global.Chart.Version | replace "+" "_" }}"
spec:
activeDeadlineSeconds: {{ $job.activeDeadlineSeconds }}
template:
metadata:
labels:
app: {{ template "name" $global }}-{{ $job.name }}
spec:
containers:
- name: {{ $global.Chart.Name }}
image: "{{ $global.Values.image.repository }}:{{ $global.Values.image.tag }}"
imagePullPolicy: {{ $global.Values.image.pullPolicy }}
command: {{ $job.command }}
env:
{{ toYaml $global.Values.service.env | indent 10 }}
ports:
- containerPort: {{ $global.Values.service.internalPort }}
{{- end }}