# -*- coding: utf-8 -*-
# Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
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 HCCIException
from plugins.DistributedStorage.logic.deploy_operate import DeployOperate
from plugins.DistributedStorage.utils.common.deploy_constant import DeployConstant
from plugins.DistributedStorage.Replication.scripts.implement.tc_add_node import AddNode
from plugins.DistributedStorage.Replication.scripts.implement.tc_config_storage_network import ConfigStorageNetwork


class InstallNode(TestCase):
    def __init__(self, project_id, pod_id, fs_args, **kwargs):
        super(InstallNode, self).__init__(project_id, pod_id)
        self.fs_args = fs_args
        self.opr = DeployOperate(self.fs_args)
        self.more_args = kwargs

    def procedure(self):
        logger.info("Start to install components to all nodes.")
        try:
            self.opr.login(DeployConstant.DM_LOGIN_USER, self.fs_args['dm_update_pwd'])
        except HCCIException as e:
            return Message(500, e)
        except Exception as e:
            return Message(500, HCCIException(626066, str(e)))

        node_list = [fsa_node['om_ip'] for fsa_node in self.fs_args['fsa_list']]
        logger.info("Install components to all nodes[%s]." % node_list)
        try:
            self.install_components_to_nodes(node_list)
        except HCCIException as e:
            return Message(500, e)
        except Exception as e:
            return Message(500, HCCIException(626066, str(e)))

        time.sleep(120)
        try:
            self.query_components_install_status(node_list)
        except HCCIException as e:
            return Message(500, e)
        except Exception as e:
            return Message(500, HCCIException(626066, str(e)))
        logger.info("Install components successful.")

        logger.info("Checking if nodes[%s] add to the cluster" % node_list)
        try:
            self.check_status_add_cluster(node_list)
        except HCCIException as e:
            return Message(500, e)
        except Exception as e:
            return Message(500, HCCIException(626066, str(e)))

        self.opr.login_out(DeployConstant.DM_LOGIN_USER, self.fs_args['dm_update_pwd'])
        return Message()

    def install_components_to_nodes(self, node_list):
        res = self.opr.deploy_service(node_list, 'all')
        status_code, error_code, error_des = res.get_res_code()
        if error_code != 0:
            err_msg = "Failed to deploy component on storage node[%s], " \
                      "Detail:[%s]%s" % (node_list, error_code, error_des)
            logger.error(err_msg)
            raise Exception(err_msg)

    def query_install_task_status(self):
        res = self.opr.query_task_status()
        status_code, error_code, error_des = res.get_res_code()
        if error_code != 0:
            err_msg = "Failed to query component installation status , Detail:[%s]%s" % (error_code, error_des)
            logger.error(err_msg)
            raise Exception(err_msg)
        task_status, server_result = res.get_res_data()
        return task_status, server_result

    def query_components_install_status(self, node_list):
        wait_time = 5400
        try_times = 1
        while wait_time > 0 and try_times < 4:
            time.sleep(30)
            wait_time -= 30
            task_status, server_result = self.query_install_task_status()
            if task_status == 'success':
                logger.info("Succeed to install component to all node.Detail:%s" % server_result)
                break
            elif task_status == "failure":
                try_times = self.operate_failed_task(try_times, task_status, server_result, node_list)
            else:
                logger.info("Processing<%s> to install storage software on node. "
                            "Detail:%s" % (task_status, str(server_result)))
        if wait_time <= 0:
            err_msg = "Wait for installation end timeout"
            logger.error(err_msg)
            raise Exception(err_msg)

    def operate_failed_task(self, try_times, task_status, server_result, node_list):
        if try_times >= 3:
            err_msg = "Failed to install component for 3 times, " \
                      "Detail:[%s]%s" % (task_status, str(server_result))
            logger.error(err_msg)
            raise Exception(err_msg)
        else:
            logger.info("Try to install component to all node %s times."
                        "Detail:%s" % (try_times, server_result))
            res = self.opr.deploy_service_retry(node_list, 'all')
            status_code, error_code, error_des = res.get_res_code()
            if error_code != 0:
                err_msg = "Failed to deploy component on all node[%s], " \
                          "Detail:[%s]%s" % (node_list, error_code, error_des)
                logger.error(err_msg)
                raise Exception(err_msg)
            try_times += 1
        return try_times

    def restore_factory(self):
        logger.info("Start to retore factory setting.")
        res = self.opr.nodes_restore_factory()
        ret_code, ret_data = res.get_nodes_data()
        fail_nodes_list = ret_data.get('data').get('failed_nodes')
        success_nodes_list = ret_data.get('data').get('successful_nodes')
        if ret_code != 0 or len(fail_nodes_list) > 0:
            code = ret_data.get('result').get('code')
            err_des = ret_data.get('result').get('code')
            suggestion = ret_data.get('result').get('code')
            err_msg = "Failed to restore factory setting on nodes%s. Succeeful nodes%s. " \
                      "DetaiL:[%s]%s. Suggestion:%s." % (fail_nodes_list, success_nodes_list, code, err_des, suggestion)
            logger.error(err_msg)
            raise Exception(err_msg)
        logger.info("Succeed to retore factory setting%s." % success_nodes_list)

    def retry_install_node(self):
        logger.info("Retry to install components to all nodes.")
        try:
            self.opr.login(DeployConstant.DM_LOGIN_USER, self.fs_args['dm_update_pwd'])
        except HCCIException as e:
            return Message(500, e)

        logger.info("Try get current the status of installed servers")
        try:
            rsp_data = self.query_installed_servers()
        except Exception as e:
            return Message(500, HCCIException(626066, str(e)))

        node_list = [fsa_node.get('om_ip') for fsa_node in self.fs_args.get('fsa_list')]
        logger.info("Query action_phase from servers%s " % rsp_data)
        action_phase_list, fail_action_phase_list = self.query_action_phase(rsp_data, node_list)
        # 通知NMS其它组件安装完成，通知NMS管理节点组件安装完成
        logger.info("The current status of all nodes%s " % action_phase_list)
        logger.info("The current status of failed nodes%s " % fail_action_phase_list)
        if 0 == len(fail_action_phase_list):
            try:
                self.try_add_cluster(node_list)
            except HCCIException as e:
                return Message(500, e)
            except Exception as e:
                return Message(500, HCCIException(626066, str(e)))

            self.opr.login_out(DeployConstant.DM_LOGIN_USER, self.fs_args.get('dm_update_pwd'))
            return Message()

        try:
            task_status, server_reslut = self.query_install_task_status()
        except HCCIException as e:
            return Message(500, e)
        except Exception as e:
            return Message(500, HCCIException(626066, str(e)))

        if task_status == 'success':
            logger.info("Renotify NMS to add nodes%s to the cluster." % node_list)
            try:
                self.renotify_nms(node_list)
            except HCCIException as e:
                return Message(500, e)
            except Exception as e:
                return Message(500, HCCIException(626066, str(e)))
        else:
            logger.info("Try to install all nodes%s from add host again" % node_list)
            try:
                rtn_msg = self.retry_install_all()
            except HCCIException as e:
                return Message(500, e)
            except Exception as e:
                return Message(500, HCCIException(626066, str(e)))
            if 200 != rtn_msg.status_code:
                return rtn_msg
        self.opr.login_out(DeployConstant.DM_LOGIN_USER, self.fs_args.get('dm_update_pwd'))
        return Message()

    def query_installed_servers(self):
        rsp_obj = self.opr.query_deploy_servers()
        rsp_code, rsp_result, rsp_data = rsp_obj.get_rsp_data()
        error_code = int(rsp_result.get('code'))
        if rsp_code != 0 or error_code != 0:
            error_des = rsp_result.get('description')
            error_sgt = rsp_result.get('suggestion')
            err_msg = "Failed to query the status of installed servers, " \
                      "Detail:[%s]%s.Suggestion:%s" % (error_code, error_des, error_sgt)
            logger.error(err_msg)
            raise Exception(err_msg)
        return rsp_data

    def query_action_phase(self, rsp_data, node_list):
        action_phase_list = list()
        fail_action_phase_list = list()
        # 通知NMS其它组件安装完成，通知NMS管理节点组件安装完成
        for server in rsp_data:
            if server.get('management_internal_ip') not in node_list:
                continue
            action_phase = server.get('action_phase')
            action_phase_list.append(action_phase)
            if action_phase != 12 and action_phase != 17:
                fail_action_phase_list.append(action_phase)
        return action_phase_list, fail_action_phase_list

    def renotify_nms(self, node_list):
        res = self.opr.deploy_service_retry(node_list, 'all')
        status_code, error_code, error_des = res.get_res_code()
        if error_code != 0:
            err_msg = "Failed to notify NMS add nodes%s to cluster, " \
                      "Detail:[%s]%s" % (node_list, error_code, error_des)
            logger.error(err_msg)
            raise Exception(err_msg)

    def try_add_cluster(self, node_list):
        logger.info("Try to install component to all nodes again")
        rsp_obj = self.opr.query_cluster_servers()
        rsp_code, rsp_result, rsp_data = rsp_obj.get_rsp_data()
        error_code = rsp_result.get('code')
        if rsp_code != 0 or error_code != 0:
            error_des = rsp_result.get('description')
            error_sgt = rsp_result.get('suggestion')
            err_msg = "Failed to query the node to join the cluster, " \
                      "Detail:[%s]%s.Suggestion:%s" % (error_code, error_des, error_sgt)
            logger.error(err_msg)
            raise Exception(err_msg)

        logger.info("Query node id list from servers%s " % rsp_data)
        servers_id_list = list()
        for server in rsp_data:
            if server.get('management_ip') not in node_list:
                continue
            servers_id_list.append(server.get('id'))

        logger.info("Try add node%s to cluster again" % servers_id_list)
        rsp_obj = self.opr.add_cluster_node(servers_id_list)
        rsp_code, rsp_result, rsp_data = rsp_obj.get_rsp_data()
        error_code = rsp_result.get('code')
        if rsp_code != 0 or error_code != 0:
            error_des = rsp_result.get('description')
            error_sgt = rsp_result.get('suggestion')
            err_msg = "Failed again to add nodes to cluster. Detail:[%s]%s. " \
                      "Suggestion:%s" % (error_code, error_des, error_sgt)
            logger.error(err_msg)
            raise Exception(err_msg)

        logger.info("Checking if nodes[%s] add to the cluster" % node_list)
        self.check_status_add_cluster(node_list)

    def retry_install_all(self):
        logger.info("Step1 retore factory setting all nodes")
        self.restore_factory()
        logger.info("Step2 add nodes again")
        AddNode(self.project_id, self.pod_id, self.fs_args).procedure()
        logger.info("Step3 config storage network again")
        rtn_msg = ConfigStorageNetwork(self.project_id, self.pod_id, self.fs_args).procedure()
        if 200 != rtn_msg.status_code:
            return rtn_msg
        logger.info("Step4 install the component again")
        rtn_msg = self.procedure()
        return rtn_msg

    def check_status_add_cluster(self, node_list):
        rsp_data = None
        not_in_cluster_list = [0]
        cwait_time = 300 + min(len(node_list)*60, 600)
        while cwait_time > 0:
            time.sleep(30)
            cwait_time -= 30
            rsp_obj = self.opr.query_cluster_servers()
            rsp_code, rsp_result, rsp_data = rsp_obj.get_rsp_data()
            error_code = rsp_result.get('code')
            if rsp_code != 0 or error_code != 0:
                error_des = rsp_result.get('description')
                error_sgt = rsp_result.get('suggestion')
                err_msg = "Failed to query the node to join the cluster, " \
                          "Detail:[%s]%s.Suggestion:%s" % (error_code, error_des, error_sgt)
                logger.error(err_msg)
                raise Exception(err_msg)
            logger.info("The current status of all nodes%s " % rsp_data)
            not_in_cluster_list = list()
            in_cluster_list = list()
            for server in rsp_data:
                if server.get('management_ip') not in node_list:
                    continue
                status_add_cluster = server.get('in_cluster')
                if not status_add_cluster:
                    not_in_cluster_list.append(server.get('management_ip'))
                if status_add_cluster:
                    in_cluster_list.append(server.get('management_ip'))
            if len(not_in_cluster_list) == 0:
                logger.info("all nodes%s is added to cluster " % in_cluster_list)
                break
            else:
                logger.info("Wait to add nodes%s in cluster " % not_in_cluster_list)
        if 0 < len(not_in_cluster_list):
            err_msg = "Existing nodes%s are not add cluster after %s, " \
                      "Detail:%s." % (not_in_cluster_list, cwait_time, rsp_data)
            logger.error(err_msg)
            raise Exception(err_msg)
        logger.info("Succeed to the cluster")
