# -*- 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.DBAdapter.DBConnector import BaseOps
from utils.common.exception import HCCIException
from plugins.ResourceCheck.common.libs import common_libs
from plugins.ResourceCheck.common.libs import raid_libs
from plugins.ResourceCheck.common.libs.BMCCmd import BMCCmdExc


class DoCheckSlots:
    CHECK_CMD = r"""if [ -n "`lspci | grep SAS39xx`" ]; then echo 'yes'; fi"""

    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.cmd_exe = BMCCmdExc()

    @classmethod
    def get_bmc_info(cls, nodes_info):
        bmc_ip_list = []
        nodes_info_dic = {}
        for node in nodes_info:
            bmc_ip = node.get("bmc_ip")
            primary_slot = node.get("primary_slot")
            nodes_info_dic[bmc_ip] = {}
            if primary_slot:
                primary_slot_start, primary_slot_end = primary_slot.split('-')
                primary_slot_total = int(primary_slot_end) - int(primary_slot_start) + 1
                nodes_info_dic.get(bmc_ip)['prm_slot_start'] = primary_slot_start
                nodes_info_dic.get(bmc_ip)['prm_slot_end'] = primary_slot_end
                if primary_slot_total < 4:
                    err_msg = "the number of main storage disks Less than 4 in the LLD"
                    logger.error(err_msg)
                    raise Exception(err_msg)
                bmc_ip_list.append(bmc_ip)
        if not bmc_ip_list:
            logger.error('All hosts have no primary storage disk, please check LLD sheet device_information')
            raise HCCIException(113085)
        return bmc_ip_list, nodes_info_dic

    def procedure(self):
        """检查云服务节点的RAID卡型号是否支持、主存槽位是否有盘"""
        nodes_info = OpenStackNodeManager.get_manage_az_nodes_info(self.db, self.pod_id)
        nodes_info = [node for node in nodes_info if 'osd' in node.get('ref_component')]
        if not nodes_info:
            logger.info('no osd node need to check')
            return
        common_libs.check_flag()
        common_libs.purge_http_log()
        timestamp = str(int(time.time()))

        bmc_ip_list, nodes_info_dic = self.get_bmc_info(nodes_info)

        supported_bmc_ip_list = self.get_support_raid_node(bmc_ip_list, timestamp)

        supported_bmc_ip_list = self.filter_3980_raid_node(supported_bmc_ip_list)

        self.check_slot(nodes_info_dic, supported_bmc_ip_list, timestamp)

        if self.error_list:
            common_libs.report_all_error(self.error_list)

    def check_slot(self, nodes_info_dic, supported_bmc_ip_list, timestamp):
        def check_slot_status():
            ret, _msg = raid_obj.check_disk_has_error(slot=slot)
            if ret:
                logger.error('slot %s has error, msg: %s.' % (slot, _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:
                logger.error('slot %s has been added to a raid.' % slot)
                slot_raid.append(slot)

        for bmc_ip in supported_bmc_ip_list:
            logger.info('Checking node: %s' % bmc_ip)
            prm_slot_start = int(nodes_info_dic.get(bmc_ip).get('prm_slot_start'))
            prm_slot_end = int(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 primary disk slots')
            slot_list = list(range(prm_slot_start, prm_slot_end + 1))
            slot_error = []
            slot_raid = []
            slot_empty = []
            total_slots = 0
            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))
                    check_slot_status()
                    total_slots += 1
                else:
                    msg = 'BMC: %s, slot %s has no disk.' % (bmc_ip, slot)
                    logger.error(msg)
                    slot_empty.append(slot)
            if total_slots < 4:
                err_msg = "The number of physical disks on the current node [%s] is %s," \
                          " which is less than 4." % (bmc_ip, total_slots)
                logger.error(err_msg)
                raise Exception(err_msg)

            self.update_error_dict(bmc_ip, slot_empty, slot_error, slot_raid)

    def filter_3980_raid_node(self, bmc_ip_list):
        cmd_result = self.cmd_exe.run(bmc_ip_list, self.CHECK_CMD)
        remain_list = []
        for bmc_ip in bmc_ip_list:
            if 'yes' in cmd_result.get(bmc_ip).get("stdout"):
                logger.info(f'skip 3908 RAID environment. bmc ip:{bmc_ip}')
                continue
            remain_list.append(bmc_ip)

        return remain_list

    def update_error_dict(self, bmc_ip, slot_empty, slot_error, slot_raid):
        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))
        if slot_raid:
            self.error_list.append(('113088', str(slot_raid), bmc_ip))

    def get_support_raid_node(self, bmc_ip_list, timestamp):
        # 从主机直接获取所有节点RAID卡型号、ID
        # 从raid_lib获取支持的RAID卡型号、ID、raid_check_lib
        host_raid_obj = raid_libs.HostRAIDCls(self.project_id, bmc_ip_list)
        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)
        common_libs.check_all_result(supported_bmc_ip_list, self.check_type, timestamp)
        return supported_bmc_ip_list


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('Manage storage slot check error:{}'.format(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
