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
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 %}
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
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
...
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
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.