Convert Unicode to String with Ansible and Jinja2 - ansible

I need help with converting a Unicode variable to a string in order for the below Ansible construct to work.
In this particular case, I want to use the item.keys() method in order to get the current env name (i.e. uat) but I get [u'uat'] instead. I have been searching the Internet but could not find a way to convert [u'uat'] to a simple uat.
defaults/main.yml:
blablabla:
env:
- uat:
accounts:
- david:
email: david#example.com
- anna:
email: anna#example.com
- develop:
accounts:
- john:
email: john#example.com
tasks/main.yml:
- include_tasks: dosomething.yml
with_items:
- "{{ blablabla.env }}"
tasks/dosomething.yml:
- name: Get accounts
set_fact:
accounts: "{%- set tmp = [] -%}
{%- for account in item[item.keys()].accounts -%}
{{ tmp.append(account) }}
{%- endfor -%}
{{ tmp }}"
error message:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<failed value="True"/>
<msg value="The task includes an option with an undefined variable. The error was: dict object has no element [u'uat']
The error appears to have been in 'dosomething.yml': line 9, column 3, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- name: Get accounts
^ here
exception type: <class 'ansible.errors.AnsibleUndefinedVariable'>
exception: dict object has no element [u'uat']"/>
</root>
Alternatively, I would also welcome alternative approaches, as long the data structure (i.e. the defaults/main.yml file) remains unchanged.

I get [u'uat']
This is not a "Unicode string", this is a list ― pay attention to [ ].
As item.keys() returns a list, but you want to use it as an index to item[], you must select the element. So either use first filter or [0]:
- name: Get accounts
set_fact:
accounts: "{%- set tmp = [] -%}
{%- for account in item[item.keys()|first].accounts -%}
{{ tmp.append(account) }}
{%- endfor -%}
{{ tmp }}"

Related

With dbt. How do I use a jinja macro in a yaml file

I have a yaml file like this:
models:
- name: test_view
description: "test"
config:
meta:
database_tags:
ACCOUNT_OBJECTS.TAGS.ENV: DEV`
I am trying automatically change 'DEV' to PROD when it's in that environment. I have a macro that gets the variable from targets.name
This is the jinja code:
{% macro test_macro(target) %}
{%- if target.name == "dev" -%} DEV
{%- elif target.name == "prod" -%} PROD
{%- else -%} invalid
{%- endif -%}
{% endmacro %}`
However, when I try to use the macro I get 'test_macro is undefined'
eg. ACCOUNT_OBJECTS.TAGS.ENV: {{ test_macro(target)}}
Is it that custom macros still cannot be used in yaml files?
Macros are for templating the SQL queries DBT compiles from its models. The YAML files are for configuration; they are not themselves models and do not support macros.
I went looking, and there is an active discussion about whether this could be supported in future, but as of the end of 2022 it is not possible.
Have you considered using a config block inside your model in order to set these metadata?

Ansible - Read subkey from dict entries and concat to string

I'm pretty new to Ansible, coming from Puppet I really like it.
I'm trying to get a string compiled from server admin_port.
Vars file:
webservers:
ws1:
listen_address: "webserver1.mydomain"
admin_port: "7779"
http_port: "7777"
ssl_port: "4443"
ws2:
listen_address: "webserver2.mydomain"
admin_port: "7779"
http_port: "7777"
ssl_port: "4443"
I'm templating quite a customised set of config files so I'm trying to get the two listen ports into this format:
ports=7779,7779
I've tried:
vars:
webserver_admin_ports: "{{lookup('subelements', webservers, 'admin_port', {'skip_missing': True})}}"
But I run into the issue:
Error was a <class 'ansible.errors.AnsibleError'>, original message: the key admin_port should point to a list, got '7779'"}
I'm sure this isn't too tricky and the data structure is simple enough, there could be 1 or 10 servers.
In Puppet I'd do this in the template, but it seems with ansible its better to pass a var.
Thanks,
For anyone with a similar question, this is my solution.
In the template, do the lookup, it keeps the code cleaner:
{%- set serverNames = [] -%}
{%- set adminPorts = [] -%}
{%- set listenAddresses = [] -%}
{%- set httpPorts = [] -%}
{%- for server in webserver_meta -%}
{{- serverNames.append(server) -}}
{{- adminPorts.append(webserver_meta[server].admin_port) -}}
{{- listenAddresses.append(webserver_meta[server].listen_address) -}}
{{- httpPorts.append(webserver_meta[server].http_port) -}}
{%- endfor %}
serverNames={{ serverNames|join(',') }}
serverAdminPorts={{ adminPorts|join(',') }}
serverListenAddress={{ listenAddresses|join(',') }}
serverHttpPorts={{ httpPorts|join(',') }}

ANSIBLE: How to append variable from inventory groups

I have an ansible inventory with groups as follows:
+hosts/
+all/
+group_vars/
- linux.yml
- webserver.yml
- dbserver.yml
...
And I have a playbook that sets monitoring for hosts; and the kind of monitoring is done by plugins. So in each group y set a list monitoring_plugins that contain the plugin to be able t monitor each service.
Inside each group yml I try to "append" to the list:
monitoring_plugins: {{ monitoring_plugins|default([]) + [ 'whatever_plugin_correspond_to_group' ] }}
But it doesn't work as expected, being expected that if a host belongs to more than one group, it should have the plugins corresponding to those groups.
Is there a way to acommplish this?
Thanks in advance.
What you're describing should work as expected from within a task, but you cannot have executable code in a vars or group_vars yaml or json file -- those are static
So you will need to set a distinct name at the low level and then roll them up at the top level:
group_vars/
dbserver.yml # sets monitoring_plugins_dbserver: ["a", "b"]
linux.yml # sets monitoring_plugins_linux: ["c", "d"]
and then in your tasks, something like this (but be forewarned I haven't tested this specific example):
- set_fact:
monitoring_plugins: >-
{% set results = [] %}
{% for g in groups.keys() %}
{% set _ = results.extend(vars['monitoring_plugins_'+g]|d([])) %}
{% endif %}
{{ results }}

Liquid syntax error: Tag was not properly terminated with regexp: /\%\}/

I have a an if condition in my liquid template like this
Username: {{user.email}}
{% if extras['password_to_be_sent'] %}
Password: {{extras['password_to_be_sent']}}
{% endif %}
Trial expiration: {{user.trial_expiration}}
However this is generating a line break when the if condition does not evaluate to true
so the above generated output like this
Username: Abc
Trail Expiration: 2019-11-10
I want to remove the additional line break when the if condition does not evaluate to true
i tried adding - as suggested here https://shopify.github.io/liquid/basics/whitespace/
so updated code to
Username: {{user.email}}
{%- if extras['password_to_be_sent'] -%}
Password: {{extras['password_to_be_sent']}}
{%- endif -%}
Trial expiration: {{user.trial_expiration}}
but this is giving an exception Liquid::SyntaxError (Liquid syntax error: Tag '{%- if extras['password_to_be_sent'] -%}' was not properly terminated with regexp: /\%\}/)
Additionally, I am saving the template code in database, if that helps.
Any help in this would be appreciated. Thanks.
Answered above in a comment, but here it is again:
I see you've also tagged Ruby on Rails — if your Rails app is consuming a version of Liquid earlier than 4.0.0, you won't be able to use the whitespace control tags. Those were added in Liquid 4.0.0.
Did you check to make sure extras['password_to_be_sent'] doesn't return a truthy but empty string? Maybe it should be:
Username: {{user.email}}
{%- if extras['password_to_be_sent'].present? -%}
Password: {{extras['password_to_be_sent']}}
{%- endif -%}
Trial expiration: {{user.trial_expiration}}
When I change line 134 from
int dis[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
to
int dis[4][2]={ {0,1},{0,-1},{1,0},{-1,0}};
It fixed !

how to get var value which with a var in name using jinja2

I am using ansible to template a jinja2 file.
IP:{{ ansible_eth0.ipv4.address }}
IP:{{ ansible_docker0.ipv4.address }}
IP:{{ ansible_{{ ka_interface }}.ipv4.address }}
there is a var named ka_interface for network adapter.
but you will get error in 3rd var
(IP:{{ ansible_{{ ka_interface }}.ipv4.address }} )
It seems that var in jinja2 template can be nested.
It's not possible to construct a dynamic variable with Jinja2 syntax.
However, you can access any play-bound variables via the builit-in vars hash object:
{{ vars['ansible_' + ka_interface]['ipv4']['address] }}
Edit: Fixed hash syntax
follow Chris Lam 's advice,
It works
- name: test
shell: echo {{ vars['ansible_' + ka_interface]['ipv4']['address'] }}
tags: test

Resources