/************************************************************************* * (c) Copyright 2017 Hewlett Packard Enterprise Development Company LP * * 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/. ************************************************************************/ package com.eucalyptus.cluster.service.fake; import java.util.List; import java.util.NoSuchElementException; import java.util.concurrent.CancellationException; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import javax.annotation.Nullable; import org.apache.log4j.Logger; import com.eucalyptus.bootstrap.Bootstrap; import com.eucalyptus.bootstrap.Databases; import com.eucalyptus.bootstrap.Hosts; import com.eucalyptus.cluster.common.internal.Cluster; import com.eucalyptus.cluster.common.internal.ClusterRegistry; import com.eucalyptus.cluster.common.internal.spi.ClusterProvider; import com.eucalyptus.cluster.common.msgs.NodeType; import com.eucalyptus.cluster.proxy.node.Nodes; import com.eucalyptus.component.Partition; import com.eucalyptus.component.Partitions; import com.eucalyptus.component.ServiceConfiguration; import com.eucalyptus.component.ServiceRegistrationException; import com.eucalyptus.component.Topology; import com.eucalyptus.component.id.Eucalyptus; import com.eucalyptus.event.ClockTick; import com.eucalyptus.event.EventListener; import com.eucalyptus.event.Listeners; import com.eucalyptus.records.Logs; import com.eucalyptus.util.Exceptions; import com.eucalyptus.util.TypeMapper; import com.eucalyptus.util.async.AsyncRequests; import com.eucalyptus.util.async.SubjectMessageCallback; import com.google.common.base.Function; import edu.ucsb.eucalyptus.msgs.BaseMessage; /** * */ @SuppressWarnings( { "WeakerAccess", "unused" } ) public class FakeClusterProvider implements ClusterProvider { private static Logger LOG = Logger.getLogger( FakeClusterProvider.class ); private Cluster cluster; private final ClusterRegistry registry; private final FakeClusterConfiguration configuration; public FakeClusterProvider( final ClusterRegistry registry, final FakeClusterConfiguration configuration ) { this.configuration = configuration; this.registry = registry; } @Override public String getName( ) { return configuration.getName( ); } @Override public String getPartition() { return configuration.getPartition( ); } @Override public String getHostName( ) { return configuration.getHostName( ); } @Override public Partition lookupPartition( ) { return Partitions.lookup( configuration ); } @Override public ServiceConfiguration getConfiguration( ) { return configuration; } @Override public void init( final Cluster cluster ) { this.cluster = cluster; } @Override public void check( ) { //TODO: fail check on errors from refresh callbacks? } @Override public void start( ) throws ServiceRegistrationException { disable( ); FakeClusterState.reload( ); } @Override public void stop( ) throws ServiceRegistrationException { registry.deregister( cluster.getName( ) ); } @Override public void enable( ) throws ServiceRegistrationException { registry.register( cluster ); } @Override public void disable( ) throws ServiceRegistrationException { registry.registerDisabled( cluster ); } @Override public void refreshResources( ) { Refresh.RESOURCES.accept( cluster ); } @Override public void updateNodeInfo( final List<NodeType> nodes ) { Nodes.updateNodeInfo( getConfiguration(), nodes ); } @Override public boolean hasNode( final String sourceHost ) { try { Nodes.lookup( getConfiguration( ), sourceHost ); return true; } catch ( NoSuchElementException e ) { return false; } } @Override public void cleanup( final Cluster cluster, final Exception ex ) { Nodes.clusterCleanup( cluster, ex ); } private enum Refresh implements Consumer<Cluster> { RESOURCES( "com.eucalyptus.cluster.callback.ResourceStateCallback" ), //TODO: revert back to class references INSTANCES( "com.eucalyptus.cluster.callback.VmStateCallback" ), ; Class<? extends SubjectMessageCallback<Cluster,?,?>> refresh; Refresh( final String refresh ) { try { //noinspection unchecked this.refresh = (Class<? extends SubjectMessageCallback<Cluster,?,?>> ) Class.forName( refresh ); } catch ( ClassNotFoundException e ) { throw Exceptions.toUndeclared( e ); } } public void accept( final Cluster cluster ) { try { final SubjectMessageCallback<Cluster,?,?> messageCallback = refresh.newInstance( ); messageCallback.setSubject( cluster ); BaseMessage baseMessage = AsyncRequests.newRequest( messageCallback ).sendSync( cluster.getConfiguration( ) ); if ( Logs.extreme( ).isDebugEnabled( ) ) { Logs.extreme( ).debug( "Response to " + messageCallback + ": " + baseMessage ); } } catch ( CancellationException ex ) { //do nothing } catch ( Exception ex ) { LOG.error( ex ); Logs.extreme( ).error( ex ); throw Exceptions.toUndeclared( ex ); } } @Override public String toString( ) { return this.name( ) + ":" + this.refresh.getSimpleName( ); } } public static class FakeClusterRefreshEventListener implements EventListener<ClockTick> { private final AtomicInteger refreshOrdinal = new AtomicInteger( 0 ); public static void register( ) { Listeners.register( ClockTick.class, new FakeClusterRefreshEventListener( ) ); } @Override public void fireEvent( final ClockTick event ) { if ( Topology.isEnabledLocally( Eucalyptus.class ) && Hosts.isCoordinator( ) && Bootstrap.isOperational( ) && !Databases.isVolatile( ) ) { final Refresh refresh = Refresh.values( )[ refreshOrdinal.getAndIncrement( ) % Refresh.values( ).length ]; for ( final Cluster cluster : ClusterRegistry.getInstance( ).listValues( ) ) { final ServiceConfiguration configuration = cluster.getConfiguration( ); if ( configuration instanceof FakeClusterConfiguration ) { refresh.accept( cluster ); // TODO: threading } } } } } @TypeMapper public enum FakeClusterConfigurationToClusterSpi implements Function<FakeClusterConfiguration,ClusterProvider> { @SuppressWarnings( "unused" ) INSTANCE; @Nullable @Override public ClusterProvider apply( @Nullable final FakeClusterConfiguration configuration ) { return new FakeClusterProvider( ClusterRegistry.getInstance( ), configuration ); } } }