ansible join list - regex

I run into a problem joining a list in ansible. Please look at the following playbook excerpt:
- name: determine how much time we have left
set_fact:
time_left1: "{{ cmd_output.stdout | regex_search(time_left_regex1, '\\1', '\\2') }}"
time_left2: "{{ cmd_output.stdout | regex_findall(time_left_regex2, '\\1') }}"
vars:
time_left_regex1: 'Remaining Time: ([0-9]+ Minutes) and ([0-9]+ Seconds)'
time_left_regex2: 'Remaining Time: (?:([0-9]+ Minutes) and )?([0-9]+ Seconds)'
- debug:
msg: "{{ time_left1 }}"
- debug:
msg: "{{ time_left1 | join(' ') }}"
- debug:
msg: "{{ time_left2 }}"
- debug:
msg: "{{ time_left2 | join(' ') }}"
When I run this playbook I get:
ok: [localhost] => {
"msg": [
"11 Minutes",
"48 Seconds"
] }
ok: [localhost] => {
"msg": "11 Minutes 48 Seconds" }
ok: [localhost] => {
"msg": [
[
"11 Minutes",
"48 Seconds"
]
] }
ok: [localhost] => {
"msg": [
"11 Minutes",
"48 Seconds"
] }
To me it seems that the regex_search returned I list that I can join to a simple string, and that regex_findall creates a nested list. Is this correct? And if so, how can convert the output to a string similar to the way regex_search works?
PS: for more information about the contents of cmd_output please take a look at my previous question

flatten the nested list. For example
- debug:
msg: "{{ time_left2|flatten|join(' ') }}"

Related

Ansible returns 'AnibleUnicode' instead of JSON, unable to filter

I'm unable to filter the following output with json_query, even though it works on a jmespath validator website.
Let me explain in detail with the code.
type_debug returns AnsibleUnicode for members value, so when I tried to use json_query to filter output it returned null. I need to get value from key ip, but I don't know how to do it in a good way.
---
- hosts: localhost
gather_facts: false
vars:
- RPN:
stdout: >-
"members": [
{
"id": 0,
"ip": "x.x.x.x",
"owner": "buzut",
"private_ip": "10.91.154.39",
"speed": 100,
"status": "active"
}
]
tasks:
- debug:
msg: "{{ RPN.stdout | type_debug }}"
From your question I understand that you like to gather all member with a json_query. To do so I've corrected the JSON slightly.
---
- hosts: localhost
become: false
gather_facts: false
vars:
RPN:
stdout: >-
{
"members": [{
"id": 0,
"ip": "x.x.x.x",
"owner": "buzut",
"private_ip": "10.91.154.39",
"speed": 100,
"status": "active"
}]
}
tasks:
- name: Show type(s)
debug:
msg:
- "{{ RPN.stdout | type_debug }}"
- "{{ RPN.stdout | from_json | type_debug }}"
- "{{ RPN.stdout | to_json | type_debug }}"
- name: Show members
debug:
msg: "{{ RPN.stdout | from_json | json_query('members') }}"
resulting into an output of
TASK [Show type(s)] *********
ok: [localhost] =>
msg:
- AnsibleUnicode
- dict
- str
TASK [Show members] *********
ok: [localhost] =>
msg:
- id: 0
ip: x.x.x.x
owner: buzut
private_ip: 10.91.154.39
speed: 100
status: active
I need to get value from key ip
- name: Show value of key 'ip'
debug:
msg: "{{ RPN.stdout | from_json | json_query('members[*].ip') }}"
resulting into an output of
TASK [Show value of key 'ip'] *********
ok: [localhost] =>
msg:
- x.x.x.x
Further Documentation
Selecting JSON data: JSON queries

Scrolling through a list with sublist in Ansible

I need to simultaneously traverse a data structure to align the values, I get this structure with the code below :
- name: Set instances
set_fact:
instance_db:
- 'db2inst1'
- 'db2inst2'
- name: Get Dialog Path
shell: db2 get dbm cfg | grep -i "Current member resolved DIAGPATH" | awk {'print $6'}
become: true
become_method: sudo
become_flags: -i
become_user: "{{ item }}"
loop: "{{ instance_db }}"
register: kud_path
- name: set_fact
set_fact:
db2_store: "[{{ instance_db | list }}] + [{{ kud_path.results|map(attribute='stdout')|list }}]"
vars:
db2_store: []
Result
{
"changed": false,
"ansible_facts": {
"db2_store": [
"db2inst1",
"db2inst2",
[
"/db2home/db2inst1/sqllib/db2dump/DIAG0000/",
"/home/db2inst2/sqllib/db2dump/DIAG0000/"
]
]
},
"_ansible_no_log": false
}
Now I need to automatically traverse these indexes where I put [*]. Because as it is, I can only access the data like this item[0][1]
- name: Creating silent config
template:
src: template.txt.j2
dest: '/tmp/template{{ item[0][*] | lower }}.txt'
mode: '0775'
loop:
- "{{ db2_store }}"
This workaround was necessary to be able to pass both values to the template
Template
################## Database connection config ##################
INSTANCE={{ item[0][*] }}
DIAGLOG_PATH={{ item[1][*] }}db2diag.log
Any suggestions on how to do this or a more elegant way to get the same result?
Assuming the following:
instance_db = ["db2inst1", "db2inst2"]
kud_path.results|map(attribute='stdout')|list = ["/db2home/db2inst1/sqllib/db2dump/DIAG0000/", "/home/db2inst2/sqllib/db2dump/DIAG0000/"]
Then you can use the zip filter which will pair the Nth element of the first list with the Nth element of the second list.
- debug:
msg: "{{ instance_db | zip(kud_path.results|map(attribute='stdout')|list) }}"
Outputs:
TASK [debug] ********************************************************************
ok: [localhost] => {
"msg": [
[
"db2inst1",
"/db2home/db2inst1/sqllib/db2dump/DIAG0000/"
],
[
"db2inst2",
"/home/db2inst2/sqllib/db2dump/DIAG0000/"
]
]
}
This makes it easy for you to loop over:
- name: Creating silent config
template:
src: template.txt.j2
dest: '/tmp/template{{ item[0] | lower }}.txt'
mode: '0775'
loop: "{{ instance_db | zip(kud_path.results|map(attribute='stdout')|list) }}"
################## Database connection config ##################
INSTANCE={{ item[0] }}
DIAGLOG_PATH={{ item[1] }}db2diag.log

Parsing a list of strings in ansible

I have this list:
ok: [localhost] => {
"ansible_facts": {
"servs": [
"Is active: Yes\nHost Name: northrend\n Ip Address: 192.168.1.12",
"Is active: Yes\nHost Name: pandaria \n IP Address: 192.168.1.30"
]
},
I've been trying to parse this list to extract the value of "Host name" but I just keep on getting the following error:
fatal: [localhost]: FAILED! => {
"msg": "Unexpected templating type error occurred on ({{ servs | regex_search('Host Name: (.)+\n'),multiline=True) }}): expected string or buffer"
}
This is what I tried:
- set_fact:
output: {{ servs | regex_search('Host Name: (.)+\n'), multiline=True) }}
How do you properly parse the contents of this list?
The list servs is nearly valid YAML. Only the indentation of the attribute 'Ip Address' is wrong. One space shifted to the right. If you manage to get rid of this space, e.g.
servs:
- "Is active: Yes\nHost Name: northrend\nIp Address: 192.168.1.12"
- "Is active: Yes\nHost Name: pandaria \nIP Address: 192.168.1.30"
the situation would be trivial. Simply convert the list of strings to a list of dictionaries, e.g.
- set_fact:
servs_list: "{{ servs_list|d([]) + [item|from_yaml] }}"
loop: "{{ servs }}"
gives
servs_list:
- Host Name: northrend
Ip Address: 192.168.1.12
Is active: true
- Host Name: pandaria
IP Address: 192.168.1.30
Is active: true
If you can't fix the indentation parse the data on your own. The task below gives the same result
- set_fact:
servs_list: "{{ servs_list|d([]) + [item|
regex_replace(regex, replace)|
from_yaml] }}"
loop: "{{ servs }}"
vars:
regex: '\n '
replace: '\n'
Get the output
- set_fact:
output: "{{ servs_list|map(attribute='Host Name')|list }}"
gives
output:
- northrend
- Pandaria
You have multiple errors here:
regex_search works on strings, not lists. So you need to apply it to each list element.
Your regex creates one group for each character in your hostname. The group should be (.+).
You are returning the whole match (including Host Name:) and not just the name itself. You need to specify which matching group to return.
I guess the list is from the output of some script or in some file. It would be a lot better to have it as json or yaml and then use from_json or from_yaml.
If you want to keep it like that, you need to do this:
- hosts: all
vars:
servs:
- |
Is active: Yes
Host Name: northrend
Ip Address: 192.168.1.12
- |
Is active: Yes
Host Name: pandaria
IP Address: 192.168.1.30
tasks:
- debug:
msg: "{{ servs }}"
- set_fact:
output: "{{ servs | map('regex_search', 'Host Name: (.+)\n', '\\1') | list }}"
- debug:
msg: "{{ output }}"
This is the output:
TASK [debug] ************
ok: [local] => {
"msg": [
"Is active: Yes\nHost Name: northrend\nIp Address: 192.168.1.12\n",
"Is active: Yes\nHost Name: pandaria\nIP Address: 192.168.1.30\n"
]
}
TASK [set_fact] ***************
ok: [local]
TASK [debug] ************
ok: [local] => {
"msg": [
[
"northrend"
],
[
"pandaria"
]
]
}

ansible identifying values in a list

The below code is attempting to find all the keys associated with the value HWIC-8A. I have tried a few different variations & I can't get the key to print, without doing something really long winded. As i'll be repeating this code with different values, i don't want to search each key/value pair individually within that list.
MODULES:
Slot_0_SubSlot_0: HWIC-8A
Slot_0_SubSlot_1: EHWIC-VA-DSL-M
Slot_0_SubSlot_3: HWIC-8A
- name: Apply HWIC-8A Build
debug:
msg: "{{ item.key }}"
with_items: "{{ MODULES }}"
when: "{{ item.value }} == HWIC-8A"
Maybe that's something for you:
---
- hosts: localhost
vars:
MODULES:
Slot_0_SubSlot_0: HWIC-8A
Slot_0_SubSlot_1: EHWIC-VA-DSL-M
Slot_0_SubSlot_3: HWIC-8A
tasks:
- debug: var=MODULES
- debug: msg="{{ MODULES | dict2items }}"
- debug: msg="{{ MODULES | dict2items | selectattr('value','match','HWIC-8A') | map(attribute='key')| list }}"
Then if you would like to have multiple matches, you could solve it with an MATCH list:
---
- hosts: localhost
vars:
MODULES:
Slot_0_SubSlot_0: HWIC-8A
Slot_0_SubSlot_1: EHWIC-VA-DSL-M
Slot_0_SubSlot_3: HWIC-8A
Slot_1_SubSlot_3: HWIC-8C
Slot_1_SubSlot_2: HWIC-8C
MATCH:
- HWIC-8A
- HWIC-8C
tasks:
- debug:
msg: "{{ MODULES | dict2items | selectattr('value','match',item) | map(attribute='key')| list }}"
with_items: "{{ MATCH }}"
Output:
TASK [debug] ***********************************************************************************************************************************************************************************
Thursday 27 August 2020 15:08:10 +0200 (0:00:00.042) 0:00:02.037 *******
ok: [localhost] => (item=HWIC-8A) => {
"msg": [
"Slot_0_SubSlot_0",
"Slot_0_SubSlot_3"
]
}
ok: [localhost] => (item=HWIC-8C) => {
"msg": [
"Slot_1_SubSlot_3",
"Slot_1_SubSlot_2"
]
}
I would use use jinja templates to do it. Something like this:
- name: Apply HWIC-8A Build
debug:
msg: '{% for m in MODULES %}{% if MODULES[m] == "HWIC-8A" %}{{ m }} {% endif %}{% endfor %}'
Which will give you this:
ok: [localhost] => {
"msg": "Slot_0_SubSlot_0 Slot_0_SubSlot_3 "
}
There is probably a fancy way using filters as well.

ansible: how to split string with multiple delimiters

Here is my output.
"result.containers": [
{
"Image": "cna.docker.dev/webproxy:1.0.0",
},
{
"Image": "cna-docker-dev-local.docker.dev/lega-customer:1.0.1",
}
]
Here is my code
- name: adding it to groups using images
add_host:
name: "{{ inventory_hostname }}"
groups: '{{ (item.Image.split("/")[1:] | regex_replace("^/", "")).split(":")[0] }}'
with_items: "{{ result.containers }}"
I'm getting the below output:
"add_host": {
"groups": [
"['webproxy"
],
"host_name": "vm4.nodekite.com",
"host_vars": {
}
I would like to filter webproxy from cna.docker.dev/webproxy:1.0.0.
But I'm getting "['webproxy" instead of "webproxy.
Could someone please help!! I even tried this code which fails
(item.Image.split("/")[1:]).split(":")[0]
- debug:
msg: "{{ item.Image | regex_replace('.*?/(.*?):.*', '\\1') }}"
loop: "{{ result.containers | flatten(1) }}"
Example regex is non greedy, adjust as needed.
Gives
ok: [localhost] => (item={'Image': 'cna.docker.dev/webproxy:1.0.0'}) => {
"msg": "webproxy"
}
ok: [localhost] => (item={'Image': 'cna-docker-dev-local.docker.dev/lega-customer:1.0.1'}) => {
"msg": "lega-customer"
}