diff --git a/ici b/ici new file mode 100755 index 0000000..d51fcaa --- /dev/null +++ b/ici @@ -0,0 +1,233 @@ +#!/usr/bin/env python +from operator import itemgetter +import json, socket, urllib2 +import sys, os, time +import argparse + +# hardcoded for now +monitoring_url = "194.177.210.168:1337" + + +# Hack to set a default subcommand (needed for status). +# +def set_default_subparser(self, name, args=None): + """default subparser selection. Call after setup, just before parse_args() + name: is the name of the subparser to call by default + args: if set is the argument list handed to parse_args() + , tested with 2.7, 3.2, 3.3, 3.4 + it works with 2.6 assuming argparse is installed + """ + subparser_found = False + for arg in sys.argv[1:]: + if arg in ['-h', '--help']: # global help if no subparser + break + else: + for x in self._subparsers._actions: + if not isinstance(x, argparse._SubParsersAction): + continue + for sp_name in x._name_parser_map.keys(): + if sp_name in sys.argv[1:]: + subparser_found = True + if not subparser_found: + # insert default in first position, this implies no + # global options without a sub_parsers specified + if args is None: + sys.argv.insert(1, name) + else: + args.insert(0, name) + +argparse.ArgumentParser.set_default_subparser = set_default_subparser + +def host_status(hostname,status_all): + # need to escape the \n in the URL + api_query = "http://" + monitoring_url + \ + "/query?q=GET%20hosts\\nFilter:%20alias%20=%20" + hostname + # handle livestatus-service being down + try: + req = urllib2.urlopen(api_query).read() + except URLError: + sys.exit("Could not contact livestatus-service.") + # handle getting garbage reply + try: + full = json.loads(req) + except ValueError: + sys.exit("Invalid livestatus request.") + # handle host not having parents registered in icinga + try: + parent_list = "[Parents: " + " ".join(set(full[0].get('parents'))) + "]" + except IndexError: + parent_list = "" + + print "Host: \033[92m" + full[0].get('display_name') + "\033[0m " + parent_list + print "Hostgroups: " + " ".join(full[0].get("groups")) + + # Flexible downtime could get messy here, because the could be an + # item in this list but no an active downtime on the host + if full[0].get('downtimes_with_info'): + print "\033[1;36mHost is in downtime: " + \ + full[0].get('downtimes_with_info')[0][2] + \ + "(" + full[0].get('downtimes_with_info')[0][1] + ")\033[0m" + + # This could also get tricky, i'm assuming the first comment is + # the one generated by icinga from the downtime command + print "\033[1;36m" + full[0].get('comments_with_info')[0][2].split(". ")[0] + "\033[0m" + + # Display full checks list (colors W/C/U) + status = full[0].get('services_with_fullstate') + print "Status: " + str(full[0].get('num_services_hard_warn')) + " warn, " + \ + str(full[0].get('num_services_hard_crit')) + " crit, " + \ + str(full[0].get('num_services_hard_unknown')) + \ + " unknown, " + str(full[0].get('num_services')) + " total." + + for i in sorted(status, key=itemgetter(0)): + if i[1] == 0: #OK + if status_all: + print ' - \033[92m%s\033[0m- \033[0;32m%s \033[0m' % (i[0], i[3]) + else: + pass + elif i[1] == 1: + print '\033\t[93m%s- %s \033[0m' % (i[0], i[3]) + elif i[1] == 2: + print '\033\t[91m%s- %s \033[0m' % (i[0], i[3]) + elif i[1] == 3: + print '\033\t[94m%s- %s \033[0m' % (i[0], i[3]) + +def downtime(hostname, duration, service, dt_type): + cur_time = int( time.time() ) + dest_time = cur_time + int(duration) + user=os.environ['USER'] + + # livestatus-service doesn't need the first time in brackets + if dt_type == 0: + ici_cmd = "SCHEDULE_SVC_DOWNTIME;" + hostname + ";" + service + ";" + str(cur_time) + ";" + \ + str(dest_time) + ";" + str(dt_type) + ";0;" + str(duration) + \ + ";" + user + ";downtime_by_script" + elif dt_type == 1: + ici_cmd = "SCHEDULE_HOST_SVC_DOWNTIME;" + hostname + ";" + str(cur_time) + ";" + \ + str(dest_time) + ";" + str(dt_type) + ";0;" + str(duration) + ";" + user + \ + ";downtime_by_script" + else: + ici_cmd = "SCHEDULE_SERVICEGROUP_SVC_DOWNTIME;" + service + ";" + str(cur_time) + ";" + \ + str(dest_time) + ";1;0;" + str(duration) + ";" + user + ";downtime_by_script" + + cmd_query = "http://" + monitoring_url + "/cmd?q=" + ici_cmd + req = urllib2.urlopen(cmd_query).read() + print req.rstrip("\n") + +def ack(hostname, service, notify, comment): + cur_time = int( time.time() ) + user=os.environ['USER'] + ici_cmd = "ACKNOWLEDGE_SVC_PROBLEM;" + hostname + ";" + service + \ + ";2;1;" + str(notify) + ";" + user + ";ACK%20By%20Script%20" + \ + comment.replace(" ","%20") + cmd_query = "http://" + monitoring_url + "/cmd?q=" + ici_cmd + req = urllib2.urlopen(cmd_query).read() + print req.rstrip("\n") + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + + # top-level parser. + parser = argparse.ArgumentParser(prog='ici') + subparsers = parser.add_subparsers(help='') + + # parser for status subcommand. + parser_a = subparsers.add_parser( + 'status', + help='Returns the status of a host' + ) + parser_a.set_defaults(function=host_status) + parser_a.add_argument( + "hostname", + help="The fqdn of the host to see it's status", + default=socket.getfqdn().strip(), + nargs='?' + ) + parser_a.add_argument( + "-a","--all", + help="Show the status of OK services too", + action="store_true" + ) + + # parser for downtime subcommand. + parser_b = subparsers.add_parser( + 'downtime', + help='schedules downtime for a host or a group of services' + ) + parser_b.set_defaults(function=downtime) + + # if a service group is defined then no hostname is needed. + group = parser_b.add_mutually_exclusive_group() + group.add_argument( + "hostname", + help="The fqdn of the host you want to schedule downtime", + default=socket.getfqdn().strip(), + nargs='?' + ) + group.add_argument( + "-g", "--service-group", + help="The group of service to be scheduled for downtime", + nargs=1 + ) + parser_b.add_argument( + "-t", "--downtime", + help = "Downtime in seconds", + nargs='?', + default=3600, + type=int + ) + parser_b.add_argument( + "-s", "--service", + help="Service Name", + nargs=1 + ) + + # parser for ack subcommand. + parser_c = subparsers.add_parser( + 'ack', + help='Acknowledge a problem' + ) + parser_c.set_defaults(function=ack) + parser_c.add_argument( + "hostname", + help="The fqdn of the host of the problem to acknowledge", + default=socket.getfqdn().strip(), + nargs='?' + ) + parser_c.add_argument( + "-s", "--service", + help="Service Name", + nargs=1 + ) + parser_c.add_argument( + "-n", "--notify", + help="Notify", + action='store_true' + ) + parser_c.add_argument( + "-c", "--comment", + help="Comment to accompany the ack", + nargs=1 + ) + + parser.set_default_subparser('status') + args = parser.parse_args() + + if args.function == host_status: + host_status(args.hostname, args.all) + elif args.function == downtime: + if args.service_group and args.service: + parser.error("You can either specify a hostname and a service to \ + downtime or a group of services.\n -s and -g are mutually exclusive.") + elif args.service: + downtime(args.hostname, args.downtime, args.service[0], 0) + elif args.service_group: + downtime("", args.downtime, args.service_group[0], 2) + else: + downtime(args.hostname, args.downtime, "", 1) + elif args.function == ack: + if args.notify is False: + notify = 0 + else: + notify = 1 + ack(args.hostname, args.service[0], notify, args.comment[0])