__author__ = 'hzp'
import logging
import sys
import os
import os.path
import collections
from datetime import datetime
import subprocess
import yaml
import ConfigParser
import get_env
import set_env

current_path = os.path.dirname(os.path.abspath(__file__))
PROJECT_PATH = os.path.dirname(current_path)
easy_env_path = "{0}/../easy_env.ini".format(current_path)

if PROJECT_PATH not in sys.path:
    sys.path.append(PROJECT_PATH)

LOG_PATH = '/usr/local/easyops/deploy_init/log'
if not os.path.exists(LOG_PATH):
    os.makedirs(LOG_PATH)

today = datetime.now().strftime('%Y%m%d')
logger = logging.getLogger('mylog')
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(logging.Formatter('[%(asctime)s %(funcName)s L%(lineno)d %(levelname)s] %(message)s'))
logger.addHandler(handler)
fhandler = logging.FileHandler('%s/deploy_init-%s.log' % (LOG_PATH, today))
fhandler.setFormatter(logging.Formatter('[%(asctime)s %(funcName)s L%(lineno)d %(levelname)s] %(message)s'))
logger.addHandler(fhandler)


def get_app_to_ips():
    with open("{}/conf/cluster.yaml".format(PROJECT_PATH)) as f:
        cluster = yaml.load(f)
    config_parser = ConfigParser.ConfigParser()
    with open("{}/conf/easyops_hosts.ini".format(PROJECT_PATH)) as f:
        config_parser.readfp(f)
    app2ips = {}  # app name -> ip list
    for host_name, apps in cluster.iteritems():
        if len(cluster) == 1:
            ip = "127.0.0.1"
        else:
            ip = config_parser.get(host_name, "ip").strip()
        for app in apps:
            app2ips.setdefault(app, [])
            app2ips[app].append(ip)
    return app2ips


def set_key(section, key, value):
    logger.debug("writing config: [%s]%s = %s", section, key, value)
    return set_env.set_key(easy_env_path, section, key, value)


def get_key(section, key):
    logger.debug("reading config: [%s]%s", section, key)
    return get_env.get_key(easy_env_path, section, key)


def exec_cmd(cmd, shell=True, check_call=True):
    sh = ' '.join(cmd)
    logger.debug(sh)
    if shell:
        cmd = sh

    proc = subprocess.Popen(args=cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=shell)
    out, err = proc.communicate()
    if out:
        logger.info(out)
    if err:
        logger.error(err)

    if check_call:
        if proc.returncode != 0:
            raise Exception('%s failed, %s' % (cmd, err))

    return proc.returncode, out or err


def _get_inner_ip():
    cmd = "which ip >/dev/null 2>&1 && ip route show | grep default | awk '{print $5}'"
    code, msg = exec_cmd([cmd], check_call=False)
    if code == 0:
        eth = msg.strip()
        # centos6: `inet addr:192.168.100.1`, centos7: `inet 192.168.100.1`
        cmd = "ifconfig %s | grep 'inet ' | awk '{print $2}' | sed 's/addr://g'" % eth
        code, msg = exec_cmd([cmd], check_call=False)
        if code == 0:
            inner_ip = msg.strip()
            return inner_ip
        else:
            return None
    return None


def fill_local_ip():
    is_cluster = get_key("common", "is_cluster")
    if is_cluster == "false":
        return set_key("common", "local_ip", "127.0.0.1")
    else:
        inner_ip = _get_inner_ip()
        if not inner_ip:
            return False
        return set_key("common", "local_ip", inner_ip)


def fill_inner_ip():
    inner_ip = _get_inner_ip()
    return set_key("common", "inner_ip", inner_ip)


def _parse_easyops_hosts():
    parser = ConfigParser.ConfigParser()
    with open("{}/conf/easyops_hosts.ini".format(PROJECT_PATH)) as f:
        parser.readfp(f)
    return parser


def fill_mongodb():
    config_parser = _parse_easyops_hosts()
    data_ips = []
    arbiter_ip = ""
    for section in config_parser.sections():
        try:
            mongo_role = config_parser.get(section, "mongo_role")
        except Exception:
            continue
        if mongo_role == "primary":
            succ = set_key("mongodb", "primary_node", config_parser.get(section, "ip"))
            if not succ:
                return False
        if mongo_role in ("primary", "secondary"):
            data_ips.append(config_parser.get(section, "ip"))
        elif mongo_role == "arbiter":
            arbiter_ip = config_parser.get(section, "ip")
    if len(data_ips) > 1:
        succ = set_key("mongodb", "data_ip", '{}'.format(" ".join(data_ips)))
        if not succ:
            return False
        return set_key("mongodb", "arbiter_ip", arbiter_ip)
    elif len(data_ips) == 1:
        succ = set_key("mongodb", "data_ip", "127.0.0.1")
        if not succ:
            return False
        return set_key("mongodb", "arbiter_ip", "")
    else:
        raise Exception("get mongodb ip error")


def _fill_mysql(ips):
    parser = _parse_easyops_hosts()
    # collect roles
    roll_ips = collections.defaultdict(list)
    for section in parser.sections():
        try:
            role = parser.get(section, "mysql_role")
        except ConfigParser.NoOptionError:
            continue
        roll_ips[role].append(parser.get(section, "ip"))

    master_ips = roll_ips['master']
    if len(master_ips) != 1:
        raise ValueError("mysql master config invalid")
    master_ip = master_ips[0]
    if master_ip not in ips:
        raise ValueError("mysql master not in node list")

    ok = set_key("mysql", "master", master_ip)
    if not ok:
        logger.error("fill mysql.master fail.")
        sys.exit(255)

    ok = fill_node("mysql", ips)
    if not ok:
        logger.error("fill mysql.node fail.")
        sys.exit(255)


def fill_node(section_name, ips):
    for idx in range(len(ips)):
        succ = set_key(section_name, "node.{}".format(idx + 1), ips[idx])
        if not succ:
            return False
    return True


def _fill_redis(ips):
    succ = set_key("redis", "config.redis.master", ips[0])
    if not succ:
        logger.error("fill redis fail.")
        sys.exit(255)


def _fill_redis_sentinel(sentinel_ips):
    is_cluster = get_key("common", "is_cluster")
    if is_cluster == 'true' and sentinel_ips:
        redis_is_cluster = 'true'
    else:
        redis_is_cluster = 'false'
    succ = set_key("redis", "is_cluster", redis_is_cluster)
    if not succ:
        logger.error("fill redis is_cluster fail.")
        sys.exit(255)


def fill_storm_supervisor():
    is_cluster = get_key("common", "is_cluster")
    if is_cluster == "false":
        succ = set_key("storm_supervisor", "heap_space", "256M")
    else:
        succ = set_key("storm_supervisor", "heap_space", "2048M")
    if not succ:
        logger.error("fill storm supervisor fail.")
        sys.exit(255)


def fill_orientdb():
    is_cluster = get_key("common", "is_cluster")
    if is_cluster == "false":
        succ = set_key("orientdb", "opts_memory", "-Xms256m -Xmx512m -Dstorage.diskCache.bufferSize=512")
    else:
        succ = set_key("orientdb", "opts_memory", "-Xms2G -Xmx2G -Dstorage.diskCache.bufferSize=2048")
    if not succ:
        logger.error("fill orientdb fail.")
        sys.exit(255)


def fill_es():
    is_cluster = get_key("common", "is_cluster")
    if is_cluster == "false":
        succ = set_key("elasticsearch", "heap_space", "512M")
    else:
        succ = set_key("elasticsearch", "heap_space", "2048M")
    if not succ:
        logger.error("fill elasticsearch fail.")
        sys.exit(255)


if __name__ == "__main__":
    succ = fill_local_ip()
    if not succ:
        logger.error("fill local ip fail.")
        sys.exit(255)

    succ = fill_inner_ip()
    if not succ:
        logger.error("fill inner ip fail.")
        sys.exit(255)

    succ = fill_mongodb()
    if not succ:
        logger.error("fill mongodb fail.")
        sys.exit(255)

    app2ips = get_app_to_ips()

    def _fill_app_node(section_name):
        if section_name not in app2ips:
            return
        ok = fill_node(section_name, app2ips[section_name])
        if not ok:
            logger.error("fill %s fail.", section_name)
            sys.exit(255)

    _fill_app_node("ens_root")
    _fill_app_node("elasticsearch")
    _fill_app_node("kafka")
    _fill_app_node("influxdb")
    _fill_app_node("zookeeper")
    _fill_app_node("rabbitmq")
    _fill_app_node("storm_nimbus")
    _fill_app_node("orientdb")
    _fill_app_node("easy_core")

    _fill_mysql(app2ips["mysql"])
    _fill_redis(app2ips["redis"])
    _fill_redis_sentinel(app2ips.get("redis-sentinel"))

    fill_storm_supervisor()
    fill_orientdb()
    fill_es()

    sys.exit(0)
