#! /usr/bin/env python
"""
Net test controller

Copyright 2011 Red Hat, Inc.
Licensed under the GNU General Public License, version 2 as
published by the Free Software Foundation; see COPYING for details.
"""

__author__ = """
jpirko@redhat.com (Jiri Pirko)
"""

import getopt
import sys
import logging
import os
import re
import datetime
from lnst.Common.Logs import LoggingCtl, log_exc_traceback
from lnst.Common.Config import lnst_config
from lnst.Common.Colours import load_presets_from_config
from lnst.Common.Utils import mkdir_p
from lnst.Controller.NetTestController import NetTestController, NetTestError
from lnst.Controller.NetTestResultSerializer import NetTestResultSerializer

RETVAL_PASS = 0
RETVAL_FAIL = 1
RETVAL_ERR = 2
def usage(retval=0):
    """
    Print usage of this app
    """
    print "Usage: %s [OPTIONS...] ACTION [RECIPES...]" % sys.argv[0]
    print ""
    print "ACTION = [ run | config_only | match_setup ]"
    print ""
    print "OPTIONS"
    print "  -a, --define-alias name=value   define top-level alias"
    print "  -A, --override-alias name=value define top-level alias that " \
              "will override any other definitions in the recipe"
    print "  -c, --config=FILE               load additional config file"
    print "  -d, --debug                     emit debugging messages"
    print "  -h, --help                      print this message"
    print "  -m, --no-colours                disable coloured terminal output"
    print "  -o, --disable-pool-checks       don't check the availability of " \
              "machines in the pool"
    print "  -p, --packet-capture            capture and log all ongoing " \
              "network communication during the test"
    print "  -x, --result=FILE               file to write xml_result"
    sys.exit(retval)

def store_alias(alias_def, aliases_dict):
    try:
        name, value = alias_def.split("=")
    except:
        msg = "The alias definition '%s' not supported. The proper" + \
              " format is alias_name=alias_value."
        raise Exception(msg)

    if name in aliases_dict:
        msg = "The same alias %s was defined multiple times through CLI."
        logging.warning(msg)

    aliases_dict[name] = value

def process_recipe(action, file_path, res_serializer,
                   packet_capture, log_ctl, pool_checks,
                   defined_aliases, overriden_aliases):
    nettestctl = NetTestController(file_path, log_ctl,
                                   res_serializer=res_serializer,
                                   pool_checks=pool_checks,
                                   defined_aliases=defined_aliases,
                                   overriden_aliases=overriden_aliases)
    if action == "run":
        return nettestctl.run_recipe(packet_capture)
    elif action == "config_only":
        return nettestctl.config_only_recipe()
    elif action == "match_setup":
        return nettestctl.match_setup()

def get_recipe_result(args, file_path, res_serializer, packet_capture,
                      log_ctl, pool_checks, defined_aliases, overriden_aliases):
    res_serializer.add_recipe(file_path)
    log_ctl.set_recipe(file_path)

    retval = RETVAL_PASS

    res = {}
    try:
        res = process_recipe(args, file_path, res_serializer,
                             packet_capture, log_ctl, pool_checks,
                             defined_aliases, overriden_aliases)
    except Exception as err:
        log_exc_traceback()
        logging.error(err)
        res["passed"] = False
        res["err_msg"] = str(err)
        retval = RETVAL_ERR

    res_serializer.set_recipe_result(res)

    # The test failed, but don't override erro
    if not res["passed"] and retval < RETVAL_FAIL:
        retval = RETVAL_FAIL

    return (file_path, res, retval)

def main():
    """
    Main function
    """
    try:
        opts, args = getopt.getopt(
            sys.argv[1:],
            "dhr:c:x:poma:A:",
            ["debug", "help", "recipe=", "config", "result=",
             "packet-capture", "disable-pool-checks", "no-colours",
             "define_alias", "override_alias"]
        )
    except getopt.GetoptError as err:
        print str(err)
        usage(RETVAL_ERR)

    lnst_config.controller_init()
    dirname = os.path.dirname(sys.argv[0])
    gitcfg = os.path.join(dirname, "lnst-ctl.conf")
    if os.path.isfile(gitcfg):
        lnst_config.load_config(gitcfg)
    else:
        lnst_config.load_config('/etc/lnst-ctl.conf')

    usr_cfg = os.path.expanduser('~/.lnst/lnst-ctl.conf')
    if os.path.isfile(usr_cfg):
        lnst_config.load_config(usr_cfg)
    else:
        usr_cfg_dir = os.path.dirname(usr_cfg)
        pool_dir = usr_cfg_dir + "/pool"
        mkdir_p(pool_dir)
        lnst_config.set_option("environment", "pool_dirs", [pool_dir])
        with open(usr_cfg, 'w') as f:
            f.write(lnst_config.dump_config())

    debug = 0
    recipe_path = None
    result_path = None
    config_path = None
    packet_capture = False
    pool_checks = True
    coloured_output = True
    defined_aliases = {}
    overriden_aliases = {}
    for opt, arg in opts:
        if opt in ("-d", "--debug"):
            debug += 1
        elif opt in ("-h", "--help"):
            usage(RETVAL_PASS)
        elif opt in ("-c", "--config"):
            config_path = arg
        elif opt in ("-x", "--result"):
            result_path = arg
        elif opt in ("-p", "--packet-capture"):
            packet_capture = True
        elif opt in ("-o", "--disable-pool-checks"):
            pool_checks = False
        elif opt in ("-m", "--no-colours"):
            coloured_output = False
        elif opt in ("-a", "--define-alias"):
            store_alias(arg, defined_aliases)
        elif opt in ("-A", "--override-alias"):
            store_alias(arg, overriden_aliases)

    if config_path is not None:
        if not os.path.isfile(config_path):
            print "File '%s' doesn't exist!" % config_path
            usage(RETVAL_ERR)
        else:
            lnst_config.load_config(config_path)

    if coloured_output:
        coloured_output = not lnst_config.get_option("colours",
                                                     "disable_colours")

    load_presets_from_config(lnst_config)

    date = datetime.datetime.now().strftime("%Y-%m-%d_%H:%M:%S")
    log_ctl = LoggingCtl(debug,
                     log_dir=lnst_config.get_option('environment', 'log_dir'),
                     log_subdir=date, colours=coloured_output)

    if len(args) <= 0:
        logging.error("No action specified")
        usage(RETVAL_ERR)

    action = args[0]
    recipes = args[1:]
    if not action in ['run', 'config_only', 'match_setup']:
        logging.error("Action '%s' not recognised" % action)
        usage(RETVAL_ERR)

    summary = []

    retval = RETVAL_PASS
    res_serializer = NetTestResultSerializer()
    for recipe_path in recipes:
        if os.path.isdir(recipe_path):
            all_files = []
            for root, dirs, files in os.walk(recipe_path):
                dirs[:] = [] # do not walk subdirs
                all_files += files

            all_files.sort()
            for f in all_files:
                recipe_file = os.path.join(recipe_path, f)
                if re.match(r'^.*\.xml$', recipe_file):
                    logging.info("Processing recipe file \"%s\"" % recipe_file)
                    fp, res, rv = get_recipe_result(action, recipe_file,
                                                    res_serializer,
                                                    packet_capture,
                                                    log_ctl, pool_checks,
                                                    defined_aliases,
                                                    overriden_aliases)
        else:
            fp, res, rv = get_recipe_result(action, recipe_path,
                                            res_serializer,
                                            packet_capture,
                                            log_ctl, pool_checks,
                                            defined_aliases, overriden_aliases)

        summary.append((fp, res))
        if rv > retval:
            retval = rv

    log_ctl.set_recipe("", clean=False)

    res_serializer.print_summary()

    if result_path:
        result_path = os.path.expanduser(result_path)
        handle = open(result_path, "w")
        handle.write(res_serializer.get_result_xml())
        handle.close()

    return retval

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