com.dell.asm.asmcore.asmmanager.util.firmwarerepository.FirmwareUtil.java Source code

Java tutorial

Introduction

Here is the source code for com.dell.asm.asmcore.asmmanager.util.firmwarerepository.FirmwareUtil.java

Source

/**************************************************************************
 *   Copyright (c) 2015 - 2016 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.firmwarerepository;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import javax.ws.rs.core.Response;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.hibernate.LazyInitializationException;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import org.quartz.JobDetail;
import org.quartz.SchedulerException;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;

import com.dell.asm.asmcore.asmmanager.AsmManagerMessages;
import com.dell.asm.asmcore.asmmanager.client.deployment.Deployment;
import com.dell.asm.asmcore.asmmanager.client.deviceinventory.CompliantState;
import com.dell.asm.asmcore.asmmanager.client.deviceinventory.DeviceState;
import com.dell.asm.asmcore.asmmanager.client.deviceinventory.FirmwareComplianceComponent;
import com.dell.asm.asmcore.asmmanager.client.deviceinventory.FirmwareComplianceReport;
import com.dell.asm.asmcore.asmmanager.client.deviceinventory.FirmwareComplianceReportComponent;
import com.dell.asm.asmcore.asmmanager.client.deviceinventory.FirmwareComplianceReportComponentVersionInfo;
import com.dell.asm.asmcore.asmmanager.client.deviceinventory.ManagedDevice;
import com.dell.asm.asmcore.asmmanager.client.discovery.DiscoverDeviceType;
import com.dell.asm.asmcore.asmmanager.client.firmware.ComponentType;
import com.dell.asm.asmcore.asmmanager.client.firmware.FirmwareDeviceInventory;
import com.dell.asm.asmcore.asmmanager.client.firmware.FirmwareRepository;
import com.dell.asm.asmcore.asmmanager.client.firmware.SoftwareComponent;
import com.dell.asm.asmcore.asmmanager.client.firmware.SourceType;
import com.dell.asm.asmcore.asmmanager.client.networkconfiguration.Interface;
import com.dell.asm.asmcore.asmmanager.client.networkconfiguration.NetworkConfiguration;
import com.dell.asm.asmcore.asmmanager.client.networkconfiguration.Partition;
import com.dell.asm.asmcore.asmmanager.client.pupetmodule.PuppetDiscoveryRequest;
import com.dell.asm.asmcore.asmmanager.client.servicetemplate.Network;
import com.dell.asm.asmcore.asmmanager.client.servicetemplate.ServiceTemplate;
import com.dell.asm.asmcore.asmmanager.client.servicetemplate.ServiceTemplateCategory;
import com.dell.asm.asmcore.asmmanager.client.servicetemplate.ServiceTemplateComponent;
import com.dell.asm.asmcore.asmmanager.client.servicetemplate.ServiceTemplateComponent.ServiceTemplateComponentType;
import com.dell.asm.asmcore.asmmanager.client.servicetemplate.ServiceTemplateSetting;
import com.dell.asm.asmcore.asmmanager.client.servicetemplate.ServiceTemplateSettingIDs;
import com.dell.asm.asmcore.asmmanager.client.util.PuppetDbUtil;
import com.dell.asm.asmcore.asmmanager.client.util.PuppetIdracServerDevice;
import com.dell.asm.asmcore.asmmanager.db.DeploymentDAO;
import com.dell.asm.asmcore.asmmanager.db.DeviceInventoryComplianceDAO;
import com.dell.asm.asmcore.asmmanager.db.DeviceInventoryDAO;
import com.dell.asm.asmcore.asmmanager.db.FirmwareRepositoryDAO;
import com.dell.asm.asmcore.asmmanager.db.GenericDAO;
import com.dell.asm.asmcore.asmmanager.db.entity.DeploymentEntity;
import com.dell.asm.asmcore.asmmanager.db.entity.DeviceInventoryComplianceEntity;
import com.dell.asm.asmcore.asmmanager.db.entity.DeviceInventoryComplianceEntity.DeviceInventoryComplianceId;
import com.dell.asm.asmcore.asmmanager.db.entity.DeviceInventoryEntity;
import com.dell.asm.asmcore.asmmanager.db.entity.FirmwareDeviceInventoryEntity;
import com.dell.asm.asmcore.asmmanager.db.entity.FirmwareRepositoryEntity;
import com.dell.asm.asmcore.asmmanager.db.entity.ServiceTemplateEntity;
import com.dell.asm.asmcore.asmmanager.db.entity.SoftwareBundleEntity;
import com.dell.asm.asmcore.asmmanager.db.entity.SoftwareComponentEntity;
import com.dell.asm.asmcore.asmmanager.exception.AsmManagerCheckedException;
import com.dell.asm.asmcore.asmmanager.exception.AsmManagerRuntimeException;
import com.dell.asm.asmcore.asmmanager.tasks.DeviceInventoryJob;
import com.dell.asm.asmcore.asmmanager.tasks.FirmwareUpdateJob;
import com.dell.asm.asmcore.asmmanager.util.DeviceInventoryUtils;
import com.dell.asm.asmcore.asmmanager.util.DownloadFileUtil;
import com.dell.asm.asmcore.asmmanager.util.PuppetModuleUtil;
import com.dell.asm.asmcore.asmmanager.util.deployment.ServiceDeploymentUtil;
import com.dell.asm.i18n2.EEMILocalizableMessage;
import com.dell.asm.i18n2.ResourceBundleLocalizableMessage;
import com.dell.asm.i18n2.exception.AsmCheckedException;
import com.dell.asm.localizablelogger.LocalizableMessageService;
import com.dell.asm.localizablelogger.LogMessage.LogCategory;
import com.dell.asm.localizablelogger.LogMessage.LogSeverity;
import com.dell.asm.rest.common.exception.LocalizedWebApplicationException;
import com.dell.pg.asm.catalogmgr.exceptions.CatalogMessages;
import com.dell.pg.asm.identitypool.api.common.model.NetworkType;
import com.dell.pg.asm.repositorymgr.exceptions.RepositoryException;
import com.dell.pg.orion.common.utilities.MarshalUtil;
import com.dell.pg.orion.common.utilities.VersionUtils;
import com.dell.pg.orion.jobmgr.IJobHistoryManager;
import com.dell.pg.orion.jobmgr.IJobManager;
import com.dell.pg.orion.jobmgr.JobCreateSpec;
import com.dell.pg.orion.jobmgr.JobManager;
import com.dell.pg.orion.jobmgr.JobManagerException;
import com.dell.pg.orion.jobmgr.JobStatus;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;

/**
 * Compliance is a bit complicated.  The primary things you need to understand Firmware is an understanding of
 * a Firmware Catalog (represented as a FirmwareRepository in ASM), Device Inventory (stored in the 
 * firmware_deviceinventory table), and the 'mapping' between these two which is primarily stored in the 
 * 'device_inventory_compliance_map' with a value propagated to the 'device_inventory' compliance field.<br />
 * <br />
 * <b>Firmware\Hardware Compliance</b><br />
 * Firmware compliance was initially calculated only based on the firmware components and their corresponding catalog
 * entries.  Software was only recently added, and required some changes to be supported.  See firmware compliance is 
 * only calculated if there is an entry in the firmware_deviceinventory table and a corresponding component found in
 * the Firmware Catalog. <br />
 * <br />
 * <b>Software Compliance</b><br />
 * Software compliance is calculated the same and yet differently.  When software information is available in the
 * firmware_deviceinventory table, then compliance is calculated the same.  However, when a Catalog is set on a 
 * server, and there are software entries in the catalog, that do not have a matching value in the 
 * firmware_deviceinventory, then we create a 'new' entry in the table, and then mark it's 'source' as Catalog.  Only 
 * software items in the firmware_deviceinventory table should be marked as catalog.  No others.  When a entry in
 * the firmware_deviceinventory table is marked as 'Catalog' for it's source, then it will automatically be considered
 * out of compliance, as no match can be found.<br />  
 * <br />
 * <b>Compliance Mappings</b><br />
 * The compliance_map table in the database contains a compliance mapping of all devices against all repositories.  Once 
 * the compliant_map entries are complete for a device, then, depending on whether a device is part of a service or not, 
 * the appropriate compliance value is propagated to the device_inventory table.  Devices are broken down into 
 * shared and non-shared (think Servers and Storages).  A shared resource (such as a Storage) may be part of multiple 
 * Services/Deployments where as a non-shared device (such as a Server) can only be be part of a single 
 * Service/Deployment at any given point in time.  A non-shared device's compliance in the device_inventory table will
 * either be the default catalog or that of the Service the device part of at the time.  A shared resource's compliance
 * value will always represent the value of the default catalog.  A shared resource will NEVER have a Service's 
 * compliance level propagated to the device_inventory table.  How could it?  There's only one entry there, and a 
 * shared resource could be in a dozen different Services/Deployments.  
 */
public class FirmwareUtil {

    // Class Variables
    private static final Logger logger = Logger.getLogger(FirmwareUtil.class);
    private static final ObjectMapper OBJECT_MAPPER = buildObjectMapper();
    private static final String COMPONENT_TYPE_DRIVER = "Driver";
    private static final String NOT_AVAILABLE = "NA";
    private static final Comparator COMPLIANCE_REPORT_COMPARATOR = new Comparator<FirmwareComplianceReport>() {

        public int compare(FirmwareComplianceReport fcr1, FirmwareComplianceReport fcr2) {
            if (fcr1.getDeviceType().isStorage() && fcr2.getDeviceType().isServer())
                return 1;
            if (fcr1.getDeviceType().isServer() && fcr2.getDeviceType().isStorage())
                return -1;
            return fcr1.getIpAddress().compareTo(fcr2.getIpAddress());
        }
    };

    // Member Variables
    private FirmwareRepositoryDAO firmwareRepositoryDAO = null;
    private DeviceInventoryDAO deviceInventoryDAO = null;
    private DeviceInventoryComplianceDAO deviceInventoryComplianceDAO = null;
    private DeploymentDAO deploymentDAO = null;
    private GenericDAO genericDAO = null;
    private DateTimeFormatter DATE_FORMATTER = ISODateTimeFormat.dateTimeParser();
    private Comparator<SoftwareComponent> componentVersionComparator = new Comparator<SoftwareComponent>() {
        @Override
        public int compare(SoftwareComponent sc1, SoftwareComponent sc2) {
            // Note that sc1 and sc2 are 'reversed' below
            // so it will sort in descending order. This
            // will put the 'latest' version in the first
            // list entry.
            return VersionUtils.compareVersions(sc2.getVendorVersion(), sc1.getVendorVersion());
        }
    };
    private IJobManager jobMgr = null;
    private IJobHistoryManager historyMgr = null;

    static ObjectMapper buildObjectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        AnnotationIntrospector ai = new JaxbAnnotationIntrospector(mapper.getTypeFactory());
        mapper.setAnnotationIntrospector(ai);
        return mapper;
    }

    /**
     * Default constructor for the class.
     */
    public FirmwareUtil() {
        this(FirmwareRepositoryDAO.getInstance(), new DeviceInventoryDAO(), new DeviceInventoryComplianceDAO(),
                DeploymentDAO.getInstance(), GenericDAO.getInstance(), JobManager.getInstance(),
                JobManager.getInstance().getJobHistoryManager());
    }

    /**
     * Constructor to set the FirmwareUpdateUtil used by the class.
     */
    public FirmwareUtil(FirmwareRepositoryDAO firmwareRepositoryDAO, DeviceInventoryDAO deviceInventoryDAO,
            DeviceInventoryComplianceDAO deviceInventoryComplianceDAO, DeploymentDAO deploymentDAO,
            GenericDAO genericDAO, IJobManager jobManager, IJobHistoryManager jobHistoryManager) {
        this.firmwareRepositoryDAO = firmwareRepositoryDAO;
        this.deviceInventoryDAO = deviceInventoryDAO;
        this.deviceInventoryComplianceDAO = deviceInventoryComplianceDAO;
        this.deploymentDAO = deploymentDAO;
        this.genericDAO = genericDAO;
        this.jobMgr = jobManager;
        this.historyMgr = jobHistoryManager;
    }

    public void testConnection(FirmwareRepository firmwareRepository) throws AsmCheckedException {
        try {
            String type = DownloadFileUtil.getType(firmwareRepository.getSourceLocation());
            if (type.equals(DownloadFileUtil.CIFS) || type.equals(DownloadFileUtil.NFS)) {
                DownloadFileUtil.testConnectionToShare(firmwareRepository.getSourceLocation(),
                        DownloadFileUtil.getDomainfromShareUserName(firmwareRepository.getUsername()),
                        DownloadFileUtil.getUserNamefromShareUserName(firmwareRepository.getUsername()),
                        firmwareRepository.getPassword());
            } else {
                DownloadFileUtil.testConnectionToURL(firmwareRepository.getSourceLocation());
            }
        } catch (RepositoryException e) {
            throw new AsmCheckedException("Could not connect to path specified",
                    AsmManagerMessages.couldNotConnectToPath(firmwareRepository.getSourceLocation()));
        }
    }

    public FirmwareRepository entityToDto(final FirmwareRepositoryEntity entity, final boolean getDeployments) {
        FirmwareRepository repository = new FirmwareRepository();
        repository.setSourceLocation(entity.getSourceLocation());
        repository.setSourceType(entity.getSourceType());
        repository.setDiskLocation(entity.getDiskLocation());
        repository.setFilename(entity.getFilename());
        repository.setMd5Hash(entity.getMd5Hash());
        repository.setUsername(entity.getUsername());
        repository.setPassword(entity.getPassword());
        repository.setDownloadStatus(entity.getDownloadStatus());
        repository.setDefaultCatalog(entity.isDefault());
        repository.setEmbedded(entity.isEmbedded());
        repository.setState(entity.getState());
        repository.setBundleCount(entity.getBundleCount());
        repository.setUserBundleCount(entity.getUserBundleCount());
        repository.setComponentCount(entity.getComponentCount());

        repository.setCreatedBy(entity.getCreatedBy());
        repository.setCreatedDate(entity.getCreatedDate());
        repository.setId(entity.getId());
        repository.setName(entity.getName());
        repository.setUpdatedBy(entity.getUpdatedBy());
        repository.setUpdatedDate(entity.getUpdatedDate());

        if (getDeployments) {
            for (DeploymentEntity d : entity.getDeployments()) {
                repository.getDeployments().add(d.getDeployment());
            }
        }

        try {
            for (SoftwareBundleEntity sbe : entity.getSoftwareBundles()) {
                repository.getSoftwareBundles().add(sbe.getSoftwareBundle());
            }
        } catch (LazyInitializationException e) {
            // Avoiding due to excessive logging
            // logger.warn("Error initializing SoftwareBundles in FirmwareUtil's entityToDTO method for repository with name: " + repository.getName());
        }

        try {
            for (SoftwareComponentEntity sc : entity.getSoftwareComponents()) {
                repository.getSoftwareComponents().add(sc.getSoftwareComponent());
            }
        } catch (LazyInitializationException e) {
            // Avoiding due to excessive logging
            // logger.warn("Error initializing SoftwareComponents in FirmwareUtil's entityToDTO method for repository with name: " + repository.getName());
        }

        return repository;
    }

    /**
     * Takes a deployment adds all the necessary firmware update components to the service template. Either all servers 
     * in the case of a service deployment or Devices marked as managefirmware from a getting started configuration
     *
     * @param firmwareRepository
     * @param deployment
     * @param allServers
     * @throws AsmManagerCheckedException 
     */
    public List<DeviceInventoryEntity> addFirmwareUpdateComponentsToDeployment(
            final FirmwareRepositoryEntity firmwareRepository, final Deployment deployment,
            final boolean allServers) throws AsmManagerCheckedException {

        List<DeviceInventoryEntity> updatedDevices = getFirmwareUpdateDevicesInDeployment(firmwareRepository,
                deployment, allServers, DriverType.ALL);
        List<ServiceTemplateComponent> updateComponents = new ArrayList<ServiceTemplateComponent>();

        for (DeviceInventoryEntity device : updatedDevices) {
            if (device != null) {
                List<FirmwareDeviceInventoryEntity> nonCompliantFirmware = this.getNonCompliantFirmware(device,
                        firmwareRepository, true, DriverType.ALL);
                if (nonCompliantFirmware != null && nonCompliantFirmware.size() > 0) {
                    logger.trace("Adding component for device: " + device.getRefId());
                    ServiceTemplateComponent updateComponent = this.createFirmwareUpdateComponent(
                            "component" + (deployment.getServiceTemplate().getComponents().size() + 1), device,
                            nonCompliantFirmware, firmwareRepository, null, true);
                    updateComponents.add(updateComponent);
                    this.setFirmwareUpdateTime(nonCompliantFirmware);

                    device.setState(DeviceState.UPDATING);
                    deviceInventoryDAO.updateDeviceInventory(device);

                } else {
                    // This would be odd since the device is in the list of non-compliant
                    logger.trace("Oddity: Not updating device: " + device.getRefId() + " No noncompliant firmware");
                }
            }
        }
        if (updateComponents != null && updateComponents.size() > 0) {
            deployment.getServiceTemplate().getComponents().addAll(updateComponents);
        }
        return updatedDevices;
    }

    /**
     * Get a list of all devices that need firmware updates. Either all servers in the case
     * of a service deployment or Devices marked as managefirmware from a getting started configuration.
     * In contrast to the add method above this does not alter the deployment object.
     *
     * @param components the components to be searched for servers that need to be updated.
     * @param allServers true when you need to get all servers as part of teh getting started configuration.
     * @return a list of devices that need firmware updates.
     */
    public List<DeviceInventoryEntity> getFirmwareUpdateDevices(List<ServiceTemplateComponent> components,
            FirmwareRepositoryEntity firmwareRepository, boolean allServers, DriverType driverType) {
        List<DeviceInventoryEntity> updatedDevices = new ArrayList<DeviceInventoryEntity>();

        for (ServiceTemplateComponent component : components) {
            if ((ServiceTemplateComponentType.SERVER.equals(component.getType()) && allServers)
                    || component.isManageFirmware()) {
                DeviceInventoryEntity device = null;
                if (component.getAsmGUID() != null) {
                    device = deviceInventoryDAO.getDeviceInventory(component.getAsmGUID());
                } else {
                    device = deviceInventoryDAO.getDeviceInventory(component.getId());
                }

                logger.debug("Looking up compliant firmware for asmguid: " + component.getAsmGUID()
                        + "componentid: " + component.getId() + " device is null: " + (device == null));

                if (device != null) {
                    List<FirmwareDeviceInventoryEntity> nonCompliantFirmware = this.getNonCompliantFirmware(device,
                            firmwareRepository, true, driverType);
                    if (nonCompliantFirmware != null && nonCompliantFirmware.size() > 0) {
                        logger.debug("Adding Firmware Update required for device: " + device.getRefId());
                        updatedDevices.add(device);
                    } else {
                        logger.debug("Not adding device: " + component.getId() + " No noncompliant firmware");
                    }
                }
            }
        }
        return updatedDevices;
    }

    /**
     * Get a list of all devices that need firmware updates. Either all servers in the case
     * of a service deployment or Devices marked as managefirmware from a getting started configuration.
     * In contrast to the add method above this does not alter the deployment object.
     *
     * @param firmwareRepository
     * @param deployment
     * @param allServers
     * @throws AsmManagerCheckedException
     */
    public List<DeviceInventoryEntity> getFirmwareUpdateDevicesInDeployment(
            final FirmwareRepositoryEntity firmwareRepository, final Deployment deployment,
            final boolean allServers, final DriverType driverType) throws AsmManagerCheckedException {
        List<DeviceInventoryEntity> updatedDevices = new ArrayList<DeviceInventoryEntity>();
        if (deployment != null && deployment.getServiceTemplate() != null) {
            if (deployment.getServiceTemplate().getComponents() != null) {
                //get the catalog
                if (firmwareRepository != null) {
                    updatedDevices = this.getFirmwareUpdateDevices(deployment.getServiceTemplate().getComponents(),
                            firmwareRepository, allServers, driverType);
                }
            }
        }
        return updatedDevices;
    }

    public void updateComponentStatus(Deployment deployment, boolean allServers) throws AsmManagerCheckedException {

        if (deployment.getServiceTemplate() != null) {
            if (deployment.getServiceTemplate().getComponents() != null) {
                for (ServiceTemplateComponent component : deployment.getServiceTemplate().getComponents()) {
                    if ((ServiceTemplateComponentType.SERVER.equals(component.getType()) && allServers)
                            || component.isManageFirmware()) {
                        DeviceInventoryEntity device = null;
                        if (component.getAsmGUID() != null) {
                            device = deviceInventoryDAO.getDeviceInventory(component.getAsmGUID());
                        } else {
                            device = deviceInventoryDAO.getDeviceInventory(component.getId());
                        }

                        if (device != null && DeviceState.UPDATING.equals(device.getState())) {
                            logger.debug("Setting device " + device.getServiceTag() + " to the state READY");
                            device.setState(DeviceState.READY);
                            deviceInventoryDAO.updateDeviceInventory(device);
                        }
                    }
                }
            }
        }
    }

    /** 
     * Runs an InventoryJob for all devices in the list, and blocks until they are all complete.
     * 
     * @param deviceEntities the devices for which an InventoryJob will be run.
     */
    public void blockUntilInventoryUpdatesAreComplete(List<DeviceInventoryEntity> deviceEntities) {
        if (CollectionUtils.isNotEmpty(deviceEntities)) {
            for (DeviceInventoryEntity deviceEntity : deviceEntities) {
                try {
                    this.runInventoryJobAndBlockUntilComplete(deviceEntity);
                } catch (JobManagerException | InterruptedException e) {
                    // Ignore and continue on...
                    logger.warn("Error while running InventoryJobs!", e);
                }
            }
        }
    }

    /**
     * Utility method to encapsulate the calling of re-running device inventory on a device after updating its firmware.
     * @param deviceEntity
     */
    public JobDetail scheduleInventoryJobImmediately(DeviceInventoryEntity deviceEntity) {
        JobDetail job = null;
        try {
            logger.info("checking and starting the scheduler");
            // NOTE[fcarta] - Note sure this is needed everywhere, this block would be better in the getInstance() method
            if (!jobMgr.getScheduler().isStarted()) {
                jobMgr.getScheduler().start();
                logger.info("scheduler started");
            }
            job = jobMgr.createNamedJob(DeviceInventoryJob.class);
            final ManagedDevice device = DeviceInventoryUtils.toDTO(deviceEntity, true);
            job.getJobDataMap().put(DeviceInventoryJob.DEVICEINVENTORY_KEY_DATA, MarshalUtil.marshal(device));
            // Create a trigger and associate it with the schedule, job,
            // and some arbitrary information. The boolean means "start now".
            final SimpleScheduleBuilder schedBuilder = SimpleScheduleBuilder.simpleSchedule();
            final Trigger trigger = jobMgr.createNamedTrigger(schedBuilder, job, true);
            jobMgr.scheduleJob(job, trigger);
        } catch (SchedulerException e) {
            logger.error("Failed to schedule inventory update.", e);
        }

        return job;
    }

    /**
     * Overloaded to not need to specify: 
     * A related ServiceTemplateComponent
     * A component Name
     * @param device
     * @param firmwareDeviceInventoryEntities
     * @param firmwareRepository
     * @param forceRestart
     * @return
     */
    public ServiceTemplateComponent createFirmwareUpdateComponent(DeviceInventoryEntity device,
            List<FirmwareDeviceInventoryEntity> firmwareDeviceInventoryEntities,
            FirmwareRepositoryEntity firmwareRepository, List<String> clusterServerList, boolean forceRestart) {
        return createFirmwareUpdateComponent(null, device, firmwareDeviceInventoryEntities, firmwareRepository,
                null, clusterServerList, forceRestart);
    }

    /**
     * Overloaded to not need to specify:
     * A related ServiceTemplateComponent
     * @param componentName
     * @param device
     * @param firmwareDeviceInventoryEntities
     * @param firmwareRepository
     * @param forceRestart
     * @return
     */
    public ServiceTemplateComponent createFirmwareUpdateComponent(String componentName,
            DeviceInventoryEntity device, List<FirmwareDeviceInventoryEntity> firmwareDeviceInventoryEntities,
            FirmwareRepositoryEntity firmwareRepository, List<String> clusterServerList, boolean forceRestart) {
        return createFirmwareUpdateComponent(componentName, device, firmwareDeviceInventoryEntities,
                firmwareRepository, null, clusterServerList, forceRestart);
    }

    /**
     * Returns the Embedded Repo that ships with ASM. 
     * @return the Embedded Repo that ships with ASM.
     */
    public FirmwareRepositoryEntity getEmbeddedRepo() {
        HashMap<String, Object> attributeMap = new HashMap<String, Object>();
        attributeMap.put("isEmbedded", Boolean.TRUE);
        FirmwareRepositoryEntity embeddedRepo = null;
        List<FirmwareRepositoryEntity> repos = this.genericDAO.getForEquals(attributeMap,
                FirmwareRepositoryEntity.class);
        if (repos != null && repos.size() > 0)
            embeddedRepo = repos.get(0);

        return embeddedRepo;
    }

    /**
     * Returns the Default catalog, or null if the default catalog has not been set.
     * @return the Default catalog, or null if the default catalog has not been set.
     */
    public FirmwareRepositoryEntity getDefaultRepo() {
        HashMap<String, Object> attributeMap = new HashMap<String, Object>();
        attributeMap.put("isDefault", Boolean.TRUE);
        FirmwareRepositoryEntity embeddedRepo = null;
        List<FirmwareRepositoryEntity> repos = this.genericDAO.getForEquals(attributeMap,
                FirmwareRepositoryEntity.class);
        if (repos != null && repos.size() > 0)
            embeddedRepo = repos.get(0);

        return embeddedRepo;
    }

    /**
     * If the Device is part of a Deployment, and then returns the Deployment FirmwareRepository if the Deployment is
     * managing the firmware. 
     * @param deviceInventoryEntity
     * @return
     */
    public FirmwareRepositoryEntity getDeploymentRepo(DeviceInventoryEntity deviceInventoryEntity) {
        FirmwareRepositoryEntity deploymentRepo = null;
        if (deviceInventoryEntity.getDeployments() != null && deviceInventoryEntity.getDeployments().size() > 0) {
            if (deviceInventoryEntity.getDeployments().get(0).isManageFirmware()) {
                deploymentRepo = deviceInventoryEntity.getDeployments().get(0).getFirmwareRepositoryEntity();
            }
        }

        return deploymentRepo;
    }

    /**
     * Returns the FirmwareRepositoryEntity for the Service/Deployment that the given deviceInventoryEntity is deployed
     * to, or null if the deviceInventoryEntity is not deployed. 
     * 
     * @param deviceInventoryEntity the device whose Service's FirmwareRepositoryEntity will be returned.
     * @return the FirmwareRepositoryEntity for the Service/Deployment that the given deviceInventoryEntity is deployed
     * to, or null if the deviceInventoryEntity is not deployed. 
     */
    public FirmwareRepositoryEntity getServiceRepo(DeviceInventoryEntity deviceInventoryEntity) {
        FirmwareRepositoryEntity serviceRepo = null;

        // Only non-shared devices should have a ServiceRepository (like Servers)
        if (!deviceInventoryEntity.getDeviceType().isSharedDevice()) {
            if (deviceInventoryEntity.getDeployments() != null
                    && deviceInventoryEntity.getDeployments().size() > 0) {
                DeploymentEntity deploymentEntity = deviceInventoryEntity.getDeployments().get(0);
                deploymentEntity = deploymentDAO.getDeployment(deploymentEntity.getId(),
                        DeploymentDAO.FIRMWARE_REPOSITORY_ENTITY);
                if (deploymentEntity.isManageFirmware()) {
                    serviceRepo = deploymentEntity.getFirmwareRepositoryEntity();
                }
            }
        }

        return serviceRepo;
    }

    /**
     * Creates a service template component that describes a firmware update request for a given device.
     * @param componentName: Name of the component, optional.  If not specified will be component0
     * @param device: The device we want to update firmware on
     * @param outOfComplianceFirmware: The list of non-compliant firmware on this device that we want to update. 
     * @param firmwareRepository: The firmware repository we will be updating against
     * @param templateComponent: A related component that we use for getting the esxi server, username, password.  Not always specified.
     * @param forceRestart: Whether we will be updating immediately or on next boot
     * @return
     */
    public ServiceTemplateComponent createFirmwareUpdateComponent(String componentName,
            DeviceInventoryEntity device, List<FirmwareDeviceInventoryEntity> outOfComplianceFirmware,
            FirmwareRepositoryEntity firmwareRepository, ServiceTemplateComponent templateComponent,
            List<String> clusterServerList, boolean forceRestart) {

        if (device == null || firmwareRepository == null) {
            return null;
        }

        logger.info(" Generating component for device " + device.getRefId() + " for repo: "
                + firmwareRepository.getId());

        final ServiceTemplateComponent component = new ServiceTemplateComponent();
        component.setId(UUID.randomUUID().toString());
        component.setComponentID((componentName == null) ? "component0" : componentName);
        component.setType(ServiceTemplateComponent.ServiceTemplateComponentType.CONFIGURATION);
        component.setPuppetCertName(PuppetModuleUtil.toCertificateName(device));
        component.setAsmGUID(device.getRefId());
        component.setName("firmware");

        String categoryId = ServiceTemplateSettingIDs.SERVICE_TEMPLATE_FIRMWARE_COMP_ID; // all device types but servers
        if (device.getDeviceType().isServer()) {
            categoryId = ServiceTemplateSettingIDs.SERVICE_TEMPLATE_FIRMWARE_SERVER_COMP_ID; // Servers use this value
        }

        final ServiceTemplateCategory category = new ServiceTemplateCategory();
        category.setDisplayName("firmware");
        category.setId(categoryId);
        component.getResources().add(category);

        final ServiceTemplateSetting product = new ServiceTemplateSetting(
                ServiceTemplateSettingIDs.SERVICE_TEMPLATE_FIRMWARE_PRODUCT,
                PuppetModuleUtil.getPuppetModuleName(device.getDiscoverDeviceType(), device.getDeviceType()),
                ServiceTemplateSetting.ServiceTemplateSettingType.STRING);
        product.setDisplayName("product");
        category.getParameters().add(product);

        final ServiceTemplateSetting title = new ServiceTemplateSetting(
                ServiceTemplateSettingIDs.SERVICE_TEMPLATE_FIRMWARE_TITLE, component.getPuppetCertName(),
                ServiceTemplateSetting.ServiceTemplateSettingType.STRING);
        title.setDisplayName("title");
        category.getParameters().add(title);

        final ServiceTemplateSetting catalog = new ServiceTemplateSetting(
                ServiceTemplateSettingIDs.SERVICE_TEMPLATE_FIRMWARE_PATH,
                firmwareRepository.getDiskLocation() + File.separator + firmwareRepository.getFilename(),
                ServiceTemplateSetting.ServiceTemplateSettingType.STRING);
        catalog.setDisplayName("path");
        category.getParameters().add(catalog);

        Set<FirmwareDeviceInventoryEntity> firmDevInvEnts = this.deviceInventoryDAO
                .getFirmwareDeviceInventoryByRefId(device.getRefId());
        if (CollectionUtils.isNotEmpty(firmDevInvEnts)) {
            // Not sure why grabbing the first entry will return the correct component type
            final String type = firmDevInvEnts.toArray(new FirmwareDeviceInventoryEntity[0])[0].getComponentType();// chassis have 2

            // if there is only one firmware entry or this is chassis then the software component for firmware update
            if (firmDevInvEnts.size() == 1 || "chassis".equals(type)) {
                // we could also grab the firmware device inventory from out of compliance passed in
                final FirmwareDeviceInventoryEntity fdi = firmDevInvEnts
                        .toArray(new FirmwareDeviceInventoryEntity[0])[0];
                // try to find the software component with the given repository ... which should be default repo
                SoftwareComponent softwareComponent = findSoftwareComponentInRepo(firmwareRepository, fdi, device);
                String diskLocation = firmwareRepository.getDiskLocation();
                if (softwareComponent == null) {
                    // fallback and try embedded repo to get the software component 
                    final FirmwareRepositoryEntity embeddedRepository = getEmbeddedRepo();
                    softwareComponent = findDefaultEmbeddedSoftwareComponent(embeddedRepository, fdi, device);
                    diskLocation = embeddedRepository.getDiskLocation();
                }

                if (softwareComponent != null) {
                    final ServiceTemplateSetting version = new ServiceTemplateSetting(
                            ServiceTemplateSettingIDs.SERVICE_TEMPLATE_FIRMWARE_SOFTWARE_VERSION,
                            softwareComponent.getVendorVersion(),
                            ServiceTemplateSetting.ServiceTemplateSettingType.STRING);
                    version.setDisplayName("version");
                    category.getParameters().add(version);

                    final String binaryPath = diskLocation + File.separator + softwareComponent.getPath();
                    final ServiceTemplateSetting path = new ServiceTemplateSetting(
                            ServiceTemplateSettingIDs.SERVICE_TEMPLATE_FIRMWARE_SOFTWARE_PATH, binaryPath,
                            ServiceTemplateSetting.ServiceTemplateSettingType.STRING);
                    path.setDisplayName("path");
                    category.getParameters().add(path);
                } else {
                    logger.error("No softwareComponent found that matches device " + device.getRefId());
                    return null;
                }
            }
        }

        // this is for getting the esxi server, username, password
        if (templateComponent != null) {
            ServiceTemplateCategory resource = getResource(templateComponent,
                    ServiceTemplateSettingIDs.SERVICE_TEMPLATE_SERVER_OS_RESOURCE);
            if (resource != null) {
                ServiceTemplateSetting imageType = resource
                        .getParameter(ServiceTemplateSettingIDs.SERVICE_TEMPLATE_SERVER_OS_TYPE_ID);
                if (imageType != null && ServiceTemplateSettingIDs.SERVICE_TEMPLATE_SERVER_ESXI_VALUE
                        .equals(imageType.getValue())) {
                    ServiceTemplateComponent vcenterComponent = getClusterComponent(templateComponent, device,
                            imageType);
                    if (vcenterComponent != null) {
                        String certName = vcenterComponent.getPuppetCertName();
                        ServiceTemplateSetting vcenterCertnameSetting = new ServiceTemplateSetting(
                                ServiceTemplateSettingIDs.SERVICE_TEMPLATE_FIRMWARE_VCENTER_CERT, certName,
                                ServiceTemplateSetting.ServiceTemplateSettingType.STRING);
                        vcenterCertnameSetting.setDisplayName("vcenterCertname");
                        category.getParameters().add(vcenterCertnameSetting);
                        if (haEnabled(vcenterComponent)) {
                            ServiceTemplateSetting vcenterSetting = buildVcenterSetting(vcenterComponent);
                            category.getParameters().add(vcenterSetting);
                        }
                    }

                    ServiceTemplateSetting password = resource
                            .getParameter(ServiceTemplateSettingIDs.SERVICE_TEMPLATE_SERVER_OS_ADMIN_PASSWORD_ID);
                    if (password != null) {
                        ServiceTemplateSetting esxiPassword = new ServiceTemplateSetting(
                                ServiceTemplateSettingIDs.SERVICE_TEMPLATE_FIRMWARE_ESX_PASSWORD,
                                password.getValue(), ServiceTemplateSetting.ServiceTemplateSettingType.PASSWORD);
                        esxiPassword.setDisplayName("esxPassword");
                        category.getParameters().add(esxiPassword);
                    }

                    String esxiIp = this.getHostNameOrIpAddress(templateComponent);
                    ServiceTemplateSetting esxiServer = new ServiceTemplateSetting(
                            ServiceTemplateSettingIDs.SERVICE_TEMPLATE_FIRMWARE_ESX_HOSTNAME, esxiIp,
                            ServiceTemplateSetting.ServiceTemplateSettingType.STRING);
                    esxiServer.setDisplayName("esxServer");
                    category.getParameters().add(esxiServer);

                    if (clusterServerList != null && !clusterServerList.isEmpty()) {
                        String commaSeparatedServerList = getJson(clusterServerList);
                        if (commaSeparatedServerList != null) {
                            ServiceTemplateSetting esxiServerList = new ServiceTemplateSetting(
                                    ServiceTemplateSettingIDs.SERVICE_TEMPLATE_FIRMWARE_VCENTER_SERVER_LIST,
                                    commaSeparatedServerList,
                                    ServiceTemplateSetting.ServiceTemplateSettingType.STRING);
                            esxiServer.setDisplayName("Vcenter Server List");
                            category.getParameters().add(esxiServerList);
                        }
                    }
                }

            }
        }

        ServiceTemplateSetting forceRestartSetting = null;
        if (forceRestart)
            forceRestartSetting = new ServiceTemplateSetting(
                    ServiceTemplateSettingIDs.SERVICE_TEMPLATE_FIRMWARE_FORCE_RESTART, "true",
                    ServiceTemplateSetting.ServiceTemplateSettingType.BOOLEAN);
        else
            forceRestartSetting = new ServiceTemplateSetting(
                    ServiceTemplateSettingIDs.SERVICE_TEMPLATE_FIRMWARE_FORCE_RESTART, "false",
                    ServiceTemplateSetting.ServiceTemplateSettingType.BOOLEAN);
        forceRestartSetting.setDisplayName("forceRestart");
        category.getParameters().add(forceRestartSetting);

        ServiceTemplateSetting Asm_hostname = new ServiceTemplateSetting(
                ServiceTemplateSettingIDs.SERVICE_TEMPLATE_FIRMWARE_ASM_HOSTNAME, null,
                ServiceTemplateSetting.ServiceTemplateSettingType.STRING);
        Asm_hostname.setDisplayName("asm_hostname");
        category.getParameters().add(Asm_hostname);

        if (outOfComplianceFirmware != null) {
            List<FirmwareUpdateInfo> firmwareUpdates = new ArrayList<>();
            List<FirmwareUpdateInfo> softwareUpdates = new ArrayList<>();
            Set<String> binariesSeen = new HashSet<>();
            for (FirmwareDeviceInventoryEntity fdi : outOfComplianceFirmware) {
                // Must pass in a boolean indicating whether it's a Catalog/Software match and thus laods the Drivers
                boolean loadDriverType = !(fdi.getOperatingSystem() == null
                        || fdi.getOperatingSystem().trim().isEmpty());

                SourceType sourceType = loadDriverType ? SourceType.Catalog : SourceType.fromValue(fdi.getSource());

                List<SoftwareComponent> components = this.getSoftwareComponents(fdi.getComponentID(),
                        fdi.getDeviceID(), fdi.getSubdeviceID(), fdi.getVendorID(), fdi.getSubvendorID(),
                        firmwareRepository, device.getSystemId(), sourceType, fdi.getOperatingSystem(),
                        loadDriverType);

                String binaryPath = null;
                if (components != null && components.size() > 0) {
                    logger.debug("Found " + components.size()
                            + " SoftwareComponents in createFirmwareUpdateComponent method.");
                    SoftwareComponent foundComponent = components.get(0);
                    binaryPath = firmwareRepository.getDiskLocation() + File.separator + foundComponent.getPath();
                } else {
                    if (firmwareRepository != null) {
                        logger.debug("No Components found for repository " + firmwareRepository.getName()
                                + " in createFirmwareUpdateComponent method!");
                    } else { // Adding a new Exception here so we can better understand the flow in which this occurs.
                        logger.warn(
                                "FirmwareRepository is null in createFirmwareUpdateComponent method, will now use embedded catalog instead!",
                                new Exception());
                    }
                    FirmwareRepositoryEntity embedded = getEmbeddedRepo();
                    components = this.getSoftwareComponents(fdi.getComponentID(), fdi.getDeviceID(),
                            fdi.getSubdeviceID(), fdi.getVendorID(), fdi.getSubvendorID(), embedded,
                            device.getSystemId(), SourceType.fromValue(fdi.getSource()), fdi.getOperatingSystem(),
                            false);
                    if (components != null && components.size() > 0) {
                        SoftwareComponent foundComponent = components.get(0);
                        binaryPath = embedded.getDiskLocation() + File.separator + foundComponent.getPath();
                    }
                }

                if (components != null && components.size() > 0) {
                    // Only add to the list of updates if this is a unique binary. There may
                    // be a better way to determine uniqueness of the software component,
                    // but this should do for now. The idrac update will fail if we try
                    // to update the same firmware more than once.
                    if (binariesSeen.add(binaryPath)) {
                        FirmwareUpdateInfo info = new FirmwareUpdateInfo();
                        info.setInstanceId(fdi.getFqdd());
                        info.setComponentId(fdi.getComponentID());
                        info.setUriPath(binaryPath);
                        if (device.getModel().contains("C6220")) {
                            info.setVersion(components.get(0).getVendorVersion());
                        }

                        if (fdi.getOperatingSystem() == null) {
                            firmwareUpdates.add(info); // Firmware does NOT have an operating system
                        } else {
                            softwareUpdates.add(info); // Only Software components will have an operating system
                        }
                    }
                } else {
                    logger.error("Did not find a software component where expecting one.");
                }
            }

            if (firmwareUpdates.isEmpty() && softwareUpdates.isEmpty()) {
                logger.debug("No software or firmware updates required for " + device.getRefId() + " for repo: "
                        + firmwareRepository.getId());
                return null;
            }

            String firmwareValueString = toJson(firmwareUpdates);
            if (device.getModel().contains("C6220")) {
                firmwareValueString = firmwareValueString.replaceAll("component_id", "component_name");
                firmwareValueString = firmwareValueString.replaceAll("uri_path", "location");
            }

            ServiceTemplateSetting server_firmware = new ServiceTemplateSetting(
                    ServiceTemplateSettingIDs.SERVICE_TEMPLATE_FIRMWARE_SERVER_FIRMWARE, firmwareValueString,
                    ServiceTemplateSetting.ServiceTemplateSettingType.STRING);
            server_firmware.setDisplayName("server_firmware");
            category.getParameters().add(server_firmware);

            if (!softwareUpdates.isEmpty()) {

                String softwareValueString = toJson(softwareUpdates);
                if (device.getModel().contains("C6220")) {
                    softwareValueString = softwareValueString.replaceAll("component_id", "component_name");
                    softwareValueString = softwareValueString.replaceAll("uri_path", "location");
                }

                ServiceTemplateSetting software_firmware = new ServiceTemplateSetting(
                        ServiceTemplateSettingIDs.SERVICE_TEMPLATE_FIRMWARE_SERVER_SOFTWARE, softwareValueString,
                        ServiceTemplateSetting.ServiceTemplateSettingType.STRING);
                software_firmware.setDisplayName("server_software");
                category.getParameters().add(software_firmware);
            }

        }

        return component;

    }

    private boolean haEnabled(ServiceTemplateComponent vcenterComponent) {
        ServiceTemplateSetting haSetting = vcenterComponent
                .getTemplateSetting(ServiceTemplateSettingIDs.SERVICE_TEMPLATE_CLUSTER_CLUSTER_HA_ID);
        return (StringUtils.equalsIgnoreCase("true", haSetting.getValue()));
    }

    //This finds the associated vCenter cluster from a serverComponent
    private ServiceTemplateComponent getClusterComponent(ServiceTemplateComponent serverComponent,
            DeviceInventoryEntity device, ServiceTemplateSetting imageTypeSetting) {
        if (device != null) {
            ServiceTemplate oldTemplate = null;
            if (device.getDeployments() != null) {
                for (DeploymentEntity originalDeployment : device.getDeployments()) {
                    oldTemplate = MarshalUtil.unmarshal(ServiceTemplate.class,
                            originalDeployment.getMarshalledTemplateData());
                }
            }
            if (oldTemplate != null && imageTypeSetting != null
                    && ServiceTemplateSettingIDs.SERVICE_TEMPLATE_SERVER_ESXI_VALUE
                            .equals(imageTypeSetting.getValue())) {
                for (String componentId : serverComponent.getAssociatedComponents().keySet()) {
                    ServiceTemplateComponent component = oldTemplate.findComponentById(componentId);
                    for (ServiceTemplateCategory category : component.getResources()) {
                        if (ServiceTemplateSettingIDs.SERVICE_TEMPLATE_ESX_CLUSTER_COMP_ID
                                .equals(category.getId())) {
                            return component;
                        }
                    }
                }
            }
        }
        return null;
    }

    private ServiceTemplateSetting buildVcenterSetting(ServiceTemplateComponent component) {
        HashMap<String, String> vCenterMap = new HashMap<>();
        String newFlag = ServiceTemplateSettingIDs.SERVICE_TEMPLATE_CREATE_NEW_PREFIX;
        String clusterId = ServiceTemplateSettingIDs.SERVICE_TEMPLATE_CLUSTER_CLUSTER_ID;
        String dcId = ServiceTemplateSettingIDs.SERVICE_TEMPLATE_CLUSTER_DATACENTER_ID;
        String drsId = ServiceTemplateSettingIDs.SERVICE_TEMPLATE_CLUSTER_CLUSTER_DRS_ID;

        String dc = component.getTemplateSetting(dcId).getValue();
        if (StringUtils.equalsIgnoreCase(newFlag, dc)) {
            dc = component.getTemplateSetting(newFlag + dcId).getValue();
        }
        String cluster = component.getTemplateSetting(clusterId).getValue();
        if (StringUtils.equalsIgnoreCase(newFlag, cluster)) {
            cluster = component.getTemplateSetting(newFlag + clusterId).getValue();
        }
        String clusterPath = "/" + dc + "/" + cluster;

        vCenterMap.put("drs_config", component.getTemplateSetting(drsId).getValue());
        vCenterMap.put("cluster_path", clusterPath);
        String mapAsJson = null;
        try {
            mapAsJson = new ObjectMapper().writeValueAsString(vCenterMap);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        ServiceTemplateSetting vcenterSetting = new ServiceTemplateSetting(
                ServiceTemplateSettingIDs.SERVICE_TEMPLATE_VCENTER_HA_CONFIG_ID, mapAsJson,
                ServiceTemplateSetting.ServiceTemplateSettingType.STRING);
        vcenterSetting.setDisplayName("vCenterHaConfig");
        return vcenterSetting;
    }

    static String toJson(List<FirmwareUpdateInfo> updates) {
        try {
            StringWriter sw = new StringWriter();
            OBJECT_MAPPER.writeValue(sw, updates);
            return sw.toString();
        } catch (IOException e) {
            throw new AsmManagerRuntimeException("Failed to marshal firmware updates", e);
        }
    }

    static String getJson(List<String> strings) {
        try {
            StringWriter sw = new StringWriter();
            OBJECT_MAPPER.writeValue(sw, strings);
            return sw.toString();
        } catch (IOException e) {
            throw new AsmManagerRuntimeException("Failed to marshal strings", e);
        }
    }

    public void setFirmwareUpdateTime(List<FirmwareDeviceInventoryEntity> nonCompliantFirmware) {
        if (nonCompliantFirmware != null)
            for (FirmwareDeviceInventoryEntity fdi : nonCompliantFirmware) {
                fdi.setLastUpdateTime(new Date());
                this.genericDAO.update(fdi);
            }
    }

    /**
     * Utility method for retreiving the ServiceTemplateCategory that contains a resource with the specified resourceId.
     * @param component
     * @param resourceId
     * @return
     */
    private ServiceTemplateCategory getResource(ServiceTemplateComponent component, String resourceId) {
        if (component == null || resourceId == null)
            return null;

        if (component.getResources() != null)
            for (ServiceTemplateCategory cat : component.getResources()) {
                if (resourceId.equals(cat.getId()))
                    return cat;
            }

        return null;
    }

    /**
     * Returns matching software components within a specified firmware
     * repository, ignoring components of type 'Driver'. See: ASM-3756
     * @param componentId The component id
     * @param deviceId The device id
     * @param subDeviceId The sub-device id
     * @param vendorId The vendor id
     * @param subVendorId The sub-vendor id
     * @param parentRepo The repository to check for matches
     * @return A list of matching components.
     */
    public List<SoftwareComponent> getSoftwareComponents(String componentId, String deviceId, String subDeviceId,
            String vendorId, String subVendorId, FirmwareRepositoryEntity parentRepo, String systemId,
            SourceType sourceType, String operatingSystem, boolean loadDriverType) {

        logger.debug("getsoftware components with componentid: " + componentId + " deviceid: " + deviceId
                + " subdeviceid: " + subDeviceId + " vendorid: " + vendorId + " subvendorid: " + subVendorId
                + " operatingSystem:" + operatingSystem);

        final List<SoftwareComponent> components = new ArrayList<>();
        try {
            final HashMap<String, Object> attributeMap = new HashMap<>();

            if (parentRepo != null) {
                // We need at least one of the following componentId, deviceId, subDeviceId, vendorId, subVendorId, systemId
                if (StringUtils.isBlank(componentId) && StringUtils.isBlank(deviceId)
                        && StringUtils.isBlank(subDeviceId) && StringUtils.isBlank(vendorId)
                        && StringUtils.isBlank(subVendorId) && StringUtils.isBlank(systemId)) {
                    return null;
                }

                attributeMap.put("firmwareRepositoryEntity", parentRepo);
                attributeMap.put("componentId", componentId);
                attributeMap.put("deviceId", deviceId);
                attributeMap.put("subDeviceId", subDeviceId);
                attributeMap.put("vendorId", vendorId);
                attributeMap.put("subVendorId", subVendorId);
                attributeMap.put("operatingSystem", operatingSystem);

                // Special case brought on by componentId not being present in
                // the wsman command response for RAID and NICs
                // The alternative is to hard code the componentid in the
                // discovery
                // Spectre has similar logic.
                if (StringUtils.isNotEmpty(deviceId) && StringUtils.isNotEmpty(subDeviceId)
                        && StringUtils.isNotEmpty(vendorId) && StringUtils.isNotEmpty(subVendorId)
                        && StringUtils.isEmpty(componentId)) {
                    attributeMap.remove("componentId");
                }

                List<SoftwareComponentEntity> entities = null;
                if (SourceType.Device == sourceType) {
                    entities = this.genericDAO.getForEquals(attributeMap, systemId);
                } else { // it's a SourceType.Catalog and systemId does not exist
                    entities = this.genericDAO.getForEquals(attributeMap, (String) null);
                }

                if (entities != null) {
                    for (final SoftwareComponentEntity entity : entities) {
                        final String componentType = entity.getComponentType();
                        // We ONLY ignore if it's Driver and SourceType.Device (versus Catalog)
                        if (!loadDriverType && StringUtils.equalsIgnoreCase(componentType, COMPONENT_TYPE_DRIVER)
                                && SourceType.Device == sourceType) {
                            // Ignore software_components of type 'Driver'
                            logger.trace("Ignoring Driver softwareComponent" + entity.getId());
                            continue;
                        }
                        logger.trace("Found software component " + entity.getId() + " for firmware repo : "
                                + parentRepo.getName() + " " + parentRepo.getId());
                        components.add(entity.getSoftwareComponent());
                        logger.trace("Found softwareComponent " + entity.getId() + " with dellversion "
                                + entity.getDellVersion() + " vendor version " + entity.getVendorVersion());
                    }
                }
            }
        } catch (LocalizedWebApplicationException e) {
            logger.error("LocalizedWebApplicationException while retrieving software components", e);
            throw e;
        } catch (Exception e) {
            logger.error("LocalizedWebApplicationException while retrieving software components", e);
            throw new LocalizedWebApplicationException(Response.Status.INTERNAL_SERVER_ERROR,
                    AsmManagerMessages.internalError());
        }

        if (CollectionUtils.isNotEmpty(components) && components.size() > 1) {
            Collections.sort(components, componentVersionComparator);
        }

        return components;
    }

    /**
     * This method ensure all of the Compliance components necessary to provide a report for the Firmware Compliance
     * at the embedded, default, and service level are loaded properly.  This method actually modified the ManagedDevice
     * by loading it's FirmwareComplianceComponents and then returns the CompliantState as evaluated based on the 
     * existence of the varying repositories (embedded, default, and service). 
     */
    public CompliantState getComplianceState(ManagedDevice device, DeviceInventoryEntity deviceEntity) {

        CompliantState compliance = CompliantState.COMPLIANT;
        try {
            FirmwareRepositoryEntity embeddedRepo = this.getEmbeddedRepo();
            FirmwareRepositoryEntity defaultRepo = this.getDefaultRepo();
            FirmwareRepositoryEntity serviceRepo = this.getServiceRepo(deviceEntity);

            final Map<DeviceInventoryComplianceId, DeviceInventoryComplianceEntity> deviceComplianceMap = new HashMap<DeviceInventoryComplianceId, DeviceInventoryComplianceEntity>();
            for (final DeviceInventoryComplianceEntity complianceItem : deviceInventoryComplianceDAO
                    .findByDeviceInventory(deviceEntity)) {
                deviceComplianceMap.put(complianceItem.getDeviceInventoryComplianceId(), complianceItem);
            }

            Set<FirmwareDeviceInventoryEntity> firmDevInv = this.deviceInventoryDAO
                    .getFirmwareDeviceInventoryByRefId(device.getRefId());

            if (firmDevInv != null) {
                for (FirmwareDeviceInventoryEntity fdi : firmDevInv) {
                    final FirmwareComplianceComponent complianceComponents = this
                            .getFirmwareComplianceWithComponents(deviceComplianceMap, embeddedRepo, defaultRepo,
                                    serviceRepo, fdi);

                    if (complianceComponents != null) {
                        // ********** NOTE **********  this was setting the firmware compliance components
                        // fdi.setFirmwareComplianceComponents(complianceComponents);
                        CompliantState temp = complianceComponents.getCompliantState();
                        compliance = this.mergeComplianceState(compliance, temp);
                    }
                }
            }
            device.setCompliance(compliance);

        } catch (Exception e) {
            logger.error("Error in FirmwareUtil.getComplianceState() call!" + e.getMessage(), e);
        }

        return compliance;
    }

    /**
     * This method returns the ComplianceState for a single firmwaredeviceinventroy entity.
     * @param deviceComplianceMap: the device compliance map
     * @param embeddedRepo: The system's embeded repo.
     * @param defaultRepo: The system's default repo.
     * @param fdi: The firmware deviceinventory entity that we are validating
     * @return
     */
    public FirmwareComplianceComponent getFirmwareComplianceWithComponents(
            final Map<DeviceInventoryComplianceId, DeviceInventoryComplianceEntity> deviceComplianceMap,
            final FirmwareRepositoryEntity embeddedRepo, final FirmwareRepositoryEntity defaultRepo,
            final FirmwareRepositoryEntity serviceRepo, final FirmwareDeviceInventoryEntity fdi) {
        logger.trace("Checking firmwaredeviceinventory " + fdi.getId());

        final FirmwareComplianceComponent complianceComponents = new FirmwareComplianceComponent();
        final DeviceInventoryEntity deviceEntity = this.deviceInventoryDAO
                .getDeviceInventory(fdi.getDeviceInventoryId());

        final List<SoftwareComponent> embeddedRepoComponents = getSoftwareComponents(fdi.getComponentID(),
                fdi.getDeviceID(), fdi.getSubdeviceID(), fdi.getVendorID(), fdi.getSubvendorID(), embeddedRepo,
                deviceEntity.getSystemId(), SourceType.valueOf(fdi.getSource()), fdi.getOperatingSystem(), false);
        final List<SoftwareComponent> components = getSoftwareComponents(fdi.getComponentID(), fdi.getDeviceID(),
                fdi.getSubdeviceID(), fdi.getVendorID(), fdi.getSubvendorID(), defaultRepo,
                deviceEntity.getSystemId(), SourceType.valueOf(fdi.getSource()), fdi.getOperatingSystem(), false);
        final List<SoftwareComponent> serviceRepoComponents = getSoftwareComponents(fdi.getComponentID(),
                fdi.getDeviceID(), fdi.getSubdeviceID(), fdi.getVendorID(), fdi.getSubvendorID(), serviceRepo,
                deviceEntity.getSystemId(), SourceType.valueOf(fdi.getSource()), fdi.getOperatingSystem(), false);

        DeviceInventoryComplianceId complianceKey = new DeviceInventoryComplianceId(deviceEntity, embeddedRepo);
        CompliantState embeddedState = deviceComplianceMap.get(complianceKey).getCompliance();

        if (embeddedState != CompliantState.UPDATEREQUIRED && defaultRepo != null) {
            complianceKey = new DeviceInventoryComplianceId(deviceEntity, defaultRepo);
        }

        complianceComponents.setCompliantState(deviceComplianceMap.containsKey(complianceKey)
                ? deviceComplianceMap.get(complianceKey).getCompliance()
                : CompliantState.COMPLIANT);

        complianceComponents.setDefaultRepoComponents(components);
        complianceComponents.setEmbeddedRepoComponents(embeddedRepoComponents);
        complianceComponents.setServiceRepoComponents(serviceRepoComponents);

        return complianceComponents;
    }

    private void logLocalizedInfoMessage(EEMILocalizableMessage msg) {
        if (msg != null) {
            LocalizableMessageService.getInstance().logMsg(
                    (ResourceBundleLocalizableMessage) msg.getDetailedDescription(), LogSeverity.INFO,
                    LogCategory.INFRASTRUCTURE_OR_HARDWARE_CONFIGURATION, msg.getCorrelationId(), msg.getAgentId());
        }
    }

    /**
     * Runs the firmware compliance check / update for all repositories and all devices
     */
    public void runFirmwareComplianceCheckForAllRepositories() {
        logger.trace("runFirmwareComplianceCheckForAll");

        for (final FirmwareRepositoryEntity firmwareRepository : firmwareRepositoryDAO.getAll()) {
            updateComplianceMapAndDeviceComplianceForRepo(firmwareRepository);
        }
    }

    /**
     * It will also update compliance status for each device
     * @param firmwareRepository
     */
    public void updateComplianceMapAndDeviceComplianceForRepo(final FirmwareRepositoryEntity firmwareRepository) {
        logger.trace("runFirmwareComplianceCheckForRepository for repository " + firmwareRepository.getId());

        // First update the compliance_map for the device and the repository
        this.updateComplianceMapForRepo(firmwareRepository);

        // Update the device_inventory compliance field properly
        FirmwareRepositoryEntity defaultRepo = this.getDefaultRepo();
        FirmwareRepositoryEntity embeddedRepo = this.getEmbeddedRepo();

        for (final DeviceInventoryEntity deviceInventory : deviceInventoryDAO.getAllDeviceInventory()) {
            FirmwareRepositoryEntity serviceRepo = this.getServiceRepo(deviceInventory);
            this.updateDeviceInventoryComplianceStatus(deviceInventory, defaultRepo, embeddedRepo, serviceRepo);
        }
    }

    /**
     * Updates the compliance_map table with all of the compliance_maps for devices that are currently in the system.
     * 
     * @param firmwareRepoEntity the newly created Firmware Repository whose compliance_map entries will be created
     *      for all devices that are currently in the system.
     */
    public void updateComplianceMapForRepo(final FirmwareRepositoryEntity firmwareRepoEntity) {
        logger.trace(
                "updateComplianceMapAndDeviceComplianceForNewRepo for repository " + firmwareRepoEntity.getId());

        for (final DeviceInventoryEntity deviceInventory : deviceInventoryDAO.getAllDeviceInventory()) {
            updateFirmwareComplianceMapForDevice(firmwareRepoEntity, deviceInventory);
        }
    }

    /**
     * Get the firmware compliance of an individual firmware device relative to the given
     * firmware repository.  If the repository is the embedded minimal, a return value
     * of UPDATEREQUIRED is possible.  For any other repository it can only be COMPLIANT
     * or NONCOMPLIANT
     */
    public CompliantState getFirmwareCompliance(final DeviceInventoryEntity deviceInventory,
            final FirmwareRepositoryEntity firmwareRepository, final FirmwareDeviceInventoryEntity fdi) {
        if (firmwareRepository.isEmbedded()) {
            final SoftwareComponent defaultEmbeddedSC = findDefaultEmbeddedSoftwareComponent(firmwareRepository,
                    fdi, deviceInventory);
            if (defaultEmbeddedSC != null
                    && VersionUtils.compareVersions(fdi.getVersion(), defaultEmbeddedSC.getVendorVersion()) < 0) {
                logger.info(
                        "Device firmware version " + fdi.getVersion() + " is less than minimal repository version "
                                + defaultEmbeddedSC.getVendorVersion() + " therefore update is required.");
                return CompliantState.UPDATEREQUIRED;
            }
        } else {
            final SoftwareComponent defaultSC = findSoftwareComponentInRepo(firmwareRepository, fdi,
                    deviceInventory);
            if (defaultSC != null
                    && VersionUtils.compareVersions(fdi.getVersion(), defaultSC.getVendorVersion()) != 0) {
                logger.info(
                        "Device firmware version " + fdi.getVersion() + " is less than current repository version "
                                + defaultSC.getVendorVersion() + " therefore it is non compliant");
                // since device and repository versions do not match device is non compliant
                return CompliantState.NONCOMPLIANT;
            }
        }
        return CompliantState.COMPLIANT;
    }

    public SoftwareComponent findDefaultEmbeddedSoftwareComponent(final FirmwareRepositoryEntity embeddedRepo,
            final FirmwareDeviceInventoryEntity fdi, final DeviceInventoryEntity deviceEntity) {

        // If the given repo is not the embedded repo then return null
        if (!embeddedRepo.isEmbedded()) {
            logger.error("Given repository is not embeddded repository");
            return null;
        }
        final List<SoftwareComponent> embeddedRepoComponents = this.getSoftwareComponents(fdi.getComponentID(),
                fdi.getDeviceID(), fdi.getSubdeviceID(), fdi.getVendorID(), fdi.getSubvendorID(), embeddedRepo,
                deviceEntity.getSystemId(), SourceType.valueOf(fdi.getSource()), fdi.getOperatingSystem(), false);
        if (embeddedRepoComponents == null) {
            logger.error("Found a component is null");
        }
        if (embeddedRepoComponents != null && embeddedRepoComponents.size() == 1) {
            return embeddedRepoComponents.get(0);
        } else if (embeddedRepoComponents != null && embeddedRepoComponents.size() > 1) {
            String embeddedRepoId = (embeddedRepo == null) ? null : embeddedRepo.getId();
            logger.error("ERROR getting embedded software components: " + embeddedRepoComponents.size()
                    + " for repo " + embeddedRepoId + " too many results for componentid " + fdi.getComponentID()
                    + "deviceID" + fdi.getDeviceID() + " subdeviceid " + fdi.getSubdeviceID() + " vendorid "
                    + fdi.getVendorID() + " subvendorid " + fdi.getSubvendorID() + " systemid "
                    + deviceEntity.getSystemId());
            for (SoftwareComponent component : embeddedRepoComponents) {
                logger.error(
                        "Component id: " + component.getId() + " vendor version: " + component.getVendorVersion());
            }
            return embeddedRepoComponents.get(0);
        }
        return null;
    }

    /**
     * Finds matching SoftwareComponent in a FirmwareCatalog for the given FirmwareDeviceInventory (firmware component).
     * 
     * @param repo the Firmware Repositor (Catalog) that will be searched for a matching SoftwareComponent.
     * @param fdi the firmwareDeviceInventoryEntity the SoftwareCatalog must match.
     * @param deviceEntity the deviceEntity whose firmware device inventory entity is being matched.
     * @return a matching SoftwareComponent or null if none can be found.
     */
    public SoftwareComponent findSoftwareComponentInRepo(final FirmwareRepositoryEntity repo,
            final FirmwareDeviceInventoryEntity fdi, final DeviceInventoryEntity deviceEntity) {

        // if the given repo is the embedded repo then return null
        if (repo.isEmbedded()) {
            return null;
        }

        // Drivers are filtered out by default.  The ONLY time we load a driver is when we are loading software that 
        // uses an OS (vs firmware) components.  Vibs for example are of type DRVR and must be loaded.  
        boolean loadDriverType = !(fdi.getOperatingSystem() == null || fdi.getOperatingSystem().trim().isEmpty());

        SourceType sourceType = loadDriverType ? SourceType.Catalog : SourceType.fromValue(fdi.getSource());

        // Find the matching components and return if there is one or null if there are none.
        final List<SoftwareComponent> components = this.getSoftwareComponents(fdi.getComponentID(),
                fdi.getDeviceID(), fdi.getSubdeviceID(), fdi.getVendorID(), fdi.getSubvendorID(), repo,
                deviceEntity.getSystemId(), sourceType, fdi.getOperatingSystem(), loadDriverType);
        return (CollectionUtils.isNotEmpty(components)) ? components.get(0) : null;
    }

    public enum DriverType {
        ALL, FIRMWARE, SOFTWARE
    }

    /**
     * Returns a list of firmware for the given device that are either non-compliant or updaterequired
     * @param deviceInventory The device to check firmware compliance of.
     * @param firmwareRepository
     * @param discardNoOp
     * @param type DriverType enum value to determine the type of non compliant list to return
     * @return
     */
    public List<FirmwareDeviceInventoryEntity> getNonCompliantFirmware(final DeviceInventoryEntity deviceInventory,
            final FirmwareRepositoryEntity firmwareRepository, final boolean discardNoOp, final DriverType type) {

        final List<FirmwareDeviceInventoryEntity> firmwareList = new ArrayList<FirmwareDeviceInventoryEntity>();
        if (deviceInventory != null) {
            // check if device/firmware combo is compliant or not 
            final DeviceInventoryComplianceEntity deviceInventoryCompliance = this.deviceInventoryComplianceDAO
                    .get(deviceInventory, firmwareRepository);
            if (deviceInventoryCompliance != null
                    && !CompliantState.COMPLIANT.equals(deviceInventoryCompliance.getCompliance())) {

                Set<FirmwareDeviceInventoryEntity> firmDevInvEnts = this.deviceInventoryDAO
                        .getFirmwareDeviceInventoryByRefId(deviceInventory.getRefId());
                // device/firmware combo is not compliant so loop through the firmware and return only non-compliant
                if (firmDevInvEnts != null) {
                    for (FirmwareDeviceInventoryEntity fdi : firmDevInvEnts) {
                        if (type.equals(DriverType.ALL)
                                || (type.equals(DriverType.FIRMWARE) && fdi.getOperatingSystem() == null)
                                || (type.equals(DriverType.SOFTWARE) && fdi.getOperatingSystem() != null)) {
                            final SoftwareComponent defaultSC = findSoftwareComponentInRepo(firmwareRepository, fdi,
                                    deviceInventory);
                            if (discardNoOp && defaultSC != null && defaultSC.getPath().contains("ASMNoOp")) {
                                logLocalizedInfoMessage(CatalogMessages.buildInfoMsg(
                                        CatalogMessages.CATALOG_FIRMWARE_NOOP_DISCARD, deviceInventory.getModel()));
                                logger.trace(
                                        "Not returning firmware device inventory because discardNoOp is set to true");
                            } else {
                                // Means it's part of the Catalog and not installed yet.
                                if (SourceType.Catalog.getValue().equals(fdi.getSource())) {
                                    firmwareList.add(fdi);
                                } else {
                                    final CompliantState firmwareCompliance = getFirmwareCompliance(deviceInventory,
                                            firmwareRepository, fdi);
                                    // if the firmware is not compliant add it to the list
                                    if (!CompliantState.COMPLIANT.equals(firmwareCompliance)) {
                                        firmwareList.add(fdi);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        return firmwareList;
    }

    /**
     * Returns the Hypervisor IpAddress from a component if it exists. 
     */
    public String getHypervisorIp(ServiceTemplateComponent component) {

        String ipAddress = null;

        List<ServiceTemplateCategory> resources = component.getResources();
        resourcesLoop: for (ServiceTemplateCategory resource : resources) {
            if (ServiceTemplateSettingIDs.SERVICE_TEMPLATE_SERVER_NETWORKING_COMP_ID.equals(resource.getId())) {
                List<ServiceTemplateSetting> parameters = resource.getParameters();
                for (ServiceTemplateSetting parameter : parameters) {
                    if (ServiceTemplateSettingIDs.SERVICE_TEMPLATE_SERVER_NETWORK_CONFIG_ID
                            .equals(parameter.getId())) {
                        NetworkConfiguration networkConfig = parameter.getNetworkConfiguration();

                        if (networkConfig != null) {
                            List<Interface> interfaces = networkConfig.getUsedInterfaces();
                            ipAddress = fetchIpFromInterfaces(interfaces);
                            break resourcesLoop;
                        }
                    } else if (ServiceTemplateSettingIDs.SERVICE_TEMPLATE_SERVER_HYPERVISOR_NETWORK_ID
                            .equals(parameter.getId())) {
                        List<Network> networks = parameter.getNetworks();
                        for (Network network : networks) {
                            if (network.isStatic()) {
                                if (network.getStaticNetworkConfiguration() != null
                                        && network.getStaticNetworkConfiguration().getIpAddress() != null) {
                                    ipAddress = network.getStaticNetworkConfiguration().getIpAddress();
                                    break resourcesLoop;
                                }
                            }
                        }
                    }
                }
            }
        }
        return ipAddress;
    }

    public String fetchIpFromInterfaces(List<Interface> interfaceObjectList) {
        if (interfaceObjectList == null) {
            return null;
        }

        for (Interface interfaceObject : interfaceObjectList) {
            List<Partition> partitions = interfaceObject.getPartitions();
            if (partitions != null) {
                for (Partition partition : partitions) {
                    List<Network> networkObjects = partition.getNetworkObjects();
                    if (networkObjects != null) {
                        for (Network networkObject : networkObjects) {
                            if (networkObject != null
                                    && NetworkType.HYPERVISOR_MANAGEMENT.equals(networkObject.getType())
                                    && networkObject.getStaticNetworkConfiguration() != null
                                    && networkObject.getStaticNetworkConfiguration().getIpAddress() != null) {
                                return networkObject.getStaticNetworkConfiguration().getIpAddress();
                            }
                        }
                    }
                }
            }
        }
        return null;
    }

    public CompliantState mergeComplianceState(CompliantState compliance, CompliantState temp) {
        if (CompliantState.UPDATEREQUIRED.equals(temp)) {
            // this overrides the compliance status for device
            return temp;
        } else if (CompliantState.NONCOMPLIANT.equals(temp)) {
            // only if it is not already set to UPDATEREQUIRED
            if (!CompliantState.UPDATEREQUIRED.equals(compliance)) {
                return temp;
            }
        } else if (!CompliantState.COMPLIANT.equals(temp) && CompliantState.COMPLIANT.equals(compliance)) {
            // any remaining non-compliant state overrides
            return temp;
        }
        return compliance;
    }

    /**
     * This method loops through all of the repositories that exist and creates the mapping in the compliance_map table
     * for each and every repository for the device.  Once that is complete, it determines if the device is part of 
     * a service and updates the device_inventory's compliance field with the proper value from the compliance_map 
     * table (for performance reasons).
     * 
     * @param deviceInventoryEntity the device whose compliance_map will be created and whose device_inventory 
     *  compliance will be updated from the compliance_map table.
     */
    public void updateComplianceMapsAndDeviceInventoryCompliance(
            final DeviceInventoryEntity deviceInventoryEntity) {
        for (final FirmwareRepositoryEntity firmwareRepository : firmwareRepositoryDAO.getAll()) {
            updateFirmwareComplianceMapForDevice(firmwareRepository, deviceInventoryEntity);
        }

        // Update the device_inventory's compliance field from the newly updated compliance_map table
        FirmwareRepositoryEntity defaultRepo = getDefaultRepo();
        FirmwareRepositoryEntity embeddedRepo = getEmbeddedRepo();
        FirmwareRepositoryEntity serviceRepo = this.getServiceRepo(deviceInventoryEntity);

        this.updateDeviceInventoryComplianceStatus(deviceInventoryEntity, defaultRepo, embeddedRepo, serviceRepo);
    }

    /**
     * Loops through all of the deviceInventoryEntities and updates their compliance_map values for all repositories
     * and then updates the proper compliance to the device_inventory compliance field.
     * 
     * @param deviceInventoryEntities the device's whose compliance_map will be updated for all firmware repositories
     *  and whose compliance firmware status will be updated.
     */
    public void updateComplianceMapsAndDeviceInventoryCompliance(
            List<DeviceInventoryEntity> deviceInventoryEntities) {
        if (deviceInventoryEntities != null && !deviceInventoryEntities.isEmpty()) {
            for (DeviceInventoryEntity die : deviceInventoryEntities) {
                this.updateComplianceMapsAndDeviceInventoryCompliance(die);
            }
        }
    }

    /**
     * Returns an Array of FirmwareComplianceReports for all of the Devices that are part of the Deployment (if the
     * device's firmware can be managed).
     * 
     * @param deploymentEntity the Deployment whose devices will be checked and a FirmwareComplianceReport generated
     * @return an Array of FirmwareComplianceReports for all of the Devices that are part of the Deployment (if the
     * device's firmware can be managed).
     */
    public FirmwareComplianceReport[] getFirmwareComplianceReportsForDeployment(DeploymentEntity deploymentEntity)
            throws AsmCheckedException {

        ArrayList<FirmwareComplianceReport> firmwareComplianceReports = new ArrayList<FirmwareComplianceReport>();

        FirmwareRepositoryEntity serviceRepoEntity = deploymentEntity.getFirmwareRepositoryEntity();
        // Verify we can do a report and throw error otherwise
        if (serviceRepoEntity == null) {
            throw new AsmCheckedException(
                    "No Service Repository found for service with id: " + deploymentEntity.getId(),
                    AsmManagerMessages.noRepositoryFoundForService(deploymentEntity.getName()));
        }
        if (!deploymentEntity.isManageFirmware()) {
            throw new AsmCheckedException(
                    "Service is not managaing firmware for service with id: " + deploymentEntity.getId(),
                    AsmManagerMessages.serviceIsNotManagingFirmware(deploymentEntity.getName()));
        }

        // If Device is part of a Deployment process the FirmwareRepository for that Service 
        for (DeviceInventoryEntity deviceInventoryEntity : deploymentEntity.getDeployedDevices()) {
            if (deviceInventoryEntity.getDeviceType().isFirmwareComplianceManaged()) {
                FirmwareComplianceReport firmwareComplianceReport = this
                        .getFirmwareComplianceReportForDevice(deviceInventoryEntity, null, null, serviceRepoEntity);
                if (firmwareComplianceReport != null && // Only load a report if there are components for it!
                        firmwareComplianceReport.getFirmwareComplianceReportComponents() != null
                        && firmwareComplianceReport.getFirmwareComplianceReportComponents().size() > 0) {
                    firmwareComplianceReport.sortFirmwareComplianceReportComponentsByNameAndStatus();
                    firmwareComplianceReports.add(firmwareComplianceReport);
                }
            }
        }

        Collections.sort(firmwareComplianceReports, COMPLIANCE_REPORT_COMPARATOR);

        return firmwareComplianceReports.toArray(new FirmwareComplianceReport[firmwareComplianceReports.size()]);
    }

    private FirmwareComplianceReport getFirmwareComplianceReportForDevice(
            DeviceInventoryEntity deviceInventoryEntity, FirmwareRepositoryEntity defaultRepoEntity,
            FirmwareRepositoryEntity embeddedRepoEntity, FirmwareRepositoryEntity serviceRepoEntity) {

        if (defaultRepoEntity == null && embeddedRepoEntity == null && serviceRepoEntity == null) {
            throw new IllegalArgumentException(
                    "A default, embedded, or service firmware repository must be passed in as a parameter.");
        }

        FirmwareComplianceReport firmwareComplianceReport = new FirmwareComplianceReport();
        firmwareComplianceReport.setCompliant(true);

        String catalogType = null;
        FirmwareRepositoryEntity repositoryEntity = null;
        if (defaultRepoEntity != null) {
            catalogType = "Default Catalog - ";
            repositoryEntity = defaultRepoEntity;
        } else if (embeddedRepoEntity != null) {
            catalogType = "Embedded Catalog - ";
            repositoryEntity = embeddedRepoEntity;
        } else if (serviceRepoEntity != null) {
            catalogType = "Service Catalog - ";
            repositoryEntity = serviceRepoEntity;
        }
        firmwareComplianceReport.setFirmwareRepositoryName(catalogType + repositoryEntity.getName());
        firmwareComplianceReport.setServiceTag(deviceInventoryEntity.getServiceTag());
        firmwareComplianceReport.setIpAddress(deviceInventoryEntity.getIpAddress());
        firmwareComplianceReport.setDeviceType(deviceInventoryEntity.getDeviceType());
        firmwareComplianceReport.setModel(deviceInventoryEntity.getModel());
        ManagedDevice device = DeviceInventoryUtils.toDTO(deviceInventoryEntity, true);
        firmwareComplianceReport.setManagedState(device.getManagedState());
        firmwareComplianceReport.setAvailable(!device.isInUse());
        firmwareComplianceReport.setEmbededRepo(repositoryEntity.isEmbedded());
        firmwareComplianceReport.setDeviceState(deviceInventoryEntity.getState());

        List<FirmwareComplianceReportComponent> svrComps = null;
        if (defaultRepoEntity != null || embeddedRepoEntity != null) {
            svrComps = this.getComponentsForDefaultAndEmbeddedRepos(deviceInventoryEntity, embeddedRepoEntity,
                    defaultRepoEntity);
        } else if (serviceRepoEntity != null) {
            svrComps = this.getComponentsForServiceRepo(deviceInventoryEntity, serviceRepoEntity);
        }
        if (svrComps != null) {
            firmwareComplianceReport.setFirmwareComplianceReportComponents(svrComps);
        }

        // Make sure it's compliant
        for (FirmwareComplianceReportComponent fcrc : firmwareComplianceReport
                .getFirmwareComplianceReportComponents()) {
            if (!fcrc.isCompliant()) {
                firmwareComplianceReport.setCompliant(false);
                break;
            }
        }

        return firmwareComplianceReport;
    }

    /**
     * Returns a FirmwareComplianceReport for the given Device.  If a Device is part of a Service, then the report 
     * is generated against the Firmware Repository assigned to the Service.  If a Device is not part of the Service, 
     * then each component will be checked against the default Firmware Repository.  If no component is found in the 
     * default Firmware Repository (or no default is set), then the component will be checked against the embedded
     * Firmware Repository that ships with ASM.  
     * 
     * @param deviceInventoryEntity the Device whom the Firmware Compliance Report is generated against.
     * @return a FirmwareComplianceReport for the given Device.
     */
    public FirmwareComplianceReport getFirmwareComplianceReportForDevice(
            DeviceInventoryEntity deviceInventoryEntity) {

        FirmwareComplianceReport firmwareComplianceReport = null;

        // If Device is part of a Deployment process the FirmwareRepository for that Service 
        if (deviceInventoryEntity.getDeployments() != null && deviceInventoryEntity.getDeployments().size() > 0
                && deviceInventoryEntity.getDeployments().get(0).isManageFirmware()
                && !deviceInventoryEntity.getDeviceType().isSharedDevice()) { // Shared Devices will be part of multiple services
            DeploymentEntity deploymentEntity = this.deploymentDAO.getDeployment(
                    deviceInventoryEntity.getDeployments().get(0).getId(), DeploymentDAO.ALL_ENTITIES);
            FirmwareRepositoryEntity serviceRepoEntity = deploymentEntity.getFirmwareRepositoryEntity();

            firmwareComplianceReport = getFirmwareComplianceReportForDevice(deviceInventoryEntity, null, null,
                    serviceRepoEntity);
        } else {
            FirmwareRepositoryEntity defaultRepo = this.getDefaultRepo();
            FirmwareRepositoryEntity embeddedRepo = this.getEmbeddedRepo();
            firmwareComplianceReport = getFirmwareComplianceReportForDevice(deviceInventoryEntity, defaultRepo,
                    embeddedRepo, null);
        }
        if (firmwareComplianceReport != null) {
            firmwareComplianceReport.sortFirmwareComplianceReportComponentsByNameAndStatus();
        }
        return firmwareComplianceReport;
    }

    public List<FirmwareComplianceReportComponent> getComponentsForDefaultAndEmbeddedRepos(
            DeviceInventoryEntity deviceInventoryEntity, FirmwareRepositoryEntity embeddedRepoEntity,
            FirmwareRepositoryEntity defaultRepoEntity) {
        ArrayList<FirmwareComplianceReportComponent> firmwareComplianceReportComponents = new ArrayList<FirmwareComplianceReportComponent>();

        Set<FirmwareDeviceInventoryEntity> firmDeviceInvEntities = this.deviceInventoryDAO
                .getFirmwareDeviceInventoryByRefId(deviceInventoryEntity.getRefId());

        for (FirmwareDeviceInventoryEntity firmDevInvEntity : firmDeviceInvEntities) {
            FirmwareComplianceReportComponent firmwareComplianceReportComponent = new FirmwareComplianceReportComponent();
            if (ComponentType.DRIVER.getValue().equals(firmDevInvEntity.getComponentType())) {
                firmwareComplianceReportComponent.setSoftware(true);
                firmwareComplianceReportComponent.setVendor(firmDevInvEntity.getVendorID());
            }

            firmwareComplianceReportComponent.setName(firmDevInvEntity.getName());
            firmwareComplianceReportComponent.setId(firmDevInvEntity.getId());
            firmwareComplianceReportComponent.setCompliant(true);

            boolean loadDriverType = !(firmDevInvEntity.getOperatingSystem() == null
                    || firmDevInvEntity.getOperatingSystem().trim().isEmpty());

            // If loadDriverType is true then it means it's from the Catalog, and there will be no systemId
            SoftwareComponent defaultSoftwareComponent = this.getSoftwareComponent(firmDevInvEntity,
                    defaultRepoEntity, deviceInventoryEntity.getSystemId(), loadDriverType);
            SoftwareComponent embeddedSoftwareComponent = this.getSoftwareComponent(firmDevInvEntity,
                    embeddedRepoEntity, deviceInventoryEntity.getSystemId(), loadDriverType);

            // Unsupported Devices will not have any components returned.
            FirmwareComplianceReportComponentVersionInfo currentVersion = this.getVersionInfo(firmDevInvEntity);
            FirmwareComplianceReportComponentVersionInfo targetVersion = this.getVersionInfo(firmDevInvEntity);

            currentVersion.setFirmwareLevel(FirmwareComplianceReportComponentVersionInfo.FIRMWARE_LEVEL_SERVICE);
            targetVersion.setFirmwareLevel(FirmwareComplianceReportComponentVersionInfo.FIRMWARE_LEVEL_SERVICE);

            // Set the TargetVersion
            targetVersion.setFirmwareVersion(
                    (defaultSoftwareComponent != null) ? defaultSoftwareComponent.getVendorVersion()
                            : (embeddedSoftwareComponent != null) ? embeddedSoftwareComponent.getVendorVersion()
                                    : NOT_AVAILABLE);

            // Set Compliance for Component
            if (embeddedSoftwareComponent != null) {
                if (VersionUtils.compareVersions(currentVersion.getFirmwareVersion(),
                        embeddedSoftwareComponent.getVendorVersion()) >= 0) {
                    if (defaultSoftwareComponent != null) {
                        // Only ok if the firmware exactly matches the default
                        if (VersionUtils.compareVersions(currentVersion.getFirmwareVersion(),
                                defaultSoftwareComponent.getVendorVersion()) == 0) {
                            firmwareComplianceReportComponent.setCompliant(true);
                        } else {
                            firmwareComplianceReportComponent.setCompliant(false);
                        }
                    } else {
                        // There is no default software component set therefore we default to compliant
                        firmwareComplianceReportComponent.setCompliant(true);
                    }
                } else {
                    // Firmware is non-compliant because it is less than our embedded minimums
                    firmwareComplianceReportComponent.setCompliant(false);
                }
            } else {
                if (defaultSoftwareComponent != null) {
                    // Only ok if the firmware exactly matches the default
                    if (VersionUtils.compareVersions(currentVersion.getFirmwareVersion(),
                            defaultSoftwareComponent.getVendorVersion()) == 0) {
                        firmwareComplianceReportComponent.setCompliant(true);
                    } else {
                        firmwareComplianceReportComponent.setCompliant(false);
                    }
                } else {
                    // there are no known software components therfore we default to compliant
                    firmwareComplianceReportComponent.setCompliant(true);
                }
            }

            firmwareComplianceReportComponent.setCurrentVersion(currentVersion);
            firmwareComplianceReportComponent.setTargetVersion(targetVersion);
            firmwareComplianceReportComponents.add(firmwareComplianceReportComponent);
        }

        return firmwareComplianceReportComponents;
    }

    private List<FirmwareComplianceReportComponent> getComponentsForServiceRepo(
            DeviceInventoryEntity deviceInventoryEntity, FirmwareRepositoryEntity serviceRepoEntity) {
        ArrayList<FirmwareComplianceReportComponent> firmwareComplianceReportComponents = new ArrayList<FirmwareComplianceReportComponent>();

        Set<FirmwareDeviceInventoryEntity> firmDeviceInvEntities = this.deviceInventoryDAO
                .getFirmwareDeviceInventoryByRefId(deviceInventoryEntity.getRefId());

        for (FirmwareDeviceInventoryEntity firmDevInvEntity : firmDeviceInvEntities) {
            FirmwareComplianceReportComponent firmwareComplianceReportComponent = new FirmwareComplianceReportComponent();
            if (ComponentType.DRIVER.getValue().equals(firmDevInvEntity.getComponentType())) {
                firmwareComplianceReportComponent.setSoftware(true);
                firmwareComplianceReportComponent.setVendor(firmDevInvEntity.getVendorID());
            }
            firmwareComplianceReportComponent.setName(firmDevInvEntity.getName());
            firmwareComplianceReportComponent.setId(firmDevInvEntity.getId());
            firmwareComplianceReportComponent.setCompliant(true);

            // Hack so we load things properly for Catalog items
            boolean loadDriverType = !(firmDevInvEntity.getOperatingSystem() == null
                    || firmDevInvEntity.getOperatingSystem().trim().isEmpty());
            String systemId = deviceInventoryEntity.getSystemId();
            if (loadDriverType) { // Means it's from the Catalog, and there will be no systemId
                systemId = null;
            }

            SoftwareComponent softwareComponent = this.getSoftwareComponent(firmDevInvEntity, serviceRepoEntity,
                    systemId, loadDriverType);

            // Unsupported Devices will not have any components returned.
            FirmwareComplianceReportComponentVersionInfo currentVersion = this.getVersionInfo(firmDevInvEntity);
            FirmwareComplianceReportComponentVersionInfo targetVersion = this.getVersionInfo(firmDevInvEntity);

            currentVersion.setFirmwareLevel(FirmwareComplianceReportComponentVersionInfo.FIRMWARE_LEVEL_SERVICE);
            targetVersion.setFirmwareLevel(FirmwareComplianceReportComponentVersionInfo.FIRMWARE_LEVEL_SERVICE);

            if (!SourceType.Catalog.getValue().equals(firmDevInvEntity.getSource())) {
                if (softwareComponent != null) {
                    targetVersion.setFirmwareVersion(softwareComponent.getVendorVersion());

                    if (VersionUtils.compareVersions(currentVersion.getFirmwareVersion(),
                            targetVersion.getFirmwareVersion()) != 0) {
                        firmwareComplianceReportComponent.setCompliant(false);
                    }
                } else { // Nothing to match against so considered compliant
                    targetVersion.setFirmwareVersion(NOT_AVAILABLE);
                }
            } else {
                currentVersion.setFirmwareVersion(NOT_AVAILABLE); // There is no current version for a Catalog as it's not installed yet
                firmwareComplianceReportComponent.setCompliant(false);
            }

            firmwareComplianceReportComponent.setCurrentVersion(currentVersion);
            firmwareComplianceReportComponent.setTargetVersion(targetVersion);
            firmwareComplianceReportComponents.add(firmwareComplianceReportComponent);
        }

        return firmwareComplianceReportComponents;
    }

    // Returns a populated VersionInfo object 
    private FirmwareComplianceReportComponentVersionInfo getVersionInfo(
            FirmwareDeviceInventoryEntity firmDevInvEntity) {
        FirmwareComplianceReportComponentVersionInfo versionInfo = new FirmwareComplianceReportComponentVersionInfo();

        versionInfo.setFirmwareLastUpdateTime(firmDevInvEntity.getLastUpdateTime());
        versionInfo.setFirmwareName(firmDevInvEntity.getName());
        versionInfo.setFirmwareType(firmDevInvEntity.getComponentType());
        versionInfo.setFirmwareVersion(
                (StringUtils.isNotBlank(firmDevInvEntity.getVersion())) ? firmDevInvEntity.getVersion()
                        : NOT_AVAILABLE);
        versionInfo.setId(firmDevInvEntity.getId());

        return versionInfo;
    }

    // Returns the SoftwareComponent for a given firmware repository
    private SoftwareComponent getSoftwareComponent(FirmwareDeviceInventoryEntity fdi,
            FirmwareRepositoryEntity firmRepoEntity, String systemId, boolean loadDriverType) {

        List<SoftwareComponent> softwareComponents = this.getSoftwareComponents(fdi.getComponentID(),
                fdi.getDeviceID(), fdi.getSubdeviceID(), fdi.getVendorID(), fdi.getSubvendorID(), firmRepoEntity,
                systemId, SourceType.valueOf(fdi.getSource()), fdi.getOperatingSystem(), loadDriverType);

        // Adding this check and logging to help identify conditions when more than one software component is 
        // returned. We should always only see one component but there may be some ambiguity in the codebase that allows
        // for more than one software component to be returned in the list.
        if (CollectionUtils.isNotEmpty(softwareComponents) && (softwareComponents.size() > 1)) {
            logger.warn(String.format(
                    "More than one default software component was returned for the given firmware device %s", fdi));
        }

        return (CollectionUtils.isNotEmpty(softwareComponents)) ? softwareComponents.get(0) : null;
    }

    /**
     * Fetch OS software packages for our server device and save to relevant database
     * @param devInv DeviceInventoryEntity object
     */
    public Set<FirmwareDeviceInventory> fetchVibsFirmwareDeviceInventory(DeviceInventoryEntity devInv,
            String parentJobId, String jobId) {
        Set<FirmwareDeviceInventory> firmwareDeviceInventory = null;

        // Ideally the server should be deployed before we retrieve OS data, but we're going to check every time in 
        // case it's in another state.  We're catching the exception below, so it should recover properly if we're 
        // unable to communicate with the server.  
        try {
            if (devInv != null) {
                logger.debug("Fetching software package inventory for " + devInv.getDisplayName());
                List<DeploymentEntity> deploymentEntities = devInv.getDeployments();
                String osIpAddress = devInv.getOsIpAddress();
                String hostIpAddress = devInv.getOsIpAddress(); // host-ip is osIpAddress 
                if (deploymentEntities != null && deploymentEntities.size() == 1) { // For server, there is only one deployment service
                    // For our deployment object, fetch our desired template settings
                    DeploymentEntity entity = deploymentEntities.get(0);
                    HashSet<String> ids = new HashSet<String>();
                    ids.add(ServiceTemplateSettingIDs.SERVICE_TEMPLATE_SERVER_OS_TYPE_ID);
                    ids.add(ServiceTemplateSettingIDs.SERVICE_TEMPLATE_SERVER_OS_ADMIN_PASSWORD_ID);
                    if (osIpAddress == null) {
                        ids.add(ServiceTemplateSettingIDs.HYPERVISOR_IP_ADDRESS);
                    }
                    String marshalledTemplateData = entity.getMarshalledTemplateData();
                    if (!StringUtils.isBlank(marshalledTemplateData)) {
                        ServiceTemplate template = MarshalUtil.unmarshal(ServiceTemplate.class,
                                marshalledTemplateData);
                        Map<String, String> parameterValues = ServiceDeploymentUtil
                                .getDeployedTemplateParameterValues(template, devInv.getRefId(),
                                        ServiceTemplateSettingIDs.SERVICE_TEMPLATE_SERVER_OS_RESOURCE, ids);
                        String os_type = parameterValues
                                .get(ServiceTemplateSettingIDs.SERVICE_TEMPLATE_SERVER_OS_TYPE_ID);

                        // If the deployed OS is ESXi, fetch packages for ESXi
                        logger.debug("Deployed OS retrieved from service template settings: " + os_type);
                        if (os_type != null
                                && os_type.equals(ServiceTemplateSettingIDs.SERVICE_TEMPLATE_SERVER_ESXI_VALUE)) {
                            String esxCredentialId = parameterValues
                                    .get(ServiceTemplateSettingIDs.SERVICE_TEMPLATE_SERVER_OS_ADMIN_PASSWORD_ID);
                            // In Brownfield based Services, there will be no no OS Password available, so we need to 
                            // pass in the VCenter Credential instead. 
                            if (esxCredentialId == null || esxCredentialId.isEmpty()) {
                                DeviceInventoryEntity vcenterDie = this.findAssociatedVCenterDie(template, devInv);
                                esxCredentialId = vcenterDie.getCredId();
                                hostIpAddress = vcenterDie.getIpAddress(); // needs to equal vcenterIp here
                            }
                            if (osIpAddress == null || osIpAddress.isEmpty()) {
                                ServiceTemplateComponent templateComponent = template
                                        .findComponentByGUID(devInv.getRefId());
                                osIpAddress = this.getHostNameOrIpAddress(templateComponent);
                            }

                            PuppetIdracServerDevice puppetIdracServerDevice = fetchEsxiSoftwareInventory(
                                    esxCredentialId, hostIpAddress, osIpAddress);
                            if (puppetIdracServerDevice != null) {
                                firmwareDeviceInventory = getFirmwareDeviceInventory(devInv,
                                        puppetIdracServerDevice, parentJobId, jobId);
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.warn("Failed to get software package inventory on installed OS", e);
        }

        return firmwareDeviceInventory;
    }

    /**
     * Fetch ESXi software packages inventory via ESXi puppet device
     * @param credentialId this could be the OS admin password Id of the ESXi host or it could be the VCenter 
     *       credentialId.  The credentialId should go along with the hostIpAddress.
     * @param hostIpAddress this will either be the ip address of the hostIpAddress or it will be the VCenter IP address
     *       there was no Credential (password) available of the ESXI OS (as in the case of brownfield)
     * @param osIpAddress IP address of the ESXi Operating System
     * @return String representing json-formatted software inventory data
     * @throws Exception
     */
    private PuppetIdracServerDevice fetchEsxiSoftwareInventory(String credentialId, String hostIpAddress,
            String osIpAddress) throws Exception {
        PuppetIdracServerDevice puppetIdracServerDevice = null;

        if (credentialId == null || hostIpAddress == null || osIpAddress == null) {
            logger.error("Cannot get OS package inventory due to missing parameters ");
        } else {
            DiscoverDeviceType vCenterDeviceType = DiscoverDeviceType.VCENTER;

            PuppetDiscoveryRequest request = new PuppetDiscoveryRequest(hostIpAddress,
                    vCenterDeviceType.getPuppetModuleName(), credentialId, vCenterDeviceType.getConnectType());
            request.setExistingRefId("esxi-" + osIpAddress);
            request.setOsIpAddress(osIpAddress);

            request.setScriptPath(vCenterDeviceType.getPuppetModuleName() + "/bin/esx_software_discovery.rb");
            Map<String, String> facts = PuppetModuleUtil.discoverAndShowFacts(request, true);
            if (facts == null || facts.size() <= 0 || facts.get("installed_packages") == null
                    || facts.get("installed_packages").isEmpty()) {
                logger.error("Cannot get OS installed packages from puppet facts");
            } else {
                ArrayList<HashMap> deviceDetails = OBJECT_MAPPER.readValue(facts.get("installed_packages"),
                        ArrayList.class);
                if (deviceDetails != null) {
                    logger.debug("VIBS installed_packages: " + deviceDetails.size()
                            + " when calling fetchEsxiSoftwareInventory(String, String)");
                } else { // Log and add Exception so we can have a stacktrace to see what flow this is occurring on
                    logger.warn(
                            "No VIBS installed_packages available when calling fetchEsxiSoftwareInventory(String, String)!",
                            new Exception());
                }
                puppetIdracServerDevice = PuppetDbUtil.convertToPuppetIdracServerDevice(deviceDetails);
                puppetIdracServerDevice.setEsxVersion(facts.get("esx_version"));
            }
        }

        return puppetIdracServerDevice;
    }

    private Set<FirmwareDeviceInventory> getFirmwareDeviceInventory(DeviceInventoryEntity devInvEntity,
            PuppetIdracServerDevice puppetIdracServerDevice, String parentJobId, String jobId) {
        Set<FirmwareDeviceInventory> currentFirmwareInventory = new HashSet<FirmwareDeviceInventory>();
        ManagedDevice managedDevice = DeviceInventoryUtils.toDTO(devInvEntity, false, false);

        String operatingSystemRev = this.getEsxiOperatingSystemRev(puppetIdracServerDevice.getEsxVersion());

        // Get the DeviceInventory from the Server itself
        if (puppetIdracServerDevice != null && puppetIdracServerDevice.getVibsInfos() != null
                && !puppetIdracServerDevice.getVibsInfos().isEmpty()) {
            for (PuppetIdracServerDevice.VibsInfo vibsInfo : puppetIdracServerDevice.getVibsInfos()) {
                FirmwareDeviceInventory frmDeviceInv = new FirmwareDeviceInventory();
                frmDeviceInv.setComponentType(ComponentType.DRIVER.getValue());
                frmDeviceInv.setComponentID(vibsInfo.getName());
                frmDeviceInv.setDeviceID(null); // No DeviceId for Vibs
                frmDeviceInv.setDeviceInventory(managedDevice);
                frmDeviceInv.setFqdd(vibsInfo.getId());
                frmDeviceInv.setIpaddress(devInvEntity.getIpAddress());
                frmDeviceInv.setJobId(jobId);
                frmDeviceInv.setName(vibsInfo.getName());
                frmDeviceInv.setOperatingSystem(operatingSystemRev);
                frmDeviceInv.setParent_job_id(parentJobId);
                frmDeviceInv.setServicetag(devInvEntity.getServiceTag());
                frmDeviceInv.setSourceType(SourceType.Device);
                frmDeviceInv.setSubdeviceID(null); // No SubDeviceId for Vibs
                frmDeviceInv.setSubvendorID(null); // No SubVendor for Vibs
                frmDeviceInv.setVendorID(vibsInfo.getVendor());
                frmDeviceInv.setVersion(vibsInfo.getVersion());
                frmDeviceInv.setLastUpdateTime(this.getDate(vibsInfo.getCreationDate())); // Not using Install Date
                currentFirmwareInventory.add(frmDeviceInv);
            }
        }

        // Get the SoftwareInventory from the Catalog that's assigned to this Server's Service
        if (devInvEntity.getDeployments() != null && !devInvEntity.getDeployments().isEmpty()) {
            DeploymentEntity deploymentEntity = devInvEntity.getDeployments().get(0);
            // Get deploymentEntity with repository
            deploymentEntity = this.deploymentDAO.getDeployment(deploymentEntity.getId(),
                    DeploymentDAO.FIRMWARE_REPOSITORY_ENTITY);
            if (deploymentEntity != null && deploymentEntity.getFirmwareRepositoryEntity() != null) {

                // Only add the Software ones whose operating system match that of the Server                
                List<SoftwareComponentEntity> softCompEntities = firmwareRepositoryDAO
                        .getSoftwareComponentEntitiesWithOperatingSystem(
                                deploymentEntity.getFirmwareRepositoryEntity().getId(), operatingSystemRev);

                for (SoftwareComponentEntity softCompEntity : softCompEntities) {
                    FirmwareDeviceInventory frmDeviceInv = new FirmwareDeviceInventory();
                    frmDeviceInv.setComponentType(ComponentType.DRIVER.getValue()); // MUST be DRIVER, so safe to set
                    frmDeviceInv.setComponentID(softCompEntity.getComponentId());
                    frmDeviceInv.setDeviceID(softCompEntity.getDeviceId()); // should be null
                    frmDeviceInv.setDeviceInventory(managedDevice);
                    frmDeviceInv.setFqdd(softCompEntity.getPackageId()); // Not quite the same as vibs id, but close
                    frmDeviceInv.setIpaddress(devInvEntity.getIpAddress());
                    frmDeviceInv.setJobId(jobId);
                    frmDeviceInv.setName(softCompEntity.getName());
                    frmDeviceInv.setOperatingSystem(operatingSystemRev);
                    frmDeviceInv.setParent_job_id(parentJobId);
                    frmDeviceInv.setServicetag(devInvEntity.getServiceTag());
                    frmDeviceInv.setSubdeviceID(null); // No SubDeviceId for Vibs
                    frmDeviceInv.setSubvendorID(null); // No SubVendor for Vibs
                    frmDeviceInv.setVendorID(softCompEntity.getVendorId());
                    frmDeviceInv.setVersion(softCompEntity.getVendorVersion());
                    frmDeviceInv.setLastUpdateTime(softCompEntity.getUpdatedDate());
                    frmDeviceInv.setSourceType(SourceType.Catalog);

                    // If the Component already exists we'll try to update the name to that of the Catalogs
                    FirmwareDeviceInventory matchFrmDevInv = this
                            .findFirmwareDeviceInventory(currentFirmwareInventory, frmDeviceInv);
                    if (matchFrmDevInv != null) {
                        matchFrmDevInv.setName(frmDeviceInv.getName());
                        matchFrmDevInv.setFqdd(frmDeviceInv.getFqdd());
                    } // if we can't find it, then we'll simply add the catalog to the firmware inventory
                    else {
                        currentFirmwareInventory.add(frmDeviceInv);
                    }
                }
            }
        }

        return currentFirmwareInventory;
    }

    private FirmwareDeviceInventory findFirmwareDeviceInventory(Set<FirmwareDeviceInventory> frmDevInvs,
            FirmwareDeviceInventory frmDevInvToFind) {
        FirmwareDeviceInventory foundFrmDeviceInv = null;

        if (frmDevInvs != null && !frmDevInvs.isEmpty() && frmDevInvToFind != null) {
            for (FirmwareDeviceInventory firmDevInv : frmDevInvs) {
                if (StringUtils.equals(firmDevInv.getComponentID(), frmDevInvToFind.getComponentID())
                        && StringUtils.equals(firmDevInv.getComponentType(), frmDevInvToFind.getComponentType())
                        && StringUtils.equals(firmDevInv.getVendorID(), frmDevInvToFind.getVendorID())
                        && StringUtils.equals(firmDevInv.getServicetag(), frmDevInvToFind.getServicetag())
                        && StringUtils.equals(firmDevInv.getIpaddress(), frmDevInvToFind.getIpaddress())
                        && StringUtils.equals(firmDevInv.getOperatingSystem(),
                                frmDevInvToFind.getOperatingSystem())) {
                    foundFrmDeviceInv = firmDevInv;
                    break;
                }
            }
        }

        return foundFrmDeviceInv;
    }

    private Date getDate(String dateStr) {
        Date date = null;
        if (dateStr != null) {
            date = DATE_FORMATTER.parseDateTime(dateStr).toDate(); // example date 2016-06-07
        }
        return date;
    }

    /**
     * This method will clear the Software Inventory for relevant Servers in a deployment and then update them again.
     *
     * @param deploymentEntity
     * @throws AsmManagerCheckedException
     */
    public void updateServersSoftwareDeviceInventory(DeploymentEntity deploymentEntity)
            throws AsmManagerCheckedException {
        if (deploymentEntity != null && deploymentEntity.getDeployedDevices() != null) {
            String jobId = UUID.randomUUID().toString();
            String parentJobId = UUID.randomUUID().toString();
            for (DeviceInventoryEntity deviceInvEnt : deploymentEntity.getDeployedDevices()) {
                if (deviceInvEnt.getDeviceType().isServer()) {
                    // Remove existing Inventory
                    this.deviceInventoryDAO.deleteSoftwareDeviceInventoryForDevice(deviceInvEnt.getRefId());

                    // Generate the new Inventory
                    Set<FirmwareDeviceInventory> firmDevInvSet = this.fetchVibsFirmwareDeviceInventory(deviceInvEnt,
                            parentJobId, jobId);
                    if (firmDevInvSet != null) {
                        for (FirmwareDeviceInventory firmDevInv : firmDevInvSet) {
                            this.deviceInventoryDAO
                                    .createFirmwareDeviceInventory(new FirmwareDeviceInventoryEntity(firmDevInv));
                        }
                    }
                }
            }
        }
    }

    public static JobDetail getUpdateFirmwareJob(final IJobManager jobManager, final String deploymentId,
            final boolean exitMaintenance, final boolean nextReboot, final String updateType,
            final String componentsToUpdate, final String firmwareRepositoryId) {
        return getUpdateFirmwareJob(jobManager, deploymentId, exitMaintenance, nextReboot, updateType,
                componentsToUpdate, firmwareRepositoryId, null);
    }

    public static JobDetail getUpdateFirmwareJob(final IJobManager jobManager, final String deploymentId,
            final boolean exitMaintenance, final boolean nextReboot, final String updateType,
            final String componentsToUpdate, final String firmwareRepositoryId, final String groupName) {
        JobDetail job;
        if (groupName != null) {
            JobCreateSpec jobspec = new JobCreateSpec(FirmwareUpdateJob.class);
            jobspec.setDescription(groupName);
            jobspec.setSelector(groupName);
            // Setting true will allow us to search jobs by groupName
            job = jobManager.createNamedJob(jobspec, true);
        } else {
            job = jobManager.createNamedJob(FirmwareUpdateJob.class);
        }
        job.getJobDataMap().put(FirmwareUpdateJob.GROUP_SELECTOR_KEY, groupName);
        job.getJobDataMap().put(FirmwareUpdateJob.UPDATE_KEY, deploymentId);
        job.getJobDataMap().put(FirmwareUpdateJob.EXIT_MAINTANACE, exitMaintenance);
        job.getJobDataMap().put(FirmwareUpdateJob.NEXT_REBOOT, nextReboot);
        job.getJobDataMap().put(FirmwareUpdateJob.UPDATE_TYPE_KEY, updateType);
        job.getJobDataMap().put(FirmwareUpdateJob.COMPONENTS_TO_UPDATE, componentsToUpdate);
        if (firmwareRepositoryId != null) {
            job.getJobDataMap().put(FirmwareUpdateJob.FIRMWARE_REPOSITORY, firmwareRepositoryId);
        }
        return job;
    }

    /**
     * Used to reset a deployment to unmanaged firmware state.  It should update the FirmwareRepositoryEntity, but
     * does not update the deployment entity on purpose.
     * @param deploymentEntity deploymentEntity to reset managing firmware
     */
    public void unmanageFirmware(DeploymentEntity deploymentEntity) {
        if (deploymentEntity != null) {
            deploymentEntity.setManageFirmware(false);
            deploymentEntity.setUseDefaultCatalog(false);
            if (deploymentEntity.getFirmwareRepositoryEntity() != null) {
                FirmwareRepositoryEntity firmware = deploymentEntity.getFirmwareRepositoryEntity();
                firmware.removeDeployment(deploymentEntity);
                firmwareRepositoryDAO.saveOrUpdate(firmware);
            }
        }
    }

    public void manageServiceTemplateFirmware(ServiceTemplate serviceTemplate,
            ServiceTemplateEntity serviceTemplateEntity) {
        if (serviceTemplate != null && serviceTemplateEntity != null) {
            if (serviceTemplate.isManageFirmware()) {
                FirmwareRepositoryEntity firmware = null;
                if (serviceTemplate.isUseDefaultCatalog()) {
                    serviceTemplateEntity.setUseDefaultCatalog(true);
                    serviceTemplateEntity.setFirmwareRepositoryEntity(null);
                } else if (serviceTemplate.getFirmwareRepository() != null) {
                    firmware = firmwareRepositoryDAO
                            .getCompleteFirmware(serviceTemplate.getFirmwareRepository().getId(), true, true);
                    if (firmware != null) {
                        serviceTemplateEntity.setFirmwareRepositoryEntity(firmware);
                    }
                }
            } else {
                serviceTemplate.setUseDefaultCatalog(false);
                serviceTemplate.setFirmwareRepository(null);
                serviceTemplateEntity.setUseDefaultCatalog(false);
                serviceTemplateEntity.setFirmwareRepositoryEntity(null);
            }
        }
    }

    public boolean manageDeploymentFirmware(Deployment deployment, DeploymentEntity deploymentEntity) {
        boolean manageFirmware = false;
        if (deployment != null && deploymentEntity != null) {
            if (deployment.isUpdateServerFirmware()) {
                FirmwareRepositoryEntity firmwarerepoEntity = null;
                if (deployment.isUseDefaultCatalog()) {
                    deploymentEntity.setUseDefaultCatalog(true);
                    firmwarerepoEntity = this.getDefaultRepo();
                } else if (deployment.getFirmwareRepositoryId() != null) {
                    deploymentEntity.setUseDefaultCatalog(false);
                    firmwarerepoEntity = firmwareRepositoryDAO.get(deployment.getFirmwareRepositoryId());
                } else if (deployment.getFirmwareRepository() != null) {
                    deploymentEntity.setUseDefaultCatalog(false);
                    firmwarerepoEntity = firmwareRepositoryDAO.get(deployment.getFirmwareRepository().getId());
                }
                if (firmwarerepoEntity != null) {
                    manageFirmware = true;
                    firmwarerepoEntity.addDeployment(deploymentEntity);
                    deploymentEntity.setManageFirmware(true);
                    deployment.setFirmwareRepositoryId(firmwarerepoEntity.getId());
                    deployment.setFirmwareRepository(firmwarerepoEntity.getSimpleFirmwareRepository());
                    deploymentEntity.setFirmwareRepositoryEntity(firmwarerepoEntity);
                } else {
                    logger.error("Could not find specified firmware repository to manage deployment firmware: "
                            + deployment.getFirmwareRepositoryId());
                }
            } else {
                this.unmanageFirmware(deploymentEntity);
                deployment.setUseDefaultCatalog(false);
                deployment.setFirmwareRepository(null);
                deployment.setFirmwareRepositoryId(null);
            }
        }
        return manageFirmware;
    }

    /**
     * This method will clear the Software Inventory from a given Catalog for relevant Servers in a deployment and 
     * then update the Software Inventory with the correct catalog Inventory.
     *
     * @param deploymentEntity the deployment whose servers catalog software device inventory will be updated.
     * @throws AsmManagerCheckedException
     */
    public void updateCatalogBasedSoftwareDeviceInventory(DeploymentEntity deploymentEntity)
            throws AsmManagerCheckedException {
        if (deploymentEntity != null && deploymentEntity.getDeployedDevices() != null) {
            String jobId = UUID.randomUUID().toString();
            String parentJobId = UUID.randomUUID().toString();
            for (DeviceInventoryEntity deviceInvEnt : deploymentEntity.getDeployedDevices()) {
                if (deviceInvEnt.getDeviceType().isServer()) {
                    // Remove existing Inventory
                    this.deviceInventoryDAO.deleteFirmwareDeviceInventoryForDevice(deviceInvEnt.getRefId(),
                            SourceType.Catalog);

                    // Generate the new Inventory
                    Set<FirmwareDeviceInventory> catalogFirmwareInv = this
                            .getCatalogFirmwareDeviceInventory(deploymentEntity, deviceInvEnt, parentJobId);

                    if (catalogFirmwareInv != null) {
                        for (FirmwareDeviceInventory firmDevInv : catalogFirmwareInv) {
                            this.deviceInventoryDAO
                                    .createFirmwareDeviceInventory(new FirmwareDeviceInventoryEntity(firmDevInv));
                        }
                    }
                }
            }
        }
    }

    private Set<FirmwareDeviceInventory> getCatalogFirmwareDeviceInventory(DeploymentEntity deploymentEntity,
            DeviceInventoryEntity devInvEntity, String parentJobId) {

        Set<FirmwareDeviceInventory> currentFirmwareInventory = null;

        if (deploymentEntity != null && deploymentEntity.getFirmwareRepositoryEntity() != null
                && devInvEntity != null) {
            try {
                if (deploymentEntity != null) {
                    // For our deployment object, fetch our desired template settings
                    String marshalledTemplateData = deploymentEntity.getMarshalledTemplateData();
                    if (!StringUtils.isBlank(marshalledTemplateData)) {
                        ServiceTemplate serviceTemplate = MarshalUtil.unmarshal(ServiceTemplate.class,
                                marshalledTemplateData);

                        String osIpaddress = serviceTemplate.findEsxiOperatingSystemIp(devInvEntity.getRefId());

                        if (osIpaddress != null) {
                            ManagedDevice managedDevice = DeviceInventoryUtils.toDTO(devInvEntity, false, false);
                            Map<String, String> facts = PuppetModuleUtil
                                    .getPuppetDevice(PuppetModuleUtil.generateEsxiCertName(osIpaddress));

                            if (facts == null || facts.size() <= 0 || facts.get("installed_packages") == null
                                    || facts.get("installed_packages").isEmpty()) {
                                logger.error("Cannot get OS installed packages from puppet facts");
                            } else {
                                ArrayList<HashMap> deviceDetails = OBJECT_MAPPER
                                        .readValue(facts.get("installed_packages"), ArrayList.class);
                                PuppetIdracServerDevice puppetIdracServerDevice = PuppetDbUtil
                                        .convertToPuppetIdracServerDevice(deviceDetails);
                                puppetIdracServerDevice.setEsxVersion(facts.get("esx_version"));

                                currentFirmwareInventory = this.getFirmwareDeviceInventory(devInvEntity,
                                        puppetIdracServerDevice, parentJobId, parentJobId);
                            }
                        }
                    }
                }
            } catch (Exception e) {
                logger.warn("Unable to generate the catalog invnetory for device.", e);
            }
        }

        return currentFirmwareInventory;
    }

    /**
     * Creates or updates DeviceInventoryComplianceEntity for given DeviceInventoryEntity and FirmwareRepositoryEntity
     * 
     * @param firmwareRepository the repository to be checked against.
     * @param deviceInventory the device to be checked.
     */
    private void updateFirmwareComplianceMapForDevice(final FirmwareRepositoryEntity firmwareRepository,
            final DeviceInventoryEntity deviceInventory) {
        DeviceInventoryComplianceEntity deviceInventoryCompliance = new DeviceInventoryComplianceEntity(
                deviceInventory, firmwareRepository);
        // innocent until proven guilty: by default we assume device compliant
        deviceInventoryCompliance.setCompliance(CompliantState.COMPLIANT);
        // Loop through all firmware / software component versions and validate they are compliant
        Set<FirmwareDeviceInventoryEntity> firmDevInvs = this.deviceInventoryDAO
                .getFirmwareDeviceInventoryByRefId(deviceInventory.getRefId());
        for (final FirmwareDeviceInventoryEntity fdi : firmDevInvs) {
            // Check to see if it exists in the catalog, but not the server itself
            if (SourceType.Catalog.getValue().equals(fdi.getSource())) { // This means it does not exist on the server and only in the catalog
                deviceInventoryCompliance.setCompliance(CompliantState.NONCOMPLIANT);
                break;
            }
            // Check against the Default/Embedded for items that exist in the catalog & server
            final CompliantState compliance = getFirmwareCompliance(deviceInventory, firmwareRepository, fdi);
            // if the firmware is not compliant stop the checks and return
            if (!CompliantState.COMPLIANT.equals(compliance)) {
                deviceInventoryCompliance.setCompliance(compliance);
                break;
            }
        }
        deviceInventoryComplianceDAO.saveOrUpdate(deviceInventoryCompliance);
    }

    /**
     *  Looks up the compliance from the compliance_map table for the given service repo and moves it to the 
     *  device_inventory table so it can be used for service compliance (primarily for performance).
     *  
     *  @param deviceInventoryEnts the devices whose compliance will be updated.
     *  @param serviceRepo the Firmware catalog from which the device's compliance will be updated.
     */
    public void updateFirmwareComplianceForDevices(List<DeviceInventoryEntity> deviceInventoryEnts,
            FirmwareRepositoryEntity serviceRepo) {

        if (deviceInventoryEnts != null && !deviceInventoryEnts.isEmpty()) {
            FirmwareRepositoryEntity defaultRepo = this.getDefaultRepo();
            FirmwareRepositoryEntity embeddedRepo = this.getEmbeddedRepo();

            for (DeviceInventoryEntity deviceInventory : deviceInventoryEnts) {
                this.updateDeviceInventoryComplianceStatus(deviceInventory, defaultRepo, embeddedRepo, serviceRepo);
            }
        }
    }

    /**
     * Updates the Device's compliance status based on rules for the given repositories.
     * 
     * @param deviceInventory the device whose compliance status is being updated.
     * @param defaultRepo the default repository (if it exists)
     * @param embeddedRepo the embedded repository (should always exists, ships with ASM)
     * @param serviceRepo the service repository (if it exists)
     */
    private void updateDeviceInventoryComplianceStatus(final DeviceInventoryEntity deviceInventory,
            final FirmwareRepositoryEntity defaultRepo, final FirmwareRepositoryEntity embeddedRepo,
            final FirmwareRepositoryEntity serviceRepo) {

        DeviceInventoryComplianceEntity defaultComplianceAssoc = deviceInventoryComplianceDAO.get(deviceInventory,
                defaultRepo);
        DeviceInventoryComplianceEntity embeddedComplianceAssoc = deviceInventoryComplianceDAO.get(deviceInventory,
                embeddedRepo);
        DeviceInventoryComplianceEntity serviceComplianceAssoc = deviceInventoryComplianceDAO.get(deviceInventory,
                serviceRepo);

        CompliantState embeddedCompliance = null;
        CompliantState deviceCompliance = null;
        if (embeddedComplianceAssoc != null) {
            embeddedCompliance = embeddedComplianceAssoc.getCompliance();
        } else {
            // If no match is found in any catalog, this will ensure the state is set to uknown for any device.
            embeddedCompliance = CompliantState.UNKNOWN;
        }

        if (embeddedCompliance != CompliantState.UPDATEREQUIRED && defaultComplianceAssoc != null) {
            deviceCompliance = defaultComplianceAssoc.getCompliance();
        } else {
            if (embeddedCompliance != null) {
                deviceCompliance = embeddedCompliance;
            }
        }

        // When there is a service we will use it as the server should not be able to be selected for deployment
        // if it was not initially compliant
        if (serviceComplianceAssoc != null) {
            // Only a non-shared service can have a service compliance in the device_inventory table. This effectively
            // ensures that only a default, or embedded value is ever stored in the device_inventory table's compliance
            // for a shared device.
            if (!deviceInventory.getDeviceType().isSharedDevice()) {
                deviceCompliance = serviceComplianceAssoc.getCompliance();
            }
        }

        if (deviceCompliance != null) {
            try {
                deviceInventory.setCompliant(deviceCompliance.name());
                deviceInventory.setComplianceCheckDate(new GregorianCalendar());
                deviceInventoryDAO.updateDeviceInventory(deviceInventory);
            } catch (AsmManagerCheckedException e) {
                logger.error("Unable to update deviceInventoryDAO for " + deviceInventory.getIpAddress(), e);
            }
        } else {
            logger.error("Cannot get neither Default nor Embedded repo compliance status for device: "
                    + deviceInventory.getIpAddress());
        }
    }

    // Parses the puppetEsxVersion and converts it into the format necessary for the firmware
    private String getEsxiOperatingSystemRev(String puppetEsxVersion) {
        String operatingSystemRev = null;

        if (puppetEsxVersion != null) {
            String[] versionInfo = puppetEsxVersion.split("\\.");
            String majorVersion = "0";
            String minorVersion = "0";
            if (versionInfo.length >= 2) {
                if (versionInfo[0] != null && !versionInfo[0].trim().isEmpty()) {
                    majorVersion = versionInfo[0];
                }
                if (versionInfo[1] != null && !versionInfo[1].trim().isEmpty()) {
                    minorVersion = versionInfo[1];
                }
            }

            operatingSystemRev = "ESX" + majorVersion + "." + minorVersion;
        }

        return operatingSystemRev;
    }

    /**
     * Runs an inventory job and blocks until it completes.  Return true if it finishes or false if it timesout.
     * 
     * @param device the device to run and block on the inventory job.
     * @throws JobManagerException
     * @throws InterruptedException
     */
    public boolean runInventoryJobAndBlockUntilComplete(final DeviceInventoryEntity device)
            throws JobManagerException, InterruptedException {
        boolean successful = true;

        logger.debug("FirmwareUtil's runInventoryJobAndBlockUntilComplete starting for " + device.getRefId());
        final JobDetail updateInventoryJob = this.scheduleInventoryJobImmediately(device);
        final String updateInventoryJobName = updateInventoryJob.getKey().getName();
        JobStatus invJobStatus;
        final Calendar now = Calendar.getInstance();
        final Calendar endTime = Calendar.getInstance();
        endTime.add(Calendar.MINUTE, 5); // set the end time to 5 min from now
        do {
            Thread.sleep(20 * 1000); // every 20 seconds
            invJobStatus = JobStatus.valueOf(historyMgr.getExecHistoryStatus(updateInventoryJobName));
            now.setTime(new Date());
        } while ((JobStatus.STARTING.equals(invJobStatus) || JobStatus.IN_PROGRESS.equals(invJobStatus))
                && (endTime.after(now)));

        if (!JobStatus.SUCCESSFUL.equals(invJobStatus)) {
            // inventory job failed or timed out
            logger.error("Inventory job failed or timed out!");
            successful = false;
        }

        logger.info(
                "FirmwareUtil's runInventoryJobAndBlockUntilComplete is complete for device " + device.getRefId());
        return successful;
    }

    private String getHostNameOrIpAddress(ServiceTemplateComponent templateComponent) {
        String hostNameOrIpAddress = null;
        if (templateComponent != null) {
            ServiceTemplateCategory resource = getResource(templateComponent,
                    ServiceTemplateSettingIDs.SERVICE_TEMPLATE_SERVER_OS_RESOURCE);
            if (resource != null) {
                hostNameOrIpAddress = getHypervisorIp(templateComponent);
                if (StringUtils.isBlank(hostNameOrIpAddress)) {
                    ServiceTemplateSetting osHostnameIdSetting = resource
                            .getParameter(ServiceTemplateSettingIDs.SERVICE_TEMPLATE_SERVER_OS_HOSTNAME_ID);
                    if (osHostnameIdSetting != null && StringUtils.isNotBlank(osHostnameIdSetting.getValue())) {
                        hostNameOrIpAddress = osHostnameIdSetting.getValue();
                    }
                }
                if (StringUtils.isBlank(hostNameOrIpAddress)) {
                    logger.error("ESXI IP Address is not a valid IP or Hostname");
                    throw new LocalizedWebApplicationException(Response.Status.INTERNAL_SERVER_ERROR,
                            AsmManagerMessages.esxiHostNotFound());
                }
            }
        }

        return hostNameOrIpAddress;
    }

    /**
     * Note that the DeviceInventoryEntity passed in should already be tested to
     * verify it has an ESXI OS as only an ESXI based server will be connected
     * to a VCenter/Cluster.
     */
    private DeviceInventoryEntity findAssociatedVCenterDie(ServiceTemplate serviceTemplate,
            DeviceInventoryEntity die) {
        DeviceInventoryEntity vcenterDie = null;

        ServiceTemplateComponent component = serviceTemplate.findComponentByGUID(die.getRefId());
        if (component.getAssociatedComponents() != null) {
            Set<String> associatedComponentsKeys = component.getAssociatedComponents().keySet();
            for (ServiceTemplateComponent cmp : serviceTemplate.getComponents()) {
                if (cmp.getType() == ServiceTemplateComponentType.CLUSTER
                        && associatedComponentsKeys.contains(cmp.getId())) {
                    // Means we have a cluster associated...
                    vcenterDie = this.deviceInventoryDAO.getDeviceInventory(cmp.getAsmGUID());
                    break;
                }
            }
        }

        return vcenterDie;
    }

}