import requests
import datetime
import utils.common.log as logger
from utils.business.project_util import ProjectApi
from utils.common.exception import FCUException
from utils.common.ssh_util import Ssh
from utils.common.param_check import check_param_ip
from utils.DBAdapter.DBConnector import BaseOps
from plugins.eBackup.common.util import get_ebackup_driver_nodes
from plugins.eBackup.common.util import Utils


def enable_other_params(check_result, all_params):
    for each in all_params:
        if -1 == str(check_result.check_list).find(each):
            check_result.set_check_result(param_keys=[each], status=200,
                                          error_msg="")


def get_password_expired_days(cmd_outputs):
    expired_days = 999
    try:
        for item in cmd_outputs:
            key = item.split(":")[0].strip()
            value = item.split(":")[1].strip()
            if key.lower() == 'password expires':
                expired_time = value.lower()
                if expired_time == 'never':
                    expired_days = 999
                else:
                    expired_days = \
                        (datetime.datetime.strptime(
                            expired_time, '%b %d, %Y') - datetime.datetime.
                            now()).days
                logger.info("The password will expire in " + str(expired_time))
                break
        expired_days += 1
        logger.info("The password will expire in %s days" % expired_days)
        return expired_days
    except Exception as e:
        logger.error("Get the expire time failed.The reason is:" + str(e))
        return expired_days


def check_ssh_login(server_ip, account, account_password,
                    account_root_password, goto_expire_days=7):
    # 0:success
    # 1:普通用户密码错误
    # 2:普通用户密码过期或者OS语言不是英语
    # 3:普通用户密码即将过期
    # 4:root用户密码错误
    ssh_client = None
    try:
        ssh_client = Ssh.ssh_create_client(server_ip,
                                           account,
                                           account_password)
        logger.info("Login %s successfully by user %s" % (server_ip, account))
    except Exception as e:
        logger.error("The password of the %s user is incorrect on host %s."
                     "The reason is:%s" % (server_ip, account, str(e)))
        Utils.close_ssh_clinet(ssh_client)
        return 1, account

    try:
        result = Ssh.ssh_send_command(ssh_client, 'su - root', 'Password:', 60)
        for sub_result in result:
            if "Account temporarily locked" in sub_result:
                logger.error("root has been locked.")
                return 5, "root"
        logger.info("Excute su - root on host %s successfully by user %s" %
                    (server_ip, account))
    except Exception as e:
        logger.error("Excute su - root on host %s failed."
                     "The reason is:%s" % (server_ip, str(e)))
        Utils.close_ssh_clinet(ssh_client)
        return 2, ""

    try:
        Ssh.ssh_send_command(ssh_client, account_root_password, '#', 60)
        logger.info("su - root successfuly on host " + server_ip)
    except Exception as e:
        logger.error("The password of the root user is incorrect on host %s."
                     "The reason is:%s" % (server_ip, str(e)))
        Utils.close_ssh_clinet(ssh_client)
        return 4, 'root'

    try:
        logger.info("Begin to check the age of the %s user." % account)
        cmd_output = Ssh.ssh_exec_command_return_list(ssh_client,
                                                      "chage -l %s" % account)
        expired_days = get_password_expired_days(cmd_output)
        if expired_days < 0:
            expired_days = 999
        if expired_days <= goto_expire_days:
            logger.info("The password of the %s user  will expire "
                        "in %s days." % (account, expired_days))
            return 3, str(expired_days)
        return 0, ""
    except Exception as e:
        logger.error("Check the the age of the %s user failed. The reason "
                     "is:%s" % (account, str(e)))
        return 0, ""
    finally:
        Utils.close_ssh_clinet(ssh_client)


def check_ebackup_nodes(check_result, params_key, nodes, account_password,
                        account_root_password,
                        unpassed_ordinary_user_nodes, unpassed_root_nodes,
                        password_goto_expire_msg, password_lock_msg):
    flag = True
    ip_list = nodes.replace(" ", "").replace("|", ";").split(";")

    for ip in ip_list:
        try:
            ret = check_param_ip(ip)
            if not ret:
                flag = False
                logger.error("Ip(%s) address format error" % ip)
                check_result.set_check_result(
                    param_keys=[params_key], status=500,
                    error_msg=FCUException(650034))
        except FCUException as error:
            check_result.set_check_result(
                param_keys=[params_key], status=500, error_msg=error)
    if flag:
        check_result.set_check_result(
            param_keys=[params_key], status=200, error_msg="")
        for ip in ip_list:
            ret_code, msg = check_ssh_login(ip, "hcp", account_password,
                                            account_root_password)
            if 0 != ret_code:
                flag = False
                if 1 == ret_code:
                    unpassed_ordinary_user_nodes.append(ip)
                elif 2 == ret_code:
                    unpassed_ordinary_user_nodes.append(ip)
                elif 3 == ret_code:
                    password_goto_expire_msg.update(
                        {ip: "The password will expire in %s days." % msg})
                elif 4 == ret_code:
                    unpassed_root_nodes.append(ip)
                elif 5 == ret_code:
                    password_lock_msg.update(
                        {ip: "root password has been locked."})
    return flag


def check_openstack_params(check_result, project_id, param_dict,
                           unpassed_fsp_nodes,
                           unpassed_root_nodes,
                           password_goto_expire_msg):
    database = BaseOps()
    region_type = database.get_region_type(project_id)
    reverse_proxy_ip = param_dict["openstack_reverse_proxy_ip"]
    logger.info("Begin to check openstack params in Region %s" % region_type)
    reverse_ips = []

    # 去级联场景需要校验级联层的密码
    reverse_ips.append(reverse_proxy_ip)

    for each_ip in reverse_ips:
        ret_code, msg = check_ssh_login(
            each_ip, "fsp",
            param_dict["openstack_fsp_pwd"],
            param_dict["openstack_root_pwd"])
        if 0 != ret_code:
            if 1 == ret_code:
                unpassed_fsp_nodes.append(each_ip)
            elif 2 == ret_code:
                unpassed_fsp_nodes.append(each_ip)
            elif 3 == ret_code:
                password_goto_expire_msg.update(
                    {each_ip: "The password will expire in %s days." % msg})
            elif 4 == ret_code:
                unpassed_root_nodes.append(each_ip)
            continue
        try:
            driver_host_dict = get_ebackup_driver_nodes(each_ip, param_dict)
            if 0 == len(driver_host_dict):
                continue
            driver_nodes = driver_host_dict["eBackupDriver"]
            [driver_nodes.append(ip) for ip in driver_host_dict["cpsnode"]
             if ip not in driver_nodes]
            for node in driver_nodes:
                ret_code, msg = check_ssh_login(
                    node, "fsp",
                    param_dict["openstack_fsp_pwd"],
                    param_dict["openstack_root_pwd"])
                if 1 == ret_code:
                    unpassed_fsp_nodes.append(each_ip)
                elif 2 == ret_code:
                    unpassed_fsp_nodes.append(each_ip)
                elif 3 == ret_code:
                    password_goto_expire_msg.update(
                        {each_ip: "The password will expire in %s days." %
                                  msg})
                elif 4 == ret_code:
                    unpassed_root_nodes.append(each_ip)

            check_result.set_check_result(
                param_keys=["openstack_cps_admin_pwd"],
                status=200, error_msg="")
        except Exception as ex:
            check_result.set_check_result(
                param_keys=["openstack_cps_admin_pwd"],
                status=500,
                error_msg=FCUException(650008, str(ex)))


def check_web_login(check_result, hosts_groups, hcp_password, root_password,
                    admin_password, role):
    web_ip = Utils.find_float_ip(hosts_groups, hcp_password, root_password)
    if web_ip == "":
        check_result.set_check_result(
            param_keys=["eBackup_admin_pwd"],
            status=500,
            error_msg=FCUException(650017, str(hosts_groups)))
        return False

    ip_version = Utils.check_ip_version(web_ip)
    try:
        token, session = Utils.login(web_ip, admin_password, ip_version)
        check_result.set_check_result(param_keys=["eBackup_admin_pwd"],
                                      status=200, error_msg="")
        return True
    except FCUException as e:
        check_result.set_check_result(param_keys=["eBackup_admin_pwd"],
                                      status=500, error_msg=e)
        return False
    except Exception as e:
        check_result.set_check_result(param_keys=["eBackup_admin_pwd"],
                                      status=500,
                                      error_msg=FCUException(650008, str(e)))
        return False
    finally:
        Utils.logout(web_ip, token, session, role, ip_version)


def check_iam_identify(check_result, workflow_hosts, hcp_password,
                       root_password, iam_user, iam_password, admin_password):
    ip = workflow_hosts[0]
    ssh_client = None
    check_keys = ["eBackup_iam_username", "eBackup_iam_pwd"]
    try:
        float_ip = Utils.find_float_ip(workflow_hosts, hcp_password,
                                       root_password)
        openstack_info_dict = Utils.query_openstack_info(float_ip,
                                                         admin_password)
        if not openstack_info_dict or "AUTHURL" not in openstack_info_dict:
            check_result.set_check_result(
                param_keys=check_keys,
                status=500,
                error_msg=FCUException(
                    650008, "Query iam domain name failed by eBackup "
                            "portal https://%s:8088" % float_ip))
            return False
        iam_identify_url = openstack_info_dict["AUTHURL"]
        identify_rul = "%s/v3/auth/tokens " % iam_identify_url
        identify_cmd = ''' curl -i -k -H "Content-Type:application/json" ''' \
                       '''-X POST -d '{"auth":{"identity":{"methods": [''' \
                       '''"password"],"password":{"user": {"name":"%s",''' \
                       '''"password": "%s","domain": {"name": ''' \
                       '''"op_service"}}}},"scope": {"domain": ''' \
                       '''{"name": "op_service"}}}}' %s ''' \
                       % (iam_user, iam_password, identify_rul)
        check_iam_cmd = '''sed -i '/op_service/ d' ~/.bash_history >''' \
                        '''/dev/null 2>&1;history -c >/dev/null 2>&1;'''
        ssh = Ssh()
        ssh_client = ssh.ssh_create_client(ip, "hcp", hcp_password)
        ssh.ssh_send_command(ssh_client, 'su - root', 'Password:', 100)
        ssh.ssh_send_command(ssh_client, root_password, '#', 100)
        result = ssh.ssh_exec_command_return_list(ssh_client, identify_cmd)
        ssh.ssh_send_command(
            ssh_client, check_iam_cmd, '#', 100)
        if -1 == str(result).find("X-Subject-Token:"):
            check_result.set_check_result(
                param_keys=check_keys,
                status=500,
                error_msg=FCUException(650012, ip, iam_identify_url))

            return False
        check_result.set_check_result(param_keys=check_keys, status=200,
                                      error_msg="")
        return True
    except Exception as e:
        check_result.set_check_result(param_keys=check_keys, status=500,
                                      error_msg=FCUException(650008, str(e)))
        return False
    finally:
        Utils.close_ssh_clinet(ssh_client)


def check_is_empty(params_dict, check_result, params_key_list, solution):
    ret = True
    check_list = params_key_list[:]
    logger.info("The solution name is:" + str(solution))
    for key in check_list:
        if params_dict[key] is None or 0 == len(params_dict[key]):
            ret = False
            check_result.set_check_result(param_keys=[key], status=500,
                                          error_msg=FCUException(650035))
    return ret


def check_connectivity(check_result, host_ip, hcp_pwd, root_pwd,
                       checked_address, key):
    ssh_client = None
    unpassed_ip = []
    for ip in host_ip:
        try:
            ping_cmd = 'ping -c2 %s >/dev/null && echo "Success"' % \
                       checked_address
            ssh_client = Ssh.ssh_create_client(ip, "hcp", hcp_pwd)
            Ssh.ssh_send_command(ssh_client, 'su - root', 'Password:', 100)
            Ssh.ssh_send_command(ssh_client, root_pwd, '#', 100)
            result = Ssh.ssh_exec_command_return_list(ssh_client, ping_cmd)
            if str(result).find("Success") < 0:
                unpassed_ip.append(ip)
        except Exception as e:
            check_result.set_check_result(param_keys=[key], status=500,
                                          error_msg=FCUException(650008,
                                                                 str(e)))
            return False
        finally:
            Utils.close_ssh_clinet(ssh_client)
    if 0 != len(unpassed_ip):
        check_result.set_check_result(param_keys=[key], status=500,
                                      error_msg=FCUException(650011, ';'.join(
                                          unpassed_ip), checked_address, key))
        return False
    else:
        check_result.set_check_result(param_keys=[key], status=200,
                                      error_msg="")
        return True


def is_exist_same_ips(check_result, key, host_ips):
    ip_list = []
    all_ip_list = host_ips.replace("|", ";").split(";")
    for ip in all_ip_list:
        if ip not in ip_list:
            ip_list.append(ip)
        else:
            check_result.set_check_result(param_keys=[key], status=500,
                                          error_msg=FCUException(650010))
            return False
    return True


def is_in_same_cluster(check_result, key, host_ips_list, hcp_password,
                       root_password, admin_password, role):
    try:
        logger.info(
            "Begin to check if the hosts %s are in the same cluster." % str(
                host_ips_list))
        web_ip = Utils.find_float_ip(host_ips_list,
                                     hcp_password,
                                     root_password)
        if 0 == len(web_ip):
            logger.error("Find float failed.")
            raise Exception("Find float failed.")
        ip_version = Utils.check_ip_version(web_ip)
        if ip_version == 4:
            url = "https://%s:8088/rest/dev/vbackup_server" % web_ip
        else:
            url = "https://[%s]:8088/rest/dev/vbackup_server" % web_ip
        token, session = Utils.login(web_ip, admin_password, ip_version)

        header = {
            'Accept': 'application/json;version=2.2;charset=UTF-8',
            'iBaseToken': token,
            'Accept-Language': 'en',
            'Cookie': 'language=en; %s;DEVICE_ID=dev;sessionIdleTime=60000;'
                      'MACHINEROLE=%s;CSRF_IBASE_TOKEN=%s'
                      % (session, role, token)
        }
        rsp = requests.get(url=url, headers=header, verify=False)
        if rsp.status_code == 200:
            rsp_dict = rsp.json()
            error_code = rsp_dict["error"]["code"]
            if error_code is None:
                logger.error("errcode is empty.")
                raise Exception("errcode is empty.")
            if error_code != 0:
                logger.error(
                    "Query alarm info failed on [%s],the description is:%s"
                    % (web_ip, rsp_dict["error"]["description"]))
                raise Exception(rsp_dict["error"]["description"])

            all_host = []
            for host in rsp_dict["data"]:
                if host["MANAGEMENTIP"] not in all_host:
                    all_host.append(host["MANAGEMENTIP"])
            logger.info(
                "The host ips by user inputing are:" + str(host_ips_list))
            logger.info("The host ips by querying are:" + str(all_host))

            if len(host_ips_list) == len(all_host):
                is_true = True
                for ip in host_ips_list:
                    if ip not in all_host:
                        is_true = False
                        break
                if is_true:
                    check_result.set_check_result(param_keys=[key], status=200,
                                                  error_msg="")
                    return True

            check_result.set_check_result(
                param_keys=[key],
                status=500,
                error_msg=FCUException(650020, ';'.join(host_ips_list),
                                       ';'.join(all_host), key))
            return False
        else:
            logger.error("Response status code is not 200.The url is" + url)
            raise Exception("Response status code is not 200.")
    except Exception as e:
        logger.error("Exception occurs:" + str(e))
        check_result.set_check_result(param_keys=[key], status=500,
                                      error_msg=FCUException(650008, str(e)))
        return False
    finally:
        Utils.logout(web_ip, token, session, role, ip_version)


def check_ebackup_role(check_result, key, host_ips_list, hcp_password,
                       root_password):
    try:
        roles = []
        unpassed_ips = []
        if key == 'eBackup_Workflow_nodes':
            roles = ['2', '3']
        elif key == 'eBackup_Datamover_nodes':
            roles = ['0', '1']
        else:
            logger.error("key %s is invalid." % key)
            raise FCUException(650008, "key %s is invalid." % key)
        query_role_cmd = '''cat /opt/huawei-data-protection/ebackup/conf''' \
                         '''/hcpconf.ini | grep MachineRole |awk -F "=" '''\
                         ''' '{print int($2)}' '''
        for ip in host_ips_list:
            ssh_client = None
            try:
                ssh_client = Ssh.ssh_create_client(ip, 'hcp', hcp_password)
                Ssh.ssh_send_command(ssh_client, 'su - root', 'Password:', 100)
                Ssh.ssh_send_command(ssh_client, root_password, '#', 100)
                result = Ssh.ssh_exec_command_return_list(ssh_client,
                                                          query_role_cmd)
                logger.info("The server role is %s of host %s" %
                            (str(result[0]), ip))
                role = result[0].strip().replace('\n', '')
                if role not in roles:
                    logger.error(
                        "The node[%s] is not a %s node" % (ip, key))
                    unpassed_ips.append(ip)
            except Exception as e:
                logger.error("Exception occurs: " + str(e))
                raise Exception(
                    "Exception occurs when check the role of"
                    " host %s.The reason is:%s" % (ip, str(e)))
            finally:
                Ssh.ssh_close(ssh_client)
        if 0 != len(unpassed_ips):
            logger.error('These nodes %s are not %s role node.' % (
                str(host_ips_list), key))
            check_result.set_check_result(
                param_keys=[key],
                status=500,
                error_msg=FCUException(650023, ';'.join(host_ips_list), key,
                                       key))
            return False
        else:
            check_result.set_check_result(param_keys=[key], status=200,
                                          error_msg="")
            return True
    except Exception as e:
        check_result.set_check_result(param_keys=[key], status=500,
                                      error_msg=FCUException(650008, str(e)))
        return False


def get_deploy_info_from_cmdb(project_id, region_id):
    # input: project_id
    # output: dict
    #    'eBackup_Datamover_nodes': {[]}
    #    'eBackup_Workflow_nodes':{[]}
    #    'openstack_reverse_proxy_ip': ''
    try:
        from utils.business.manageone_cmdb_util import ManageOneCmdbUtil
        logger.info("Begin to query ebackup deploy info from cmdb...")
        deploy_info_dict = {
            'eBackup_Datamover_nodes': {},
            'eBackup_Workflow_nodes': [],
            'openstack_reverse_proxy_ip': ''
        }
        cmdb = ManageOneCmdbUtil(project_id)
        ebackup_nodes_list = cmdb.get_deploy_node_info(region_id, 'eBackup')
        if 0 == len(ebackup_nodes_list):
            logger.error("Query ebackup nodes info failed.")
            raise Exception("Query ebackup nodes info failed.")
        for node in ebackup_nodes_list:
            if node["name"] in ["eBackup-Manager", "eBackup-Workflow"]:
                deploy_info_dict['eBackup_Workflow_nodes'].append(
                    node['ipAddresses'][0]['ip'])
            elif -1 != node["name"].find("eBackup_service"):
                group_name = node["name"][:-1] + 's'
                if group_name not in \
                        deploy_info_dict['eBackup_Datamover_nodes']:
                    deploy_info_dict['eBackup_Datamover_nodes'].update(
                        {group_name: [node['ipAddresses'][0]['ip']]})
                else:
                    deploy_info_dict['eBackup_Datamover_nodes'][
                        group_name].append(node['ipAddresses'][0]['ip'])
            else:
                logger.error("Parse ebackup nodes info failed.")
                raise Exception("Parse ebackup nodes info failed.")

        openstack_info = cmdb.get_cloud_service_info(region_id, 'OpenStack')
        if 0 == len(openstack_info):
            logger.error("Query openstack deploy info failed.")
            raise Exception("Query openstack deploy info failed.")

        for openstack in openstack_info:
            if openstack['indexName'] == 'OpenStack':
                for item in openstack['extendInfos']:
                    if -1 != str(item).find('openstack_reverse_proxy_ip'):
                        deploy_info_dict['openstack_reverse_proxy_ip'] = item[
                            'value']
                        break
                if 0 != len(deploy_info_dict['openstack_reverse_proxy_ip']):
                    logger.info(
                        "The openstack reverse proxy ip ""is:" + str(
                            deploy_info_dict['openstack_reverse_proxy_ip']))
                    break

        if 0 == len(deploy_info_dict['openstack_reverse_proxy_ip']):
            logger.error("Get openstack reverse proxy ip failed.")
            raise Exception("Get openstack reverse proxy ip failed.")

        logger.info(
            "Query ebackup deploy info completed: " + str(deploy_info_dict))
        return deploy_info_dict
    except Exception as e:
        logger.info(
            "Exception occurs when query ebackup deploy info: " + str(e))
        raise e


def init_params(check_data, db_opt, project_id, check_result, all_params):
    check_data.update(
        db_opt.get_need_check_cloud_params(project_id, "OpenStack"))
    check_data.update({"eBackup_Workflow_nodes": check_data[
        'eBackup_Workflow_nodes'].replace(",", ";")})
    check_data.update({"eBackup_Datamover_nodes": check_data[
        'eBackup_Datamover_nodes'].replace(",", ";")})
    check_data.update(
        {"current_region_id": ProjectApi().get_regionlist_by_project_Id(
            project_id)[0]})
    solution, version = ProjectApi.get_solution_and_version(project_id)
    logger.info("The solution name is:" + str(solution))
    is_true = check_is_empty(check_data, check_result, all_params, solution)
    if not is_true:
        return check_result
    is_true = is_exist_same_ips(check_result, "eBackup_Datamover_nodes",
                                check_data.get("eBackup_Datamover_nodes"))
    is_true = is_true and is_exist_same_ips(check_result,
                                            "eBackup_Workflow_nodes",
                                            check_data[
                                                "eBackup_Workflow_nodes"])
    if not is_true:
        return check_result


def datamover_web_login_check(check_data, check_result, hcp_password,
                              root_password, admin_password):
    for datamover_groups in \
            check_data.get("eBackup_Datamover_nodes").split("|"):
        datamover_hosts = datamover_groups.split(";")
        is_true = check_web_login(check_result, datamover_hosts,
                                  hcp_password, root_password,
                                  admin_password, 1)
        if is_true:
            b_result = is_in_same_cluster(
                check_result, 'eBackup_Datamover_nodes', datamover_hosts,
                hcp_password, root_password, admin_password, 1)
            if b_result:
                b_result = check_ebackup_role(
                    check_result, 'eBackup_Datamover_nodes', datamover_hosts,
                    hcp_password, root_password)
                if not b_result:
                    break
            else:
                break
        else:
            break


def check_workflow_web_log(check_data, check_result, all_params):
    hcp_password = check_data.get("eBackup_hcp_pwd")
    root_password = check_data.get("eBackup_root_pwd")
    admin_password = check_data.get("eBackup_admin_pwd")
    workflow_hosts = check_data.get("eBackup_Workflow_nodes").split(";")
    is_true = check_web_login(check_result, workflow_hosts, hcp_password,
                              root_password, admin_password, 2)
    if is_true:
        b_result = is_in_same_cluster(check_result, 'eBackup_Workflow_nodes',
                                      workflow_hosts, hcp_password,
                                      root_password, admin_password, 2)
        if b_result:
            check_ebackup_role(check_result, 'eBackup_Workflow_nodes',
                               workflow_hosts, hcp_password, root_password)
        datamover_web_login_check(check_data, check_result, hcp_password,
                                  root_password, admin_password)
    enable_other_params(check_result, all_params)
    return check_result
