XML to JSON Parsing Error with Ansible using Parse_xml - python-3.x

I was trying to parse a XML response to JSON using parse_xml from an AWS VPN output, but I'm getting the below error.
I am able to parse data with "vpn_conn_facts.vpn_connections[0].customer_gateway_configuration" but, it is when I apply the filter parse_xml, that the error arise.
fatal: [localhost]: FAILED! => {"msg": "Unexpected templating type error occurred on ({{ vpn_conn_facts.vpn_connections[0].customer_gateway_configuration | parse_xml('aws_vpn_parser.yaml') }}): 'NoneType' object is not subscriptable"}
The playbook:
- hosts: '{{ PALO_HOST | default("localhost") }}'
connection: local
gather_facts: true
collections:
- paloaltonetworks.panos
tasks:
- name: load var
include_vars: provider.yaml
- name: load aws var
include_vars: /etc/ansible/aws/vpn_facts.yaml
- name: load variable dir
include_vars:
dir: /etc/ansible/aws/vars/
- name: aws_vpn connection info
ec2_vpc_vpn_info:
vpn_connection_ids: '{{ vpn_id }}'
region: "{{ region }}"
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
register: vpn_conn_facts
- name: set_fact
set_fact:
parsed: "{{ vpn_conn_facts.vpn_connections[0].customer_gateway_configuration
| parse_xml('aws_vpn_parser.yaml') }}"
- debug:
msg: '{{ parsed }}'
output of "vpn_conn_facts.vpn_connections[0].customer_gateway_configuration"
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<vpn_connection id=\"vpn-395e9883880\">\n <customer_gateway_id>cgw-136da58e954a</customer_gateway_id>\n <vpn_gateway_id>vgw-015c18a444e89</vpn_gateway_id>\n <vpn_connection_type>ipsec.1</vpn_connection_type>\n <vpn_connection_attributes>NoBGPVPNConnection</vpn_connection_attributes>\n <ipsec_tunnel>\n <customer_gateway>\n <tunnel_outside_address>\n <ip_address>131.226.22.251</ip_address>\n </tunnel_outside_address>\n <tunnel_inside_address>\n <ip_address>169.254.254.58</ip_address>\n <network_mask>255.255.255.252</network_mask>\n <network_cidr>30</network_cidr>\n </tunnel_inside_address>\n </customer_gateway>\n <vpn_gateway>\n <tunnel_outside_address>\n <ip_address>34.232.238.139</ip_address>\n </tunnel_outside_address>\n <tunnel_inside_address>\n <ip_address>169.254.254.57</ip_address>\n <network_mask>255.255.255.252</network_mask>\n <network_cidr>30</network_cidr>\n </tunnel_inside_address>\n </vpn_gateway>\n <ike>\n <authentication_protocol>sha1</authentication_protocol>\n <encryption_protocol>aes-128-cbc</encryption_protocol>\n <lifetime>28800</lifetime>\n <perfect_forward_secrecy>group2</perfect_forward_secrecy>\n <mode>main</mode>\n <pre_shared_key>JXzQgDDNG944e0nnh4w</pre_shared_key>\n </ike>\n <ipsec>\n <protocol>esp</protocol>\n <authentication_protocol>hmac-sha1-96</authentication_protocol>\n <encryption_protocol>aes-128-cbc</encryption_protocol>\n <lifetime>3600</lifetime>\n <perfect_forward_secrecy>group2</perfect_forward_secrecy>\n <mode>tunnel</mode>\n <clear_df_bit>true</clear_df_bit>\n <fragmentation_before_encryption>true</fragmentation_before_encryption>\n <tcp_mss_adjustment>1379</tcp_mss_adjustment>\n <dead_peer_detection>\n <interval>10</interval>\n <retries>3</retries>\n </dead_peer_detection>\n </ipsec>\n </ipsec_tunnel>\n <ipsec_tunnel>\n <customer_gateway>\n <tunnel_outside_address>\n <ip_address>131.226.22.231</ip_address>\n </tunnel_outside_address>\n <tunnel_inside_address>\n <ip_address>169.254.207.46</ip_address>\n <network_mask>255.255.255.252</network_mask>\n <network_cidr>30</network_cidr>\n </tunnel_inside_address>\n </customer_gateway>\n <vpn_gateway>\n <tunnel_outside_address>\n <ip_address>54.225.7.92</ip_address>\n </tunnel_outside_address>\n <tunnel_inside_address>\n <ip_address>169.254.207.45</ip_address>\n <network_mask>255.255.255.252</network_mask>\n <network_cidr>30</network_cidr>\n </tunnel_inside_address>\n </vpn_gateway>\n <ike>\n <authentication_protocol>sha1</authentication_protocol>\n <encryption_protocol>aes-128-cbc</encryption_protocol>\n <lifetime>28800</lifetime>\n <perfect_forward_secrecy>group2</perfect_forward_secrecy>\n <mode>main</mode>\n <pre_shared_key>RDt7vieaxRkjUwaCJ8M8Lo.Qztdhhfdq</pre_shared_key>\n </ike>\n <ipsec>\n <protocol>esp</protocol>\n <authentication_protocol>hmac-sha1-96</authentication_protocol>\n <encryption_protocol>aes-128-cbc</encryption_protocol>\n <lifetime>3600</lifetime>\n <perfect_forward_secrecy>group2</perfect_forward_secrecy>\n <mode>tunnel</mode>\n <clear_df_bit>true</clear_df_bit>\n <fragmentation_before_encryption>true</fragmentation_before_encryption>\n <tcp_mss_adjustment>1379</tcp_mss_adjustment>\n <dead_peer_detection>\n <interval>10</interval>\n <retries>3</retries>\n </dead_peer_detection>\n </ipsec>\n </ipsec_tunnel>\n</vpn_connection>"

Related

Multiple with_items in an Ansible module block

I want to create multiple logical volumes with a variable file but it return a sintax error found character that cannot start any token, I have tried in different ways but still doesn't work
main.yml
---
- name: playbook for create volume groups
hosts: localhost
become: true
tasks:
- include_vars: vars.yml
- name: Create a logical volume
lvol:
vg: vg03
lv: "{{ item.var1 }}"
size: "{{ item.var2 }}"
with_items:
- { var1: "{{ var_lv_name }}", var2: "{{ var_lv_size }}" }
vars.yml
var_lv_name:
- lv05
- lv06
var_lv_size:
- 1g
- 1g
Use with_together. Test it first. For example,
- debug:
msg: "Create lv: {{ item.0 }} size: {{ item.1 }}"
with_together:
- "{{ var_lv_name }}"
- "{{ var_lv_size }}"
gives (abridged)
msg: 'Create lv: lv05 size: 1g'
msg: 'Create lv: lv06 size: 1g'
Optionally, put the declaration below into the file vars.yml
var_lv: "{{ var_lv_name|zip(var_lv_size) }}"
This creates the list
var_lv:
- [lv05, 1g]
- [lv06, 1g]
Use it in the code. The simplified task below gives the same results
- debug:
msg: "Create lv: {{ item.0 }} size: {{ item.1 }}"
loop: "{{ var_lv }}"
The previous answer it's totally correct but In my humble opinion we should be getting into the new way to do the things with loop and filters.
Here's my answer:
---
- name: playbook for create volume groups
hosts: localhost
gather_facts: no
become: true
vars_files: vars.yml
tasks:
- name: Create a logical volume
lvol:
vg: vg03
lv: "{{ item[0] }}"
size: "{{ item[1] }}"
loop: "{{ var_lv_name | zip(var_lv_size) | list }}"
In this answer you're using the new way to use loops with keyword loop and using filters like zip and turning the result into a list type for iteration in the loop.

how to traverse through each line of a text file in ansible

I have to traverse through package list file which contains list of packages with their architecture. How can I feed those input to my playbook file? I found a way to get the package names alone but architecture version is not coming. This is my package_list file
nginx | x86_64
telnet| x86_64
openssh | i386
This is my playbook
- name: get contents of package.txt
command: cat "/root/packages.txt"
register: _packages
- name: get contents of architecture from packages.txt
command: cat "/root/packages.txt" | awk '{print $3}'
register: _arch
- name: Filter
theforeman.foreman.content_view_filter:
username: "admin"
password: "mypass"
server_url: "myhost"
name: "myfilter"
organization: "COT"
content_view: "M_view"
filter_type: "rpm"
architecture: "{{ _arch }}"
package_name: "{{ item }}"
inclusion: True
loop: "{{ _packages.stdout_lines }}"
loop: "{{ _arch.stdout_lines }}"
Any help would be appreciated
The required output is package name and architecture should be read from packages.txt file through ansible-playbook
try this playbook:
- name: Reproduce issue
hosts: localhost
gather_facts: no
tasks:
- name: get contents of package.txt
command: cat "/root/packages.txt"
register: _packages
- debug:
msg: "package: {{ line.0 }}, arch: {{ line.1 }}"
loop: "{{ _packages.stdout_lines }}"
vars:
line: "{{ item.split('|')|list }}"
result:
ok: [localhost] => (item=nginx | x86_64) => {
"msg": "package: nginx , arch: x86_64 "
}
ok: [localhost] => (item=telnet| x86_64) => {
"msg": "package: telnet, arch: x86_64 "
}
ok: [localhost] => (item=openssh | i386) => {
"msg": "package: openssh , arch: i386 "
}
for your case:
- name: Filter
theforeman.foreman.content_view_filter:
:
:
architecture: "{{ line.1 }}"
package_name: "{{ line.0 }}"
inclusion: True
loop: "{{ _packages.stdout_lines }}"
vars:
line: "{{ item.split('|')|list }}"
following the version of ansible you could write too line: "{{ item | split('|') | list }}"
You need to split up the line into the necessary values by filtering.
---
- hosts: localhost
become: false
gather_facts: false
tasks:
- name: Gahter package list
shell:
cmd: cat package.txt
register: _packages
- name: Show packages
debug:
msg: "Name: {{ item.split('|')[0] }}, Arch: {{ item.split('|')[1] }}"
loop: "{{ _packages.stdout_lines }}"
Further Documentation
Playbook filters - Manipulating strings
Jinja Template Designer - Filters
Further Q&A
Split string into list in Jinja?

Ansible - load multiplne local yml files and merge their content

my host_vars file has about 5k lines of yml code. So I would like to have separate yml files - one file per one service.
Simplified example:
user#test $ cat production/split_configs/a.example.net.yml
my_array:
- a.example.net
user#test $ cat production/split_configs/b.example.net.yml
my_array:
- b.example.net
user#test $ cat webhosts.yml
- hosts: myservers
pre_tasks:
- name: merge ansible arrays
tags: always
delegate_to: localhost
block:
- name: find config files
find:
paths: production/configs/
patterns: '*.yml'
register: find_results
- name: aaa
debug:
msg: "{{ find_results.files }}"
- name: bbb
debug:
msg: "{{ item.path }}"
with_items: "{{ find_results.files }}"
- name: ccc
debug:
msg: "{{ lookup('file', 'production/configs/a.example.net.yml') }}"
- name: ddd
debug:
msg: "{{ lookup('file', item.path) }}"
loop: "{{ find_results.files }}"
tasks:
- name: eee
debug:
msg: "{{ my_array }}"
The goal is to merge content of both arrays an print the merged content in task eee:
my_array:
- a.example.net
- b.example.net
Task aaa print informations about files (path, mode, uid, ...) - it works.
Tasks bbb, and ddd print nothing. I do not understand why.
Task ccc print content of file. But the path is written in playbook :-(
After load files I need to merge them. My idea is to use something like set_fact: my_array="{{ my_array + my_array }}" in task with with_items: "{{ find_results.files }}". Is it good idea? Or how better I can do it?
For example, the tasks below do the job
- include_vars:
file: "{{ item }}"
name: "my_prefix_{{ item|regex_replace('\\W', '_') }}"
with_fileglob: production/split_configs/*
- set_fact:
my_vars: "{{ my_vars|d({})|
combine(lookup('vars', item),
recursive=True,
list_merge='append') }}"
loop: "{{ q('varnames', 'my_prefix_.*') }}"
give
my_vars:
my_array:
- a.example.net
- b.example.net
You can use the simple cat commnd to merge the files into the one file and later include var file for example -
- raw: "cat production/split_configs/* >my_vars.yml"
- include_vars: file=my_vars.yml name=my_vars
will give you the result -
my_vars:
my_array:
- a.example.net
- b.example.net

Extend Volume Group using Ansible

I have trying to extend the VG via ansible passing the pvname by variable, however I really don't understand why is not working.
Below you can see my code.
Variable file:
new_disk:
- diskname: /dev/sdc
pvname: /dev/sdb1, dev/sdc1
vgname: datavg
lvm_settings:
- lv_name: datalv
lv_size: +100%FREE
fs_name: ansible_fs_test
lvpath: /dev/mapper/datavg-datalv
filesystem_type: ext4
tasks file:
include_vars: "{{ vm_name }}.yml"
- name: First disk partition settings
block:
- name: Create a new primary partition
community.general.parted:
device: "{{ item.diskname }}"
number: 1
state: present
with_items: "{{ new_disk }}"
register: partition_status
rescue:
- name: Debug messages to check the error
debug:
msg: "{{ partition_status }}"
- name: Extending the Volume Group
community.general.lvg:
vg: "{{ vgname }}"
pvs: "{{ pvname }}"
pvresize: yes
Below, you can see the error message:
TASK [resize_fs_linux : Extending the Volume Group] **********************************************************************************************************************************************************fatal: [10.1.33.225]: FAILED! => {"changed": false, "msg": "Device /home/icc-admin/ dev/sdc1 not found."}
Do you know have any idea why is not working?
I really appreciate your help and time
Best Regards,
For it works that way:
Variable file
diskname:
- /dev/sdb
- /dev/sdc
disks_settings:
- vgname: datavg
pvname:
- /dev/sdb1
- /dev/sdc1
lvm_settings:
- vgname: datavg
lv_name: datalv
lv_size: +100%FREE
fs_name: ansible_fs_test
lvpath: /dev/mapper/datavg-datalv
filesystem_type: ext4
Tasks file:
---
# tasks file for resize_fs_linux
- include_vars: "{{ vm_name }}.yml"
- name: First disk partition settings
block:
- name: Create a new primary partition
community.general.parted:
device: "{{ item }}"
number: 1
state: present
with_items: "{{ diskname }}"
register: partition_status
run_once: true
rescue:
- name: Debug messages to check the error
debug:
msg: "{{ partition_status }}"
- name: Extending the Volume Group
community.general.lvg:
vg: "{{ item.vgname }}"
pvs: "{{ item.pvname }}"
pvresize: yes
with_items: "{{ disks_settings }}"
- name: Increasing the filesystems
community.general.lvol:
vg: "{{ vgname }}"
lv: "{{ item.lv_name }}"
size: "{{ item.lv_size }}"
resizefs: true
with_items: "{{ lvm_settings }}"

Ansible to execute a task only when multiple files exist

I want to execute a task only when multiple files exist . if only single file is exist i need to ignore this task. How can i achieve this.
Am unable to achieve with the below playbook
---
- name: Standardize
hosts: test
gather_facts: false
vars:
file_vars:
- {id: 1, name: /etc/h_cm}
- {id: 2, name: /etc/H_CM}
tasks:
- block:
- name: Check if both exists
stat:
path: "{{ item.name }}"
with_items: "{{ file_vars }}"
register: cm_result
- name: Move both files
shell: mv "{{ item.item }}" /tmp/merged
with_items: "{{ cm_result.results }}"
when: item.stat.exists
After check if both exist task, you can add a set fact task like this one:
- name: set facts
set_fact:
files_exist: "{{ (files_exist | default([])) + [item.stat.exists] }}"
with_items: "{{ cm_result.results }}"
And you change your move files task to:
- name: Move both files
debug:
msg: "{{ item.stat.exists }}"
with_items: "{{ cm_result.results }}"
when: false not in files_exist
You have to specify shell: mv "{{ item.item.name }}" /tmp/merged insted of shell: mv "{{ item.item }}" /tmp/merged
Check the below works?:
- name: Standardize
hosts: test
gather_facts: false
become: yes ## If needed
vars:
file_vars:
- {id: 1, name: /etc/h_cm}
- {id: 2, name: /etc/H_CM}
tasks:
- block:
- name: Check if both file exists
stat:
path: "{{ item.name }}"
with_items: "{{ file_vars }}"
register: cm_result
- debug:
var: item.stat.exists
loop: "{{ cm_result.results }}"
- name: Crate a dummy list
set_fact:
file_state: []
- name: Add true to list if file exists
set_fact:
file_state: "{{ file_state }} + ['{{ item.stat.exists }}']"
loop: "{{ cm_result.results }}"
when: item.stat.exists == true
- name: Move both files
shell: mv "{{ item.item.name }}" /tmp/merged
loop: "{{ cm_result.results }}"
when: file_state|length > 1

Resources