/*************************************************************************
* Copyright 2009-2013 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.common.internal.instances;
import static com.eucalyptus.autoscaling.common.AutoScalingMetadata.AutoScalingGroupMetadata;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Property;
import org.hibernate.criterion.Restrictions;
import com.eucalyptus.autoscaling.common.internal.metadata.AbstractOwnedPersistents;
import com.eucalyptus.autoscaling.common.internal.metadata.AutoScalingMetadataException;
import com.eucalyptus.autoscaling.common.internal.metadata.AutoScalingMetadataNotFoundException;
import com.eucalyptus.component.annotation.ComponentNamed;
import com.eucalyptus.util.Callback;
import com.eucalyptus.util.CollectionUtils;
import com.eucalyptus.auth.principal.OwnerFullName;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
/**
*
*/
@ComponentNamed
public class PersistenceAutoScalingInstances extends AutoScalingInstances {
private final Logger logger = Logger.getLogger( PersistenceAutoScalingInstances.class ) ;
private PersistenceSupport persistenceSupport = new PersistenceSupport();
@Override
public <T> List<T> list( final OwnerFullName ownerFullName,
final Predicate<? super AutoScalingInstance> filter,
final Function<? super AutoScalingInstance,T> transform ) throws AutoScalingMetadataException {
return persistenceSupport.list( ownerFullName, filter, transform );
}
@Override
public <T> List<T> listByGroup( final OwnerFullName ownerFullName,
final String groupName,
final Function<? super AutoScalingInstance,T> transform ) throws AutoScalingMetadataException {
final AutoScalingInstance example = AutoScalingInstance.withOwner( ownerFullName );
example.setAutoScalingGroupName( groupName );
return persistenceSupport.listByExample( example, Predicates.alwaysTrue(), transform );
}
@Override
public <T> List<T> listByGroup( final AutoScalingGroupMetadata group,
final Predicate<? super AutoScalingInstance> filter,
final Function<? super AutoScalingInstance,T> transform ) throws AutoScalingMetadataException {
final AutoScalingInstance example = exampleForGroup( group );
return persistenceSupport.listByExample( example, filter, transform );
}
@Override
public <T> List<T> listByState( final LifecycleState lifecycleState,
final ConfigurationState configurationState,
final Function<? super AutoScalingInstance,T> transform ) throws AutoScalingMetadataException {
final AutoScalingInstance example = AutoScalingInstance.withStates( lifecycleState, configurationState );
return persistenceSupport.listByExample( example, Predicates.and( lifecycleState, configurationState ), transform );
}
@Override
public <T> List<T> listUnhealthyByGroup( final AutoScalingGroupMetadata group,
final Function<? super AutoScalingInstance,T> transform ) throws AutoScalingMetadataException {
final AutoScalingInstance example = exampleForGroup( group );
example.setHealthStatus( HealthStatus.Unhealthy );
return persistenceSupport.listByExample( example, Predicates.alwaysTrue(), transform );
}
@Override
public <T> T lookup( final OwnerFullName ownerFullName,
final String instanceId,
final Function<? super AutoScalingInstance,T> transform ) throws AutoScalingMetadataException {
return persistenceSupport.lookupByExample(
persistenceSupport.exampleWithName( ownerFullName, instanceId ),
ownerFullName,
instanceId,
Predicates.alwaysTrue(),
transform );
}
@Override
public void update( final OwnerFullName ownerFullName,
final String instanceId,
final Callback<AutoScalingInstance> instanceUpdateCallback ) throws AutoScalingMetadataException {
persistenceSupport.updateByExample(
persistenceSupport.exampleWithName( ownerFullName, instanceId ),
ownerFullName,
instanceId,
instanceUpdateCallback );
}
@Override
public void markMissingInstancesUnhealthy( final AutoScalingGroupMetadata group,
final Collection<String> instanceIds ) throws AutoScalingMetadataException {
final AutoScalingInstance example = exampleForGroup( group );
example.setHealthStatus( HealthStatus.Healthy );
final List<AutoScalingInstance> instancesToMark = persistenceSupport.listByExample(
example,
LifecycleState.InService,
instanceIds.isEmpty() ?
Restrictions.conjunction() :
Restrictions.not( Property.forName( "displayName" ).in( instanceIds ) ),
Collections.<String,String>emptyMap(),
Functions.<AutoScalingInstance>identity() );
for ( final AutoScalingInstance instance : instancesToMark ) {
try {
persistenceSupport.updateByExample(
AutoScalingInstance.withUuid( instance.getNaturalId() ),
group.getOwner(),
instance.getInstanceId(),
new Callback<AutoScalingInstance>(){
@Override
public void fire( final AutoScalingInstance instance ) {
if ( instance.healthStatusGracePeriodExpired() ) {
logger.info( "Marking instance unhealthy: " + instance.getInstanceId() );
instance.setHealthStatus( HealthStatus.Unhealthy );
} else {
logger.debug( "Instance not healthy but within grace period: " + instance.getInstanceId() );
}
}
});
} catch ( final AutoScalingMetadataNotFoundException e ) {
// removed, no need to mark unhealthy
}
}
}
@Override
public void markExpiredPendingUnhealthy( final AutoScalingGroupMetadata group,
final Collection<String> instanceIds,
final long maxAge ) throws AutoScalingMetadataException {
final AutoScalingInstance example = exampleForGroup( group );
example.setHealthStatus( HealthStatus.Healthy );
final List<AutoScalingInstance> instancesToMark = instanceIds.isEmpty() ?
Collections.<AutoScalingInstance>emptyList() :
persistenceSupport.listByExample(
example,
LifecycleState.Pending,
Property.forName( "displayName" ).in( instanceIds ),
Collections.<String,String>emptyMap(),
Functions.<AutoScalingInstance>identity() );
for ( final AutoScalingInstance instance : instancesToMark ) {
try {
persistenceSupport.updateByExample(
AutoScalingInstance.withUuid( instance.getNaturalId() ),
group.getOwner(),
instance.getInstanceId(),
new Callback<AutoScalingInstance>(){
@Override
public void fire( final AutoScalingInstance instance ) {
if ( instance.getCreationTimestamp().getTime() < maxAge ) {
logger.info( "Marking pending instance unhealthy: " + instance.getInstanceId() );
instance.setHealthStatus( HealthStatus.Unhealthy );
} else {
logger.debug( "Not marking pending instance unhealthy (within timeout): " + instance.getInstanceId() );
}
}
});
} catch ( final AutoScalingMetadataNotFoundException e ) {
// removed, no need to mark unhealthy
}
}
}
@Override
public Set<String> verifyInstanceIds( final String accountNumber,
final Collection<String> instanceIds ) throws AutoScalingMetadataException {
final Set<String> verifiedInstanceIds = Sets.newHashSet();
if ( !instanceIds.isEmpty() ) {
final AutoScalingInstance example = AutoScalingInstance.withOwner( accountNumber );
final Criterion idCriterion = Property.forName( "displayName" ).in( instanceIds );
Iterables.addAll(
verifiedInstanceIds,
persistenceSupport.listByExample(
example,
Predicates.alwaysTrue(),
idCriterion,
Collections.<String, String>emptyMap(),
AutoScalingInstances.instanceId() ) );
}
return verifiedInstanceIds;
}
@Override
public void transitionState( final AutoScalingGroupMetadata group,
final LifecycleState from,
final LifecycleState to,
final Collection<String> instanceIds ) throws AutoScalingMetadataException {
final AutoScalingInstance example = exampleForGroup( group );
example.setLifecycleState( from );
updateInstances( example, from, from.transitionTo( to ), instanceIds );
}
@Override
public void transitionConfigurationState( final AutoScalingGroupMetadata group,
final ConfigurationState from,
final ConfigurationState to,
final Collection<String> instanceIds ) throws AutoScalingMetadataException {
final AutoScalingInstance example = exampleForGroup( group );
example.setConfigurationState( from );
updateInstances( example, from, from.transitionTo( to ), instanceIds );
}
@Override
public int registrationFailure( final AutoScalingGroupMetadata group,
final Collection<String> instanceIds ) throws AutoScalingMetadataException {
final AutoScalingInstance example = exampleForGroup( group );
final Map<String,Integer> failureCountMap = Maps.newHashMap();
updateInstances( example, Predicates.alwaysTrue(), new Predicate<AutoScalingInstance>() {
@Override
public boolean apply( final AutoScalingInstance instance ) {
failureCountMap.put( instance.getInstanceId(), instance.incrementRegistrationAttempts() );
return true;
}
}, instanceIds );
return CollectionUtils.reduce( failureCountMap.values(), Integer.MAX_VALUE, CollectionUtils.min() );
}
@Override
public boolean delete( final OwnerFullName ownerFullName,
final String instanceId ) throws AutoScalingMetadataException {
return !persistenceSupport.withRetries( ).deleteByExample(
persistenceSupport.exampleWithName( ownerFullName, instanceId )
).isEmpty( );
}
@Override
public boolean deleteByGroup( final AutoScalingGroupMetadata group ) throws AutoScalingMetadataException {
final AutoScalingInstance example = exampleForGroup( group );
return !persistenceSupport.withRetries( ).deleteByExample( example ).isEmpty( );
}
@Override
public AutoScalingInstance save( final AutoScalingInstance autoScalingInstance ) throws AutoScalingMetadataException {
return persistenceSupport.save( autoScalingInstance );
}
private AutoScalingInstance exampleForGroup( final AutoScalingGroupMetadata group ) {
final AutoScalingInstance example = AutoScalingInstance.withOwner( group.getOwner() );
example.clearUserIdentity();
example.setAutoScalingGroupName( group.getDisplayName() );
return example;
}
private void updateInstances( final AutoScalingInstance fromExample,
final Predicate<? super AutoScalingInstance> fromPredicate,
final Predicate<? super AutoScalingInstance> updatePredicate,
final Collection<String> instanceIds ) throws AutoScalingMetadataException {
final AbstractOwnedPersistents.WorkCallback<Void> updateCallback = new AbstractOwnedPersistents.WorkCallback<Void>() {
@Override
public Void doWork() throws AutoScalingMetadataException {
final List<AutoScalingInstance> instances = persistenceSupport.listByExample(
fromExample,
fromPredicate,
Property.forName( "displayName" ).in( instanceIds ),
Collections.<String, String>emptyMap(),
Functions.<AutoScalingInstance>identity() );
CollectionUtils.each(
instances,
updatePredicate
);
return null;
}
};
if ( !instanceIds.isEmpty( ) ) {
persistenceSupport.transactionWithRetry( AutoScalingInstance.class, updateCallback );
}
}
private static class PersistenceSupport extends AbstractOwnedPersistents<AutoScalingInstance> {
private PersistenceSupport() {
super( "auto scaling instance" );
}
@Override
protected AutoScalingInstance exampleWithOwner( final OwnerFullName ownerFullName ) {
return AutoScalingInstance.withOwner( ownerFullName );
}
@Override
protected AutoScalingInstance exampleWithName( final OwnerFullName ownerFullName, final String name ) {
return AutoScalingInstance.named( ownerFullName, name );
}
}
}