I have this playbook and I need to find a way to display the values of the list of dictionaries under prerequisite knowing that we have different key names. how can I display theses values keeping the same syntax of my playbook?
Expected output:
"linux/mysql/8.0.19/prerequis/mysql-community-common-8.0.19-1.el7.x86_64.rpm"
"mysql-community-common-8.0.19-1.el7.x86_64"
"linux/mysql/8.0.19/prerequis/mysql-community-libs-8.0.19-1.el7.x86_64.rpm"
"mysql-community-libs-8.0.19-1.el7.x86_64"
"linux/mysql/8.0.19/prerequis/mysql-community-client-8.0.11-1.el7.x86_64.rpm"
"mysql-community-client-8.0.11-1.el7.x86_64"
"linux/mysql/8.0.19/prerequis/mysql-shell-8.0.19-1.el7.x86_64.rpm"
"mysql-shell-8.0.19-1.el7.x86_64"
- name: "extract values from a list of dictionaries"
hosts: localhost
tasks:
- name: "adding variables"
set_fact:
products:
mysql_8_0_19:
CentOS_7:
signature: "mysql-community-server-8.0.19-1.el7.x86_64"
url: "linux/mysql/8.0.19/mysql-community-server-8.0.19-1.el7.x86_64.rpm"
pymysql_url: "linux/mysql/8.0.19/prerequis/PyMySQL-0.9.3.tar.gz"
prerequisite:
- mysql_c_common_url: "linux/mysql/8.0.19/prerequis/mysql-community-common-8.0.19-1.el7.x86_64.rpm"
mysql_c_common_signature: "mysql-community-common-8.0.19-1.el7.x86_64"
- mysql_c_libs_url: "linux/mysql/8.0.19/prerequis/mysql-community-libs-8.0.19-1.el7.x86_64.rpm"
mysql_c_libs_signature: "mysql-community-libs-8.0.19-1.el7.x86_64"
- mysql_c_client_url: "linux/mysql/8.0.19/prerequis/mysql-community-client-8.0.11-1.el7.x86_64.rpm"
mysql_c_client_signature: "mysql-community-client-8.0.11-1.el7.x86_64"
- mysql_shell_url: "linux/mysql/8.0.19/prerequis/mysql-shell-8.0.19-1.el7.x86_64.rpm"
mysql_shell_signature: "mysql-shell-8.0.19-1.el7.x86_64"
- name: " display "
debug:
msg: "{{ item }}"
with_items: "{{ products.mysql_8_0_19.prerequisite }}"'
Getting the values of a dictionary in Ansible with an "unknown" set of keys usually call for the usage of dict2items.
Your use case does fit the bill, you just have to map this filter on all elements of the prerequisite list, then flatten it and finally extract the values with another map filter.
So, you can achieve what you need with the task:
- debug:
msg: "{{ item }}"
loop: >-
{{
products.mysql_8_0_19.prerequisite
| map('dict2items')
| flatten
| map(attribute='value')
}}
You might want to add a | list filter at the end if you are on an old version of Ansible and get an error like <generator object do_map at 0x7f3xxxxx>
Given the task:
- debug:
var: >-
products.mysql_8_0_19.prerequisite
| map('dict2items')
| flatten
| map(attribute='value')
vars:
products:
mysql_8_0_19:
prerequisite: "{{ prerequisite }}"
prerequisite:
- mysql_c_common_url: >-
linux/mysql/8.0.19/prerequis{#- allows line wrap -#}
/mysql-community-common-8.0.19-1.el7.x86_64.rpm
mysql_c_common_signature: >-
mysql-community-common-8.0.19-1.el7.x86_64
- mysql_c_libs_url: >-
linux/mysql/8.0.19/prerequis{#- allows line wrap -#}
/mysql-community-libs-8.0.19-1.el7.x86_64.rpm
mysql_c_libs_signature: >-
mysql-community-libs-8.0.19-1.el7.x86_64
- mysql_c_client_url: >-
linux/mysql/8.0.19/prerequis{#- allows line wrap -#}
/mysql-community-client-8.0.11-1.el7.x86_64.rpm
mysql_c_client_signature: >-
mysql-community-client-8.0.11-1.el7.x86_64
- mysql_shell_url: >-
linux/mysql/8.0.19/prerequis{#- allows line wrap -#}
/mysql-shell-8.0.19-1.el7.x86_64.rpm
mysql_shell_signature: >-
mysql-shell-8.0.19-1.el7.x86_64
This yields:
- linux/mysql/8.0.19/prerequis/mysql-community-common-8.0.19-1.el7.x86_64.rpm
- mysql-community-common-8.0.19-1.el7.x86_64
- linux/mysql/8.0.19/prerequis/mysql-community-libs-8.0.19-1.el7.x86_64.rpm
- mysql-community-libs-8.0.19-1.el7.x86_64
- linux/mysql/8.0.19/prerequis/mysql-community-client-8.0.11-1.el7.x86_64.rpm
- mysql-community-client-8.0.11-1.el7.x86_64
- linux/mysql/8.0.19/prerequis/mysql-shell-8.0.19-1.el7.x86_64.rpm
- mysql-shell-8.0.19-1.el7.x86_64
Related
I have got an ansible problem where I have to change the items of a list in some cases.
Imagine, if there is the string "apple0" and another digit from 0 to 9 the list element should be appended by the string T2. If there is any other element, lets say banana or peach, it should stay as it is.
---
- hosts: localhost
become: no
vars:
fruit: [banana,apple05,apple04,peach]
tasks:
- name: my task
set_fact:
"{{ item | replace('^apple0[0-9]*$','?1T2') }}"
loop: "{{fruit}}"
- debug:
msg:
- "{{fruit}}"
suggested output:
ok: [localhost] => {
"msg": [
[
"banana",
"apple05T2",
"apple04T2",
"peach"
]
]
}
For example
- set_fact:
fruit2: "{{ fruit2|default([]) + [(item is match('^apple0\\d$'))|
ternary(item ~ 'T2', item)] }}"
loop: "{{ fruit }}"
gives
fruit2:
- banana
- apple05T2
- apple04T2
- peach
The next option is to map the filter regex_replace. For example, the task below gives the same result
- set_fact:
fruit2: "{{ fruit|map('regex_replace', my_regex, my_replace)|list }}"
vars:
my_regex: '^(apple0\d)$'
my_replace: '\1T2'
NOTE: Original question expanded on here: Is there a way in Ansible to replace a dictionary value based on k:v lookup to another dictionary?
I have 3 dictionaries, the first 2 are k:v with string:integer type values; My 3rd dictionary is a k:v of string:string which I want to loop through first with dict #1 and to replace the k with the k:v and then the same with dict #2 but replacing v with k:v.
"dict_1": {
"office-core01": 85,
"office-core02": 86,
"office-fw01": 87,
"office-fw02": 88,
"office-server-sw01": 91,
"office-vpn01": 92,
"office-vpn02": 93
}
"dict_2": {
"con1": 129,
"con2": 130,
"con3": 131,
"con4": 132,
"con5": 133,
"con6": 134,
"con7": 135,
"con8": 136,
"con9": 137
}
"dict_3": {
"office-core01": "con1",
"office-core02": "con2",
"office-fw01": "con3",
"office-fw02": "con4",
"office-server-sw01": "con7",
"office-vpn01": "con5",
"office-vpn02": "con6"
}
In the end I need a dictionary of k:v pairs of integers; For example in the first iteration I need the hostnames/keys (office-core01) in dict_3 replaced with value from dict_1 (85) and then the 2nd run to replace the ports/values (con1) replaced with key from dict_2 (129) however using code supplied by Vladimir in original question complains about object of type 'int' has no len().
Include task (console-portid.yml):
---
- name: Replace Console Hostname ID
set_fact:
port_mapping: "{{ port_mapping | difference([item]) +
[dict(my_value | zip(my_keys))] }}"
vars:
my_key: "{{ item.keys() | list }}"
my_value: "{{ item.values() | list }}"
my_keys: "{{ my_key | map('regex_replace', port_id.key, port_id.value) | list }}"
loop: "{{ dict_3 | dict2items }}"
Invocation:
- name: Replace Device Console Ports ID
include_tasks: console-portid.yml
loop: "{{ dict_1 | dict2items }}"
loop_control:
loop_var: port_id
Q: "I want to turn office-core01: con1 into 85: 129"
A: The task below does the job
- set_fact:
dict_3a: "{{ dict_3a|default({})|
combine({dict_1[item.key]: dict_2[item.value]}) }}"
loop: "{{ dict_3|dict2items }}"
- debug:
var: dict_3a
give
"dict_3a": {
"85": 129,
"86": 130,
"87": 131,
"88": 132,
"91": 135,
"92": 133,
"93": 134
}
Q: "Is there a reason key is string and value still integer?"
A: Internal type of the variable is preserved. The variables evaluate to strings unless configured by DEFAULT_JINJA2_NATIVE. Quoting "This option preserves variable types during template operations. This requires Jinja2 >= 2.10."
Q: "Is there a reason key is string and value still integer? Does key always have to be a string?"
A: There are no restrictions on the keys. Quoting from 3.2.1.1. Nodes
"A YAML node represents a single native data structure. Such nodes have content of one of three kinds: scalar, sequence, or mapping. In addition, each node has a tag which serves to restrict the set of possible values the content can have."
"Mapping: The content of a mapping node is an unordered set of key: value node pairs, with the restriction that each of the keys is unique. YAML places no further restrictions on the nodes. In particular, keys may be arbitrary n
odes, the same node may be used as the value of several key: value pairs, and a mapping could even contain itself as a key or a value (directly or indirectly)."
---
- hosts: localhost
gather_facts: no
tasks:
- name: Loop over dict_3
debug:
msg: "{{ item.key }}: {{ item.value }}"
with_items: "{{ lookup('dict', dict_3) }}"
- name: Loop over dict_3 with replacements
debug:
msg: "{{ dict_1[item.key] }}: {{ dict_2[item.value] }}"
with_items: "{{ lookup('dict', dict_3) }}"
- name: Create new dictionary
set_fact:
dict_4: "{{ dict_4|default({}) | combine( {dict_1[item.key]: dict_2[item.value]} ) }}"
with_items: "{{ lookup('dict', dict_3) }}"
- name: Show dict_4
debug:
var: dict_4
i got a problem how to consume the following command output in Ansible
basically im trying to get the list of Active Directory OUs and loop over that list to search for specific name. My script works well when multiple OUs exists but i have an issue when only single OU exists. Explained below
tasks:
- name: PS - Pull System OUs from AD
win_command: powershell -
args:
stdin: "Get-ADOrganizationalUnit -LDAPFilter '(name=*)' -SearchScope 1 -SearchBase 'OU=SYSTEMS,DC=domain,DC=int' -Server domain.int | select-object name | ConvertTo-json"
become: yes
register: ou_reg_out
- name: Select Systems OU
block:
- name: Set list of standardized OUs as facts
set_fact:
ou_reg_list: "{{ ou_reg_out.stdout }}"
- name: Set System OU for system
set_fact:
ou_name: "OU={{item.name}}"
loop: "{{ ou_reg_list }}"
when: (item.name|upper) == (srv_type|upper)
when: ou_reg_out.stdout|length != 0
basically i need to be able to loop over the ou_reg_out.stdout.
It works when command returns multiple OUs as ou_reg_out.stdout returns list:
ou_reg_out.stdout:
- { name: OU1 }
- { name: OU2 }
issue is when only single OU exists , command doesnt return the list
ou_reg_out.stdout:
{ name: OU1 }
Any idea how to workaround this problem ?
Test the type of the variable and branch the code.
json_query filter helps to select the items from the list. Then ternary helps to conditionally select the value. The value of the first item that matches the condition is used. Defaults to 'NOTFOUND'.
For example the play bellow for both versions of ou_reg_list
- hosts: localhost
vars:
ou_reg_list:
- { name: OU1 }
- { name: OU2 }
# ou_reg_list:
# { name: OU1 }
srv_type: 'ou1'
tasks:
- set_fact:
ou_name: "OU={{ (ou_reg_list.name == srv_type|upper)|
ternary( ou_reg_list.name, 'NOTFOUND') }}"
when: ou_reg_list is mapping
- block:
- set_fact:
ou_names: "{{ ou_reg_list|json_query(query) }}"
vars:
query: "[?name=='{{ srv_type|upper }}'].name"
- set_fact:
ou_name: "OU={{ (ou_names|length > 0)|
ternary( ou_names.0, 'NOTFOUND') }}"
when: ou_reg_list is not mapping
- debug:
var: ou_name
gives
"ou_name": "OU=OU1"
I have a list in following format and need to replace commas with backlash but getting error when trying to use replace function.
"sub_keys": [
",,ps1,printer1",
",,ps1,printer2",
",,ps2,printer1"]
expected result:
new_list:[ "\\ps1\printer1",
"\\ps1\printer2"
"\\ps2\printer1"]
i tried the following code:
- name: Convert list
set_fact:
new_list: '{{ new_list + [item.replace (",","\")] }}'
with_items: "{{ sub_keys }}"
Have you tried:
- name: Convert list
set_facts:
new_list:
- "\\\\ps1\\printer1"
- "\\\\ps1\\printer2"
- "\\\\ps2\\printer1"
This question already has an answer here:
Ansible with_subelements
(1 answer)
Closed 4 years ago.
I'm trying to create a bunch of groups and members for a custom module using an external var file. All I need to do is to access a list of dictionaries in a list.
I have tried many things but the closest I might have for now is:
vars.yml:
groups_and_members:
FIRST_GROUP:
"memberx":
octet: 1
action: add
"nostandardy":
octet: 3
action: add
"memberz":
octet: 5
action: add
OTHER_GROUP:
"member1":
octet: 7
action: delete
"unexpected":
octet: 1
action: add
task.yml:
- name: "add members"
mymodule:
cmd: "{{ item.value.action }}"
parameters:
name: "{{ item.key }}"
octet: "{{ item.value.octet }}"
with_items: "{{ groups_and_members }}"
So the idea is to take from the list (groups_and_members).value(FIRST_GROUP in the first iteration). and use its members attribute in cmd. But because I don't know the name of the group nor the name of the member, I'm not sure how to access them. I will not know the name of the groups or the members beforehand. The dicts should be in a list, to not have to make a new task in case there's a new dict.
How can I access the group, member and member attributes?
I have tried with_dict, with_subelements etc. but can't seem to figure it out.
I should have formatted the list of dictionaries differently. This is a duplicate question: Answer here:
Ansible with_subelements
My vars should have looked like this:
groups_members:
- groupname: FIRST_GROUP
members:
- name: memberx
octet: "1"
action: add
- name: nostandardy
octet: "3"
action: add
- name: memberz
octet: "2"
action: add
- name: unexpected
octet: "1"
action: add
- groupname: RHOST
members:
- member: other1
octet: "1"
action: add
Could then be accessed as item.1.action or item.0.value and task be with
with_subelements:
- "{{ groups_members }}"
- members