#!/usr/bin/env python
#
# Copyright 2015: Mirantis Inc.
# All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

import errno
import json
import os
import pwd
import re
import shutil
import subprocess
import sys
import tempfile

from six.moves.urllib import parse


def use_keystone_v3():
    """Alter deployment to use keystone v3."""
    print("Changing deployment to v3")
    config = json.loads(subprocess.check_output(["rally", "deployment",
                                                 "config"]))
    v3_url = parse.urlsplit(config["auth_url"])._replace(path="v3").geturl()
    config["auth_url"] = v3_url
    endpoint = config.get("endpoint")
    if endpoint:
        v3_enpoint = parse.urlsplit(endpoint)._replace(path="v3").geturl()
        config["endpoint"] = v3_enpoint
    config["project_name"] = config["tenant"]
    config["project_domain_name"] = config["tenant"]
    cfg_file = tempfile.NamedTemporaryFile()
    json.dump(config, cfg_file)
    print("New config for keystone v3:")
    print(json.dumps(config, indent=2))
    cfg_file.flush()
    subprocess.call(["rally", "deployment", "create",
                     "--name", "V3", "--file", cfg_file.name])
    print(subprocess.check_output(["rally", "deployment", "check"]))

TAG_HANDLERS = {"v3": use_keystone_v3}


def perror(s):
    sys.stderr.write(s + "\n")
    sys.stderr.flush()


def run(cmd, stdout=None, gzip=True, check=False):
    """Run shell command.

    Save output to file, and gzip-compress if needed.
    If exit status is non-zero and check is True then raise exception.
    Return exit status otherwise.
    """
    print("Starting %s" % " ".join(cmd))
    status = subprocess.call(cmd, stdout=open(stdout, "w") if stdout else None)
    if stdout and gzip:
        subprocess.call(["gzip", "-9", stdout])
    if check and status:
        raise Exception("Failed with status %d" % status)
    return status


def run_task(task, tags=None):
    new_home_dir = tempfile.mkdtemp(prefix="rally_gate_")
    shutil.copytree(os.path.join(pwd.getpwuid(os.getuid()).pw_dir, ".rally"),
                    os.path.join(new_home_dir, ".rally"))
    print("Setting $HOME to %s" % new_home_dir)
    os.environ["HOME"] = new_home_dir
    for tag in tags or []:
        if tag == "args":
            continue
        if tag not in TAG_HANDLERS:
            perror("Warning! Unknown tag '%s'" % tag)
            continue
        try:
            TAG_HANDLERS[tag]()
        except Exception as e:
            perror("Error processing tag '%s': %s" % (tag, e))

    run(["rally", "task", "validate", "--task", task], check=True)
    cmd = ["rally", "task", "start", "--task", task]
    args_file, ext = task.rsplit(".", 1)
    args_file = args_file + "_args." + ext
    if os.path.isfile(args_file):
        cmd += ["--task-args-file", args_file]
    run(cmd, check=True)
    task_name = os.path.split(task)[-1]
    pub_dir = os.environ.get("RCI_PUB_DIR", "rally-plot")
    try:
        os.makedirs(os.path.join(pub_dir, "extra"))
    except Exception as e:
        if e.errno != errno.EEXIST:
            raise
    run(["rally", "task", "report", "--out",
         "%s/%s.html" % (pub_dir, task_name)])
    run(["rally", "task", "results"],
        stdout="%s/results-%s.json" % (pub_dir, task_name))
    status = run(["rally", "task", "sla_check"],
                 stdout="%s/%s.sla.txt" % (pub_dir, task_name))
    run(["rally", "task", "detailed"],
        stdout="rally-plot/detailed-%s.txt" % task_name)
    run(["rally", "task", "detailed", "--iterations-data"],
        stdout="rally-plot/detailed_with_iterations-%s.txt" % task_name)

    return status


def get_name_from_git():
    """Determine org/project name from git."""
    r = re.compile(".*/(.*?)/(.*?).git$")
    for l in open(".git/config"):
        m = r.match(l.strip())
        if m:
            return m.groups()
    raise Exception("Unable to get project name from git")


def get_project_name():
    for var in ("ZUUL_PROJECT", "GERRIT_PROJECT"):
        if var in os.environ:
            return os.environ[var].split("/")
    return get_name_from_git()


def main():
    statuses = []
    org, project = get_project_name()

    base = os.environ.get("BASE")
    if base:
        base_jobs_dir = os.path.join(base, "new", project)
    else:
        base_jobs_dir = os.path.realpath(".")

    rally_root = "/home/rally/rally/"
    if not os.path.exists(rally_root):
        rally_root = os.environ["BASE"] + "/new/rally/"

    jobs_dir = os.path.join(base_jobs_dir, "rally-jobs")
    if not os.path.exists(jobs_dir):
        # fallback to legacy path
        jobs_dir = os.path.join(base_jobs_dir, "rally-scenarios")
    if not os.path.exists(jobs_dir):
        raise Exception("Rally jobs directory does not exist.")

    for directory in ("plugins", "extra"):
        dst = os.path.expanduser("~/.rally/%s" % directory)
        try:
            shutil.copytree(os.path.join(jobs_dir, directory), dst)
        except OSError as e:
            if e.errno != errno.EEXIST:
                raise

    scenario = os.environ.get("RALLY_SCENARIO", project + ".yaml")
    scenario_name, scenario_ext = scenario.split(".")
    print("Processing scenario %s" % scenario)

    for fname in os.listdir(jobs_dir):
        print("Processing %s" % fname)
        if fname.startswith(scenario_name):
            tags = fname[len(scenario_name):-len(scenario_ext) - 1].split("_")
            statuses.append(run_task(os.path.join(jobs_dir, fname), tags))
        else:
            print("Ignoring file %s" % fname)
    print("Exit statuses: %r" % statuses)

    run(["python", rally_root + "/rally/ui/utils.py", "render",
         "tests/ci/rally-gate/index.mako"],
        gzip=False, stdout="rally-plot/extra/index.html")

    return any(statuses)


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