# coding=utf-8
# Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
import IPy

from utils.common.exception import HCCIException
from utils.common.message import Message
from utils.common.fic_base import StepBaseInterface
from utils.DBAdapter.DBConnector import BaseOps
from utils.business.project_condition_utils import get_project_condition_boolean
import utils.common.log as logger
from utils.business.vm_util import can_vm_pinged_to
from plugins.DistributedStorage.utils.common.general_query import GeneralQuery


class CheckIPOccupy(StepBaseInterface):
    def __init__(self, project_id, pod_id):
        super(CheckIPOccupy, self).__init__(project_id, pod_id)
        self.project_id = project_id
        self.pod_id = pod_id
        self.db = BaseOps()
        self.get_param = self.db.get_user_input_cloud_param_by_key

    @staticmethod
    def invalid_float_ip(float_ip, ipy_obj, gateway, pool_end, pool_start):
        return (float_ip not in ipy_obj) \
               or (IPy.IP(pool_start).int() <= IPy.IP(float_ip).int() <= IPy.IP(pool_end).int()) \
               or float_ip == gateway \
               or float_ip == str(ipy_obj.net()) \
               or float_ip == str(ipy_obj.broadcast())

    def pre_check(self, project_id, pod_id):
        """
        插件内部接口：执行安装前的资源预检查，该接口由execute接口调用，工具框架不会直接调用此接口。
        :param project_id:
        :param pod_id:
        :return:Message类对象
        """
        return Message(200)

    def execute(self, project_id, pod_id):
        try:
            self.procedure(project_id)
            return Message(200)
        except HCCIException as e1:
            return Message(500, e1)
        except Exception as err:
            return Message(500, err)

    def rollback(self, project_id, pod_id):
        """
        标准调用接口：执行回滚
        :param project_id:
        :param pod_id:
        :return:Message类对象
        """
        return Message(200)

    def retry(self, project_id, pod_id):
        """
        标准调用接口：重试
        :return: Message类对象
        """
        return self.execute(project_id, pod_id)

    def check(self, project_id, pod_id):
        """
        标准调用接口：重试
        :param project_id:
        :param pod_id:
        :return:Message类对象
        """
        return Message(200)

    def procedure(self, project_id):
        params = dict()
        if get_project_condition_boolean(
                project_id, 'TenantStorFB80&(ExpansionAZ_KVM|ExpansionPOD_KVM|ExpansionAZ_BMS|ExpansionPOD_BMS)'):
            expansion_new_fsm_float_ip = self.db.get_user_input_cloud_param_by_key(
                project_id, "expansion_new_fsm_float_ip")
            params['expansion_new_fsm_float_ip'] = expansion_new_fsm_float_ip
        elif get_project_condition_boolean(project_id, 'TenantStorFB80&ExpansionServiceStorage'):
            expansion_fusionstorage_float_ip = self.db.get_user_input_cloud_param_by_key(
                project_id, "expansion_fusionstorage_float_ip")
            params['expansion_fusionstorage_float_ip'] = expansion_fusionstorage_float_ip

        for key, value in params.items():
            logger.info("Start to ping %s[%s]" % (key, value))
            if can_vm_pinged_to(value):
                raise HCCIException(626307, "{}:{}".format(key, value))
            self.check_float_ip_range(value)
            self.check_ip_conflict(value)

    def check_float_ip_range(self, float_ip):
        """
        1、浮动IP必须在external_om平面内，但不能在external_om_info.get("subnet")内，且不为网关、广播地址、网络号等。
        2、如果第1点不满足，且为新增external_om场景，进一步检查浮动IP是否在新增的external_om子网内
        :param float_ip:
        :return:
        """
        external_om_info_list = GeneralQuery.get_external_om_info(self.db, self.pod_id, self.project_id)
        logger.info("external_om_info_list:{}".format(external_om_info_list))
        subnet = []
        can_not_use_ip_pool = []
        gateway = []
        broadcast = []
        network_id = []
        for external_om_info in external_om_info_list:
            external_om_gw = external_om_info.get("gateway")
            ipy_obj = IPy.IP(external_om_info.get("subnet"))
            pool_start, pool_end = external_om_info.get("poolstart"), external_om_info.get("poolend")

            subnet.append(external_om_info.get("subnet"))
            can_not_use_ip_pool.append("{}-{}".format(pool_start, pool_end))
            gateway.append(external_om_gw)
            broadcast.append(str(ipy_obj.broadcast()))
            network_id.append(str(ipy_obj.net()))

            if self.invalid_float_ip(float_ip, ipy_obj, external_om_gw, pool_end, pool_start):
                logger.warn(f"The floating IP {float_ip} is not in the external_om subnet: {ipy_obj}.")
            else:
                logger.info(f"The floating IP {float_ip} is in the external_om subnet: {ipy_obj}. Pass.")
                return

        if get_project_condition_boolean(self.project_id, "Expansion_ComputeManageNetwork"):
            networks = self.db.get_computing_node_network_planning(self.project_id)
            for network_dic in networks:
                if network_dic.get("network_plane") != "external_om":
                    continue
                ipy_obj = IPy.IP(network_dic.get("network_segment_plan", "0.0.0.0/0"))
                pool_start, pool_end = network_dic.get("start_addr"), network_dic.get("end_addr")
                external_om_gw = network_dic.get("gateway")
                if self.invalid_float_ip(float_ip, ipy_obj, external_om_gw, pool_end, pool_start):
                    logger.warn(f"The floating IP {float_ip} is not in the new external_om subnet: {ipy_obj}.")
                else:
                    logger.info(f"The floating IP {float_ip} is in the external_om subnet: {ipy_obj}. Pass.")
                    return
                if network_dic.get("network_segment_plan"):
                    subnet.append(network_dic.get("network_segment_plan"))
                    can_not_use_ip_pool.append("{}-{}".format(pool_start, pool_end))
                    gateway.append(external_om_gw)
                    broadcast.append(str(ipy_obj.broadcast()))
                    network_id.append(str(ipy_obj.net()))

        raise HCCIException(626398, float_ip, subnet, can_not_use_ip_pool, gateway, broadcast, network_id)

    def check_ip_conflict(self, float_ip):
        if not get_project_condition_boolean(self.project_id, "!TenantStorFB_Heterogeneous"):
            return

        def check_conflict_(ip_addr, gateway, ip_range):
            if ip_addr == gateway:
                raise HCCIException(626399, ip_addr, gateway)
            start_ip, end_ip = ip_range.split("-")
            if IPy.IP(start_ip).int() <= IPy.IP(ip_addr).int() <= IPy.IP(end_ip).int():
                raise HCCIException(626399, ip_addr, ip_range)

        logger.info('Get network plane parameters from LLD')
        fusionstorage_manage_gateway = self.get_param(self.project_id, "fusionstorage_manage_gateway")
        fusionstorage_manage_range = self.get_param(self.project_id, "fusionstorage_manage_range")
        check_conflict_(float_ip, fusionstorage_manage_gateway, fusionstorage_manage_range)

        fusionstorage_service_gateway = self.get_param(self.project_id, "fusionstorage_service_gateway")
        fusionstorage_service_range = self.get_param(self.project_id, "fusionstorage_service_range")
        check_conflict_(float_ip, fusionstorage_service_gateway, fusionstorage_service_range)
        if get_project_condition_boolean(self.project_id, "DRStorage_TFB_Sep&(CSDRStorage_TFB|CSHAStorage_TFB)"):
            fusionstorage_rep_range1 = self.get_param(self.project_id, "fusionstorage_rep_range1")
            fusionstorage_rep_gateway1 = self.get_param(self.project_id, "fusionstorage_rep_gateway1")
            check_conflict_(float_ip, fusionstorage_rep_gateway1, fusionstorage_rep_range1)

            fusionstorage_rep_range2 = self.get_param(self.project_id, "fusionstorage_rep_range2")
            fusionstorage_rep_gateway2 = self.get_param(self.project_id, "fusionstorage_rep_gateway2")
            check_conflict_(float_ip, fusionstorage_rep_gateway2, fusionstorage_rep_range2)

            if get_project_condition_boolean(self.project_id, "CSHAStorage_TFB"):
                fusionstorage_arb_range = self.get_param(self.project_id, "fusionstorage_arb_range")
                fusionstorage_arb_gateway = self.get_param(self.project_id, "fusionstorage_arb_gateway")
                check_conflict_(float_ip, fusionstorage_arb_gateway, fusionstorage_arb_range)

        if get_project_condition_boolean(self.project_id, "TenantStorBackendNetSep"):
            fusionstorage_inner_range = self.get_param(self.project_id, "fusionstorage_inner_range")
            fusionstorage_inner_gateway = self.get_param(self.project_id, "fusionstorage_inner_gateway")
            check_conflict_(float_ip, fusionstorage_inner_gateway, fusionstorage_inner_range)
