# -*- coding: utf-8 -*-
# Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
import json
import os.path
import pathlib
import random
import string
import utils.common.log as logger
from utils.security import crypt
from plugins.DistributedStorage.common.rest_client import StorageSSHClient
from plugins.DistributedStorage.common.upgrade_operate import UpgradeOperate
from plugins.DistributedStorage.Upgrade.scripts.impl.tc_pre_upgrade_check import \
    PreUpgradeCheck
from plugins.DistributedStorage.common.base import TestCase


class UpgradeEvadeIntf(TestCase):
    def __init__(self, project_id, pod_id, fs_args, **kwargs):
        super(UpgradeEvadeIntf, self).__init__(project_id, pod_id)
        self.more_args = kwargs
        self.opr = UpgradeOperate(fs_args)
        self.fs_args = fs_args
        self.user_name = fs_args["user_name"]
        self.password = fs_args["password"]
        self.zk_node_infos = fs_args.get("zk_node_infos")
        self.tmp_path_suffix = ''.join(random.choices(string.digits + string.ascii_letters, k=10))
        self.remote_file_tmp_path = "upgrade_" + self.tmp_path_suffix
        self.remote_file_path = os.path.join("/tmp/", self.remote_file_tmp_path)
        self.local_file_path = ""

    def procedure(self):
        self.login_dm()
        is_manage_storage_flag, cluster_zk_nodes_ip = self.check_manage_storage_tag()
        if not is_manage_storage_flag:
            logger.info("Current storage is business storage, no need to modify ccdb config.")
            return
        if not self.check_product_version():
            logger.info("Current storage is version no need to modify ccdb config.")
            return
        self.evade_operate()
        self.opr.logout()

    def login_dm(self):
        status_code, error_code, error_des = self.opr.try_login(
            self.user_name, self.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)
            raise Exception(err_msg)

    def check_manage_storage_tag(self):
        """
        检查当前集群是否为管理存储，只有管理存储storage和compute角色会在同一个节点
        :return:
        """
        ret_res, ret_data = self.opr.get_servers()
        if ret_res.get("code") != 0:
            err_msg = "get servers failed, Detail:[result:%s, data:%s]" \
                      % (ret_res, ret_data)
            logger.error(err_msg)
            raise Exception(err_msg)
        cluster_zk_nodes_ip = list()
        is_manage_storage_flag = False
        for node_info in ret_data:
            if 'zk' in node_info.get("usage"):
                cluster_zk_nodes_ip.append(node_info.get("management_ip"))
            if 'storage' in node_info.get("role") \
                    and 'compute' in node_info.get("role"):
                is_manage_storage_flag = True
        return is_manage_storage_flag, cluster_zk_nodes_ip

    def check_product_version(self):
        ret_result, ret_data = self.opr.get_product()
        if ret_result["code"] != 0:
            err_msg = "get cluster product failed, Detail: [result:%s, data:%s]" % (ret_result, ret_data)
            logger.error(err_msg)
            raise Exception(err_msg)
        logger.info("Product info is :%s" % ret_data)
        product_version = ret_data.get("version")
        hotpatch_version = ret_data.get("hotpatch_version")
        product_version_list = ["8.0.1", "8.1.0", "8.1.1", "8.1.2"]
        need_evade_version_list = ["8.1.2.SPH101", '8.1.2.SPH102', "8.1.2.SPH103", "8.1.2.SPH104"]
        # 813不做规避
        if product_version not in product_version_list:
            return False
        # 812SPH105及之后版本不做规避
        if product_version == "8.1.2" and hotpatch_version \
                and hotpatch_version not in need_evade_version_list:
            return False
        return True

    def upgrade_pre_check(self):
        """
        执行升级前检查
        """
        PreUpgradeCheck(self.project_id, self.pod_id, self.fs_args).procedure()

    def evade_operate(self):
        """
        依次登录zk节点进行规避操作，单节点规避完成后进行一次升级前检查，避免集群出现多zk故障问题
        """
        self.zk_node_infos = json.loads(self.zk_node_infos)
        for node_ip, node_info in self.zk_node_infos.items():
            self.upgrade_pre_check()
            user, passwd, root_passwd = \
                node_info.get("user"), node_info.get("user_pwd"), node_info.get("root_pwd")
            passwd = crypt.decrypt(passwd)
            root_passwd = crypt.decrypt(root_passwd)
            ssh_client = StorageSSHClient(node_ip, user, passwd)
            logger.info("Create ssh client for host[%s] success" % node_ip)
            ssh_client.switch_root(root_passwd)
            self.upload_scripts_to_host(ssh_client)
            try:
                self.modify_thread_priority(ssh_client)
            finally:
                self.del_scripts_on_host(ssh_client)
            self.upgrade_pre_check()

    def modify_thread_priority(self, ssh_client):
        """
        登录zk节点，执行ccdb脚本修改线程优先级
        ccdb_evade.sh 退出1表示sed失败
                      退出2表示stop失败
                      退出3表示start失败
        :param ssh_client: ssh客户端
        :return:
        """
        cmd = f"sh {self.remote_file_path}/ccdb_evade.sh;echo last_result=$?;"
        cmd_res = ssh_client.send_cmd(cmd, "#")
        cmd_res = "".join(cmd_res)
        logger.info("Execute cmd detail: %s " % cmd_res)
        if "last_result=1" in cmd_res:
            err_msg = "Modify ccdb thread priority failed."
            logger.error(err_msg)
            raise Exception(err_msg)
        if "last_result=2" in cmd_res:
            err_msg = "Stop ccdb thread failed."
            logger.error(err_msg)
            raise Exception(err_msg)
        if "last_result=3" in cmd_res:
            err_msg = "Start ccdb thread failed."
            logger.error(err_msg)
            raise Exception(err_msg)
        if "last_result=0" not in cmd_res:
            err_msg = "Execute scripts[ccdb_evade.sh] failed"
            logger.error(err_msg)
            raise Exception(err_msg)
        logger.info("Modify ccdb thread priority success, and reboot ccdb thread success")

    def upload_scripts_to_host(self, ssh_client):
        """
        上传ccdb规避脚本至zk节点
        :param ssh_client:
        :return:
        """
        local_file_path = pathlib.Path(pathlib.Path(__file__).resolve().parent.parent.parent.parent,
                                       "tool/ccdb_evade.sh")
        mkdir_cmd = 'su - {user} -s /bin/bash -c "mkdir -p {pkg};chmod 700 {pkg};' \
                    'ls -l {tgt_dir} | grep {tmp_pkg_dir}"'\
            .format(user=ssh_client.username,
                    pkg=self.remote_file_path,
                    tgt_dir="/tmp",
                    tmp_pkg_dir=self.remote_file_tmp_path)
        cmd_ret = ssh_client.send_cmd_return(mkdir_cmd, timeout=60)
        cmd_ret = str(cmd_ret)
        if self.remote_file_tmp_path not in cmd_ret:
            err_msg = "make dir failed in node %s, ret: %s" \
                      % (ssh_client.host_ip, cmd_ret)
            logger.error(err_msg)
            raise Exception(err_msg)
        ssh_client.upload(local_file_path, self.remote_file_path)
        cmd_tmp = 'chown -h -R root:root %s' % self.remote_file_path
        ssh_client.send_cmd(cmd_tmp, "#")
        logger.info("Modify %s right success" % self.remote_file_path)

    def del_scripts_on_host(self, ssh_client):
        """
        删除zk节点上ccdb规避脚本
        :param ssh_client:
        :return:
        """
        cmd = f"rm -rf {self.remote_file_path};echo last_result=$?;"
        cmd_res = ssh_client.send_cmd(cmd, "#")
        cmd_res = "".join(cmd_res)
        if "last_result=0" not in cmd_res:
            err_msg = f"Del {self.remote_file_path}/ccdb_evade.sh failed"
            logger.error(err_msg)
            raise Exception(err_msg)
