I would like to quickly monitor some hosts using commands like ps,dstat etc using ansible-playbook. The ansible command itself perfectly does what I want, for instance I'd use:
ansible -m shell -a "ps -eo pcpu,user,args | sort -r -k1 | head -n5"
and it nicely prints all std output for every host like this:
localhost | success | rc=0 >>
0.0 root /sbin/init
0.0 root [kthreadd]
0.0 root [ksoftirqd/0]
0.0 root [migration/0]
otherhost | success | rc=0 >>
0.0 root /sbin/init
0.0 root [kthreadd]
0.0 root [ksoftirqd/0]
0.0 root [migration/0]
However this requires me to keep a bunch of shell scripts around for every task which is not very 'ansible' so I put this in a playbook:
---
-
hosts: all
gather_facts: no
tasks:
- shell: ps -eo pcpu,user,args | sort -r -k1 | head -n5
and run it with -vv, but the output baiscally shows the dictionary content and newlines are not printed as such so this results in an unreadable mess like this:
changed: [localhost] => {"changed": true, "cmd": "ps -eo pcpu,user,args | sort -r -k1
head -n5 ", "delta": "0:00:00.015337", "end": "2013-12-13 10:57:25.680708", "rc": 0,
"start": "2013-12-13 10:57:25.665371", "stderr": "", "stdout": "47.3 xxx Xvnc4 :24
-desktop xxx:24 (xxx) -auth /home/xxx/.Xauthority -geometry 1920x1200\n
....
I also tried adding register: var and the a 'debug' task to show {{ var.stdout }} but the result is of course the same.
Is there a way to get nicely formatted output from a command's stdout/stderr when run via a playbook? I can think of a number of possible ways (format output using sed? redirect output to file on the host then get that file back and echo it to the screen?), but with my limited knowledge of the shell/ansible it would take me a day to just try it out.
The debug module could really use some love, but at the moment the best you can do is use this:
- hosts: all
gather_facts: no
tasks:
- shell: ps -eo pcpu,user,args | sort -r -k1 | head -n5
register: ps
- debug: var=ps.stdout_lines
It gives an output like this:
ok: [host1] => {
"ps.stdout_lines": [
"%CPU USER COMMAND",
" 1.0 root /usr/bin/python",
" 0.6 root sshd: root#notty ",
" 0.2 root java",
" 0.0 root sort -r -k1"
]
}
ok: [host2] => {
"ps.stdout_lines": [
"%CPU USER COMMAND",
" 4.0 root /usr/bin/python",
" 0.6 root sshd: root#notty ",
" 0.1 root java",
" 0.0 root sort -r -k1"
]
}
This is a start may be :
- hosts: all
gather_facts: no
tasks:
- shell: ps -eo pcpu,user,args | sort -r -k1 | head -n5
register: ps
- local_action: command echo item
with_items: ps.stdout_lines
NOTE: Docs regarding ps.stdout_lines are covered here: ('Register Variables' chapter).
Expanding on what leucos said in his answer, you can also print information with Ansible's humble debug module:
- hosts: all
gather_facts: no
tasks:
- shell: ps -eo pcpu,user,args | sort -r -k1 | head -n5
register: ps
# Print the shell task's stdout.
- debug: msg={{ ps.stdout }}
# Print all contents of the shell task's output.
- debug: var=ps
I found using the minimal stdout_callback with ansible-playbook gave similar output to using ad-hoc ansible.
In your ansible.cfg (Note that I'm on OS X so modify the callback_plugins path to suit your install)
stdout_callback = minimal
callback_plugins = /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/ansible/plugins/callback
So that a ansible-playbook task like yours
---
-
hosts: example
gather_facts: no
tasks:
- shell: ps -eo pcpu,user,args | sort -r -k1 | head -n5
Gives output like this, like an ad-hoc command would
example | SUCCESS | rc=0 >>
%CPU USER COMMAND
0.2 root sshd: root#pts/3
0.1 root /usr/sbin/CROND -n
0.0 root [xfs-reclaim/vda]
0.0 root [xfs_mru_cache]
I'm using ansible-playbook 2.2.1.0
ANSIBLE_STDOUT_CALLBACK=debug ansible-playbook /tmp/foo.yml -vvv
Tasks with STDOUT will then have a section:
STDOUT:
What ever was in STDOUT
If you need a specific exit status, Ansible provides a way to do that via callback plugins.
Example. It's a very good option if you need a 100% accurate exit status.
If not, you can always use the Debug Module, which is the standard for this cases of use.
Cheers
for me, the only one that works (because of register+with_items combination) is this:
- name: "download and distribute certs"
shell: "python3 /tmp/bla.py {{ item.name }}"
register: python3
with_items: "{{ my_list }}"
- debug: msg="{{ item.stdout_lines | join("\n") }}"
with_items: "{{ python3['results'] }}"
I personally have several shell or command calls in a playbook and gather that's output to different variables. At the end I sum up the information and list all at once like so:
- ansible.builtin.shell: "df -h"
register: disk
- ansible.builtin.shell: "free -m"
register: mem
.....
- name: Summarize
local_action: ansible.builtin.debug var={{ item }}
become: no
with_items:
- disk.stdout_lines
- mem.stdout_lines
if you call it with
ANSIBLE_STDOUT_CALLBACK=minimal ansible-playbook getServerInfo.yml
it gives a nice, clean output
Perhaps not relevant if you're looking to do this ONLY using ansible. But it's much easier for me to have a function in my .bash_profile and then run _check_machine host1 host2
function _check_machine() {
echo 'hostname,num_physical_procs,cores_per_procs,memory,Gen,RH Release,bios_hp_power_profile,bios_intel_qpi_link_power_management,bios_hp_power_regulator,bios_idle_power_state,bios_memory_speed,'
hostlist=$1
for h in `echo $hostlist | sed 's/ /\n/g'`;
do
echo $h | grep -qE '[a-zA-Z]'
[ $? -ne 0 ] && h=plabb$h
echo -n $h,
ssh root#$h 'grep "^physical id" /proc/cpuinfo | sort -u | wc -l; grep "^cpu cores" /proc/cpuinfo |sort -u | awk "{print \$4}"; awk "{print \$2/1024/1024; exit 0}" /proc/meminfo; /usr/sbin/dmidecode | grep "Product Name"; cat /etc/redhat-release; /etc/facter/bios_facts.sh;' | sed 's/Red at Enterprise Linux Server release //g; s/.*=//g; s/\tProduct Name: ProLiant BL460c //g; s/-//g' | sed 's/Red Hat Enterprise Linux Server release //g; s/.*=//g; s/\tProduct Name: ProLiant BL460c //g; s/-//g' | tr "\n" ","
echo ''
done
}
E.g.
$ _machine_info '10 20 1036'
hostname,num_physical_procs,cores_per_procs,memory,Gen,RH Release,bios_hp_power_profile,bios_intel_qpi_link_power_management,bios_hp_power_regulator,bios_idle_power_state,bios_memory_speed,
plabb10,2,4,47.1629,G6,5.11 (Tikanga),Maximum_Performance,Disabled,HP_Static_High_Performance_Mode,No_CStates,1333MHz_Maximum,
plabb20,2,4,47.1229,G6,6.6 (Santiago),Maximum_Performance,Disabled,HP_Static_High_Performance_Mode,No_CStates,1333MHz_Maximum,
plabb1036,2,12,189.12,Gen8,6.6 (Santiago),Custom,Disabled,HP_Static_High_Performance_Mode,No_CStates,1333MHz_Maximum,
$
Needless to say function won't work for you as it is. You need to update it appropriately.
Related
I am using the below ansible playbook to check the root(/) size. if it's greater than 80%, then condition should be passed. But this is not working as expected. Can someone please help me with this.
tasks:
- name: Check the space of the root
shell: |
df -h / | awk -F" " '{print $5}' | sed 's/%//g' | tail -1
register: disk_output
- debug:
msg: "the root is greater than 80%"
when: disk_output.stdout > 80
Ansible uses Jinja2 tests and filters in conditionals.
You can use the int filter to convert the disk_output.stdout to integer and proceed to compare it with the 80 value:
- debug:
msg: "the root is greater than 80%"
when: disk_output.stdout | int > 80
cheers
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.
Good morning, everyone,
I am currently creating a Playbook that allows me to make an inventory of the different services on the Linux park of my company.
The problem is the following, none of my variables are interpreted. Here is the playbook in question :
- hosts: all
become: true
become_method: sudo
tasks:
- name: Get running process
shell: netstat -lntp
register: process
- name: Check Apache Packages
shell: yum list installed httpd | grep httpd | awk '{print $2}' | cut -d '-' -f1
register: Apache
when: process.stdout.find('httpd') != -1
- name: Check Tomcat Packages
shell: yum list installed tomcat | grep tomcat | awk '{print $2}' | cut -d '-' -f1
register: Tomcat
when: process.stdout.find('tomcat') != -1
- name: Check Mariadb / MySQL Packages
shell: yum list installed mariadb | grep mariadb | awk '{print $2}' | cut -d '-' -f1
register: Mariadb
when: process.stdout.find('mariadb') != -1 or process.stdout.find('mysql') != -1
- name: Write Output to CSV
blockinfile:
create: yes
path: /etc/ansible/projets/Inventoring/Output.csv
marker: ""
block: "{{ inventory_hostname }};Apache={{ Apache.stdout }};Tomcat={{ Tomcat.stdout }};Mariadb={{ Mariadb.stdout }}"
delegate_to: localhost
And an example of the CSV output that doesn't work:
{{ inventory_hostname }};OS={{ OSVersion.stdout }};Apache={{ Apache.stdout }};Tomcat={{ Tomcat.stdout }};Mariadb={{ Mariadb.stdout }}
Thank you in advance for your help.
I have a bash script under files which needs a variable to get passed from default\main1.yml. How can I declare the same?
roles
config
defaults
main1.yml
files
script.sh
tasks
main2.yml
defaults\main1.yml
log_group_name: "{{ lookup('env','LOG_GROUP') }}"
script.sh
for NAME in $(ls -1p /home/ec2-user/ |grep -v "^_" | grep -v "/$" |cut -d. -f1);
do
if ! grep $NAME /etc/awslogs/awslogs.conf;
then
sudo tee -a /etc/awslogs/awslogs.conf << END
[$DAGNAME]
datetime_format = %b %d %H:%M:%S
file = /var/log/airflow/$NAME/*/*/*.log
buffer_duration = 5000
log_stream_name = $NAME
initial_position = start_of_file
log_group_name = ${log_group_name}
END
fi
done
sudo service awslogsd start
sudo systemctl enable awslogsd
task/cloud.yml
---
- name: 'Cloud | AWSlogs.conf file'
when: inventory_hostname == 'master'
script: ../files/cloud.sh
- name: 'Cloud | AWSlogs.conf file'
when: inventory_hostname == 'worker_1'
script: ../files/cloud.sh
ansible command to execute the play:- deploy.py
execute(['sudo', '-E', 'ansible-playbook', 'ansible/plays/deploy.yml', '-i', 'hosts.yml'],
stdout=sys.stdout, stderr=sys.stderr, env=aug_env)
See if this works -
- hosts: localhost
tasks:
- shell: "script.sh {{ log_group_name }}"
You need to set the variable within the execution environment of the script, but it clearly already exists since you're pulling it out of the ENV so you don't even need to do that. If you did, you could install the script via the Ansible template module and execute the script that way.
https://docs.ansible.com/ansible/latest/user_guide/playbooks_environment.html
Simply use the script's parameter
script.sh
log_group_name=$1
...
and run the script with the parameter
roles/config/tasks/main2.yml
- command: "{{ role_path }}/files/script.sh {{ log_group_name }}"
You probably know, but just to be sure. This will work with the "localhost" only. In other cases, the script must be copied to the "remote hosts" first and the path to the script must be changed.
My intention with below playbook is to run a shell command only when it finds any of the value from disk_list. I need help to frame out when condition as told above.
---
- hosts: localhost
connection: local
gather_facts: false
tasks:
- set_fact:
disk_list:
- sda
- sdb
- sdc
- name: Get df -hT output to know XFS file system
shell: df -hT |grep xfs|awk '{print $1}'
register: df_result
- name: Run shell command on each XFS file system
shell: ls -l {{ item }} | awk '{print $1}'
with_items: "{{ df_result.stdout_lines }}"
when: "{{ disk_list.[] }} in {{ item }}"
BTW, in my system, "df_result" variable looks as below:
TASK [debug] ***************************************************************************************************************************************
ok: [localhost] => {
"df_result": {
"changed": true,
"cmd": "df -hT |grep xfs|awk '{print $1}'",
"delta": "0:00:00.017588",
"end": "2019-03-01 23:55:21.318871",
"failed": false,
"rc": 0,
"start": "2019-03-01 23:55:21.301283",
"stderr": "",
"stderr_lines": [],
"stdout": "/dev/sda3\n/dev/sda1",
"stdout_lines": [
"/dev/sda3",
"/dev/sda1"
]
}
}
Please help !
Some notes on the playbook.
For localhost, connection: local is implicit. So there is no technical need to specify this.
set_fact is correct, but in this case I believe is more appropiate to use "vars" at a play level instead of the set_fact module in a task. This also allows you to use "vars_files" for easier use of disk device lists.
I would reccomend to try to keep your task names "easy", as you may want to use the --start-at-task option at some point.
with_items is depricated in favor of "loop".
The conditional "when" works at a task level. So it will execute the task (for all its items) if the condition is met.
Here is a working version of what you need which also reflects the (5) recommendations made:
---
- hosts: localhost
gather_facts: false
vars:
disk_list:
- sda
- sdb
- sdc
tasks:
- name: cleans previous runs
shell: cat /dev/null > /tmp/df_result
- name: creates temporary file
shell: df -hT | grep ext4 | grep -i {{ item }} | awk '{print $1}' >> /tmp/df_result
loop: "{{ disk_list }}"
- name: creates variable
shell: cat /tmp/df_result
register: df_result
- name: shows info
shell: ls -l {{ item }} | awk '{print $1}'
loop: "{{ df_result.stdout_lines }}"
I basically just use a temporary file (/tmp/df_result) with the results you need already filtered by the "grep -i {{ item }}" used in the loop on task "creates temporary file". Then, the loop in "shows info" just iterates over an already clean list of items.
To see the result on screen, you could use "-v" option when running the playbook or if you want to save the result in a file, you could add " >> /tmp/df_final" at the end of the shell line in "shows info" task.
I accidently step on this post, which is kind of old. I'm sure you already fixed this in your environment, maybe you find a better way to do it, hopefully you did.
Regards,