/* * Copyright 2008 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.rioproject.monitor.service; import net.jini.id.Uuid; import org.rioproject.associations.AssociationDescriptor; import org.rioproject.associations.AssociationType; import org.rioproject.deploy.*; import org.rioproject.impl.servicebean.ServiceElementUtil; import org.rioproject.monitor.service.util.LoggingUtil; import org.rioproject.opstring.ServiceElement; import org.rioproject.sla.ServiceLevelAgreements; import org.rioproject.system.MeasuredResource; import org.rioproject.system.ResourceCapability; import org.rioproject.system.capability.PlatformCapability; import org.rioproject.system.capability.connectivity.TCPConnectivity; import org.rioproject.system.capability.platform.OperatingSystem; import org.rioproject.system.capability.platform.ProcessorArchitecture; import org.rioproject.system.capability.platform.StorageCapability; import org.rioproject.watch.ThresholdValues; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.rmi.MarshalledObject; import java.rmi.RemoteException; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; /** * An {@code InstantiatorResource} is the object being leased and controlled by the * {@code ServiceResource}, and represents an available {@link ServiceBeanInstantiator} service * that can be used to instantiate a service. * * @see org.rioproject.servicebean.ServiceBean * @see org.rioproject.deploy.ServiceBeanInstantiator * * @author Dennis Reedy */ public class InstantiatorResource { /** * The ServiceBeanInstantiator */ private final ServiceBeanInstantiator instantiator; /** * The ServiceBeanInstantiator wrapped in a MarshalledObject */ private final MarshalledObject<ServiceBeanInstantiator> wrappedServiceBeanInstantiator; /** * The maximum number of services the ServiceBeanInstantiator can instantiate */ private final AtomicInteger serviceLimit = new AtomicInteger(0); /** * An in-process counter indicating the InstantiatorResource is being used to * provision a service */ private final AtomicInteger inProcessCounter = new AtomicInteger(); /** * The handback option provided by the ServiceBeanInstantiator and sent back * to the ServiceBeanInstantiator as part of the ProvisionEvent */ private final MarshalledObject handback; /** * A Copy of the ResourceCapability object from the ServiceBeanInstantiator */ private ResourceCapability resourceCapability; private final Object resourceCapabilityLock = new Object(); /** * Whether the instantiator is ready to accept requests for the * instantiation of dynamic services */ private boolean dynamicEnabled = false; /** * Table of ServiceElement instances and how many the InstantiatorResource * has instantiated */ private final Map<ServiceElement, List<DeployedService>> serviceElementMap = new HashMap<ServiceElement, List<DeployedService>>(); /** Table of in process ServiceElement instances */ private final Map<ServiceElement, Integer> inProcessMap = new HashMap<ServiceElement, Integer>(); /** * Name of the ServiceBeanInstantiator */ private final String instantiatorName; /** * The Uuid that has been assigned to the ServiceBeanInstantiator */ private final Uuid instantiatorUuid; /** The Logger */ static final Logger logger = LoggerFactory.getLogger(InstantiatorResource.class); /** * Create an InstantiatorResource * * @param wrappedServiceBeanInstantiator The ServiceBeanInstantiator wrapped in a MarshalledObject * @param instantiator A ServiceBeanInstantiator * @param instantiatorName Name for the ServiceBeanInstantiator * @param instantiatorUuid The Uuid that has been assigned to the * ServiceBeanInstantiator, may be null * @param handback The handback object the ServiceBeanInstantiator has * provided, may be null * @param resourceCapability The ResourceCapability object for the * ServiceBeanInstantiator * @param serviceLimit The total number of services the ServiceBeanInstantiator * will allocate */ public InstantiatorResource(MarshalledObject<ServiceBeanInstantiator> wrappedServiceBeanInstantiator, ServiceBeanInstantiator instantiator, String instantiatorName, Uuid instantiatorUuid, MarshalledObject handback, ResourceCapability resourceCapability, int serviceLimit) { this.wrappedServiceBeanInstantiator = wrappedServiceBeanInstantiator; this.instantiator = instantiator; this.instantiatorName = instantiatorName; this.instantiatorUuid = instantiatorUuid; this.handback = handback; this.resourceCapability = resourceCapability; this.serviceLimit.set(serviceLimit); } public MarshalledObject<ServiceBeanInstantiator> getWrappedServiceBeanInstantiator() { return wrappedServiceBeanInstantiator; } /** * Add a DeployedService instance to the serviceElementMap. * * @param newDeployedService The service to add */ public void addDeployedService(DeployedService newDeployedService) { ServiceElement sElem = newDeployedService.getServiceElement(); synchronized(serviceElementMap) { if(serviceElementMap.containsKey(sElem)) { List<DeployedService> list = serviceElementMap.get(sElem); if(!list.contains(newDeployedService)) { list.add(newDeployedService); serviceElementMap.put(sElem, list); } } else { List<DeployedService> list = new ArrayList<DeployedService>(); list.add(newDeployedService); serviceElementMap.put(sElem, list); } } } /** * Set the DeployedService instances * * @param deployedServices List of active & deployed services */ void setDeployedServices(List<DeployedService> deployedServices) { synchronized(serviceElementMap) { serviceElementMap.clear(); for(DeployedService deployedService : deployedServices) { ServiceElement sElem = deployedService.getServiceElement(); if (serviceElementMap.containsKey(sElem)) { List<DeployedService> list = serviceElementMap.get(sElem); if(!list.contains(deployedService)) { list.add(deployedService); serviceElementMap.put(sElem, list); } } else { List<DeployedService> list = new ArrayList<DeployedService>(); list.add(deployedService); serviceElementMap.put(sElem, list); } } } } /** * Get the name of the ServiceBeanInstantiator * * @return The name of the ServiceBeanInstantiator */ public String getName() { return(instantiatorName); } /** * Get the Uuid that has been assigned to the ServiceBeanInstantiator * * @return The Uuid for the ServiceBeanInstantiator */ public Uuid getInstantiatorUuid() { return(instantiatorUuid); } /** * Get the ServiceBeanInstantiator * * @return The ServiceBeanInstantiator */ ServiceBeanInstantiator getServiceBeanInstantiator() { return instantiator; } /** * Get the active ServiceRecord instances for this InstantiatorResource * * @return Array of active ServiceRecord instances for this * InstantiatorResource * * @throws RemoteException If the active ServiceReecords cannot be obtained */ ServiceRecord[] getActiveServiceRecords() throws RemoteException { ServiceRecord[] records = null; /* * Addresses an observed anomaly where for some reason we could not * communicate back to the Cybernode, the connection was reset. The * strategy here is to retry 3 times, waiting 1 second between retries * to attempt to get the active ServiceRecord instances */ int RETRY = 3; RemoteException toThrow = null; for(int i=0; i< RETRY; i++) { try { records = getInstantiator().getServiceRecords(ServiceRecord.ACTIVE_SERVICE_RECORD); break; } catch(RemoteException e) { logger.warn("Exception [{}] occurred, retry [{}] ....", e.getClass().getName(), i); toThrow = e; try { Thread.sleep(1000); } catch (InterruptedException ignore) { logger.trace("Timeout Interrupted, handled"); } } } if(toThrow!=null) throw toThrow; return(records); } /** * Get the ServiceRecord instances for a ServiceElement on this * InstantiatorResource * * @param elem The ServiceElement * * @return Array of ServiceRecord instances for a ServiceElement on this * InstantiatorResource. If there are no ServiceRecords, return a * zero-length array * * @throws RemoteException If the ServiceReecords cannot be obtained */ ServiceRecord[] getServiceRecords(ServiceElement elem) throws RemoteException { ServiceStatement statement = null; /* * Addresses an observed anomaly where for some reason we could not * communicate back to the Cybernode, the connection was reset. The * strategy here is to retry 3 times, waiting 1 second between retries * to attempt to get the active ServiceRecord instances */ int RETRY = 3; RemoteException toThrow = null; for(int i=0; i< RETRY; i++) { try { statement = getInstantiator().getServiceStatement(elem); break; } catch(RemoteException e) { logger.warn("Exception [{}] occurred, retry [{}] ....", e.getClass().getName(), i); toThrow = e; try { Thread.sleep(1000); } catch (InterruptedException ignore) { logger.trace("Timeout Interrupted, handled"); } } } if(toThrow!=null) throw toThrow; return(statement==null? new ServiceRecord[0] : statement.getServiceRecords()); } /** * Determine if the instance is found on this InstantiatorResource * * @param sElem The ServiceElement instance * @param uuid The id of the instance * * @return true if the instance is found on this InstantiatorResource */ boolean hasServiceElementInstance(ServiceElement sElem, Uuid uuid) { boolean found = false; synchronized(serviceElementMap) { if(serviceElementMap.containsKey(sElem)) { List<DeployedService> list = serviceElementMap.get(sElem); DeployedService[] ids = list.toArray(new DeployedService[list.size()]); for (DeployedService deployedService : ids) { if (deployedService.getServiceBeanInstance().getServiceBeanID().equals(uuid)) { found = true; break; } } } } return(found); } /** * Remove (decrement) a ServiceElement instance in the serviceElementMap. * If the ServiceElement exists in the table decrease it's instance counter * by one. If the ServiceElement instances counter is decremented to zero, * remove the ServiceElement from the serviceElementMap * * @param sElem The ServiceElement instance to decrease * @param uuid The id of the instance to remove * * @return The removed ServiceBeanInstance, or null if the instance was removed */ ServiceBeanInstance removeServiceElementInstance(ServiceElement sElem, Uuid uuid) { ServiceBeanInstance removedInstance = null; synchronized(serviceElementMap) { if(serviceElementMap.containsKey(sElem)) { List<DeployedService> list = serviceElementMap.get(sElem); DeployedService[] ids = list.toArray(new DeployedService[list.size()]); for (DeployedService deployedService : ids) { if (deployedService.getServiceBeanInstance().getServiceBeanID().equals(uuid)) { list.remove(deployedService); removedInstance = deployedService.getServiceBeanInstance(); break; } } if(list.isEmpty()) { serviceElementMap.remove(sElem); } else { serviceElementMap.put(sElem, list); } } } return removedInstance; } ServiceElement[] getServiceElements() { ServiceElement[] elems; synchronized(serviceElementMap) { elems = new ServiceElement[serviceElementMap.size()]; int i=0; for (Map.Entry<ServiceElement, List<DeployedService>> entry : serviceElementMap.entrySet()) { elems[i++] = entry.getKey(); } } return(elems); } /** * Get the number of ServiceElement instances * * @param sElem The ServiceElement to count * * @return The number of instances of the ServiceElement the * ServiceBeanInstantiator has instantiated. If not found return 0 */ public int getServiceElementCount(ServiceElement sElem) { int numInstances = 0; synchronized(serviceElementMap) { if(serviceElementMap.containsKey(sElem)) { List<DeployedService> list = serviceElementMap.get(sElem); numInstances = list.size(); } logger.trace("Get service element count for [{}], {} has {} instances", LoggingUtil.getLoggingName(sElem), getName(), numInstances); } return (numInstances); } /** * Get the total number of all ServiceElement instances * * @return The total number of ServiceElement instances the * ServiceBeanInstantiator has instantiated */ public int getServiceElementCount() { int totalInstances = 0; synchronized(serviceElementMap) { Set<ServiceElement> keys = serviceElementMap.keySet(); for (ServiceElement key : keys) { List<DeployedService> list = serviceElementMap.get(key); totalInstances += list.size(); } } return totalInstances; } /** * Get the ServiceBeanInstantiator * * @return The Instantiator */ public ServiceBeanInstantiator getInstantiator() { return (instantiator); } /** * Get the number of active and in-process services * * @return The number of active and in-process services. */ public int getServiceCount() { int count = getServiceElementCount(); int inProcess = getInProcessCounter(); return count + inProcess; } /** * Get the handback object * * @return The handback object */ public MarshalledObject getHandback() { return (handback); } /** * Get the ServiceDeployment for a ServiceBeanInstance * * @param sElem The ServiceElement, used as the key to the serviceElementMap * @param instance The ServiceBeanInstance to locate * @return The ServiceDeployment for a ServiceBeanInstance, or if not * found return a null */ DeployedService getServiceDeployment(ServiceElement sElem, ServiceBeanInstance instance) { DeployedService deployedService = null; synchronized(serviceElementMap) { if(serviceElementMap.containsKey(sElem)) { List<DeployedService> list = serviceElementMap.get(sElem); DeployedService[] services = list.toArray(new DeployedService[list.size()]); for (DeployedService service : services) { if (service.getServiceBeanInstance().equals(instance)) { deployedService = service; break; } } } } return deployedService; } /** * Get the ResourceCapability object * * @return The ResourceCapability of the ServiceBeanInstantiator */ public ResourceCapability getResourceCapability() { ResourceCapability rCap; synchronized(resourceCapabilityLock) { rCap = resourceCapability; } return (rCap); } /** * Set the ResourceCapability * * @param resourceCapability The ResourceCapability object of the * ServiceBeanInstantiator */ void setResourceCapability(ResourceCapability resourceCapability) { synchronized(resourceCapabilityLock) { this.resourceCapability = resourceCapability; } } /** * Set the serviceLimit property * * @param serviceLimit The maximum number of services the ServiceBeanInstantiator can instantiate */ void setServiceLimit(int serviceLimit) { this.serviceLimit.set(serviceLimit); } /** * Get the serviceLimit property * * @return The maximum number of services the ServiceBeanInstantiator can instantiate */ public int getServiceLimit() { return serviceLimit.get(); } /** * Increment the inprocess counter * * @param sElem The ServiceElement to add */ public void incrementProvisionCounter(ServiceElement sElem) { inProcessCounter.incrementAndGet(); synchronized(inProcessMap) { if(inProcessMap.containsKey(sElem)) { int i = inProcessMap.get(sElem); i++; inProcessMap.put(sElem, i); } else { inProcessMap.put(sElem, 1); } } } /** * Decrement the inprocess counter * * @param sElem The ServiceElement to remove */ public synchronized void decrementProvisionCounter(ServiceElement sElem) { if(inProcessCounter.get()>0) { inProcessCounter.decrementAndGet(); } synchronized(inProcessMap) { if(inProcessMap.containsKey(sElem)) { int i = inProcessMap.get(sElem); i--; if(i==0) inProcessMap.remove(sElem); else inProcessMap.put(sElem, i); } } } /** * Get the in-process counter value * * @return The in-process counter value */ public int getInProcessCounter() { return inProcessCounter.get(); } /** * Get the inprocess counter value for a ServiceElement * * @param sElem The ServiceElement to use * * @return The inprocess counter value for a ServiceElement */ public int getInProcessCounter(ServiceElement sElem) { int count = 0; synchronized(inProcessMap) { if(inProcessMap.containsKey(sElem)) { count = inProcessMap.get(sElem); } } return(count); } /** * Get all in process elements, excluding the element passed in * * @param exclude The ServiceElement to exclude * * @return An array of ServiceElements */ ServiceElement[] getServiceElementsInprocess(ServiceElement exclude) { ArrayList<ServiceElement> list = new ArrayList<ServiceElement>(); synchronized(inProcessMap) { Set<ServiceElement> keys = inProcessMap.keySet(); for (ServiceElement element : keys) { if (!element.equals(exclude)) list.add(element); } } return (list.toArray(new ServiceElement[list.size()])); } /** * Get the host address of the ServiceBeanInstantiator * * @return The host address of the ServiceBeanInstantiator */ public String getHostAddress() { return (resourceCapability.getAddress()); } /** * Get the host name of the ServiceBeanInstantiator * * @return The host name of the ServiceBeanInstantiator */ public String getHostName() { return (resourceCapability.getHostName()); } /** * Set the dynamicEnabled attribute to <code>true</code> indicating that * the ServiceBeanInstantiator is available for the provisioning of * ServiceBean objects which have a provisioning type of <i>dynamic </i> */ public void setDynamicEnabledOn() { dynamicEnabled = true; } /** * Get the dynamicEnabled property * * @return <code>true</code> if the ServiceBeanInstantiator is available * for the provisioning of ServiceBean objects which have a provisioning * type of <i>dynamic </i>, otherwise return <code>false</code> */ public boolean getDynamicEnabled() { return (dynamicEnabled); } /** * Determine if the provided {@code ProvisionRequest} can be instantiated on the * compute resource represented by this {@code InstantiatorResource}. If it is * determined that there are downloadable {@code PlatformCapability} components * which can meet the platform requirements the service has declared, these components will be * verified, and the targeted {@code InstantiatorResource} checked to ensure adequate disk space is available. * * @param provisionRequest The {@code ProvisionRequest} * @return Return true if the {@code InstantiatorResource} supports the * operational requirements of the {@code ProvisionRequest} * @throws ProvisionException If there are errors obtaining available disk space. Note this will only * happen if the {@code ProvisionRequest} contains downloadable {@code PlatformCapability} components and there * is a problem obtaining the size of the download. */ public boolean canProvision(final ProvisionRequest provisionRequest) throws ProvisionException { ServiceElement sElem = provisionRequest.getServiceElement(); if(sElem.getPlanned()==0) return(false); String provType = sElem.getProvisionType().toString(); /* * Check if the serviceLimit has been reached */ if(getServiceElementCount() == serviceLimit.get() && !provType.equals(ServiceElement.ProvisionType.FIXED.toString())) { String failureReason = String.format("%s not selected to allocate service [%s], it has reached it's service limit of [%d]", getName(), LoggingUtil.getLoggingName(sElem), serviceLimit.get()); provisionRequest.addFailureReason(failureReason); logger.debug(failureReason); return(false); } /* * Check if the maximum amount per machine has been reached */ if(sElem.getMaxPerMachine()!=-1) { int serviceCount = getServiceElementCount(sElem); int inProcessCount = getInProcessCounter(sElem); int numInstances = serviceCount+inProcessCount; if(numInstances >= sElem.getMaxPerMachine()) { String failureReason = String.format("%s not selected to allocate service [%s], declaration specifies no more than %d services per machine, found %d", getName(), LoggingUtil.getLoggingName(sElem), sElem.getMaxPerMachine(), numInstances); provisionRequest.addFailureReason(failureReason); logger.debug(failureReason); return(false); } } /* * Fixed service allocation is similar to maxPerMachine, ensure that * there are not too many service allocated */ if(sElem.getProvisionType() == ServiceElement.ProvisionType.FIXED) { int planned = sElem.getPlanned(); int actual = getServiceElementCount(sElem); int numAllowed = planned-actual; if(numAllowed <=0) { String failureReason = String.format("Do not allocate %s service [%s] to %s has [%d] instance(s), planned [%d]", provType, LoggingUtil.getLoggingName(sElem), getName(), actual, planned); provisionRequest.addFailureReason(failureReason); logger.debug(failureReason); return(false); } else { String failureReason = String.format("%s has [%d] instance(s), planned [%d] of %s service [%s]", getName(), actual, planned, provType, LoggingUtil.getLoggingName(sElem)); provisionRequest.addFailureReason(failureReason); logger.debug(failureReason); } } if(!AssociationMatcher.meetsColocationRequirements(sElem, this)) { StringBuilder b = new StringBuilder(); b.append(getName()).append(" not selected to allocate ").append(LoggingUtil.getLoggingName(sElem)); b.append(", required colocated services not present: "); AssociationDescriptor[] aDesc = ServiceElementUtil.getAssociationDescriptors(sElem, AssociationType.COLOCATED); int found = 0; for (AssociationDescriptor anADesc : aDesc) { if (found > 0) b.append(", "); found++; b.append(anADesc.getName()); } String failureReason = b.toString(); provisionRequest.addFailureReason(failureReason); logger.debug(failureReason); return (false); } if(!AssociationMatcher.meetsOpposedRequirements(sElem, this)) { String failureReason = AssociationMatcher.getLastErrorMessage(); provisionRequest.addFailureReason(failureReason); logger.debug(failureReason); return (false); } if(!resourceCapability.measuredResourcesWithinRange()) { StringBuilder buffer = new StringBuilder(); MeasuredResource[] m = resourceCapability.getMeasuredResources(ResourceCapability.MEASURED_RESOURCES_BREACHED); for (MeasuredResource aM : m) { buffer.append("\n"); buffer.append("[").append(aM.getIdentifier()).append("] "); buffer.append("Low: ").append(aM.getThresholdValues().getLowThreshold()).append(", "); buffer.append("High: ").append(aM.getThresholdValues().getHighThreshold()).append(", "); buffer.append("Actual: ").append(aM.getValue()); } String failureReason = String.format("%s not selected to allocate service [%s], MeasuredResources have exceeded threshold constraints: %s", getName(), LoggingUtil.getLoggingName(sElem), buffer.toString()); provisionRequest.addFailureReason(failureReason); logger.debug(failureReason); return(false); } if(meetsGeneralRequirements(provisionRequest) && meetsQuantitativeRequirements(provisionRequest)) { Collection<SystemComponent> unsupportedReqs = meetsQualitativeRequirements(provisionRequest); if(unsupportedReqs.isEmpty()) { logger.debug("{} meets qualitative requirements for [{}]", getName(), LoggingUtil.getLoggingName(sElem)); return (true); } else { /* Create a String representation of the unsupportedReqs * object for logging */ int x = 0; StringBuilder buffer = new StringBuilder(); for (SystemComponent unsupportedReq : unsupportedReqs) { if (x > 0) buffer.append(", "); buffer.append("[").append(unsupportedReq.toString()).append("]"); x++; } String unsupportedReqsString = buffer.toString(); logger.debug("{} does not meet requirements for {} service [{}]", getName(), provType, LoggingUtil.getLoggingName(sElem)); /* Determine if the resource supports persistent provisioning */ if(!resourceCapability.supportsPersistentProvisioning()) { String failureReason = String.format("Cannot allocate %s service [%s] to %s, required SystemComponents cannot be " + "provisioned. This is because the %s is not configured for persistentProvisioning. " + "If you want to enable this feature, verify the %s's configuration for the " + "org.rioproject.cybernode.persistentProvisioning property is set to true", provType, LoggingUtil.getLoggingName(sElem), getName(), getName(), getName()); provisionRequest.addFailureReason(failureReason); logger.debug(failureReason); return (false); } /* * Check if the unsupported PlatformCapability objects can be * provisioned. If there are any that cannot be provisioned move * onto the next resource */ boolean provisionableCaps = true; for (SystemComponent sysReq : unsupportedReqs) { if (sysReq.getStagedSoftware()==null) { provisionableCaps = false; break; } } if(!provisionableCaps) { StringBuilder message = new StringBuilder(); message.append(getName()).append(" does not meet requirements for "); message.append(provType).append(" service "); message.append("[").append(LoggingUtil.getLoggingName(sElem)).append("] "); message.append(unsupportedReqsString); String failureReason = message.toString(); provisionRequest.addFailureReason(failureReason); logger.warn(failureReason); return (false); } /* Get the size of the download(s) */ int requiredSize = 0; IOException failed = null; try { for (SystemComponent sysReq : unsupportedReqs) { StagedSoftware download = sysReq.getStagedSoftware(); if(download!=null) { int size = download.getDownloadSize(); if(size < 0) { logger.warn("Unable to obtain download size for {}, abort provision request", download.getLocation()); requiredSize = size; break; } requiredSize += size; if(download.getPostInstallAttributes() != null && download.getPostInstallAttributes().getStagedData() != null) { StagedData postInstall = download.getPostInstallAttributes().getStagedData(); size = postInstall.getDownloadSize(); if(size < 0) { logger.warn("Unable to obtain download size for PostInstall {}, abort provision request", postInstall.getLocation()); requiredSize = size; break; } requiredSize += size; } } } } catch(IOException e) { failed = e; } if (requiredSize < 0 || failed!=null) throw new ProvisionException("Service ["+LoggingUtil.getLoggingName(sElem)+"] "+ "instantiation failed", failed==null? new IOException("Unable to obtain download size"):failed, true); /* Find out if the resource has the necessary disk-space */ if(supportsStorageRequirement(requiredSize, resourceCapability.getPlatformCapabilities())) { logger.debug("{} supports provisioning requirements for {} service [{}]", getName(), provType, LoggingUtil.getLoggingName(sElem)); sElem.setProvisionablePlatformCapabilities(unsupportedReqs); return (true); } double avail = getAvailableStorage(resourceCapability.getPlatformCapabilities()); StringBuilder sb = new StringBuilder(); sb.append(getName()).append(" "); if(avail>0) { /* For logging purposes compute the size in GB */ double GB = Math.pow(1024, 3); avail = avail/GB; sb.append("does not have adequate disk-space for ") .append("[") .append(LoggingUtil.getLoggingName(sElem)).append("] ") .append("Required=") .append(+requiredSize).append(", ") .append("Available=").append(avail).append(" GB"); } else { sb.append("does not report a StorageCapability. ") .append("Rio cannot allocate the ") .append("[") .append(LoggingUtil.getLoggingName(sElem)) .append("] ") .append("service with a software download size of ") .append(+requiredSize).append(". ") .append("This may be due to a known limitation ") .append("found when running the ").append(getName()) .append(" on a Windows machine, or if the ") .append("DiskSpace monitor has been disabled. ") .append("Check the Cybernode environment and ") .append("configuration."); } String failureReason = sb.toString(); provisionRequest.addFailureReason(failureReason); logger.warn(failureReason); return (false); } } else { String failureReason = String.format("%s does not meet general or quantitative requirements for %s service [%s]", getName(), provType, LoggingUtil.getLoggingName(sElem)); logger.debug(failureReason); return (false); } } /** * Determine if an Array of PlatformCapability components contains a * StorageCapability and if that StorageCapability has the requested disk * space size available * * @param requestedSize The size to verify * @param pCaps Array of PlatformCapability instances to use * @return Return true if the Array of PlatformCapability * components contains a StorageCapability and if that StorageCapability has * the requested disk space size available */ boolean supportsStorageRequirement(int requestedSize, PlatformCapability[] pCaps) { boolean supports = false; for (PlatformCapability pCap : pCaps) { if (pCap instanceof StorageCapability) { StorageCapability storage = (StorageCapability) pCap; supports = storage.supports(requestedSize); break; } } return (supports); } /** * Get the available storage from the StorageCapability * * @param pCaps Array of PlatformCapability instances to use * * @return The available storage from the StorageCapability. If a * StorageCapability cannot be found return -1 */ double getAvailableStorage(PlatformCapability[] pCaps) { double available = -1; for (PlatformCapability pCap : pCaps) { if (pCap instanceof StorageCapability) { StorageCapability storage = (StorageCapability) pCap; Double dCap = (Double) storage.getValue(StorageCapability.CAPACITY); if (dCap != null) { available = dCap; } break; } } return(available); } /** * This method determines whether or not the defined criteria meets general * requirements: * <ul> * <li>If there is a cluster of machines defined, the compute resource is * defined in the cluster of machines that have been defined * </ul> * <br> * * @param provisionRequest The ProvisionRequest * @return Return true if the provided ResourceCapability meets * general requirements */ boolean meetsGeneralRequirements(final ProvisionRequest provisionRequest) { /* * If we have a cluster defined, then see if the provided resource has * either an IP address or hostname thats in the list of IP addresses * and hostnames in our machine cluster list. If it isnt in the list, * then there is no sense in proceeding */ ServiceElement sElem = provisionRequest.getServiceElement(); String[] machineCluster = sElem.getCluster(); if(machineCluster != null && machineCluster.length > 0) { logger.debug("ServiceBean [{}] has a cluster requirement", LoggingUtil.getLoggingName(sElem)); boolean found = false; for (String aMachineCluster : machineCluster) { if (aMachineCluster.equals(resourceCapability.getAddress()) || aMachineCluster.equalsIgnoreCase(resourceCapability.getHostName())) found = true; } if(!found) { StringBuilder builder = new StringBuilder(); for(String m : machineCluster) { if(builder.length()>0) builder.append(", "); builder.append(m); } String failureReason = String.format("%s not found in cluster requirement [%s] for [%s]", getName(), builder.toString(), LoggingUtil.getLoggingName(sElem)); provisionRequest.addFailureReason(failureReason); logger.debug(failureReason); return (false); } } return (true); } /** * This method verifies whether the ResourceCapability can support the * Qualitative Requirements specified by the ServiceBean * * @param request The ProvisionRequest object * @return A Collection of SystemRequirement objects which the * ResourceCapability does not support. If the Collection has zero entries, * then the provided ResourceCapability supports the Qualitative * Requirements specified by the ServiceBean */ Collection<SystemComponent> meetsQualitativeRequirements(final ProvisionRequest request) { ServiceElement sElem = request.getServiceElement(); ServiceLevelAgreements sla = sElem.getServiceLevelAgreements(); SystemComponent[] serviceRequirements = sla.getSystemRequirements().getSystemComponents(); List<SystemComponent> unsupportedRequirements = new ArrayList<SystemComponent>(); /* * If there are no PlatformCapability requirements we can return * successfully */ if(serviceRequirements.length == 0) return unsupportedRequirements; PlatformCapability[] platformCapabilities = resourceCapability.getPlatformCapabilities(); List<SystemComponent> operatingSystems = new ArrayList<SystemComponent>(); List<SystemComponent> architectures = new ArrayList<SystemComponent>(); List<SystemComponent> machineAddresses = new ArrayList<SystemComponent>(); List<SystemComponent> remaining = new ArrayList<SystemComponent>(); for (SystemComponent serviceRequirement : serviceRequirements) { if(isOperatingSystem(serviceRequirement)) { operatingSystems.add(serviceRequirement); } else if(isArchitecture(serviceRequirement)) { architectures.add(serviceRequirement); } else if(isMachineAddress(serviceRequirement)) { machineAddresses.add(serviceRequirement); } else { remaining.add(serviceRequirement); } } /* * Check if we have a match in one of the sought after architectures */ if(!architectures.isEmpty()) { ProcessorArchitecture architecture = getArchitecture(); Result result = check(architecture, architectures); if (!result.supported) { String failureReason = formatFailureReason(architectures, (String)architecture.getCapabilities().get(ProcessorArchitecture.ARCHITECTURE), "architecture", sElem, result.excluded.isEmpty(), ProcessorArchitecture.ARCHITECTURE); if(logger.isWarnEnabled()) { logger.warn(failureReason); } request.addFailureReason(failureReason); unsupportedRequirements.addAll(architectures); return unsupportedRequirements; } } /* * Check if we have a match in one of the sought after operating systems */ if(!operatingSystems.isEmpty()) { OperatingSystem operatingSystem = getOperatingSystem(); Result result = check(operatingSystem, operatingSystems); if (!result.supported) { String failureReason = formatFailureReason(operatingSystems, operatingSystem.getCapabilities().get(OperatingSystem.NAME).toString(), "operating system", sElem, result.excluded.isEmpty(), OperatingSystem.NAME); if(logger.isWarnEnabled()) { logger.warn(failureReason); } request.addFailureReason(failureReason); unsupportedRequirements.addAll(operatingSystems); return unsupportedRequirements; } } /* * Check if we have a match in one of the sought after machine addresses */ if(!machineAddresses.isEmpty()) { TCPConnectivity tcpConnectivity = getTCPConnectivity(); Result result = check(tcpConnectivity, machineAddresses); if (!result.supported) { String formattedComponents = formatSystemComponents(machineAddresses, TCPConnectivity.HOST_NAME, TCPConnectivity.HOST_ADDRESS); String failureReason; if(result.excluded.isEmpty()) { failureReason = String.format("The machine addresses being requested [%s] do not match the " + "target resource's machine name/ip [%s/%s] for [%s]", formattedComponents, tcpConnectivity.getCapabilities().get(TCPConnectivity.HOST_NAME), tcpConnectivity.getCapabilities().get(TCPConnectivity.HOST_ADDRESS), LoggingUtil.getLoggingName(sElem)); } else { failureReason = String.format("The target resource's machine name/ip [%s/%s] is on the exclusion list of [%s] for [%s]", tcpConnectivity.getCapabilities().get(TCPConnectivity.HOST_NAME), tcpConnectivity.getCapabilities().get(TCPConnectivity.HOST_ADDRESS), formattedComponents, LoggingUtil.getLoggingName(sElem)); } if(logger.isWarnEnabled()) { logger.warn(failureReason); } request.addFailureReason(failureReason); unsupportedRequirements.addAll(machineAddresses); return unsupportedRequirements; } } /* * Check remaining PlatformCapability objects for supportability */ for (SystemComponent serviceRequirement : remaining) { boolean supported = false; /* * Iterate through all resource PlatformCapability objects and see * if any of them supports the current PlatformCapability. If none * are found, then we don't have a match */ for (PlatformCapability platformCapability : platformCapabilities) { if (platformCapability.supports(serviceRequirement)) { if(serviceRequirement.exclude()) { continue; } supported = true; break; } } if (!supported) { unsupportedRequirements.add(serviceRequirement); } } return unsupportedRequirements; } Result check(final PlatformCapability platformCapability, final List<SystemComponent> systemComponents) { Result result = new Result(); boolean supported = false; for (SystemComponent serviceRequirement : systemComponents) { if(serviceRequirement.exclude()) { if(platformCapability.supports(serviceRequirement)) { result.excluded.add(serviceRequirement); break; } else { supported = true; break; } } else { if(platformCapability.supports(serviceRequirement)) { supported = true; break; } } } result.supported = supported; return result; } class Result { boolean supported; List<SystemComponent> excluded = new ArrayList<SystemComponent>(); } String formatSystemComponents(final List<SystemComponent> systemComponents, final String... keys) { StringBuilder builder = new StringBuilder(); for(String key : keys) { for (SystemComponent serviceRequirement : systemComponents) { if(builder.length()>0) builder.append(", "); String value = (String) serviceRequirement.getAttributes().get(key); if(value!=null) builder.append(value); } } return builder.toString(); } String formatFailureReason(final List<SystemComponent> systemComponents, final String capability, final String name, final ServiceElement sElem, final boolean notExcluded, final String... keys) { String formattedComponents = formatSystemComponents(systemComponents, keys); String failureReason; if(notExcluded) { failureReason = String.format("The %ss being requested [%s] are not supported by the " + "target resource's %s [%s] for [%s]", name, formattedComponents, name, capability, LoggingUtil.getLoggingName(sElem)); } else { failureReason = String.format("The target resource's %s [%s] is on the exclusion list of [%s] for [%s]", name, capability, formattedComponents, LoggingUtil.getLoggingName(sElem)); } return failureReason; } boolean isOperatingSystem(SystemComponent systemComponent) { String name = systemComponent.getName(); String className = systemComponent.getClassName(); if(className==null) { return name.equals(OperatingSystem.ID); } return systemComponent.getClassName().equals(OperatingSystem.class.getName()); } boolean isArchitecture(SystemComponent systemComponent) { String name = systemComponent.getName(); String className = systemComponent.getClassName(); if(className==null) { return name.equals(ProcessorArchitecture.ID); } return systemComponent.getClassName().equals(ProcessorArchitecture.class.getName()); } boolean isMachineAddress(SystemComponent systemComponent) { String name = systemComponent.getName(); String className = systemComponent.getClassName(); if(className==null) { return name.equals(TCPConnectivity.ID); } return systemComponent.getClassName().equals(TCPConnectivity.class.getName()); } boolean isHardwareRelated(SystemComponent systemComponent) { return isArchitecture(systemComponent) || isOperatingSystem(systemComponent) || isMachineAddress(systemComponent); } ProcessorArchitecture getArchitecture () { ProcessorArchitecture architecture = null; for (PlatformCapability platformCapability : resourceCapability.getPlatformCapabilities()) { if(platformCapability instanceof ProcessorArchitecture) { architecture = (ProcessorArchitecture) platformCapability; break; } } return architecture; } OperatingSystem getOperatingSystem () { OperatingSystem operatingSystem = null; for (PlatformCapability platformCapability : resourceCapability.getPlatformCapabilities()) { if(platformCapability instanceof OperatingSystem) { operatingSystem = (OperatingSystem) platformCapability; break; } } return operatingSystem; } TCPConnectivity getTCPConnectivity () { TCPConnectivity tcpConnectivity = null; for (PlatformCapability platformCapability : resourceCapability.getPlatformCapabilities()) { if(platformCapability instanceof TCPConnectivity) { tcpConnectivity = (TCPConnectivity) platformCapability; break; } } return tcpConnectivity; } /** * This method verifies whether the ResourceCapability can support the * Quantitative Requirements specified by the ServiceBean * * @param provisionRequest The ProvisionRequest * @return Return true if the provided ResourceCapability meets * Quantitative requirements */ boolean meetsQuantitativeRequirements(final ProvisionRequest provisionRequest) { ServiceElement sElem = provisionRequest.getServiceElement(); ServiceLevelAgreements sla = sElem.getServiceLevelAgreements(); boolean provisionable = true; String[] systemThresholdIDs = sla.getSystemRequirements().getSystemThresholdIDs(); if(systemThresholdIDs.length == 0) return (true); MeasuredResource[] measured = resourceCapability.getMeasuredResources(); /* * If the number of MeasuredCapabilities is less then what we are asking * for there is no reason to continue */ if(measured == null || measured.length < systemThresholdIDs.length) { StringBuilder message = new StringBuilder(); message.append(getName()).append(" "); if(measured==null) { message.append("has a [null] MeasuredCapability instance, ServiceBean ["); message.append(LoggingUtil.getLoggingName(sElem)).append("] "); message.append("has a requirement to test ").append(systemThresholdIDs.length); } else { message.append("only has [").append(measured.length).append("] MeasuredCapability instances, "); message.append("ServiceBean [").append(LoggingUtil.getLoggingName(sElem)).append("] "); message.append("has a requirement to test [").append(systemThresholdIDs.length).append("]"); } provisionRequest.addFailureReason(message.toString()); logger.debug(message.toString()); return (false); } /* * Check each of the MeasuredResource objects */ for (String systemThresholdID : systemThresholdIDs) { boolean supported = false; ThresholdValues systemThreshold = sla.getSystemRequirements().getSystemThresholdValue(systemThresholdID); if (systemThresholdID.equals(SystemRequirements.SYSTEM)) { double systemUtilization = systemThreshold.getHighThreshold(); if (systemUtilization < resourceCapability.getUtilization()) { String failureReason = String.format("%s cannot meet system utilization requirement. Desired: %f, Actual: %f", getName(), systemUtilization, resourceCapability.getUtilization()); provisionRequest.addFailureReason(failureReason); logger.debug(failureReason); return (false); } else { supported = true; logger.debug("[System] utilization requirement met. Desired {}, Actual {}", systemUtilization, resourceCapability.getUtilization()); } } /* * Iterate through all resource MeasuredResource objects and see if * any of them supports the current MeasuredResource. If none are * found, then we don't have a match */ for (MeasuredResource mRes : measured) { if (mRes.getIdentifier().equals(systemThresholdID)) { if (mRes.evaluate(systemThreshold)) { supported = true; logger.debug("{} meets [{}] utilization requirement. Desired Low: {}, High: {}, Actual: {}",getName(), getName(), systemThresholdID, systemThreshold.getLowThreshold(), systemThreshold.getHighThreshold(), mRes.getValue()); break; } else { String failureReason = String.format("%s cannot meet [%s], utilization requirement. Desired Low: %f, High: %f, Actual: %f", getName(), systemThresholdID, systemThreshold.getLowThreshold(), systemThreshold.getHighThreshold(), mRes.getValue()); provisionRequest.addFailureReason(failureReason); logger.debug(failureReason); } } } if (!supported) { provisionable = false; break; } } return provisionable; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; InstantiatorResource that = (InstantiatorResource) o; return instantiatorUuid.equals(that.instantiatorUuid); } @Override public int hashCode() { return instantiatorUuid.hashCode(); } }