jinja2 include files based of of dict variable - ansible

I'm trying to create a templated file with jinja2 and ansible
currently i have a dict with multiple entries e.g:
internal_sites:
- {name: "site1" ,url: "site1.example.com" }
- {name: "site2" ,url: "site2.example.com" }
- {name: "site3" ,url: "site3.example.com" }
- {name: "site4" ,url: "site4.example.com" }
I have a a directory that has multiple files eached also named after the value in 'name'
├── templates
│ ├── base.cfg.j2
│ └── services
│ ├── internal
│ │ ├── site1.txt
│ │ ├── site2.txt
│ │ ├── site3.txt
│ │ ├── site4.txt
Within base.cfg.j2 im trying to loop over each item in internal_sites which generates the appropriate config i require and at the same time im trying to include the content of the text files.
the instructions i have are as follows:
{% for site in internal_sites %}
acl {{ site.name }} hdr(host) -i {{ site.url }}
use_backend {{ site.name }} if {{ site.name }}
backend {{ site.name}}
{% include "templates/services/internal/"{{lookup('vars', site.name)}} %}
{% endfor %}
however whenever i run my ansible i get the following error;
FAILED! => {"changed": false, "msg": "AnsibleError: template error while templating string: expected token 'end of statement block', got '{'. String: \n{% for site in internal_sites %}\n acl {{ site.name }} hdr(host) -i {{ site.url }}\n use_backend {{ site.name }} if {{ site.name }}\n\n backend {{ site.name}}\n {% include \"templates/services/internal/\"{{lookup('vars', site.name)}} %}\n\n{% endfor %}\n"}
i have also tried with the following
{% for site in internal_sites %}
{% set fileloc = 'templates/services/internal/' + site.name %}
acl {{ site.name }} hdr(host) -i {{ site.url }}
use_backend {{ site.name }} if {{ site.name }}
backend {{ site.name}}
{% include {{fileloc}} %}
{% endfor %}
I'm aware this is likely wrong but could anyone shed some guidance on the most appropriate way to do this?
expected output
acl site1 hdr(host) -i site1.example.com
use_backend site1 if site1
backend site1
contents of site1.txt
are included
here

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 loops matching on array/dict_list

I have an array like so
our_domains:
- domain: www.example.com
urls:
- { path: '/' }
- { path: '/test1' }
- domain: www.example2.com
urls:
- { path: '/' }
- { path: '/test2' }
and in a template I want to match on a given domain and then loop over the contents of urls
e.g
{% if item.value.domain == 'www.example2.com' %}
{% for item_url in item.urls %}
service_description http://anotherwebsite.com{{ item_url['path'] }}
{% endfor %}
{% endif %}
Im sure the FOR loop will work ok as its working in a similar context elsewhere in the code.
Im just struggling getting a conditional match on the domain name.
Any help would be appreciated
Thanks
it looks like you want to execute the template task for each of the elements of the our_domains list.
you should just remove the value from the if statement, rest looks fine:
{% if item.domain == 'www.example2.com' %}
{% for item_url in item.urls %}
service_description http://anotherwebsite.com{{ item_url['path'] }}
{% endfor %}
{% endif %}
if on the other hand, your intention was to generate only 1 file, you should use this template (one more for loop added enclosing the previous code):
{% for item in our_domains %}
{% if item.domain == 'www.example2.com' %}
{% for item_url in item.urls %}
service_description http://anotherwebsite.com{{ item_url['path'] }}
{% endfor %}
{% endif %}
{% endfor %}

Ansible jinja block

How to comment a particular line only by using jinja template.
I have my line
/var/log/maillog
I have tried
{% comment %}{{ var }} {% endcomment %}
also
{% if var is defined %}
{{ var }}
{% endif %}
Can you try this:
{# {{ var }} #}

Ansible template adds 'u' to array in template

I have the following vars inside of my ansible playbook I got the following structure
domains:
- { main: 'local1.com', sans: ['test.local1.com', 'test2.local.com'] }
- { main: 'local3.com' }
- { main: 'local4.com' }
And have the following inside of the my conf.j2
{% for domain in domains %}
[[acme.domains]]
{% for key, value in domain.iteritems() %}
{% if value is string %}
{{ key }} = "{{ value }}"
{% else %}
{{ key }} = {{ value }}
{% endif %}
{% endfor %}
{% endfor %}
Now when I go in the VM and see the file I get the following:
Output
[[acme.domains]]
main = "local1.com
sans = [u'test.local1.com', u'test2.local.com']
[[acme.domains]]
main = "local3.com"
[[acme.domains]]
main = "local4.com"
Notice the u inside of the sans array.
Excpeted output
[[acme.domains]]
main = "local1.com"
sans = ["test.local1.com", "test2.local.com"]
[[acme.domains]]
main = "local3.com"
[[acme.domains]]
main = "local4.com"
Why is this happening and how can I fix it?
You get u' ' because you print the object containing the Unicode strings and this is how Python renders it by default.
You can filter it with list | join filters:
{% for domain in domains %}
[[acme.domains]]
{% for key, value in domain.iteritems() %}
{% if value is string %}
{{ key }} = "{{ value }}"
{% else %}
{{ key }} = ["{{ value | list | join ('\',\'') }}"]
{% endif %}
{% endfor %}
{% endfor %}
Or you can rely on the fact, that the string output after sans = is a JSON and render it with to_json filter:
{{ key }} = {{ value | to_json }}
Either will get you:
[[acme.domains]]
main = "local1.com"
sans = ["test.local1.com", "test2.local.com"]
[[acme.domains]]
main = "local3.com"
[[acme.domains]]
main = "local4.com"
But the first one is more versatile.

octopress: how to display HTML based on user path

TLDR - How do I access the current user path in octopress/jekyll?
On my Octopress blog, I would like to display an HTML element only when the user is on the root path. The trouble is that {{page.url}} returns /index.html on the root path, while my root path in _config.yml is set to '/'.
Thus, this conditional does not work:
{% if page.url == site.root %}
<div class="blurb">
<p>{{ site.description }}</p>
</div>
{% endif %}
When I change the root in _config.yml to match /index.html it breaks all of the CSS. Why is page.url pointing to index.html? There is no /index.html in the url of my live website. Is /index.html referencing a controller somewhere?
Is there an easy way to access the current user path in Octopress/Jekyll?
For reference - I am pulling the page.url variable from a Jekyll doc. {{site.root}} refers to the root value in the _config.yml file.
Thanks!
I solved this by hardcoding '/index.html' into the conditional.
{% if page.url == 'index.html' %}
<div class="blurb">
<p>{{ site.description }}</p>
</div>
{% endif %}
In source/_layouts/default.html
{% if page.front_page %} {% include front_page.html %}{% endif %}
and then add front_page: true
in index.html
---
layout: default
navbar: Blog
front_page: true
---

Resources