concatenate variables in Jinja2 - ansible

I'm struggling with the following example data from Satellite server when templating with Jinja2 in Ansible:
"results": {
"test.example.com": {
"interfaces": "eth0,lo",
"ipaddress_eth0": "10.251.0.45",
"ipaddress_lo": "127.0.0.1",
"netmask_eth0": "255.255.255.0",
"netmask_lo": "255.0.0.0",
"network_eth0": "10.251.0.0",
"network_lo": "127.0.0.0",
This piece of code will return: 127.0.0.1, but I want to replace 'ipaddress_lo' with the variable interface.
I've tried a million combinations... without any luck... please advice.
{% for items in my_host_facts.json.results %}
{% for interface in my_host_facts.json.results[items].interfaces.split(",") %}
{{ interface }}:
address: "{{ my_host_facts.json.results[items].ipaddress_lo }}"
netmask: "{{ my_subnet.json.mask }}"
network: "{{ my_subnet.json.network }}"
gateway: "{{ my_subnet.json.gateway }}"
{% endfor %}
dns: [ "{{ my_subnet.json.dns_primary }}", "{{ my_subnet.json.dns_secondary }}" ]
{% endfor %}
Kind regards,

Try {{ my_host_facts.json.results[items]['ipaddress_'+interface] }}.

Related

Ansible Nested variables and Jinja2 templates

I'm trying to figure out why my jinja2 template (and ansible for that matter) cannot find my variables in my inventory file.
Here is my inventory file:
all:
hosts:
test05:
ansible_host: 192.168.x.x
filebeat:
version: 7.15.2
applog:
- title: Separate Application Log Path with Tags
type: log
paths:
- /var/log/something/moresomething/current
tags: '["something", "application"]'
- title: Separate Application Log Path, with Tags, and "decode_json_fields" processor.
type: log
paths:
- /var/log/something/moresomething/blah-shell.log
tags: ["application", "something"]
fields: ["message"]
depth: 2
- title: Separate Application Log Path, with Tags, and Multiline fields
type: log
paths:
- /var/log/something/moresomething/production.log
tags: ["application", "something"]
multiline_type: pattern
multiline_patern: 'Started'
multiline_negate: true
multiline_match: after
Then attempting to get the first title. I'm doing the following:
- name: debugging
debug:
var: filebeat.applog.title
when I run this I end up getting filebeat.applog.title: VARIABLE IS NOT DEFINED! which I think is good since it doesn't know what title I want. So changing this to
- name: debugging
debug:
var: filebeat.applog.0.title
I end up getting what I want filebeat.applog.0.title: Separate Application Log Path with Tags. However, how do I use this in a jinja2 template?
I have the following for a template, I know I need to update this to loop through the different items in my inventory. That's a different problem on how to loop through this.
title: {{ filebeat.applog.title }}
- type: {{ filebeat.applog.type }}
enabled: true
paths:
- {{ filebeat.applog.path }}
tags: {{ filebeat.applog.tags }}
{% if filebeat.applog.fields is defined %}
processors:
- decode_json_fields:
fields: {{ filebeat.applog.fields }}
max_depth: {{ filebeat.applog.depth }}
target: {{ filebeat.applog.target | default "" }}
{% endif %}
{% if filebeat.applog.multiline_pattern is defined %}
multiline.type: {{ filebeat.applog.multiline_type }}
multiline.pattern: {{ filebeat.applog.multiline_pattern }}
multiline.negate: {{ filebeat.applog.multiline_negate }}
multiline.match: {{ filebeat.applog.multiline_match }}
{% endif %}
each time I get the following, even when I do use {{ filebeat.applog.0.logtitle }} in the template:
fatal: [test05]: FAILED! => changed=false
msg: |-
AnsibleError: template error while templating string: expected token 'end of print statement', got 'string'. String: title: {{ filebeat.applog.title }}
- type: {{ filebeat.applog.type }}
enabled: true
paths:
- {{ filebeat.applog.path }}
tags: {{ filebeat.applog.tags }}
{% if filebeat.applog.fields is defined %}
processors:
- decode_json_fields:
fields: {{ filebeat.applog.fields }}
max_depth: {{ filebeat.applog.depth }}
target: {{ filebeat.applog.target | default "" }}
{% endif %}
{% if filebeat.applog.multiline_pattern is defined %}
multiline.type: {{ filebeat.applog.multiline_type }}
multiline.pattern: {{ filebeat.applog.multiline_pattern }}
multiline.negate: {{ filebeat.applog.multiline_negate }}
multiline.match: {{ filebeat.applog.multiline_match }}
{% endif %}
I'm not sure what I'm missing or doing wrong. I'm thinking I'm doing something wrong since this the first time doing something like this.
So the template you have should either:
have a for loop to iterate over filebeat.applog
OR
reference n'th element of filebeat.applog
Aside from that, there are some errors like below:
1.
target: {{ filebeat.applog.target | default "" }}
This is the main one, and this is what the error message is complaining about, i.e. got 'string'. The correct usage for default filter is {{ some_variable | default("") }}.
2.
{% if filebeat.applog.multiline_pattern is defined %}
In the inventory this variable is mis-spelled, i.e. multiline_patern (missing one "t"). Fix this in your inventory.
3.
when I do use {{ filebeat.applog.0.logtitle }} in the template
This should be {{ filebeat.applog.0.title }} to work.
Considering the above fixes, a template that loops over filebeat.applog such as below should work:
{% for applog in filebeat.applog %}
title: {{ applog.title }}
- type: {{ applog.type }}
enabled: true
paths: {{ applog.paths }}
tags: {{ applog.tags }}
{% if applog.fields is defined %}
processors:
- decode_json_fields:
fields: {{ applog.fields }}
max_depth: {{ applog.depth }}
target: {{ applog.target | default("") }}
{% endif %}
{% if applog.multiline_pattern is defined %}
multiline.type: {{ applog.multiline_type }}
multiline.pattern: {{ applog.multiline_pattern }}
multiline.negate: {{ applog.multiline_negate }}
multiline.match: {{ applog.multiline_match }}
{% endif %}
{% endfor %}

Ansible conditional betwen variables

I have to check whether source and target are not the same directories and if they are not to change the name:
#Renaming bamboo-home folder
- name: Get {{ bamboo_home_symlink }} symlink target
stat:
path: "{{ bamboo_home_symlink }}"
register: symlinkhome_target
tags:
- upgrade
- name: change name of folder to be {{ bamboo_home }}
shell: mv '{{ symlinkhome_target.stat.lnk_source }}' '{{ bamboo_home }}'
when: symlinkhome_target.stat.lnk_source is not {{ bamboo_home }}
tags:
- upgrade
Unfortunately I have got following error:
19:02:31 TASK [change name of folder to be /usr/data/atlassian/bamboo-8.1.2-home] *******
19:02:31 task path: /opt/data/jenkins/workspace/Team Infrastructure Services and Tools/Bamboo/Bamboo Dev/Bamboo-dev install/roles/bamboo/tasks/main.yml:146
19:02:31 [WARNING]: conditional statements should not include jinja2 templating
19:02:31 delimiters such as {{ }} or {% %}. Found: symlinkhome_target.stat.lnk_source is
19:02:31 not {{ bamboo_home }}
19:02:31 Read vars_file 'vars/secure-user.yml'
19:02:31 Read vars_file 'vars/dev.yml'
19:02:31 [WARNING]: conditional statements should not include jinja2 templating
19:02:31 delimiters such as {{ }} or {% %}. Found: symlinkhome_target.stat.lnk_source is
19:02:31 not {{ bamboo_home }}
19:02:31 fatal: [server.domain.company.net]: FAILED! => {
19:02:31 "msg": "The conditional check 'symlinkhome_target.stat.lnk_source is not {{ bamboo_home }}' failed. The error was: template error while templating string: expected token 'name', got '/'. String: {% if symlinkhome_target.stat.lnk_source is not /usr/data/atlassian/bamboo-8.1.2-home %} True {% else %} False {% endif %}\n\nThe error appears to be in '/opt/data/jenkins/workspace/Team Infrastructure Services and Tools/Bamboo/Bamboo Dev/Bamboo-dev install/roles/bamboo/tasks/main.yml': line 146, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- name: change name of folder to be {{ bamboo_home }}\n ^ here\nWe could be wrong, but this one looks like it might be an issue with\nmissing quotes. Always quote template expression brackets when they\nstart a value. For instance:\n\n with_items:\n - {{ foo }}\n\nShould be written as:\n\n with_items:\n - \"{{ foo }}\"\n"
19:02:31 }
symlinkhome_target is defined. See result of stat:
19:02:31 "stat": {
19:02:31 "exists": true,
19:02:31 "gid": 0,
19:02:31 "gr_name": "root",
19:02:31 "inode": 142127337,
19:02:31 "lnk_source": "/opt/data/atlassian/bamboo-8.1.2-home",
19:02:31 "lnk_target": "/usr/data/atlassian/bamboo-8.1.2-home",
19:02:31 "mimetype": "inode/symlink",
19:02:31 }
The warning tells you what you are doing wrong that causes the error message.
19:02:31 [WARNING]: conditional statements should not include jinja2 templating
19:02:31 delimiters such as {{ }} or {% %}.
Additionally, you should be testing for equality (!=) not identity (is not).
- name: change name of folder to be {{ bamboo_home }}
shell: mv '{{ symlinkhome_target.stat.lnk_source }}' '{{ bamboo_home }}'
when: symlinkhome_target.stat.lnk_source != bamboo_home
tags:
- upgrade

Ansible - problem with ansible_facts for packages in jinja template

I try to get all installed packages with Ansible and write them in a "pretty" way to a file.
Calling the module works:
- name: Gather the rpm package facts
package_facts:
manager: auto
In a Jinja template I am using a loop, what works too:
{% for item in ansible_facts.packages %}
{{ item }}
{% endfor %}
Unfortunately the simple output creates this "mess":
"yum": [
{
"arch": "noarch",
"epoch": null,
"name": "yum",
"release": "4.el8",
"source": "rpm",
"version": "4.2.23"
}
],
"zlib": [
{
"arch": "x86_64",
"epoch": null,
"name": "zlib",
"release": "16.el8_2",
"source": "rpm",
"version": "1.2.11"
}
]
Some of these elements are unnecassry for the current job, so the first call coming in mind was this:
{% for item in ansible_facts.packages %}
{{ item.name }} {{ item.version }}
{% endfor %}
But this ended just in an error:
fatal: [somehost.example.org]: FAILED! => {"changed": false, "msg": "AnsibleUndefinedVariable: 'unicode object' has no attribute 'name'"}
Searched through the internet, looked into the documentation of Ansible, tried various notations and nothing worked:
vars[item].name
item[0].name
item["name"]
As last option I tried it with iteritems:
{% for (key,value) in ansible_facts.packages.iteritems() %}
{{ value }}
{% endfor %}
But this ended I an error to:
fatal: [somehost.example.org]: FAILED! => {"changed": false, "msg": "AnsibleUndefinedVariable: 'list object' has no attribute 'name'"}
It seems I am not smart enough to figure out the solution, can someone lend me a hand?
Sincerely,
a frustrated Ansible user
In the data sample you posted, package name is the key and value is a list of 1 element comprising of a dictionary.
To get the version you have to access the first element. Like so:
{% for key, value in ansible_facts.packages.iteritems() %}
{{ key }} {{ value[0].version }}
{% endfor %}
Should render the file with package list like:
...
zlib 1.2.11
...

Ansible, pg_hba.conf configuration with IPs from hosts file

I have problem and i don't did i even start correctly.
i have hosts file that looks like
all:
children:
application1:
children:
application1-webserver:
hosts:
host1.domain.net:
host2.domain.net:
application1-database:
hosts:
dbhost1.domain.net:
application2:
children:
application2-webserver:
hosts:
host3.domain.net:
host4.domain.net:
application1-database:
hosts:
dbhost2.domain.net:
app-servers:
hosts:
host1.domain.net:
host2.domain.net:
host3.domain.net:
host4.domain.net:
I have created template file. I know it is not pg_hba.conf, but it is no mater now, if i get IPs out it will be easy
{% for i in groups['app-servers'] %}
{{ hostvars[i]['ansible_default_ipv4_address'] }}
{% endfor %}
So maybe for time to time i have to run this script to create "new" enviroment, and i don't want to change IPs from app-servers manually. What i want is to get IP from FQDN.
Need this so i can limit access to db from network, just to those servers.
Thanks for help.
The variable holding the global info is ansible_default_ipv4. It is only available if you have gathered facts on your host (make sure you did not use gather_facts: false on your play).
It's a hash containing several keys among which address. As an example (obfuscated), this is what I get on my localhost:
$ ansible localhost -m setup -a gather_subset=network -a filter=*default_ipv4*
localhost | SUCCESS => {
"ansible_facts": {
"ansible_default_ipv4": {
"address": "x.y.z.a",
"alias": "interface",
"broadcast": "x.y.z.255",
"gateway": "x.y.z..1",
"interface": "interface",
"macaddress": "xx:xx:xx:xx:xx:xx",
"mtu": 1500,
"netmask": "255.255.255.0",
"network": "x.y.z..0",
"type": "ether"
}
},
"changed": false
}
So the name of the var you are looking for in your template is ansible_default_ip4.address
Your full fixed template:
{% for i in groups['app-servers'] %}
{{ hostvars[i].ansible_default_ipv4.address }}
{% endfor %}
Possible alternative notations (mixing dot and array notation):
{{ hostvars[i]['ansible_default_ipv4']['address'] }}
{{ hostvars[i].ansible_default_ipv4['address'] }}
etc.
So i got it. It is little complicated.
Playbook looks like this:
---
- hosts: app-servers
tasks:
- ping:
- hosts: dbhost1.domain.net
tasks:
- include_role:
name: test-for
...
Hosts file like above:
all:
children:
application1:
children:
application1-webserver:
hosts:
host1.domain.net:
host2.domain.net:
application1-database:
hosts:
dbhost1.domain.net:
application2:
children:
application2-webserver:
hosts:
host3.domain.net:
host4.domain.net:
application1-database:
hosts:
dbhost2.domain.net:
app-servers:
hosts:
host1.domain.net:
host2.domain.net:
host3.domain.net:
host4.domain.net:
Only after i ping hosts i get IPs i need
Then in template:
{% for i in groups['app-servers'] %}
{{ hostvars[i].ansible_default_ipv4.address }}
{% endfor %}
And it works.
Thanks

Ansible: multi line over playbook

I have a question about multi line and Ansible playbook:
I have created a playbook with a line very large, I need to cut this line for better reading. How I can doing it?
- name: 'Create VM Azure :-P '
shell: if ! grep {{ item }} /tmp/vm_{{ rgName }}; then azure vm create --vm-size {{ groups['item'][vmsize] }} --resource-group {{ rgName }} --name {{ item }} --location {{ location }} --admin-username {{ username }} --ssh-publickey-file {{ sshfile }} --storage-account-name {{ rgName | lower }} --os-type {{ groups['item'][type_os] }} --image-urn {{ image }} --data-disk-size {{ disksize }} --subnet-id {{ subnetid_key }} --nic-names {{ item }}; fi
with_items: groups['test']
I would like to make it as follows way, but I have some errors when execute the playbook
- name: 'Create VM Azure :-P '
shell: if ! grep {{ item }} /tmp/vm_{{ rgName }}; then
azure vm create --vm-size {{ groups['item'][vmsize] }}
--resource-group {{ rgName }} --name {{ item }}
--location {{ location }} --admin-username {{ username }}
--ssh-publickey-file {{ sshfile }} --storage-account-name {{ rgName | lower }}
--os-type {{ groups['item'][type_os] }} --image-urn {{ image }}
--data-disk-size {{ disksize }} --subnet-id {{ subnetid_key }}
--nic-names {{ item }}; fi
with_items: groups['test']
The error:
ERROR! Syntax Error while loading YAML.
The error appears to have been in '/home/pvillarruel/docker/azure-ansible/data/playbook.yml': line 79, column 1, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
shell: if ! grep {{ item }} /tmp/vm_{{ rgName }}; then azure vm create --vm-size
{{ groups['item'][vmsize] }} --resource-group {{ rgName }} --name {{ item }}
^ here
We could be wrong, but this one looks like it might be an issue with
missing quotes. Always quote template expression brackets when they
start a value. For instance:
with_items:
- {{ foo }}
Should be written as:
with_items:
- "{{ foo }}"
Thanks
Use a YAML block scalar, they were designed exactly for this kind of thing:
shell: >
if ! grep {{ item }} /tmp/vm_{{ rgName }}; then
azure vm create --vm-size {{ groups['item'][vmsize] }}
--resource-group {{ rgName }} --name {{ item }}
--location {{ location }} --admin-username {{ username }}
--ssh-publickey-file {{ sshfile }}
--storage-account-name {{ rgName | lower }}
--os-type {{ groups['item'][type_os] }} --image-urn {{ image }}
--data-disk-size {{ disksize }} --subnet-id {{ subnetid_key }}
--nic-names {{ item }}; fi
The > indicates that the following block is a scalar and lines should be folded, i.e. a newline character will be converted into a single space. You also do not need to worry about special characters, everything will be taken literally in a folded block scalar.

Resources