Ansible parse stdout_lines to verify values of a particular item - ansible

I am using ansible to write some tests. I have to parse through the output of a command (stdout_lines) and verify the information corresponding to a particular name. The stdout_lines looks like the following.
The output is obtained from a cli command executed in bash.
"stdout_lines": [
"----------------------------------------------------------------------------------------",
"| Name | Count | Score | State|",
"----------------------------------------------------------------------------------------",
"| Jake | 5| 10 | CA |",
"| Mike | 3| 15 | AR |",
"----------------------------------------------------------------------------------------",
"|Total Scores: 2 |",
"----------------------------------------------------------------------------------------"
]
I would like to parse over the stdout_lines and find out the information related to, say for example 'Jake', and then verify if the the corresponding values are correct.
If in Python, I would Split the string into a list, find list-element that has Jake at [0] index and verify the other elements in it. I tried looking up but couldnot stumble upon anything that could help me. Can anyone throw some light on how to do this. Appreciate your help.
Thanks in advance,

here is a working example to get you started. i simulated your stdout_lines with the test_var.
we parse the test_var to get lines with 6 columns, when split with |.
we parse the list of rows from above task and try to find rows with 2nd column = Jake.
assuming its only 1 result (if you may have more rows, additional tasks are needed), get the 3 attributes in 3 variables and finally
print results
playbook:
---
- hosts: localhost
gather_facts: false
vars:
search_name: Jake
test_var:
- "----------------------------------------------------------------------------------------"
- "| Name | Count | Score | State|"
- "----------------------------------------------------------------------------------------"
- "| Jake | 5| 10 | CA |"
- "| Mike | 3| 15 | AR |"
- "| Jane | 3| 15 | AR |"
- "----------------------------------------------------------------------------------------"
- "|Total Scores: 2 |"
- "----------------------------------------------------------------------------------------"
tasks:
- name: pick up the lines we are interested in.
set_fact:
important_lines: "{{ important_lines|default([]) + [item] }}"
when: item.split('|') | length == 6
with_items:
- "{{ test_var }}"
- name: find the line with the name we are looking for in 2nd column
set_fact:
target_line: "{{ item }}"
when: item|trim is search(search_name)
with_items:
- "{{ important_lines }}"
- name: get the 3 attributes from the target line
set_fact:
attribute_count: "{{ target_line.split('|')[2]|trim }}"
attribute_score: "{{ target_line.split('|')[3]|trim }}"
attribute_state: "{{ target_line.split('|')[4]|trim }}"
- name: print results
debug:
msg: "name: {{ search_name }}, count: {{ attribute_count }}, score: {{ attribute_score }}, state: {{ attribute_state }}"
result:
[http_offline#greenhat-29 tests]$ ansible-playbook test.yml
PLAY [localhost] *******************************************************************************************************************************************************************************************************
TASK [pick up the lines we are interested in.] *************************************************************************************************************************************************************************
skipping: [localhost] => (item=----------------------------------------------------------------------------------------)
ok: [localhost] => (item=| Name | Count | Score | State|)
skipping: [localhost] => (item=----------------------------------------------------------------------------------------)
ok: [localhost] => (item=| Jake | 5| 10 | CA |)
ok: [localhost] => (item=| Mike | 3| 15 | AR |)
ok: [localhost] => (item=| Jane | 3| 15 | AR |)
skipping: [localhost] => (item=----------------------------------------------------------------------------------------)
skipping: [localhost] => (item=|Total Scores: 2 |)
skipping: [localhost] => (item=----------------------------------------------------------------------------------------)
TASK [find the line with the name we are looking for in 2nd column] ****************************************************************************************************************************************************
skipping: [localhost] => (item=| Name | Count | Score | State|)
ok: [localhost] => (item=| Jake | 5| 10 | CA |)
skipping: [localhost] => (item=| Mike | 3| 15 | AR |)
skipping: [localhost] => (item=| Jane | 3| 15 | AR |)
TASK [get the 3 attributes from the target line] ***********************************************************************************************************************************************************************
ok: [localhost]
TASK [print results] ***************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "name: Jake, count: 5, score: 10, state: CA"
}
PLAY RECAP *************************************************************************************************************************************************************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=0
[http_offline#greenhat-29 tests]$
hope it helps

Related

Debug of output in ansible fails with error

I am using following playbook
---
- name: ""
hosts: nexus
tasks:
- name: ""
debug:
var="{{ vlans | map(attribute='vlan_id') | join(',') }}"
and the output is:
ok: [nx01] => {
"10,20,100,30": "(10, 20, 100, 30)"
}
What is the type of my output ?
I tried
---
- name: ""
hosts: nexus
tasks:
- name: ""
debug:
var="{{ vlans | map(attribute='vlan_id') | join(',') | type_debug }} "
but get an error
ok: [nx02] => {
"str ": "VARIABLE IS NOT DEFINED!"
}
try this:
- name: Create a string with vlans
debug:
msg:
"{{ vlans | map(attribute = 'vlan_id') | join(',') }}"
- name: Create a string with vlans | type_debug
debug:
msg:
"{{ vlans | map(attribute = 'vlan_id') | join(',') | type_debug }}"
output:
TASK [Create a string with vlans] **************************************
ok: [localhost] => {
"msg": "10,20,100,30"
}
TASK [Create a string with vlans | type_debug] *************************
ok: [localhost] => {
"msg": "str"
}
The same example with list:
- name: Create a list with vlans
debug:
msg:
"{{ vlans | map(attribute = 'vlan_id') | list | default([]) }}"
- name: Create a string with vlans | type_debug
debug:
msg:
"{{ vlans | map(attribute = 'vlan_id') | list | default([]) | type_debug }}"
And the Output:
TASK [Create a list with vlans] ***************************************
ok: [localhost] => {
"msg": [
10,
20,
100,
30
]
}
TASK [Create a string with vlans | type_debug] ************************
ok: [localhost] => {
"msg": "list"
}
And if you need to set a variable:
- name: Set variable
set_fact:
mystrvar: "{{ vlans | map(attribute = 'vlan_id') | join(',') }}"
mylistvar: "{{ vlans | map(attribute = 'vlan_id') | list | default([]) }}"
- name: Debug mystrvar
debug:
var: mystrvar
- name: Debug mystrvar
debug:
var: mystrvar | type_debug
- name: Debug mylistvar
debug:
var: mylistvar
- name: Debug mylistvar
debug:
var: mylistvar | type_debug
The output:
TASK [Set variable] *******************************************************
ok: [localhost]
TASK [Debug mystrvar] *****************************************************
ok: [localhost] => {
"mystrvar": "10,20,100,30"
}
TASK [Debug mystrvar] *****************************************************
ok: [localhost] => {
"mystrvar | type_debug": "str"
}
TASK [Debug mylistvar] ****************************************************
ok: [localhost] => {
"mylistvar": [
10,
20,
100,
30
]
}
TASK [Debug mylistvar] ****************************************************
ok: [localhost] => {
"mylistvar | type_debug": "list"
}

Ansible lookup filter works only if more than one item in hashmap

I have this playbook:
- name: "This works"
hosts: localhost
tasks:
- debug:
msg: "{{ lookup('dict', foo) | map(attribute='key') | list}}"
vars:
foo:
bar:
type: v1
baz:
type: v2
- name: "This does not work"
hosts: localhost
tasks:
- debug:
msg: "{{ lookup('dict', foo) | map(attribute='key') | list}}"
vars:
foo:
bar:
type: v1
When running this, I get the following output:
PLAY [This works] *******************************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] **************************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [debug] ************************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
"bar",
"baz"
]
}
PLAY [This does not work] ***********************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] **************************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [debug] ************************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "[AnsibleUndefined, AnsibleUndefined]"
You see, it prints out bar and baz as list in the first example, but instead of a list containing just bar in the second example, I get some AnsibleUndefined output.
What do I have to change to make these filters also work with a single-item dict?
This is because lookup does not always return a list. And in your second case, if you debug, you'll see it returns one single object which is not inside a list:
{
"key": "bar",
"value": {
"type": "v1"
}
}
2 solutions to get around the problem:
instruct lookup you want a list
msg: {{ lookup('dict', foo, wantlist=true) | map(attribute='key') | list }}
use query in place of lookup which always returns a list and is better suited for this kind of processing (loops, mapping)
msg: {{ query('dict', foo) | map(attribute='key') | list }}
Reference:
https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html#ensuring-list-input-for-loop-using-query-rather-than-lookup

What is a better way to set a new list variable with cartesian products of several lists in Ansible?

I'm trying to combine data from different variables into a new list. I've used a combination of cartesian products and map/joins filters.
---
- name: Debug
hosts: localhost
gather_facts: no
vars:
uid: 'uid1'
product_types:
- product1
- product2
- product3
data_types:
- {data_name: data1, data_port: 111}
- {data_name: data2, data_port: 222}
- {data_name: data3, data_port: 333}
tasks:
- set_fact:
test_list: "{{ test_list | default([]) + ([item.data_name] | product(product_types) | map('join', '-') | product([uid]) | map('join', '-') | product([item.data_port]) | map('join', '_') | product(['pool']) | map('join', '_') | list) }}"
loop: "{{ data_types }}"
- debug: msg="{{ test_list | to_nice_yaml }}"
The new list shows the correct output but I would like to know if there was a better way for me to assemble the list/data.
$ ansible-playbook playbooks/test.yaml
Vault password:
PLAY [Debug] ************************************************************************************************************************************************************
TASK [debug] ************************************************************************************************************************************************************
ok: [localhost] => (item={'data_name': 'data1', 'data_port': 111}) => {}
$ ansible-playbook playbooks/test.yaml
PLAY [Debug] ************************************************************************************************************************************************************
TASK [set_fact] *********************************************************************************************************************************************************
ok: [localhost] => (item={'data_name': 'data1', 'data_port': 111})
ok: [localhost] => (item={'data_name': 'data2', 'data_port': 222})
ok: [localhost] => (item={'data_name': 'data3', 'data_port': 333})
TASK [debug] ************************************************************************************************************************************************************
ok: [localhost] => {}
MSG:
- data1-product1-uid1_111_pool
- data1-product2-uid1_111_pool
- data1-product3-uid1_111_pool
- data2-product1-uid1_222_pool
- data2-product2-uid1_222_pool
- data2-product3-uid1_222_pool
- data3-product1-uid1_333_pool
- data3-product2-uid1_333_pool
- data3-product3-uid1_333_pool
PLAY RECAP **************************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Q: "A better way for me to assemble the list/data?"
A: No. It's good. Formatting might improve readability
- set_fact:
test_list: "{{ test_list|default([]) +
[item.data_name]|
product(product_types)|map('join', '-')|
product([uid])|map('join', '-')|
product([item.data_port])|map('join', '_')|
product(['pool'])|map('join', '_')|
list }}"
loop: "{{ data_types }}"

How to concatenate with a string each element of a list in ansible

I've got a list of string element in a ansible var. I'm looking how to append to each element of the list with a defined string.
Do you know how I can do? I didn't find a way to do so.
Input:
[ "a", "b", "c" ]
Output:
[ "a-Z", "b-Z", "c-Z" ]
I really didn't like using the add-on filters or the loop. However, I stumbled across this blog post https://www.itix.fr/blog/ansible-add-prefix-suffix-to-list/ that used a different method that worked in Ansible 2.9.x.
- set_fact:
output: "{{ list_to_suffix | product(['-Z']) | map('join') | list }}"
You can use join for this. Please see the code below:
playbook -->
---
- hosts: localhost
vars:
input: [ "a", "b", "c" ]
tasks:
- name: debug
set_fact:
output: "{{ output | default([]) + ['-'.join((item,'Z'))] }}"
loop: "{{ input | list}}"
- debug:
var: output
output -->
PLAY [localhost] ********************************************************************************************************
TASK [Gathering Facts] **************************************************************************************************
ok: [localhost]
TASK [debug] ************************************************************************************************************
ok: [localhost] => (item=a)
ok: [localhost] => (item=b)
ok: [localhost] => (item=c)
TASK [debug] ************************************************************************************************************
ok: [localhost] => {
"output": [
"a-Z",
"b-Z",
"c-Z"
]
}
PLAY RECAP **************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0
With simple filters
shell> cat filter_plugins/string_filters.py
def string_prefix(prefix, s):
return prefix + s
def string_postfix(postfix, s):
return s + postfix
class FilterModule(object):
''' Ansible filters. Python string operations.'''
def filters(self):
return {
'string_prefix' : string_prefix,
'string_postfix' : string_postfix
}
the tasks below
- set_fact:
output: "{{ input|map('string_prefix', '-Z')|list }}"
- debug:
var: output
give
"output": [
"a-Z",
"b-Z",
"c-Z"
]
The same output gives the loop below
- set_fact:
output: "{{ output|default([]) + [item + '-Z'] }}"
loop: "{{ input }}"
- debug:
var: output
Below is how both prefix and suffix can be done in one line
- debug:
var: result
vars:
prefix: foo1
suffix: foo2
a_list: [ "bar", "bat", "baz" ]
result: "{{ [prefix] | product(a_list) | map('join') | list | product([suffix]) | map('join') | list }}"
Yet another solution, for pre-fixing and post-fixing, without custom filters (which make a very elegant code, by the way):
- set_fact:
input: ['a', 'b', 'c']
suffix: '-Z'
prefix: 'A-'
- debug:
var: suffixed
vars:
suffixed: "{{ input | zip_longest([], fillvalue=suffix) | map('join') | list }}"
- debug:
var: prefixed
vars:
prefixed: "{{ [] | zip_longest(input, fillvalue=prefix) | map('join') | list }}"
"suffixed": [
"a-Z",
"b-Z",
"c-Z"
]
"prefixed": [
"A-a",
"A-b",
"A-c"
]
A lot of the other answers felt a bit cumbersome when I wanted to both append and prepend data, so I ended up using regex_replace with map instead, which simplifies things quite considerably in my opinion:
- name: Create some data to play with
set_fact:
domains:
- example-one
- example-two
- example-three
tld: com
- name: Demonstrate the method
debug:
msg: >-
{{
domains
| map('regex_replace', '^(.*)$', 'www.\1.' + tld)
}}
Outputs:
TASK [example : Create some data to play with] ****************************
ok: [server]
TASK [example : Demonstrate the method] ***********************************
ok: [server] => {
"msg": [
"www.example-one.com",
"www.example-two.com",
"www.example-three.com"
]
}
In writing this answer, I actually found this documented in the Ansible docs, so it appears this is a recommended method too.

How to conditionally replace text in a list with regex_replace?

I have a variable in my playbook that's derived from a list. In some instances this variable contains a "-" to separate two values. For example,
Numbers:
- 2211
- 2211-2212
When this is the case I would like to replace the "-" with a "_" based on a conditional: If the number is 4 characters long, do this. Else, replace the "-" with a " _ " and do that.
I've already tried to fiddle around with jinja2 ans regex in my playbooks but so far no luck. Here's what I tried,
number: {% if length(item) == 4 %} {{ item | regex_replace("^(.*)$", "Number_\1") | string }} {% else %} {{ item | regex_replace("^(.*)$", "Number_\1") |replace("-", "_") | string }}
The result that I would like to have,
Number is four characters long:
number: Number_2211
Number is more then 4 characters long:
number: Number_2211_2212
Some of the Error messages I have received are,
ERROR! Syntax Error while loading YAML.
did not find expected key
ERROR! Syntax Error while loading YAML.
found character that cannot start any token
Is there a way to achieve this within the Ansible playbook?
Thanks in advance!
It's not really clear how you're trying to use this data. Ansible isn't great at modifying complex data structures in place, but it has lots of way of transforming data when you access it. So for example, this playbook:
---
- hosts: localhost
gather_facts: false
vars:
numbers:
- "2211"
- "2211-2212"
tasks:
- debug:
msg: "number: {{ item.replace('-', '_') }}"
loop: "{{ numbers }}"
Will output:
TASK [debug] **********************************************************************************
ok: [localhost] => (item=2211) => {
"msg": "number: 2211"
}
ok: [localhost] => (item=2211-2212) => {
"msg": "number: 2211_2212"
}
If you really need to make the transformation conditional on the length (and it's not clear that you do), you could do something like:
- debug:
msg: "{{ item.replace('-', '_') if item|length > 4 else item }}"
loop: "{{ numbers }}"
Update
I see you've selected the other answer. The solution presented here seems substantially simpler (there is no "incomprehensible sequence of filters, regex expressions, and equality checks"), and produces almost identical output:
TASK [debug] **********************************************************************************
ok: [localhost] => (item=445533) => {
"msg": "445533"
}
ok: [localhost] => (item=112234-538) => {
"msg": "112234_538"
}
ok: [localhost] => (item=11) => {
"msg": "11"
}
ok: [localhost] => (item=1111) => {
"msg": "1111"
}
ok: [localhost] => (item=1111-1111) => {
"msg": "1111_1111"
}
ok: [localhost] => (item=11-11) => {
"msg": "11_11"
}
It's not clear, given 11-11, whether you expect 11_11 or 11-11 as output. If you expect the former, this answer is more correct.
You can use an incomprehensible sequence of filters, regex expressions, and equality checks to do this.
#!/usr/bin/env ansible-playbook
- name: Lets munge some data
hosts: localhost
gather_facts: false
become: false
vars:
array:
- 445533
- 112234-538
- 11
- 1111
- 1111-1111
- 11-11
tasks:
- name: Replace hypens when starting with 4 numbers
debug:
msg: "{{ ((item | string)[0:4] | regex_search('[0-9]{4}') | string != 'None')
| ternary((item | regex_replace('-', '_')), item) }}"
loop: "{{ array }}"
PLAY [Lets munge some data] *****************************************************************************************************************************************************************************************************
TASK [Replace hypens when starting with 4 numbers] ******************************************************************************************************************************************************************************
ok: [localhost] => (item=445533) => {
"msg": "445533"
}
ok: [localhost] => (item=112234-538) => {
"msg": "112234_538"
}
ok: [localhost] => (item=11) => {
"msg": "11"
}
ok: [localhost] => (item=1111) => {
"msg": "1111"
}
ok: [localhost] => (item=1111-1111) => {
"msg": "1111_1111"
}
ok: [localhost] => (item=11-11) => {
"msg": "11-11"
}
PLAY RECAP **********************************************************************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0

Resources