#!/usr/bin/env python

from op5 import OP5
import argparse
import sys
import yaml #pip install PyYAML
import json
import os
import ast

import getpass
try:
    import keyring
except:
    pass

try:
    import argcomplete
except:
    pass

import logging
logger = logging.getLogger("op5")
logger.setLevel(logging.DEBUG)

fh = logging.FileHandler("/tmp/op5-cli.log")
fh.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - [%(filename)s:%(lineno)s - %(funcName)10s() ] %(message)s')
fh.setFormatter(formatter)

logger.addHandler(fh)

#the system exit codes are up to 255, so I can't use the full range of HTTP status codes as exit codes
#but I can still return something indicative of the HTTP status code received

def report(args):
    if not op5.report(args.query):
        return op5.status_code - 300 if op5.status_code >= 400 else 1
    else:
        print json.dumps(op5.data, sort_keys=True, indent=4)

def query(args):
    if not op5.filter("query", args.query):
        return op5.status_code - 300 if op5.status_code >= 400 else 1
    else:
        print json.dumps(op5.data, sort_keys=True, indent=4)

def querycount(args):
    if not op5.filter("count", args.query):
        return op5.status_code - 300 if op5.status_code >= 400 else 1
    else:
        print json.dumps(op5.data, sort_keys=True, indent=4)

def command(args):
    if not op5.command(args.command_type, args.json_data):
        return op5.status_code - 300 if op5.status_code >= 400 else 1
    else:
        print json.dumps(op5.data, sort_keys=True, indent=4)

def read(args):
    if not op5.read(args.object_type, args.object_name):
        return op5.status_code - 300 if op5.status_code >= 400 else 1
    else:
        print json.dumps(op5.data, sort_keys=True, indent=4)

def delete(args):
    if not op5.delete(args.object_type, args.object_name):
        return op5.status_code - 300 if op5.status_code >= 400 else 1
    else:
        print json.dumps(op5.data, sort_keys=True, indent=4)

def create(args):
    if not op5.create(args.object_type, args.json_data):
        return op5.status_code - 300 if op5.status_code >= 400 else 1
    else:
        print json.dumps(op5.data, sort_keys=True, indent=4)

def update(args):
    if not op5.update(args.object_type, args.object_name, args.json_data):
        return op5.status_code - 300 if op5.status_code >= 400 else 1
    else:
        print json.dumps(op5.data, sort_keys=True, indent=4)

def overwrite(args):
    if not op5.overwrite(args.object_type, args.object_name, args.json_data):
        return op5.status_code - 300 if op5.status_code >= 400 else 1
    else:
        print json.dumps(op5.data, sort_keys=True, indent=4)

def get_changes(args):
    if not op5.get_changes():
        return op5.status_code - 300 if op5.status_code >= 400 else 1
    else:
        print json.dumps(op5.data, sort_keys=True, indent=4)

def undo_changes(args):
    if not op5.undo_changes():
        return op5.status_code - 300 if op5.status_code >= 400 else 1
    else:
        print json.dumps(op5.data, sort_keys=True, indent=4)

def commit_changes(args):
    if not op5.commit_changes(True):
        return op5.status_code - 300 if op5.status_code >= 400 else 1
    else:
        print json.dumps(op5.data, sort_keys=True, indent=4)

def read_config_file():
    default_cfg_dir = "%s/.config/op5-cli" % os.path.expanduser("~")
    default_cfg_file = "%s/config.yaml" % default_cfg_dir

    if os.path.isfile(default_cfg_file):
        config_file = default_cfg_file
    # else create the default config path and create an example config here
    else:
        example_cfg = {'op5_url':'https://demo.op5.com/api', 'op5_username':'demo@op5.com', 'op5_password':'MonitorDemo123', '#READANDREMOVEME': 'Remove the definition of op5_password in this file to prompt for a password to store in your keyring instead'}
        if not os.path.isdir(default_cfg_dir):
            os.makedirs(default_cfg_dir)
        stream = file(default_cfg_file+".example", 'w')
        yaml.dump(example_cfg, stream, allow_unicode=True, default_flow_style=False)

        print "I could not find a config.yaml file, so I created an example "  \
              "in your home directory at %s/config.yaml.example.\nPlease "     \
              "rename this to config.yaml and add the OP5 REST API endpoint "  \
              "along with the authentication credentials." % default_cfg_dir
        sys.exit(0)
    try:
        config = yaml.load(file(config_file))
    except IOError:
        print 'Unable to open config file.  The path for the ' \
              'config file is ~/.config/op5-cli/config.yaml.'
        sys.exit(1)
    except Exception:
        print 'Unable to read config file.  YAML syntax issue, perhaps?'
        sys.exit(1)

    required_params = ["op5_url", "op5_username"]
    if not set(required_params).issubset(config.keys()):
        print "Required parameters not set: %s\n" % set(required_params) - set(config.keys)
        sys.exit(1)

    return config

def main():
    config = read_config_file()

    parser = argparse.ArgumentParser(description='A command-line interface for the OP5 monitoring system using its REST API')
    subparsers = parser.add_subparsers()

    valid_object_types = ["host", "hostgroup", "service", "servicegroup", "contact", "contactgroup", "host_template", "service_template", "contact_template", "hostdependency", "servicedependency", "hostescalation", "serviceescalation", "user", "usergroup", "combined_graph", "graph_collection", "graph_template", "management_pack", "timeperiod"]
    valid_command_types = ["ACKNOWLEDGE_HOST_PROBLEM","ACKNOWLEDGE_SVC_PROBLEM","PROCESS_HOST_CHECK_RESULT","PROCESS_SERVICE_CHECK_RESULT","SCHEDULE_AND_PROPAGATE_HOST_DOWNTIME","SCHEDULE_AND_PROPAGATE_TRIGGERED_HOST_DOWNTIME","SCHEDULE_HOST_CHECK","SCHEDULE_HOST_DOWNTIME","SCHEDULE_SVC_CHECK","SCHEDULE_SVC_DOWNTIME"]

    parser_query = subparsers.add_parser('query')
    parser_querycount = subparsers.add_parser('querycount')
    parser_report= subparsers.add_parser('report')
    parser_command = subparsers.add_parser('command')

    parser_create = subparsers.add_parser('create')
    parser_read = subparsers.add_parser('read')
    parser_update = subparsers.add_parser('update')
    parser_delete = subparsers.add_parser('delete')
    parser_overwrite = subparsers.add_parser('overwrite')

    for sp in [parser_query,parser_querycount,parser_report]:
        sp.add_argument('query', action='store', help='Query string')
    for sp in [parser_command]:
        sp.add_argument('command_type', action='store', choices=valid_command_types, help='Command type')
    for sp in [parser_create, parser_read, parser_update, parser_delete, parser_overwrite]:
        sp.add_argument('object_type', action='store', choices=valid_object_types, help='Object type')
    for sp in [parser_read, parser_update, parser_delete, parser_overwrite]:
        sp.add_argument('object_name', action='store', help='Object name')
    for sp in [parser_create, parser_update, parser_overwrite, parser_command]:
        sp.add_argument('--data', dest='json_data', action='store', type=str, required=True, help='Data to be sent in HTTP body in JSON format')

    parser_query.set_defaults(func=query)
    parser_querycount.set_defaults(func=querycount)
    parser_report.set_defaults(func=report)
    parser_command.set_defaults(func=command)

    parser_create.set_defaults(func=create)
    parser_read.set_defaults(func=read)
    parser_update.set_defaults(func=update)
    parser_delete.set_defaults(func=delete)
    parser_overwrite.set_defaults(func=overwrite)

    parser_getchanges = subparsers.add_parser('get-changes')
    parser_getchanges.set_defaults(func=get_changes)

    parser_undochanges = subparsers.add_parser('undo-changes')
    parser_undochanges.set_defaults(func=undo_changes)

    parser_commitchanges = subparsers.add_parser('commit-changes')
    parser_commitchanges.set_defaults(func=commit_changes)

    subparsers = [parser_create, parser_read, parser_update, parser_delete, parser_overwrite, parser_query, parser_querycount, parser_report, parser_command, parser_getchanges, parser_undochanges, parser_commitchanges]
    for sp in subparsers:
        sp.add_argument('--dryrun', dest='dryrun', action='store_true', default=False, help='If set, no create/update/delete/overwrite will be performed on the OP5 server')
        sp.add_argument('--debug', dest='debug', action='store_true', help='If set, output more text')

    if "argcomplete" in sys.modules:
        argcomplete.autocomplete(parser)

    if len(sys.argv) == 1:
        parser.print_help()
        sys.exit(1)

    try:
        args, unknown = parser.parse_known_args()
        if "json_data" in args:
            args.json_data=json.loads(args.json_data) #convert given string to JSON
        if not "op5_password" in config.keys():
            if "keyring" in sys.modules:
                password = keyring.get_password("op5-cli", config["op5_username"])
                if password:
                    config["op5_password"] = password
                else:
                    password = getpass.getpass("Enter password for user %s: " % config["op5_username"], stream=sys.stderr)
                    keyring.set_password("op5-cli", config["op5_username"], password)
                    config["op5_password"] = password
            else:
                config["op5_password"] = getpass.getpass("Enter password for user %s: " % config["op5_username"], stream=sys.stderr)
    except Exception as e:
        import pprint; pprint.pprint(e)
        return 1

    if len(unknown) > 0:
        print "INFO: Unknown arguments ignored:", unknown

    if args.debug:
        fh.setLevel(logging.DEBUG)

    global op5
    op5 = OP5(config["op5_url"], config["op5_username"], config["op5_password"], dryrun=args.dryrun, debug=args.debug, logtofile=True, interactive=True)
    return args.func(args)

if __name__ == "__main__":
    sys.exit(main())

