package com.eucalyptus.bootstrap; import java.io.File; import java.util.Arrays; import java.util.List; import org.apache.log4j.Logger; import com.eucalyptus.bootstrap.transitions.LoadConfigs; import com.eucalyptus.component.Component; import com.eucalyptus.component.Components; import com.eucalyptus.component.Lifecycles; import com.eucalyptus.component.Resource; import com.eucalyptus.component.ServiceRegistrationException; import com.eucalyptus.records.EventRecord; import com.eucalyptus.records.EventType; import com.eucalyptus.system.Ats; import com.eucalyptus.system.BaseDirectory; import com.eucalyptus.util.Committor; import com.eucalyptus.util.Exceptions; import com.eucalyptus.util.LogUtil; import com.eucalyptus.util.Transition; import com.google.common.collect.Lists; public class Bootstrap { private static Logger LOG = Logger.getLogger( Bootstrap.class ); public enum Stage { SystemInit, PrivilegedConfiguration, UnprivilegedConfiguration, SystemCredentialsInit, /* <-- this means system credentials, not user. */ RemoteConfiguration, DatabaseInit, PersistenceContextInit, DeferredClassInit, RemoteServicesInit, UserCredentialsInit, CloudServiceInit, Verification, Anonymous, Final; public static List<Stage> list( ) { return Arrays.asList( Stage.values( ) ); } public <A> Transition<A, Bootstrap.Stage> to( final Bootstrap.Stage s, final Committor<A> c ) throws Exception { return ( Transition<A, Bootstrap.Stage> ) Transition.anonymous( this, s, c ); } private List<Bootstrapper> bootstrappers = Lists.newArrayList( ); private List<Resource> resources = Lists.newArrayList( ); public <A, B extends Comparable<? extends Comparable<?>>> Transition<A,Comparable<? extends Comparable<?>>> to( B s, Committor<A> c ) throws Exception { return ( Transition<A,Comparable<? extends Comparable<?>>> ) Transition.anonymous( this, s, c ); } public List<Bootstrapper> getBootstrappers( ) { return this.bootstrappers; } public void addBootstrapper( Bootstrapper b ) { if ( this.bootstrappers.contains( b ) ) { throw BootstrapException.throwFatal( "Duplicate bootstrapper registration: " + b.getClass( ).toString( ) ); } else { this.bootstrappers.add( b ); } } public void printAgenda( ) { EventRecord.here( Bootstrap.class, EventType.BOOTSTRAP_STAGE_AGENDA, this.name( ), Bootstrap.loading ? "LOAD" : "START" ).info( ); for ( Bootstrapper b : this.bootstrappers ) { EventRecord.here( Bootstrap.class, EventType.BOOTSTRAP_STAGE_AGENDA, this.name( ), b.getClass( ).getCanonicalName( ) ).info( ); } } public void updateBootstrapDependencies( ) { for ( Bootstrapper b : Lists.newArrayList( this.bootstrappers ) ) { if ( !b.checkLocal( ) ) { EventRecord.here( Bootstrap.class, EventType.BOOTSTRAPPER_SKIPPED, "stage:" + Bootstrap.getCurrentStage( ), this.getClass( ).getSimpleName( ), "Depends.local=" + b.toString( ), "Component." + b.toString( ) + "=remote" ).info( ); this.bootstrappers.remove( b ); } else if ( !b.checkRemote( ) ) { EventRecord.here( Bootstrap.class, EventType.BOOTSTRAPPER_SKIPPED, "stage:" + Bootstrap.getCurrentStage( ), this.getClass( ).getSimpleName( ), "Depends.remote=" + b.toString( ), "Component." + b.toString( ) + "=local" ).info( ); this.bootstrappers.remove( b ); } } } public void load( ) { this.updateBootstrapDependencies( ); this.printAgenda( ); for ( Bootstrapper b : this.bootstrappers ) { try { EventRecord.here( Bootstrap.class, EventType.BOOTSTRAPPER_LOAD, this.name( ), b.getClass( ).getCanonicalName( ) ).info( ); boolean result = b.load( this ); if ( !result ) { throw BootstrapException.throwFatal( b.getClass( ).getSimpleName( ) + " returned 'false' from load( ): terminating bootstrap." ); } } catch ( Throwable e ) { EventRecord.here( Bootstrap.class, EventType.BOOTSTRAPPER_ERROR, this.name( ), b.getClass( ).getCanonicalName( ) ).info( ); throw BootstrapException.throwFatal( b.getClass( ).getSimpleName( ) + " threw an error in load( ): " + e.getMessage( ), e ); } } } public void start( ) { this.updateBootstrapDependencies( ); this.printAgenda( ); for ( Bootstrapper b : this.bootstrappers ) { try { EventRecord.here( Bootstrap.class, EventType.BOOTSTRAPPER_START, this.name( ), b.getClass( ).getCanonicalName( ) ).info( ); boolean result = b.start( ); if ( !result ) { throw BootstrapException.throwFatal( b.getClass( ).getSimpleName( ) + " returned 'false' from start( ): terminating bootstrap." ); } } catch ( Throwable e ) { EventRecord.here( Bootstrap.class, EventType.BOOTSTRAPPER_ERROR, this.name( ), b.getClass( ).getCanonicalName( ) ).info( ); throw BootstrapException.throwFatal( b.getClass( ).getSimpleName( ) + " threw an error in start( ): " + e.getMessage( ), e ); } } } public String describe( ) { StringBuffer buf = new StringBuffer( this.name( ) ).append( " " ); for ( Bootstrapper b : this.bootstrappers ) { buf.append( b.getClass( ).getSimpleName( ) ).append( " " ); } return buf.append( "\n" ).toString( ); } public String getResourceName( ) { return String.format( "com.eucalyptus.%sProvider", this.name( ).replaceAll( "Init\\Z", "" ) ); } public List<Resource> getResources( ) { return this.resources; } } private static Boolean loading = false; private static Boolean starting = false; private static Boolean finished = false; private static Stage currentStage = Stage.SystemInit; public static Stage getCurrentStage( ) { return currentStage; } private static void doDiscovery( ) { File libDir = new File( BaseDirectory.LIB.toString( ) ); for ( File f : libDir.listFiles( ) ) { if ( f.getName( ).startsWith( com.eucalyptus.bootstrap.Component.eucalyptus.name( ) ) && f.getName( ).endsWith( ".jar" ) && !f.getName( ).matches( ".*-ext-.*" ) ) { LOG.debug( "Found eucalyptus component jar: " + f.getName( ) ); try { ServiceJarDiscovery.processFile( f ); } catch ( Throwable e ) { LOG.error( e.getMessage( ) ); continue; } } } ServiceJarDiscovery.runDiscovery( ); } @SuppressWarnings( "deprecation" ) public static void initBootstrappers( ) { for ( Bootstrapper bootstrap : BootstrapperDiscovery.getBootstrappers( ) ) {//these have all been checked at discovery time com.eucalyptus.bootstrap.Component comp, old = com.eucalyptus.bootstrap.Component.any; String bc = bootstrap.getClass( ).getCanonicalName( ); Bootstrap.Stage stage = Ats.from( bootstrap ).get( RunDuring.class ).value( ); Provides p = Ats.from( bootstrap ).get( Provides.class ); comp = ( p.value( ) == null ? old : p.value( ) );//TODO: remap orphan bootstrapper to 'any' if ( Components.delegate.any.equals( comp ) ) { EventRecord.here( Bootstrap.class, EventType.BOOTSTRAPPER_ADDED, currentStage.name( ), bc, "Provides", comp.name( ), "Component." + comp.name( ) + ".isEnabled", "true" ).info( ); stage.addBootstrapper( bootstrap ); } else if ( !comp.isCloudLocal( ) && !comp.isEnabled( ) && Components.contains( comp ) ) { //report skipping a bootstrapper for an enabled component EventRecord.here( Bootstrap.class, EventType.BOOTSTRAPPER_SKIPPED, currentStage.name( ), bc, "Provides", comp.name( ), "Component." + comp.name( ) + ".isEnabled", comp.isEnabled( ).toString( ) ).info( ); } else if ( !bootstrap.checkLocal( ) ) { EventRecord.here( Bootstrap.class, EventType.BOOTSTRAPPER_SKIPPED, currentStage.name( ), bc, "DependsLocal", comp.name( ), "Component." + comp.name( ) + ".isLocal", comp.isLocal( ).toString( ) ).info( ); } else if ( !bootstrap.checkRemote( ) ) { EventRecord.here( Bootstrap.class, EventType.BOOTSTRAPPER_SKIPPED, currentStage.name( ), bc, "DependsRemote", comp.name( ), "Component." + comp.name( ) + ".isLocal", comp.isLocal( ).toString( ) ).info( ); } else if ( !Components.contains( comp ) ) { Exceptions.eat( "Bootstrap class provides a component for which registration failed: " + bc + " provides " + comp.name( ) ); // throw BootstrapException.throwFatal try { Component realComponent = Components.create( comp.name( ), null ); EventRecord.here( Bootstrap.class, EventType.BOOTSTRAPPER_ADDED, currentStage.name( ), bc, "Provides", comp.name( ), "Component." + comp.name( ) + ".isEnabled", comp.isEnabled( ).toString( ) ).info( ); realComponent.getConfiguration( ).addBootstrapper( bootstrap ); stage.addBootstrapper( bootstrap ); } catch ( ServiceRegistrationException ex ) { LOG.error( ex , ex ); } } else { EventRecord.here( Bootstrap.class, EventType.BOOTSTRAPPER_ADDED, currentStage.name( ), bc, "Provides", comp.name( ), "Component." + comp.name( ) + ".isEnabled", comp.isEnabled( ).toString( ) ).info( ); Component realComponent = Components.lookup( comp ); realComponent.getConfiguration( ).addBootstrapper( bootstrap ); stage.addBootstrapper( bootstrap ); } } } public static Stage transition( ) { if ( currentStage == Stage.SystemInit && !loading && !starting && !finished ) { loading = true; starting = false; finished = false; } else if ( currentStage != null ) { EventRecord.here( Bootstrap.class, EventType.BOOTSTRAP_STAGE_COMPLETE, currentStage.toString( ) ).info( ); if ( Stage.Final.equals( currentStage ) ) { currentStage = null; if ( loading && !starting && !finished ) { loading = true; starting = true; finished = false; } else if ( loading && starting && !finished ) { loading = true; starting = true; finished = true; } return currentStage; } } int currOrdinal = currentStage != null ? currentStage.ordinal( ) : -1; for ( int i = currOrdinal + 1; i <= Stage.Final.ordinal( ); i++ ) { currentStage = Stage.values( )[i]; if ( currentStage.bootstrappers.isEmpty( ) ) { EventRecord.here( Bootstrap.class, EventType.BOOTSTRAP_STAGE_SKIPPED, currentStage.name( ) ).info( ); continue; } else { return currentStage; } } return currentStage; } public static Boolean isFinished( ) { return finished; } public static void describeComponents( String message ) { LOG.info( LogUtil.header( message ) ); } public static void initialize( ) throws Throwable { LOG.info( LogUtil.header( "Initializing component resources." ) ); Transition.anonymous( LoadConfigs.class ).transition( Stage.list( ) ); for ( Component c : Components.list( ) ) { LOG.info( c.toString( ) ); } LOG.info( LogUtil.header( "Initializing discoverable bootstrap resources." ) ); Bootstrap.doDiscovery( ); LOG.info( LogUtil.header( "Initializing local singleton component services." ) ); Lifecycles.State.PRIMORDIAL.to( Lifecycles.State.INITIALIZED, new Committor<Component>( ) { @Override public void commit( Component comp ) throws Exception { if( ( comp.getPeer( ).isEnabled( ) && comp.getPeer( ).isAlwaysLocal( ) ) || ( Components.delegate.eucalyptus.isLocal( ) && comp.getPeer( ).isCloudLocal( ) ) ){ comp.buildService( ); } } } ).transition( Components.list( ) ); Bootstrap.describeComponents( "Preparing to initialize the system." ); LOG.info( LogUtil.header( "Initializing bootstrappers." ) ); Bootstrap.initBootstrappers( ); Bootstrap.describeComponents( "Initialized system: ready to start bootstrap." ); } }