#!/usr/bin/env python3

"""Launch forwarder script for Faucet/Gauge"""

# Copyright (C) 2015 Brad Cowie, Christopher Lorier and Joe Stringer.
# Copyright (C) 2015 Research and Education Advanced Network New Zealand Ltd.
# Copyright (C) 2015--2019 The Contributors
#
# 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 argparse
import os
import sys

from pbr.version import VersionInfo

if sys.version_info < (3,) or sys.version_info < (3, 5):
    raise ImportError(
        """You are trying to run faucet on python {py}

Faucet is not compatible with python {py}, please upgrade to python 3.5 or newer.""".format(
            py=".".join([str(v) for v in sys.version_info[:3]])
        )
    )

RYU_OPTIONAL_ARGS = [
    ("ca-certs", "CA certificates"),
    (
        "config-dir",
        """Path to a config directory to pull `*.conf` files
                      from. This file set is sorted, so as to provide a
                      predictable parse order if individual options are
                      over-ridden. The set is parsed after the file(s)
                      specified via previous --config-file, arguments hence
                      over-ridden options in the directory take precedence.""",
    ),
    (
        "config-file",
        """Path to a config file to use. Multiple config files
                       can be specified, with values in later files taking
                       precedence. Defaults to None.""",
        "/etc/faucet/ryu.conf",
    ),
    ("ctl-cert", "controller certificate"),
    ("ctl-privkey", "controller private key"),
    ("default-log-level", "default log level"),
    ("log-config-file", "Path to a logging config file to use"),
    ("log-dir", "log file directory"),
    ("log-file", "log file name"),
    ("log-file-mode", "default log file permission"),
    ("observe-links", "observe link discovery events"),
    ("ofp-listen-host", "openflow listen host (default 0.0.0.0)"),
    ("ofp-ssl-listen-port", "openflow ssl listen port (default: 6653)"),
    (
        "ofp-switch-address-list",
        """list of IP address and port pairs (default empty).
                                   e.g., "127.0.0.1:6653,[::1]:6653""",
    ),
    (
        "ofp-switch-connect-interval",
        "interval in seconds to connect to switches (default 1)",
    ),
    ("ofp-tcp-listen-port", "openflow tcp listen port (default: 6653)"),
    ("pid-file", "pid file name"),
    ("user-flags", "Additional flags file for user applications"),
]


def parse_args(sys_args):
    """Parse Faucet/Gauge arguments.

    Returns:
        argparse.Namespace: command line arguments
    """

    args = argparse.ArgumentParser(prog="faucet", description="Faucet SDN Controller")
    args.add_argument("--gauge", action="store_true", help="run Gauge instead")
    args.add_argument(
        "-v", "--verbose", action="store_true", help="produce verbose output"
    )
    args.add_argument(
        "-V", "--version", action="store_true", help="print version and exit"
    )
    args.add_argument("--use-stderr", action="store_true", help="log to standard error")
    args.add_argument("--use-syslog", action="store_true", help="output to syslog")
    args.add_argument(
        "--ryu-app-lists",
        action="append",
        help="add Ryu app (can be specified multiple times)",
        metavar="APP",
    )

    for ryu_arg in RYU_OPTIONAL_ARGS:
        if len(ryu_arg) >= 3:
            args.add_argument(
                "--ryu-%s" % ryu_arg[0], help=ryu_arg[1], default=ryu_arg[2]
            )
        else:
            args.add_argument("--ryu-%s" % ryu_arg[0], help=ryu_arg[1])

    return args.parse_args(sys_args)


def print_version():
    """Print version number and exit."""
    version = VersionInfo("c65faucet").semantic_version().release_string()
    message = "c65faucet %s" % version
    print(message)


def build_ryu_args(argv):
    args = parse_args(argv[1:])

    # Checking version number?
    if args.version:
        print_version()
        return []

    prog = os.path.basename(argv[0])
    ryu_args = []

    # Handle log location
    if args.use_stderr:
        ryu_args.append("--use-stderr")
    if args.use_syslog:
        ryu_args.append("--use-syslog")

    # Verbose output?
    if args.verbose:
        ryu_args.append("--verbose")

    for arg, val in vars(args).items():
        if not val or not arg.startswith("ryu"):
            continue
        if arg == "ryu_app_lists":
            continue
        if arg == "ryu_config_file" and not os.path.isfile(val):
            continue
        arg_name = arg.replace("ryu_", "").replace("_", "-")
        ryu_args.append("--%s=%s" % (arg_name, val))

    # Running Faucet or Gauge?
    if args.gauge or os.path.basename(prog) == "gauge":
        ryu_args.append("faucet.gauge")
    else:
        ryu_args.append("faucet.faucet")

    # Check for additional Ryu apps.
    if args.ryu_app_lists:
        ryu_args.extend(args.ryu_app_lists)

    # Replace current process with ryu-manager from PATH (no PID change).
    ryu_args.insert(0, "osken-manager")
    return ryu_args


def main():
    """Main program."""
    ryu_args = build_ryu_args(sys.argv)
    if ryu_args:
        os.execvp(ryu_args[0], ryu_args)


if __name__ == "__main__":
    main()
