/************************************************************************* * Copyright 2009-2015 Eucalyptus Systems, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. * * Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta * CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need * additional information or have any questions. ************************************************************************/ package com.eucalyptus.autoscaling.backend; import static com.eucalyptus.autoscaling.common.AutoScalingResourceName.InvalidResourceNameException; import static com.eucalyptus.autoscaling.common.AutoScalingMetadata.AutoScalingGroupMetadata; import static com.eucalyptus.autoscaling.common.AutoScalingResourceName.Type.autoScalingGroup; import static com.google.common.base.Strings.nullToEmpty; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import javax.inject.Inject; import org.apache.log4j.Logger; import org.hibernate.exception.ConstraintViolationException; import com.eucalyptus.auth.AuthQuotaException; import com.eucalyptus.auth.Permissions; import com.eucalyptus.auth.principal.AccountFullName; import com.eucalyptus.auth.principal.UserFullName; import com.eucalyptus.autoscaling.activities.ActivityManager; import com.eucalyptus.autoscaling.common.internal.activities.ScalingActivity; import com.eucalyptus.autoscaling.common.AutoScalingMetadata; import com.eucalyptus.autoscaling.common.AutoScalingMetadatas; import com.eucalyptus.autoscaling.common.AutoScalingResourceName; import com.eucalyptus.autoscaling.common.backend.msgs.CreateAutoScalingGroupResponseType; import com.eucalyptus.autoscaling.common.backend.msgs.CreateAutoScalingGroupType; import com.eucalyptus.autoscaling.common.backend.msgs.CreateLaunchConfigurationResponseType; import com.eucalyptus.autoscaling.common.backend.msgs.CreateLaunchConfigurationType; import com.eucalyptus.autoscaling.common.backend.msgs.CreateOrUpdateTagsResponseType; import com.eucalyptus.autoscaling.common.backend.msgs.CreateOrUpdateTagsType; import com.eucalyptus.autoscaling.common.backend.msgs.DeleteAutoScalingGroupResponseType; import com.eucalyptus.autoscaling.common.backend.msgs.DeleteAutoScalingGroupType; import com.eucalyptus.autoscaling.common.backend.msgs.DeleteLaunchConfigurationResponseType; import com.eucalyptus.autoscaling.common.backend.msgs.DeleteLaunchConfigurationType; import com.eucalyptus.autoscaling.common.backend.msgs.DeletePolicyResponseType; import com.eucalyptus.autoscaling.common.backend.msgs.DeletePolicyType; import com.eucalyptus.autoscaling.common.backend.msgs.DeleteTagsResponseType; import com.eucalyptus.autoscaling.common.backend.msgs.DeleteTagsType; import com.eucalyptus.autoscaling.common.backend.msgs.DescribePoliciesResponseType; import com.eucalyptus.autoscaling.common.backend.msgs.DescribePoliciesType; import com.eucalyptus.autoscaling.common.backend.msgs.DisableMetricsCollectionResponseType; import com.eucalyptus.autoscaling.common.backend.msgs.DisableMetricsCollectionType; import com.eucalyptus.autoscaling.common.backend.msgs.EnableMetricsCollectionResponseType; import com.eucalyptus.autoscaling.common.backend.msgs.EnableMetricsCollectionType; import com.eucalyptus.autoscaling.common.backend.msgs.ExecutePolicyResponseType; import com.eucalyptus.autoscaling.common.backend.msgs.ExecutePolicyType; import com.eucalyptus.autoscaling.common.backend.msgs.PutScalingPolicyResponseType; import com.eucalyptus.autoscaling.common.backend.msgs.PutScalingPolicyType; import com.eucalyptus.autoscaling.common.backend.msgs.ResumeProcessesResponseType; import com.eucalyptus.autoscaling.common.backend.msgs.ResumeProcessesType; import com.eucalyptus.autoscaling.common.backend.msgs.SetDesiredCapacityResponseType; import com.eucalyptus.autoscaling.common.backend.msgs.SetDesiredCapacityType; import com.eucalyptus.autoscaling.common.backend.msgs.SetInstanceHealthResponseType; import com.eucalyptus.autoscaling.common.backend.msgs.SetInstanceHealthType; import com.eucalyptus.autoscaling.common.backend.msgs.SetInstanceProtectionResponseType; import com.eucalyptus.autoscaling.common.backend.msgs.SetInstanceProtectionType; import com.eucalyptus.autoscaling.common.backend.msgs.SuspendProcessesResponseType; import com.eucalyptus.autoscaling.common.backend.msgs.SuspendProcessesType; import com.eucalyptus.autoscaling.common.backend.msgs.TerminateInstanceInAutoScalingGroupResponseType; import com.eucalyptus.autoscaling.common.backend.msgs.TerminateInstanceInAutoScalingGroupType; import com.eucalyptus.autoscaling.common.backend.msgs.UpdateAutoScalingGroupResponseType; import com.eucalyptus.autoscaling.common.backend.msgs.UpdateAutoScalingGroupType; import com.eucalyptus.autoscaling.common.msgs.Activity; import com.eucalyptus.autoscaling.common.msgs.Alarms; import com.eucalyptus.autoscaling.common.msgs.BlockDeviceMappingType; import com.eucalyptus.autoscaling.common.msgs.ScalingPolicyType; import com.eucalyptus.autoscaling.common.msgs.TagType; import com.eucalyptus.autoscaling.common.policy.AutoScalingPolicySpec; import com.eucalyptus.autoscaling.config.AutoScalingConfiguration; import com.eucalyptus.autoscaling.common.internal.configurations.LaunchConfiguration; import com.eucalyptus.autoscaling.common.internal.configurations.LaunchConfigurationMinimumView; import com.eucalyptus.autoscaling.common.internal.configurations.LaunchConfigurations; import com.eucalyptus.autoscaling.common.internal.groups.AutoScalingGroup; import com.eucalyptus.autoscaling.common.internal.groups.AutoScalingGroupCoreView; import com.eucalyptus.autoscaling.common.internal.groups.AutoScalingGroups; import com.eucalyptus.autoscaling.common.internal.groups.MetricCollectionType; import com.eucalyptus.autoscaling.common.internal.groups.HealthCheckType; import com.eucalyptus.autoscaling.common.internal.groups.ScalingProcessType; import com.eucalyptus.autoscaling.common.internal.groups.SuspendedProcess; import com.eucalyptus.autoscaling.common.internal.groups.TerminationPolicyType; import com.eucalyptus.autoscaling.common.internal.instances.AutoScalingInstance; import com.eucalyptus.autoscaling.common.internal.instances.AutoScalingInstanceGroupView; import com.eucalyptus.autoscaling.common.internal.instances.AutoScalingInstances; import com.eucalyptus.autoscaling.common.internal.instances.HealthStatus; import com.eucalyptus.autoscaling.common.internal.metadata.AutoScalingMetadataException; import com.eucalyptus.autoscaling.common.internal.metadata.AutoScalingMetadataNotFoundException; import com.eucalyptus.autoscaling.common.internal.policies.AdjustmentType; import com.eucalyptus.autoscaling.common.internal.policies.ScalingPolicies; import com.eucalyptus.autoscaling.common.internal.policies.ScalingPolicy; import com.eucalyptus.autoscaling.common.internal.policies.ScalingPolicyView; import com.eucalyptus.autoscaling.common.internal.tags.AutoScalingGroupTag; import com.eucalyptus.autoscaling.common.internal.tags.Tag; import com.eucalyptus.autoscaling.common.internal.tags.TagSupport; import com.eucalyptus.autoscaling.common.internal.tags.Tags; import com.eucalyptus.component.annotation.ComponentNamed; import com.eucalyptus.context.Context; import com.eucalyptus.context.Contexts; import com.eucalyptus.entities.AbstractOwnedPersistent; import com.eucalyptus.entities.Entities; import com.eucalyptus.entities.TransactionException; import com.eucalyptus.records.Logs; import com.eucalyptus.util.Callback; import com.eucalyptus.util.Consumers; import com.eucalyptus.util.EucalyptusCloudException; import com.eucalyptus.util.Exceptions; import com.eucalyptus.util.FUtils; import com.eucalyptus.util.Numbers; import com.eucalyptus.auth.principal.OwnerFullName; import com.eucalyptus.util.RestrictedTypes; import com.eucalyptus.util.Strings; import com.eucalyptus.util.TypeMappers; import com.google.common.base.Functions; import com.google.common.base.MoreObjects; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.base.Splitter; import com.google.common.base.Supplier; import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; @SuppressWarnings( { "UnusedDeclaration", "Guava", "StaticPseudoFunctionalStyleMethod", "Convert2Lambda", "Convert2streamapi" } ) @ComponentNamed public class AutoScalingBackendService { private static final Logger logger = Logger.getLogger( AutoScalingBackendService.class ); private static final Set<String> reservedPrefixes = ImmutableSet.<String>builder().add("aws:").add("euca:").build(); private final LaunchConfigurations launchConfigurations; private final AutoScalingGroups autoScalingGroups; private final AutoScalingInstances autoScalingInstances; private final ScalingPolicies scalingPolicies; private final ActivityManager activityManager; @Inject public AutoScalingBackendService( final LaunchConfigurations launchConfigurations, final AutoScalingGroups autoScalingGroups, final AutoScalingInstances autoScalingInstances, final ScalingPolicies scalingPolicies, final ActivityManager activityManager ) { this.launchConfigurations = launchConfigurations; this.autoScalingGroups = autoScalingGroups; this.autoScalingInstances = autoScalingInstances; this.scalingPolicies = scalingPolicies; this.activityManager = activityManager; } public EnableMetricsCollectionResponseType enableMetricsCollection( final EnableMetricsCollectionType request ) throws EucalyptusCloudException { final EnableMetricsCollectionResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); try { final AccountFullName accountFullName = ctx.getUserFullName().asAccountFullName(); final Callback<AutoScalingGroup> groupCallback = new Callback<AutoScalingGroup>() { @Override public void fire( final AutoScalingGroup autoScalingGroup ) { if ( RestrictedTypes.filterPrivileged().apply( autoScalingGroup ) ) { final Set<MetricCollectionType> metricsToEnable = EnumSet.allOf( MetricCollectionType.class ); if ( request.getMetrics() != null && !request.getMetrics().getMember().isEmpty() ) { metricsToEnable.clear(); Iterables.addAll( metricsToEnable, Iterables.transform( request.getMetrics().getMember(), FUtils.valueOfFunction( MetricCollectionType.class ) ) ); } autoScalingGroup.getEnabledMetrics().addAll( metricsToEnable ); } } }; autoScalingGroups.update( accountFullName, request.getAutoScalingGroupName(), groupCallback ); } catch ( AutoScalingMetadataNotFoundException e ) { throw new ValidationErrorException( "Auto scaling group not found: " + request.getAutoScalingGroupName() ); } catch ( Exception e ) { handleException( e ); } return reply; } public ResumeProcessesResponseType resumeProcesses( final ResumeProcessesType request ) throws EucalyptusCloudException { final ResumeProcessesResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); try { final AccountFullName accountFullName = ctx.getUserFullName().asAccountFullName(); final Callback<AutoScalingGroup> groupCallback = new Callback<AutoScalingGroup>() { @Override public void fire( final AutoScalingGroup autoScalingGroup ) { if ( RestrictedTypes.filterPrivileged().apply( autoScalingGroup ) ) { final Set<ScalingProcessType> processesToResume = EnumSet.allOf( ScalingProcessType.class ); if ( request.getScalingProcesses() != null && !request.getScalingProcesses().getMember().isEmpty() ) { processesToResume.clear(); Iterables.addAll( processesToResume, Iterables.transform( request.getScalingProcesses().getMember(), FUtils.valueOfFunction(ScalingProcessType.class) ) ); } for ( final ScalingProcessType scalingProcessType : processesToResume ) { autoScalingGroup.getSuspendedProcesses().remove( SuspendedProcess.createManual( scalingProcessType ) ); } } } }; autoScalingGroups.update( accountFullName, request.getAutoScalingGroupName(), groupCallback ); } catch ( AutoScalingMetadataNotFoundException e ) { throw new ValidationErrorException( "Auto scaling group not found: " + request.getAutoScalingGroupName() ); } catch ( Exception e ) { handleException( e ); } return reply; } public DeleteLaunchConfigurationResponseType deleteLaunchConfiguration( final DeleteLaunchConfigurationType request ) throws EucalyptusCloudException { final DeleteLaunchConfigurationResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); try { final LaunchConfigurationMinimumView launchConfiguration = launchConfigurations.lookup( ctx.getUserFullName( ).asAccountFullName( ), request.getLaunchConfigurationName( ), TypeMappers.lookup( LaunchConfiguration.class, LaunchConfigurationMinimumView.class ) ); if ( RestrictedTypes.filterPrivileged().apply( launchConfiguration ) ) { launchConfigurations.delete( launchConfiguration ); } // else treat this as though the configuration does not exist } catch ( AutoScalingMetadataNotFoundException e ) { // so nothing to delete, move along } catch ( Exception e ) { handleException( e ); } return reply; } public DescribePoliciesResponseType describePolicies(final DescribePoliciesType request) throws EucalyptusCloudException { final DescribePoliciesResponseType reply = request.getReply( ); //TODO: MaxRecords / NextToken support for DescribePolicies final Context ctx = Contexts.lookup( ); final boolean isGroupResourceName = AutoScalingResourceName.isResourceName().apply( request.getAutoScalingGroupName() ); final boolean showAll = request.policyNames().remove( "verbose" ) || isGroupResourceName; final OwnerFullName ownerFullName = ctx.isAdministrator( ) && showAll ? null : ctx.getUserFullName( ).asAccountFullName( ); try { final Predicate<ScalingPolicy> requestedAndAccessible = Predicates.and( AutoScalingMetadatas.filterPrivilegesByIdOrArn( ScalingPolicy.class, request.policyNames() ), isGroupResourceName ? AutoScalingMetadatas.filterByProperty( AutoScalingResourceName.parse( request.getAutoScalingGroupName(), autoScalingGroup ).getUuid(), ScalingPolicies.toGroupUuid() ) : AutoScalingMetadatas.filterByProperty( request.getAutoScalingGroupName(), ScalingPolicies.toGroupName() ) ); final List<ScalingPolicyType> results = reply.getDescribePoliciesResult().getScalingPolicies().getMember(); results.addAll( scalingPolicies.list( ownerFullName, requestedAndAccessible, TypeMappers.lookup( ScalingPolicy.class, ScalingPolicyType.class ) ) ); final List<String> scalingPolicyArns = Lists.transform( results, ScalingPolicyType.policyArn() ); final Map<String,Collection<String>> policyArnToAlarmArns = activityManager.getAlarmsForPolicies( ctx.getUserFullName( ), scalingPolicyArns ); for ( final ScalingPolicyType scalingPolicyType : results ) { final Collection<String> alarmArns = policyArnToAlarmArns.get( scalingPolicyType.getPolicyARN() ); if ( alarmArns != null && !alarmArns.isEmpty() ) { scalingPolicyType.setAlarms( new Alarms( alarmArns ) ); } } } catch ( Exception e ) { handleException( e ); } return reply; } public CreateAutoScalingGroupResponseType createAutoScalingGroup( final CreateAutoScalingGroupType request ) throws EucalyptusCloudException { final CreateAutoScalingGroupResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); if ( request.getTags() != null ) { for ( final TagType tagType : request.getTags().getMember() ) { final String key = tagType.getKey(); if ( com.google.common.base.Strings.isNullOrEmpty( key ) || key.trim().length() > 128 || isReserved( key ) ) { throw new ValidationErrorException( "Invalid key (max length 128, must not be empty, reserved prefixes "+reservedPrefixes+"): "+key ); } } if ( request.getTags().getMember().size() > AutoScalingConfiguration.getMaxTags( ) ) { throw Exceptions.toUndeclared( new LimitExceededException("Tag limit exceeded") ); } } final Supplier<AutoScalingGroup> allocator = new Supplier<AutoScalingGroup>( ) { @Override public AutoScalingGroup get( ) { try { final Integer minSize = Numbers.intValue( request.getMinSize() ); final Integer maxSize = Numbers.intValue( request.getMaxSize() ); final Integer desiredCapacity = Numbers.intValue( request.getDesiredCapacity() ); if ( desiredCapacity != null && desiredCapacity < minSize ) { throw Exceptions.toUndeclared( new ValidationErrorException( "DesiredCapacity must not be less than MinSize" ) ); } if ( desiredCapacity != null && desiredCapacity > maxSize ) { throw Exceptions.toUndeclared( new ValidationErrorException( "DesiredCapacity must not be greater than MaxSize" ) ); } final Iterable<String> subnetIds = Splitter.on( ',' ).trimResults().omitEmptyStrings().split( nullToEmpty( request.getVpcZoneIdentifier() ) ); final AtomicReference<Map<String,String>> subnetsByZone = new AtomicReference<>( ); final List<String> referenceErrors = activityManager.validateReferences( ctx.getUserFullName(), Consumers.atomic( subnetsByZone ), request.availabilityZones(), request.loadBalancerNames(), subnetIds ); verifyUnsupportedReferences( referenceErrors, request.getPlacementGroup( ) ); if ( !referenceErrors.isEmpty() ) { throw Exceptions.toUndeclared( new ValidationErrorException( "Invalid parameters " + referenceErrors ) ); } final AccountFullName accountFullName = ctx.getUserFullName( ).asAccountFullName( ); final AutoScalingGroups.PersistingBuilder builder = autoScalingGroups.create( ctx.getUserFullName(), request.getAutoScalingGroupName(), verifyOwnership( accountFullName, launchConfigurations.lookup( accountFullName, request.getLaunchConfigurationName(), Functions.identity() ) ), minSize, maxSize ) .withAvailabilityZones( request.availabilityZones( ) != null && !request.availabilityZones( ).isEmpty( ) ? request.availabilityZones( ) : subnetsByZone.get( ) == null ? null : subnetsByZone.get( ).keySet( ) ) .withSubnetsByZone( subnetsByZone.get( ) ) .withDefaultCooldown( Numbers.intValue( request.getDefaultCooldown() ) ) .withDesiredCapacity( desiredCapacity ) .withHealthCheckGracePeriod( Numbers.intValue( request.getHealthCheckGracePeriod() ) ) .withHealthCheckType( request.getHealthCheckType()==null ? null : HealthCheckType.valueOf( request.getHealthCheckType() ) ) .withNewInstancesProtectedFromScaleIn( request.getNewInstancesProtectedFromScaleIn( ) ) .withLoadBalancerNames( request.loadBalancerNames() ) .withTerminationPolicyTypes( request.terminationPolicies() == null ? null : Collections2.filter( Collections2.transform( request.terminationPolicies(), FUtils.valueOfFunction( TerminationPolicyType.class ) ), Predicates.not( Predicates.isNull() ) ) ) .withTags( request.getTags() == null ? null : Iterables.transform( request.getTags().getMember(), TypeMappers.lookup( TagType.class, AutoScalingGroupTag.class ) ) ); return builder.persist(); } catch ( AutoScalingMetadataNotFoundException e ) { throw Exceptions.toUndeclared( new ValidationErrorException( "Launch configuration not found: " + request.getLaunchConfigurationName() ) ); } catch ( IllegalArgumentException e ) { throw Exceptions.toUndeclared( new ValidationErrorException( "Invalid health check type: " + request.getHealthCheckType() ) ); } catch ( Exception ex ) { throw new RuntimeException( ex ); } } }; try { RestrictedTypes.allocateUnitlessResource( allocator ); } catch ( Exception e ) { handleException( e, true ); } return reply; } public ExecutePolicyResponseType executePolicy(final ExecutePolicyType request) throws EucalyptusCloudException { final ExecutePolicyResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final AccountFullName accountFullName = ctx.getUserFullName( ).asAccountFullName( ); try { final ScalingPolicyView scalingPolicy; try { scalingPolicy = scalingPolicies.lookup( accountFullName, request.getAutoScalingGroupName(), request.getPolicyName(), TypeMappers.lookup( ScalingPolicy.class, ScalingPolicyView.class )); } catch ( AutoScalingMetadataNotFoundException e ) { throw new ValidationErrorException( "Scaling policy not found: " + request.getPolicyName() ); } final Callback<AutoScalingGroup> updateCallback = new Callback<AutoScalingGroup>( ) { @Override public void fire( final AutoScalingGroup autoScalingGroup ) { final boolean isCloudWatch = Contexts.lookup( ).isPrivileged( ); if ( isCloudWatch && !scalingProcessEnabled( ScalingProcessType.AlarmNotification, autoScalingGroup ) ) { logger.debug( "Ignoring policy execution due to alarm notification suspension" ); return; } failIfScaling( activityManager, autoScalingGroup ); final Integer desiredCapacity = scalingPolicy.getAdjustmentType().adjustCapacity( autoScalingGroup.getDesiredCapacity( ), scalingPolicy.getScalingAdjustment( ), MoreObjects.firstNonNull( scalingPolicy.getMinAdjustmentStep( ), 0 ), MoreObjects.firstNonNull( autoScalingGroup.getMinSize( ), 0 ), MoreObjects.firstNonNull( autoScalingGroup.getMaxSize( ), Integer.MAX_VALUE ) ); setDesiredCapacityWithCooldown( autoScalingGroup, request.getHonorCooldown(), scalingPolicy.getCooldown(), desiredCapacity, String.format( isCloudWatch ? "a CloudWatch alarm triggered policy %1$s changing the desired capacity from %2$d to %3$d" : "a user request executed policy %1$s changing the desired capacity from %2$d to %3$d", scalingPolicy.getDisplayName(), autoScalingGroup.getDesiredCapacity(), desiredCapacity ) ); } }; if ( RestrictedTypes.filterPrivileged( ).apply( scalingPolicy ) ) { autoScalingGroups.update( AccountFullName.getInstance( scalingPolicy.getOwnerAccountNumber( ) ), scalingPolicy.getAutoScalingGroupName( ), updateCallback ); } } catch( Exception e ) { handleException( e ); } return reply; } public DeleteTagsResponseType deleteTags( final DeleteTagsType request ) throws EucalyptusCloudException { final DeleteTagsResponseType reply = request.getReply( ); final Context context = Contexts.lookup(); final OwnerFullName ownerFullName = context.getUserFullName().asAccountFullName(); final List<TagType> tagTypes = MoreObjects.firstNonNull( request.getTags().getMember(), Collections.emptyList() ); for ( final TagType tagType : tagTypes ) { final String key = tagType.getKey(); if ( com.google.common.base.Strings.isNullOrEmpty( key ) || key.trim().length() > 128 || isReserved( key ) ) { throw new ValidationErrorException( "Invalid key (max length 128, must not be empty, reserved prefixes "+reservedPrefixes+"): "+key ); } } if ( tagTypes.size() > 0 ) { final Predicate<Void> delete = new Predicate<Void>(){ @Override public boolean apply( final Void v ) { for ( final TagType tagType : tagTypes ) { try { final TagSupport tagSupport = TagSupport.fromResourceType( tagType.getResourceType() ); final AutoScalingMetadata resource = tagSupport.lookup( ownerFullName, tagType.getResourceId() ); final Tag example = tagSupport.example( resource, ownerFullName, tagType.getKey(), tagType.getValue() ); if ( example != null && Permissions.isAuthorized( AutoScalingPolicySpec.VENDOR_AUTOSCALING, AutoScalingPolicySpec.AUTOSCALING_RESOURCE_TAG, example.getResourceType() + ":" + example.getResourceId() + ":" + example.getKey(), context.getAccount(), AutoScalingPolicySpec.AUTOSCALING_DELETETAGS, context.getAuthContext() ) ) { Tags.delete( example ); } } catch ( AutoScalingMetadataNotFoundException e ) { logger.debug( e, e ); } catch ( TransactionException e ) { throw Exceptions.toUndeclared(e); } } return true; } }; try { Entities.asTransaction( Tag.class, delete ).apply( null ); } catch ( Exception e ) { handleException( e ); } } return reply; } public PutScalingPolicyResponseType putScalingPolicy(final PutScalingPolicyType request) throws EucalyptusCloudException { final PutScalingPolicyResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final AccountFullName accountFullName = ctx.getUserFullName( ).asAccountFullName(); try { // Try update final ScalingPolicy scalingPolicy = scalingPolicies.update( accountFullName, request.getAutoScalingGroupName(), request.getPolicyName(), new Callback<ScalingPolicy>() { @Override public void fire( final ScalingPolicy scalingPolicy ) { if ( RestrictedTypes.filterPrivileged().apply( scalingPolicy ) ) { if ( request.getAdjustmentType() != null ) scalingPolicy.setAdjustmentType( FUtils.valueOfFunction( AdjustmentType.class ).apply( request.getAdjustmentType() ) ); if ( request.getScalingAdjustment() != null ) { if ( AdjustmentType.ExactCapacity == scalingPolicy.getAdjustmentType() && request.getScalingAdjustment() < 0 ) { throw Exceptions.toUndeclared( new ValidationErrorException( "ScalingAdjustment cannot be negative with the specified adjustment type." ) ); } scalingPolicy.setScalingAdjustment( request.getScalingAdjustment() ); } if ( request.getCooldown() != null ) scalingPolicy.setCooldown( request.getCooldown() ); if ( request.getMinAdjustmentStep() != null ) { if ( AdjustmentType.PercentChangeInCapacity != scalingPolicy.getAdjustmentType() ) { throw Exceptions.toUndeclared( new ValidationErrorException( "MinAdjustmentStep is not supported by the specified adjustment type." ) ); } scalingPolicy.setMinAdjustmentStep( request.getMinAdjustmentStep() ); } } } } ); reply.getPutScalingPolicyResult().setPolicyARN( scalingPolicy.getArn() ); } catch ( AutoScalingMetadataNotFoundException e ) { // Not found, create final Supplier<ScalingPolicy> allocator = new Supplier<ScalingPolicy>( ) { @Override public ScalingPolicy get( ) { try { final AdjustmentType adjustmentType = FUtils.valueOfFunction( AdjustmentType.class ).apply( request.getAdjustmentType() ); if ( request.getMinAdjustmentStep() != null && AdjustmentType.PercentChangeInCapacity != adjustmentType ) { throw Exceptions.toUndeclared( new ValidationErrorException( "MinAdjustmentStep is not supported by the specified adjustment type." ) ); } if ( AdjustmentType.ExactCapacity == adjustmentType && request.getScalingAdjustment() < 0 ) { throw Exceptions.toUndeclared( new ValidationErrorException( "ScalingAdjustment cannot be negative with the specified adjustment type." ) ); } final ScalingPolicies.PersistingBuilder builder = scalingPolicies.create( ctx.getUserFullName( ), verifyOwnership( accountFullName, autoScalingGroups.lookup( accountFullName, request.getAutoScalingGroupName(), Functions.identity() ) ), request.getPolicyName(), adjustmentType, request.getScalingAdjustment() ) .withCooldown( request.getCooldown() ) .withMinAdjustmentStep( request.getMinAdjustmentStep() ); return builder.persist(); } catch ( AutoScalingMetadataNotFoundException e ) { throw Exceptions.toUndeclared( new ValidationErrorException( "Auto scaling group not found: " + request.getAutoScalingGroupName() ) ); } catch ( IllegalArgumentException e ) { throw Exceptions.toUndeclared( new ValidationErrorException( "Invalid adjustment type: " + request.getAdjustmentType() ) ); } catch ( Exception ex ) { throw new RuntimeException( ex ); } } }; try { final ScalingPolicy scalingPolicy = RestrictedTypes.allocateUnitlessResource( allocator ); reply.getPutScalingPolicyResult().setPolicyARN( scalingPolicy.getArn() ); } catch ( Exception exception ) { handleException( exception, true ); } } catch ( Exception e ) { handleException( e ); } return reply; } public DeletePolicyResponseType deletePolicy(final DeletePolicyType request) throws EucalyptusCloudException { final DeletePolicyResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); try { final ScalingPolicyView scalingPolicy = scalingPolicies.lookup( ctx.getUserFullName( ).asAccountFullName( ), request.getAutoScalingGroupName(), request.getPolicyName( ), TypeMappers.lookup( ScalingPolicy.class, ScalingPolicyView.class ) ); if ( RestrictedTypes.filterPrivileged().apply( scalingPolicy ) ) { scalingPolicies.delete( scalingPolicy ); } // else treat this as though the configuration does not exist } catch ( AutoScalingMetadataNotFoundException e ) { // so nothing to delete, move along } catch ( Exception e ) { handleException( e ); } return reply; } public SetInstanceHealthResponseType setInstanceHealth(final SetInstanceHealthType request) throws EucalyptusCloudException { final SetInstanceHealthResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final OwnerFullName ownerFullName = ctx.isAdministrator( ) ? null : ctx.getUserFullName( ).asAccountFullName( ); try { final Callback<AutoScalingInstance> instanceUpdateCallback = new Callback<AutoScalingInstance>() { @Override public void fire( final AutoScalingInstance instance ) { if ( RestrictedTypes.filterPrivileged().apply( instance ) ) { if ( !MoreObjects.firstNonNull( request.getShouldRespectGracePeriod(), Boolean.FALSE ) || instance.healthStatusGracePeriodExpired() ) { instance.setHealthStatus( FUtils.valueOfFunction( HealthStatus.class ).apply( request.getHealthStatus( ) ) ); } } else { throw Exceptions.toUndeclared( new AutoScalingMetadataNotFoundException("Instance not found") ); } } }; autoScalingInstances.update( ownerFullName, request.getInstanceId(), instanceUpdateCallback ); } catch ( AutoScalingMetadataNotFoundException e ) { throw new ValidationErrorException( "Auto scaling instance not found: " + request.getInstanceId( ) ); } catch ( Exception e ) { handleException( e ); } return reply; } public CreateOrUpdateTagsResponseType createOrUpdateTags( final CreateOrUpdateTagsType request ) throws EucalyptusCloudException { final CreateOrUpdateTagsResponseType reply = request.getReply( ); final Context context = Contexts.lookup(); final UserFullName ownerFullName = context.getUserFullName(); final AccountFullName accountFullName = ownerFullName.asAccountFullName(); for ( final TagType tagType : request.getTags().getMember() ) { final String key = tagType.getKey(); final String value = nullToEmpty( tagType.getValue() ).trim(); if ( com.google.common.base.Strings.isNullOrEmpty( key ) || key.trim().length() > 128 || isReserved( key ) ) { throw new ValidationErrorException( "Invalid key (max length 128, must not be empty, reserved prefixes "+reservedPrefixes+"): "+key ); } if ( value.length() > 256 || isReserved( key ) ) { throw new ValidationErrorException( "Invalid value (max length 256, reserved prefixes "+reservedPrefixes+"): "+value ); } } if ( request.getTags().getMember().size() > 0 ) { final Predicate<Void> creator = new Predicate<Void>(){ @Override public boolean apply( final Void v ) { try { for ( final TagType tagType : request.getTags().getMember() ) { final TagSupport tagSupport = TagSupport.fromResourceType( tagType.getResourceType() ); AutoScalingMetadata resource = null; try { resource = tagSupport.lookup( accountFullName, tagType.getResourceId() ); } catch ( NoSuchElementException e ) { Logs.extreme().info( e, e ); } if ( resource == null || !RestrictedTypes.filterPrivileged().apply( resource ) ) { throw Exceptions.toUndeclared( new ValidationErrorException( "Resource not found " + tagType.getResourceId() ) ); } final String key = nullToEmpty( tagType.getKey() ).trim(); final String value = nullToEmpty( tagType.getValue() ).trim(); final Boolean propagateAtLaunch = MoreObjects.firstNonNull( tagType.getPropagateAtLaunch(), Boolean.FALSE ); tagSupport.createOrUpdate( resource, ownerFullName, key, value, propagateAtLaunch ); final Tag example = tagSupport.example( resource, accountFullName, null, null ); if ( Entities.count( example ) > AutoScalingConfiguration.getMaxTags( ) ) { throw Exceptions.toUndeclared( new LimitExceededException("Tag limit exceeded for resource '"+resource.getDisplayName()+"'") ); } } return true; } catch ( Exception e ) { throw Exceptions.toUndeclared( e ); } } }; try { Entities.asTransaction( Tag.class, creator ).apply( null ); } catch ( Exception e ) { handleException( e, true ); } } return reply; } public SuspendProcessesResponseType suspendProcesses( final SuspendProcessesType request ) throws EucalyptusCloudException { final SuspendProcessesResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); try { final AccountFullName accountFullName = ctx.getUserFullName().asAccountFullName(); final Callback<AutoScalingGroup> groupCallback = new Callback<AutoScalingGroup>() { @Override public void fire( final AutoScalingGroup autoScalingGroup ) { if ( RestrictedTypes.filterPrivileged().apply( autoScalingGroup ) ) { final boolean isAdminSuspension = ctx.isAdministrator( ) && !autoScalingGroup.getOwnerAccountNumber().equals( accountFullName.getAccountNumber() ); final Set<ScalingProcessType> processesToSuspend = EnumSet.allOf( ScalingProcessType.class ); if ( request.getScalingProcesses() != null && !request.getScalingProcesses().getMember().isEmpty() ) { processesToSuspend.clear(); Iterables.addAll( processesToSuspend, Iterables.transform( request.getScalingProcesses().getMember(), FUtils.valueOfFunction(ScalingProcessType.class) ) ); } for ( final ScalingProcessType scalingProcessType : processesToSuspend ) { if ( scalingProcessType.apply( autoScalingGroup ) ) { autoScalingGroup.getSuspendedProcesses().add( isAdminSuspension ? SuspendedProcess.createAdministrative( scalingProcessType ) : SuspendedProcess.createManual( scalingProcessType ) ); } } } } }; autoScalingGroups.update( accountFullName, request.getAutoScalingGroupName(), groupCallback ); } catch ( AutoScalingMetadataNotFoundException e ) { throw new ValidationErrorException( "Auto scaling group not found: " + request.getAutoScalingGroupName() ); } catch ( Exception e ) { handleException( e ); } return reply; } public CreateLaunchConfigurationResponseType createLaunchConfiguration( final CreateLaunchConfigurationType request ) throws EucalyptusCloudException { final CreateLaunchConfigurationResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final Supplier<LaunchConfiguration> allocator = new Supplier<LaunchConfiguration>( ) { @Override public LaunchConfiguration get( ) { try { final LaunchConfigurations.PersistingBuilder builder = launchConfigurations.create( ctx.getUserFullName(), request.getLaunchConfigurationName(), request.getImageId(), request.getInstanceType() ) .withKernelId( request.getKernelId() ) .withRamdiskId( request.getRamdiskId() ) .withKeyName( request.getKeyName() ) .withUserData( request.getUserData() ) .withInstanceMonitoring( request.getInstanceMonitoring() != null ? request.getInstanceMonitoring().getEnabled() : null ) .withInstanceProfile( request.getIamInstanceProfile() ) .withSecurityGroups( request.getSecurityGroups() != null ? request.getSecurityGroups().getMember() : null ) .withAssociatePublicIpAddress( request.getAssociatePublicIpAddress( ) ); if ( request.getBlockDeviceMappings() != null ) { for ( final BlockDeviceMappingType blockDeviceMappingType : request.getBlockDeviceMappings().getMember() ) { builder.withBlockDeviceMapping( blockDeviceMappingType.getDeviceName(), blockDeviceMappingType.getVirtualName(), blockDeviceMappingType.getEbs() != null ? blockDeviceMappingType.getEbs().getSnapshotId() : null, blockDeviceMappingType.getEbs() != null ? Numbers.intValue( blockDeviceMappingType.getEbs().getVolumeSize() ) : null ); } } final List<String> referenceErrors = activityManager.validateReferences( ctx.getUserFullName(), Iterables.filter( Lists.newArrayList( request.getImageId(), request.getKernelId(), request.getRamdiskId() ), Predicates.notNull() ), request.getInstanceType(), request.getKeyName(), request.getSecurityGroups() == null ? Collections.emptyList() : request.getSecurityGroups().getMember(), request.getIamInstanceProfile() ); if ( !referenceErrors.isEmpty() ) { throw Exceptions.toUndeclared( new ValidationErrorException( "Invalid parameters " + referenceErrors ) ); } return builder.persist(); } catch ( Exception ex ) { throw new RuntimeException( ex ); } } }; try { RestrictedTypes.allocateUnitlessResource( allocator ); } catch ( Exception e ) { handleException( e, true ); } return reply; } public DeleteAutoScalingGroupResponseType deleteAutoScalingGroup( final DeleteAutoScalingGroupType request ) throws EucalyptusCloudException { final DeleteAutoScalingGroupResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); try { final AutoScalingGroupCoreView group = autoScalingGroups.lookup( ctx.getUserFullName( ).asAccountFullName( ), request.getAutoScalingGroupName(), TypeMappers.lookup( AutoScalingGroup.class, AutoScalingGroupCoreView.class ) ); if ( RestrictedTypes.filterPrivileged().apply( group ) ) { // Terminate instances first if requested (but don't wait for success ...) if ( MoreObjects.firstNonNull( request.getForceDelete(), Boolean.FALSE ) ) { final List<String> instanceIds = autoScalingInstances.listByGroup( group, Predicates.alwaysTrue(), AutoScalingInstances.instanceId() ); if ( !instanceIds.isEmpty() ) { final List<ScalingActivity> activities = activityManager.terminateInstances( group, instanceIds ); if ( activities==null || activities.isEmpty() ) { throw new ScalingActivityInProgressException("Scaling activity in progress"); } autoScalingInstances.deleteByGroup( group ); } } else { failIfScaling( activityManager, group ); } autoScalingGroups.delete( group ); } // else treat this as though the group does not exist } catch ( AutoScalingMetadataNotFoundException e ) { // so nothing to delete, move along } catch ( Exception e ) { handleException( e ); } return reply; } public DisableMetricsCollectionResponseType disableMetricsCollection( final DisableMetricsCollectionType request ) throws EucalyptusCloudException { final DisableMetricsCollectionResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); try { final AccountFullName accountFullName = ctx.getUserFullName().asAccountFullName(); final Callback<AutoScalingGroup> groupCallback = new Callback<AutoScalingGroup>() { @Override public void fire( final AutoScalingGroup autoScalingGroup ) { if ( RestrictedTypes.filterPrivileged().apply( autoScalingGroup ) ) { final Set<MetricCollectionType> metricsToDisable = EnumSet.allOf( MetricCollectionType.class ); if ( request.getMetrics() != null && !request.getMetrics().getMember().isEmpty() ) { metricsToDisable.clear(); Iterables.addAll( metricsToDisable, Iterables.transform( request.getMetrics().getMember(), FUtils.valueOfFunction(MetricCollectionType.class) ) ); } autoScalingGroup.getEnabledMetrics().removeAll( metricsToDisable ); } } }; autoScalingGroups.update( accountFullName, request.getAutoScalingGroupName(), groupCallback ); } catch ( AutoScalingMetadataNotFoundException e ) { throw new ValidationErrorException( "Auto scaling group not found: " + request.getAutoScalingGroupName() ); } catch ( Exception e ) { handleException( e ); } return reply; } public UpdateAutoScalingGroupResponseType updateAutoScalingGroup( final UpdateAutoScalingGroupType request ) throws EucalyptusCloudException { final UpdateAutoScalingGroupResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); try { final AccountFullName accountFullName = ctx.getUserFullName().asAccountFullName(); final Callback<AutoScalingGroup> groupCallback = new Callback<AutoScalingGroup>() { @Override public void fire( final AutoScalingGroup autoScalingGroup ) { if ( RestrictedTypes.filterPrivileged().apply( autoScalingGroup ) ) { if ( request.getDefaultCooldown() != null ) autoScalingGroup.setDefaultCooldown( Numbers.intValue( request.getDefaultCooldown( ) ) ); if ( request.getHealthCheckGracePeriod( ) != null ) autoScalingGroup.setHealthCheckGracePeriod( Numbers.intValue( request.getHealthCheckGracePeriod( ) ) ); if ( request.getHealthCheckType( ) != null ) autoScalingGroup.setHealthCheckType( FUtils.valueOfFunction( HealthCheckType.class ).apply( request.getHealthCheckType( ) ) ); if ( request.getNewInstancesProtectedFromScaleIn( ) != null ) autoScalingGroup.setNewInstancesProtectedFromScaleIn( request.getNewInstancesProtectedFromScaleIn( ) ); if ( request.getLaunchConfigurationName( ) != null ) try { autoScalingGroup.setLaunchConfiguration( verifyOwnership( accountFullName, launchConfigurations.lookup( accountFullName, request.getLaunchConfigurationName(), Functions.identity() ) ) ); } catch ( AutoScalingMetadataNotFoundException e ) { throw Exceptions.toUndeclared( new ValidationErrorException( "Launch configuration not found: " + request.getLaunchConfigurationName() ) ); } catch ( AutoScalingMetadataException e ) { throw Exceptions.toUndeclared( e ); } if ( request.getMaxSize( ) != null ) autoScalingGroup.setMaxSize( Numbers.intValue( request.getMaxSize() ) ); if ( request.getMinSize( ) != null ) autoScalingGroup.setMinSize( Numbers.intValue( request.getMinSize( ) ) ); if ( request.terminationPolicies() != null && !request.terminationPolicies().isEmpty() ) autoScalingGroup.setTerminationPolicies( Lists.newArrayList( Sets.newLinkedHashSet( Iterables.filter( Iterables.transform( request.terminationPolicies( ), FUtils.valueOfFunction( TerminationPolicyType.class ) ), Predicates.not( Predicates.isNull( ) ) ) ) ) ); if ( request.getDesiredCapacity() != null || ( request.getDesiredCapacity() == null && autoScalingGroup.getDesiredCapacity() < autoScalingGroup.getMinSize( ) ) || ( request.getDesiredCapacity() == null && autoScalingGroup.getDesiredCapacity() > autoScalingGroup.getMaxSize( ) ) ) { Integer updatedDesiredCapacity = request.getDesiredCapacity() != null ? Numbers.intValue( request.getDesiredCapacity( ) ) : Math.min( Math.max( autoScalingGroup.getDesiredCapacity( ), autoScalingGroup.getMinSize( ) ), autoScalingGroup.getMaxSize( ) ); autoScalingGroup.updateDesiredCapacity( updatedDesiredCapacity, String.format("a user request update of AutoScalingGroup constraints to min: %1$d, max: %2$d, desired: %4$d changing the desired capacity from %3$d to %4$d", autoScalingGroup.getMinSize(), autoScalingGroup.getMaxSize(), autoScalingGroup.getDesiredCapacity(), updatedDesiredCapacity ) ); } if ( autoScalingGroup.getDesiredCapacity() < autoScalingGroup.getMinSize() ) { throw Exceptions.toUndeclared( new ValidationErrorException( "DesiredCapacity must not be less than MinSize" ) ); } if ( autoScalingGroup.getDesiredCapacity() > autoScalingGroup.getMaxSize() ) { throw Exceptions.toUndeclared( new ValidationErrorException( "DesiredCapacity must not be greater than MaxSize" ) ); } final Iterable<String> subnetIds = Splitter.on( ',' ).trimResults().omitEmptyStrings().split( nullToEmpty( request.getVpcZoneIdentifier() ) ); final AtomicReference<Map<String, String>> subnetsByZone = new AtomicReference<>(); final List<String> referenceErrors = activityManager.validateReferences( autoScalingGroup.getOwner(), Consumers.atomic( subnetsByZone ), request.availabilityZones( ), Collections.emptyList(), // load balancer names cannot be updated subnetIds ); verifyUnsupportedReferences( referenceErrors, request.getPlacementGroup() ); if ( !referenceErrors.isEmpty() ) { throw Exceptions.toUndeclared( new ValidationErrorException( "Invalid parameters " + referenceErrors ) ); } if ( request.getVpcZoneIdentifier() != null ) { autoScalingGroup.setSubnetIdByZone( subnetsByZone.get( ) ); autoScalingGroup.updateAvailabilityZones( Lists.newArrayList( Sets.newLinkedHashSet( request.availabilityZones( ) != null && !request.availabilityZones( ).isEmpty( ) ? request.availabilityZones( ) : subnetsByZone.get( ).keySet( ) ) ) ); } else if ( request.availabilityZones() != null && !request.availabilityZones().isEmpty() ) { autoScalingGroup.updateAvailabilityZones( Lists.newArrayList( Sets.newLinkedHashSet( request.availabilityZones() ) ) ); } } } }; autoScalingGroups.update( accountFullName, request.getAutoScalingGroupName(), groupCallback ); } catch ( AutoScalingMetadataNotFoundException e ) { throw new ValidationErrorException( "Auto scaling group not found: " + request.getAutoScalingGroupName() ); } catch ( Exception e ) { handleException( e ); } return reply; } public SetInstanceProtectionResponseType setInstanceProtection( SetInstanceProtectionType request) throws EucalyptusCloudException { final SetInstanceProtectionResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); try { final AccountFullName accountFullName = ctx.getUserFullName( ).asAccountFullName( ); final Callback<AutoScalingGroup> groupCallback = new Callback<AutoScalingGroup>() { @Override public void fire( final AutoScalingGroup autoScalingGroup ) { if ( RestrictedTypes.filterPrivileged().apply( autoScalingGroup ) ) { for ( final AutoScalingInstance instance : autoScalingGroup.getAutoScalingInstances( ) ) { if ( request.getInstanceIds( ).getMember( ).contains( instance.getInstanceId( ) ) ) { instance.setProtectedFromScaleIn( request.getProtectedFromScaleIn( ) ); } } } } }; autoScalingGroups.update( accountFullName, request.getAutoScalingGroupName( ), groupCallback ); } catch ( AutoScalingMetadataNotFoundException e ) { throw new ValidationErrorException( "Auto scaling group not found: " + request.getAutoScalingGroupName() ); } catch ( Exception e ) { handleException( e ); } return reply; } public SetDesiredCapacityResponseType setDesiredCapacity( final SetDesiredCapacityType request ) throws EucalyptusCloudException { final SetDesiredCapacityResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); try { final Callback<AutoScalingGroup> groupCallback = new Callback<AutoScalingGroup>() { @Override public void fire( final AutoScalingGroup autoScalingGroup ) { if ( RestrictedTypes.filterPrivileged().apply( autoScalingGroup ) ) { failIfScaling( activityManager, autoScalingGroup ); final Integer desiredCapacity = Numbers.intValue( request.getDesiredCapacity( ) ); if ( desiredCapacity < autoScalingGroup.getMinSize( ) ) { throw Exceptions.toUndeclared( new ValidationErrorException( String.format( "New SetDesiredCapacity value %d is below min value %d for the AutoScalingGroup.", desiredCapacity, autoScalingGroup.getMinSize( ) ) ) ); } if ( desiredCapacity > autoScalingGroup.getMaxSize( ) ) { throw Exceptions.toUndeclared( new ValidationErrorException( String.format( "New SetDesiredCapacity value %d is above max value %d for the AutoScalingGroup.", desiredCapacity, autoScalingGroup.getMaxSize( ) ) ) ); } setDesiredCapacityWithCooldown( autoScalingGroup, request.getHonorCooldown(), null, desiredCapacity, String.format( "a user request explicitly set group desired capacity changing the desired capacity from %1$d to %2$d", autoScalingGroup.getDesiredCapacity(), desiredCapacity ) ); } } }; autoScalingGroups.update( ctx.getUserFullName().asAccountFullName(), request.getAutoScalingGroupName(), groupCallback); } catch ( AutoScalingMetadataNotFoundException e ) { throw new ValidationErrorException( "Auto scaling group not found: " + request.getAutoScalingGroupName() ); } catch ( Exception e ) { handleException( e ); } return reply; } public TerminateInstanceInAutoScalingGroupResponseType terminateInstanceInAutoScalingGroup( final TerminateInstanceInAutoScalingGroupType request ) throws EucalyptusCloudException { final TerminateInstanceInAutoScalingGroupResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final OwnerFullName ownerFullName = ctx.isAdministrator( ) ? null : ctx.getUserFullName( ).asAccountFullName( ); try { final AutoScalingInstanceGroupView instance = autoScalingInstances.lookup( ownerFullName, request.getInstanceId(), TypeMappers.lookup( AutoScalingInstance.class, AutoScalingInstanceGroupView.class )); if ( !RestrictedTypes.filterPrivileged().apply( instance ) ) { throw new AutoScalingMetadataNotFoundException("Instance not found"); } final List<ScalingActivity> activities = activityManager.terminateInstances( instance.getAutoScalingGroup(), Collections.singletonList( instance.getInstanceId() ) ); if ( activities == null || activities.isEmpty() ) { throw new ScalingActivityInProgressException("Scaling activity in progress"); } reply.getTerminateInstanceInAutoScalingGroupResult().setActivity( TypeMappers.transform( activities.get( 0 ), Activity.class ) ); final String groupArn = instance.getAutoScalingGroup().getArn(); final Callback<AutoScalingGroup> groupCallback = new Callback<AutoScalingGroup>() { @Override public void fire( final AutoScalingGroup autoScalingGroup ) { if ( MoreObjects.firstNonNull( request.getShouldDecrementDesiredCapacity(), Boolean.FALSE ) ) { autoScalingGroup.setDesiredCapacity( autoScalingGroup.getDesiredCapacity() - 1 ); } else { autoScalingGroup.setScalingRequired( true ); } } }; autoScalingGroups.update( ctx.getUserFullName().asAccountFullName(), groupArn, groupCallback); } catch ( AutoScalingMetadataNotFoundException e ) { throw new ValidationErrorException( "Auto scaling instance not found: " + request.getInstanceId( ) ); } catch ( Exception e ) { handleException( e ); } return reply; } private static void failIfScaling( final ActivityManager activityManager, final AutoScalingGroupMetadata group ) { if ( activityManager.scalingInProgress( group ) ) { throw Exceptions.toUndeclared( new ScalingActivityInProgressException("Scaling activity in progress") ); } } private static boolean scalingProcessEnabled( final ScalingProcessType type, final AutoScalingGroup group ) { return !AutoScalingConfiguration.getSuspendedProcesses().contains( type ) && type.apply( group ); } private static void setDesiredCapacityWithCooldown( final AutoScalingGroup autoScalingGroup, final Boolean honorCooldown, final Integer cooldown, final int capacity, final String reason ) { final long cooldownMs = TimeUnit.SECONDS.toMillis( MoreObjects.firstNonNull( cooldown, autoScalingGroup.getDefaultCooldown() ) ); if ( !MoreObjects.firstNonNull( honorCooldown, Boolean.FALSE ) || ( System.currentTimeMillis() - autoScalingGroup.getCapacityTimestamp().getTime() ) > cooldownMs ) { autoScalingGroup.updateDesiredCapacity( capacity, reason ); autoScalingGroup.setCapacityTimestamp( new Date() ); } else { throw Exceptions.toUndeclared( new InternalFailureException("Group is in cooldown") ); } } private static void verifyUnsupportedReferences( final List<String> referenceErrors, final String placementGroup ) { if ( !com.google.common.base.Strings.isNullOrEmpty( placementGroup ) ) { referenceErrors.add( "Invalid placement group: " + placementGroup ); } } private static <AOP extends AbstractOwnedPersistent> AOP verifyOwnership( final OwnerFullName ownerFullName, final AOP aop ) throws AutoScalingMetadataNotFoundException { if ( !AutoScalingMetadatas.filterByOwner( ownerFullName ).apply( aop ) ) { throw new AutoScalingMetadataNotFoundException( "Not found: " + aop.getDisplayName( ) ); } return aop; } private static boolean isReserved( final String text ) { return !Contexts.lookup( ).isPrivileged( ) && Iterables.any( reservedPrefixes, Strings.isPrefixOf( text ) ); } private static void handleException( final Exception e ) throws AutoScalingException { handleException( e, false ); } private static void handleException( final Exception e, final boolean isCreate ) throws AutoScalingException { final AutoScalingException cause = Exceptions.findCause( e, AutoScalingException.class ); if ( cause != null ) { throw cause; } final AuthQuotaException quotaCause = Exceptions.findCause( e, AuthQuotaException.class ); if ( quotaCause != null ) { throw new LimitExceededException( "Request would exceed quota for type: " + quotaCause.getType() ); } final ConstraintViolationException constraintViolationException = Exceptions.findCause( e, ConstraintViolationException.class ); if ( constraintViolationException != null ) { throw isCreate ? new AlreadyExistsException( "Resource already exists" ): new ResourceInUseException( "Resource in use" ); } final InvalidResourceNameException invalidResourceNameException = Exceptions.findCause( e, InvalidResourceNameException.class ); if ( invalidResourceNameException != null ) { throw new ValidationErrorException( invalidResourceNameException.getMessage() ); } logger.error( e, e ); final InternalFailureException exception = new InternalFailureException( String.valueOf(e.getMessage()) ); if ( Contexts.lookup( ).hasAdministrativePrivileges() ) { exception.initCause( e ); } throw exception; } }