# -*- coding: utf-8 -*-
import ConfigParser
import os
import subprocess as sp
import logging
from datetime import datetime
import sys

today = datetime.now().strftime('%Y%m%d')
PKG_CONF = '/usr/local/easyops/pkg/conf'
PYTHON_TOOL_PATH = '/usr/local/easyops/python/tools'
RTIMEOUT = 60
DOWNLOAD_PATH = '/data/easyops/tmp/uc'
if not os.path.exists(DOWNLOAD_PATH):
    os.makedirs(DOWNLOAD_PATH)
DEFAULT_INSTALL_BASE = '/usr/local/easyops'
DEFAULT_DATA_BASE = '/data/easyops'

BACKUP_PATH = '/usr/local/easyops/uc-upgrade/backup/%s' % today
if not os.path.exists(BACKUP_PATH):
    os.makedirs(BACKUP_PATH)

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

easyops_file = "/root/.easyops"

current_path = os.path.dirname(os.path.abspath(__file__))
tools_path = os.path.join(os.path.dirname(current_path), 'tools')
loggers = {}


def init_logger(name, need_output=True):
    global loggers
    if loggers.get(name):
        return loggers.get(name)

    formatter = logging.Formatter('[%(asctime)s %(funcName)s L%(lineno)d %(levelname)s] %(message)s')
    logger = logging.getLogger(name)
    logger.setLevel(logging.DEBUG)

    log_folder = '%s/%s' % (LOG_PATH, today)
    if not os.path.exists(log_folder):
        os.makedirs(log_folder, 0o755)

    log_file = '%s/%s.log' % (log_folder, name)
    if need_output:
        handlers = [logging.StreamHandler(sys.stdout), logging.FileHandler(log_file)]
    else:
        handlers = [logging.FileHandler(log_file)]

    for handler in handlers:
        handler.setFormatter(formatter)
        logger.addHandler(handler)

    loggers[name] = logger
    return logger


def exec_cmd(cmd, shell=True, check_call=True, logger=None, need_env=True):
    env = None
    if need_env:
        env = os.environ.copy()
        env['PATH'] = '%s:%s' % (PYTHON_TOOL_PATH, env['PATH'])
    if logger is None:
        logger = init_logger('update')
    
    sh = ' '.join(cmd)
    logger.debug(sh)
    if shell:
        cmd = sh

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

    if check_call:
        if proc.returncode != 0:
            err_msg = u'%s failed, %s' % (cmd, err.decode('utf-8'))
            logger.error(err_msg)
            raise Exception(err_msg)
    proc.wait()

    return_msg = out or err
    try:
        return_msg = return_msg.decode('utf-8')
    except Exception as e:
        logger.error(e.message)
    return proc.returncode, return_msg


def get_version(version_file):
    if not os.path.exists(version_file):
        return ''

    lines = ''
    with open(version_file) as f:
        for line in f.readlines():
            lines += line

    version = lines.strip().split('\n')[-1]
    return version


def is_app_started(install_path):
    if install_path == '/data/easyops/agent':
        return True

    code, _ = exec_cmd(["cd %s && easyops status | grep started | grep '\[ok\]'" % install_path], check_call=False)
    if code == 0:
        # found ok
        return True
    else:
        return False


def is_valid_package(install_path):
    pkg_conf_path = os.path.join(PKG_CONF, os.path.basename(install_path))
    if os.path.exists(pkg_conf_path):
        return True
    code, _ = exec_cmd(["[[ -d {0} ]] && cd {0} && easyops which >/dev/null 2>&1".format(install_path)], check_call=False)
    if code == 0:
        # found ok
        return True
    else:
        return False


def set_elasticsearch55_resource():
    code, msg = exec_cmd(['sysctl vm.max_map_count'], check_call=False)
    if code == 0:
        _, _, count = msg.strip().split()
        max_map_count = int(count)
        # es 5.5
        if max_map_count < 262144:
            exec_cmd(["echo 'vm.max_map_count = 262144' >> /etc/sysctl.conf"])
            exec_cmd(['sysctl -p'])

    code, msg = exec_cmd(['sysctl vm.max_map_count'], check_call=False)
    if code == 0:
        _, _, count = msg.strip().split()
        max_map_count = int(count)
        if max_map_count < 262144:
            return False

    return True


def check_ulimit(max_limit, user):
    for option in ["-u", "-n"]:
        code, msg = exec_cmd(['su - {0} -c "ulimit {1}" -s /bin/bash'.format(user, option)], check_call=False)
        msg = msg.strip()
        if code == 0:
            if msg != "unlimited":
                nofile = int(msg)
                if nofile < max_limit:
                    err_msg = "ulimit {0}({1}) too small, please modify /etc/security/limits.conf and set it to {2} or above".format(option, nofile, max_limit)
                    update_logger.error(err_msg)
                    return False
        else:
            update_logger.error("get user root ulimit -n failed, {0}".format(msg))
            return False
    return True


def set_resource_limit():
    easyops_num = 100000

    if not check_ulimit(easyops_num, 'root'):
        code, msg = exec_cmd(["bash %s/set_ulimit.sh root" % tools_path], check_call=False)
        if code != 0:
            return False

    if not check_ulimit(easyops_num, 'easyops'):
        code, msg = exec_cmd(["bash %s/set_ulimit.sh easyops" % tools_path], check_call=False)
        if code != 0:
            return False

    ok = set_elasticsearch55_resource()
    return ok


def set_tcp_fast_recycling():
    sysctl_conf = "/etc/sysctl.conf"
    code, msg = exec_cmd(['grep "net.ipv4.tcp_tw_reuse = 1" %s' % sysctl_conf], check_call=False)
    if code != 0:
        exec_cmd(["echo 'net.ipv4.tcp_tw_reuse = 1' >> %s" % sysctl_conf], check_call=False)
        exec_cmd(["echo 'net.ipv4.tcp_tw_recycle = 1' >> %s" % sysctl_conf], check_call=False)
        exec_cmd(['sysctl -p'], check_call=False)


def link_essential_cmd():
    profile_file = "/etc/profile"
    cmd = 'grep "%s" %s' % (PYTHON_TOOL_PATH, profile_file)
    code, _ = exec_cmd([cmd], check_call=False)
    if code != 0:
        cmd = 'echo "export PATH=%s:$PATH" >> %s' % (PYTHON_TOOL_PATH, profile_file)
        exec_cmd([cmd], check_call=False, need_env=False)


def add_easyops_user():
    code, _ = exec_cmd(['id easyops'], check_call=False)
    if code != 0:
        exec_cmd(['useradd easyops -s /sbin/nologin'])


def make_basic_dir():
    data_path = '/data/easyops'
    if not os.path.exists(data_path):
        exec_cmd(['mkdir -p %s' % data_path])
        exec_cmd(['chown -R easyops:easyops %s' % data_path])
    else:
        code, msg = exec_cmd(['ls -ld %s |grep root' % data_path], check_call=False)
        if code == 0:
            exec_cmd(['chown -R easyops:easyops %s' % data_path])

    src_path = '/usr/local/easyops'
    if not os.path.exists(src_path):
        exec_cmd(['mkdir -p %s' % src_path])
        exec_cmd(['chown -R easyops:easyops %s' % src_path])
    else:
        code, msg = exec_cmd(['ls -ld %s |grep root' % src_path], check_call=False)
        if code == 0:
            exec_cmd(['chown -R easyops:easyops %s' % src_path])


def get_inner_ip():
    res = ''

    cmd = "ip route show | grep default | awk '{print $5}'"
    code, msg = exec_cmd([cmd], check_call=False)
    if code == 0:
        eth = msg.strip()
        cmd = "ifconfig %s | grep 'inet addr' | awk '{print $2}' | awk -F: '{print $2}'" % eth
        code, msg = exec_cmd([cmd], check_call=False)
        if code == 0:
            res = msg.strip()

    return res


def get_local_ip():
    res = ''
    sys_ini = '/usr/local/easyops/agent/easyAgent/conf/sysconf.ini'
    if os.path.exists(sys_ini):
        config = ConfigParser.ConfigParser()
        config.read(sys_ini)
        res = config.get('sys', 'local_ip')

    return res


def write_hostname(ip2hostname):
    host_file = "/etc/hosts"
    hosts = []
    flag_str = '# easyops begin line marked by easyuc'
    cmd = 'grep "%s" /etc/hosts' % flag_str
    code, _ = exec_cmd([cmd], check_call=False)
    if code != 0:
        hosts.append(flag_str)

    for ip, hostname in ip2hostname.iteritems():
        ip_hostname = '%s %s' % (ip, hostname)
        cmd = 'grep "%s" %s' % (ip_hostname, host_file)
        code, _ = exec_cmd([cmd], check_call=False)
        if code != 0:
            hosts.append(ip_hostname)

    localhost = '127.0.0.1 localhost'
    cmd = 'grep "%s" %s' % (localhost, host_file)
    code, _ = exec_cmd([cmd], check_call=False)
    if code != 0:
        hosts.append(localhost)

    str_hosts = '\\n'.join(hosts)
    cmd = 'echo -e "%s" >> %s' % (str_hosts, host_file)
    code, msg = exec_cmd([cmd], check_call=False)
    if code != 0:
        raise Exception('write hosts to /etc/hosts failed')

# init uclogger for event
uclogger = init_logger('event', need_output=False)
# init global update_logger
update_logger = init_logger('update')


def format_install_path(pkg_name, install_path):
    if install_path:
        return install_path
    if pkg_name == "agent":
        install_base = DEFAULT_DATA_BASE
    else:
        install_base = DEFAULT_INSTALL_BASE
    if pkg_name == "console-private":
        pkg_name = "console"
    return os.path.join(install_base, pkg_name)

def get_easyops_info():
    global easyops_file
    if os.path.exists(easyops_file):
        with open(easyops_file, 'r') as fp:
            easyops_info = fp.read()
    else:
        easyops_info = ""

    return easyops_info


def get_org():
    if not os.path.exists("/usr/local/easyops/deploy_init/easy_env.ini"):
        return 0
    code, org = exec_cmd(["/usr/local/easyops/deploy_init/tools/get_env.py common org"], check_call=False)
    if code != 0:
        raise Exception('easy_env get org failed')
    return int(org)
