#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
import utils.common.log as logger
from utils.DBAdapter.DBConnector import BaseOps
from plugins.DistributedStorage.logic.deploy_operate import DeployOperate
from plugins.DistributedStorage.utils.common.deploy_constant import DeployConstant, NodeRole, DeployType


class DealInfoForCMDB(object):

    def __init__(self, project_id, pod_id, fs_args):
        self.fs_args = fs_args
        self.float_ip = self.fs_args.get('float_ip')
        self.project_id = project_id
        self.pod_id = pod_id
        self.db = BaseOps()
        self.opr = DeployOperate(float_ip=self.float_ip)
        self.plane_dict = {
            'management_internal': "External_om",
            'storage_frontend': DeployConstant.MGR_STORAGE_NET_INTF,
            'storage_backend': DeployConstant.BACKEND_STORAGE_NET_INTF,
            'replication': "replication",
            'quorum': "quorum"
        }

    @staticmethod
    def get_services_info(version, region_id, osd_host_names, vm_hostnames):
        """
        组装微服务参数
        """
        service_info = [
            {
                "indexName": DeployConstant.CLOUD_SERVICE_INDEX,
                "version": version,
                "regionCode": region_id,
                "extendInfos": [
                    {
                        "key": "Distributed-storage",
                        "value": "True"
                    }
                ],
                "components": [
                    {
                        "indexName": 'FSM',
                        "version": version,
                        "nodeNames": vm_hostnames
                    },
                    {
                        "indexName": 'FSA',
                        "version": version,
                        "nodeNames": osd_host_names
                    }
                ]
            }
        ]
        return service_info

    @classmethod
    def get_serial_number(cls, cluster_info: list, node_ip: str) -> str:
        ret = ""
        for node_info in cluster_info:
            management_ip = node_info.get("management_ip")
            if management_ip == node_ip:
                ret = node_info.get("serial_number")
                break
        return ret

    def tool_logout(self):
        logger.info("rest client logout, client ip:%s" % str(self.float_ip))
        self.opr.logout()

    def tool_login(self):
        """
        rest client 登录
        :return:
        """
        logger.info("rest client login, client ip:%s" % str(self.float_ip))
        self.opr.login(DeployConstant.DM_LOGIN_USER, self.fs_args['dm_update_pwd'])

    def get_name(self):
        """
        返回集群name
        :return: str
        """
        return DeployConstant.CLOUD_SERVICE_INDEX + "_" + self.get_esn()

    def get_ip_address(self, node):
        def extract_net_info(plane_dict, _ip_usage):
            net_info = {
                "netPlane": plane_dict[_ip_usage],
                "ip": str(ip_info['ip_address']),
                "gateway": str(ip_info['default_gateway']),
                "netMask": str(ip_info['subnet_prefix']),
            }
            ip_addresses.append(net_info)

        ip_addresses = []
        frontend_and_backend_converge = sorted(['storage_frontend', 'storage_backend'])
        for ip_info in node.get('ip_address_list'):
            ip_usage_list = ip_info.get('ip_usage')
            if not ip_usage_list:
                continue
            if sorted(ip_usage_list) == frontend_and_backend_converge:
                extract_net_info(self.plane_dict, _ip_usage="storage_frontend")
                continue
            for ip_usage in ip_usage_list:
                if ip_usage not in self.plane_dict:
                    continue
                extract_net_info(self.plane_dict, ip_usage)
        return ip_addresses

    def get_node_info(self):
        """
        获取和构造节点信息 按照cmdb的接口要求
        节点信息中填写serverSn，则注册后CMDB对节点信息增加hostClass=PM；填写vmNativeId，则注册后CMDB对节点信息增加hostClass=VM
        :return: "deployNodeInfos":[
        {
            "name":"",
            "serverSn": 序列号(物理机). 非必填
            "vmNativeId": 序列号(虚拟机). 非必填
            "ipAddresses":[
                {
                    "netPlane":"",
                    "ip":"",
                    "gateway":"",
                    "netMask":"",
                }
            ],
            "floatIpAddresses":[
                {
                    "floatIp":"",
                    "fixedIp":"",
                    "cloudServiceIndex":""
                }
            ]
        }
        ]
        """
        fsm_list = []
        deploy_node_infos = []
        result = self._parse_rest()
        cluster_info = self.get_cluster_servers()
        for node in result:
            if NodeRole.MANAGEMENT_ROLE in node['role']:
                # 浮动ip部分
                fsm_list.append(str(node['management_internal_ip']))
                continue
            if NodeRole.STORAGE_ROLE not in node['role']:
                continue
            item = {"name": str(node.get('name'))}
            # 节点信息部分
            ip_addresses = self.get_ip_address(node)
            item.update({'ipAddresses': ip_addresses})
            item["serverSn"] = self.get_serial_number(cluster_info, node.get('management_internal_ip'))
            logger.info("get node success. detail:%s" % str(item))
            deploy_node_infos.append(item)
        if not fsm_list:
            logger.error("FSM list length is zero.")
            raise Exception("FSM list length is zero.")
        elif len(fsm_list) > 1:
            fsm_primary_ip = fsm_list[0]
            fsm_slave_ip = fsm_list[1]
        else:
            fsm_primary_ip = fsm_list[0]
            fsm_slave_ip = fsm_list[0]
        return deploy_node_infos, [fsm_primary_ip, fsm_slave_ip]

    def get_fsm_node_info(self, master_fsm, standby_fsm):
        """
        获取和构造节点信息 按照cmdb的接口要求(管理高可用改造工程，FSM虚拟机需要注册CMDB)
        :return:
        """
        deploy_node_infos = []
        node_name = dict()
        if master_fsm:
            node_name.update({master_fsm.get("om_ip"): master_fsm.get("hostname")})
        if standby_fsm:
            node_name.update({standby_fsm.get("om_ip"): standby_fsm.get("hostname")})
        if not node_name:
            return deploy_node_infos
        result = self._parse_rest()
        for node in result:
            if "management" in node['role']:
                current_node_ip = node.get("management_internal_ip")
                if not node_name.get(current_node_ip):
                    continue
                item = {"name": str(node_name.get(current_node_ip))}
                ip_addresses = self.get_ip_address(node)
                item.update({'ipAddresses': ip_addresses})

                logger.info("get node success. detail:%s" % str(item))
                deploy_node_infos.append(item)
        return deploy_node_infos

    def get_cluster_servers(self):
        rsp_obj = self.opr.query_cluster_servers()
        rsp_code, rsp_result, rsp_data = rsp_obj.get_rsp_data()
        logger.info("rsp_code:{}, rsp_result:{}, cluster_servers:{}".format(rsp_code, rsp_result, rsp_data))
        error_code = rsp_result.get('code')
        if rsp_code != 0 or error_code != 0:
            err_msg = "Failed to query the node to join the cluster, Detail:{}".format(rsp_result)
            logger.error(err_msg)
            raise Exception(err_msg)
        return rsp_data

    def get_deploy_type(self):
        ret = DeployType.SEPARATE
        cluster_info = self.get_cluster_servers()
        for node_info in cluster_info:
            usage = node_info.get("usage", [])
            if "replicate_ControlCluster" in usage or "replicate_BusinessCluster" in usage:
                break
            node_role = node_info.get("role", [])
            if NodeRole.COMPUTE_ROLE in node_role and NodeRole.STORAGE_ROLE in node_role:
                ret = DeployType.CONVERGE
                break
        return ret

    def get_version(self):
        url = DeployConstant.HTTPS + self.float_ip + ":" + DeployConstant.PORT + DeployConstant.VERSION
        logger.info("get version url is: %s." % url)
        res = self.opr.rest_client.normal_request(url, 'get').json()
        status, result = res.get('result').get('code'), res.get('data')
        if status == 0:
            logger.info("get version success.detail:%s" % str(res))
            return str(result.get('version'))
        raise Exception('get version fail.detail:%s' % str(res))

    def get_esn(self):
        url = DeployConstant.HTTPS + self.float_ip + ":" + DeployConstant.PORT + DeployConstant.ESN
        logger.info("get ens url is: %s." % url)
        res = self.opr.rest_client.normal_request(url, 'get').json()
        status, result = res.get('result').get('code'), res.get('data')
        if status == 0:
            logger.info("get esn success.detail:%s" % str(res))
            return str(result.get('esn'))
        raise Exception("get esn fail.detail:%s" % str(res))

    def _parse_rest(self):
        """
        POST：https://${float_ip}:${port}/api/v2/network_service/servers -> DeployConstant.NET_SERVICE
        :param float_ip: DistributedStorage float_ip
        :return: all node net info
        """
        url = DeployConstant.HTTPS + self.float_ip + ":" + DeployConstant.PORT + DeployConstant.NET_SERVICE
        logger.info("get node info url is: %s." % url)
        res = self.opr.rest_client.normal_request(url, 'post', {}).json()
        status, result = res.get('result').get('code'), res.get('data')
        if status == 0:
            logger.info("get node info success.detail:%s" % str(res))
            return result
        raise Exception('get node info fail. detail:\n %s' % str(res))


class DistributedStorageRegCMDBBase:
    def __init__(self, project_id, pod_id):
        self.project_id = project_id
        self.pod_id = pod_id

    def get_info_from_dm(self, dm_login_args):
        deal_tool = DealInfoForCMDB(self.project_id, self.pod_id, dm_login_args)
        deal_tool.tool_login()
        try:
            name = deal_tool.get_name()
            version = deal_tool.get_version()
            deploy_node_infos, fsm_list = deal_tool.get_node_info()
        finally:
            deal_tool.tool_logout()
        osd_host_name_list = [deploy_node.get("name") for deploy_node in deploy_node_infos]
        ret = deploy_node_infos, fsm_list, name, version, osd_host_name_list
        return ret
