Create a string from dictionary - ansible

I want to generate a string "key=value key=value ..." from this dictionary
command:
chain: dstnat
action: dst-nat
to-addresses: blaah
How can I achieve something like that in Ansible?
string = ""
for key, value in dict.items:
string += f'{key}={value} '

For example
- debug:
msg: "{{ string }}"
vars:
command:
chain: dstnat
action: dst-nat
to-addresses: blaah
string: "{{ command.keys()|list|
zip(command.values()|list)|
map('join', '=')|
join(' ') }}"
gives
msg: chain=dstnat action=dst-nat to-addresses=blaah

Related

Get Odd or Even Index Value from Variable in Ansible Playbook

I need to get odd or even index value from a variable list:
For example:
- hosts: myhost
vars:
- var1: ["test1","test2","test3","test4","test5"]
- odd_var: []
- even_var: []
I need odd_var to be ["test1","test3","test5"] and even_var to be ["test2","test4"] and also concatenate each variable string of odd_var and even_var to be one string like:
odd_string: "test1,test3,test5"
even_string: "test2,test4"
What should i do to achieve this?
I have tried :
- name: test
set_fact:
odd_list: "{{ odd_list | default([]) + [item] }}"
loop: "{{ var1 }}"
when: "{{ lookup('ansible.utils.index_of', var1, 'eq', item) is even }}
it works but i wonder if i can get more eficient way to do this
Since you said "index value", I'm going to take you at your word and base it on position in the list, not the numbers contained in the strings.
- hosts: localhost
vars:
var1:
- test1
- test2
- test3
- test4
- test5
odd_var: "{{ var1[::2] | join(',') }}"
even_var: "{{ var1[1::2] | join(',') }}"
tasks:
- debug:
msg: "{{ odd_var }} / {{ even_var }}"
Output:
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": "test1,test3,test5 / test2,test4"
}
Using regex, checking the last character.
- var1: ["test1","test2","test3","test4","test5"]
- even: "{{ var1 | select('search','.*[02468]$') | join(',') }}"
- odd: "{{ var1 | select('search','.*[13579]$') | join(',') }}"

Ansible string split

I have a split function in ansible based on a delimiter. But only want to get the first occurance of the delimiter string and the rest as the second string.
string: "hello=abcd=def=asd"
string1= string.split("=")[0]
string2= string.split("=)[1..n] (This is what i missing)
How can i achieve this in ansible with string.split?
Q: "Get the first occurrence of the delimiter string and the rest as the second string."
A: Join the rest of the string again
arr: "{{ string.split('=') }}"
string1: "{{ arr[0] }}"
string2: "{{ arr[1:]|join('=') }}"
Optionally, set the maxsplit parameter to 1
arr: "{{ string.split('=', 1) }}"
string1: "{{ arr.0 }}"
string2: "{{ arr.1 }}"
Both options give the same result
string1: hello
string2: abcd=def=asd

jinja2 turning lists into strings even when from_json is being used

I am trying to run a nested for loop in order to retrieve a nested value.
I would like to retrieve some_value_4 when some_value_3 matches a criteria that's predefined.
{
"some_dict": {
"some_key_0": "value_0",
"some_key_1": "value_1"
},
"testval_dict": {
"test_key_0": "some_value_0",
"test_key_1": "some_value_1",
"test_key_2": "some_value_2",
"test_key_3": "some_value_3",
"test_key_4": "some_value_4"
}
}
The playbook:
- hosts: localhost
tasks:
- set_fact:
mydict: "{{ lookup('file', '/tmp/file.json' ) | from_json }}"
- debug:
msg: |
"{% for item in mydict %}
{{ item }}
{% endfor %}"
when run, it alreay looks like dict names are treated as string and nothing more:
ansible-playbook /tmp/test_playbook.yml -c local -i ', localhost'
TASK [debug] ******************************************************************
ok: [localhost] => {}
MSG:
" somee_dict
testval_dict
"
Then when I add an itme.key to the debug task, the playbook fails:
MSG:
The task includes an option with an undefined variable. The error was: 'str object' has no attribute 'value'
Thank you.
edit for clarification
In the real example, I will not know the names of the dicts, so I cannot use some_dict or testval_dict, that is why I'm trying to go over this data source in an item.key or item.value form.
Q: "{% for item in mydict %} ... dict names are treated as string and nothing more."
A: This is correct. A dictionary, when evaluated as a list, returns the list of its keys. See some examples below
- debug:
msg: "{{ mydict.keys()|list }}"
- debug:
msg: "{{ mydict[item] }}"
loop: "{{ mydict.keys()|list }}"
- debug:
msg: "{{ mydict|difference(['testval_dict']) }}"
give
msg:
- some_dict
- testval_dict
msg:
some_key_0: value_0
some_key_1: value_1
msg:
test_key_0: some_value_0
test_key_1: some_value_1
test_key_2: some_value_2
test_key_3: some_value_3
test_key_4: some_value_4
msg:
- some_dict
See How to iterate through a list of dictionaries in Jinja template?
If you need to loop over the dictionary, you can use with_dict loop functionality. This way, if you loop over mydict and get item.key you will get somee_dict and testval_dict.
tasks:
- set_fact:
mydict: "{{ lookup('file', '/tmp/file.json')|from_json }}"
# This will get the top level dictionaries somee_dict and testval_dict
- debug:
var: item.key
with_dict: "{{ mydict }}"
And if you get item.value of mydict you will get the sub-dictionary:
- debug:
var: item.value
with_dict: "{{ mydict }}"
Will produce (showing output only for testval_dict):
"item.value": {
"test_key_0": "some_value_0",
"test_key_1": "some_value_1",
"test_key_2": "some_value_2",
"test_key_3": "some_value_3",
"test_key_4": "some_value_4"
}

How to get interpolated value of variable in Ansible / Jinja2

I'm trying to define Ansible variables this way:
user:
name: First Last
nick: '{{ vars["user"]["name"] | regex_replace("\W", "_") }}'
email: '{{ vars["user"]["nick"] }}#example.com'
And the result email is: "{{ vars[\"user\"][\"name\"] | regex_replace(\"\\W\", \"_\") }}#example.com.
I also tried to set email like this: {{ lookup("vars", "user.nick") }}#example.com or {{ lookup("vars", "user")["nick"] }}#example.com, and it says An unhandled exception occurred while running the lookup plugin 'vars'.
Is there a way to get resulting variable values as:
user:
name: First Last
nick: First_Last
email: First_Last#example.com
?
ansible 2.9.10,
python version = 3.8.5
It's not possible cross-reference keys in a dictionary. It's necessary to declare the variables outside the dictionary. For example, the playbook
- hosts: localhost
vars:
my_name: First Last
my_nick: "{{ my_name | regex_replace('\\W', '_') }}"
user:
name: "{{ my_name }}"
nick: "{{ my_nick }}"
email: "{{ my_nick }}#example.com"
tasks:
- debug:
var: user
gives (abridged)
user:
email: First_Last#example.com
name: First Last
nick: First_Last
A more flexible option is to create the variables in the loop. For example, the playbook
- hosts: localhost
vars:
users:
"First Last":
domain: example.com
tasks:
- debug:
msg:
- "name: {{ name }}"
- "nick: {{ nick }}"
- "email: {{ email }}"
loop: "{{ users|dict2items }}"
vars:
name: "{{ item.key }}"
nick: "{{ item.key|regex_replace('\\W', '_') }}"
email: "{{ nick ~ '#' ~ item.value.domain }}"
gives (abridged)
msg:
- 'name: First Last'
- 'nick: First_Last'
- 'email: First_Last#example.com'

Ansible - Not able to add prefix 'ansible_hostname' to each item in an array when array already has variables

I am trying to add 'ansible_hostname' as prefix to each item in an array but getting two different results.
when the array is static without any variables then the hostname is getting added.
when the array is having a variable declared then the fact 'ansible_hostname' is not getting resolved and is getting passed as a string.
I have array declared with variable, need help in passing the hostname without using loops.
Scenario 1:
- name: test_array
set_fact:
test_array: ["This is test1 {{ansible_hostname}}", "This is test2"]
- set_fact:
test_fact: "{{ test_array | map('regex_replace', '^(.*)$', ' {{ansible_hostname}}, \\1') | join('\n') }}"
output:
"test_fact": " {{ansible_hostname}}, This is test1 control\n {{ansible_hostname}}, This is test2"
Scenario 2:
- name: test_array
set_fact:
test_array: ["This is test1", "This is test2"]
- set_fact:
test_fact: "{{ test_array | map('regex_replace', '^(.*)$', ' {{ansible_hostname}}, \\1') | join('\n') }}"
output:
"test_fact": " host, This is test1 control\n host, This is test2"
You cannot use jinja2 expansion inside jinja2 expansion. You have to concatenate the hostname with the rest of your regexp replacement with the + operator:
- set_fact:
test_fact: "{{ test_array | map('regex_replace', '^(.*)$', ansible_hostname + ', \\1') | join('\n') }}"

Resources