com.emc.storageos.systemservices.impl.licensing.LicenseManagerImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.emc.storageos.systemservices.impl.licensing.LicenseManagerImpl.java

Source

/*
 * Copyright (c) 2015 EMC Corporation
 * All Rights Reserved
 */

package com.emc.storageos.systemservices.impl.licensing;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigInteger;
import java.net.URI;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.emc.storageos.model.vpool.ManagedResourcesCapacity;
import com.emc.storageos.model.vpool.ManagedResourcesCapacity.ManagedResourceCapacity;
import com.emc.storageos.svcs.errorhandling.resources.APIException;
import com.emc.storageos.svcs.errorhandling.resources.InternalServerErrorException;

import org.joda.time.DateTime;
import org.joda.time.Days;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.emc.cams.elm.ELMFeatureDetail;
import com.emc.cams.elm.ELMLicenseProps;
import com.emc.cams.elm.ELMLicenseSource;
import com.emc.cams.elm.exception.ELMLicenseException;
import com.emc.storageos.coordinator.client.service.CoordinatorClient.LicenseType;
import com.emc.storageos.coordinator.client.service.LicenseInfo;
import com.emc.storageos.coordinator.client.model.Constants;
import com.emc.storageos.coordinator.common.Service;
import com.emc.storageos.systemservices.exceptions.CoordinatorClientException;
import com.emc.storageos.systemservices.exceptions.LocalRepositoryException;
import com.emc.storageos.systemservices.exceptions.SysClientException;
import com.emc.storageos.systemservices.impl.client.SysClientFactory;
import com.emc.storageos.systemservices.impl.client.SysClientFactory.SysClient;
import com.emc.storageos.systemservices.impl.eventhandler.connectemc.SendEventScheduler;
import com.emc.storageos.systemservices.impl.upgrade.CoordinatorClientExt;
import com.emc.vipr.model.sys.licensing.License;
import com.emc.vipr.model.sys.licensing.LicenseFeature;

import static com.emc.storageos.coordinator.client.service.LicenseInfo.*;

public class LicenseManagerImpl implements LicenseManager {

    private CoordinatorClientExt _coordinator;
    private SendEventScheduler _sendEventScheduler;
    private static final Logger _log = LoggerFactory.getLogger(LicenseManagerImpl.class);

    public static final int waitClusterStableInterval = 5000;
    public static final int waitRetryConvertLicneseInterval = 5000;

    public static final String LICENSETYPE_DELIMITER = ":";

    // used to ensure thread-safety of parsing license
    final Lock parseLicenseLock = new ReentrantLock();
    public final static int waitAcquireParseLicenseLock = 60; // 60 seconds

    /**
     * Configure the license information in properties file, disk and coordinator.
     * 
     * @param license
     * @throws LocalRepositoryException
     * @throws CoordinatorClientException
     */
    public void addLicense(License license)
            throws LocalRepositoryException, CoordinatorClientException, ELMLicenseException {

        if (getTargetInfoLock()) {
            try {
                // Step 1: Add the license test to disk in the .license file
                // Step 2: parse the .license file on disk in root directory using the ELMS API. This is required by the
                // ELMS API.
                License fullLicense = buildLicenseObjectFromText(license.getLicenseText());
                if (fullLicense != null) {
                    boolean isTrial = false;
                    for (LicenseFeature feature : fullLicense.getLicenseFeatures()) {
                        if (feature.getModelId().startsWith(LicenseConstants.VIPR_CONTROLLER)
                                && feature.isTrialLicense()) {
                            isTrial = true;
                        }
                    }
                    if (!isTrial) {
                        // Do not support the licenses of pre-yoda releases unless it is trial license.
                        for (LicenseFeature licenseFeature : fullLicense.getLicenseFeatures()) {
                            if (licenseFeature.getModelId().contains(LicenseFeature.OLD_LICENSE_SUBMODEL)) {
                                _log.info(
                                        "The license file contains a feature which is not supported any more. The license was not added to the system.");
                                throw APIException.badRequests.licenseIsNotValid(
                                        "The license file contains a feature which is not supported any more. The license was not added to the system.");
                            }
                        }
                    }
                }

                // Step 3: Add license features to coordinator service.
                updateCoordinatorWithLicenseFeatures(fullLicense, true);

                // Step 4: Add the raw license file to coordinator to keep a copy of the actual license file.
                updateCoordinatorWithLicenseText(license);

                // Step 5: Force the events to run
                _sendEventScheduler.run();
            } finally {
                releaseTargetVersionLock();
            }
        } else {
            _log.warn("Cannot acquire lock for adding license");
            throw APIException.serviceUnavailable.postLicenseBusy();
        }
    }

    /**
     * Check if it is a one-node deployment required by trial package in vipr 1.1
     * 
     * @return true if it is a 1+0 vipr deployment
     */
    public boolean isTrialPackage() {
        // check if it is 1+0 deployment of controller
        return (_coordinator.getNodeCount() == 1
                && Constants.CONTROL_NODE_SYSSVC_ID_PATTERN.matcher(_coordinator.getMySvcId()).matches());
    }

    /**
     * Check if the license is a trial license. In vipr 1.1, only ViPR_Controller license can be trial license.
     * 
     * @param license license object
     * @return true if the license object is a trial license
     * @throws ELMLicenseException
     */
    public boolean isTrialLicense(License license) throws ELMLicenseException {
        // parse the license text
        License fullLicense = buildLicenseObjectFromText(license.getLicenseText());
        if (fullLicense != null) {
            for (LicenseFeature feature : fullLicense.getLicenseFeatures()) {
                if (feature.getModelId().startsWith(LicenseConstants.VIPR_CONTROLLER) && feature.isTrialLicense()) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Returns a full license object complete with features.
     * 
     * @return
     */
    public License getLicense() throws Exception {
        LicenseInfoListExt licenseInfoList = getLicenseInfoListFromCoordinator();
        License license = new License();
        if (licenseInfoList != null) {
            // create a license object from the above license features.
            createLicenseObject(licenseInfoList.getLicenseList(), license);
        }
        // get the raw license text from coordinator.
        LicenseTextInfo licenseTextInfo = getLicenseTextFromCoordinator();

        // if text is found, add the raw license text to the License object.
        // if text is not found, put message in license text stating that the product
        // is not licensed.
        if (licenseTextInfo != null) {
            license.setLicenseText(licenseTextInfo.getLicenseText());
        } else {
            license.setLicenseText("The product is not licensed");
        }

        return license;
    }

    /**
     * Build a valid license object from the license string. If there is any
     * error while reading the license file, a license object will be created
     * with a licensed value of false.
     * 
     * @return license object if successful, otherwise null
     */
    protected License buildLicenseObjectFromText(String licenseText) throws ELMLicenseException {

        boolean bGetLock = false;
        try {
            bGetLock = parseLicenseLock.tryLock(waitAcquireParseLicenseLock, TimeUnit.SECONDS);
        } catch (Exception e) {
            _log.warn("Exception when adding license, msg: {}", e.getMessage());
            throw APIException.internalServerErrors.processLicenseError(
                    "failed getting lock to validate and parse license, error:" + e.getMessage());
        }

        if (bGetLock) {
            try {

                License license = new License();

                // Add the license test to disk in the .license file in root. This is required in order
                // to parse the license using the ELMS API.
                addLicenseToDiskLicenseFile(licenseText);

                ELMLicenseProps licProps = new ELMLicenseProps();
                licProps.setLicPath(LicenseConstants.LICENSE_FILE_PATH);
                ELMFeatureDetail[] featureDetails = null;

                ELMLicenseSource licSource = new ELMLicenseSource(licProps);
                featureDetails = licSource.getFeatureDetailList();

                LicenseFeature licenseFeature = null;
                for (ELMFeatureDetail featureDetail : featureDetails) {
                    // create a license feature object.
                    licenseFeature = new LicenseFeature();
                    if (!featureDetail.getFeatureName().equals(LicenseConstants.VIPR_CONTROLLER)) {
                        throw APIException.badRequests.licenseIsNotValid(
                                String.format("The license file contains a not supported feature: %s.",
                                        featureDetail.getFeatureName())
                                        + "Non controller license is no longer supported.");
                    }
                    if (featureDetail.getDaysUntilExp() > 0) {
                        licenseFeature.setLicensed(true);
                        licenseFeature.setVersion(featureDetail.getVersion());
                        licenseFeature.setIssuer(featureDetail.getIssuer());
                        licenseFeature.setNotice(featureDetail.getNotice());
                        licenseFeature.setDateExpires(convertCalendarToString(featureDetail.getExpDate()));
                        licenseFeature.setExpired(isExpired(licenseFeature.getDateExpires()));
                        licenseFeature.setDateIssued(convertCalendarToString(featureDetail.getIssuedDate()));

                        String subModelId = LicenseFeature.OLD_LICENSE_SUBMODEL;
                        Properties p = featureDetail.getVendorString(";");
                        if (p.size() > 0) {
                            for (Enumeration e = p.propertyNames(); e.hasMoreElements();) {
                                String str = (String) e.nextElement();
                                if (str.equals(LicenseConstants.LICENSE_TYPE_PROPERTYNAME)) {
                                    subModelId = p.getProperty(str);
                                    _log.info("Get a license increment with type: {}", subModelId);
                                    break;
                                }
                            }
                        }
                        licenseFeature.setModelId(
                                featureDetail.getFeatureName() + LicenseFeature.MODELID_DELIMETER + subModelId);
                        setVendorStringFields(featureDetail, licenseFeature, p);
                    } else {
                        _log.info(
                                "The license file contains a feature which is in an expired state. The license was not added to the system.");
                        throw APIException.badRequests.licenseIsNotValid(
                                "The license file contains a feature which is in an expired state. The license was not added to the system.");
                    }
                    license.addLicenseFeature(licenseFeature);
                }

                // delete /tmp/.license if it exists
                deleteCurrentLicenseFileOnDisk();

                _log.debug("Finished parsing of license");
                return license;
            } finally {
                parseLicenseLock.unlock();
            }
        } else {
            _log.warn("Cannot acquire lock. Another thread is holding the lock validating and parsing license");
            throw APIException.serviceUnavailable.postLicenseBusy();
        }
    }

    /**
     * Convert Calendar to MM/dd/yyyy format.
     * 
     * @param calendar
     * @return
     */
    private static String convertCalendarToString(Calendar calendar) {
        if (calendar != null) {
            SimpleDateFormat sdf = new SimpleDateFormat(LicenseConstants.MM_DD_YYYY_FORMAT);
            return sdf.format(calendar.getTime());
        } else {
            return null;
        }
    }

    /**
     * Verify if product is licensed for the specified feature.
     * 
     * @return boolean
     */
    public boolean isProductLicensed(LicenseType licenseType) {
        return _coordinator.isProductLicensed(licenseType);
    }

    /**
     * Get all license info (features) from coordinator.
     * 
     * @return LicenseInfoListExt which represent a list of license features
     * @throws Exception
     */
    public LicenseInfoListExt getLicenseInfoListFromCoordinator() {
        try {
            return _coordinator.getTargetInfo(LicenseInfoListExt.class);

        } catch (Exception e) {
            throw APIException.internalServerErrors.getObjectFromError("license info list", "coordinator", e);
        }
    }

    /**
     * Get license info for a specific license type from coordinator.
     * 
     * @return license info for the specified license type
     * @throws Exception
     */
    public LicenseInfoExt getLicenseInfoFromCoordinator(LicenseType licenseType) {
        try {
            LicenseInfoListExt licInfoList = _coordinator.getTargetInfo(LicenseInfoListExt.class);

            if (licInfoList != null) {
                for (LicenseInfoExt licenseInfo : licInfoList.getLicenseList()) {
                    if (licenseInfo.getLicenseType() == licenseType) {
                        return licenseInfo;
                    }
                }
            }
            return null;
        } catch (Exception e) {
            throw APIException.internalServerErrors.getObjectFromError(licenseType + " license info", "coordinator",
                    e);
        }
    }

    /**
     * Get raw license text in LicenseTextInfo from coordinator.
     * 
     * @return
     * @throws Exception
     */
    public LicenseTextInfo getLicenseTextFromCoordinator() throws Exception {
        return _coordinator.getTargetInfo(LicenseTextInfo.class);
    }

    /**
     * Write the contents of the license file to /tmp/.license file. This is required
     * by the ELMS API for parsing the license.
     * 
     * @param licenseText
     */
    private void addLicenseToDiskLicenseFile(String licenseText) {

        // check if file exists, if so, delete it first and create a new one.
        deleteCurrentLicenseFileOnDisk();
        // create a new file to hold the license text.
        // licenseFile.createNewFile();
        writeFile(LicenseConstants.LICENSE_FILE_PATH, licenseText);
    }

    /**
     * Write license file text to disk.
     * 
     * @param file licenseFile, String licenseText
     */
    private void writeFile(String file, String licenseText) {

        BufferedWriter writer = null;
        File licenseFile = new File(file);
        try {
            writer = new BufferedWriter(new FileWriter(licenseFile.getAbsoluteFile()));
            writer.write(licenseText);
            writer.close();
        } catch (IOException e) {
            _log.error("IO Exception while writing to .license file: {}", e);
            APIException.internalServerErrors.ioWriteError("/tmp/.license");
        } finally {
            try {
                writer.close();
            } catch (Exception e) {
                // do nothing.
            }
        }
    }

    /**
     * Deletes current version of license file in /tmp/.license
     * 
     * @return File
     */
    private void deleteCurrentLicenseFileOnDisk() {

        File licenseFile = new File(LicenseConstants.LICENSE_FILE_PATH);
        if (licenseFile.exists()) {
            licenseFile.delete();
        }
    }

    /**
     * Build the coordinator service version of the license features from the
     * License object.
     * 
     * @param license license to update
     * @param checkClusterUpgradable check if cluster is upgradable
     * @throws CoordinatorClientException
     */
    private void updateCoordinatorWithLicenseFeatures(License license, boolean checkClusterUpgradable)
            throws CoordinatorClientException {

        LicenseInfoListExt licenseList = null;
        List<LicenseInfoExt> licenseInfoList = new ArrayList<LicenseInfoExt>();
        for (LicenseFeature licenseFeature : license.getLicenseFeatures()) {
            LicenseType licenseType;
            if (licenseFeature.getModelId().startsWith(LicenseConstants.VIPR_CONTROLLER)) {
                licenseType = LicenseType.CONTROLLER;
            } else {
                throw APIException.internalServerErrors
                        .licenseInfoNotFoundForType("invalid license model id" + licenseFeature.getModelId());
            }

            LicenseInfoExt licenseInfo = new LicenseInfoExt();
            licenseInfo.setLicenseType(licenseType);
            licenseInfo.setExpirationDate(licenseFeature.getDateExpires());
            licenseInfo.setStorageCapacity(licenseFeature.getStorageCapacity());
            licenseInfo.setProductId(licenseFeature.getProductId());
            licenseInfo.setModelId(licenseFeature.getModelId());
            licenseInfo.setIssuedDate(licenseFeature.getDateIssued());
            licenseInfo.setLicenseTypeIndicator(licenseFeature.getLicenseIdIndicator());
            licenseInfo.setVersion(licenseFeature.getVersion());
            licenseInfo.setNotice(licenseFeature.getNotice());
            if (licenseFeature.isTrialLicense()) {
                licenseInfo.setTrialLicense(true);
            }
            licenseInfoList.add(licenseInfo);
        }
        if (!licenseInfoList.isEmpty()) {
            licenseList = new LicenseInfoListExt(licenseInfoList);
            _coordinator.setTargetInfo(licenseList, checkClusterUpgradable);
        }
    }

    /**
     * Update Coordinator Service with the customers actual raw license file text.
     * 
     * @param license
     * @throws CoordinatorClientException
     */
    public void updateCoordinatorWithLicenseText(License license) throws CoordinatorClientException {

        LicenseTextInfo licenseTextInfo = new LicenseTextInfo();
        licenseTextInfo.setLicenseText(license.getLicenseText());
        _coordinator.setTargetInfo(licenseTextInfo);
    }

    /**
     * Update Coordinator Service with license information.
     * 
     * @throws CoordinatorClientException
     */
    public void updateCoordinatorWithLicenseInfo(LicenseInfoExt licenseInfo) throws CoordinatorClientException {

        LicenseInfoListExt licenseList = getLicenseInfoListFromCoordinator();
        if (licenseList != null) {
            licenseList.updateLicense(licenseInfo);
            _coordinator.setTargetInfo(licenseList, TARGET_PROPERTY_ID, LicenseInfo.LICENSE_INFO_TARGET_PROPERTY);
        }
    }

    /**
     * Verify if the license has expired.
     * 
     * @param licenseInfo
     * @return
     */
    public boolean isLicenseExpired(LicenseInfoExt licenseInfo) {

        LicenseFeature licenseFeature = createLicenseFeatureFromLicenseInfoExt(licenseInfo);
        if (licenseFeature != null) {
            return licenseFeature.isExpired();
        }

        return false;
    }

    /**
     * Verify if storage capacity currently used has exceeded the licensed capacity from the license file.
     * 
     * @param licenseInfo
     * @return true if capacity is exceeded
     */
    public boolean isCapacityExceeded(LicenseInfoExt licenseInfo) {
        double currentCapacityUsed = 0;
        double licenseCapacity = Double.parseDouble(licenseInfo.getStorageCapacity());
        try {
            // if (licenseInfo.getModelId().equalsIgnoreCase(LicenseConstants.getModelId(LicenseType.CONTROLLER)))
            if (licenseInfo.getLicenseType().equals(LicenseType.CONTROLLER)) {
                // Get capacity from apisvc
                currentCapacityUsed = getTotalControllerCapacity();
            } else {
                return false;
            }
            Object[] args = new Object[] { licenseInfo.getModelId(), currentCapacityUsed, licenseCapacity };
            _log.info("Capacity currently used by {}: {}, licensed capacity: {}", args);
            return currentCapacityUsed > licenseCapacity;
        } catch (Exception e) {
            _log.warn("Internal server error occurred while getting capacity: {}", e);
        }
        return false;
    }

    /**
     * Update License object with data license information from coordinator service.
     * 
     * @param licenseInfoExts licenseInfoExts, License license
     * @return
     */
    private void createLicenseObject(List<LicenseInfoExt> licenseInfoExts, License license) {

        if (licenseInfoExts.isEmpty()) {
            return;
        }

        for (LicenseInfoExt licenseExt : licenseInfoExts) {
            LicenseFeature licenseFeature = createLicenseFeatureFromLicenseInfoExt(licenseExt);
            license.addLicenseFeature(licenseFeature);
        }
    }

    /**
     * Create a LicenseFeature object from a LicenseInfoExt from coordinator service.
     * 
     * @return LicenseFeature
     */
    private LicenseFeature createLicenseFeatureFromLicenseInfoExt(LicenseInfoExt licenseInfo) {

        if (licenseInfo == null) {
            return null;
        }

        LicenseFeature licenseFeature = new LicenseFeature();
        licenseFeature.setDateExpires(licenseInfo.getExpirationDate());
        licenseFeature.setExpired(isExpired(licenseFeature.getDateExpires()));
        licenseFeature.setStorageCapacity(licenseInfo.getStorageCapacity());
        licenseFeature.setProductId(licenseInfo.getProductId());
        licenseFeature.setSerial(licenseInfo.getProductId());
        licenseFeature.setModelId(licenseInfo.getModelId());
        licenseFeature.setDateIssued(licenseInfo.getIssuedDate());
        licenseFeature.setLicenseIdIndicator(licenseInfo.getLicenseTypeIndicator());
        licenseFeature.setVersion(licenseInfo.getVersion());
        licenseFeature.setNotice(licenseInfo.getNotice());
        licenseFeature.setTrialLicense(licenseInfo.isTrialLicense());
        licenseFeature.setLicensed(true);
        return licenseFeature;
    }

    /**
     * Parse the license text manually to get the model id's and appropriate vendor string.
     * This must be done manually because retrieving this via the ELMS API does not work
     * 
     * @param licenseText
     * @return Map
     */
    private Map<String, String> parseLicenseVendorStrings(String licenseText) {

        Pattern pattern = Pattern.compile(LicenseConstants.LICENSE_PATTERN, Pattern.DOTALL);
        Map<String, String> featureMap = new HashMap<String, String>();
        // get a String array of the license features.
        String[] licenseFeatures = licenseText.split(LicenseConstants.LICENSE_FEATRES_DELIM);
        // iterate through the license features, building the vendor string map.
        for (String licenseFeature : licenseFeatures) {
            if (licenseFeature == null || licenseFeature.isEmpty()) {
                continue;
            }
            Matcher matcher = pattern.matcher(licenseFeature.trim());
            if (matcher.find()) {
                featureMap.put(matcher.group(1), matcher.group(2));
            }
        }
        return featureMap;
    }

    /**
     * Set the license feature with the appropriate data from model number's
     * vendor string.
     */
    private void setVendorStringFields(ELMFeatureDetail featureDetail, LicenseFeature licenseFeature,
            Properties vendorProps) {

        if (vendorProps.getProperty(LicenseConstants.STORAGE_CAPACITY) != null) {
            licenseFeature.setStorageCapacity(computeLicensedAmount(vendorProps));
        }

        // If SWID is returned in the vendor string, we will use that as the Serial number,
        // otherwise, use SN. Set the LicenseIndicator so that the SYR knows if we're using a SWID or LAC.
        String swidValue = vendorProps.getProperty(LicenseConstants.SWID_VALUE);
        if (swidValue == null || swidValue.startsWith("ERR")) {
            licenseFeature.setSerial(featureDetail.getSN());
            licenseFeature.setProductId(featureDetail.getSN());
            licenseFeature.setLicenseIdIndicator(LicenseConstants.LAC);
        } else {
            licenseFeature.setSerial(swidValue);
            licenseFeature.setProductId(swidValue);
            licenseFeature.setLicenseIdIndicator(LicenseConstants.SWID);
        }
        // if it is a trial license
        String trialLicenseStr = vendorProps.getProperty(LicenseConstants.TRIAL_LICENSE_NAME);
        if (trialLicenseStr != null) {
            for (String value : LicenseConstants.TRIAL_LICENSE_VALUE) {
                if (trialLicenseStr.equals(value)) {
                    licenseFeature.setTrialLicense(true);
                    _log.info("License {} is trial license", featureDetail.getFeatureName());
                    break;
                }
            }
        }
    }

    /**
     * Splits the vendor string from license into individual entries in a map.
     */
    private Map<String, String> splitVendorString(String vendorString) {
        Map<String, String> vendorMap = new HashMap<String, String>();
        String[] vendorStringArray = vendorString.split(";");
        for (int x = 0; x < vendorStringArray.length; x++) {
            String[] vendorEntry = vendorStringArray[x].split("=");
            vendorMap.put(vendorEntry[0], vendorEntry[1]);
        }

        return vendorMap;
    }

    /**
     * Gets capacity from controller.
     * List of returned resources include volume, file and free storage pool capacities.
     */
    public ManagedResourcesCapacity getControllerCapacity() throws InternalServerErrorException {
        _log.info("Getting controller capacity");
        List<Service> services = _coordinator.locateAllServices(LicenseConstants.API_SVC_LOOKUP_KEY,
                LicenseConstants.SERVICE_LOOKUP_VERSION, null, null);
        for (Service service : services) {
            try {
                // service could be null, if so get next service.
                if (service != null) {
                    return getClient(service).get(SysClientFactory._URI_PROVISIONING_MANAGED_CAPACITY,
                            ManagedResourcesCapacity.class, null);
                }
            } catch (SysClientException exception) {
                _log.error("LicenseManager::getCapacity for Controller. Cannot connect to host: {}",
                        service.getEndpoint().toString());
            }
        }
        // if capacity cannot be retrieved
        _log.error("Controller capacity could not be retrieved");
        throw APIException.internalServerErrors.getObjectError("controller capacity", null);
    }

    /**
     * Gets controller total managed capacity.
     * sum of volume managed + file managed + free managed storage pool
     * This is mainly used to check capacity against licensed capacity amount.
     */
    private double getTotalControllerCapacity() throws InternalServerErrorException {
        _log.info("Getting controller total capacity");
        ManagedResourcesCapacity resourceCapacities = getControllerCapacity();
        double total = 0;
        for (ManagedResourceCapacity cap : resourceCapacities.getResourceCapacityList()) {
            _log.debug("{} capacity is {}", cap.getType(), cap.getResourceCapacity());
            total += cap.getResourceCapacity();
        }
        _log.info("Controller total capacity is {}", total);
        return total;
    }

    /**
     * Get a instance of the SysClient for the base url.
     * 
     * @return
     */
    private SysClient getClient(Service service) {
        URI hostUri = null;
        hostUri = service.getEndpoint();
        String baseNodeURL = String.format(LicenseConstants.BASE_URL_FORMAT, hostUri.getHost(), hostUri.getPort());
        _log.info("Calling URI: " + baseNodeURL);
        return SysClientFactory.getSysClient(URI.create(baseNodeURL));
    }

    /**
     * Compute the licensed amount by computing storage capacity times the
     * storage capacity unit.
     * 
     * @param vendorProps
     * @return
     */
    private String computeLicensedAmount(Properties vendorProps) {

        String units = vendorProps.getProperty(LicenseConstants.STORAGE_CAPACITY_UNITS);
        if (units == null) {
            return null;
        }

        BigInteger computedLicensedCapacity = null;
        // Currently, only TERABYTE is supported in the license.
        if (units.equalsIgnoreCase(LicenseConstants.TERABYTE)) {
            BigInteger licensedCapacity = new BigInteger(
                    vendorProps.getProperty(LicenseConstants.STORAGE_CAPACITY));
            computedLicensedCapacity = licensedCapacity.multiply(LicenseConstants.TB_VALUE);
        }

        // If for some reason we cannot compute the capacity for the capacity values in the license
        // (CAPACITY * CAPACITY_UNIT), return 0.
        if (computedLicensedCapacity != null) {
            return computedLicensedCapacity.toString();
        } else {
            return "0";
        }
    }

    protected static boolean isExpired(String expiryDateString) {
        // if date expires is empty, check to see if the product is licensed. If
        // it is licensed and the _dateExpires is false, the license has expired.
        // This is because the ELM software does not set the date expires when
        // it notices a license is invalid. This license must have expired
        // sometime between server restarts. The product is technically
        // licenses..but in an expired state.
        if (expiryDateString == null) {
            return false;
        }

        if (!expiryDateString.equalsIgnoreCase(PERMANENT_LICENSE)) {
            SimpleDateFormat sdf = new SimpleDateFormat(EXPIRE_DATE_FORMAT);
            Date expireDate = null;
            try {
                expireDate = sdf.parse(expiryDateString);
                Date today = Calendar.getInstance().getTime();
                int days = Days.daysBetween(new DateTime(expireDate), new DateTime(today)).getDays();
                if (days > 0) {
                    return true;
                }
            } catch (ParseException e) {
                _log.error("Parse Exception in License::isExpired() : " + e.getMessage());
            }
        }

        return false;
    }

    /**
     * Get a target info lock from coordinator.
     * 
     * @return
     */
    public boolean getTargetInfoLock() {
        return _coordinator.getTargetInfoLock();
    }

    /**
     * calls coordinator client to release a target version lock.
     */
    public void releaseTargetVersionLock() {
        _coordinator.releaseTargetVersionLock();
    }

    /**
     * Set the CoordinatorClientExt
     * 
     * @param coordinatorClientExt
     */
    public void setCoordinatorClientExt(CoordinatorClientExt coordinatorClientExt) {
        _coordinator = coordinatorClientExt;
    }

    /**
     * Set the events scheduler.
     * 
     * @param scheduler
     */
    @Autowired
    public void setSendEventScheduler(SendEventScheduler scheduler) {
        _sendEventScheduler = scheduler;
    }
}