I'm trying to do a simple list to show all ports associated with a particular vlan and have this output to a csv.
Code works but can't figure out how can I get around removing the 'u' and the [] associated with the line 'InterfaceID' in the output. I know the array need to be converted to a string just wondering how I can do this without modifying the code too much.
Thank you in advance for any tips.
Code:
- name: parse output
set_fact:
vlan_output: "{{vlan_info.stdout[0] | parse_cli_textfsm(parse_template)}}"
- name: write lines to file
copy:
content: "{{ ['InterfaceID','VlanID','NAME'] | zip([item.INTERFACES,item.VLAN_ID,item.NAME]) | map('join', ', ') | join('\n') }}"
dest: "output.csv"
with_items: "{{vlan_output}}"
- debug:
var: vlan_output
Debug of vlan_output:
"vlan_output": [
{
"INTERFACES": [
"Gi1/0/2",
"Gi1/0/5",
"Gi1/0/7"
],
"NAME": "test",
"STATUS": "active",
"VLAN_ID": "10"
}
]
Excel Output:
InterfaceID, [u'Gi1/0/2', u'Gi1/0/5', u'Gi1/0/7']
VlanID, 10
NAME, test
While you cannot directly do that, once you upgrade version of python used by ansible to 3.x you get rid of these. Only Python 2.x does print unicode strings with u prefix. On 3 all are unicode anyway and the prefix is not printed anymore.
Keep in mind that the u is part of printing the data, is not part of the data itself.
If you try to parse as string an array that was converted to a string you are already doing something wrong.
Since you have a list of Interfaces, you can use the join filter to "join" the list items into a string. From the question its not clear if you'd like to join them with space or comma, but a small change, such as - item.INTERFACES|join(', ') will give (comma separated) - Gi1/0/2, Gi1/0/5, Gi1/0/7.
Example:
- name: write lines to file
copy:
dest: output.csv
content: "{{ ['InterfaceID','VlanID','NAME'] | zip([item.INTERFACES|join(', '),item.VLAN_ID,item.NAME]) | map('join', ', ') | join('\n') }}"
with_items: "{{ vlan_output }}"
Produces:
InterfaceID, Gi1/0/2, Gi1/0/5, Gi1/0/7
VlanID, 10
NAME, test
Related
My ansible negative regex is returning everything
So I'm getting results of dns names from JSON, of which I'm trying to sort to create an automated ansible inventory.
I have servers with naming schemes such as:
abc02vsomeserver01.subdomain1.domain.gov
abc02someserver01.subdomain1.domain.gov
xyz03votherserver11.subdomain2.domain.gov
wyz03otherserver11.subdomain2.domain.gov
qrsmainserver02.maindomain.domain.gov
I'm getting the "v" servers divided out by environment, then I want to create a catchall group that is not the "v" servers
So! I'm attempting to do:
{{ jsonOutput | json_query('json.response.results[].dnsName') | regex_findall('(?![a-z]{3}[0-9]{2}v)^.*', multiline=true) }}
Which does seem to work when I plug it into https://pythex.org.
But ansible is returning everything instead...
What the heck am I doing wrong here?
It's because that json_query emits a list[str] which when fed directly into regex_findall doesn't become a newline delimited string, it becomes the same as str(["alpha", "beta"]) (e.g. ['alpha', 'beta']) and then the multiline regex fails to do what you are expecting
There are, as with many things in life, a few ways to fix that. One is to just feed the results into |join("\n") and then you're likely back where you thought you were to begin with:
- debug:
msg: "{{ jsonOutput | json_query('json.response.results[].dnsName') | join('\n') | regex_findall('(?![a-z]{3}[0-9]{2}v)^.*', multiline=true) }}"
The other is to acknowledge that it's a list[str] and use the | select("match", ...) filter to only allow though items that match:
- debug:
msg: >-
{{ response | json_query('results[].dnsName')
| select('match', '(?![a-z]{3}[0-9]{2}v)^.*')
| list }}
vars:
response:
results:
- dnsName: abc02vsomeserver01.subdomain1.domain.gov
- dnsName: abc02someserver01.subdomain1.domain.gov
- dnsName: xyz03votherserver11.subdomain2.domain.gov
- dnsName: wyz03otherserver11.subdomain2.domain.gov
- dnsName: qrsmainserver02.maindomain.domain.gov
similarly produces:
"msg": [
"abc02someserver01.subdomain1.domain.gov",
"wyz03otherserver11.subdomain2.domain.gov",
"qrsmainserver02.maindomain.domain.gov"
]
I would guess it's personal preference which style works best in your playbook
I want to combine two attribute into single string separated by delimiter using the json_query in ansible
Sample data
{
"locations": [
{"name": "Seattle", "state": "WA"},
{"name": "New York", "state": "NY"},
{"name": "Bellevue", "state": "WA"},
{"name": "Olympia", "state": "WA"}
]
}
As shown in above data set i'm trying to filter the state "WA" and execpted output is:
[
"Seattle-WA",
"Bellevue-WA",
"Olympia-WA"
]
What i have tried as of now:
- debug:
msg: "{{ chart_list.HELM_CHARTS | json_query(\"[?state == 'WA'].{name:name,state:state}\") }}"
Output:
[
{
"name": "Seattle",
"state": "WA"
},
{
"name": "Bellevue",
"state": "WA"
},
{
"name": "Olympia",
"state": "WA"
}
]
Updated :
I was able to get the expected result by trial and error method and these are my findings:
[?state == 'WA'].[join('-',[name,state])][]
Output:
[
"Seattle-WA",
"Bellevue-WA",
"Olympia-WA"
]
Also if the input which you give is in unicode format, i suggest you to add to_json | from_json expressions as mentioned below:
selected_cities: "{{ test.locations| to_json | from_json | json_query(\"[?state == 'WA'].[join('-',[name,state])][]\") }}"
Using above expression will eliminate unicode error whil using the values or in any condition.
Check JMESPath site for more detail on the json_query, it was really helpful in resolving the issue.
For example
- debug:
msg: "{{ locations|
json_query('[?state == `WA`].[name,state]')|
map('join', '-')|list }}"
gives
msg:
- Seattle-WA
- Bellevue-WA
- Olympia-WA
The same result gives the task below using Jinja2 filters only
- debug:
msg: "{{ _names|zip(_states)|map('join', '-')|list }}"
vars:
_locations: "{{ locations|selectattr('state', 'eq', 'WA')|list }}"
_names: "{{ _locations|map(attribute='name')|list }}"
_states: "{{ _locations|map(attribute='state')|list }}"
json_query issue (fixed in 2.10 and later)
There is JMESPath join. Unfortunately
- debug:
msg: "{{ locations|
json_query('[].join(`-`, [name,state])') }}"
fails
msg: |-
JMESPathError in json_query filter plugin:
In function join(), invalid type for value: Seattle, expected one of: ['array-string'], received: "AnsibleUnicode"
to_json|from_json workaround
Quoting from json_query: Add examples for starts_with and contains #72821
data structure returned from register variables needs to be parsed using to_json | from_json in order to get a correct result. Fixes: ansible-collections/community.general#320
- debug:
msg: "{{ locations|to_json|from_json|
json_query('[].join(`-`, [name,state])') }}"
gives
msg:
- Seattle-WA
- New York-NY
- Bellevue-WA
- Olympia-WA
Just for the sake of a pure JMESPath way of doing it, as your trial and error solution still have an unneeded extra layer of complexity.
When you are doing
[?state == 'WA'].[join('-', [name, state])][]
You are creating an array [join('-', [name, state])] then flattening it [] for no reason.
You can just go to the solution with a shorter approach:
[?state == `WA`].join(`-`, [name, state])
Also mind that you can overcome the quotes in quotes (simple or double) complication for JMESPath queries using:
YAML multilines string: How do I break a string in YAML over multiple lines?
Backticks in your JMESPath query, as pointed in the documentation:
In the example above, quoting literals using backticks avoids escaping quotes and maintains readability.
Source: https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#selecting-json-data-json-queries
So you end up with (see note below if you are on an Ansible version < 2.10):
- debug:
msg: >-
{{ test.locations
| json_query('[?state == `WA`].join(`-`, [name, state])') }}
Please note: as raised by #Vladimir Botka on the versions prior to 2.10, you will be affected by this issue: https://github.com/ansible/ansible/issues/27299#issuecomment-331068246, forcing you to add a | to_json | from_json filter on the list.
Given the playbook:
- hosts: all
gather_facts: yes
tasks:
- debug:
msg: >-
{{ test.locations
| json_query('[?state == `WA`].join(`-`, [name, state])')
}}
vars:
test:
locations:
- name: Seattle
state: WA
- name: New York
state: NY
- name: Bellevue
state: WA
- name: Olympia
state: WA
This yields:
[
"Seattle-WA",
"Bellevue-WA",
"Olympia-WA"
]
I am trying to create a list out of a dictionary based on condition. But when I pass it through a loop, the last value of loop overwrites the fact instead of creating a list
input.yml
execution:
pre-deploy:
post-deploy:
shell-files:
name: abc, def, gef
type: deploy
target_host: server1
check: enabled
xml-files:
name: xyz, uvw
type: deploy
target_host: server2
check: enabled
shell-files:
name: pqr
type: migrate
target_host: server1
check: enabled
My Code:
Hosts: local
vars_file:
- input.yml
vars:
post_list:"{{ lookup( 'dict', operations.post-deploy, wantList=Ture ) }}"
tasks:
- set_fact:
get_deploy_list: "{{ item.key }}: {{ item.value.name.split(',') | list }}"
get_host_list: "{{ item.value.target_host }}"
when: ( item.value.type == "deploy" and item.value.check == "enabled")
loop:"{{ post_list | items2dict }}"
- debug: msg="{{ get_deploy_list }}"
Expected Output:
debug:
[ {
shell-files: abc,
shell-files: def,
shell-files: ghi
}
{
xml-files: xyz,
xml-files: uvw
} ]
Actual output:
[{
xml-files: xyz,
xml-files: uvw
} ]
The last value of list overwrites the fact.
The situation is the same as in any programming language with loops: if you don't reference the existing list, then it is just repeatedly reassigning a variable and you will end up with the last state of the world as the loop exits
The traditional way I have seen that solved is via | default and | combine
- set_fact:
get_deploy_list: >-
{{ (get_deploy_list|default([]))
| combine({item.key: item.value.name.split(',') | list})
}}
loop: "{{ post_list | items2dict }}"
although in my playbooks, I consider that pattern a bug since jinja is perfectly capable of building up dictionaries using its looping syntax, without invoking set_fact repeatedly (which, by definition, will open connections to every host in the inventory multiple times)
be aware that I didn't get your exact output format with that code snippet, because there was already too much wrong with your playbook; this answer was just "why did the assignment overwrite the fact all the time"
I need to achieve two things.
Remove duplicate entries from ansible variable called install_loc
Remove any empty blank lines from install_loc.
Below is how install_loc variable is constructed.
- set_fact:
install_loc: "{{ install_loc | default('') + item.split( )[4] | dirname + '\n' }}"
loop: "{{ fdet }}"
Below are the contents of install_loc after writing it to a file.
/app/logs/scripts
/app/logs/mrt
/app/logs/com
/app/logs/exe
/app/logs/scripts
/app/logs/mrt
/app/logs/com
/app/logs/exe
/app/logs/scripts
/app/logs/mrt
/app/logs/com
/app/logs/exe
As you can see the variable as newlines as well as duplicate entries.
Desired output should be as below. Note: Order does not matter:
/app/logs/scripts
/app/logs/mrt
/app/logs/com
/app/logs/exe
I tried
- set_fact:
install_loc: "{{ install_loc | unique }}"
But i get distorted text like below:
set([u'\n', u'/', u'.', u'1', u'0', u'3', u'2', u'C', u'E', u'G', u'F', u'I', u'N', u'a', u'c', u'e', u't', u'i', u'm', u'o', u'o', u'n', u's', u's', u'r', u'u', u't', u'x'])
Can you please suggest ?
The solution:
- set_fact:
install_loc: "{{ install_loc.split('\n') | unique | select | list }}"
Explanation:
install_loc.split('\n') - Split the install_loc by newlines
unique - Remove duplicates
select - Get rid of empty or null values
list - Convert from a Python "generator" to a list
If you want a single string (as the input) replace list by join('\n').
I have an ansible list value:
hosts = ["site1", "site2", "site3"]
if I try this:
hosts | join(", ")
I get:
site1, site2, site3
But I want to get:
"site1", "site2", "site3"
Why not simply join it with the quotes?
"{{ hosts | join('", "') }}"
Ansible has a to_json, to_nice_json, or a to_yaml in it's filters:
{{ some_variable | to_json }}
{{ some_variable | to_yaml }}
Useful if you are outputting a JSON/YAML or even (it's a bit cheeky, but JSON mostly works) a python config file (ie Django settings).
For reference: https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#filters-for-formatting-data
The previous answer leaves "" if the list is empty. There is another approach that may be more robust, e.g. for assigning the joined list as a string to a different variable (example with single quotes):
{{ hosts | map("regex_replace","(.+)","\'\\1\'") | join(',')}}
From a json file file_name.json array like this
"hosts": [
"site1",
"site2",
"site3"
]
Set_fact from a json file
- name: Set variables from parameters file
set_fact:
vars_from_json: "{{ lookup('file', 'file_name.json') | from_json }}"
Use join with double quote and comma, and wrap all around in double quote
- name: Create host list
set_fact:
host_list: "{{ '\"' + vars_from_json.hosts | join('\"'', ''\"') + '\"' }}"