ansible check max number list - filter

I am having issues with script in Ansible. I want to check the last sequence number the route-map in Cisco NXOS and I did the next:
My idea was to show the output and then, with map('regex_replace") remove all except the numbers.
Inside the role:
vars file:
sequence: 10
tasks file:
---
- name: check route-map
nxos_command:
commands: sh route-map |inc TEST-MAP
register: output
- name: Calculate max number
set_fact:
max_seq_number: |
{{ output.stdout_lines[0] | map ('regex_replace','route-map TEST-MAP, permit, sequence','') | list |max}}
- name: Calculate number to use
set_fact:
sum: "{{ (max_seq_number |int) + (sequence |int)}}"
That it works but if the number of prefixes is more than 90 it fails.
This is the result:
"stdout_lines": [
[
"route-map TEST-MAP, permit, sequence 1 ",
"route-map TEST-MAP, permit, sequence 10 ",
"route-map TEST-MAP, permit, sequence 20 ",
"route-map TEST-MAP, permit, sequence 30 ",
"route-map TEST-MAP, permit, sequence 40 ",
"route-map TEST-MAP, permit, sequence 50 ",
"route-map TEST-MAP, permit, sequence 60 ",
"route-map TEST-MAP, permit, sequence 70 ",
"route-map TEST-MAP, permit, sequence 80 ",
"route-map TEST-MAP, permit, sequence 90 ",
"route-map TEST-MAP, permit, sequence 100 ",
"route-map TEST-MAP, permit, sequence 110 ",
"route-map TEST-MAP, permit, sequence 120 ",
"route-map TEST-MAP, permit, sequence 130"
]
]
}
TASK [checks : Calculate max number] **********************************************************************************************************************************************************************
task path: /ansible/Config-Generator/roles/checks/tasks/Route-map-SequenceNumber.yml:6
ok: [localhost] => {
"ansible_facts": {
"**max_seq_number": " 90 \n"**
},
"changed": false
}
TASK [checks : Calculate number to use] *******************************************************************************************************************************************************************
task path: /ansible/Config-Generator/roles/checks/tasks/Route-map-SequenceNumber.yml:10
ok: [localhost] => {
"ansible_facts": {
"sum": "100"
},
"changed": false
}
Does anyone know why the Max number is 90 instead of 130?
If I use numbers until 90 it works perfectly, but more than 100 it does not work. Any idea?
Thanks

You are comparing your values as string so the result is totally expected. "90" (starts with 9) is further in the alphabet than "130" (starts with 1).
You need to compare as int to get the greater int value.
The following shows the differences with the solution:
---
- hosts: localhost
gather_facts: false
vars:
# simple one-liner with your original data for demo
output: {"stdout_lines": [["route-map TEST-MAP, permit, sequence 1 ", "route-map TEST-MAP, permit, sequence 10 ", "route-map TEST-MAP, permit, sequence 20 ", "route-map TEST-MAP, permit, sequence 30 ", "route-map TEST-MAP, permit, sequence 40 ", "route-map TEST-MAP, permit, sequence 50 ", "route-map TEST-MAP, permit, sequence 60 ", "route-map TEST-MAP, permit, sequence 70 ", "route-map TEST-MAP, permit, sequence 80 ", "route-map TEST-MAP, permit, sequence 90 ", "route-map TEST-MAP, permit, sequence 100 ", "route-map TEST-MAP, permit, sequence 110 ", "route-map TEST-MAP, permit, sequence 120 ", "route-map TEST-MAP, permit, sequence 130"]]}
tasks:
- name: show max string value
debug:
msg: >-
{{
output.stdout_lines[0]
| map ('regex_replace','route-map TEST-MAP, permit, sequence (\d+).*','\1')
| max
}}
- name: show max int value
debug:
msg: >-
{{
output.stdout_lines[0]
| map ('regex_replace','route-map TEST-MAP, permit, sequence (\d+).*','\1')
| map('int')
| max
}}
which gives:
PLAY [localhost] ***********************************************************************************************************************************************************************************************************************
TASK [show max string value] ***********************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "90"
}
TASK [show max int value] **************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "130"
}
PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Related

Create dictionary from list of strings using delimiter with Ansible

I have a nested list of strings, which I am trying to convert into a dictionary. The list seems to be in a reasonable format, but my dictionary is getting overwritten each time I append to it.
Initial list:
TASK [test first_list] **********************************************************************************************************************************************************************************************************************************************************************************************************
ok: [node11] => {
"first_list": [
[
"DEVNAME: /dev/sanstorage/node11-data103-2fd31b74e1ef5a8006c9ce9000b10399f",
"UUID: 2751719f-d1a2-49b0-9e42-3d686c90d2e6",
"TYPE: xfs"
],
[
"DEVNAME: /dev/sanstorage/node11-data104-2e81f1279171dce656c9ce9000b10399f",
"UUID: 6a6265d6-b103-471e-9e25-e8cc5f5585a8",
"TYPE: xfs"
],
[
"DEVNAME: /dev/sanstorage/node11-data102-2e385a327788d866e6c9ce9000b10399f",
"UUID: 8c52d974-8584-4aa6-89b8-f1e1db016118",
"TYPE: xfs"
],
[
"DEVNAME: /dev/sanstorage/node11-data101-241afe5ab93b5c0ee6c9ce9000b10399f",
"UUID: 94b56164-6717-4b82-8f11-86fd94a39672",
"TYPE: xfs"
],
[
"DEVNAME: /dev/sanstorage/node11-data100-25626d38d4c4239456c9ce9000b10399f",
"UUID: 55a1a388-fe0a-4dd0-980a-a10c5317952e",
"TYPE: xfs"
],
[
"DEVNAME: /dev/sanstorage/node11-data114-231d2661b7ab88f8f6c9ce9000b10399f",
"UUID: f87ad708-1d12-41a5-9441-d32a97b5318c",
"TYPE: ext4"
],
[
"DEVNAME: /dev/sanstorage/node11-data115-2d3a824975e90550f6c9ce9000b10399f",
"UUID: b8b79886-9710-4205-900b-7b9d7d4ad933",
"TYPE: ext4"
],
[
"DEVNAME: /dev/sanstorage/oneview-FA-data8165-284392eae1ad17d846c9ce9000b10399f",
"UUID: c5a43057-676e-49b7-b2a9-b53a40f7010b",
"TYPE: xfs"
],
[
"DEVNAME: /dev/sanstorage/oneview-FA-data3420-2e262703236f9b7046c9ce9000b10399f",
"UUID: 1a70c187-9364-4f48-92f8-f9b9dec9824f",
"TYPE: xfs"
],
[
"DEVNAME: /dev/sanstorage/node11-data112-2464954fd324508e66c9ce9000b10399f",
"UUID: 2238a12e-2ca1-466e-8617-11051e1d612e",
"TYPE: ext4"
],
[
"DEVNAME: /dev/sanstorage/node11-data111-25bb14827531149456c9ce9000b10399f",
"UUID: db37479f-80b0-46b2-85e0-aef679bea164",
"TYPE: ext4"
],
[
"DEVNAME: /dev/sanstorage/node11-data105-28ac6825aa80520d46c9ce9000b10399f",
"UUID: e987fc7c-9a4f-46ea-91f0-4d5f37d3421e",
"TYPE: xfs"
],
[
"DEVNAME: /dev/sanstorage/node11-data113-2177c50c4dd5063506c9ce9000b10399f",
"UUID: 36c1194d-ac4f-4cee-8688-353974cb6be0",
"TYPE: ext4"
]
]
}
From here, I try to form a dictionary, but only the last list entry is stored at the end:
TASK [test final_list] **********************************************************************************************************************************************************************************************************************************************************************************************************
ok: [node11] => {
"final_list": {
"DEVNAME": "/dev/sanstorage/node11-data113-2177c50c4dd5063506c9ce9000b10399f",
"TYPE": "ext4",
"UUID": "36c1194d-ac4f-4cee-8688-353974cb6be0"
}
}
Ideally, it would look something like this, I'm guessing I need to nest the dictionary within a list?
"final list": [
{
"DEVNAME: /dev/sanstorage/node11-data103-2fd31b74e1ef5a8006c9ce9000b10399f",
"UUID: 2751719f-d1a2-49b0-9e42-3d686c90d2e6",
"TYPE: xfs"
},
{
"DEVNAME: /dev/sanstorage/node11-data104-2e81f1279171dce656c9ce9000b10399f",
"UUID: 6a6265d6-b103-471e-9e25-e8cc5f5585a8",
"TYPE: xfs"
},
{
"DEVNAME: /dev/sanstorage/node11-data102-2e385a327788d866e6c9ce9000b10399f",
"UUID: 8c52d974-8584-4aa6-89b8-f1e1db016118",
"TYPE: xfs"
},
... snip ...
Below is the important part of the playbook:
- set_fact:
first_list: "{{ first_list | default([]) + [disk_data.split('\n')] }}"
vars:
disk_data: '{{ item.stdout }}'
with_items: "{{ disk_check.results }}"
- name: test first_list
debug:
var: first_list
- set_fact: final_list:{}
- set_fact:
final_list: "{{ final_list | default([]) | combine(dict([ item.partition(':')[::2]|map('trim')])) }}"
with_items: "{{ first_list }}"
- name: test final_list
debug:
var: final_list
Thoughts?
TL;DR
- debug:
msg: "{{ disk_check.results | map(attribute='stdout') | map('from_yaml') | list }}"
You did not provide the initial task which returns disk_check. From your usage in your example, I'll take for granted it is a shell or command task used in a loop.
Meanwhile you still provided enough information so we can avoid falling into an x/y problem. Your question is basically "how can I split a string on delimiters and use the result to create a dict?" where what you really want is more "How can I parse a string representing a yaml dict into an actual dict?"
To start with
- set_fact:
first_list: "{{ first_list | default([]) + [disk_data.split('\n')] }}"
vars:
disk_data: '{{ item.stdout }}'
with_items: "{{ disk_check.results }}"
This is going through all your command results, spliting stdout on a new line and adding that list to a top level list.
Since the output of shell/command contains a stdout_lines attribute and that we can extract attributes from a list of dicts with the jinja2 map filter, this could be replaced in one go without having to run a task at all with the following jinja expression:
"{{ disk_check.results | map(attribute='stdout_lines') | list }}"
But we would still be walking the wrong path.
Let's have a look at one of your (reconstructed) individual stdout from results
"stdout": "DEVNAME: /dev/sanstorage/node11-data102-2e385a327788d866e6c9ce9000b10399f\nUUID: 8c52d974-8584-4aa6-89b8-f1e1db016118\nTYPE: xfs"
This is a string representation of a yaml dict. Ansible has a from_yaml filter. And we can use the map filter to apply a filter to each element in a list.
The below playbook tries to reproduce your original data to display it in one go
---
- name: Parse a yaml dict in each element of a list
hosts: localhost
gather_facts: false
vars:
example_disks: 3
tasks:
- name: Faking your disk check supposed command
shell: |-
cat <<EOF
DEVNAME: /dev/sanstorage/node{{ (item | string)*2 }}-data{{ (item | string)*3 }}-$(uuidgen)
UUID: $(uuidgen)
TYPE: xfs
EOF
loop: "{{ range(1, example_disks+1) }}"
register: disk_check
- name: Show the original data (use -v to trigger)
debug:
var: disk_check
verbosity: 1
- name: Display a list of dicts from the above result
debug:
msg: "{{ disk_check.results | map(attribute='stdout') | map('from_yaml') | list }}"
and gives (run with -v to show the intermediate debug):
PLAY [Parse a yaml dict in each element of a list] *************************************************************************************************************************************************************************************
TASK [Faking your disk check supposed command] *****************************************************************************************************************************************************************************************
changed: [localhost] => (item=1)
changed: [localhost] => (item=2)
changed: [localhost] => (item=3)
TASK [Show the original data (use -v to trigger)] **********************************************************************************************************************************************************************************************************
skipping: [localhost]
TASK [Display a list of dicts from the above result] ***********************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
{
"DEVNAME": "/dev/sanstorage/node11-data111-10e7a318-62af-46db-895e-5d94b0c2cf88",
"TYPE": "xfs",
"UUID": "5a87ae1c-c312-4325-88ac-b9cc1edfa69b"
},
{
"DEVNAME": "/dev/sanstorage/node22-data222-18247d1d-87b2-4c7d-8d48-cd333d7530f9",
"TYPE": "xfs",
"UUID": "bc0d7f1a-16e4-4694-b3e2-904e69cc208d"
},
{
"DEVNAME": "/dev/sanstorage/node33-data333-21bc7fde-645f-4cf2-9e18-72d004700085",
"TYPE": "xfs",
"UUID": "58985028-3eb1-4f34-90e9-f0e4c8257607"
}
]
}
PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
Let's simplify the data, e.g.
first_list:
- - a: 1
- b: 1
- - a: 2
- b: 2
The expected result is
final_list:
- a: 1
b: 1
- a: 2
b: 2
The task below does the job
- set_fact:
final_list: "{{ final_list|default([]) + [_dict|from_yaml] }}"
loop: "{{ first_list }}"
vars:
_dict: |
{% for i in item %}
{{ (i|to_yaml)[1:-2] }}
{% endfor %}
There are other options on how to format the item, e.g.
{{ i.keys()|first }}: {{ i.values()|first }}
Write a filter if you use such conversions frequently, e.g.
shell> cat filter_plugins/list_filters.py
def list2dict(l):
out = []
for i in l:
item = {}
for j in range(0, len(i)):
item.update(i[j])
out.append(item)
return out
class FilterModule(object):
''' List filters. '''
def filters(self):
return {
'list2dict': list2dict,
}
Then the simple filter below gives the same result
- set_fact:
final_list: "{{ first_list|list2dict }}"

Difference between loop and with_items behavior with a single item and a list of items

While looking at a question here, came across another question.
Let's assume a variable with list of dicts like so:
some_var:
- k: key1
m: value1
- k: key2
m: value2
- k: key3
m: value3
Looping over some_var using below code gives each item as expected.
- debug:
msg: "{{ item }}"
loop: "{{ some_var | flatten(1) }}"
However, when placing the loop variable as a list like below, it doesn't loop through individual dict item rather prints the whole list.
- debug:
msg: "{{ item }}"
loop:
- "{{ some_var | flatten(1) }}"
What is the reason for this difference in loop behavior compare to with_items that works as expected in both scenarios.
The difference in behaviour your are observing between with_items and loop is actually due to the way with_items flatten the list for you, implicitly, which makes it happen later than when you flatten it explicitly in your loop, as advised by the Ansible documentation.
So, here the difference just stands in the moment where the list is flattened:
Using with_items, the list is flattened internally by Ansible, so it would be an equivalent to:
[ some_var ] | flatten(1)
When, in the loop documentation of Ansible, as you saw it, you have to flatten the list yourself, but by introducing a list one level upper, your flatten happen too early, and, thus, is an equivalent to:
[ some_var | flatten(1) ]
So if you are looking to exactly and explicitly reproduce the same behaviour as with_items in your loop, you have to do this:
- hosts: all
gather_facts: no
tasks:
- debug:
msg: "{{ item }}"
loop: "{{ [ some_var | flatten(1) ] | flatten(1) }}"
vars:
some_var:
- k: key1
m: value1
- k: key2
m: value2
- k: key3
m: value3
Which works, as expected:
PLAY [all] **********************************************************************************************************
TASK [debug] ********************************************************************************************************
ok: [localhost] => (item={'k': 'key1', 'm': 'value1'}) => {
"msg": {
"k": "key1",
"m": "value1"
}
}
ok: [localhost] => (item={'k': 'key2', 'm': 'value2'}) => {
"msg": {
"k": "key2",
"m": "value2"
}
}
ok: [localhost] => (item={'k': 'key3', 'm': 'value3'}) => {
"msg": {
"k": "key3",
"m": "value3"
}
}
PLAY RECAP **********************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
This is because you are creating a list of list in the second example, while the loop only loops over the first level of a list.
So your first, working playbook is an equivalent to looping on the variable debugged here under:
- hosts: all
gather_facts: no
tasks:
- debug:
msg: "{{ some_var | flatten(1) }}"
vars:
some_var:
- k: key1
m: value1
- k: key2
m: value2
- k: key3
m: value3
That gives:
PLAY [all] **********************************************************************************************************
TASK [debug] ********************************************************************************************************
ok: [localhost] => {
"msg": [
{
"k": "key1",
"m": "value1"
},
{
"k": "key2",
"m": "value2"
},
{
"k": "key3",
"m": "value3"
}
]
}
PLAY RECAP **********************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Your second example, though, is an equivalent to:
- hosts: all
gather_facts: no
tasks:
- debug:
msg:
- "{{ some_var | flatten(1) }}"
## Note that this above syntax is actually an equivalent to:
# msg: "{{ [ some_var | flatten(1) ] }}"
vars:
some_var:
- k: key1
m: value1
- k: key2
m: value2
- k: key3
m: value3
That gives:
PLAY [all] **********************************************************************************************************
TASK [debug] ********************************************************************************************************
ok: [localhost] => {
"msg": [
[
{
"k": "key1",
"m": "value1"
},
{
"k": "key2",
"m": "value2"
},
{
"k": "key3",
"m": "value3"
}
]
]
}
PLAY RECAP **********************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
See how you have a list of list in this second example?
So, the first element of the first example is
{'k': 'key1', 'm': 'value1'}
As you expect it, while the first element of the second one, is just a list containing all of your element:
[
{
"k": "key1",
"m": "value1"
},
{
"k": "key2",
"m": "value2"
},
{
"k": "key3",
"m": "value3"
}
]

Accessing AWS RDS Arn from aws rds describe-db-instances

I am using Ansible to access some RDS clusters in my account. I have the command working with this.
This is the input data.
"stdout_lines": [
"{",
" \"DBInstances\": [",
" {",
" \"PubliclyAccessible\": false, ",
" \"MasterUsername\": \"xxxxx\", ",
" \"MonitoringInterval\": 0, ",
" \"LicenseModel\": \"general-public-license\", ",
" \"VpcSecurityGroups\": [",
" {",
" \"Status\": \"active\", ",
" \"VpcSecurityGroupId\": \"xxxx\"",
" }",
" ], ",
" \"InstanceCreateTime\": \"xxxxxx\", ",
" \"CopyTagsToSnapshot\": false, ",
" \"OptionGroupMemberships\": [",
" {",
" \"Status\": \"in-sync\", ",
" \"OptionGroupName\": \"xxxxx\"",
........
" \"DBInstanceArn\": \"arn:aws:rds:region:xxxx:db:xxxx\", ",
.......
When running the command below targeting the [0[ item in the array I receive this output.
"{{ (registered.stdout_lines | from_json)['DBInstances'][0]['DBInstanceArn'] }}"
ok: [localhost] => {
"msg": "arn:aws:rds:region :xxxx:db:xxxx"
}
I can get down to the item I need by specifying it like this. I need the array ID in there as there are 3 of them. I have tried to run it this way. But I get all the data.
- name: Show
set_fact:
var: "item.db_instance_arn"
with_items: "{{rds.instances}}"
register: variable
I also get the fact that I want. I just cant output that individual fact.
Is there a way to hit all the items in the array and output the one variable I need?
Thanks in advance. Happy New Year!
You will want (registered.stdout | from_json), not (registered.stdout_lines | from_json) }}, because stdout_lines is a list of str, but from_json wants a str -- I'm surprised your question didn't include the error message that you experienced, because it for sure produces one
Then, once you have straightened out the JSON parsing, you will want to use the map filter, because as you observed, DBInstances is a list, meaning you need to effectively iterate over its objects like so:
- debug:
msg: my DB ARNs are {{
(registered.stdout | from_json).DBInstances
| map(attribute="DBInstanceArn")
| list }}
If you prefer, you can use the json_query filter which uses JMESPath syntax, although most of the time I find it more opaque than helpful:
- debug:
msg: my DB ARNs are {{ (registered.stdout | from_json)
| json_query('DBInstances[*].DBInstanceArn') }}

Fetch specific output from stdout_lines and store it in a list

I'm working on automating F5 BIG IP configuration decommission.I need to fetch the pool names from the executed arbitrary bigip command. I have stored the content of the output to a register and wanted to fetch the pool name and store it in a list.
I have achieved till obtaining the wide ip information (which contains Pool name) from the output of the executed command.
Expected output :-
Can i use any Regular expression to fetch the pool information , as far as i know i can do a dot walk to fetch the exact content from JSON . I'm pretty much sure that the output won't be same when there are multiple pool names . What would be the logic if there are multiple pools as well ?
---
- name: Playbook to verify the Wideip and fetch the GTM configuration
hosts: test-gtm.com
connection: local
gather_facts: no
tasks:
- name: Verify WideIP Exists
bigip_command:
user: admin
password: admin
commands: "list gtm wideip a wideip"
validate_certs: no
register: output
delegate_to: localhost
- debug:
var: output
- name: Fetch the Pool name from the WideIP
set_fact:
gtm_pool: "{{ output.stdout_lines[0][1].split()[0] }}"
``
Output without the dot walk :-
"stdout_lines": [
[
"gtm wideip a test.abc.com {",
" pools {",
" test-pool {",
" order 0",
" }",
" }",
"}"
]
Whole debug output :-
"output": {
"changed": false,
"deprecations": [
{
"msg": "Param 'server' is deprecated. See the module docs for more information",
"version": 2.9
},
{
"msg": "Param 'user' is deprecated. See the module docs for more information",
"version": 2.9
},
{
"msg": "Param 'password' is deprecated. See the module docs for more information",
"version": 2.9
},
{
"msg": "Param 'validate_certs' is deprecated. See the module docs for more information",
"version": 2.9
}
],
"executed_commands": [
"tmsh -c \\\"list gtm wideip a wideip\\\""
],
"failed": false,
"stdout": [
"gtm wideip a wideip {\n pools {\n test-pool {\n order 0\n }\n }\n}"
],
"stdout_lines": [
[
"gtm wideip a wideip {",
" pools {",
" test-pool {",
" order 0",
" }",
" }",
"}"
]
]
}
}
Debug output consisting of more than single pool:-
ok: [device.abc.com] => {
"output": {
"changed": false,
"deprecations": [
{
"msg": "Param 'server' is deprecated. See the module docs for more information",
"version": 2.9
},
{
"msg": "Param 'user' is deprecated. See the module docs for more information",
"version": 2.9
},
{
"msg": "Param 'password' is deprecated. See the module docs for more information",
"version": 2.9
},
{
"msg": "Param 'validate_certs' is deprecated. See the module docs for more information",
"version": 2.9
}
],
"executed_commands": [
"tmsh -c \\\"list gtm wideip a wideip\\\""
],
"failed": false,
"stdout": [
"gtm wideip a wideip {\n description wideip\n pool-lb-mode topology\n pools {\n test1-pool {\n order 1\n }\n test2-pool {\n order 0\n }\n }\n}"
],
"stdout_lines": [
[
"gtm wideip a wideip {",
" description wideip",
" pool-lb-mode topology",
" pools {",
" test1-pool {",
" order 1",
" }",
" test2-pool {",
" order 0",
" }",
" }",
"}"
]
]
Parsing that output robustly will be tricky, because it is effectively a proprietary serialization format. If there's a way to get the switch to just give you JSON that would make your life easier (I know some Cisco switches have this option).
To do this The Right Way you would want to write a parser that understood the output format. But we can cheat, and make lots of assumptions, and solve this with a couple of regular expressions.
I think that in any case we're going to need to parse this output in a tool other than Ansible. I generally just write a filter module in Python in situations like this, although sometimes "pipe to awk" also works.
For parsing this output, I hacked together the following (and dropped it into filter_plugins/bigip.py adjacent to my playbook):
import re
# Recall that "\s+" means "one or more whitespace characters"
re_pools = re.compile('''
gtm \s+ wideip \s+ a \s+ (\S+) \s+ { \s+
(?P<parameters>(\S+ \s+ \S+ \s+)*)
pools \s+ { \s+ (?P<pools>
(?:
\S+ \s+ { \s+
[^}]* \s+
} \s+
)+ \s+
)
}
''', flags=re.VERBOSE)
re_pool = re.compile('''
(\S+) \s+ { \s+ [^}]* \s+ } \s+
''', flags=re.VERBOSE)
def filter_get_pool_names(v):
combined = ' '.join(v.splitlines())
res = re_pools.match(combined)
if not res or not res.group('pools'):
pools = []
else:
pools = re_pool.findall(res.group('pools'))
return pools
class FilterModule(object):
filter_map = {
'get_pool_names': filter_get_pool_names,
}
def filters(self):
return self.filter_map
The above defines a get_pool_names filter. If I use it in a playbook like this:
---
- hosts: localhost
gather_facts: false
vars:
output:
stdout: ["gtm wideip a test.abc.com {\n pools {\n test-pool1 {\n order 0\n }\n test-pool2 {\n order 1\n }\n test-pool3 {\n order 2\n }\n }\n}"]
tasks:
- debug:
msg: "{{ output.stdout.0 | get_pool_names }}"
I get the result:
TASK [debug] **********************************************************************************
ok: [localhost] => {
"msg": [
"test-pool1",
"test-pool2",
"test-pool3"
]
}
Note that I've made assumptions about the format of the output here, and there's really not much in the way of error checking in the filter. But I think it demonstrates one way of tackling this problem.
Update
Regarding:
while i was trying to pass these pool names to the consecutive playbook i could see it is passing this as "[u\'test1-pool']" . But i just need "test1-pool" . Any suggestions ?
The result of the filter is a list of names. You are trying to treat it as a string. You need to either loop over the result, like this:
- set_fact:
pool_names: "{{ output.stdout.0 | get_pool_names }}"
- debug:
msg: "processing pool: {{ item }}"
loop: "{{ pool_names }}"
Or refer to individual elements in the list:
- debug:
msg: "the first pool is {{ pool_names.0 }}"
The content in the question is a series of lines, and it's not even JSON so you can't simply deserialize it. Valid JSON would be
{ "gtm wideip a test.abc.com": { "pools": { "test-pool": { "order": 0 }}}}
As an example, if such data is available, the play below
vars:
my_stdout_lines:
gtm wideip a test.abc.com:
pools:
test-pool:
order: 0
test-pool1:
order: 0
test-pool2:
order: 0
tasks:
- set_fact:
my_list: "{{ my_stdout_lines|json_query('*.pools.keys(#)') }}"
- debug:
var: my_list
gives (abridged):
"my_list": [
[
"test-pool2",
"test-pool",
"test-pool1"
]
]

Ansible parse stdout_lines to verify values of a particular item

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

Resources