# -*- coding: utf-8 -*-
import datetime
import re
import threading
import time

import utils.common.log as logger
from utils.common.ssh_util import Ssh
from utils.common.message import Message
from utils.common.exception import HCCIException
from utils.common.fic_base import StepBaseInterface
from utils.DBAdapter.DBConnector import BaseOps
from utils.common.error.hcci_error_code import get_code_msg
from plugins.DistributedStorage.logic.InstallOperate import InstallOperate
from plugins.DistributedStorage.utils.common.DeployConstant import DeployConstant


class CheckNodeEulerOSVersionInf(StepBaseInterface):
    def __init__(self, project_id, pod_id):
        super().__init__(project_id, pod_id)
        self.project_id = project_id
        self.pod_id = pod_id

    def pre_check(self, project_id, pod_id):
        """
        插件内部接口：
        :param project_id:
        :param pod_id:
        :return:
        """
        return Message()

    def execute(self, project_id, pod_id):
        """
        标准调用接口：
        :param project_id:
        :param pod_id:
        :return:Message类对象
        """
        try:
            EulerOSVersionCheck(self.project_id, self.pod_id).procedure()
        except HCCIException as e1:
            return Message(500, e1)
        except Exception as e2:
            return Message(500, e2)
        return Message(200)

    def rollback(self, project_id, pod_id):
        """
        标准调用接口：执行回滚
        :param project_id:
        :param pod_id:
        :return:Message类对象
        """
        return Message()

    def retry(self, project_id, pod_id):
        """
        标准调用接口：重试
        :return: Message类对象
        """
        return self.execute(project_id, pod_id)

    def check(self, project_id, pod_id):
        """
        标准调用接口：重试
        :param project_id:
        :param pod_id:
        :return:
        """
        return Message()


class EulerOSVersionCheck(object):
    def __init__(self, project_id, pod_id):
        self.project_id = project_id
        self.pod_id = pod_id
        self.db = BaseOps()
        self.kernel = DeployConstant.KERNEL_PATTERN
        self.check_res = {'version_error': [], 'ssh_request_error': [], 'execute_cmd_error': [], 'timeout_error': []}

    def procedure(self):
        """
        分布式存储分离部署手动装机场景 检查存储节点和复制节点欧拉版本
        """
        node_info = self.db.get_install_os_list_info(self.pod_id) + self.db.get_install_os_list_info(self.pod_id, 'rep')
        if not node_info:
            logger.info('No storage node exists, pass')
            return
        logger.info('Get node account info')
        node_dic = self.get_node_account_info(node_info)
        logger.info('Check EulerOS Version')
        self.parallel_query_kernel(node_dic)
        logger.info('Check result:{}'.format(self.check_res))
        error_lst = []
        if self.check_res['version_error']:
            err_msg = get_code_msg(627620) % (self.check_res['version_error'], self.kernel)
            error_lst.append(err_msg)
        if self.check_res['ssh_request_error']:
            err_msg = get_code_msg(627621) % self.check_res['ssh_request_error']
            error_lst.append(err_msg)
        if self.check_res['execute_cmd_error']:
            err_msg = get_code_msg(627616) % self.check_res['execute_cmd_error']
            error_lst.append(err_msg)
        if self.check_res['timeout_error']:
            err_msg = get_code_msg(627619) % self.check_res['timeout_error']
            error_lst.append(err_msg)
        if error_lst:
            err = '\n'.join(error_lst)
            logger.error('ERROR:{}'.format(err))
            raise Exception(err)

    def get_node_account_info(self, node_info):
        """
        检查并获取节点IP、用户名、密码
        """
        logger.info('Querying node information')
        node_dic = dict()
        error_account_node = set()
        for node in node_info:
            manage_ip = node.get('manageIp')
            if not manage_ip:
                error_account_node.add(node.get('bmc_ip'))
                continue
            create_user = node.get('creuser')
            if not self._check_sys_account(create_user):
                error_account_node.add(node.get('bmc_ip'))
                continue
            _, root_pwd, user, user_pwd = create_user.split(',')
            node_dic[manage_ip] = (manage_ip, user, user_pwd, root_pwd)
        if error_account_node:
            logger.error('The parameters of manage_ip or creuser are incorrect, details:{}'.format(error_account_node))
            raise HCCIException(627617, error_account_node)
        return node_dic

    @staticmethod
    def _check_sys_account(sys_account: str):
        if not sys_account:
            return False
        sys_account_lst = sys_account.split(',')
        if len(sys_account_lst) != 4 or sys_account_lst[0] != 'root':
            return False
        return True

    def parallel_query_kernel(self, node_dic: dict, parallel_num=20):
        """
        检查Euler版本
        """
        logger.info('Checking the Euler version in parallel')
        parallel_thread_dic = dict()
        for manage_ip, node_info in node_dic.items():
            logger.info('Create a new thread to check EulerOS version, node:{}'.format(manage_ip))
            t = threading.Thread(target=self.check_kernel_version, args=node_info, daemon=True)
            t.start()
            parallel_thread_dic[manage_ip] = t, datetime.datetime.now()
            while len(parallel_thread_dic) == parallel_num:
                time.sleep(1)
                self._check_thread(parallel_thread_dic)
        logger.info('Check thread status')
        while parallel_thread_dic:
            time.sleep(1)
            self._check_thread(parallel_thread_dic)

    def _check_thread(self, parallel_thread_dic, timeout=20):
        check_dic = parallel_thread_dic.copy()
        for cur_ip, (cur_t, cur_time_stamp) in check_dic.items():
            if not cur_t.is_alive():
                parallel_thread_dic.pop(cur_ip)
                continue
            if (datetime.datetime.now() - cur_time_stamp).total_seconds() > timeout:
                self.check_res['timeout_error'].append(cur_ip)
                logger.info('node:{}, thread status:{}'.format(cur_ip, cur_t.is_alive()))
                parallel_thread_dic.pop(cur_ip)

    def check_kernel_version(self, management_plane_ip, user, user_pwd, root_pwd):
        logger.info('Start to query the kernel, node ip: {}'.format(management_plane_ip))
        try:
            ssh_client = InstallOperate.create_ssh_root_client(management_plane_ip, user, user_pwd, root_pwd)
        except Exception as e:
            logger.error('Connection setup failure, node:{}, details:{}'.format(management_plane_ip, e))
            self.check_res['ssh_request_error'].append(management_plane_ip)
            raise HCCIException(627621, management_plane_ip)
        try:
            ret_cmd_lst = Ssh.ssh_exec_command_return(ssh_client, 'uname -r')
        except Exception as e:
            logger.error('Failed to execute the uname -r command,node:{}, details:{}'.format(management_plane_ip, e))
            self.check_res['execute_cmd_error'].append(management_plane_ip)
            raise HCCIException(627616, management_plane_ip)
        finally:
            Ssh.ssh_close(ssh_client)
        logger.info('Node ip: {}, kernel:{}'.format(management_plane_ip, ret_cmd_lst))
        if not re.search('{}+'.format(self.kernel), str(ret_cmd_lst)):
            logger.error('The current kernel [{}] does not match the kernel [{}]'.format(ret_cmd_lst, self.kernel))
            self.check_res['version_error'].append(management_plane_ip)
        Ssh.ssh_close(ssh_client)
