# -*- coding: utf-8 -*-
import traceback

import utils.common.log as logger
from utils.business.param_util import HandleParamCheck
from utils.business.param_util import ParamUtil as ParamService
from plugins.DistributedStorage.common.RestClient import StorageSSHClient
from plugins.DistributedStorage.common.UpgradeOperate import UpgradeOperate
from plugins.DistributedStorage.common.constants import UnifyPwdInfo
from utils.business.unite_password.unite_pwd_api import UnitePwdApi
from utils.business.project_util import ProjectApi
from utils.business.vm_util import can_vm_pinged_to
from utils.security import crypt
from utils.common.exception import HCCIException

COMPONENT_NAME = UnifyPwdInfo.COMPONENT_NAME
SUB_COMPONENT_NAME = UnifyPwdInfo.SUB_COMPONENT_NAME


def params_config(project_id):
    """
    插件参数动态获取接口函数
    中英文错误信息需要通过code2Msg()函数获取,入参为错误码，返回值为
        元组形式:中文错误信息，英文错误信息, 中文建议信息，英文建议信息
    查询所有已经校验的参数 调用ParamService()类中get_need_check_cloud_params(project_id, service_name)方法查询已经校验的参数
    """
    logger.info('Start parameter verification task.')
    config_ins = ParamsConfig(project_id)
    suit_info = config_ins.project_api.get_storage80_info(project_id)
    for suit_id, float_ip in suit_info.items():
        # 已经校验的参数float_ip,DM登录名、登陆密码
        already_check_list = ['FSM_{}_float_ip', 'FSM_{}_rest_user', 'FSM_{}_rest_pwd']
        already_check_params = [str(i).format(suit_id) for i in already_check_list]
        dynamic_check_list = ['FSM_{}_active_ip', 'FSM_{}_active_user',
                              'FSM_{}_active_pwd', 'FSM_{}_active_root_pwd',
                              'FSM_{}_standby_ip', 'FSM_{}_standby_user',
                              'FSM_{}_standby_pwd', 'FSM_{}_standby_root_pwd']
        dynamic_param_keys = [str(i).format(suit_id) for i in dynamic_check_list]
        # fsm ip、普通用户名、密码、root密码校验
        config_ins.get_dynamic_param(already_check_params, dynamic_param_keys)

    return config_ins.check_result


class ParamsConfig(object):
    def __init__(self, project_id):
        self.project_id = project_id
        self.db_opt = ParamService()
        self.project_api = ProjectApi()
        self.region_id = self.project_api.get_project_info(project_id).get("region_id")
        self.check_data = self.db_opt.get_need_check_cloud_params(project_id, 'DistributedStorage')
        self.check_result = HandleParamCheck()
        self.opr = None

    def _update_check_result(self, param_keys, status, error_msg=''):
        self.check_result.set_check_result(param_keys, status, error_msg)

    def get_dynamic_param(self, already_check_params, dynamic_param_keys):
        float_ip = self.check_data.get(already_check_params[0])
        username = self.check_data.get(already_check_params[1])
        password = self.check_data.get(already_check_params[2])
        port = '8088'
        fs_args = dict()
        fs_args["float_ip"] = float_ip
        fs_args["port"] = port
        self.opr = UpgradeOperate(fs_args)
        status_code, error_code, error_des = self.opr.try_login(
            username, password)
        if status_code != 200 or error_code != 0:
            err_msg = "login failed, Detail:[status:%s,code:%s]%s" \
                      % (status_code, error_code, error_des)
            logger.error(err_msg)
            self._update_check_result(dynamic_param_keys, 500,
                                      HCCIException(620055, err_msg))
            return
        ret_result, ret_data = self.opr.get_servers()
        fsm_ips = []
        for node in ret_data:
            if 'management' in node.get("role"):
                fsm_ips.append(node.get('management_ip'))
        if len(fsm_ips) != 2:
            err_msg = "Failed to obtain the fsm host IP"
            logger.error(err_msg)
            self._update_check_result(dynamic_param_keys, 500,
                                      HCCIException(620055, err_msg))
            return
        logger.info("Success to obtain the FSM IP: %s" % fsm_ips)
        logger.info("Start to get unified passwd!")
        check_data = self.get_dynamic_param_pwd(self.project_id, self.region_id, fsm_ips, dynamic_param_keys)
        if not check_data:
            err_msg = "Host[%s] unregistered unified pwd management or failed obtain the unified passwd" % fsm_ips
            logger.error(err_msg)
            self._update_check_result(dynamic_param_keys, 500,
                                      HCCIException(620055, err_msg))
            return
        active_check = self.check_fsm_connectivity(dynamic_param_keys[0:4], check_data, self.check_result)
        standby_check = self.check_fsm_connectivity(dynamic_param_keys[4:], check_data, self.check_result)
        if active_check and standby_check:
            self._update_cloud_params(self.db_opt, self.project_id, check_data)

    @staticmethod
    def _update_cloud_params(db_opt, project_id, check_data):
        data = list()
        for key, value in check_data.items():
            data.append({key: value})
        db_opt.update_user_input_cloud_params_by_key(project_id, data)

    def get_dynamic_param_pwd(self, project_id, region_id, fsm_ips, dynamic_param_keys):
        unite_pwd_api = UnitePwdApi(project_id)
        account_list = [('fsadmin', 1), ('root', 1)]
        accounts_info = unite_pwd_api.get_account_info(
            region_id, account_list, '', COMPONENT_NAME,
            SUB_COMPONENT_NAME)
        if not accounts_info.get('data'):
            err_msg = "Failed to obtain host:%s the unified passwd." % fsm_ips
            self._update_check_result(dynamic_param_keys, 500,
                                      HCCIException(620055, err_msg))
            return None
        data = dict()
        for account in accounts_info.get('data'):
            if account.get('ip') in fsm_ips:
                offset = fsm_ips.index(account.get('ip')) * 4
                if account.get("accountName") == 'root':
                    data[dynamic_param_keys[0 + offset]] = account.get('ip')
                    data[dynamic_param_keys[3 + offset]] = crypt.encrypt(account.get('newPasswd'))
                else:
                    data[dynamic_param_keys[1 + offset]] = account.get("accountName")
                    data[dynamic_param_keys[2 + offset]] = crypt.encrypt(account.get('newPasswd'))
        return data

    def check_fsm_connectivity(self, check_list, data_dict, check_result):
        if not self.check_ping(check_list, check_result, data_dict):
            return False
        if self.check_ssh_connectivity(check_list, data_dict,
                                       check_result):
            check_result.set_check_result(param_keys=check_list,
                                          status=200)
            return True
        return False

    @staticmethod
    def check_ssh_connectivity(check_list, data_dict, check_result):
        """
        :param data_dict:
        :param check_result:
        :param check_list:
        :return:
        """
        try:
            check_pass_flag = True
            # 组合请求数据
            logger.info('Start check ssh connectivity.')
            fs_args = dict()
            node_ip = data_dict.get(check_list[0])
            node_username = data_dict.get(check_list[1])
            node_password = crypt.decrypt(data_dict.get(check_list[2]))
            root_password = crypt.decrypt(data_dict.get(check_list[3]))
            fs_args['port'] = "22"
            try:
                ssh_client = StorageSSHClient(node_ip, node_username,
                                              node_password)
            except Exception as err:
                err_msg = "login {0} with {1} failed. exc {2}".format(
                    node_ip, node_username, err)
                raise HCCIException(621009, err_msg)
            try:
                cmd_ret = ssh_client.send_cmd('su -', 'assword:', 20)
                logger.info("rsp %s" % cmd_ret)
            except Exception:
                err_msg = "root password has expired. Host ip [%s]" % node_ip
                raise HCCIException(621010, err_msg)
            try:
                cmd_ret = ssh_client.send_cmd(root_password, '#', 20, sensitive=True)
                logger.info("rsp %s" % cmd_ret)
            except Exception:
                err_msg = "root user password is wrong. Host ip [%s]" % node_ip
                raise HCCIException(621010, err_msg)

        except HCCIException as err:
            logger.error(err)
            logger.error(traceback.format_exc())
            check_result.set_check_result(param_keys=check_list,
                                          status=500,
                                          error_msg=err)
            check_pass_flag = False
        except Exception as ex:
            logger.error(ex)
            logger.error(traceback.format_exc())
            check_result.set_check_result(param_keys=check_list,
                                          status=500,
                                          error_msg=HCCIException(620050, str(ex)))
            check_pass_flag = False
        finally:
            logger.info('check_ssh_connectivity end.')

        return check_pass_flag

    @staticmethod
    def check_ping(check_list, check_result, data_dict):
        check_pass_flag = True
        ip_addr = data_dict.get(check_list[0], None)
        if ip_addr is None:
            err_msg = "Failed to obtain the unified passwd"
            logger.error("Failed to obtain the unified passwd.")
            check_result.set_check_result(check_list,
                                          500,
                                          HCCIException(621014, err_msg))
            return False
        result = can_vm_pinged_to(ip_addr)
        if not result:
            logger.error("IP({}) is unreachable.".format(ip_addr))
            logger.error("check ping ip {ip_list} error, "
                         "error: {result}".format(ip_list=ip_addr,
                                                  result=result))
            check_result.set_check_result(check_list,
                                          500,
                                          HCCIException(620054, ip_addr))
            check_pass_flag = False
        return check_pass_flag
