package org.cloudifysource.rest.controllers; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import org.cloudifysource.domain.Service; import org.cloudifysource.domain.scalingrules.ScalingRuleDetails; import org.cloudifysource.domain.statistics.AbstractStatisticsDetails; import org.cloudifysource.domain.statistics.ServiceStatisticsDetails; import org.cloudifysource.dsl.internal.CloudifyConstants; import org.cloudifysource.dsl.internal.DSLException; import org.cloudifysource.rest.util.IsolationUtils; import org.cloudifysource.rest.util.OpenspacesDomainStatisticsAdapter; import org.openspaces.admin.pu.elastic.config.AutomaticCapacityScaleConfig; import org.openspaces.admin.pu.elastic.config.AutomaticCapacityScaleConfigurer; import org.openspaces.admin.pu.elastic.config.AutomaticCapacityScaleRuleConfig; import org.openspaces.admin.pu.elastic.config.CapacityRequirementsConfig; import org.openspaces.admin.pu.elastic.config.CapacityRequirementsConfigurer; import org.openspaces.admin.pu.elastic.config.EagerScaleConfig; import org.openspaces.admin.pu.elastic.config.EagerScaleConfigurer; import org.openspaces.admin.pu.elastic.config.ManualCapacityScaleConfig; import org.openspaces.admin.pu.elastic.config.ManualCapacityScaleConfigurer; import org.openspaces.admin.pu.statistics.InstancesStatisticsConfig; import org.openspaces.admin.pu.statistics.LastSampleTimeWindowStatisticsConfig; import org.openspaces.admin.pu.statistics.ProcessingUnitStatisticsId; import org.openspaces.admin.zone.config.AnyZonesConfig; import org.openspaces.core.util.MemoryUnit; /** * * @author elip * */ public final class ElasticScaleConfigFactory { private ElasticScaleConfigFactory() { } private static final String SCALING_RULE_MUST_SPECIFY_SERVICE_STATISTICS_ERROR_MSG = "scalingRule must specify serviceStatistics (either a closure or " + "reference a predefined serviceStatistics name)."; private static final Logger logger = Logger .getLogger(ElasticScaleConfigFactory.class.getName()); /** * * @param totalMemoryInMB . * @param totalCpuCores . * @param locationAware . * @param dedicated . * @return . */ public static ManualCapacityScaleConfig createManualCapacityScaleConfig( final int totalMemoryInMB, final double totalCpuCores, final boolean locationAware, final boolean dedicated) { final ManualCapacityScaleConfig config = new ManualCapacityScaleConfigurer() .memoryCapacity(totalMemoryInMB, MemoryUnit.MEGABYTES) .numberOfCpuCores(totalCpuCores).create(); config.setGridServiceAgentZonesAware(locationAware); if (dedicated) { config.setAtMostOneContainerPerMachine(true); } return config; } /** * * @return . */ public static EagerScaleConfig createEagerScaleConfig() { return new EagerScaleConfigurer().atMostOneContainerPerMachine() .create(); } /** * @param serviceName * - the absolute name of the service * @param service * - the service DSL or null if not exists * @param externalProcessMemoryInMB * - MB memory allocated for the GSC plus the external service. * @param locationAware . * @return a @{link AutomaticCapacityScaleConfig} based on the specified * service and memory. * @throws InvocationTargetException * @throws ClassNotFoundException * @throws IllegalAccessException * @throws InstantiationException * @throws DSLException . */ public static AutomaticCapacityScaleConfig createAutomaticCapacityScaleConfig( final String serviceName, final Service service, final int externalProcessMemoryInMB, final boolean locationAware, final boolean dedicated) throws DSLException { if (externalProcessMemoryInMB <= 0) { throw new IllegalArgumentException( "externalProcessMemoryInMB must be positive"); } final List<ScalingRuleDetails> scalingRules = service.getScalingRules(); if (scalingRules.isEmpty()) { throw new IllegalStateException("scalingRules cannot be empty"); } double instanceCpuCores = IsolationUtils.getInstanceCpuCores(service); final CapacityRequirementsConfig minCapacity = new CapacityRequirementsConfigurer() .memoryCapacity( service.getMinAllowedInstances() * externalProcessMemoryInMB, MemoryUnit.MEGABYTES) .numberOfCpuCores( service.getMinAllowedInstances() * instanceCpuCores) .create(); final CapacityRequirementsConfig initialCapacity = new CapacityRequirementsConfigurer() .memoryCapacity( service.getNumInstances() * externalProcessMemoryInMB, MemoryUnit.MEGABYTES) .numberOfCpuCores(service.getNumInstances() * instanceCpuCores) .create(); final CapacityRequirementsConfig maxCapacity = new CapacityRequirementsConfigurer() .memoryCapacity( service.getMaxAllowedInstances() * externalProcessMemoryInMB, MemoryUnit.MEGABYTES) .numberOfCpuCores( service.getMaxAllowedInstances() * instanceCpuCores) .create(); final CapacityRequirementsConfig minCapacityPerZone = new CapacityRequirementsConfigurer() .memoryCapacity( service.getMinAllowedInstancesPerLocation() * externalProcessMemoryInMB, MemoryUnit.MEGABYTES) .numberOfCpuCores( service.getMinAllowedInstancesPerLocation() * instanceCpuCores).create(); final CapacityRequirementsConfig maxCapacityPerZone = new CapacityRequirementsConfigurer() .memoryCapacity( service.getMaxAllowedInstancesPerLocation() * externalProcessMemoryInMB, MemoryUnit.MEGABYTES) .numberOfCpuCores( service.getMaxAllowedInstancesPerLocation() * instanceCpuCores).create(); final AutomaticCapacityScaleConfigurer scaleConfigurer = new AutomaticCapacityScaleConfigurer() .minCapacity(minCapacity) .initialCapacity(initialCapacity) .maxCapacity(maxCapacity) .minCapacityPerZone(minCapacityPerZone) .maxCapacityPerZone(maxCapacityPerZone) .statisticsPollingInterval( service.getSamplingPeriodInSeconds(), TimeUnit.SECONDS) .cooldownAfterScaleOut(service.getScaleOutCooldownInSeconds(), TimeUnit.SECONDS) .cooldownAfterScaleIn(service.getScaleInCooldownInSeconds(), TimeUnit.SECONDS); if (locationAware) { scaleConfigurer.enableGridServiceAgentZonesAware(); } if (dedicated) { scaleConfigurer.atMostOneContainerPerMachine(); } final Map<String, ServiceStatisticsDetails> serviceStatisticsByName = new HashMap<String, ServiceStatisticsDetails>(); if (service.getServiceStatistics() != null) { for (final AbstractStatisticsDetails calculatedStatistics : service .getServiceStatistics()) { if (calculatedStatistics instanceof ServiceStatisticsDetails) { serviceStatisticsByName.put(calculatedStatistics.getName(), (ServiceStatisticsDetails) calculatedStatistics); } } } for (final ScalingRuleDetails scalingRule : scalingRules) { final Object serviceStatisticsObject = scalingRule .getServiceStatistics(); ServiceStatisticsDetails serviceStatistics = null; if (serviceStatisticsObject instanceof String) { final String serviceStatisticsName = (String) serviceStatisticsObject; serviceStatistics = serviceStatisticsByName .get(serviceStatisticsName); if (serviceStatistics == null) { throw new DSLException( SCALING_RULE_MUST_SPECIFY_SERVICE_STATISTICS_ERROR_MSG + " " + serviceStatisticsName + " is not recognized. Possible values are: " + serviceStatisticsByName.keySet()); } } else if (serviceStatisticsObject instanceof ServiceStatisticsDetails) { serviceStatistics = (ServiceStatisticsDetails) serviceStatisticsObject; } else { throw new DSLException( SCALING_RULE_MUST_SPECIFY_SERVICE_STATISTICS_ERROR_MSG + " Unsupported type " + serviceStatisticsObject.getClass()); } final ProcessingUnitStatisticsId statisticsId = new ProcessingUnitStatisticsId(); statisticsId.setMonitor(CloudifyConstants.USM_MONITORS_SERVICE_ID); statisticsId.setMetric(serviceStatistics.getMetric()); OpenspacesDomainStatisticsAdapter adapter = new OpenspacesDomainStatisticsAdapter(); InstancesStatisticsConfig instanceStatistics; try { instanceStatistics = adapter.createInstanceStatisticsConfig(serviceStatistics .getInstancesStatistics().createInstancesStatistics()); statisticsId.setInstancesStatistics(instanceStatistics); } catch (Exception e) { throw new DSLException("Failed instantiating instance statistics.", e); } if (serviceStatistics.getMovingTimeRangeInSeconds() <= service .getSamplingPeriodInSeconds()) { if (logger.isLoggable(Level.FINE)) { logger.fine("Deploying service " + serviceName + " with auto scaling that monitors the last sample of " + serviceStatistics.getMetric()); } statisticsId .setTimeWindowStatistics(new LastSampleTimeWindowStatisticsConfig()); } else { org.openspaces.admin.pu.statistics.TimeWindowStatisticsConfig timeWindowStatistics; try { timeWindowStatistics = adapter.createTimeWindowStatisticsConfig(serviceStatistics.getTimeStatistics() .createTimeWindowStatistics(serviceStatistics.getMovingTimeRangeInSeconds(), TimeUnit.SECONDS)); } catch (Exception e) { throw new DSLException("Failed instantiating time window statistics.", e); } statisticsId.setTimeWindowStatistics(timeWindowStatistics); } statisticsId.setAgentZones(new AnyZonesConfig()); final AutomaticCapacityScaleRuleConfig rule = new AutomaticCapacityScaleRuleConfig(); rule.setStatistics(statisticsId); if (scalingRule.getLowThreshold() == null) { if (logger.isLoggable(Level.FINE)) { logger.fine(serviceName + " scalingRule for " + serviceStatistics.getMetric() + " lowThreshold is undefined"); } } else { final Comparable<?> threshold = scalingRule.getLowThreshold() .getValue(); if (threshold == null) { throw new DSLException(serviceName + " scalingRule for " + serviceStatistics.getMetric() + " lowThreshold value is missing"); } final int instancesDecrease = scalingRule.getLowThreshold() .getInstancesDecrease(); if (instancesDecrease < 0) { throw new DSLException( serviceName + " scalingRule for " + serviceStatistics.getMetric() + " lowThreshold instancesDecrease cannot be a negative number (" + instancesDecrease + ")"); } if (instancesDecrease == 0) { if (logger.isLoggable(Level.FINE)) { logger.fine(serviceName + " scalingRule for " + serviceStatistics.getMetric() + " lowThreshold instancesDecrease is 0"); } } else { rule.setLowThreshold(threshold); rule.setLowThresholdBreachedDecrease(new CapacityRequirementsConfigurer() .memoryCapacity( instancesDecrease * externalProcessMemoryInMB, MemoryUnit.MEGABYTES).create()); } } if (scalingRule.getHighThreshold() == null) { if (logger.isLoggable(Level.FINE)) { logger.fine(serviceName + " scalingRule for " + serviceStatistics.getMetric() + " highThreshold is undefined"); } } else { final Comparable<?> threshold = scalingRule.getHighThreshold() .getValue(); if (threshold == null) { throw new DSLException(serviceName + " scalingRule for " + serviceStatistics.getMetric() + " highThreshold value is missing"); } final int instancesIncrease = scalingRule.getHighThreshold() .getInstancesIncrease(); if (instancesIncrease < 0) { throw new DSLException( serviceName + " scalingRule for " + serviceStatistics.getMetric() + " highThreshold instancesIncrease cannot be a negative number (" + instancesIncrease + ")"); } if (instancesIncrease == 0) { if (logger.isLoggable(Level.FINE)) { logger.fine(serviceName + " scalingRule for " + serviceStatistics.getMetric() + " highThreshold instancesIncrease is 0"); } } else { rule.setHighThreshold(threshold); rule.setHighThresholdBreachedIncrease(new CapacityRequirementsConfigurer() .memoryCapacity( instancesIncrease * externalProcessMemoryInMB, MemoryUnit.MEGABYTES).create()); } } scaleConfigurer.addRule(rule); } return scaleConfigurer.create(); } }