84

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.

1

9 Answers 9

126

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" ] } 
Sign up to request clarification or add additional context in comments.

7 Comments

how did you get it to pretty print the results? when i do it the output is all on one line.
accepted! this is basically geerlingguy's answer, but using stdout_lines instead of stdout gives it reasonably formatted output
Relevant documentation on the register action.
Is there any way to get stdout and stderr in one?
@PythonNut yes it is. You can just use: - debug: var="ps.stdout_lines + [ ps.stderr ]"
|
18

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).

2 Comments

it's a start, but the lines are output in what seems a completely random order?
Ansible will return stdout line-by-line without any sorting (I assume it just will run the different tasks and print the lines as they are received). To keep the lines from each host together, put something like |tr "\n" "~" at the end to put everything into one line and take that apart at the receiving end again
14

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 

3 Comments

this just prints "msg": "<cpu usage>" for the first debug task and "msg": "Hello world!" for the second one
For me, I get "msg": "12.7" for first debug entry (it just prints the first line of stdout), and the entire stdout printed for second debug entry. I ran the playbook against localhost (127.0.0.1).
@stijn "Hello world!" is the default value of msg. docs.ansible.com/ansible/debug_module.html
5

ANSIBLE_STDOUT_CALLBACK=debug ansible-playbook /tmp/foo.yml -vvv

Tasks with STDOUT will then have a section:

STDOUT: What ever was in STDOUT 

Comments

4

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

Comments

3

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'] }}" 

Comments

2

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

Comments

2

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

Comments

0

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.

5 Comments

The output isn't bad, but one of the points of ansible is I do not have to bother with hostlist/ssh/for loops etc. So I'm not going to drop the convenience of that for a function :P
You're not answering the question. "... using ansible-playbook"
+1 for pointing out that there may be a better/easier tool for the job. Just because Ansible is the latestgreatest doesn't mean we need to kill the chicken with a machine gun. Ansible is "used for IT tasks such as configuration management, application deployment, intraservice orchestration, and provisioning". The questioner simply wants to monitor his systems with ps output. Sorry, but Ansible ain't magic. You're going to need an inventory somewhere. And you'll need to "bother" with something- if not ssh, then Ansible. Finally, if a for loop is that hard, well... I guess there's Ansible.
Hi @MikeS Sure but whether Ansible is the correct tool for the job, I think is another question, to the more direct one of how to format Ansible output. He also might be using Ansible for something else so the overhead is worth it. That's a whole other discussion.
You're right. I guess I was reacting to stijn's comment, where he talks about how convenient Ansible is as a tool to frontend loops. I see so many comments about how easy Ansible is, and then we have a question like this, with all sorts of "...it didn't work for me..." comments and such. Just to reasonably print output? Yeesh, methinks Ansible is out of its league on this one. But only this one; you're right- he did ask about Ansible and far be it for me to judge his needs. I hate seeing that on StackOverflow, too.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.