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

import utils.common.log as logger
from utils.business.hardware_driver_util import DriverApi
from utils.DBAdapter.DBConnector import BaseOps
from utils.common.exception import HCCIException
from utils.common.fic_base import StepBaseInterface
from utils.common.fic_base import TestCase
from utils.common.message import Message
from plugins.ResourceCheck.common.libs.BMCCmd import BMCCmdExc
from plugins.DistributedStorage.Deploy.scripts.PreCheck.common.device_operate import PreCheckOperate


class DoCheckDisk(TestCase):
    def __init__(self, project_id, pod_id):
        super(DoCheckDisk, self).__init__(project_id, pod_id)
        self.project_id = project_id
        self.pod_id = pod_id
        self.cmd_exe = BMCCmdExc()
        self.db = BaseOps()
        self.bmc_info_lists = self.db.get_install_os_list_info(self.pod_id)

    @classmethod
    def check_disk_size(cls, bmc_info, bmc_ip, discs):
        min_size = 400
        max_size = 2048
        disk_size_list = discs.strip().split(" ")
        for disk_size in disk_size_list:
            logger.info("Disk_size is %s" % disk_size)
            if min_size <= int(disk_size) <= max_size:
                logger.info("Disk_size check is ok !")
            else:
                logger.error("Disk_size check is Failed! BMC: %s,"
                             " Current Configuration: %s GB, Required Configuration: %s ~ %s GB." %
                             (bmc_ip, disk_size, min_size, max_size))

                raise HCCIException("625306", bmc_info.get("equipment_model"), bmc_ip, disk_size, min_size, max_size)

    @classmethod
    def cpu_classification(cls, bmc_ip_list):
        """
        根据cpu类型就行分组处理，x86和arm服务器软raid检查系统盘方法不同
        :param bmc_ip_list:
        :return: list
        """
        logger.info("Checking cpu type")
        cpu_arch_dict = PreCheckOperate.get_system_arch(bmc_ip_list)
        x86_ip_list, arm_ip_list = list(), list()
        for _ip, value in cpu_arch_dict.items():
            if value.get("stdout") == "X86":
                x86_ip_list.append(_ip)
            if value.get("stdout") == "ARM":
                arm_ip_list.append(_ip)
        return x86_ip_list, arm_ip_list

    def procedure(self):
        """
        检查eBackupDISK
        """
        # 查询带装机的IP
        # 判断是否为全消A场景,全消A场景与非全消A场景查询硬盘容量执行不同命令
        x86_full_off_ip_list, arm_full_off_ip_list, other_ip_list = self.divide_ip_list()
        if x86_full_off_ip_list:
            self.check_disk_capacity(x86_full_off_ip_list, FULL_OFF_CMD_FOR_X86)
        if arm_full_off_ip_list:
            self.check_disk_capacity(arm_full_off_ip_list, FULL_OFF_CMD_FOR_ARM)
        if other_ip_list:
            cmd = "fdisk -l | grep 'Disk /dev/sd' | head -n 1 | awk '{print int($5/1024/1024/1024)}'"
            self.check_disk_capacity(other_ip_list, cmd)

    def divide_ip_list(self):
        """
        根据节点是否为full_off_A划分不同节点
        :return:full_off_A节点IP列表和非full_off_A节点（Raid与half_off_A）列表
        """
        full_off_ip_list = []
        other_ip_list = []
        for sub_bmc_info in self.bmc_info_lists:
            bmc_ip = sub_bmc_info.get("bmc_ip")
            if PreCheckOperate.get_1880_raid_node([bmc_ip]).get(bmc_ip).get('is_1880'):
                logger.info("This [{}] is 1880 Raid card, pass".format(bmc_ip))
                continue
            if PreCheckOperate.get_3152_raid_node([bmc_ip]).get(bmc_ip).get('is_bp3152'):
                logger.info("[{}] This is 3152 Raid card, pass".format(bmc_ip))
                continue
            raid_card_flag = DriverApi().check_host_is_hard_raid(bmc_ip)
            if not raid_card_flag:
                full_off_ip_list.append(bmc_ip)
                msg = ("There is no RAID card on the server [%s], full off A mode" % bmc_ip)
                logger.info(msg)
            else:
                other_ip_list.append(bmc_ip)
        x86_ip_list, arm_ip_list = self.cpu_classification(full_off_ip_list)
        logger.info("Full off A bmc ip list X86: %s ARM: %s, other bmc ip list:%s" %
                    (x86_ip_list, arm_ip_list, other_ip_list))
        return x86_ip_list, arm_ip_list, other_ip_list

    def check_disk_capacity(self, bmc_ip_list, cmd):

        result = self.cmd_exe.run(bmc_ip_list, cmd)
        logger.info("Exec result: %s" % result)
        for bmc_info in self.bmc_info_lists:
            bmc_ip = bmc_info.get("bmc_ip")
            if not result.get(bmc_ip):
                continue
            logger.info("bmc_ip:%s Disk check" % bmc_ip)
            bmc_result = result.get(bmc_ip)
            if bmc_result.get("result") == '0':
                discs = bmc_result.get("stdout")
                if not discs:
                    err_msg = "The first disk does not exist."
                    logger.error(err_msg)
                    raise Exception(err_msg)
                self.check_disk_size(bmc_info, bmc_ip, discs)

            else:
                logger.info("Disk_size check is Failed,%s!", bmc_result.get("stderr"))
                raise HCCIException("625301", bmc_result.get("stderr"))


class CheckDisk(StepBaseInterface):
    def __init__(self, project_id, pod_id):
        super(CheckDisk, self).__init__(project_id, pod_id)
        self.project_id = project_id
        self.pod_id = pod_id
        self.implement = DoCheckDisk(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('Storage Node disk check Error, details:{}'.format(traceback.format_exc()))
            return Message(500, e1)
        except Exception as e2:
            logger.error('Storage Node disk check Error, details:{}'.format(traceback.format_exc()))
            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)

    def check(self, project_id, pod_id):
        """
        标准调用接口：重试
        :param project_id:
        :param pod_id:
        :return:
        """
        pass


# ARM服务器全消A场景查询系统盘命令
FULL_OFF_CMD_FOR_ARM = r'''
raid_card_flag=`lspci | grep -E 'SAS3508|SAS3408|SAS3004|SAS3008|Smart Storage PQI|SAS2308|SAS-3 3108|SAS3416|SAS39xx|RAID.*3758|RAID.*3858'`
if [[ -z "${raid_card_flag}" ]]; then
    # pacific ocean or Atlantic Ocean system
    fs_sd1=`ls /sys/devices/pci0000\:74/0000\:74\:03.0/ata1/host*/target*/*/block/ 2>/dev/null`
    fs_sd2=`ls /sys/devices/pci0000\:74/0000\:74\:03.0/ata2/host*/target*/*/block/ 2>/dev/null`
    if [ -n "${fs_sd1}" -a -n "${fs_sd2}" ]; then
        is_pacific=1
    fi

    #系统盘插在后面板IO模组1上
    if [ -z "${fs_sd1}" -o -z "${fs_sd2}" ]; then
        fs_sd1=`ls /sys/devices/pci0000\:74/0000\:74\:04.0/host*/phy-*\:4/port*/end_device-*/target*/*/block/ 2>/dev/null`
        fs_sd2=`ls /sys/devices/pci0000\:74/0000\:74\:04.0/host*/phy-*\:5/port*/end_device-*/target*/*/block/ 2>/dev/null`
    fi
    #系统盘插在后面板IO模组2上
    if [ -z "${fs_sd1}" -o -z "${fs_sd2}" ]; then
        fs_sd1=`ls /sys/devices/pci0000\:b4/0000\:b4\:04.0/host*/phy-*\:4/port*/end_device-*/target*/*/block/ 2>/dev/null`
        fs_sd2=`ls /sys/devices/pci0000\:b4/0000\:b4\:04.0/host*/phy-*\:5/port*/end_device-*/target*/*/block/ 2>/dev/null`
    fi

    Slot_number1=0
    Slot_number2=1
    #匹配后面板槽位失败，默认使用前面板0号与1号槽位创建raid（IO模组1）
    if [ -z "${fs_sd1}" -o -z "${fs_sd2}" ]; then
        fs_sd1=`ls /sys/devices/pci0000\:74/0000\:74\:02.0/host*/phy-*\:${Slot_number1}/port*/end_device-*/target*/*/block/ 2>/dev/null`
        fs_sd2=`ls /sys/devices/pci0000\:74/0000\:74\:02.0/host*/phy-*\:${Slot_number2}/port*/end_device-*/target*/*/block/ 2>/dev/null`
    fi
    #匹配后面板槽位失败，默认使用前面板0号与1号槽位创建raid（IO模组2）
    if [ -z "${fs_sd1}" -o -z "${fs_sd2}" ]; then
        fs_sd1=`ls /sys/devices/pci0000\:b4/0000\:b4\:02.0/host*/phy-*\:${Slot_number1}/port*/end_device-*/target*/*/block/ 2>/dev/null`
        fs_sd2=`ls /sys/devices/pci0000\:b4/0000\:b4\:02.0/host*/phy-*\:${Slot_number2}/port*/end_device-*/target*/*/block/ 2>/dev/null`
    fi
    disk_size1=$(fdisk -l | grep 'Disk /dev/'${fs_sd1} | head -n 1 | awk '{print int($5/1024/1024/1024)}')
    disk_size2=$(fdisk -l | grep 'Disk /dev/'${fs_sd2} | head -n 1 | awk '{print int($5/1024/1024/1024)}')
    echo ${disk_size1} ${disk_size2}
else
    raid_flag=''
    all_disk=`ls -l /dev/sd* | awk '{print $NF}'`
    for cur_disk in ${all_disk}
    do
        raid_flag=`udevadm info ${cur_disk} | grep -E "ID_VENDOR=AVAGO|ID_VENDOR=LSI|ID_VENDOR=MSCC|ID_VENDOR=Adaptec|ID_VENDOR=BROADCOM|ID_VENDOR=HUAWEI" 2>/dev/null`
        if [ -n "${raid_flag}" ]; then
            # 针对 1880 raid卡避免硬盘同厂商名干扰选盘
            if [[ $raid_flag =~ "HUAWEI" ]]; then
                is_raid_vendor=`udevadm info ${cur_disk} | grep "ID_MODEL=RAID" 2>/dev/null`
                if [[ -z "${is_raid_vendor}" ]]; then
                    raid_flag=''
                    continue
                fi
            fi
            raid_disk=`udevadm info -q name ${cur_disk} 2>/dev/null`
            disk_size=$(fdisk -l | grep 'Disk /dev/'${raid_disk} | head -n 1 | awk '{print int($5/1024/1024/1024)}')
            echo ${disk_size} ${disk_size}
            break;
        fi
    done
fi

'''
# X86服务器全消A场景查询系统盘命令
FULL_OFF_CMD_FOR_X86 = r"""
function get_same_capacity_index() {
    local sizeArray=("${@:3}")
    sizeNum=${#sizeArray[@]}
    for((i=0;i<$sizeNum;i++))
    do
       for((j=$i+1;j<$sizeNum;j++))
       do
           if [ ${sizeArray[$i]} -eq ${sizeArray[$j]}  ];then
             eval $1=$i
             eval $2=$j
             return
           fi
       done
    done
}
function get_driver()
{
    devNum=0
    # 组软raid的卡容量大于400
    raid_size=400
    cd /sys/devices/pci0000\:00
    target=`find -name 'target*' -type d | grep ata`
    targetN=($target)
    devNum=${#targetN[@]}
    if [ $devNum -gt 4 ] || [ $devNum -lt 2 ]; then
        return
    fi
    sdArray=()
    sizeArray=()
    for tar in ${targetN[@]}
    do
        cd /sys/devices/pci0000\:00/$tar
        block=`find -name 'block' -type d`
        cd $block
        sd=`ls`
        sd_size=`lsblk | grep $sd | awk 'NR==1 {print $4}' | awk -F '.' '{print int($1)}'`
        if [[ $sd_size -gt $raid_size ]]; then
            sdArray=(${sdArray[@]} $sd)
            sizeArray=(${sizeArray[@]} $sd_size)
        fi
    done
    eval get_same_capacity_index key1 key2 "${sizeArray[@]}"
    eval $1=${sdArray[$key1]}
    eval $2=${sdArray[$key2]}
}
function check_v6_12nvme()
{
    # 检测riser卡单板id 轮询1-3 倒数第四位
    riser_id=()
    for((i=1;i<=3;i++))
    do
        slot_id=$(printf '%#X' $i)
        borad_id=`ipmitool raw 0x30 0x93 0xDB 0x07 0x00 0x27 0x0f $slot_id 0x06 0x00 0x02 | awk '{print $5}'`
        if [ x"$borad_id" = x"" ]; then
            return 1
        fi

        if [ $borad_id = "30" ]; then
            riser_id=(${riser_id[@]} $borad_id)
        fi
        if [ ${#riser_id[@]} -eq 2 ]; then
            break
        fi
    done
    if [ ${#riser_id[@]} -ne 2 ]; then
        return 1
    fi

    # 检测硬件背板单板id 轮询1-10 倒数第四位
    back_id=()
    for((i=1;i<=10;i++))
    do
        slot_id=$(printf '%#X' $i)
        borad_id=`ipmitool raw 0x30 0x93 0xDB 0x07 0x00 0x27 0x05 $slot_id 0x06 0x00 0x02 | awk '{print $5}'`
        if [ x"$borad_id" = x"" ]; then
            return 1
        fi

        if [ $borad_id = "88" ] || [ $borad_id = "4b" ]; then
            back_id=(${back_id[@]} $borad_id)
        fi
        if [ ${#back_id[@]} -eq 2 ]; then
            return 0
        fi
    done
    return 1
}
check_v6_12nvme
v6_12nv_flag=$?
if [ $v6_12nv_flag -eq 0 ]; then
    eval get_driver fs_sd1 fs_sd2
else
    # Arctic Ocean system
    fs_sd1=$(ls /sys/devices/pci0000\:00/0000\:00\:11.5/ata1/host*/target*/*\:*\:*\:*/block/)
    fs_sd2=$(ls /sys/devices/pci0000\:00/0000\:00\:11.5/ata3/host*/target*/*\:*\:*\:*/block/)
fi
#匹配后面板槽位失败，默认使用前面板0号与1号槽位创建raid（IO模组2）
if [ -z "${fs_sd1}" -o -z "${fs_sd2}" ]; then
    exit 1
fi
disk_size1=$(fdisk -l | grep 'Disk /dev/'${fs_sd1} | head -n 1 | awk '{print int($5/1024/1024/1024)}')
disk_size2=$(fdisk -l | grep 'Disk /dev/'${fs_sd2} | head -n 1 | awk '{print int($5/1024/1024/1024)}')
echo ${disk_size1} ${disk_size2}
"""
