/************************************************************************* * Copyright 2009-2016 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. * * This file may incorporate work covered under the following copyright * and permission notice: * * Software License Agreement (BSD License) * * Copyright (c) 2008, Regents of the University of California * All rights reserved. * * Redistribution and use of this software in source and binary forms, * with or without modification, are permitted provided that the * following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. USERS OF THIS SOFTWARE ACKNOWLEDGE * THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE LICENSED MATERIAL, * COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS SOFTWARE, * AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING * IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA, * SANTA BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, * WHICH IN THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION, * REPLACEMENT OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO * IDENTIFIED, OR WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT * NEEDED TO COMPLY WITH ANY SUCH LICENSES OR RIGHTS. ************************************************************************/ package com.eucalyptus.bootstrap; import groovy.sql.Sql; import java.net.InetAddress; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.log4j.Level; import org.apache.log4j.Logger; import com.eucalyptus.component.ServiceUris; import com.eucalyptus.component.id.Eucalyptus; import com.eucalyptus.component.id.Database; import com.eucalyptus.empyrean.Empyrean; import com.eucalyptus.entities.PersistenceContexts; import com.eucalyptus.scripting.Groovyness; import com.eucalyptus.scripting.ScriptExecutionFailedException; import com.eucalyptus.system.Threads; import com.eucalyptus.util.Exceptions; import com.eucalyptus.util.Internets; import com.eucalyptus.util.Strings; import com.google.common.base.Joiner; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; public class Databases { private static final Logger LOG = Logger.getLogger( Databases.class ); private static final int MAX_TX_START_SYNC_RETRIES = 120; private static final ScriptedDbBootstrapper singleton = new ScriptedDbBootstrapper( ); private static final AtomicBoolean volatileAtomic = new AtomicBoolean( true ); private static Predicate<StackTraceElement> notStackFilterYouAreLookingFor = Predicates.or( Threads.filterStackByQualifiedName( "com\\.eucalyptus\\.entities\\..*" ), Threads.filterStackByQualifiedName( "java\\.lang\\.Thread.*" ), Threads.filterStackByQualifiedName( "com\\.eucalyptus\\.system\\.Threads.*" ), Threads.filterStackByQualifiedName( "com\\.eucalyptus\\.bootstrap\\.Databases.*" ) ); private static Predicate<StackTraceElement> stackFilter = Predicates.not( notStackFilterYouAreLookingFor ); public static class DatabaseStateException extends IllegalStateException { private static final long serialVersionUID = 1L; public DatabaseStateException( String string ) { super( string ); } } public enum Events { INSTANCE; public static Sql getConnection( ) throws Exception { return Databases.getBootstrapper( ).getConnection( INSTANCE.getName( ), null ); } public String getName( ) { return "database_events"; } public static void create( ) { if ( !getBootstrapper( ).listDatabases( ).contains( INSTANCE.getName( ) ) ) { try { getBootstrapper( ).createDatabase( INSTANCE.getName( ) ); } catch ( Exception ex ) { LOG.error( ex , ex ); } } } } public static Iterable<String> databases( ) { return Sets.newTreeSet( Iterables.transform( PersistenceContexts.list( ), PersistenceContexts.toDatabaseName( ) ) ); } public static Iterable<String> remoteDatabases( ) { return Sets.newTreeSet( PersistenceContexts.listRemotable() ); } @Provides( Empyrean.class ) @RunDuring( Bootstrap.Stage.PoolInit ) public static class DatabasePoolBootstrapper extends Bootstrapper.Simple { @Override public boolean load( ) throws Exception { Hosts.awaitDatabases( ); Groovyness.run( "setup_dbpool.groovy" ); return true; } } @Provides( Empyrean.class ) @RunDuring( Bootstrap.Stage.RemoteDbPoolInit ) public static class DatabaseRemotePoolBootstrapper extends Bootstrapper.Simple { @Override public boolean load( ) throws Exception { try{ Groovyness.run( "setup_dbpool_remote.groovy" ); }catch(final Exception ex){ ; } return true; } } static boolean shouldInitialize( ) {//GRZE:WARNING:HACKHACKHACK do not duplicate pls thanks. final String context = "eucalyptus_config"; final String databaseName = PersistenceContexts.toDatabaseName( ).apply( context ); final String schemaName = PersistenceContexts.toSchemaName( ).apply( context ); final String schemaPrefix = schemaName == null ? "" : schemaName + "."; for ( final Host h : Hosts.listActiveDatabases( ) ) { final String url = String.format( "jdbc:%s", ServiceUris.remote( Database.class, h.getBindAddress( ), databaseName ) ); try ( final Connection conn = DriverManager.getConnection( url, Databases.getUserName( ), Databases.getPassword( ) ) ) { try ( final PreparedStatement statement = conn.prepareStatement( "select config_component_hostname from " + schemaPrefix + "config_component_base where config_component_partition='eucalyptus';" ) ) { try ( final ResultSet result = statement.executeQuery( ) ) { while ( result.next( ) ) { final Object columnValue = result.getObject( 1 ); if ( Internets.testLocal( columnValue.toString( ) ) ) { return true; } } } } } catch ( final Exception ex ) { LOG.error( ex, ex ); } } return false; } /** * List all known databases. */ static Set<String> listDatabases( ) { final Set<String> dbNames = Sets.newHashSet(); final Predicate<String> dbNamePredicate = Predicates.or( Strings.startsWith( "eucalyptus_" ), Predicates.equalTo( "database_events" ) ); for ( final Host h : Hosts.listActiveDatabases( ) ) { Iterables.addAll( dbNames, Iterables.filter( Databases.getBootstrapper().listDatabases( h.getBindAddress( ) ), dbNamePredicate ) ); } return dbNames; } public static Boolean isVolatile( ) { return !BootstrapArgs.isUpgradeSystem( ) && !Bootstrap.isShuttingDown( ) && volatileAtomic.get( ); } public static void setVolatile( boolean isVolatile ) { if ( volatileAtomic.compareAndSet( !isVolatile, isVolatile ) ) { LOG.debug( "Database availability changed to: " + !isVolatile ); } } public static void awaitSynchronized( ) { if ( !isVolatile( ) ) { return; } else { Collection<StackTraceElement> stack = Threads.filteredStack( stackFilter ); String caller = ( stack.isEmpty( ) ? "" : stack.iterator( ).next( ).toString( ) ); for ( int i = 0; i < MAX_TX_START_SYNC_RETRIES && isVolatile( ); i++ ) { try { TimeUnit.MILLISECONDS.sleep( 1000 ); LOG.debug( "Transaction blocked on sync: " + caller ); } catch ( InterruptedException ex ) { Exceptions.maybeInterrupted( ex ); return; } } if ( isVolatile( ) ) { throw new DatabaseStateException( "Transaction begin failed due to concurrent database synchronization: " + Hosts.listDatabases( ) + " for caller:\n" + Joiner.on( "\n\tat " ).join( stack ) ); } } } public static String getUserName( ) { return singleton.getUserName( ); } public static String getPassword( ) { return singleton.getPassword( ); } public static String getDriverName( ) { return singleton.getDriverName( ); } public static String getDefaultSchemaName( ) { return singleton.getDefaultSchemaName( ); } public static String getJdbcDialect( ) { return singleton.getJdbcDialect( ); } public static String getHibernateDialect( ) { return singleton.getHibernateDialect( ); } public static DatabaseBootstrapper getBootstrapper( ) { return singleton; } public static void initialize( ) { singleton.init( ); Databases.Events.create( ); } @RunDuring( Bootstrap.Stage.DatabaseInit ) @Provides( Empyrean.class ) @DependsLocal( Eucalyptus.class ) public static class ScriptedDbBootstrapper extends Bootstrapper.Simple implements DatabaseBootstrapper { DatabaseBootstrapper db; public ScriptedDbBootstrapper( ) { try { this.db = Groovyness.newInstance( "setup_db" ); } catch ( ScriptExecutionFailedException ex ) { LOG.error( ex, ex ); } } @Override public boolean load( ) throws Exception { boolean result = this.db.load( ); Databases.Events.create( ); return result; } @Override public boolean start( ) throws Exception { return this.db.start( ); } @Override public boolean stop( ) throws Exception { return this.db.stop( ); } @Override public void destroy( ) throws Exception { this.db.destroy( ); } @Override public boolean isRunning( ) { return this.db.isRunning( ); } @Override public void hup( ) { this.db.hup( ); } @Override public String getUserName( ) { return db.getUserName( ); } @Override public String getPassword( ) { return db.getPassword( ); } @Override public String getDefaultSchemaName( ) { return this.db.getDefaultSchemaName( ); } @Override public String getDriverName( ) { return this.db.getDriverName( ); } @Override public String getJdbcDialect( ) { return this.db.getJdbcDialect( ); } @Override public String getHibernateDialect( ) { return this.db.getHibernateDialect( ); } @Override public void init( ) { try { this.db.init( ); } catch ( Exception ex ) { LOG.error( ex, ex ); } } public static DatabaseBootstrapper getInstance( ) { return singleton; } @Override public String getServicePath( String... pathParts ) { return this.db.getServicePath( pathParts ); } @Override public Map<String, String> getJdbcUrlQueryParameters( ) { return this.db.getJdbcUrlQueryParameters( ); } @Override public boolean check( ) throws Exception { return this.db.isRunning( ); } /** * @see DatabaseBootstrapper#getJdbcScheme() */ @Override public String getJdbcScheme( ) { return this.db.getJdbcScheme( ); } @Override public List<String> listDatabases() { return db.listDatabases(); } @Override public List<String> listDatabases( final InetAddress host ) { return db.listDatabases( host ); } @Override public List<String> listSchemas( final String database ) { return db.listSchemas( database ); } @Override public List<String> listSchemas( final InetAddress host, final String database ) { return db.listSchemas( host, database ); } @Override public List<String> listTables( final String database, final String schema ) { return db.listTables( database, schema ); } @Override public void createDatabase( final String database ) { db.createDatabase( database ); } @Override public void deleteDatabase( final String database ) { db.deleteDatabase( database ); } @Override public void renameDatabase( final String from, final String to ) { db.renameDatabase( from, to ); } @Override public void copyDatabase( final String sourceDatabase, final String destinationDatabase ) { db.copyDatabase( sourceDatabase, destinationDatabase ); } @Override public void copyDatabaseSchema( final String sourceDatabase, final String sourceSchema, final String destinationDatabase, final String destinationSchema ) { db.copyDatabaseSchema( sourceDatabase, sourceSchema, destinationDatabase, destinationSchema ); } @Override public Sql getConnection( final String context ) throws Exception { return db.getConnection( context ); } @Override public Sql getConnection( final String database, final String schema ) throws Exception { return db.getConnection( database, schema ); } } public static boolean isRunning( ) { try { return singleton.check( ); } catch ( Exception ex ) { LOG.error( ex, ex ); return false; } } public static String getServicePath( String... pathParts ) { return singleton.getServicePath( pathParts ); } public static Map<String, String> getJdbcUrlQueryParameters( ) { return singleton.getJdbcUrlQueryParameters(); } public static String getJdbcScheme( ) { return singleton.getJdbcScheme( ); } }