I'd like to build to simple if logic into my Jinja2 template with nested arrays.
Firstly, including my vars for reference.
In YML (from host_vars):
interfaces:
- name: ae10
ipv4:
- address: 4.4.4.4
mask: 8
in JSON (from Ansible debug):
"interfaces": [
{
"ipv4": [
{
"address": "4.4.4.4",
"mask": 8
}
],
"name": "ae10"
}
],
I would like the if statement to make sure that ipv4.address and ipv4.mask have values. The only way I seem to be able to do it at the moment, is to map the nested array first, and then run if statement. But I'm wondering if the if statement can be ran earlier? Like even before the for loop starts?
{%for interface in interfaces%}
{%for ip in interface.ipv4%}
{% if ip.address and ip.mask%}
matched {{ip.address}}/{{ip.mask}} on {{interface.name}}
{% else %}
nothing matched
{% endif %}
{%endfor%}
{%endfor%}
It might, or might not be possible - and I might be missing something silly. Your thoughts would be greatly appreciated.
Q: "Can the if statement be run earlier? Like even before the for loop starts?"
A: No. It's not possible. It's possible to omit items that don't comply with the conditions, create a new dictionary, and process the new dictionary.
The task below is testing in the loop
- debug:
msg: "matched {{ item.1.address }}/{{ item.1.mask }} on {{ item.0.name }}"
loop: "{{ interfaces|subelements('ipv4') }}"
when:
- item.1.address is defined
- item.1.mask is defined
gives
"msg": "matched 4.4.4.4/8 on ae10"
Conditional output can be achieved also with the template below
{% for item in interfaces %}
{% for ip in item.ipv4 %}
{% if ip.address and ip.mask%}
matched {{ ip.address }}/{{ ip.mask }} on {{ item.name }}
{% else %}
nothing matched
{% endif %}
{% endfor %}
{% endfor %}
Related
I am trying to run a procedure using Custom Macro. The Macro run the SQL(Call Statememnt) present in the Model sql file.
I am able to run the procedure but my objective is to capture the response of the procedure and fail the dbt run if response is 'Failure'. The procedure can return string 'Success' or 'Failure'.
I am able to capture the return but not sure how to explicitly fail the run based on the return value.
Macro:
{% materialization call_proc, adapter='snowflake' -%}
{% set query %}
use schema "{{ database }}"."{{ schema }}"
{% endset %}
{% do run_query(query) %}
{%- call statement('main',fetch_result=True) -%}
{{ sql }}
{%- endcall -%}
{% if execute %}
{%- set QRY_RSLT = load_result('main')['data'][0][0] -%}
{{log (QRY_RSLT)}}
{% endif %}
{{ return({'relations': []}) }}
{%- endmaterialization %}
Model SQL:
{{ config(
materialized='call_proc',
database='TEST_DB',
schema = 'TEST'
)
}}
call "{{ database }}"."{{ schema }}".TEST_PROC('TEST');
Checking the QRY_RSLT in logs I can see Success/Failure being printed. How to fail the model if 'Failure' is returned by Proc?
As I mentioned in my other answer, I don't think it's a good idea to use a custom materialization to call a stored procedure. You're better off using a macro for this.
But putting that aside, you can raise an exception using jinja:
{% if execute %}
{%- set QRY_RSLT = load_result('main')['data'][0][0] -%}
{% if QRY_RSLT == "failure" %}
{{ exceptions.raise_compiler_error("Invalid `QRY_RESULT`. Got: " ~ QRY_RSLT) }}
{% endif %}
{% endif %}
I have this piece of code
---
- name: gather facts
hosts: all #
- hosts: proxy
tasks:
- blockinfile:
path: /etc/example
block: |
{% for n in groups['all'] %}
{{ hostvars[n]['ansible_default_ipv4']['address'] }}
{% endfor %}
if one node from the group all is down, the jinja loop is failing.
I did not find a solution to prevent that (tried with skip_untreachable: true without success for instance). Off course I can comment the down node in the inventory but this is neither convenient nor reliable.
How can I prevent that ?
I am trying to print the list of hosts in an ansible task;
I want to have spaces among them, however the following loop does not achieve the purpose:
{% for host in groups['all'] -%} {{ host }}:6379 {%- endfor %}
Any suggestions?
If you're aiming to get them all on a single line and don't mind having a space at the start you could simply do:
{% for host in groups['all'] -%} {{ ' ' ~ host }}:6379 {%- endfor %}
You could also add a character to the end of each item (e.g space or something else), while skipping the last one. Note the space between the if and endif:
{% for host in groups['all'] -%}
{{ host }}:6379
{%- if not loop.last %} {% endif %}
{%- endfor %}
As an alternative to the jinia loop, you could use ansibles join-filter, see in https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#id8
in this case :
{{ groups["all"] | join(":6379 ") }}
Join is the opposite for split. split takes a string and a delimiter and then return a list. join takes a list and a string to concatenate all list-elements to one string.
i want to create an dynamical playbook for my infrastructure.
i need this line in my config:
wsrep_cluster_address='gcomm://192.168.126.38,192.168.126.39,192.168.126.40'
my template looks like this:
wsrep_cluster_address = 'gcomm://{% for host in groups['db-server']%}{{hostvars[host]['ansible_host']}},{% endfor %}'
it works and looks like this on the host:
wsrep_cluster_address = 'gcomm://172.16.120.45,172.16.120.40,172.16.120.42,'
the last comma is breaking my nerves.
Is there a way to tell ansible not to comma the last entry of the loop?
Tanks for any help, have a great day
Found the solution, thanks to my developer.
wsrep_cluster_address = 'gcomm://{% for host in groups['db-server']%}{{hostvars[host]['ansible_host']}}{% if not loop.last %},{% endif %}{% endfor %}'
You saved my Day!
Also if you need line in config file like JSON:
nodelist = ["192.168.126.38","192.168.126.39","192.168.126.40"]
This is your Ansible for this:
nodelist={%for host in groups['mygroup']%}"{{hostvars[host].ansible_eth0.ipv4.address}}"{% if not loop.last %},{% endif %}{% endfor %}
Here is if full example:
- name: Create List of nodes to be added into Cluster
set_fact: nodelist={%for host in groups['mygroup']%}"{{hostvars[host].ansible_eth0.ipv4.address}}"{% if not loop.last %},{% endif %}{% endfor %}
- debug: msg=[{{nodelist}}]
- name: Set Cluster node list in config file
lineinfile:
path: "/etc/myfonfig.cfg"
line: "hosts: [{{ nodelist }}]"
as results you will have the following line in config file:
hosts: ["192.168.126.38","192.168.126.39","192.168.126.40"]
I am trying to create a sidebar-menu that looks like this.
iOS
-entry1
-entry2
-entry3
Web
-entry1
-entry2
Other
-entry1
-entry2
Each "post" (as in - the files in the dir "_posts") will have one (or more) categories in the meta-data (at the top of the file) and is to be listed underneath that header in the menu. So for example, if I make a post which has got "categories: iOS" it should be listed under the "iOS" header in the menu.
{% for cat in ["jekyll","ios","test"] %} // This is what I am unable to get right
{% for post in site.posts %}
{% if post.category == cat %}
{{ post.categories }} // test
<li>{{ post.title }}</li>
{% endif %}
{% endfor %}
{% endfor %}
Is it not possible to create an array on the fly like this? I am finding it really hard to google this specific question I have.
Alright to create an array on the current page I needed to use "front matter" i.e. the things on top of the pages surrounded by "---"
This is the solution:
Top of page:
---
cats: [1,2,3,asd]
---
Further down:
{% for c in page.cats %}
{{c}}<br/>
#loop through more here.
{% endfor %}
</body>