copy list of set_fact output in csv column - ansible - ansible

i got the output with set_fact as below
"my_var": [
[
" iDRAC.Embedded.1",
" NIC.Integrated.1-1-1",
" NIC.Integrated.1-2-1",
" NIC.Integrated.1-3-1",
" NIC.Integrated.1-4-1",
" NIC.Slot.4-1-1",
" NIC.Slot.4-2-1",
" NIC.Slot.7-1-1",
" NIC.Slot.7-2-1",
" NIC.Slot.1-1-1",
" NIC.Slot.1-2-1",
" NIC.Slot.2-1-1",
" NIC.Slot.2-2-1"
],
[
" ABC",
" Not Supported",
" No Link",
" No Link",
" No Link",
" XYZ",
" XYZ",
" XYZ",
" XYZ",
" XYZ",
" XYZ",
" XYZ",
" XYZ"
]
]
}
now, i want this to be updated in csv file in column. for Example iDRAC.Embedded.1 in first column and ABC in next column. so on and so forth

For example
- copy:
dest: my_var.csv
content: |-
{% for line in csv %}
{{ line }}
{% endfor %}
vars:
csv: "{{ my_var.0|map('trim')|
zip(my_var.1|map('trim'))|
map('join', ',')|
list }}"
gives
shell> cat my_var.csv
iDRAC.Embedded.1,ABC
NIC.Integrated.1-1-1,Not Supported
NIC.Integrated.1-2-1,No Link
NIC.Integrated.1-3-1,No Link
NIC.Integrated.1-4-1,No Link
NIC.Slot.4-1-1,XYZ
NIC.Slot.4-2-1,XYZ
NIC.Slot.7-1-1,XYZ
NIC.Slot.7-2-1,XYZ
NIC.Slot.1-1-1,XYZ
NIC.Slot.1-2-1,XYZ
NIC.Slot.2-1-1,XYZ
NIC.Slot.2-2-1,XYZ

Related

Ansible: convert content of list into list of dicts with data manipulation

community, I'm trying to convert the list to the desired data structure, so I could work with it later:
output of the previous task:
ok: [ubuntu_vm] => {
"msg": [
"[[machine]]",
" name = \"foo\"",
" year = \"bar\"",
"[[machine]]",
" name = \"notfoo\"",
" year = \"notbar\"",
"[[machine]]",
" name = \"smth\"",
" year = \"else\"",
]
}
How can I achieve the desired structure as list of dictionaries, so that I could iterate through it using selectattr() filter?
[
- {"name": "foo", "year": "bar"}
- {"name": "notfoo", "year": "notbar" }
- ...
]
I've started with smth like this, but it produces lots of garbage as well:
{{ name_exists.stdout_lines | string | split('[[machine]]') | list }}
And after that I believe that I should apply smth like:
{{ existing_runners + [ { item.split(',')[1]: item.split(',')[2] } ] }}
Given the data is a list of strings
data:
- '[[machine]]'
- ' name = "foo"'
- ' year = "bar"'
- '[[machine]]'
- ' name = "notfoo"'
- ' year = "notbar"'
- '[[machine]]'
- ' name = "smth"'
- ' year = "else"'
Create lists of names and years
names: "{{ data|select('match', '^.*name\\s*=.*$')|
map('split', '=')|map('last')|map('trim')|
list }}"
years: "{{ data|select('match', '^.*year\\s*=.*$')|
map('split', '=')|map('last')|map('trim')|
list }}"
gives
names: ['"foo"', '"notfoo"', '"smth"']
years: ['"bar"', '"notbar"', '"else"']
Then strip the double-quotes and create a YAML dictionary
regex: '^"*(.*?)"*$'
replace: '\1'
names_strip: "{{ names|map('regex_replace', regex, replace)|list }}"
years_strip: "{{ years|map('regex_replace', regex, replace)|list }}"
content_dict: "{{ dict(names_strip|zip(years_strip)) }}"
gives
content_dict:
foo: bar
notfoo: notbar
smth: else
Next, you can convert it to a list of dictionaries
content: "{{ content_dict|dict2items(key_name='name',
value_name='year') }}"
gives the desired structure
content:
- {name: foo, year: bar}
- {name: notfoo, year: notbar}
- {name: smth, year: else}
Create the list without the intermediary dictionary if the names are not unique. The declaration below will create the desired structure too
content: "{{ names_strip|zip(years_strip)|
map('zip', ['name', 'year'])|
map('map', 'reverse')|
map('community.general.dict')|
list }}"
Q: "How can I transform it, so that it can be used content | selectattr(...)?"
A: No transformation is needed. For example,
content_foo: "{{ content|selectattr('name', 'search', 'foo') }}"
gives as expected
content_foo:
- {name: foo, year: bar}
- {name: notfoo, year: notbar}
Notes
Example of a complete playbook
- hosts: localhost
vars:
data: [
"[[machine]]",
" name = \"foo\"",
" year = \"bar\"",
"[[machine]]",
" name = \"notfoo\"",
" year = \"notbar\"",
"[[machine]]",
" name = \"smth\"",
" year = \"else\"",
]
names: "{{ data|select('match', '^.*name\\s*=.*$')|
map('split', '=')|map('last')|map('trim')|
list }}"
years: "{{ data|select('match', '^.*year\\s*=.*$')|
map('split', '=')|map('last')|map('trim')|
list }}"
regex: '^"*(.*?)"*$'
replace: '\1'
names_strip: "{{ names|map('regex_replace', regex, replace)|list }}"
years_strip: "{{ years|map('regex_replace', regex, replace)|list }}"
content_dict: "{{ dict(names_strip|zip(years_strip)) }}"
content: "{{ content_dict|dict2items(key_name='name',
value_name='year') }}"
tasks:
- debug:
var: content
The code will be much simpler if the blocks are unique. For example,
data:
- '[[machine1]]'
- ' name = "foo"'
- ' year = "bar"'
- '[[machine2]]'
- ' name = "notfoo"'
- ' year = "notbar"'
- '[[machine3]]'
- ' name = "smth"'
- ' year = "else"'
It will be possible to parse the INI data
machines: "{{ data|join('\n')|community.general.jc('ini') }}"
gives
machines:
'[machine1': {name: foo, year: bar}
'[machine2': {name: notfoo, year: notbar}
'[machine3': {name: smth, year: else}
and create the desired structure
content: "{{ machines.values()|list }}"

padding/ljust with ansible template and jinja

I am looking for a way to create a template in ansible with this dictionary.
data= {
"_dictionary": {
"keyone": "abc",
"rtv 4": "data2",
"longtexthere": "1",
"keythree": "data3",
"keyfour": "data1234",
}
}
The template output should have this format:
keyone abc
keytwo data2
longtexthere 1
keythree data3
keyfour data1234
With python I can create it with:
w = max([len(x) for x in data['_dictionary'].keys()])
for k,v in data['_dictionary'].items():
print(' ', k.ljust(w), ' ', v)
But I can't a way to create it in a jinja2 template in ansible. I have not found a replacement for ljust.
Currently my template is this, but I got a output without format.
{% for key, value in data['_dictionary'].items() %}
{{ "%s\t%s" | format( key, value ) }}
{% endfor %}
Any ideas, sugestion?
For example
- debug:
msg: |
{% for k,v in data['_dictionary'].items() %}
{{ "{:<15} {}".format(k, v) }}
{% endfor %}
gives
msg: |-
keyone abc
rtv 4 data2
longtexthere 1
keythree data3
keyfour data1234
See format and Format String Syntax.
Q: "Create the format dynamicaly."
A: For example, find the longest key in the dictionary. Add 1 more space to the length of the first column. In the same way, calculate the length of the second column and create the format string in a separate variable
- debug:
msg: |
{% for k,v in data['_dictionary'].items() %}
{{ fmt.format(k, v) }} # comment
{% endfor %}
vars:
col1: "{{ data._dictionary.keys()|map('length')|max + 1 }}"
col2: "{{ data._dictionary.values()|map('length')|max + 1 }}"
fmt: "{:<{{ col1 }}} {:<{{ col2 }}}"
gives
msg: |-
keyone abc # comment
rtv 4 data2 # comment
longtexthere 1 # comment
keythree data3 # comment
keyfour data1234 # comment
Is working, at the end my j2 file is:
{% set col1 = data._dictionary.keys()|map('length')|max %}
{% set fmt = "{:<" + col1|string + "} {}" %}
{% for key, value in data._dictionary.items() %}
{{ fmt.format(key, value) }}
{% endfor %}
Thank you.

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') }}

Ansible search string in stdout_lines and user later as variable

I Have below ansible output in stdout_lines
"test.stdout_lines": [
"Get VM Power State Script",
"Loading vmware.vimautomation.core",
"",
"Attempting to connect to server1. Please wait.",
"",
"Connected to server1",
"\tGathering VM Info...",
"",
"Attempting to connect to serevr2. Please wait.",
"",
"Connected to server2.",
"\tGathering VM Info...",
"File Exported to D:\\Scripts\\Exports\\VM_State_201907151824.csv . Please verify the content",
"",
"Complete."
I want to store the file location in last line of the output as variable,i.e., location:- D:\Scripts\Exports\VM_State_201907151824.csv. How can it be done?
Does this work for you?
{{ test.stdout_lines | join(' ') | regex_replace( '^.*File Exported to (.*) \. Please .*$', '\\1') }}
Try below
- name: Get the file_path
set_fact:
file_path: "{{ test.stdout_lines | select('match', 'File Exported to.+') | list | regex_replace( '^.*File Exported to (.*) \\. Please .*$', '\\1') }}"
- name: debug
debug:
msg: "{{ file_path }}"

ansible expression to use with with_item

I have a variable, more specifically it's the result (registered in a variable with register:) of a set_fact task using with_item, let's named it myvar, and to simply, I only display the relevant part of it.
{
"msg": "All items completed",
"results": [
{
"item": "item1",
"stdout_lines": [
[
"result line 1",
"result line 2"
]
]
},
{
"item": "item2",
"stdout_lines": [
[
"result line 1"
]
]
}
]
}
I need to run a task for each "result line X" that will include in the command the value of "item" and the "result line X" value
I'm not sure how to do this.
if I do
- debug:
msg: "{{ item.item }} , {{ item | json_query('stdout_lines[0][*]') | list }}"
with_items: "{{ myvar | json_query('results[*]') }}"
The following would output
item1 ; [u'result line 1',u'result line 2']
item2 ; [u'result line 1']
I have both all stdout_lines entries on the same line. so the split of the stdout_lines should happen in the with_item expression
But here I don't find how I could "fetch" all the stdout_lines of all results and at the same time keep the "item" value.
If you'd have flat stdout_lines, you could just do with_subelements.
In your case, you need to spice it with JMESPath a bit:
---
- hosts: localhost
gather_facts: no
vars:
myvar:
"results": [
{
"item": "item1",
"stdout_lines": [
[
"result line 1",
"result line 2"
]
]
},
{
"item": "item2",
"stdout_lines": [
[
"result line 1"
]
]
}
]
tasks:
- command: echo {{ item.0.name }} {{ item.1 }}
with_subelements:
- "{{ myvar.results | json_query('[].{name:item,lines:stdout_lines[]}') }}"
- lines

Resources