#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
import re

from utils.business.param_util import ParamUtil
from utils.common.exception import HCCIException
from utils.common.fic_base import StepBaseInterface, TestCase
from utils.common.message import Message
import utils.common.log as logger

from plugins.ResourceCheck.common.libs.BMCCmd import BMCCmdExc
from plugins.DistributedStorage.utils.common.deploy_constant import DeployConstant
from plugins.DistributedStorage.Deploy.scripts.PreCheck.implement.job_disk_check import DiskCheck


class DoCheckSlotType(TestCase):
    def __init__(self, project_id, pod_id):
        super(DoCheckSlotType, self).__init__(project_id, pod_id)
        self.params = ParamUtil()
        self.pod_id = pod_id
        self.project_id = project_id
        self.exist_err = []
        self.type_err = []
        self.size_err = []
        self.raid_card_id = "3108|3408|3416|3508|39xx|1e81|3008|3758"
        self.bmc_info_list = self.db.get_install_os_list_info(self.pod_id, 'rep')

    @staticmethod
    def check_size(disk_info):
        """
        检查磁盘大小
        :param disk_info:
        :return:
        """
        disk_required = 110
        disk_size = ''
        disk_size_unit = ''
        for item in disk_info:
            key = item.split(': ')[0]
            value = item.split(': ')[1]
            if 'Raw Size' == key:
                disk_size = value.split(' ')[0]
                disk_size_unit = value.split(' ')[1]
                break
        if disk_size_unit == 'TB':
            size = float(disk_size) * 1024
        else:
            size = float(disk_size)
        if size < disk_required:
            return False
        return True

    @staticmethod
    def check_type(disk_info):
        """
        检查自动化分区磁盘类型  已支持SATA,SAS -- HHD,SSD, 未加NVME
        :param disk_info:
        :return:
        """
        logger.info('Start to check disk type info ,info:%s' % disk_info)
        disk_type = ['Solid State Device', 'Hard Disk Device']
        for line in disk_info:
            key, value = line.split(":")
            if key.strip() == 'Media Type' and value.strip() in disk_type:
                return True, value.strip()
        return False, ''

    @staticmethod
    def get_disk_info(result, slot='1'):
        """
        获取对应槽位磁盘信息
        :param result:
        :param slot:
        :return:
        """
        disk_info_list = []
        disk_info_start = 0
        for line in result.splitlines():
            pattern = 'Slot Number: ' + str(slot)
            if pattern == line:
                disk_info_start = 1
                continue
            if disk_info_start == 1:
                if not re.findall('Slot Number:', line):
                    disk_info_list.append(line)
                else:
                    break
        return disk_info_list

    @staticmethod
    def check_disk_exist(disk_info, slot):
        pattern = 'Slot Number: ' + str(slot) + '\n'
        if re.findall(pattern, disk_info):
            return True
        else:
            return False

    @staticmethod
    def nvme_zk_disk_check(node_infos):
        """
        nvme盘做元数据：cache_type=='none'，主存primary_type=='ssd_card' --> 不检查槽位
        """
        for node_info in node_infos:
            rep_zp_type = node_info.get("replication_cluster_meta_type")
            logger.info("replication_cluster_meta_type:{}".format(rep_zp_type))
            if rep_zp_type != DeployConstant.REP_METADATA_SUPPORT_DISK_TYPE[2]:
                return False
        return True

    def procedure(self):
        self.check_disk()
        if self.exist_err or self.type_err or self.size_err:
            raise HCCIException(627428, self.exist_err, self.type_err, self.size_err)

    def check_disk(self):
        """
        使用通用接口执行检查命令。获取检查结果。39xx:3908raid卡
        :return:
        """
        bmc_client = BMCCmdExc()
        rep_bmc_info = [info for info in self.bmc_info_list if 'rep_ctl' in info.get('deploy_component')]
        is_nvme = self.nvme_zk_disk_check(rep_bmc_info)
        if is_nvme:
            return

        exist_raid_card_node_bmc_list = self.get_exist_raid_card_rep_node(bmc_client, rep_bmc_info)

        type_list, msg_list = [], []
        result_list = bmc_client.run(exist_raid_card_node_bmc_list, CHECK_CMD, time_out=60)
        for info in rep_bmc_info:
            bmc_ip = info.get("bmc_ip")
            if bmc_ip not in exist_raid_card_node_bmc_list:
                logger.error('node BMC [%s] is not raid list. Skip check' % bmc_ip)
                continue
            res = result_list.get(bmc_ip)
            if res.get('result') == '1':
                logger.error('BMCIP[%s] get disk info false.' % bmc_ip)
                self.exist_err.append(bmc_ip)
                continue
            # 检查槽位是否存在
            slot = info['replication_cluster_meta_info']
            flag = self.check_disk_exist(res.get('stdout'), slot)
            if not flag:
                logger.error('BMCIP[%s] slot check fail ,the slot is invalid.' % bmc_ip)
                self.exist_err.append(bmc_ip)
                continue
            logger.info('BMCIP[%s] slot check success' % bmc_ip)
            # 根据槽位获取对应槽位信息并解析
            disk_info = self.get_disk_info(res.get('stdout'), slot)
            flag, disk_type = self.check_type(disk_info)
            if not flag:
                logger.error('BMCIP[%s] disk type check fail.' % bmc_ip)
                self.type_err.append(info['bmc_ip'])
            type_list.append({info['bmc_ip']: disk_type})
            msg_list.append(disk_type)
            logger.info('BMCIP[%s] disk type check success.' % bmc_ip)
            # 检查对应槽位磁盘大小
            flag = self.check_size(disk_info)
            if not flag:
                logger.error('BMCIP[%s] disk size is not enough.' % bmc_ip)
                self.size_err.append(bmc_ip)
            logger.info('BMCIP[%s] disk size is enough.' % bmc_ip)
        # 检查一致性
        if len(set(msg_list)) > 1:
            logger.error('disk type exist different type,detail:%s' % str(type_list))
            raise HCCIException(627429, str(type_list))

    def get_exist_raid_card_rep_node(self, bmc_client, rep_bmc_info):
        rep_node_bmc_list = [info.get("bmc_ip") for info in rep_bmc_info]
        lspcicmd = 'lspci | grep RAID'
        pattern = re.compile(self.raid_card_id)
        results_list = bmc_client.run(rep_node_bmc_list, lspcicmd, time_out=60)
        for info in rep_bmc_info:
            bmc_ip = info.get("bmc_ip")
            node_res = results_list.get(bmc_ip)
            raid_ret = node_res.get("stdout")
            if not pattern.findall(raid_ret):
                logger.info('Failed to find raid[{}] on node[{}]. Skip check'.format(self.raid_card_id, bmc_ip))
                rep_node_bmc_list.remove(bmc_ip)
        return rep_node_bmc_list


class CheckSlotType(StepBaseInterface):
    def __init__(self, project_id, pod_id):
        super(CheckSlotType, self).__init__(project_id, pod_id)
        self.project_id = project_id
        self.pod_id = pod_id
        self.implement = DoCheckSlotType(project_id, 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 e1:
            logger.error(str(e1))
            return Message(500, e1)
        except Exception as e2:
            logger.error(str(e2))
            return Message(500, HCCIException('625301', str(e2)))
        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)


GREP_CMD = "egrep 'Slot Number|Error Count|Failure Count|Raw Size|Firmware state|Foreign State|PD Type|Media Type'"
CHECK_CMD = """
myCurl http://$FCDIP/tools/MegaCli.zip -o MegaCli.zip
unzip -o MegaCli.zip || {
        CHECKRESULT=failed
        CHECKOUTPUT="113082"
        feedback $TIMESTAMP $CHECKTYPE $CHECKRESULT "$CHECKOUTPUT"
        exit 1
}
./MegaCli64 -pdlist -a0 | %s
""" % GREP_CMD
