ansible/scripts/logview
Francois Andrieu 71c8cedc00 logview: add an option to list all playbook ever run
Signed-off-by: Francois Andrieu <darknao@fedoraproject.org>
2021-01-05 23:34:01 +01:00

172 lines
6.2 KiB
Python
Executable file

#!/usr/bin/python3
# vim: et ts=4 ai sw=4 sts=0
import sys
import json
from optparse import OptionParser
import os
import glob
import gzip
from datetime import datetime, date, timedelta
import dateutil.parser as dateparser
import configparser
from ansible.config.manager import find_ini_config_file
from collections import Counter
logpath = '/var/log/ansible'
search_terms = ['CHANGED', 'FAILED']
date_terms = {
"today": date.today,
"yesterday": lambda: date.today() - timedelta(1),
}
def parse_info(infofile):
data = {}
with open(infofile) as f:
content = f.read()
obj_list = [x+'}' for x in content.split('\n}')]
plays = []
for obj in obj_list[:-1]:
js = json.loads(obj)
if 'play' in js:
plays.append(js)
else:
data.update(json.loads(obj))
data['plays'] = plays
return data
def date_cheat(datestr):
dc = date_terms.get(datestr, lambda: dateparser.parse(datestr))
return dc()
def parse_args(args):
usage = """
logview [options] [-d datestr] [-p playbook]
examples:
logview -d yesterday -l # lists playbooks run on that date
logview -s OK -s FAILED -d yesterday # list events from yesterday that failed or were ok
logview -s CHANGED -d yesterday -p mirrorlist # list events that changed from the mirrorlist playbook
logview -s ANY -d yesterday -p mirrorlist # list all events from the mirrorlist playbook
"""
parser = OptionParser(usage=usage)
parser.add_option("-d", default='today', dest='datestr', help="time string of when you want logs")
parser.add_option("-p", default='*', dest='playbook', help="the playbook you want to look for")
parser.add_option("-H", default=[], dest='hostname', action='append', help="Limit to the specified hostname")
parser.add_option("-v", default=False, dest='verbose', action='store_true', help='Verbose')
parser.add_option("-s", default=[], dest='search_terms', action='append', help="status to search for")
parser.add_option("-l", default=False, dest="list_pb", action='store_true', help="list playbooks for a specific date")
parser.add_option("-L", default=False, dest="list_all_pb", action='store_true', help="list all playbooks ever ran")
parser.add_option("--profile", default=False, dest="profile", action='store_true', help="output timing input per task")
(opts, args) = parser.parse_args(args)
opts.datestr = date_cheat(opts.datestr)
if not opts.search_terms:
opts.search_terms = search_terms
return opts, args
def search_logs(opts, logfiles):
msg = ''
for fn in sorted(logfiles):
hostname = os.path.basename(fn).replace('.log', '').replace('.gz', '')
timestamp = os.path.basename(os.path.dirname(fn))
if opts.hostname and hostname not in opts.hostname:
continue
try:
with gzip.open(fn) as f:
f.read()
open_f = gzip.open(fn, "rt")
except:
open_f = open(fn)
for line in open_f:
things = line.split('\t')
if len(things) < 5:
msg += "(logview error - unhandled line): %r\n" % line
continue
# See callback_plugins/logdetail.py for how these lines get created.
# MSG_FORMAT="%(now)s\t%(count)s\t%(category)s\t%(name)s\t%(data)s\n"
task_ts, count, category, name, data = things
if category in opts.search_terms or 'ANY' in opts.search_terms:
slurp = json.loads(data)
if opts.profile:
st = slurp.get('task_start', 0)
end = slurp.get('task_end', 0)
if st and end:
dur = '%.2f' % (float(end) - float(st))
else:
dur = None
msg += '%s\t%s\t%s\t%s\t%s\t%s' % (
timestamp, hostname, task_ts, count, category, name)
if not opts.verbose:
if type(slurp) == dict:
for term in ['task_userid', 'cmd']:
if term in slurp:
msg += '\t%s:%s' % (term, slurp.get(term, None))
if opts.profile and dur:
msg += '\t%s:%s' % ('dur', dur)
msg += '\n'
else:
if opts.profile and dur:
msg += '\t%s:%s' % ('dur', dur)
msg += '\n'
msg += json.dumps(slurp, indent=4)
msg += '\n'
return msg
def main(args):
cfg = find_ini_config_file()
if cfg:
cp = configparser.ConfigParser()
cp.read(cfg)
logpath = cp.get('callback_logdetail', "log_path", fallback="/var/log/ansible")
opts, args = parse_args(args)
if opts.list_pb or opts.list_all_pb:
for r,d,f in os.walk(logpath):
for file in f:
if file.endswith('.info'):
pb = parse_info(os.path.join(r,file))
pb_name = os.path.splitext(os.path.basename(pb['playbook']))[0]
pb_date = datetime.fromtimestamp(pb['playbook_start'])
if opts.list_all_pb or opts.datestr == pb_date.date():
stats = Counter()
for stat in pb['stats'].values():
del stat['task_userid']
stats += Counter(stat)
print("%s\tplaybook:%s\tran by:%s\tsummary: ok:%s chd:%s unr:%s faild:%s"
% (pb_date, pb_name, pb['userid'],
stats['ok'], stats['changed'], stats['unreachable'],
stats['failures']))
else:
for pb in glob.glob(os.path.join(logpath, opts.playbook)):
pb_name = os.path.basename(pb)
for pb_logdir in glob.glob(os.path.join(pb, opts.datestr.strftime("%Y/%m/%d"))):
logfiles = glob.glob(pb_logdir + '/*/*.log*')
msg = search_logs(opts, logfiles)
if msg:
print(pb_name)
print(msg)
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))