Filter Salt Stack Return Data Output

Sometimes you only want to see what has changed, and that is OK.

Create a file like this:

filter.py

#!/usr/bin/python

from json import loads
from json import dumps

import fileinput

stdin_lines = [line for line in fileinput.input()]

ret = loads(''.join(stdin_lines))

for minion_id, data in ret.items():
    print(minion_id)
    print('='*len(minion_id))
    for key, value in ret[minion_id].items():
        if value['changes'] or value['result'] == False:
            print('')
            print(dumps(value, indent=4))
            print('')

Make the file executable:

chmod 755 filter.py

Execute your remote execution like this:

sudo salt-call --out=json state.highstate | ./filter.py

sudo salt '*' --out=json  --timeout=60 --static state.highstate | ./filter.py

The flags --timeout=60 and --static will cause the Salt command to block until the specified seconds for each minion to return results. We then pipe the returned JSON into our filter.py script to filter out only the changes and failures!

Profit!

Change the conditional depending on what you want. For example, for just failures do this:

if value['result'] == False:

Example output:

# sudo salt 'graphite.foxhop.net' --out=json --static --timeout=60 state.highstate | ./filter.py

graphite.foxhop.net
===================

{
    "comment": "File /tmp/taco updated",
    "__run_num__": 15,
    "changes": {
        "diff": "New file",
        "mode": "0640"
    },  
    "name": "/tmp/taco",
    "result": true
}   

4 thoughts on “Filter Salt Stack Return Data Output

  1. Thank you. I set up a local Salt on a server using salt-call, using your script in crontab, so for anyone else who might want to do the same thing:

    In order to run salt-call every 20 minutes from crontab and get mail when something gets changed, comment out line 13 and 14, the ones that print minion_id and “=” signs, and add this line to /etc/crontab:
    */20 * * * * root salt-call –local state.highstate –out=json -l quiet | /path/to/filter.py

  2. Thanks for the post, useful!

    I changed a little bit to have the parsing when a new line gets in (for long term operations) instead to wait for the end stream and then parse the data.

    BTW: I used the raw output.

    “`
    r = {
    “True” : “true”,
    “False” : “false”,
    “\”” : “\\\””,
    “\'” : “\””
    }

    for l in fileinput.input():
    for k in r: l = l.replace(k,r[k])
    j = loads(l)
    print j
    “`

Leave a Reply

Your email address will not be published. Required fields are marked *