# -*- coding: utf-8 -*-
import traceback
import time
from utils.common.fic_base import TestCase
import utils.common.log as logger
from utils.common.message import Message
from utils.common.exception import FCDException
from plugins.DistributedStorage.scripts.logic.DeployOperate import DeployOperate
from plugins.DistributedStorage.scripts.logic.InstallOperate import InstallOperate
from plugins.DistributedStorage.scripts.utils.common.DeployConstant import DeployConstant


class ConfigExternalKMS(TestCase):
    def __init__(self, project_id, pod_id, fs_args):
        super(ConfigExternalKMS, self).__init__(project_id, pod_id)
        self.fs_args = fs_args
        self.opr = DeployOperate(fs_args)
        self.username = DeployConstant.DM_LOGIN_USER
        self.update_pwd = fs_args.get('dm_update_pwd')
        self.install_operate = InstallOperate(project_id, pod_id, fs_args)
        self.external_kms_cert_dir_path = '/home/pkg/DistributedStorage_ExternalKMSCert'
        self.external_kms_cert_name = 'kms'
        self.ca_cert_name = 'ca'
        self.key_cert_name = 'key'

    def procedure(self):
        try:
            logger.info("Start to config external KMS.")
            # 登录deploy manager
            logger.info("Start to login deploy manager.")
            self.login_deploy_manager()

            # 上传外置秘钥管理服务证书
            logger.info("Start to upload certificate.")
            self.upload_certificate()
            logger.info("Upload certificate finished.")

            # 激活证书
            logger.info("Start to activating a Certificate")
            self.activat_certificate()
            logger.info("Activating Certificate finished.")

            # 配置外置密管服务器
            logger.info("Start to config external kms info.")
            self.config_external_kms()
            logger.info("Config external kms finished.")

            # 循环测试外置密管连通性,最大测试时间5分钟
            logger.info("Start to test external kms connectivity.")
            self.test_external_kms_connectivity()
            logger.info("Test external kms connectivity successful.")

            logger.info('Config external kms successfully.')
        except FCDException as e:
            logger.error(traceback.format_exc())
            return Message(500, e)
        except Exception as e:
            return Message(500, FCDException(626356, str(e)))
        finally:
            self.opr.login_out(self.username, self.update_pwd)
        return Message()

    def cleanup(self):
        try:
            logger.info("Try to clean up external kms.")
            # 登录deploy manager
            logger.info("Start to login deploy manager.")
            self.login_deploy_manager()

            # 查询外置密管
            logger.info("Query external kms info.")
            kms_list = self.get_external_kms()
            logger.info("Get external kms info list:%s" % kms_list)

            # 有外置密管，根据id删除密管
            if kms_list:
                external_kms_id_list = self.query_external_kms_id(kms_list)
                # 删除外置密管配置
                logger.info("Get the external KMS id list[%s] need to delete." % external_kms_id_list)
                self.delete_external_kms(external_kms_id_list)
                logger.info('Delete external KMS successful')
            logger.info("No external KMS. Cleanup.")
        except FCDException as e:
            logger.error(traceback.format_exc())
            return Message(500, e)
        except Exception as e:
            logger.error(traceback.format_exc())
            return Message(500, FCDException(626360, str(e)))
        finally:
            self.opr.login_out(self.username, self.update_pwd)
        return Message()

    def login_deploy_manager(self):
        status_code, error_code, error_des = self.opr.login(self.username, self.update_pwd, keep_session=True)
        if status_code != 200 or error_code != 0:
            err_msg = "Failed to login deploy manager, " \
                      "Detail:[status:%s,code:%s]%s" % (status_code, error_code, error_des)
            logger.error(err_msg)
            raise Exception(err_msg)

    def _get_external_kms_info(self):
        external_kms_info = dict()
        external_kms_info['server_ip'] = self.fs_args.get('external_kms_ip')
        external_kms_info['server_port'] = self.fs_args.get('external_kms_port')
        kms_type = self.fs_args.get('external_kms_type')
        if kms_type == 'Thales KMIP':
            external_kms_info['server_type'] = '0'
        elif kms_type == 'SafeNet KMIP':
            external_kms_info['server_type'] = '1'
        elif kms_type == 'Sansec KMIP':
            external_kms_info['server_type'] = '2'
        elif kms_type == 'Utimaco KMIP':
            external_kms_info['server_type'] = '3'
        return external_kms_info

    def config_external_kms(self):
        external_kms_info = self._get_external_kms_info()
        status_code, error_code, error_des = self.opr.config_external_kms_info(external_kms_info)
        if status_code != 0 or error_code is not None:
            err_msg = "Failed to config external kms info. " \
                       "Detail:[status:%s,code:%s]%s" % (status_code, error_code, error_des)
            logger.error(err_msg)
            raise FCDException(626356, err_msg)

    def upload_certificate(self):
        # 上传外置密管服务证书
        logger.info("Start to upload external kms certificate.")
        file_info = self.get_cert_file_info('kms')
        rsp_result = self.opr.import_certificate(self.username, self.update_pwd, file_info)
        if rsp_result != 200:
            logger.error("Failed to import external kms certificate. Detail:[result:%s]" % rsp_result)
            raise FCDException(626369, 'KMS')
        # 上传CA证书
        logger.info("Start to upload CA certificate.")
        file_info = self.get_cert_file_info('ca')
        rsp_result = self.opr.import_certificate(self.username, self.update_pwd, file_info)
        if rsp_result != 200:
            logger.error("Failed to import ca certificate. Detail:[result:%s]" % rsp_result)
            raise FCDException(626369, 'CA')
        # 上传私钥证书
        logger.info("Start to upload key.")
        file_info = self.get_cert_file_info('key')
        rsp_result = self.opr.import_certificate(self.username, self.update_pwd, file_info)
        if rsp_result != 200:
            logger.error("Failed to import key certificate. Detail:[result:%s]" % rsp_result)
            raise FCDException(626369, 'KEY')

    def activat_certificate(self):
        # 等待10s证书上传成功
        time.sleep(10)
        cert_type = 67
        logger.info("Activating certificate.")
        rsp_result, status_code = self.opr.activat_certificate(cert_type)
        if status_code != 0:
            logger.error("Failed to activate certificate. Detail:[result:%s]" % rsp_result)
            raise FCDException(626370)
        time.sleep(20)

    def get_cert_file_info(self, cert_name):
        # 构建上传证书文件信息，cert_name取值：kms：外置密管服务证书 ca：CA证书 key:私钥证书
        file_info = dict()
        file_info['cert_type'] = '67'
        file_info['file_type'] = 'crt'
        if cert_name == 'kms':
            file_info['file_path'] = self.install_operate.get_certificate_path(self.external_kms_cert_name,
                                                                               self.external_kms_cert_dir_path)
            file_info['file_name'] = 'CERTIFICATE_NAME'
        elif cert_name == 'ca':
            file_info['file_path'] = self.install_operate.get_certificate_path(self.ca_cert_name,
                                                                               self.external_kms_cert_dir_path)
            file_info['file_name'] = 'CA_CERTIFICATE_NAME'
        elif cert_name == 'key':
            file_info['file_path'] = self.install_operate.get_certificate_path(self.key_cert_name,
                                                                               self.external_kms_cert_dir_path)
            file_info['file_name'] = 'PRIVATE_KEY_NAME'
            file_info['file_type'] = 'key'
        logger.info("Get file info.")
        return file_info

    def test_external_kms_connectivity(self, timeout=300):
        time.sleep(30)
        external_kms_info = self._get_external_kms_info()
        current_time = 0
        task_data = dict()
        while current_time < timeout:
            # 循环测试外置密管连通性，每轮测试20s, 不超出最大时间范围
            status_code, error_code, error_des = self.opr.test_external_kms_connectivity(external_kms_info)
            if status_code == 0 and error_code is None:
                logger.info('External KMS connectivity test successful')
                break
            if error_code == '33759547':
                # 报证书不存在错误码多等待10s
                time.sleep(10)
            current_time += 50
            task_data['status_code'] = status_code
            task_data['error_code'] = error_code
            task_data['error_des'] = error_des
            logger.info('Loop test external kms connectivity, remaining time:%s second. Detail:[status:%s,'
                        'code:%s]%s' % ((timeout - current_time), status_code, error_code, error_des))
        if task_data:
            err_msg = "Connection timeout. Failed to connect external kms, Detail:[status:%s,code:%s]%s"\
                      % (task_data['status_code'], task_data['error_code'], task_data['error_des'])
            logger.error(err_msg)
            raise FCDException(626359, err_msg)

    def get_external_kms(self):
        rsp_result, rsp_data = self.opr.query_external_kms_info()
        if rsp_result != 0:
            err_msg = ("Failed to query the external kms, Detail:[result:%s]" % rsp_result)
            logger.error(err_msg)
            raise FCDException(626362, err_msg)
        return rsp_data

    def query_external_kms_id(self, kms_list):
        kms_id_list = dict()
        for external_kms in kms_list:
            kms_id = external_kms.get('id')
            kms_id_list[external_kms.get('serverIp')] = kms_id
        return kms_id_list

    def delete_external_kms(self, kms_id_list):
        logger.info('Start delete external kms')
        for kms_id in kms_id_list.values():
            rsp_result = self.opr.delete_external_kms_info(kms_id)
            if rsp_result.get('result') != 0:
                err_msg = "Failed to delete external KMS with id[%s]. Detail:%s" % (kms_id, rsp_result)
                logger.error(err_msg)
                raise FCDException(626360, err_msg)
