# -*- coding: utf-8 -*-
import traceback
from tenacity import stop_after_attempt, wait_fixed
from tenacity import retry as retry_adorn

import utils.common.log as logger
from utils.common.exception import HCCIException
from plugins.DistributedStorage.common.UpgradeOperate import UpgradeOperate
from plugins.DistributedStorage.common.UpgradeHotPatchOperate import UpgradeHotPatchOperate
from plugins.DistributedStorage.common.base import TestCase
from plugins.DistributedStorage.common.PublicHandleNew import RestPublicMethod
from plugins.DistributedStorage.common.RestClient import StorageSSHClient
from plugins.DistributedStorage.Upgrade.scripts.impl.TC_Post_Upgrade_Check import\
    PostUpgradeCheck
from plugins.DistributedStorage.Upgrade.scripts.impl.TC_Upgrade_Pkg import \
    UpgradePkg
from plugins.DistributedStorage.Upgrade.scripts.impl.TC_Upgrade_Confirm import \
    UpgradeConfirm


class ProductUpgradePkg(TestCase):
    def __init__(self, project_id, pod_id, fs_args, condition=None,
                 metadata=None, **kwargs):
        super(ProductUpgradePkg, self).__init__(project_id, pod_id)
        self.project_id = project_id
        self.pod_id = pod_id
        self.fs_args = fs_args
        self.condition = condition
        self.metadata = metadata
        self.more_args = kwargs
        self.node_list = list()
        self.single_mode = None
        self.master_node = fs_args.get("master_node")
        self.slaver_node = fs_args.get("slaver_node")
        self.master_client = None
        self.slaver_client = None
        self.remote_path = "/tmp/upgrade_tmp_hotpatch"
        self.opr = UpgradeOperate(fs_args)
        self.rest_opr = RestPublicMethod(project_id, pod_id, fs_args)
        self.region_id = fs_args.get("region_id")
        self.user_name = fs_args["user_name"]
        self.password = fs_args["password"]
        self.upgrade_state_key = fs_args.get("upgrade_state_key")

    def procedure(self):
        logger.info('Start upgrade task.')
        try:
            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 = "Failed to login, Detail:[status:%s,code:%s]%s" % (status_code, error_code, error_des)
                logger.error(err_msg)
                raise Exception(err_msg)
            self.get_host_list()
            # 获取主备节点
            self.create_fsm_client()
            # 上传脚本到主备fsm节点
            self.upload_script_to_fsm()
            upgrade_tasks = [
                "product_upgrade",
                "post_upgrade_check",
                "backup_product_db_data",
                "backup_repo_data",
                "set_ignore_hosts",
                "upgrade_confirm",
                "del_ignore_hosts"
            ]
            for index, task in enumerate(upgrade_tasks):
                self.update_upgrade_state(index)
                getattr(self, task)()

        except HCCIException as e:
            logger.error('upgrade pkg failed:{}'.format(e))
            logger.error(traceback.format_exc())
            raise e
        except Exception as e:
            logger.error('upgrade pkg failed:{}'.format(e))
            logger.error(traceback.format_exc())
            raise HCCIException(620009, str(e))
        finally:
            self.del_script_om_fsm()

    def retry(self):
        logger.info('Start retry upgrade task.')
        try:
            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 = "Failed to login, Detail:[status:{},code:{}]{}".format(status_code, error_code, error_des)
                logger.error(err_msg)
                raise Exception(err_msg)
            self.get_host_list()
            # 获取主备节点
            self.create_fsm_client()
            # 上传脚本到主备fsm节点
            self.upload_script_to_fsm()
            upgrade_tasks = [
                "product_upgrade_retry",
                "post_upgrade_check",
                "backup_product_db_data",
                "backup_repo_data",
                "set_ignore_hosts",
                "upgrade_confirm",
                "del_ignore_hosts"
            ]
            state = self.get_upgrade_state()
            for index, task in enumerate(upgrade_tasks[state:]):
                self.update_upgrade_state(index + state)
                getattr(self, task)()

        except HCCIException as e:
            logger.error('retry upgrade pkg failed:{}'.format(e))
            logger.error(traceback.format_exc())
            raise e
        except Exception as e:
            logger.error('retry upgrade pkg failed:{}'.format(e))
            logger.error(traceback.format_exc())
            raise HCCIException(620009, str(e))
        finally:
            self.del_script_om_fsm()

    def del_ignore_hosts(self):
        logger.info("Del ignore host: %s." % self.node_list)
        params = {
            "agentIps": self.node_list
        }
        ret_result, ret_data = self.opr.handle_ignore_host(params, "delete")
        if ret_result.get("code") != "0":
            err_msg = "Failed to del the node to ignore, Details: %s." % ret_data
            logger.error(err_msg)
            raise Exception(err_msg)

    def set_ignore_hosts(self):
        logger.info("Set ignore host: %s." % self.node_list)
        params = {
            "agentIps": self.node_list
        }
        ret_result, ret_data = self.opr.handle_ignore_host(params, "post")
        if ret_result.get("code") != "0":
            err_msg = "Failed to set the node to ignore, Details: %s." % ret_data
            logger.error(err_msg)
            raise Exception(err_msg)

    def product_upgrade(self):
        """
        存储大包升级
        """
        UpgradePkg(self.project_id, self.pod_id, self.fs_args).procedure()

    def product_upgrade_retry(self):
        UpgradePkg(self.project_id, self.pod_id, self.fs_args).retry()

    @retry_adorn(stop=stop_after_attempt(5), wait=wait_fixed(120), reraise=True)
    def post_upgrade_check(self):
        """
        升级后检查
        """
        PostUpgradeCheck(self.project_id, self.pod_id, self.fs_args).procedure()

    def upgrade_confirm(self):
        """
        升级确认
        """
        try:
            UpgradeConfirm(self.project_id, self.pod_id, self.fs_args).procedure()
        except Exception as e:
            if str(e).find("Perform the post-upgrade check first") != -1:
                # 升级确认前fsm进行了主备倒换，需要重新进行升级后检查
                self.del_ignore_hosts()
                self.post_upgrade_check()
                self.set_ignore_hosts()
                UpgradeConfirm(self.project_id, self.pod_id, self.fs_args).procedure()
            else:
                raise Exception(e) from e
        UpgradeHotPatchOperate.config_oam_u_firewall(self.master_client)
        UpgradeHotPatchOperate.config_oam_u_firewall(self.slaver_client)

    def get_host_list(self):
        """
        获取存储节点列表
        """
        osd_nodes_list, vbs_nodes_list = self.rest_opr.get_server_list()
        self.node_list.extend(osd_nodes_list)
        self.node_list.extend(vbs_nodes_list)
        self.node_list.append(self.master_node.ip)
        self.node_list.append(self.slaver_node.ip)
        # 去重
        self.node_list = list(set(self.node_list))

    def upload_script_to_fsm(self):
        """
        上传脚本至fsm主备节点
        """
        node_list = [(self.master_client, self.master_node),
                     (self.slaver_client, self.slaver_node)]
        for ssh_client, node in node_list:
            UpgradeHotPatchOperate.upload_script_to_fsm(ssh_client, node, self.remote_path)

    def del_script_om_fsm(self):
        """
        删除fsm节点上的脚本文件
        """
        client_list = [client for client in
                       [self.master_client, self.slaver_client] if client]
        for ssh_client in client_list:
            UpgradeHotPatchOperate.del_script_om_fsm(ssh_client, self.remote_path)
            del ssh_client

    def create_fsm_client(self):
        """
        获取当前主节点
        """
        self.master_client = StorageSSHClient(*self.master_node[:3])
        self.master_client.switch_root(self.master_node.root_pwd)
        self.slaver_client = StorageSSHClient(*self.slaver_node[:3])
        self.slaver_client.switch_root(self.slaver_node.root_pwd)

    def backup_product_db_data(self):
        self.backup_db_data(step="product")

    def backup_db_data(self, step="hotpatch"):
        """
        备份主备fsm数据库
        step: hotpatch:备份热补丁数据库
        step: product:备份产品大包数据
        """
        logger.info("Back %s data start." % step)
        for ssh_client in [self.master_client, self.slaver_client]:
            UpgradeHotPatchOperate.backup_db_data(ssh_client, self.remote_path, step=step)
        logger.info("Back %s data success." % step)

    def backup_repo_data(self):
        """
        备份主备fsm软件包仓
        """
        logger.info("Back software repository start.")
        for ssh_client in [self.master_client, self.slaver_client]:
            UpgradeHotPatchOperate.backup_repo_data(ssh_client, self.remote_path)
        logger.info("Back software repository success.")

    def update_upgrade_state(self, state=0):
        """
        更新记录当前升级步骤
        0：大包升级初级状态
        1：大包升级完成
        2：大包升级后检查完成
        3：备份大包数据库完成
        4：备份大包软件仓完成
        5：设置忽略节点完成
        6：大包升级确认完成
        7：删除忽略节点完成
        8：上传补丁包完成
        9：补丁包升级前检查完成
        10：补丁升级完成
        11：部分补丁数据库完成
        12：备份补丁软件仓完成
        """
        UpgradeHotPatchOperate.update_operate_state(self.project_id, self.region_id,
                                                    self.upgrade_state_key, state=state)

    def get_upgrade_state(self):
        return UpgradeHotPatchOperate.get_operate_state(self.project_id, self.region_id, self.upgrade_state_key)
