/************************************************************************* * 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.activities; import java.util.Map; import java.util.Set; import org.apache.log4j.Logger; import com.eucalyptus.cluster.common.ClusterController; import com.eucalyptus.component.Partition; import com.eucalyptus.component.Partitions; import com.eucalyptus.component.Topology; import com.eucalyptus.empyrean.DescribeServicesResponseType; import com.eucalyptus.empyrean.DescribeServicesType; import com.eucalyptus.empyrean.Empyrean; import com.eucalyptus.empyrean.EmpyreanMessage; import com.eucalyptus.empyrean.ServiceStatusType; import com.eucalyptus.records.Logs; import com.eucalyptus.util.Callback; import com.eucalyptus.util.DispatchingClient; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.common.collect.Sets; /** * Monitors availability zones for failure detection. */ class ZoneMonitor { private static Logger logger = Logger.getLogger( ZoneMonitor.class ); private static final Map<String,ZoneFailureInfo> failureMap = Maps.newHashMap(); private static final Object failureMapSync = new Object(); /** * Get the set of availability zones that have failed for the given duration. * * @param duration The duration in milliseconds * @return The zones that have been unavailable for the duration */ public Set<String> getUnavailableZones( final long duration ) { synchronized ( failureMapSync ) { return Sets.newHashSet( Iterables.transform( Iterables.filter( failureMap.values(), unavailableSince( System.currentTimeMillis() - duration ) ), ZoneName.INSTANCE ) ); } } public static void checkZones() { try { final DescribeServicesType describeServices = new DescribeServicesType(); describeServices.setByServiceType( "cluster" ); final DispatchingClient<EmpyreanMessage,Empyrean> client = new DispatchingClient<EmpyreanMessage,Empyrean>( Empyrean.class ); client.init(); client.dispatch( describeServices, ZoneCallback.INSTANCE ); } catch ( DispatchingClient.DispatchingClientException e ) { logWarning( "Error describing zones", e ); } catch ( Exception e ) { logger.error( e, e ); } } private static void checkAvailabilityForZones( final Set<String> zones ) { for ( final String zone : zones ) { final boolean available = ZoneAvailableFilter.INSTANCE.apply( zone ); synchronized ( failureMapSync ) { if ( available ) { failureMap.remove( zone ); } else { final ZoneFailureInfo info = failureMap.get( zone ); if ( info == null ) { failureMap.put( zone, new ZoneFailureInfo( zone ) ); } } } } } private static Predicate<ZoneFailureInfo> unavailableSince( final long since ) { return new Predicate<ZoneFailureInfo>() { @Override public boolean apply( final ZoneFailureInfo zoneFailureInfo ) { return zoneFailureInfo.since < since; } }; } private static void logWarning( final String description, final Throwable t ) { final String message = description + ": " + t.getMessage(); logger.warn( message ); Logs.extreme().warn( message, t ); } private enum ZoneAvailableFilter implements Predicate<String> { INSTANCE; @Override public boolean apply( final String zone ) { boolean enabled = false; final Partition partition = Partitions.lookupByName( zone ); if ( partition != null ) try { Topology.lookup( ClusterController.class, partition ); //TODO:STEVE: should should be describe-availability-zones? (check zoneState?) enabled = true; } catch ( Exception e ) { Logs.exhaust().info( "Lookup failed for zone: " + zone, e ); } return enabled; } } private enum ZoneName implements Function<ZoneFailureInfo,String> { INSTANCE; @Override public String apply( final ZoneFailureInfo info ) { return info.name; } } private enum ZoneCallback implements Callback.Checked<DescribeServicesResponseType> { INSTANCE; @Override public void fireException( final Throwable t ) { logWarning( "Error describing zones", t ); } @Override public void fire( final DescribeServicesResponseType zonesResponse ) { final Set<String> zones = Sets.newHashSet(); if ( zonesResponse.getServiceStatuses() != null ) { for ( final ServiceStatusType serviceStatus : zonesResponse.getServiceStatuses() ) { if ( serviceStatus != null && serviceStatus.getServiceId() != null && serviceStatus.getServiceId().getPartition() != null ) { zones.add( serviceStatus.getServiceId().getPartition() ); } } } checkAvailabilityForZones( zones ); } } private static final class ZoneFailureInfo { private final String name; private final long since; private ZoneFailureInfo( final String name ) { this.name = name; this.since = System.currentTimeMillis(); } } }