I have an Ansible template file which I'm applying correctly with the 'template' directive, but it's showing up on the remote machine with no substitutions:
- name: "buildAgent.properties for {{ agent_name }}"
template:
src: buildAgent.properties.j2
dest: "{{ config_path }}/buildAgent.properties"
The template file looks something like this:
serverUrl={{ teamcity_url }}
name={{ agent_name }}
{% if teamcity_agent_variables %}
{% for variable in teamcity_agent_variables %}
{{ variable }}={{ teamcity_agent_variables[variable] }}
{% endfor %}
{% else %}
# no teamcity_agent_variables from ansible
{% endif %}
and when it arrived on the remote machine, without errors from ansible, it looked exactly the same - even though when I displayed the variables in the step before the template step, they existed
Update: version stuff.
% ansible --version
ansible [core 2.13.3]
config file = /Users/timb/git/mre-ansible/ansible.cfg
configured module search path = ['/Users/timb/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/local/Cellar/ansible/6.3.0/libexec/lib/python3.10/site-packages/ansible
ansible collection location = /Users/timb/.ansible/collections:/usr/share/ansible/collections
executable location = /usr/local/bin/ansible
python version = 3.10.6 (main, Aug 30 2022, 05:12:36) [Clang 13.1.6 (clang-1316.0.21.2.5)]
jinja version = 3.1.2
libyaml = True
% grep error_on /Users/timb/git/mre-ansible/ansible.cfg
#error_on_missing_handler = True
error_on_undefined_vars = True
It turned out that the teamcity_agent_variables had been over-ridden in one file by a list instead of a hash, so the attempts to deference it failed.
I have no idea why Ansible didn't print a useful error when jinja failed - nor indeed when the type of the variable changed - instead of simply copying the template.
Related
I'm using Ansible to retrieve a list of website we host over 50+ servers. I'm using a SQL query to store in a text file named as hostname_websites.txt using Ansible.
The next step is to build a prometheus (blackbox exporter) configuration file using that data.
Each line of each text file represent an URL. For the sake of representation I'll declare that the URL is $URL and the hostname is $HOSTNAME.
I need some jinja2 magic to generate a single yaml file as :
{% for site in *** each text file *** %}
- targets: ['{{ $URL }}']
labels: {'node': '{{ $HOSTNAME }}'}
{% endfor %}
Given that in the end I need to have a config line containing each time the URL which is a line in text file, and the hostname as a label which is in the txt filename.
Every hostname is in a group called production in Ansible, so I tried looping over that group using jinja2 as follow:
{% for host in group['production'] %}
{{ lookup('file', '{{ host }}_websites.txt).splitlines()' }}
{% endfor %}
That gave me some jinja2 parsing error, as if it wasn't rendering the for loop at all.
I stopped here, in the spirit that I would build by configuration skel around this statement.
There are two problems with your template that jump out immediately:
There is no variable group in Ansible (unless you've created one explicitly); you probably want groups.
You never nest Jinja {{...}} markers. If you want to generate a string containing a variable value, you can use string concatenation:
lookup('file', host ~ '_websites.txt')
Or string formatting:
lookup('file', '%s_websites.txt' % host)
Assuming that our inventory has a host node0 in the group production and that there exists a file node0_websites.txt with the following content:
http://stackoverflow.com/
http://ansible.com/
http://stackexchange.com/
Then running this playbook:
- hosts: localhost
gather_facts: false
tasks:
- copy:
dest: output.txt
content: |
{% for host in groups['production'] %}
# Host: {{ host }}
{% for url in lookup('file', host ~ '_websites.txt').splitlines() %}
- {{ url }}
{% endfor %}
{% endfor %}
Generates the following content in output.txt:
# Host: node0
- http://stackoverflow.com/
- http://ansible.com/
- http://stackexchange.com/
I try to build elasticsearch cluster with ansible and I have a problem with jinja2.
How can I set in jinja2 IP addresses of other hosts?
I have in my inventory.ini:
[elasticsearch]
192.168.0.1
192.168.0.2
192.168.0.3
and I want in jinja2 template to pass two addresses 192.168.0.2,192.168.0.3 when there is more hosts than 1, to look something like that:
- discovery.seeds_hosts=192.168.0.2,192.168.0.3
but when is one host in inventory.ini
it should look like this:
- discovery.seeds_hosts=192.168.0.1
I tried with something like this(hostnames):
{% for host in groups['elasticsearch'] %}{% if host == ansible_host %}{% else %}{%if loop.index0 > 0 %}{% endif %}elasticsearch-{{ loop.index }}{% if not loop.last %},{% endif %}{% endif %}{% endfor %}
and it works only for more than 1 host in elasticsearch group. If in the inventory.ini is only one host, the variable discovery.seeds_hosts will be empty.
Ansible version:
ansible [core 2.14.1]
config file = /etc/ansible/ansible.cfg
configured module search path = ['/home/ansible/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/local/lib/python3.10/site-packages/ansible
ansible collection location = /home/ansible/.ansible/collections:/usr/share/ansible/collections
executable location = /usr/local/bin/ansible
python version = 3.10.9 (main, Dec 8 2022, 01:46:27) [GCC 10.2.1 20210110] (/usr/local/bin/python)
jinja version = 3.1.2
libyaml = True
You could use a conditional that checks the number of hosts:
{% if groups.elasticsearch|length > 1 %}
- discovery.seeds_hosts={{ groups.elasticsearch[1:]|join(',') }}
{% else %}
- discovery.seeds_hosts={{ groups.elasticsearch.0 }}
{% endif %}
I've tested this locally, and with multiple hosts in the inventory:
[elasticsearch]
192.168.0.1
192.168.0.2
192.168.0.3
This produces:
- discovery.seeds_hosts=192.168.0.2,192.168.0.3
With a single host in the inventory:
[elasticsearch]
192.168.0.1
This produces:
- discovery.seeds_hosts=192.168.0.1
I have an Ansible playbook that sets a lot of variables. One the playbooks has this task:
- name: create config file
template:
src: 'templates/main_config.j2'
dest: "{{ tmp_dir }}/main_config.json"
The template main_config.j2 writes strings that are defined as variables in the parent Ansible playbooks and tasks.
I want to include another Jinja2 template based on a value of an Ansible variable.
{% include "./templates/configurations.j2" %},
{% include "./templates/security.j2" %},
{% include './templates/' + {{ job }} + '_steps.j2' %}
job is a Ansible variable set in a parent playbook.
This is not working. What could be the problem?
You don't need to open a Jinja2 expression ({{ ... }}) to refer to a variable inside a statement ({% ... %}). You can use the variable name directly:
{% include './templates/' + job + '_steps.j2' %}
When running salt '*' state.highstate, my SLS files fail to render with the following message:
Data failed to compile:
----------
Rendering SLS 'base:files' failed: Jinja syntax error: no filter named 'yaml_encode'; line 6
---
{% for folder, options in salt['pillar.get']('dirs', {}).items() %}
{{ folder }}:
{%- load_yaml as foo %}
file.directory:
{% for key, val in options.items() %}
- {{ key }}: {{ val|yaml_encode }} <======================
{% endfor %}
- order: 1
{%- endload %}
{% endfor %}
---
The docs show this filter just being used wherever you want to escape YAML so I don't know why the filter is not defined.
All minions and the master are running Ubuntu Server 14.04, and Salt 2014.7.1 from the Launchpad PPA
Although the filter is documented, it is misleading as the docs are generated from the current develop branch, not the latest stable branch.
The docs for older versions are available from readthedocs.org.
The pull request that introduced this feature did not yet get "merged back" into 2014.7.
how can i loop in an ansible playbook over gathered facts?
I tried:
...
haproxy_backends:
- name: 'backend'
servers:
{% for host in groups['app-servers'] %}
- name: "{{ hostvars[host]['ansible_hostname'] }}"
ip: "{{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}"
{% endfor %}
But this doesn't work, it results in a Syntax Error. Is it even possible to use jinja in a playbook?
I use a ansible galaxy role (info.haproxy) and i don't want to change the provided templates.
No you can't do this.
This has to be done in a template, something like :
template/haproxy.cfg.j2 :
...
{% for host in groups['app-servers'] %}
backend {{ hostvars[host]['ansible_hostname'] }}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}:1234 check inter 5000 slowstart 2m
{% endfor %}
...
and use :
tasks:
- name: Deploy haproxy config
template: src=templatepath/haproxy.cfg.j2 dest=/etc/haproxy/haproxy.cfg
You get the idea, YMMV.
Good luck.