Want to use awk command within ansible playbook items - bash

This is my playbook which is not working if I put the 4th item with awk command with double quote like below:
tasks:
- shell: "{{item}}"
with_items:
- hostname
- df -kh
- uptime
- grep -i diameter /tmp/PSC-CIPDiameter_8.1_*_1.stat.0 | awk -F"," '{ if ($4!=0 || $5!=0|| $6!=0) print "Diameter Time : "$1 " Success: "$3 " Fail: " $4" Timeout: " $5" Reject: " $6}'
register: test
- debug:
msg: "{{test.results|map(attribute='stdout_lines')|list}}"

Put the awk code into the file created from the template. This way, you can test the code separately and improve the readability of the code. For example, the template
shell> cat templates/script01.j2.awk
{
if ($4!=0 || $5!=0|| $6!=0)
print "Diameter Time:" $1 " Success:" $3 " Fail:" $4 " Timeout:" $5 " Reject:"$6;
}
and the playbook
- hosts: localhost
tasks:
- template:
src: script01.j2.awk
dest: script01.awk
- shell: "{{ item }}"
loop:
- uptime
- "echo 1,2,3,4,5,6 | awk -F',' -f script01.awk"
register: test
- debug:
msg: "{{ test.results|map(attribute='stdout')|list }}"
give
"msg": [
" 17:05:13 up 3 days, 13:42, 1 user, load average: 1.93, 1.45, 1.74",
"Diameter Time:1 Success:3 Fail:4 Timeout:5 Reject:6"
]

Related

Comparing the Mounts in ansible

I have a requirement in ansible to compare the mount counts before and after the maintenance, for this i am using the below tasks. We need to compare the premounts and postmounts and if there is difference in the value, it should throw a message to check the host. Please advise.
- name: Take premount counts
shell: df -h |awk ' NR>1{print $1}'|grep -v "tmpfs"|wc -l
register: premounts
- name: Take postmount counts
shell: df -h |awk ' NR>1{print $1}'|grep -v "tmpfs"|wc -l
register: postmounts
I think you are looking for the assert module. You can use this to print a message or fail as per condition.
Example:
- name: Take premount counts
shell: df -h |awk ' NR>1{print $1}'|grep -v "tmpfs"|wc -l
register: premounts
- name: Take postmount counts
shell: df -h |awk ' NR>1{print $1}'|grep -v "tmpfs"|wc -l
register: postmounts
- assert:
that:
- premounts == postmounts
fail_msg: "Premounts: {{ premounts }} and postmounts: {{ postmounts }} do not match!"
success_msg: "Mounts match."
Instead of running the df command and getting number of mounts, you can use the Ansible fact ansible_mounts to get this count also.
Example:
- set_fact:
premounts: "{{ ansible_mounts|count }}"
# some tasks in between
# Gather facts again
- setup:
- set_fact:
postmounts: "{{ ansible_mounts|count }}"
- assert:
that:
- premounts == postmounts
fail_msg: "Premounts: {{ premounts }} and postmounts: {{ postmounts }} do not match!"
success_msg: "Mounts match."
Try as below:
- name: Take premount counts
shell: df -h |awk ' NR>1{print $1}'|grep -v "tmpfs"|wc -l
register: premounts
- name: Take postmount counts
shell: df -h |awk ' NR>1{print $1}'|grep -v "tmpfs"|wc -l
register: postmounts
- debug:
msg: 'There are difference in the mount count, check it'
when: premounts.stdout_lines[0] | int != postmounts.stdout_lines[0] | int

How to use register in this context? (Ansible)

Thanks for your time reading this (probably dumb question)...
I have the following playbook:
- hosts: all
gather_facts: true
vars:
ansible_python_interpreter: /usr/bin/python
tasks:
- name: Get shard status.
shell:
cmd: |
mongo node1:27020 --eval "sh.status()" | grep shards -A 4 | awk -F ':|,|/' '{ print $2 " ", " ", $5,$7,$9}' | sed -e '1 i Shard Nodes' | column -t
when: ansible_fqdn == 'node1'
register: shards_status
- name: Get replica status from SHARD1.
shell:
cmd: |
mongo --eval 'printjson(rs.status())' | grep -E "stateStr|name" | awk -F ':' '{ print $2 }' | sed 's/"//g'| sed 's/,//g'| xargs -n2 | sed -e '1 i Server Status' | column -t
when: ansible_fqdn == 'node1'
register: shard1
- name: Get replica status from SHARD2.
shell:
cmd: |
mongo --eval 'printjson(rs.status())' | grep -E "stateStr|name" | awk -F ':' '{ print $2 }' | sed 's/"//g'| sed 's/,//g'| xargs -n2 | sed -e '1 i Server Status' | column -t
when: ansible_fqdn == 'node2'
register: shard2
- name: Get replica status from SHARD3.
shell:
cmd: |
mongo --eval 'printjson(rs.status())' | grep -E "stateStr|name" | awk -F ':' '{ print $2 }' | sed 's/"//g'| sed 's/,//g'| xargs -n2 | sed -e '1 i Server Status' | column -t
when: ansible_fqdn == 'node3'
register: shard3
- name: Get replica status from SHARD4.
shell:
cmd: |
mongo --eval 'printjson(rs.status())' | grep -E "stateStr|name" | awk -F ':' '{ print $2 }' | sed 's/"//g'| sed 's/,//g'| xargs -n2 | sed -e '1 i Server Status' | column -t
when: ansible_fqdn == 'node4'
register: shard4
- name: Append logs.
lineinfile:
dest: /tmp/status.txt
line: "{{ item }}"
insertafter: EOF
with_items:
- "{{ shards_status.stdout }}"
- "{{ shard1.stdout }}"
- "{{ shard2.stdout }}"
- "{{ shard3.stdout }}"
- "{{ shard4.stdout }}"
delegate_to: localhost
I want to have in one file (/tmp/status.txt) the result of the above vars (shard1,2,3,4). The issue is, if I run the playbook, I got the following message:
TASK [Append logs.] ****************************************************************************************************************************************************************************************
fatal: [node1]: FAILED! => {"failed": true, "msg": "'dict object' has no attribute 'stdout'"}
fatal: [node2]: FAILED! => {"failed": true, "msg": "'dict object' has no attribute 'stdout'"}
fatal: [node3]: FAILED! => {"failed": true, "msg": "'dict object' has no attribute 'stdout'"}
fatal: [node4]: FAILED! => {"failed": true, "msg": "'dict object' has no attribute 'stdout'"}
Any idea of how I can achieve this?
I have tried accessing into the dic, running only that task from one node and multiple workarounds.. but no luck
I believe this failure is because each time you are guarding your tasks with when: ansible_fqdn == 'node1'.
So when ansible is executing this playbook in node1, its executing all node1 tasks and then when it reaches the Append logs. task it tries to dump the stdout from node2. But since we are running in node1, node2 dictionary is empty, hence 'dict object' has no attribute 'stdout'.
One solution would be to give it a default value, for example
with_items:
- "{{ shards_status.stdout | default("") }}"
- "{{ shard1.stdout | default("") }}"
- "{{ shard2.stdout | default("") }}"
- "{{ shard3.stdout | default("") }}"
- "{{ shard4.stdout | default("") }}"
default value for dictionary in jinja2 (ansible)
But the proper ansible solution would be to change your playbook and way of thinking.
With an inventory like this
[mongo]
shard1 get_shard_status=true
shard2
shard3
shard4
your playbook could look something like this
- hosts: mongo
vars:
get_shard_status: false
tasks:
- name: get shard status
shell: mongo node1:27020 --eval "sh.status()" | grep shards -A 4 | awk -F ':|,|/' '{ print $2 " ", " ", $5,$7,$9}' | sed -e '1 i Shard Nodes' | column -t
register: shard_status
when: get_shard_status
- name: get the replica status
shell: mongo --eval 'printjson(rs.status())' | grep -E "stateStr|name" | awk -F ':' '{ print $2 }' | sed 's/"//g'| sed 's/,//g'| xargs -n2 | sed -e '1 i Server Status' | column -t
register: replicas
- name: dump status to localhost
lineinfile:
line: "{{ shard_status + '\n' | default('') }}{{ replicas.stdout }}"
state: present
become: false
dest: "/tmp/foobar"
delegate_to: localhost
With this way, if you had another mongo node, you would only modify your inventory and not your playbook and you also dont have to guard the replicas.stdout with a | default("") as you are always executing that in your nodes.

ansible shell command syntax error in using colon

Team,
I have a task finding mounts and counting them if they are 0 or greater than 64 in count.
I tried double quoting colon and also single quote whole command but no luck. Can anyone hint how to resolve this?
- name: "Mounts count on GPU Nodes should not be 64+"
shell: 'mount | grep -Ec '/dev/sd.*\<csi' | awk '{ print $0,"mounts found on hostname"($0>64? " that are more than 64." : ".") }''
register: mounts_count
changed_when: false
failed_when:
delegate_to: "{{ item }}"
with_items: "{{ groups['kube-gpu-node'] }}"
- name: Check if csi related mounts are present on gpu nodes
assert:
that:
- item.stdout is search('0')
fail_msg: " mounts are present on this node"
success_msg: "mounts are not present on this node"
loop: "{{ mounts_count.results }}"
loop_control:
label: "{{ item.item }}"
ignore_errors: yes
output:
ERROR! Syntax Error while loading YAML.
expected <block end>, but found '<scalar>'
The offending line appears to be:
- name: "Mounts count on GPU Nodes should not be 64+"
shell: 'mount | grep -Ec '/dev/sd.*\<csi' | awk '{ print $0,"mounts found on hostname"($0>64? " that are more than 64." : ".") }''
^ here
This one looks easy to fix. It seems that there is a value started
with a quote, and the YAML parser is expecting to see the line ended
with the same kind of quote. For instance:
when: "ok" in result.stdout
Could be written as:
when: '"ok" in result.stdout'
Or equivalently:
when: "'ok' in result.stdout"
We could be wrong, but this one looks like it might be an issue with
unbalanced quotes. If starting a value with a quote, make sure the
line ends with the same set of quotes. For instance this arbitrary
example:
foo: "bad" "wolf"
Could be written as:
foo: '"bad" "wolf"'
Resolved it by espacing < and double quotes inside expression and double quoting whole command
shell: "mount | grep -Ec '/dev/sd.*\\<csi' | awk '{ print $0,\"mounts found on hostname\"($0>64? \" that are more than 64.\" : \".\") }'"

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

complex loop in ansible with_together

How do I skip empty item in list if I use with_together?
see the code below:
- name: get data_files list
shell: ls -l data_files | awk -F " " {'print $9'}
register: csv_file_list
- debug: var=csv_file_list
- name: get table name list
shell: ls -l data_files/ | awk -F " " {'print $9'} | sed -e "s/.csv//g" | sed -e "s/-/./g"
register: table_list
- debug: var=table_list
- name: copy table from csv to demo db
shell: psql -U postgres -d demo -c "\copy {{ item.1 }} from /home/ubuntu/data_files/{{ item.0 }} DELIMITER ',' CSV HEADER"
with_together:
- csv_file_list.stdout_lines
- table_list.stdout_lines
when: {{ item.1 }} != ''
Test if item.1 is not none.
when: item.1 != None

Resources