# -*- coding: utf-8 -*-
# Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
import time
import os
import traceback
import utils.common.log as logger
from utils.common.fic_base import StepBaseInterface
from utils.common.OpenStackNodeManager import OpenStackNodeManager
from utils.common.message import Message
from utils.common.exception import HCCIException
from utils.DBAdapter.DBConnector import BaseOps
from plugins.ResourceCheck.common.libs import common_libs
from plugins.ResourceCheck.common.libs import raid_libs
from plugins.ResourceCheck.common.libs.BMCCmd import BMCCmdExc
from utils.business.resource_check.do_disable_jbod import DoDisableJBODLibs
from plugins.DistributedStorage.Deploy.scripts.PreCheck.common.device_operate import PreCheckPublicOperate


class DoCheckSlots:
    def __init__(self, project_id, pod_id):
        self.this_path = os.path.realpath(__file__ + '/../../')
        self.script_dst_dir = "/home/pkg/precheck/scripts/"
        self.db = BaseOps()
        self.project_id = project_id
        self.pod_id = pod_id
        self.check_type = 'disk_info'
        self.error_list = []
        self.bmc_ip_list_zk = []
        self.primary_storage_bmc_ip_list = []
        self.nodes_info_dic = {}
        self.dc_node_infos = dict()
        self.cmd_exe = BMCCmdExc()
        self.nodes_info = OpenStackNodeManager.get_manage_az_nodes_info(self.db, self.pod_id)

    def procedure(self):
        """检查管理节点、云服务节点的CPU"""
        all_bmc_ip_list = [node.get("bmc_ip") for node in self.nodes_info]
        rest_bmc_ip_list, _ = PreCheckPublicOperate.filter_3980_raid_node(all_bmc_ip_list, self.cmd_exe)
        if not rest_bmc_ip_list:
            return

        self.get_bmc_ip_and_slot_map(rest_bmc_ip_list)

        common_libs.check_flag()
        common_libs.purge_http_log()
        timestamp = str(int(time.time()))
        supported_bmc_ip_list = self.get_support_bmc_ip_lst(timestamp)
        common_libs.check_all_result(supported_bmc_ip_list, self.check_type, timestamp)
        self.do_check_raid_slot(supported_bmc_ip_list, timestamp)

    def procedure_3908(self):
        bmc_ip_list = [node.get("bmc_ip") for node in self.nodes_info]
        _, node_3908_bmc_ip_list = PreCheckPublicOperate.filter_3980_raid_node(bmc_ip_list, self.cmd_exe)
        if not node_3908_bmc_ip_list:
            logger.info("The 3908 RAID controller card node does not exist. Pass")
            return
        logger.info("The 3908 RAID controller card nodes:{}".format(node_3908_bmc_ip_list))
        body = [{"bmc_role": "osd", "bmc_ip": bmc_ip} for bmc_ip in node_3908_bmc_ip_list]
        DoDisableJBODLibs(self.project_id, body).disable_jbod()

        self.get_bmc_ip_and_slot_map(node_3908_bmc_ip_list)

        common_libs.check_flag()
        common_libs.purge_http_log()
        timestamp = str(int(time.time()))
        for bmc_ip in node_3908_bmc_ip_list:
            raid_libs.CheckRAID(self.project_id, bmc_ip, self.check_type, timestamp).create_check_script()
        common_libs.check_all_result(node_3908_bmc_ip_list, self.check_type, timestamp)
        self.do_check_raid_slot(node_3908_bmc_ip_list, timestamp)

    def get_bmc_ip_and_slot_map(self, bmc_ip_list):
        node_info = []
        for node in self.nodes_info:
            if node.get("bmc_ip") in bmc_ip_list:
                node_info.append(node)
        self.get_zk_and_primary_node_info(node_info)

    def do_check_raid_slot(self, supported_bmc_ip_list, timestamp):
        for bmc_ip in supported_bmc_ip_list:
            logger.info('Checking node: %s' % bmc_ip)
            zk_slot = self.nodes_info_dic.get(bmc_ip).get('zk_slot')
            prm_slot_start = int(self.nodes_info_dic.get(bmc_ip).get('prm_slot_start'))
            prm_slot_end = int(self.nodes_info_dic.get(bmc_ip).get('prm_slot_end'))
            raid_obj = raid_libs.CheckRAID(self.project_id, bmc_ip, self.check_type, timestamp)
            raid_obj.get_disk_info_all()
            logger.info('Checking zk and primary disk slots')
            slot_list = list(range(prm_slot_start, prm_slot_end + 1))
            if zk_slot:
                zk_slot = int(zk_slot)
                slot_list = slot_list + [zk_slot]
            self.check_slot_status(bmc_ip, raid_obj, slot_list)
        if self.error_list:
            common_libs.report_all_error(self.error_list)

    def check_slot_status(self, bmc_ip, raid_obj, slot_list):
        slot_error = []
        slot_raid = []
        slot_empty = []
        for slot in slot_list:
            if raid_obj.check_disk_exist(slot):
                logger.info('BMC: %s,slot %s has a disk.' % (bmc_ip, slot))
                logger.info('Checking error of BMC: %s, slot %s.' % (bmc_ip, slot))
                ret, msg = raid_obj.check_disk_has_error(slot=slot)
                if ret:
                    msg = 'slot %s has error,msg: %s.' % (slot, msg)
                    logger.error(msg)
                    slot_error.append(slot)

                logger.info('Checking if BMC: %s slot %s is a member of RAID' % (bmc_ip, slot))
                ret = raid_obj.check_disk_in_raid(slot=slot)
                if ret:
                    msg = 'slot %s has been added to a raid.' % slot
                    logger.error(msg)
                    slot_raid.append(slot)
            else:
                msg = 'BMC: %s, slot %s has no disk.' % (bmc_ip, slot)
                logger.error(msg)
                slot_empty.append(slot)
        self.update_error_list(bmc_ip, slot_empty, slot_error, slot_raid)

    def update_error_list(self, bmc_ip, slot_empty, slot_error, slot_raid):
        if slot_raid:
            self.error_list.append(('113088', str(slot_raid), bmc_ip))
        if slot_empty:
            self.error_list.append(('113081', str(slot_empty), bmc_ip))
        if slot_error:
            self.error_list.append(('113083', str(slot_error), bmc_ip))

    def get_support_bmc_ip_lst(self, timestamp):
        # 从主机直接获取所有节点RAID卡型号、ID
        bmc_ip_list = list(set(self.bmc_ip_list_zk + self.primary_storage_bmc_ip_list))
        host_raid_obj = raid_libs.HostRAIDCls(self.project_id, bmc_ip_list)
        # 从raid_lib获取支持的RAID卡型号、ID、raid_check_lib
        supported_bmc_ip_list = []
        for bmc_ip in bmc_ip_list:
            logger.info('Checking RAID compatibility for node:%s' % bmc_ip)
            host_supported_ctl_info = host_raid_obj.check_disk_controller_model(bmc_ip)
            if not host_supported_ctl_info:
                host_raid_dev_list = host_raid_obj.get_host_raid_dev_info(bmc_ip)
                logger.error('RAID controller on node: %s is not supported.Model: %s' %
                             (bmc_ip, host_raid_dev_list))
                self.error_list.append(('113061', host_raid_dev_list, bmc_ip))
            else:
                logger.info('Create checking script for node: %s' % bmc_ip)
                raid_libs.CheckRAID(self.project_id, bmc_ip, self.check_type, timestamp).create_check_script()
                supported_bmc_ip_list.append(bmc_ip)
        return supported_bmc_ip_list

    def get_zk_and_primary_node_info(self, nodes_info):
        primary_slot_total = 0
        for node in nodes_info:
            bmc_ip = node["bmc_ip"]
            zk_slot = node["zk_slot"]
            primary_slot = node["primary_slot"]
            node_dc = node.get("site")
            if not node_dc:
                node_dc = "dc001"
            if node_dc not in self.dc_node_infos:
                self.dc_node_infos[node_dc] = {}
            self.nodes_info_dic[bmc_ip] = {}
            if zk_slot:
                if "zk_slot_total" not in self.dc_node_infos.get(node_dc):
                    self.dc_node_infos.get(node_dc)["zk_slot_total"] = 0
                self.dc_node_infos.get(node_dc)["zk_slot_total"] += 1
                self.nodes_info_dic.get(bmc_ip)['zk_slot'] = zk_slot
                self.bmc_ip_list_zk.append(bmc_ip)
            if primary_slot:
                primary_slot_start, primary_slot_end = primary_slot.split('-')
                primary_slot_total = primary_slot_total + int(primary_slot_end) - int(primary_slot_start) + 1
                if "primary_slot_total" not in self.dc_node_infos.get(node_dc):
                    self.dc_node_infos.get(node_dc)["primary_slot_total"] = 0
                self.dc_node_infos.get(node_dc)["primary_slot_total"] += int(primary_slot_end) - int(
                    primary_slot_start) + 1
                self.nodes_info_dic.get(bmc_ip)['prm_slot_start'] = primary_slot_start
                self.nodes_info_dic.get(bmc_ip)['prm_slot_end'] = primary_slot_end
                self.primary_storage_bmc_ip_list.append(bmc_ip)
        return primary_slot_total


class CheckSlots(StepBaseInterface):
    def __init__(self, project_id, pod_id):
        super(CheckSlots, self).__init__(project_id, pod_id)
        self.project_id = project_id
        self.pod_id = pod_id
        self.implement = DoCheckSlots(self.project_id, self.pod_id)

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

    def execute(self, project_id, pod_id):
        try:
            self.implement.procedure()
        except HCCIException as e:
            logger.error(traceback.format_exc())
            return Message(500, e)
        except Exception as e:
            logger.error("Error msg:%s" % str(e))
            logger.error(traceback.format_exc())
            return Message(500, HCCIException('113999', str(e)))
        return Message(200)

    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:
        """
        pass


class Check3908Slots(CheckSlots):
    def execute(self, project_id, pod_id):
        try:
            self.implement.procedure_3908()
        except HCCIException as e:
            logger.error("Error msg:%s" % str(e))
            return Message(500, e)
        except Exception as e:
            logger.error(traceback.format_exc())
            return Message(500, HCCIException('113999', str(e)))
        return Message(200)
