#!/usr/bin/env python
# Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.

import time
import traceback
import functools

import utils.common.log as logger
from utils.common.exception import HCCIException
from plugins.DistributedStorage.implement.tc_add_node import DeployOperate
from plugins.DistributedStorage.implement.tc_add_node import AddNode
from plugins.DistributedStorage.implement.tc_install_node import InstallNode
from plugins.DistributedStorage.utils.common.deploy_constant import DeployConstant


class DistributedStorageDeployParams:
    def __init__(self, args: dict, node_deploy_info: list, network_info: list, route_info: dict):
        """
        :param args:
                     "float_ip": float_ip,
                     "portal_pwd": portal_pwd,
                     "net_type": "storage_frontend",
                     "scenario": "extend"
        :param node_deploy_info:
                                [{"serial_number": "",
                                  "name": name,
                                  "management_internal_ip": ip,
                                  "role": "compute",
                                  "cabinet": cabinet,
                                  "authentication_mode": "password",
                                  "user_name": user_name,
                                  "password": password,
                                  "root_password": root_password
                                  },
                                  ...]
        :param network_info:
                            [{'port_name': port_name,
                             'ip_segment': {'begin_ip': start_ip, 'end_ip': end_ip},
                             'subnet_prefix': net_mask,
                             'default_gateway': gateway
                             },
                             ...]
        :param route_info:
                           "nodeMgrIps": [ip1, ip2, ...],
                           "portName1": portName1,
                           "gateway1": gateway1,
                           "portName2": 可选,
                           "gateway2": 可选,
        """
        self.args = args
        self.node_deploy_info = node_deploy_info
        self.network_info = network_info
        self.route_info = route_info
        self.node_ip_list = [node_info.get("management_internal_ip") for node_info in node_deploy_info]

    def __repr__(self):
        return str([self.args, self.node_deploy_info, self.network_info, self.route_info])


class DeployInfPatch:
    def __init__(self, cls):
        self.cls = cls
        functools.wraps(cls)(self)

    def __call__(self, *args):
        self.cls.check_task_status = InstallNode.check_task_status
        try:
            self.cls._failure_status_opr = getattr(InstallNode, '_failure_status_opr')
        except AttributeError as e:
            logger.error(traceback.format_exc())
            raise e
        self.cls.check_status_add_cluster = InstallNode.check_status_add_cluster
        return self.cls(*args)


@DeployInfPatch
class DistributedStorageDeployInf:
    """对接分布式块存储公共接口"""
    def __init__(self, params: DistributedStorageDeployParams):
        self.params = params
        self.update_pwd = self.params.args.get("portal_pwd")
        self.opr = DeployOperate(params.args)

    @staticmethod
    def check_node_in_cluster(rsp_data, node_list):
        return InstallNode.check_node_in_cluster(rsp_data, node_list)

    def query_storage_network(self, opr: DeployOperate, net_type='storage_frontend'):
        """查询分布式存储网络平面

        :param opr:
        :param net_type: 'storage_frontend' or 'storage_backend'...
        :return:
        """
        opr.login(DeployConstant.DM_LOGIN_USER, self.update_pwd)
        try:
            rsp_result, rsp_data = opr.get_storage_net_plane(net_type)
        finally:
            opr.logout()
        return rsp_result, rsp_data

    def deploy(self):
        try:
            self.opr.login(DeployConstant.DM_LOGIN_USER, self.update_pwd)
            self.__add_node()
            self.__config_storage_network()
            self.__install_node()
            self.__config_policy_route()
            self.__create_vbs_client()
        finally:
            self.opr.logout()

    def retry(self):
        try:
            functools.partial(AddNode.restore_factory, self)()
        except Exception as e:
            logger.error(traceback.format_exc())
            raise e
        return self.deploy()

    def rollback(self):
        try:
            functools.partial(AddNode.restore_factory, self)()
        except Exception as e:
            logger.error(traceback.format_exc())
            raise e

    def __add_node(self):
        logger.info('Start to add nodes:{}'.format(self.params.node_ip_list))
        res = self.opr.add_servers('post', self.params.node_deploy_info, timeout=1900)
        status_code, error_code, error_des = res.get_res_code()
        if error_code != 0:
            err_msg = "Failed to add hosts, Detail: {}".format(str(res.get_res_code()))
            logger.error(err_msg)
            raise Exception(err_msg)

        time.sleep(60)
        res = self.opr.deploy_service(self.params.node_ip_list, 'agent')
        status_code, error_code, error_des = res.get_res_code()
        if error_code != 0:
            err_msg = "Failed to install network manage component , Detail: {}".format(str(res.get_res_code()))
            logger.error(err_msg)
            raise Exception(err_msg)

        time.sleep(120)
        functools.partial(AddNode.query_component_installation_status, self)(self.params.node_ip_list)
        logger.info('The nodes are added successfully')

    def __config_storage_network(self):
        logger.info('Start to config storage network')
        net_type, scenario = self.params.args.get("net_type"), self.params.args.get("scenario")
        res_result, res_data = self.opr.set_net_plane(self.params.network_info, net_type, scenario=scenario)
        if res_result.get("code") != 0 or res_data is not None:
            err_msg = "Failed to config storage network [network:{}, net type:{}], Detail: {}".format(
                self.params.network_info, net_type, [res_result, res_data])
            logger.error(err_msg)
            raise Exception(err_msg)

        logger.info('Start to validity network')
        rsp_result, rsp_data = self.opr.validity_net(net_type, self.params.node_ip_list)
        fail_ip_list = list(rsp_data.keys())
        if rsp_result.get("code") != 0 or len(fail_ip_list) > 0:
            err_msg = "Failed to config storage network on host {}, Detail: {}".format(
                fail_ip_list, [rsp_result, rsp_data])
            logger.error(err_msg)
            raise Exception(err_msg)
        logger.info("The storage network is configured successfully.")

    def __install_node(self):
        logger.info('Start to install storage software')
        try:
            _deploy_service = getattr(InstallNode, '_deploy_service')
        except AttributeError as e:
            logger.error(traceback.format_exc())
            raise e
        functools.partial(_deploy_service, self)(self.params.node_ip_list)
        time.sleep(120)
        logger.info("Checking if nodes {} add to the cluster".format(self.params.node_ip_list))
        self.check_status_add_cluster(self.params.node_ip_list)
        logger.info('The storage software is installed successfully.')

    def __config_policy_route(self):
        logger.info('Start to Configure the policy-based route')
        rsp_result, res_date = self.opr.add_policy_route(self.params.route_info)
        if rsp_result != 0:
            err_msg = ("Failed to configure the policy-based route, result: {}, data: {}".format(rsp_result, res_date))
            raise Exception(err_msg)
        logger.info('Policy-based routing is configured successfully')

    def __create_vbs_client(self):
        logger.info('Start to create a VBS Client')
        res_client = self.opr.create_client(self.params.node_ip_list)
        status_code, result, error_code, error_des = res_client.get_create_client_code()
        if status_code != 200 or result != 0:
            err_msg = "Failed to Create VBS client, Details: status code:{}, result:{},error code:{}, " \
                      "error des:{}".format(status_code, result, error_code, error_des)
            logger.error(err_msg)
            raise Exception(err_msg)
        logger.info('The VBS client is successfully created.')
