com.dell.asm.asmcore.asmmanager.util.deployment.HardwareUtil.java Source code

Java tutorial

Introduction

Here is the source code for com.dell.asm.asmcore.asmmanager.util.deployment.HardwareUtil.java

Source

/**************************************************************************
 *   Copyright (c) 2013 Dell Inc. All rights reserved.                    *
 *                                                                        *
 * DELL INC. CONFIDENTIAL AND PROPRIETARY INFORMATION. This software may  *
 * only be supplied under the terms of a license agreement or             *
 * nondisclosure agreement with Dell Inc. and may not be copied or        *
 * disclosed except in accordance with the terms of such agreement.       *
 **************************************************************************/
package com.dell.asm.asmcore.asmmanager.util.deployment;

import com.dell.asm.asmcore.asmmanager.client.hardware.RAIDConfiguration;
import com.dell.asm.asmcore.asmmanager.client.hardware.VirtualDisk;
import com.dell.asm.asmcore.asmmanager.client.servicetemplate.TemplateRaidConfiguration;
import com.dell.asm.asmcore.asmmanager.client.servicetemplate.VirtualDiskConfiguration;
import com.dell.asm.asmcore.asmmanager.exception.AsmManagerNotEnoughDisksException;
import com.dell.asm.localizablelogger.LocalizableMessageService;
import com.dell.asm.server.client.policy.RaidLevel;
import com.dell.pg.asm.server.client.device.Controller;
import com.dell.pg.asm.server.client.device.PhysicalDisk;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

/**
 * Prepare RAID configuration
 */
public class HardwareUtil {
    private static final Logger logger = Logger.getLogger(HardwareUtil.class);
    private static HardwareUtil instance;

    private final static int MAX_DRIVES_RAID1 = 2;

    public HardwareUtil() {
    }

    public static synchronized HardwareUtil getInstance() {
        if (instance == null)
            instance = new HardwareUtil();
        return instance;
    }

    /**
     * Inputs:
     Virtual Disks (VDs)
     Number
     Raid level
     Minimum / Exactly / Maximum (UI only shows Minimum and Exactly for now, but I am going to consider all 3 here)
     # of Disks
     Type: Any / Require SSD / Require HDD
     Minimum total # of Hot Spares
     Minimum # of SSD Hot Spares (subset of the total above)
        
     Assigning Physical Drives: (filtering is true if this does not fail)
     For each controller:
     Create list of SSDs sorted by Size and Drive number (available SSDs)
     Create list of HDDs sorted by Size and Drive number (available HDDs)
     Consume Min SSD Hot Spares from available SSDs. If not enough, fail (go to next controller)
     Consume (Min total hot spares  Min SSD Hot Spares) from available HDDs, falling back to available SSDs if needed. If not enough, fail
     Distribute minimum disks. For each VD:
     If Type is Require SSD, consume min # required from available SSDs. If not enough, fail
     If Type is Require HDD, consume min # required from available HDDs. If not enough, fail
     If Type is Any, consume min # required from SSDs, falling back to HDDs if needed. If not enough, fail
     Distribute remaining disks. For each VD:
     If VD contains SSDs (as determined previously), and has Minimum or Maximum set, consume one SSD if the total number of disks in the VD is less than the Maximum (if any)
     If VD contains HDDs (as determined previously), and has Minimum or Maximum set, consume one HDD if the total number of disks in the VD is less than the Maximum (if any)
     Continue "Distribute remaining didks" until all HDDs and SSDs are consumed or all VDs have reached their Maximum number of disks (if any).
     All remaining disks go into the Global Hot Spare.
     If here, break and use this controller
        
     * @param controllers
     * @param filterEnvironment
     * @return
     * @throws AsmManagerNotEnoughDisksException
     */
    public RAIDConfiguration prepareRAID(List<Controller> controllers, FilterEnvironment filterEnvironment)
            throws AsmManagerNotEnoughDisksException {
        RAIDConfiguration raidConfiguration = new RAIDConfiguration();

        if (filterEnvironment.getRaidConfiguration() == null)
            return raidConfiguration;

        if (controllers == null || controllers.size() == 0) {
            throw new AsmManagerNotEnoughDisksException("No RAID controllers reported by IDRAC");
        }

        if (filterEnvironment.getRaidConfiguration().getRaidtype() == TemplateRaidConfiguration.RaidTypeUI.basic) {
            // make up virtual disks
            filterEnvironment.getRaidConfiguration().getVirtualdisks().clear();

            VirtualDiskConfiguration vdisk = new VirtualDiskConfiguration();
            vdisk.setDisktype(VirtualDiskConfiguration.DiskMediaType.any);
            vdisk.setRaidlevel(filterEnvironment.getRaidConfiguration().getBasicraidlevel());
            vdisk.setComparator(VirtualDiskConfiguration.ComparatorValue.minimum);
            vdisk.setId(UUID.randomUUID().toString());
            vdisk.setNumberofdisks(RaidLevel.fromUIValue(vdisk.getRaidlevel().name()).getMinDisksRequired());
            filterEnvironment.getRaidConfiguration().getVirtualdisks().add(vdisk);
        }

        RAIDConfiguration externalConfig = assignDisks(controllers, filterEnvironment, true);
        RAIDConfiguration internalConfig = assignDisks(controllers, filterEnvironment, false);

        if (internalConfig == null) {
            throw new AsmManagerNotEnoughDisksException(
                    "No suitable RAID controllers. There are some that could not be used by current template.");
        }

        if (externalConfig != null) {
            internalConfig.setExternalHddHotSpares(externalConfig.getExternalHddHotSpares());
            internalConfig.setExternalSsdHotSpares(externalConfig.getExternalSsdHotSpares());
            internalConfig.setExternalVirtualDisks(externalConfig.getExternalVirtualDisks());
        }
        return internalConfig;
    }

    private RAIDConfiguration assignDisks(List<Controller> controllers, FilterEnvironment filterEnvironment,
            boolean forExternal) {
        AsmManagerNotEnoughDisksException lastError = null;
        RAIDConfiguration raidConfiguration = null;

        // Embedded Controller unable to deploy to ESX or RedHat6/CentOS 6 (Windows and SLES OK)
        if (!filterEnvironment.isWindowsOS()) {
            rejectEmbeddedController(controllers);
        }

        ControllerComparator controllerComparator = new ControllerComparator();
        Collections.sort(controllers, controllerComparator);

        for (Controller controller : controllers) {

            //RaidConfigWrapper will return data related to internal or external depending on how it's initialized.
            RaidConfigWrapper raidConfigWrapper = new RaidConfigWrapper(filterEnvironment.getRaidConfiguration(),
                    controller, forExternal);
            // reset prepared configuration
            raidConfigWrapper.reset();
            HashSet<String> enclosures = raidConfigWrapper.getEnclosures();

            try {
                /**
                 * For each controller:
                 * Create list of SSDs sorted by Size and Drive number (available SSDs)
                 * Create list of HDDs sorted by Size and Drive number (available HDDs)
                 */
                List<PhysicalDisk> physicalDisks = controller.getPhysicalDisks();
                List<PhysicalDisk> availableSsds = new ArrayList<>();
                List<PhysicalDisk> availableHdds = new ArrayList<>();
                List<PhysicalDisk> ssdPool = new ArrayList<>();
                List<PhysicalDisk> hddPool = new ArrayList<>();
                List<PhysicalDisk> diskPool = new ArrayList<>(); // all available disks, need to detect disk type when we ask First or Last

                for (PhysicalDisk pd : physicalDisks) {
                    //                    //Disk FQDD will be in the form of DiskName:EnclosureFQDD, so we can get the Enclosure FQDD easily from it
                    String enclosureFqdd = pd.getFqdd().split(":", 2)[1];
                    boolean isEmbeddedController = controller.getFqdd().contains("Embedded");
                    // Embedded Controllers do not have enclosures
                    if (enclosures.contains(enclosureFqdd) || (isEmbeddedController && !forExternal)) {
                        if (pd.getMediaType() == PhysicalDisk.PhysicalMediaType.SSD) {
                            availableSsds.add(pd);
                            ssdPool.add(pd);
                        } else if (pd.getMediaType() == PhysicalDisk.PhysicalMediaType.HDD) {
                            availableHdds.add(pd);
                            hddPool.add(pd);
                        }
                    }
                }

                int requestedDrivesNum = 0;
                int ssdCount = 0;
                int hddCount = 0;

                /**
                 * Distribute minimum disks. For each VD:
                 If Type is Require SSD, consume min # required from available SSDs. If not enough, fail
                 If Type is Require HDD, consume min # required from available HDDs. If not enough, fail
                 If Type is Any, consume min # required from SSDs, falling back to HDDs if needed. If not enough, fail
                 */
                int vdCnt = 0;
                Comparator<PhysicalDisk> lastComparator = null;

                List<VirtualDisk> currentVirtualDisks = raidConfigWrapper.getWorkingVirtualDisks();
                for (VirtualDiskConfiguration vdc : raidConfigWrapper.getVirtualDisks()) {

                    Comparator<PhysicalDisk> comparator;
                    // first VD requires special sorting
                    if (vdCnt == 0) {
                        if (VirtualDiskConfiguration.DiskMediaType.first.equals(vdc.getDisktype())) {
                            comparator = new DiskComparatorAscending();
                        } else {
                            comparator = new DiskComparator();
                        }
                    } else {
                        comparator = new DiskComparator();
                    }

                    if (comparator != lastComparator) {
                        Collections.sort(availableSsds, comparator);
                        Collections.sort(availableHdds, comparator);
                        Collections.sort(ssdPool, comparator);
                        Collections.sort(hddPool, comparator);

                        diskPool.clear();
                        diskPool.addAll(ssdPool);
                        diskPool.addAll(hddPool);
                        Collections.sort(diskPool, comparator);

                        lastComparator = comparator;
                    }

                    vdCnt++;

                    // for Hadoop cases First and Last we detect media type from the first
                    // drive in all available list. This lits is already sorted as needed.
                    PhysicalDisk.PhysicalMediaType mt = null;
                    if (vdc.getDisktype().isPhysiclaDisdType()) {
                        mt = PhysicalDisk.PhysicalMediaType.fromUIValue(vdc.getDisktype().name());
                    } else {
                        if (diskPool.size() > 0) {
                            mt = diskPool.get(0).getMediaType();
                        } else {
                            // this is just a safe harbor, if we don't have available disks - nothing will happen
                            mt = PhysicalDisk.PhysicalMediaType.ANY;
                        }
                    }

                    VirtualDisk virtualDisk = new VirtualDisk();
                    virtualDisk.setController(controller.getFqdd());
                    virtualDisk.setConfiguration(vdc);
                    virtualDisk.setRaidLevel(vdc.getRaidlevel());
                    virtualDisk.setMediaType(mt);

                    currentVirtualDisks.add(virtualDisk);

                    if (vdc.getComparator() == VirtualDiskConfiguration.ComparatorValue.minimum
                            || vdc.getComparator() == VirtualDiskConfiguration.ComparatorValue.exact)
                        requestedDrivesNum = vdc.getNumberofdisks();
                    else {
                        requestedDrivesNum = RaidLevel.fromUIValue(vdc.getRaidlevel().name()).getMinDisksRequired();
                    }

                    if (PhysicalDisk.PhysicalMediaType.SSD.equals(mt)) {
                        requestedDrivesNum = consumeDisks(availableSsds, virtualDisk.getPhysicalDisks(),
                                requestedDrivesNum);

                        if (requestedDrivesNum > 0) {
                            throw new AsmManagerNotEnoughDisksException(
                                    "Not enough SSDs for virtual disk #" + vdCnt);
                        }

                    } else if (PhysicalDisk.PhysicalMediaType.HDD.equals(mt)) {
                        requestedDrivesNum = consumeDisks(availableHdds, virtualDisk.getPhysicalDisks(),
                                requestedDrivesNum);

                        if (requestedDrivesNum > 0) {
                            throw new AsmManagerNotEnoughDisksException(
                                    "Not enough HDDs for virtual disk #" + vdCnt);
                        }

                    } else {
                        int remains = consumeDisks(availableSsds, virtualDisk.getPhysicalDisks(),
                                requestedDrivesNum);

                        if (remains > 0) {
                            // can't get all from SSD, release
                            releaseDisks(ssdPool, availableSsds, virtualDisk.getPhysicalDisks());
                            // try HDDs
                            remains = consumeDisks(availableHdds, virtualDisk.getPhysicalDisks(),
                                    requestedDrivesNum);
                        }

                        if (remains > 0) {
                            throw new AsmManagerNotEnoughDisksException(
                                    "Not enough SSDs and HDDs for virtual disk #" + vdCnt);
                        }
                    }
                }

                // Consume Min SSD Hot Spares from available SSDs. If not enough, fail (go to next controller)
                if (raidConfigWrapper.isEnableGlobalHotspares()) {
                    /*
                     * If only SSD type virtual disks are created, the Global Hot spare must be all SSD.
                     * If only HDD type virtual disks are created, the Global Hot spare must be all HDD.
                    */
                    for (VirtualDisk vd : raidConfigWrapper.getWorkingVirtualDisks()) {
                        for (String fqdd : vd.getPhysicalDisks()) {
                            for (PhysicalDisk pd : physicalDisks) {
                                if (pd.getFqdd().equals(fqdd)) {
                                    if (pd.getMediaType() == PhysicalDisk.PhysicalMediaType.SSD) {
                                        ssdCount++;
                                    } else if (pd.getMediaType() == PhysicalDisk.PhysicalMediaType.HDD) {
                                        hddCount++;
                                    }
                                }
                            }
                        }
                    }

                    // if no HDD VD, all hot spare must be SSD
                    int requestedSSDHS = 0;
                    requestedDrivesNum = (hddCount == 0) ? raidConfigWrapper.getGlobalHotspares()
                            : raidConfigWrapper.getMinimumSsd();
                    requestedSSDHS = requestedDrivesNum;
                    if (availableSsds.size() >= requestedDrivesNum) {
                        requestedDrivesNum = consumeDisks(availableSsds, raidConfigWrapper.getWorkingSsdHotSpares(),
                                requestedDrivesNum);
                    }
                    if (requestedDrivesNum > 0) {
                        throw new AsmManagerNotEnoughDisksException("Not enough SSDs for hot spares");
                    }

                    // Consume (Min total hot spares  Min SSD Hot Spares) from available HDDs, falling back to available SSDs if needed. If not enough, fail
                    requestedDrivesNum = raidConfigWrapper.getGlobalHotspares() - requestedSSDHS;
                    if ((availableHdds.size() + availableSsds.size()) >= requestedDrivesNum) {
                        if (hddCount > 0) {
                            requestedDrivesNum = consumeDisks(availableHdds,
                                    raidConfigWrapper.getWorkingHddHotSpares(), requestedDrivesNum);
                        }

                        if (requestedDrivesNum > 0 && ssdCount > 0) {
                            requestedDrivesNum = consumeDisks(availableSsds,
                                    raidConfigWrapper.getWorkingSsdHotSpares(), requestedDrivesNum);
                        }
                    }

                    if (requestedDrivesNum > 0) {
                        throw new AsmManagerNotEnoughDisksException("Not enough SSD and HDD for hot spares");
                    }
                }

                /**
                 * Distribute remaining disks. For each VD:
                 * If VD contains SSDs (as determined previously), and has Minimum or Maximum set, consume one SSD if the total number of disks in the VD is less than the Maximum (if any)
                 * If VD contains HDDs (as determined previously), and has Minimum or Maximum set, consume one HDD if the total number of disks in the VD is less than the Maximum (if any)
                 */
                int maxAllowed = 0;
                int numAllocatedDisks = 0;
                for (VirtualDisk vd : currentVirtualDisks) {
                    if (vd.getConfiguration().getComparator() == VirtualDiskConfiguration.ComparatorValue.maximum) {
                        maxAllowed = vd.getConfiguration().getNumberofdisks();
                    } else {
                        maxAllowed = Integer.MAX_VALUE;
                    }
                    numAllocatedDisks += vd.getPhysicalDisks().size();
                }

                while ((availableSsds.size() > 0 || availableHdds.size() > 0) && numAllocatedDisks < maxAllowed) {
                    int nAlloc = numAllocatedDisks;
                    for (VirtualDisk vd : currentVirtualDisks) {
                        if (vd.getConfiguration()
                                .getComparator() != VirtualDiskConfiguration.ComparatorValue.exact) {

                            if (vd.getConfiguration()
                                    .getComparator() == VirtualDiskConfiguration.ComparatorValue.maximum
                                    && vd.getPhysicalDisks().size() == vd.getConfiguration().getNumberofdisks()) {
                                continue;
                            }
                            // special case: limit max drives for raid1
                            if (vd.getRaidLevel() == VirtualDiskConfiguration.UIRaidLevel.raid1
                                    && vd.getPhysicalDisks().size() == MAX_DRIVES_RAID1) {
                                continue;
                            }

                            int incrementNumber = getIncrementNumber(vd.getRaidLevel());

                            if (vd.getConfiguration()
                                    .getDisktype() == VirtualDiskConfiguration.DiskMediaType.requiressd
                                    && availableSsds.size() >= incrementNumber) {
                                int remains = consumeDisks(availableSsds, vd.getPhysicalDisks(), incrementNumber);
                                if (remains == 0)
                                    numAllocatedDisks += incrementNumber;
                                else
                                    releaseDisks(ssdPool, availableSsds, vd.getPhysicalDisks());

                            } else if (vd.getConfiguration()
                                    .getDisktype() == VirtualDiskConfiguration.DiskMediaType.requirehdd
                                    && availableHdds.size() >= incrementNumber) {
                                int remains = consumeDisks(availableHdds, vd.getPhysicalDisks(), incrementNumber);
                                if (remains == 0)
                                    numAllocatedDisks += incrementNumber;
                                else
                                    releaseDisks(hddPool, availableHdds, vd.getPhysicalDisks());

                            } else {
                                // what type we choose for ANY? find that by the first disk
                                PhysicalDisk.PhysicalMediaType mediaType = null;
                                for (PhysicalDisk pd : physicalDisks) {
                                    if (pd.getFqdd().equals(vd.getPhysicalDisks().get(0))) {
                                        mediaType = pd.getMediaType();
                                        break;
                                    }
                                }
                                if (mediaType == PhysicalDisk.PhysicalMediaType.SSD
                                        && availableSsds.size() >= incrementNumber) {
                                    int remains = consumeDisks(availableSsds, vd.getPhysicalDisks(),
                                            incrementNumber);
                                    if (remains == 0)
                                        numAllocatedDisks += incrementNumber;
                                    else
                                        releaseDisks(ssdPool, availableSsds, vd.getPhysicalDisks());
                                } else if (mediaType == PhysicalDisk.PhysicalMediaType.HDD
                                        && availableHdds.size() >= incrementNumber) {
                                    int remains = consumeDisks(availableHdds, vd.getPhysicalDisks(),
                                            incrementNumber);
                                    if (remains == 0)
                                        numAllocatedDisks += incrementNumber;
                                    else
                                        releaseDisks(hddPool, availableHdds, vd.getPhysicalDisks());
                                }
                            }
                            // check if no more disks left
                            if ((availableSsds.size() == 0 && availableHdds.size() == 0)
                                    || numAllocatedDisks == maxAllowed)
                                break;
                        }
                    }
                    // if we were unable to allocate any disks for any VD - quit
                    if (nAlloc == numAllocatedDisks)
                        break;
                }

                /**
                 * All remaining disks go into the Global Hot Spare.
                 */
                if (raidConfigWrapper.isEnableGlobalHotspares()) {
                    // recalculate ssd and hdd VD
                    ssdCount = 0;
                    hddCount = 0;
                    for (VirtualDisk vd : raidConfigWrapper.getWorkingVirtualDisks()) {
                        for (String fqdd : vd.getPhysicalDisks()) {
                            for (PhysicalDisk pd : physicalDisks) {
                                if (pd.getFqdd().equals(fqdd)) {
                                    if (pd.getMediaType() == PhysicalDisk.PhysicalMediaType.SSD) {
                                        ssdCount++;
                                    } else if (pd.getMediaType() == PhysicalDisk.PhysicalMediaType.HDD) {
                                        hddCount++;
                                    }
                                }
                            }
                        }
                    }

                    if (availableSsds.size() > 0 && ssdCount > 0) {
                        consumeDisks(availableSsds, raidConfigWrapper.getWorkingSsdHotSpares(),
                                availableSsds.size());
                    }
                    if (availableHdds.size() > 0 && hddCount > 0) {
                        consumeDisks(availableSsds, raidConfigWrapper.getWorkingHddHotSpares(),
                                availableHdds.size());
                    }
                }

                /**
                 * If here, break and use this controller
                 */
                lastError = null;
                raidConfiguration = raidConfigWrapper.getWorkingRaidConfig();
                break;
            } catch (AsmManagerNotEnoughDisksException e) {
                // use next controller
                logger.debug("Controller " + controller.getFqdd() + " failed to satisfy RAID requirements: "
                        + e.getMessage());
                lastError = e;
            }
        }
        if (lastError != null)
            throw lastError;
        return raidConfiguration;
    }

    /**
     * Reject Embedded Controller
     *
     * This removes the embedded (S130) controller from the controller list
     *
     * @param controllers
     */
    public void rejectEmbeddedController(List<Controller> controllers) {
        Iterator<Controller> i = controllers.iterator();
        while (i.hasNext()) {
            Controller controller = i.next();
            if (controller.isEmbeddedController()) {
                logger.debug("Removed embedded controller " + controller.getFqdd());
                i.remove();
            }
        }
    }

    private int getIncrementNumber(VirtualDiskConfiguration.UIRaidLevel raidLevel) {
        switch (raidLevel) {
        case raid0:
            return 1;
        case raid1:
            return 2;
        case raid5:
        case raid6:
            return 1;
        case raid10:
            return 2;
        case raid50:
            return 3;
        default:
            return 1;
        }
    }

    /**=
     * Remove disks from supplied list and add removed FQDD to destination.
     * @param source List of Disks
     * @param dest List of Strings
     * @param requestedDrivesNum Number of remaining disks to process. 0 if resource had enough disks. In this case dest.size() == requestedDrivesNum
     * @return
     */
    private int consumeDisks(List<PhysicalDisk> source, List<String> dest, int requestedDrivesNum) {
        Iterator<PhysicalDisk> it = source.iterator();
        while (it.hasNext() && requestedDrivesNum > 0) {
            PhysicalDisk pd = it.next();
            dest.add(pd.getFqdd());
            it.remove();
            requestedDrivesNum--;
        }
        return requestedDrivesNum;
    }

    private void releaseDisks(List<PhysicalDisk> pool, List<PhysicalDisk> source, List<String> dest) {
        Iterator<String> it = dest.iterator();
        while (it.hasNext()) {
            String fqdd = it.next();
            for (PhysicalDisk pd : pool) {
                if (pd.getFqdd().equals(fqdd)) {
                    source.add(pd);
                    break;
                }
            }
            it.remove();
        }
        Collections.sort(source, new DiskComparator());
    }

    /**
     * Compare controllers by type
     *
     */
    public static class ControllerComparator implements Comparator<Controller> {
        // Prefer integrated slot/raid, then Embedded (S130), then Stash (Modular)
        Integer getControllerTypeRank(String fqdd) {
            if (StringUtils.contains(fqdd, "Embedded")) {
                return 1;
            } else if (StringUtils.contains(fqdd, "Modular")) {
                return 2;
            } else {
                return 0;
            }
        }

        @Override
        public int compare(Controller a, Controller b) {
            return getControllerTypeRank(a.getFqdd()).compareTo(getControllerTypeRank(b.getFqdd()));
        }
    }

    /**
     * Compare disks by size and slot.
     */
    private class DiskComparator implements Comparator<PhysicalDisk> {
        @Override
        public int compare(PhysicalDisk x, PhysicalDisk y) {
            if (x.getSize() > y.getSize())
                return -1;
            else if (x.getSize() < y.getSize())
                return 1;
            else {
                if (x.getDriveNumber() < y.getDriveNumber())
                    return 1;
                else if (x.getDriveNumber() > y.getDriveNumber())
                    return -1;
                else
                    return 0;
            }
        }
    }

    /**
     * Compare disks by size and slot.
     */
    private class DiskComparatorAscending implements Comparator<PhysicalDisk> {
        @Override
        public int compare(PhysicalDisk x, PhysicalDisk y) {
            if (x.getSize() > y.getSize())
                return -1;
            else if (x.getSize() < y.getSize())
                return 1;
            else {
                if (x.getDriveNumber() < y.getDriveNumber())
                    return -1;
                else if (x.getDriveNumber() > y.getDriveNumber())
                    return 1;
                else
                    return 0;
            }
        }
    }

}