package com.bocloud.ims.service.installation;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.entity.enums.installation.EndPoints;
import com.bocloud.cmp.model.CpuInfo;
import com.bocloud.cmp.model.ServerCpu;
import com.bocloud.cmp.model.installation.DriveId;
import com.bocloud.cmp.model.installation.catalog.Basic;
import com.bocloud.cmp.model.installation.catalog.BiosBasic;
import com.bocloud.cmp.model.installation.catalog.BiosLanguage;
import com.bocloud.cmp.model.installation.catalog.Board;
import com.bocloud.cmp.model.installation.catalog.BoardBasic;
import com.bocloud.cmp.model.installation.catalog.Cache;
import com.bocloud.cmp.model.installation.catalog.Controller;
import com.bocloud.cmp.model.installation.catalog.Cpu;
import com.bocloud.cmp.model.installation.catalog.Disk;
import com.bocloud.cmp.model.installation.catalog.Drive;
import com.bocloud.cmp.model.installation.catalog.DriveGroup;
import com.bocloud.cmp.model.installation.catalog.Hardware;
import com.bocloud.cmp.model.installation.catalog.Memory;
import com.bocloud.cmp.model.installation.catalog.MemoryBasic;
import com.bocloud.cmp.model.installation.catalog.MemoryInfo;
import com.bocloud.cmp.model.installation.catalog.NodeDetail;
import com.bocloud.cmp.model.installation.catalog.SystemSlot;
import com.bocloud.common.enums.BoCloudMethod;
import com.bocloud.common.model.Result;
import com.bocloud.ims.common.utils.RackHdUtils;
import com.bocloud.ims.common.utils.constant.Constant;
import com.bocloud.ims.common.utils.constant.installation.BasicConstant;
import com.bocloud.ims.common.utils.constant.installation.BiosConstant;
import com.bocloud.ims.common.utils.constant.installation.BiosLangConstant;
import com.bocloud.ims.common.utils.constant.installation.BoardConstant;
import com.bocloud.ims.common.utils.constant.installation.CacheConstant;
import com.bocloud.ims.common.utils.constant.installation.CpuConstant;
import com.bocloud.ims.common.utils.constant.installation.DiskConstant;
import com.bocloud.ims.common.utils.constant.installation.MemoryBasicConstant;
import com.bocloud.ims.common.utils.constant.installation.MemoryConstant;
import com.bocloud.ims.common.utils.constant.installation.SlotConstant;
import com.bocloud.ims.entity.common.MathUtils;
import com.bocloud.ims.entity.resource.NetworkCard;
import com.bocloud.ims.interfaces.installation.CatalogService;
import com.bocloud.ims.interfaces.installation.RackHdService;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 物理机catalog服务实现
 *
 * @author 007
 */
@Service("catalogService")
public class CatalogServiceImpl implements CatalogService {

    public static final Map<String, String> raidMap = new HashMap<>();
    private static Logger logger = LoggerFactory.getLogger(CatalogServiceImpl.class);

    static {
        raidMap.put("0", "RAID0");
        raidMap.put("1", "RAID1");
        raidMap.put("5", "RAID5");
        raidMap.put("6", "RAID6");
        raidMap.put("1+0", "RAID10");
        raidMap.put("50", "RAID50");
        raidMap.put("60", "RAID60");
    }

    @Autowired
    private RackHdService rackHdService;

    private static String parsePciAddress(String pciAddress) {
        String[] arr = pciAddress.split("\\:|\\.");
        String result = pciAddress;
        if (arr.length == 4) {
            result = String.format("pci@%s:%s:%s.%s",
                    MathUtils.addZeroForNum(arr[0], 4),
                    MathUtils.addZeroForNum(arr[1], 2),
                    MathUtils.addZeroForNum(arr[2], 2),
                    MathUtils.addZeroForNum(arr[3], 2));
        } else if (arr.length == 3) {
            result = String.format("pci@0000:%s:%s.%s",
                    MathUtils.addZeroForNum(arr[0], 2),
                    MathUtils.addZeroForNum(arr[1], 2),
                    MathUtils.addZeroForNum(arr[2], 2));
        }
        return result;
    }

    @Override
    public List<String> bmc(String rackhdNodeId) {
        try {
            // bmc channel max is 15
            List<String> bmcIpList = Lists.newArrayList();
            for (int i = 0; i < 16; i++) {
                String url = rackHdService.buildUrl(EndPoints.NODE_CATALOGS_V2, rackhdNodeId) + "/bmc";
                url += i == 0 ? "" : "-" + i;

                Result result = rackHdService.request(null, url, BoCloudMethod.GET);
                if (result.isSuccess()) {
                    JSONObject bmc = JSONObject.parseObject(result.getMessage(), JSONObject.class).getJSONObject(Constant.DATA);
                    String ip = bmc.getString(Constant.IP_ADDRESS);
                    if (StringUtils.isNotEmpty(ip) && !"0.0.0.0".equals(ip) && !bmcIpList.contains(ip)) {
                        bmcIpList.add(ip);
                    }
                }
            }
            return bmcIpList;
        } catch (Exception e) {
            logger.error("获取bmc信息失败:", e);
            return null;
        }
    }

    @Override
    public DriveId driveId(String rackhdNodeId) {
        try {
            Result result = rackHdService.request(null,
                    rackHdService.buildUrl(EndPoints.NODE_CATALOGS_V2, rackhdNodeId) + "/driveid", BoCloudMethod.GET);
            if (result.isFailed()) {
                logger.error("获取driveId信息失败:{}", result.toString());
                return null;
            }
            logger.debug("获取driveId信息内容：{}", result.getMessage());
            // 解析driveId
            JSONObject drive = JSONObject.parseObject(result.getMessage(), JSONObject.class);
            List<DriveId> disks = JSONObject.parseArray(drive.getString(Constant.DATA), DriveId.class);
            if (disks.size() < 1) {
                logger.error("获取driveId信息不正常:{}", disks.toString());
                return null;
            }
            disks.sort(DriveId::compareTo);
            DriveId driveId = disks.get(0);
            logger.info("系统盘为{}", driveId.toString());
            if ("{}".equals(driveId.toString())) {
                logger.error("获取driveId信息不正常:{}", drive.toString());
                return null;
            }
            return driveId;
        } catch (Exception e) {
            logger.error("获取driveId信息异常:", e);
            return null;
        }
    }

    @Override
    public NodeDetail getCatalog(String rackhdNodeId) {
        try {
            NodeDetail nodeDetail = new NodeDetail();
            // get dmi info
            Result result = rackHdService.request(null,
                    rackHdService.buildUrl(EndPoints.NODE_CATALOGS_V2, rackhdNodeId) + "/dmi", BoCloudMethod.GET);
            if (result.isSuccess()) {
                JSONObject dmi = JSONObject.parseObject(result.getMessage(), JSONObject.class);
                this.parseDmi(nodeDetail, dmi.getObject(Constant.DATA, JSONObject.class));
            }

            // get lsscsi info
            result = rackHdService.request(null,
                    rackHdService.buildUrl(EndPoints.NODE_CATALOGS_V2, rackhdNodeId) + "/lsscsi", BoCloudMethod.GET);
            if (result.isSuccess()) {
                JSONObject lsscsi = JSONObject.parseObject(result.getMessage(), JSONObject.class);
                nodeDetail.setDisks(this.parseLsscsi(lsscsi.getJSONArray(Constant.DATA)));
                parseInstallDisk(nodeDetail);
            }

            nodeDetail.setIpmiUsers(this.ipmiUsers(rackhdNodeId));
            return nodeDetail;
        } catch (Exception e) {
            logger.error("获取catalog信息失败:", e);
            return null;
        }
    }

    @Override
    public List<Controller> getRaid(String rackhdNodeId) {
        try {
            List<Controller> controllerList = new ArrayList<>();

            // get megaraid-controllers info
            Result result = rackHdService.request(null,
                    rackHdService.buildUrl(EndPoints.NODE_CATALOGS_V2, rackhdNodeId) + "/megaraid-controllers", BoCloudMethod.GET);
            if (result.isSuccess()) {
                JSONObject raid = JSONObject.parseObject(result.getMessage(), JSONObject.class);
                controllerList.addAll(this.parseRaid(raid.getString(Constant.DATA)));
            }

            // get sasraid-controllers info
            result = rackHdService.request(null,
                    rackHdService.buildUrl(EndPoints.NODE_CATALOGS_V2, rackhdNodeId) + "/sasraid-controllers", BoCloudMethod.GET);
            if (result.isSuccess()) {
                JSONObject raid = JSONObject.parseObject(result.getMessage(), JSONObject.class);
                controllerList.addAll(this.parseRaid(raid.getString(Constant.DATA)));
            }

            // get hpsaraid-controllers info
            result = rackHdService.request(null,
                    rackHdService.buildUrl(EndPoints.NODE_CATALOGS_V2, rackhdNodeId) + "/hpsaraid-controllers", BoCloudMethod.GET);
            if (result.isSuccess()) {
                JSONObject raid = JSONObject.parseObject(result.getMessage(), JSONObject.class);
                controllerList.addAll(this.parseHpsaRaid(raid.getJSONObject(Constant.DATA)));
            }

            return controllerList;
        } catch (Exception e) {
            logger.error("获取Raid信息失败:", e);
            return new ArrayList<>();
        }
    }

    @Override
    public List<String> ipmiUsers(String rackhdNodeId) {
        try {
            // bmc channel max is 15
            List<String> userList = Lists.newArrayList();
            for (int i = 1; i < 16; i++) {
                String url = rackHdService.buildUrl(EndPoints.NODE_CATALOGS_V2, rackhdNodeId) + "/ipmi-user-list-" + i;

                Result result = rackHdService.request(null, url, BoCloudMethod.GET);
                if (result.isSuccess()) {
                    JSONObject user = JSONObject.parseObject(result.getMessage(), JSONObject.class).getJSONObject(Constant.DATA);
                    String name;
                    Collection<Object> values = user.values();
                    for (Object value : values) {
                        if (!(value instanceof JSONObject)) {
                            continue;
                        }
                        JSONObject userInfo = (JSONObject) value;
                        if (userInfo.containsKey(Constant.IPMI_USER_NAME_KEY1)) {
                            // 苏州2.77浪潮服务器ipmi用户的key为__rackhd__
                            name = userInfo.getString(Constant.IPMI_USER_NAME_KEY1);
                        } else if (userInfo.containsKey(Constant.IPMI_USER_NAME_KEY2)) {
                            name = userInfo.getString(Constant.IPMI_USER_NAME_KEY2);
                            // 北京2.211戴尔服务器ipmi用户的key为空string
                        } else if (userInfo.containsKey("")) {
                            // 北京2.211戴尔服务器ipmi用户的key为root
                            name = userInfo.getString("");
                        } else if (userInfo.containsKey(Constant.IPMI_USER_NAME_KEY3)) {
                            // 新版RackHD的ipmi用户的key为Name
                            name = userInfo.getString(Constant.IPMI_USER_NAME_KEY3);
                        } else {
                            name = null;
                        }
                        if (!StringUtils.isEmpty(name) && !Constant.IPMI_USER_VALUE_EMPTY.equals(name) && !userList.contains(name)) {
                            userList.add(name);
                        }
                    }
                }
            }
            return userList;
        } catch (Exception e) {
            logger.error("获取ipmi用户名信息失败:", e);
            return null;
        }
    }

    private void parseInstallDisk(NodeDetail detail) {
        // 获取安装盘的信息
        List<String> diskPaths = Lists.newArrayList();
        for (Disk disk : detail.getDisks()) {
            if (Constant.DISK.equals(disk.getPeripheralType())) {
                diskPaths.add(disk.getDevicePath());
            }
        }
        if (diskPaths.isEmpty()) {
            logger.warn("安装盘信息不正常，为{}", diskPaths);
        } else {
            detail.setInstallDisk(JSON.toJSONString(diskPaths));
        }
    }

    private void parseDmi(NodeDetail detail, JSONObject obj) {
        // get node basic information
        JSONObject jsonBasic = obj.getObject(BasicConstant.JSON_KEY, JSONObject.class);
        detail.setBasic(this.getSystemInfo(jsonBasic));

        // get CPU information
        JSONArray listCpu = obj.getJSONArray(CpuConstant.JSON_KEY);
        detail.setCpus(this.getCpu(listCpu));

        // get memory basic information
        detail.setMemory(new MemoryInfo());
        Object jsonMemoryBasic = obj.getObject(MemoryBasicConstant.JSON_KEY, Object.class);
        if (jsonMemoryBasic instanceof List) {
            List<JSONObject> list = JSONArray.parseArray(jsonMemoryBasic.toString(), JSONObject.class);
            List<MemoryBasic> listBasic = Lists.newArrayList();
            for (JSONObject item : list) {
                listBasic.add(this.getMemoryBasic(item));
            }
            detail.getMemory().setBasic(listBasic);
        } else if (jsonMemoryBasic instanceof JSONObject) {
            detail.getMemory().setBasic(this.getMemoryBasic((JSONObject) jsonMemoryBasic));
        }

        // get memory information
        JSONArray listMemory = obj.getJSONArray(MemoryConstant.JSON_KEY);
        detail.getMemory().setMemories(this.getMemory(listMemory));

        // set memory capacity
        Integer capacity = 0;
        int slot = 0;
        for (Memory memory : detail.getMemory().getMemories()) {
            if (!"No Module Installed".equalsIgnoreCase(memory.getSize())) {
                slot++;
                Integer size = null;
                try {
                    size = Integer.parseInt(memory.getSize().split("MB")[0].trim());
                } catch (Exception ignored) {
                }
                capacity += size;
            }
        }
        detail.getMemory().getBasic().setCapacity(capacity.toString() + " MB");
        detail.getMemory().getBasic().setSlot(Integer.toString(slot));

        // get disk information, this information at from lsscsi

        // get base board information
        detail.setBoard(new Board());
        JSONObject jsonBoard = obj.getObject(BoardConstant.JSON_KEY, JSONObject.class);
        detail.getBoard().setBasic(this.getBoardBasic(jsonBoard));

        // get BIOS basic information
        JSONObject jsonBoisInfo = obj.getObject(BiosConstant.JSON_KEY, JSONObject.class);
        detail.getBoard().setBiosBasic(this.getBiosBasic(jsonBoisInfo));

        // get BIOS language information
        JSONObject jsonBiosLang = obj.getObject(BiosLangConstant.JSON_KEY, JSONObject.class);
        detail.getBoard().setBiosLang(this.getBiosLang(jsonBiosLang));

        // get cache information
        JSONArray listCache = obj.getJSONArray(CacheConstant.JSON_KEY);
        detail.getBoard().setCaches(this.getBoardCache(listCache));

        // get system slots information
        JSONArray listSlot = obj.getJSONArray(SlotConstant.JSON_KEY);
        detail.getBoard().setSlots(this.getSystemSlot(listSlot));
    }

    private List<Cpu> getCpu(JSONArray jsonArray) {
        List<Cpu> listCpu = Lists.newArrayList();
        for (Object obj : jsonArray) {
            if (obj instanceof JSONObject) {
                JSONObject jsonCpu = (JSONObject) obj;
                Cpu cpu = new Cpu();
                cpu.setAssetTag(jsonCpu.getString(CpuConstant.ASSET_TAG));
                cpu.setCharacteristics(parseData(jsonCpu.get(CpuConstant.CHARACTERISTICS)));
                cpu.setCoreCount(jsonCpu.getString(CpuConstant.CORE_COUNT));
                cpu.setCoreEnabled(jsonCpu.getString(CpuConstant.CORE_ENABLED));
                cpu.setCurrentSpeed(jsonCpu.getString(CpuConstant.CORE_ENABLED));
                cpu.setExternalClock(jsonCpu.getString(CpuConstant.EXTERNAL_CLOCK));
                cpu.setFamily(jsonCpu.getString(CpuConstant.FAMILY));
                cpu.setCharacteristics(parseData(jsonCpu.get(CpuConstant.CHARACTERISTICS)));
                cpu.setFlags(parseData(jsonCpu.get(CpuConstant.FLAGS)));
                cpu.setId(jsonCpu.getString(CpuConstant.ID));
                cpu.setL1CacheHandle(jsonCpu.getString(CpuConstant.L1_CACHE_HANDLE));
                cpu.setL2CacheHandle(jsonCpu.getString(CpuConstant.L2_CACHE_HANDLE));
                cpu.setL3CacheHandle(jsonCpu.getString(CpuConstant.L3_CACHE_HANDLE));
                cpu.setManufacturer(jsonCpu.getString(CpuConstant.MANUFACTURER));
                cpu.setMaxSpeed(jsonCpu.getString(CpuConstant.MAX_SPEED));
                cpu.setPartNumber(jsonCpu.getString(CpuConstant.PART_NUMBER));
                cpu.setSerialNumber(jsonCpu.getString(CpuConstant.SERIAL_NUMBER));
                cpu.setSignature(jsonCpu.getString(CpuConstant.SIGNATURE));
                cpu.setSocket(jsonCpu.getString(CpuConstant.SOCKET_DESIGNATION));
                cpu.setStatus(jsonCpu.getString(CpuConstant.STATUS));
                cpu.setThreadCount(jsonCpu.getString(CpuConstant.THREAD_COUNT));
                cpu.setType(jsonCpu.getString(CpuConstant.TYPE));
                cpu.setUpgrade(jsonCpu.getString(CpuConstant.UPGRADE));
                cpu.setVersion(jsonCpu.getString(CpuConstant.VERSION));
                cpu.setVoltage(jsonCpu.getString(CpuConstant.VOLTAGE));
                listCpu.add(cpu);
            }
        }

        return listCpu;
    }

    private Basic getSystemInfo(JSONObject jsonBasic) {
        Basic basic = new Basic();
        if (jsonBasic != null) {
            basic.setFamily(jsonBasic.getString(BasicConstant.FAMILY));
            basic.setManufacturer(jsonBasic.getString(BasicConstant.MANUFACTURER));
            basic.setProductorName(jsonBasic.getString(BasicConstant.PRODUCT_NAME));
            basic.setSkuNumber(jsonBasic.getString(BasicConstant.SKU_NUMBER));
            basic.setSerialNumer(jsonBasic.getString(BasicConstant.SERIAL_NUMBER));
            basic.setUuid(jsonBasic.getString(BasicConstant.UUID));
            basic.setVersion(jsonBasic.getString(BasicConstant.VERSION));
            basic.setWakeupType(jsonBasic.getString(BasicConstant.WAKEUP_TYPE));
        }
        return basic;
    }

    private MemoryBasic getMemoryBasic(JSONObject jsonMemoryBasic) {
        MemoryBasic memoryBasic = new MemoryBasic();
        if (jsonMemoryBasic != null) {
            memoryBasic.setErrorCorrectionType(jsonMemoryBasic.getString(MemoryBasicConstant.ERROR_CORRECTION_TYPE));
            memoryBasic.setErrorInfoHandle(jsonMemoryBasic.getString(MemoryBasicConstant.ERROR_INFO_HANDLE));
            memoryBasic.setLocation(jsonMemoryBasic.getString(MemoryBasicConstant.LOCATION));
            memoryBasic.setMaxCapacity(jsonMemoryBasic.getString(MemoryBasicConstant.MAX_CAPACITY));
            memoryBasic.setDevicesNumber(jsonMemoryBasic.getString(MemoryBasicConstant.DEVICES_NUMBER));
            memoryBasic.setUse(jsonMemoryBasic.getString(MemoryBasicConstant.USE));
        }

        return memoryBasic;
    }

    private List<Memory> getMemory(JSONArray jsonArray) {
        List<Memory> listMemory = Lists.newArrayList();
        for (Object obj : jsonArray) {
            if (obj instanceof JSONObject) {
                JSONObject jsonMemory = (JSONObject) obj;
                Memory memory = new Memory();
                memory.setArrayHandle(jsonMemory.getString(MemoryConstant.ARRAY_HANDLE));
                memory.setAssetTag(jsonMemory.getString(MemoryConstant.ASSET_TAG));
                memory.setBankLocator(jsonMemory.getString(MemoryConstant.BANK_LOCATOR));
                memory.setDataWidth(jsonMemory.getString(MemoryConstant.DATA_WIDTH));
                memory.setErrorInfoHandle(jsonMemory.getString(MemoryConstant.ERROR_INFO_HANDLE));
                memory.setFormFactor(jsonMemory.getString(MemoryConstant.FORM_FACTOR));
                memory.setLocator(jsonMemory.getString(MemoryConstant.LOCATOR));
                memory.setManufacturer(jsonMemory.getString(MemoryConstant.MANUFACTURER));
                memory.setPartNumber(jsonMemory.getString(MemoryConstant.PART_NUMBER));
                memory.setRank(jsonMemory.getString(MemoryConstant.RANK));
                memory.setSerialNumber(jsonMemory.getString(MemoryConstant.SERIAL_NUMBER));
                memory.setSet(jsonMemory.getString(MemoryConstant.SET));
                memory.setSize(jsonMemory.getString(MemoryConstant.SIZE));
                memory.setClockSpeed(jsonMemory.getString(MemoryConstant.CLOCK_SPEED));
                memory.setSpeed(jsonMemory.getString(MemoryConstant.SPEED));
                memory.setTotalWidth(jsonMemory.getString(MemoryConstant.TOTAL_WIDTH));
                memory.setType(jsonMemory.getString(MemoryConstant.TYPE));
                memory.setTypeDetail(jsonMemory.getString(MemoryConstant.TYPE_DETAIL));

                listMemory.add(memory);
            }
        }

        return listMemory;
    }

    private BiosBasic getBiosBasic(JSONObject biosInfo) {
        BiosBasic biosBasic = new BiosBasic();
        if (biosInfo != null) {
            biosBasic.setAddress(biosInfo.getString(BiosConstant.ADDRESS));
            biosBasic.setRevision(biosInfo.getString(BiosConstant.BIOS_REVISION));
            biosBasic.setCharacteristics(parseData(biosInfo.get(BiosConstant.CHARACTERISTICS)));
            biosBasic.setRomSize(biosInfo.getString(BiosConstant.ROM_SIZE));
            biosBasic.setReleaseDate(biosInfo.getString(BiosConstant.RELEASE_DATE));
            biosBasic.setRuntimeSize(biosInfo.getString(BiosConstant.ROM_SIZE));
            biosBasic.setVendor(biosInfo.getString(BiosConstant.VENDOR));
            biosBasic.setVersion(biosInfo.getString(BiosConstant.VERSION));
        }
        return biosBasic;
    }

    private BiosLanguage getBiosLang(JSONObject json) {
        BiosLanguage biosLang = new BiosLanguage();
        if (json != null) {
            biosLang.setInstalledLang(json.getString(BiosLangConstant.CURR_INSTALLED));
            biosLang.setInstallableLang(parseData(json.get(BiosLangConstant.INSTALLABLE_LANG)));
            biosLang.setLangFormat(json.getString(BiosLangConstant.LANG_FORMAT));
        }
        return biosLang;
    }

    private BoardBasic getBoardBasic(JSONObject json) {
        BoardBasic boardBasic = new BoardBasic();
        if (json != null) {
            boardBasic.setManufacturer(json.getString(BoardConstant.MANUFACTURER));
            boardBasic.setProductName(json.getString(BoardConstant.PRODUCT_NAME));
            boardBasic.setSerialNumber(json.getString(BoardConstant.SERIAL_NUMBER));
            boardBasic.setVersion(json.getString(BoardConstant.VERSION));
        }

        return boardBasic;
    }

    private List<Cache> getBoardCache(JSONArray jsonArray) {
        List<Cache> listCache = Lists.newArrayList();
        for (Object obj : jsonArray) {
            if (obj instanceof JSONObject) {
                JSONObject jsonCache = (JSONObject) obj;
                Cache cache = new Cache();
                cache.setAssociativity(jsonCache.getString(CacheConstant.ASSOCIATIVITY));
                cache.setConfiguration(jsonCache.getString(CacheConstant.CONFIGURATION));
                cache.setErrorCorrectionType(jsonCache.getString(CacheConstant.ERROR_CORRECTION_TYPE));
                cache.setInstalledSpamType(jsonCache.getString(CacheConstant.INSTALLED_SPAM));
                cache.setInstalledSize(jsonCache.getString(CacheConstant.INSTALLED_SIZE));
                cache.setLocation(jsonCache.getString(CacheConstant.LOCATION));
                cache.setMaxSize(jsonCache.getString(CacheConstant.MAX_SIZE));
                cache.setOperationalMode(jsonCache.getString(CacheConstant.OPERATIONAL_MODEL));
                cache.setSocketDesignation(jsonCache.getString(CacheConstant.SOCKET_DESIGNATION));
                cache.setSpeed(jsonCache.getString(CacheConstant.SPEED));
                cache.setSupportedSpamType(parseData(jsonCache.get(CacheConstant.SUPPORTED_SRAM)));
                cache.setSystemType(jsonCache.getString(CacheConstant.SYSTEM_TYPE));

                listCache.add(cache);
            }
        }

        return listCache;
    }

    private List<SystemSlot> getSystemSlot(JSONArray jsonArray) {
        List<SystemSlot> listSlot = Lists.newArrayList();
        for (Object obj : jsonArray) {
            if (obj instanceof JSONObject) {
                JSONObject jsonSlot = (JSONObject) obj;
                SystemSlot slot = new SystemSlot();
                slot.setBusAddress(jsonSlot.getString(SlotConstant.BUS_ADDRESS));
                slot.setCharacteristics(parseData(jsonSlot.get(SlotConstant.CHARACTERISTICS)));
                slot.setCurrentUsage(jsonSlot.getString(SlotConstant.CURRENT_USAGE));
                slot.setDesignation(jsonSlot.getString(SlotConstant.DESIGNATION));
                slot.setId(jsonSlot.getString(SlotConstant.ID));
                slot.setLength(jsonSlot.getString(SlotConstant.LENGTH));
                slot.setType(jsonSlot.getString(SlotConstant.TYPE));

                listSlot.add(slot);
            }
        }

        return listSlot;
    }

    private List<Disk> parseLsscsi(JSONArray jsonArray) {
        List<Disk> listDisk = Lists.newArrayList();
        // just get disk information from lsscsi session
        for (Object obj : jsonArray) {
            if (obj instanceof JSONObject) {
                JSONObject jsonDisk = (JSONObject) obj;
                // skip the device that path is "-"
                if (!"-".equals(jsonDisk.getString(DiskConstant.DEVICE_PATH))) {
                    Disk disk = new Disk();
                    disk.setDevicePath(jsonDisk.getString(DiskConstant.DEVICE_PATH));
                    disk.setModelName(jsonDisk.getString(DiskConstant.MODEL_NAME));
                    disk.setPeripheralType(jsonDisk.getString(DiskConstant.PERIPHERAL_TYPE));
                    disk.setRevision(jsonDisk.getString(DiskConstant.REVISION));
                    disk.setScsiInfo(jsonDisk.getString(DiskConstant.SCSI_INFO));
                    disk.setSize(jsonDisk.getString(DiskConstant.SIZE));
                    disk.setVendor(jsonDisk.getString(DiskConstant.VENDOR));

                    listDisk.add(disk);
                }
            }
        }
        return listDisk;
    }

    private List<Controller> parseHpsaRaid(JSONObject data) {
        List<Controller> results = new ArrayList<>();
        for (String model : data.keySet()) {
            JSONObject info = data.getJSONObject(model);

            Controller controller = new Controller();
            controller.setPciAddress(parsePciAddress(info.getString("PCI Address (Domain:Bus:Device_Function)")));
            controller.setSn(info.getString("Serial Number"));
            controller.setControllerId(Integer.parseInt(info.getString("Slot")));
            controller.setType("HPSA");
            controller.setPath("hpsa.py");
            controller.setModel(model);
            List<Drive> drives = new ArrayList<>();
            List<DriveGroup> driveGroups = new ArrayList<>();

            for (String key : info.keySet()) {
                if (key.startsWith("Array:")) {
                    DriveGroup driveGroup = new DriveGroup();
                    List<Drive> dgDrives = new ArrayList<>();
                    for (String dgKey : info.getJSONObject(key).keySet()) {
                        if (dgKey.startsWith("Logical Drive:")) {
                            // 一般一个Array只有一个Logical Drive
                            JSONObject object = info.getJSONObject(key).getJSONObject(dgKey);
                            driveGroup.setId(info.getString("Slot") + ":" +
                                    key.split("Array: ")[1].trim() + ":" +
                                    dgKey.split("Logical Drive:")[1].trim());
                            driveGroup.setDiskName(object.getString("Disk Name"));
                            driveGroup.setIdentifier(object.getString("Unique Identifier"));
                            driveGroup.setSize(object.getString("Size"));
                            driveGroup.setState(object.getString("Status"));
                            driveGroup.setType(raidMap.get(object.getString("Fault Tolerance")));
                        } else if (dgKey.startsWith("physicaldrive")) {
                            // 一般一个Array只有一个或者多个physicaldrive
                            Drive drive = parsePhysicalDrive(info.getJSONObject(key).getJSONObject(dgKey));
                            dgDrives.add(drive);
                        }
                    }
                    for (Drive dgDrive : dgDrives) {
                        dgDrive.setDirveGroup(Integer.parseInt(driveGroup.getId().split(":")[2]));
                        drives.add(dgDrive);
                    }
                    driveGroup.setDrives(dgDrives);
                    driveGroups.add(driveGroup);
                } else if ("Unassigned".equalsIgnoreCase(key)) {
                    for (String unAssignedKey : info.getJSONObject(key).keySet()) {
                        if (unAssignedKey.startsWith("physicaldrive")) {
                            // 一般一个Array只有一个或者多个physicaldrive
                            Drive drive = parsePhysicalDrive(info.getJSONObject(key).getJSONObject(unAssignedKey));
                            drives.add(drive);
                        }
                    }
                }
            }
            controller.setDriveGroups(driveGroups);
            controller.setDrives(drives);
            results.add(controller);
        }
        return results;
    }

    private Drive parsePhysicalDrive(JSONObject object) {
        Drive drive = new Drive();
        drive.setEnclosureId(Integer.parseInt(object.getString("Port").split("I")[0]));
        drive.setSlot(Integer.parseInt(object.getString("Box")));
        drive.setDriveId(Integer.parseInt(object.getString("Bay")));
        drive.setIntf(object.getString("Interface Type"));
        drive.setSeSz(object.getString("Logical/Physical Block Size").split("/")[0] + "B");
        drive.setModel(object.getString("Model"));
        drive.setSize(object.getString("Size"));
        drive.setState(object.getString("Status"));
        return drive;
    }

    private List<Controller> parseRaid(String object) {
        try {
            List<Controller> results = Lists.newArrayList();
            List<JSONObject> controllers = JSONArray.parseArray(JSONObject.parseObject(object).getString("Controllers"),
                    JSONObject.class);
            for (JSONObject controller : controllers) {
                Controller result = new Controller();
                // get controller id
                JSONObject status = controller.getObject("Command Status", JSONObject.class);
                result.setControllerId(status.getInteger("Controller"));

                JSONObject response = controller.getObject("Response Data", JSONObject.class);
                // get controller type
                JSONObject basics = response.getObject("Basics", JSONObject.class);
                if (StringUtils.isNotEmpty(basics.getString("Type"))) {
                    result.setType(basics.getString("Type"));
                } else {
                    result.setType("UNKNOWN");
                }
                result.setModel(basics.getString("Model"));
                result.setSn(basics.getString("Serial Number"));
                result.setSasAddress(basics.getString("SAS Address"));
                result.setPciAddress(parsePciAddress(basics.getString("PCI Address")));
                result.setRevisionNo(basics.getString("Revision No"));
                result.setPath(basics.getString("path"));

                try {
                    SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yy");
                    result.setMfgDate(sdf.parse(basics.getString("Mfg Date")));
                    result.setReworkDate(sdf.parse(basics.getString("Rework Date")));
                } catch (Exception ignored) {
                }

                // get physical drive info
                List<Drive> drives = Lists.newArrayList();
                List<JSONObject> pds = JSONArray.parseArray(response.getString("PD LIST"), JSONObject.class);
                if (pds != null) {
                    for (JSONObject pd : pds) {
                        String eIdSlt = pd.getString("EID:Slt");
                        Integer enclosureId = Integer.parseInt(eIdSlt.split(":")[0]);
                        Integer slot = Integer.parseInt(eIdSlt.split(":")[1]);
                        Drive drive = new Drive(enclosureId, slot, pd.getInteger("DID"), pd.getString("State"),
                                pd.get("DG") instanceof Integer ? pd.getInteger("DG") : null, pd.getString("Size"),
                                pd.getString("Intf"), pd.getString("Med"), pd.getString("SeSz"), pd.getString("Model"));
                        drives.add(drive);
                    }
                }
                result.setDrives(drives);

                // get logical drive info
                List<DriveGroup> driveGroups = Lists.newArrayList();
                List<JSONObject> vds = JSONArray.parseArray(response.getString("VD LIST"), JSONObject.class);
                if (vds != null) {
                    for (JSONObject vd : vds) {
                        DriveGroup driveGroup = new DriveGroup();
                        String id = vd.getString("DG/VD").split("/")[0];
                        driveGroup.setId(id);
                        driveGroup.setType(vd.getString("TYPE"));
                        driveGroup.setState(vd.getString("State"));
                        driveGroup.setAccess(vd.getString("Access"));
                        driveGroup.setConsist("Yes".equalsIgnoreCase(vd.getString("Consist")));
                        driveGroup.setSize(vd.getString("Size"));
                        driveGroup.setName(vd.getString("Name"));
                        List<Drive> vdDrives = drives.stream().filter(drive -> {
                            try {
                                if (drive.getDirveGroup() == null) {
                                    return false;
                                }
                                return Integer.parseInt(id) == drive.getDirveGroup();
                            } catch (NumberFormatException e) {
                                return false;
                            }
                        }).collect(Collectors.toList());
                        if (vdDrives.isEmpty()) {
                            // 处理lsi raid info
                            List<String> ids = JSONObject.parseArray(vd.getString("disks"), String.class);
                            vdDrives = drives.stream().filter(drive -> ids.contains(drive.getEnclosureId() + ":" + drive.getSlot())).collect(Collectors.toList());
                        }
                        vdDrives.forEach(drive -> drive.setDirveGroup(Integer.parseInt(id)));
                        driveGroup.setDrives(vdDrives);
                        driveGroups.add(driveGroup);
                    }
                }
                result.setDriveGroups(driveGroups);

                for (Drive drive : result.getDrives()) {
                    for (DriveGroup driveGroup : driveGroups) {
                        if (driveGroup.getDrives().contains(drive)) {
                            drive.setDirveGroup(Integer.parseInt(driveGroup.getId()));
                            break;
                        }
                    }
                }
                results.add(result);
            }
            return results;
        } catch (Exception e) {
            logger.error("get raid info exception: ", e);
            return new ArrayList<>();
        }
    }

    private Object parseData(Object object) {
        if (object == null) {
            return "";
        }
        if (object instanceof List) {
            List<String> list = JSONArray.parseArray(object.toString(), String.class);
            if (list == null) {
                return new ArrayList<>();
            }
            List<String> newList = Lists.newArrayList();
            for (String info : list) {
                newList.add(RackHdUtils.modify(info));
            }
            return newList;
        } else {
            return object.toString();
        }
    }

    @Override
    public String dhcpIp(String identifier, boolean slient) {
        try {
            Map<String, String> paramMap = Maps.newHashMap();
            paramMap.put("q", identifier);
            Result result = rackHdService.request(null, rackHdService.buildUrl(EndPoints.LOOKUPS_V2, null, paramMap),
                    BoCloudMethod.GET);
            if (result.isFailed()) {
                if (!slient) {
                    logger.error("获取dhcpIp信息失败:{}", result.toString());
                }
                return null;
            }
            // 解析dhcpIp
            List<JSONObject> lookups = JSONObject.parseArray(result.getMessage(), JSONObject.class);
            if (lookups.size() < 1) {
                if (!slient) {
                    logger.error("获取dhcpIp信息不正常:{}", JSON.toJSON(lookups));
                }
                return null;
            }
            return lookups.get(0).getString(Constant.IPADDRESS);
        } catch (Exception e) {
            if (!slient) {
                logger.error("获取dhcpIp信息异常:", e);
            }
            return null;
        }
    }

    @Override
    public Hardware hardWard(String rackhdNodeId) {
        try {
            Result result = rackHdService.request(null,
                    rackHdService.buildUrl(EndPoints.NODE_CATALOGS_V2, rackhdNodeId) + "/lshw", BoCloudMethod.GET);
            if (result.isFailed()) {
                logger.error("获取hardWard信息失败:{}", result.toString());
                return null;
            }
            return JSONObject.parseObject(JSONObject.parseObject(result.getMessage()).getJSONObject(Constant.DATA).toJSONString(), Hardware.class);
        } catch (Exception e) {
            logger.error("获取hardWard信息异常:", e);
            return null;
        }
    }

    @Override
    public List<NetworkCard> interfaces(String rackhdNodeId) {
        try {
            Result result = rackHdService.request(null,
                    rackHdService.buildUrl(EndPoints.NODE_CATALOGS_V2, rackhdNodeId) + "/ohai", BoCloudMethod.GET);
            if (result.isFailed()) {
                logger.error("获取interfaces信息失败:{}", result.toString());
                return null;
            }

            // 获取lldp信息，用于获取网卡链接的交换机信息，交换机端口信息
            Result lldpResult = rackHdService.request(null,
                    rackHdService.buildUrl(EndPoints.NODE_CATALOGS_V2, rackhdNodeId) + "/lldp", BoCloudMethod.GET);
            JSONObject lldpObject = null;
            if (lldpResult.isSuccess()) {
                lldpObject = JSONObject.parseObject(lldpResult.getMessage(), JSONObject.class).getJSONObject(Constant.DATA);
            }

            // 解析ohai, 从network的interfaces里面获取网卡信息
            List<NetworkCard> interfaces = Lists.newArrayList();
            JSONObject ohai = JSONObject.parseObject(result.getMessage(), JSONObject.class);
            JSONObject interfacesInfo = ohai.getJSONObject(Constant.DATA).getJSONObject(Constant.NETWORK)
                    .getJSONObject(Constant.INTERFACES);
            for (String ifName : interfacesInfo.keySet()) {
                if (!"lo".equals(ifName)) {
                    JSONObject perIfInfo = interfacesInfo.getJSONObject(ifName);
                    JSONObject addressInfo = perIfInfo.getJSONObject("addresses");
                    String mac = null;
                    for (String address : addressInfo.keySet()) {
                        if ("lladdr".equals(addressInfo.getJSONObject(address).getString("family"))) {
                            mac = address.toLowerCase();
                        }
                    }
                    String type = perIfInfo.getString("type");
                    if ("eth".equals(type) && mac != null) {
                        NetworkCard networkCard = new NetworkCard();
                        networkCard.setPortType(perIfInfo.getString("type"));
                        networkCard.setNumber(Integer.parseInt(perIfInfo.getString("number")));
                        networkCard.setMtu(Integer.parseInt(perIfInfo.getString("mtu")));
                        networkCard.setMac(mac);
                        networkCard.setStatus(perIfInfo.getString("state"));
                        for (String key : perIfInfo.getJSONObject("addresses").keySet()) {
                            if ("inet".equalsIgnoreCase(perIfInfo.getJSONObject("addresses").getJSONObject(key).getString("family"))) {
                                networkCard.setIpAddress(key.replaceAll("_", "."));
                            }
                        }
                        if (lldpObject != null && lldpObject.getJSONObject(ifName) != null) {
                            /**
                             *  props example
                             *
                             * "{
                             *     "port":{ "descr": "GigabitEthernet 0/13", "ifname":"Gi0/13"},
                             * "    chassis": {
                             *         "descr":"Ruijie Full Gigabit Security & Intelligence Access Switch (S2928G-12P) By Ruijie Networks",
                             *         "name":"Bocloud242",
                             *         "Repeater":{"enabled":"on"},
                             *         "Router":{"enabled":"on"},
                             *         "mgmt-ip":"127.0.0.1",
                             *         "mac":"14:14:4b:5d:dd:07",
                             *         "Bridge":{"enabled":"on"}
                             *     },
                             *     "rid":"1",
                             *     "age":"0 day, 00:04:17",
                             *     "via":"LLDP"
                             * }"
                             */
                            networkCard.setProps(JSON.toJSONString(lldpObject.getJSONObject(ifName)));
                        }
                        interfaces.add(networkCard);
                    }
                }
            }
            if (interfaces.isEmpty()) {
                logger.warn("获取interfaces信息失败");
                return Lists.newArrayList();
            }
            interfaces.sort(Comparator.comparingInt(NetworkCard::getNumber));
            logger.info("获取interfaces为={}", JSON.toJSONString(interfaces));
            return interfaces;
        } catch (Exception e) {
            logger.error("获取interfaces信息异常:", e);
            return Lists.newArrayList();
        }
    }

    @Override
    public ServerCpu cpus(String rackhdNodeId) {
        try {
            Result result = rackHdService.request(null,
                    rackHdService.buildUrl(EndPoints.NODE_CATALOGS_V2, rackhdNodeId) + "/ohai", BoCloudMethod.GET);
            if (result.isFailed()) {
                logger.error("获取cpu信息失败:{}", result.toString());
                return null;
            }
            // 解析ohai, 从cpu里面获取cpu信息
            ServerCpu serverCpu = new ServerCpu();
            JSONObject ohai = JSONObject.parseObject(result.getMessage(), JSONObject.class);
            JSONObject cpuObject = ohai.getJSONObject(Constant.DATA).getJSONObject(Constant.CPU);
            List<CpuInfo> cpus = Lists.newArrayList();
            for (Integer index = 0; index < cpuObject.getInteger("total"); index++) {
                JSONObject perCpuInfo = cpuObject.getJSONObject(index.toString());
                CpuInfo cpuInfo = new CpuInfo();
                cpuInfo.setVendorId(perCpuInfo.getString("vendor_id"));
                cpuInfo.setFamily(perCpuInfo.getInteger("family"));
                cpuInfo.setModel(perCpuInfo.getInteger("model"));
                cpuInfo.setPhysicalId(perCpuInfo.getInteger("physical_id"));
                cpuInfo.setModelName(perCpuInfo.getString("model_name"));
                cpuInfo.setMHz(perCpuInfo.getDouble("mhz"));
                cpuInfo.setCacheSize(perCpuInfo.getString("cache_size"));
                cpuInfo.setCoreId(perCpuInfo.getInteger("core_id"));
                cpuInfo.setCores(perCpuInfo.getInteger("cores"));
                cpuInfo.setFlags(perCpuInfo.getJSONArray("flags").toJavaList(String.class));
                if (cpuInfo.getFlags().contains("vmx") || cpuInfo.getFlags().contains("smx")) {
                    cpuInfo.setVirtualized(true);
                } else {
                    cpuInfo.setVirtualized(false);
                }
                cpus.add(cpuInfo);
            }
            serverCpu.setCpus(cpus);
            serverCpu.setTotal(cpuObject.getInteger("total"));
            serverCpu.setReal(cpuObject.getInteger("real"));
            return serverCpu;
        } catch (Exception e) {
            logger.error("获取cpu信息异常:", e);
            return null;
        }
    }
}
