Reputation: 59
i am stuck with the template below :
- hosts: localhost
gather_facts: false
vars:
test1:
role1: groupA
role2: [groupB, groupC]
role3: groupD
test2:
groupA: role1
groupB: role2
groupC: role2
tasks:
- name: "[Test1] Mapping Role/Group"
debug:
var: test1.items()|map('join', ":" )| join(",")
- name: "[Test1] List roles"
debug:
var: test1.keys() | join(',')
- name: "[Test2] Mapping Role/Group"
debug:
var: test2.items()|join(",")
- name: "[Test2] List roles"
debug:
var: test2.values() | unique() | join(",")
i prefer to use the format of the dict "test1" to construct the role/group mapping below
groupA: role1, groupB: role2, groupC: role2, groupD: role3
Actually, the result of the playlook is :
TASK [[Test1] Mapping Role/Group] **************************************************************************************************************************************************
Thursday 21 October 2021 15:06:25 +0200 (0:00:00.021) 0:00:00.021 ******
ok: [localhost] => {
"test1.items()|map('join', \":\" )| join(\",\")": "role1:groupA,role2:['groupB', 'groupC'],role3:groupD"
}
TASK [[Test1] List roles] **********************************************************************************************************************************************************
Thursday 21 October 2021 15:06:25 +0200 (0:00:00.030) 0:00:00.051 ******
ok: [localhost] => {
"test1.keys() | join(',')": "role1,role2,role3"
}
TASK [[Test2] Mapping Role/Group] **************************************************************************************************************************************************
Thursday 21 October 2021 15:06:25 +0200 (0:00:00.028) 0:00:00.080 ******
ok: [localhost] => {
"test2.items()|join(\",\")": "('groupA', 'role1'),('groupB', 'role2'),('groupC', 'role2')"
}
TASK [[Test2] List roles] **********************************************************************************************************************************************************
Thursday 21 October 2021 15:06:25 +0200 (0:00:00.028) 0:00:00.108 ******
ok: [localhost] => {
"test2.values() | unique() | join(\",\")": "role2,role1"
}
is it possible to do what i need with one line ? thanks
Upvotes: 0
Views: 237
Reputation: 17007
when the logic becomes complex, i think its easier to use a custom filter (written in python):
you create a folder filter_plugins in your playbook folder (i have named the file myfilters.py and the filter customfilter)
#!/usr/bin/python
class FilterModule(object):
def filters(self):
return {
'customfilter': self.customfilter
}
def customfilter(self, obj):
result = ""
for r in obj:
if type(obj[r]) is list:
for s in obj[r]:
result += ', ' if result else ''
result += '{value}: {key}'.format(value=s, key=r)
else:
result += ', ' if result else ''
result += '{value}: {key}'.format(value=obj[r], key=r)
return result
use case:
- name: vartest
hosts: localhost
vars:
test1:
role1: groupA
role2: [groupB, groupC]
role3: groupD
tasks:
- name: transform variable
set_fact:
disp: "{{ test1 | customfilter }}"
- name: debug
debug:
msg: "{{ disp }}"
result:
ok: [localhost] => {
"msg": "groupA: role1, groupB: role2, groupC: role2, groupD: role3"
}
the solution is generic, with list or no list
same result with:
vars:
test1:
role1: [groupA]
role2: [groupB, groupC]
role3: [groupD]
Upvotes: 1
Reputation: 44615
In my above comment, I was sure that a simple refactoring of your initial test1
dict to be consistent using list everywhere would made this very easy.
My goal was to answer with a solution not needing a single task/loop to calculate the result and to use some json_query
magic in the middle.
This was before I (re)discovered that there is still an unsolved issue and an unmerged PR in jmespath which prevent to easily work with data returned as tuples by some ansible filters like zip
or subelements
.
This ended up being quite a nightmare but since I finally got the expected result with my original goal (no tasks or loop), I'll share it here. The solution is using several workarounds to transform what ansible returns as tuples to json strings, parsed back in ansible so they become lists that jmespath can finally work with.
The following playbook, using a slighly transformed definition of your original var
---
- hosts: localhost
gather_facts: false
vars:
test1:
role1: [groupA]
role2: [groupB, groupC]
role3: [groupD]
q1: >-
[].[to_string(@)]
q2: >-
[].join(': ', [[1], [0].key])
my_map_string: >-
{{
test1
| dict2items
| subelements('value')
| json_query(q1)
| map('map', 'from_json')
| json_query(q2)
| join(', ')
}}
tasks:
- name: Show result
debug:
var: my_map_string
Gives:
PLAY [localhost] *****************************************************************************
TASK [Show result] *****************************************************************************
ok: [localhost] => {
"my_map_string": "groupA: role1, groupB: role2, groupC: role2, groupD: role3"
}
PLAY RECAP *****************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Upvotes: 0