copy multiple output stream to a file in ansible - ansible

I am pulling network switches interfaces information e.g. interface name, admin status, description etc. and parsing those values into a single .csv file but it seems like it is overwriting a file rather than appending a file, can you please let me know if I am doing something wrong here ? or there is better way to achieve this, here is my playbook.
---
- name: NXOS interfaces operation status
hosts: NXOS
connection: local
gather_facts: false
tasks:
- name: Discover NXOS interfaces
nxos_command:
commands:
- show run | grep ignore-case interface | exclude feature | exclude source | exclude destination | exclude logging
register: interface_names
- name: Get the interfaces stats
nxos_command:
commands:
# Full interface operational status
# - command: show interface {{ item.split(' ')[1] }}
# Interface name
- command: show interface {{ item.split(' ')[1] }} | head lines 1 | awk '{ print $1 }'
# Interface description
- command: show interface {{ item.split(' ')[1] }} | grep "Description"
# Interface operation status:
- command: show interface {{ item.split(' ')[1] }} | head lines 1 | awk '{ print $3}'
# Interface admin-status
- command: show interface {{ item.split(' ')[1] }} | grep "admin state" | awk '{ print $4 }'
# Interface MTU size
- command: show interface {{ item.split(' ')[1] }} | grep "MTU" | awk '{ print $2 }'
# Loop over the interfaces output from the previous task
loop: "{{ interface_names.stdout_lines[0] }}"
register: interfaces_stats
- name: Saving interfaces facts for NXOS
copy:
content: |
#jinja2: lstrip_blocks: True, trim_blocks: True
{{ inventory_hostname }}
Interface name,Interface description,Interface operational-status,Interface admin-status,Interface MTU size
{{ item.stdout_lines[0] }},{{ item.stdout_lines[1] }},{{ item.stdout_lines[2] }},{{ item.stdout_lines[3] }},{{ item.stdout_lines[4] }}
dest: /opt/ansible/vsansible/info/{{ inventory_hostname }}_info.csv
with_items:
- '{{ interfaces_stats.results }}'
here is the current output shows
Interface name,Interface description,Interface operational-status,Interface admin-status,Interface MTU size
[u'mgmt0'],[u'Description: -sw01-Gig0/39'],[u'up'],[u'up,'],1500
However, it should show me the output for every interface I am looking through in my first task.
please advice.

Related

Ansible multiple variable contains multiple values

i have some ansible task below :
- name: counting object from json
shell: >
jq '.results[].stdout_lines | length' backup/{{ inventory_hostname }}_rsl.json | wc -l
register: jsondata
- name: counting object converting
shell: >
seq 0 {{ jsondata.stdout|int - 1 }} | tr '\n' ' ' | xargs | sed 's/[[:space:]]/,/g'
register: seq
- name: get interface
shell: >
cat backup/{{ inventory_hostname }}_{{ item }}_rsl_result_nows.json | cut -d, -f1
register: interface
with_items:
- "{{ seq.stdout.split(',') | list }}"
- name: get rsl value
shell: >
cat backup/{{ inventory_hostname }}_{{ item }}_rsl_result_nows.json | cut -d, -f2-
register: rslvalue
with_items:
- "{{ seq.stdout.split(',') | list }}"
- name: post to DB via curl
shell: >
curl -d "ip_address={{ inventory_hostname }}&hostname={{ varhostname.stdout }}&interface={{ interface }}&rslvalue={{ rslvalue }}" -X POST http://dev.trm.net:8088/ip_planning/rsl/postrsl -v
i want to post data with the same hostname but different interface and some other attributes.
This my curl command that i want :
curl -d "ip_address=IP_A&hostname=HOST_A&interface=IFACE_1&rslvalue=1,2,3,4,5" -X POST http://dev.trm.net:8088/ip_planning/rsl/postrsl -v
curl -d "ip_address=IP_A&hostname=HOST_A&interface=IFACE_2&rslvalue=5,4,3,2,1" -X POST http://dev.trm.net:8088/ip_planning/rsl/postrsl -v
curl -d "ip_address=IP_B&hostname=HOST_B&interface=IFACE_1&rslvalue=11,21,31,41,51" -X POST http://dev.trm.net:8088/ip_planning/rsl/postrsl -v
i'm tired a full-day, please help me. i've tried from my ansible script above but error
"msg": "The task includes an option with an undefined variable. The error was: 'item' is undefined
You have {{ item.interface.stdout }} in the last task, but with_items is not present.
Also when you use item in the shell command, use as "{{item}}"
a little improvement make me small fun...
- name: post to DB via curl
shell: >
curl -d "ip_address={{ inventory_hostname }}&hostname={{ varhostname.stdout }}&interface={{ item }}&rslvalue={{ item }}" -X POST http://dev.tr$
with_items:
- "{{ rslvalue.results | map(attribute='stdout') | list }}"
- "{{ interface.results | map(attribute='stdout') | list }}"
but "item" variable always print "rslvalue" result. how to declare "item" with multiple with_items ??

Ansible get first element from list

Suppose I have the following vars_file:
mappings:
- primary: 1.1.1.1
secondary: 2.2.2.2
- primary: 12.12.12.12
secondary: 11.11.11.11
and hosts file
1.1.1.1
12.12.12.12
5.5.5.5
and the following playbook task
- name: Extract secondary from list
debug:
msg: "{{ (mappings | selectattr('primary', 'search', inventory_hostname) | list | first | default({'secondary':None})).secondary }}"
The current task works and will give empty string when no match are found, but I would like to know if there is a better way/cleaner way of doing it without passing a dictionary to the default constructor.
An option would be to use json_query
- debug:
msg: "{{ mappings | json_query(\"[?primary=='\" + inventory_hostname + \"'].secondary\") }}"
, but selectattr works too
- debug:
msg: "{{ mappings | selectattr('primary', 'equalto', inventory_hostname) | map(attribute='secondary') | list }}"

Ansible - complex loop with conditional and register stdoutput

I have an ansible playbook which creates libvirt guests based on parsed arguments. I am trying to extend the playbook with an extra argument which will allow me to attach guest OS to specidied Virtual network and assign custom mac address written in a file.
The playbook should is invoked like this:
ansible-playbook -e "CPU=2 MEMORY=1024 VM_STORAGE=/tmp/zz VM_NAME=desktop5 VM_NETWORK=bridge,Isolated2" vm.yml
as one of the last step I had to create nics and use specific mac addresses which are defined in a file in following format:
#NAT#52:54:00:aa:aa:01#desktop1
#bridge#52:54:00:aa:bb:01#desktop1
#Isolated1#52:54:00:aa:cc:01#desktop1
#Isolated2#52:54:00:aa:dd:01#desktop1
How to use just ansible playbook features to attach network interfaces based on the specified extra argument -e VM_NETWORK and which determines the mac addresses.
So far I came with following:
- name: Setting up network
shell: "/bin/grep -e {{ vm_name }} {{ mac_file }} | grep -e {{ item }} | awk -F\"#\" '{print $3}'"
register: vm_mac
command: "/usr/bin/virsh attach-interface --domain {{ vm_name }} --type bridge --source 'bridge0' --model virtio --mac {{ vm_mac }} --config"
when: item == "bridge"
with_items:
- "{{ VM_NETWORK.split(',') }}"
but the problem is I cannot have command and shell action in one task??
Should I fetch the mac address by invoking a shell script and then use it in command?
At the moment I have a bash script which executes virsh command in order to attach or detach interfaces but I was wondering if there is a way to do it dynamically in ansible.
You are correct, you would just have two tasks. The first would get the MAC and register the vm_mac variable while the second would use that variable.
For what it's worth, your shell task can be substantially simplified, so maybe in the end things would look like:
- name: Get MAC
command: >
awk -F# -vvm_name="{{ vm_name }}" -vvm_network="{{ item }}"
'$4 == vm_name && $2 == vm_network {print $3}' "{{ mac_file }}"
register: vm_macs
with_items: "{{ vm_networks.split(',') }}"
For each network, this will print out the MAC address associated with vm_name. The contents of the vm_macs variable will include the list of results in the vm_macs.results key, and each element of this list will include the network name in the item key and the MAC address in the stdout key:
"vm_macs.results": [
{
"item": "bridge",
"stdout": "52:54:00:aa:bb:01",
},
{
"item": "Isolated2",
"stdout": "52:54:00:aa:dd:01",
}
]
There's a bunch of other stuff there, too, but this is what we care about.
You could then use that in a subsequent task like this:
- name: attach interface
command: >
virsh attach-interface
--domain {{ vm_name }}
--type bridge
--source bridge0
--model virtio
--mac {{ item.stdout }}
--config
when: item.item == "bridge"
with_items: "{{ vm_macs.results }}"
If you execute commands and searching mac_file on localhost, you can use pipe lookup (see more lookups chapter):
- name: Setting up network
command: >
/usr/bin/virsh
attach-interface
--domain {{ vm_name }}
--type bridge
--source 'bridge0'
--model virtio
--mac {{ vm_mac }}
--config
vars:
vm_mac: "{{ lookup('pipe', grep_cmd) }}"
grep_cmd: "/bin/grep -e {{ vm_name }} {{ mac_file }} | grep -e {{ item }} | awk -F\"#\" '{print $3}'"
when: item == "bridge"
with_items: "{{ VM_NETWORK.split(',') }}"
Note: I didn't test this exact grep_cmd
This will work only on localhost, because plugins are executed locally.

Ansible - using {{ item }} with csvfile lookups

I'm trying to work out a way to cut down on lines of code for various modules, repeating stanzas seems pointless. I'd like to use csvfile lookups to help fill in blanks. Take for example the following CSV:
# groups.csv
# name, gid [optional - leave blank], state [present|absent], system [yes|no]
accounts,502,present,no
engineering,504,present,no
So, I have all my group definitions in csv format. The problem is, processing it, no matter what I try I cannot get lookups to work inside the groups module.
So initially, I wanted to do this:
---
- hosts: localhost
become: True
become_user: root
tasks:
- name: get groups
command: /usr/bin/awk -F',' '!/^#/ && !/^$/ { print $1 }' groups.csv
register: groups_out
- debug: var=groups_out.stdout_lines
- name: Process groups
group: >
name="{{ lookup('csvfile', 'item file=groups.csv col=0') }}"
gid="{{ lookup('csvfile', 'item file=groups.csv col=1') }}"
state="{{ lookup('csvfile', 'item file=groups.csv col=2') }}"
system="{{ lookup('csvfile', 'item file=groups.csv col=3') }}"
# with_lines: "/usr/bin/awk -F',' '!/^#/ && !/^$/ { print $1 }' groups.csv"
# with_items: "{{ groups_out.stdout_lines }}"
with_lines: "{{ groups_out.stdout_lines }}"
The result of which is this:
TASK [Process groups] **********************************************************
/bin/sh: accounts: command not found
fatal: [localhost]: FAILED! => {"failed": true, "msg": "lookup_plugin.lines(accounts) returned 127"}
As you can see from the code, I've also tried using with_items and with_lines using the awk command directly, however it appears the groups module doesn't like me doing this.
Ansible 2.1.1.0 on Centos 7.
Python 2.7.5
Jinja 2.8
Any ideas how I might achieve this?
Thanks in advance,
R
Answer below. Thanks to Jon and Kai on the ansible-project googlegroup for their assistance.
---
- hosts: localhost
become: True
become_user: root
tasks:
- name: get groups
command: /usr/bin/awk -F',' '!/^#/ && !/^$/ { print $1 }' /var/tmp/groups.csv
register: groups_out
- name: Process groups one
group: >
name={{ lookup('csvfile', item + ' file=groups.csv col=0 delimiter=,') }}
gid={{ lookup('csvfile', item + ' file=groups.csv col=1 delimiter=,') }}
state={{ lookup('csvfile', item + ' file=groups.csv col=2 delimiter=,') }}
system={{ lookup('csvfile', item + ' file=groups.csv col=3 delimiter=,') }}
with_items: "{{ groups_out.stdout_lines }}"
ignore_errors: True
- name: Process groups two
group: >
name={{ lookup('csvfile', item + ' file=groups.csv col=0 delimiter=,') }}
gid={{ lookup('csvfile', item + ' file=groups.csv col=1 delimiter=,') }}
state={{ lookup('csvfile', item + ' file=groups.csv col=2 delimiter=,') }}
system={{ lookup('csvfile', item + ' file=groups.csv col=3 delimiter=,') }}
with_lines: /usr/bin/awk -F',' '!/^#/ && !/^$/ { print $1 }' /var/tmp/groups.csv

How to conditionally execute group of tasks on ansible?

I want to conditionally execute a set of tasks. Is there any syntax available that would let me execute a group of tasks, where the condition is evaluated once per whole group (like in a if statement in programming languages)?
Take a look at the code snippets below. I know the difference is small, but the first code better expresses my intention without polluting the namespace with additional variables (user_home_result2).
Pseudocode of what I want to do:
- name: Capturing user's home directory
shell: "getent passwd {{ user }} | awk -F: '{ print $6 }'"
register: user_home_result
- set_fact: user_home={{ user_home_result.stdout }}
- when: user_home != ''
- name: Setting up user {{ user }}
user: >
generate_ssh_key=yes
name="{{ user }}"
- name: Capturing user's home directory
shell: "getent passwd {{ user }} | awk -F: '{ print $6 }'"
register: user_home_result
- set_fact: user_home={{ user_home_result.stdout }}
Walkaround:
- name: Capturing user's home directory
shell: "getent passwd {{ user }} | awk -F: '{ print $6 }'"
register: user_home_result
- set_fact: user_home={{ user_home_result.stdout }}
- name: Setting up user {{ user }}
user: >
generate_ssh_key=yes
name="{{ user }}"
when: user_home != ''
- name: Capturing user's home directory
shell: "getent passwd {{ user }} | awk -F: '{ print $6 }'"
register: user_home_result2
when: user_home != ''
- set_fact: user_home={{ user_home_result2.stdout }}
when: user_home != ''
You can put the tasks in a new yml file and use a conditional include:
# subtasks.yml
---
- name: Setting up user {{ user }}
user: >
generate_ssh_key=yes
name="{{ user }}"
- name: Capturing user's home directory
shell: "getent passwd {{ user }} | awk -F: '{ print $6 }'"
register: user_home_result
- set_fact: user_home={{ user_home_result.stdout }}
And in the playbook:
- name: Capturing user's home directory
shell: "getent passwd {{ user }} | awk -F: '{ print $6 }'"
register: user_home_result
- set_fact: user_home={{ user_home_result.stdout }}
- include: subtask.yml
when: user_home != ''
As of version 2.1, ansible has blocks for logical task grouping. Blocks allow you to specify common things for a few tasks only once, including the when conditionals. Ex:
- block:
- name: put a file somewhere
copy: src=asdf dest=asdf
- name: put another file somewhere
template: src=asdf.j2 dest=asdf
when: bool_is_true
The above is equivalent to attaching a when: bool_is_true to both of the tasks inside the block.
More information at https://docs.ansible.com/ansible/latest/user_guide/playbooks_blocks.html

Resources