PageRenderTime 36ms CodeModel.GetById 1ms app.highlight 27ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/galaxy/model/migrate/check.py

https://bitbucket.org/cistrome/cistrome-harvard/
Python | 135 lines | 103 code | 8 blank | 24 comment | 7 complexity | 78b172252efa934aec005cb091c4f1de MD5 | raw file
  1import sys
  2import os.path
  3import logging
  4
  5from galaxy import eggs
  6eggs.require( "SQLAlchemy" )
  7eggs.require( "decorator" ) #Required by sqlalchemy-migrate
  8eggs.require( "Tempita " ) #Required by sqlalchemy-migrate
  9eggs.require( "sqlalchemy-migrate" )
 10
 11from sqlalchemy import *
 12from sqlalchemy.exc import NoSuchTableError
 13from migrate.versioning import repository, schema
 14
 15from galaxy.model.orm import dialect_to_egg
 16
 17log = logging.getLogger( __name__ )
 18
 19# path relative to galaxy
 20migrate_repository_directory = os.path.dirname( __file__ ).replace( os.getcwd() + os.path.sep, '', 1 )
 21migrate_repository = repository.Repository( migrate_repository_directory )
 22
 23def create_or_verify_database( url, galaxy_config_file, engine_options={}, app=None ):
 24    """
 25    Check that the database is use-able, possibly creating it if empty (this is
 26    the only time we automatically create tables, otherwise we force the
 27    user to do it using the management script so they can create backups).
 28
 29    1) Empty database --> initialize with latest version and return
 30    2) Database older than migration support --> fail and require manual update
 31    3) Database at state where migrate support introduced --> add version control information but make no changes (might still require manual update)
 32    4) Database versioned but out of date --> fail with informative message, user must run "sh manage_db.sh upgrade"
 33    """
 34    dialect = ( url.split( ':', 1 ) )[0]
 35    try:
 36        egg = dialect_to_egg[dialect]
 37        try:
 38            eggs.require( egg )
 39            log.debug( "%s egg successfully loaded for %s dialect" % ( egg, dialect ) )
 40        except:
 41            # If the module is in the path elsewhere (i.e. non-egg), it'll still load.
 42            log.warning( "%s egg not found, but an attempt will be made to use %s anyway" % ( egg, dialect ) )
 43    except KeyError:
 44        # Let this go, it could possibly work with db's we don't support
 45        log.error( "database_connection contains an unknown SQLAlchemy database dialect: %s" % dialect )
 46    # Create engine and metadata
 47    engine = create_engine( url, **engine_options )
 48
 49    def migrate():
 50        try:
 51            # Declare the database to be under a repository's version control
 52            db_schema = schema.ControlledSchema.create( engine, migrate_repository )
 53        except:
 54            # The database is already under version control
 55            db_schema = schema.ControlledSchema( engine, migrate_repository )
 56        # Apply all scripts to get to current version
 57        migrate_to_current_version( engine, db_schema )
 58
 59    meta = MetaData( bind=engine )
 60    if app and getattr( app.config, 'database_auto_migrate', False ):
 61        migrate()
 62        return
 63
 64    # Try to load dataset table
 65    try:
 66        dataset_table = Table( "dataset", meta, autoload=True )
 67    except NoSuchTableError:
 68        # No 'dataset' table means a completely uninitialized database.  If we have an app, we'll
 69        # set it's new_installation setting to True so the tool migration process will be skipped.
 70        if app:
 71            app.new_installation = True
 72        log.info( "No database, initializing" )
 73        migrate()
 74        return
 75    try:
 76        hda_table = Table( "history_dataset_association", meta, autoload=True )
 77    except NoSuchTableError:
 78        raise Exception( "Your database is older than hg revision 1464:c7acaa1bb88f and will need to be updated manually" )
 79    # There is a 'history_dataset_association' table, so we (hopefully) have
 80    # version 1 of the database, but without the migrate_version table. This
 81    # happens if the user has a build from right before migration was added.
 82    # Verify that this is true, if it is any older they'll have to update
 83    # manually
 84    if 'copied_from_history_dataset_association_id' not in hda_table.c:
 85        # The 'copied_from_history_dataset_association_id' column was added in
 86        # rev 1464:c7acaa1bb88f.  This is the oldest revision we currently do
 87        # automated versioning for, so stop here
 88        raise Exception( "Your database is older than hg revision 1464:c7acaa1bb88f and will need to be updated manually" )
 89    # At revision 1464:c7acaa1bb88f or greater (database version 1), make sure
 90    # that the db has version information. This is the trickiest case -- we
 91    # have a database but no version control, and are assuming it is a certain
 92    # version. If the user has postion version 1 changes this could cause
 93    # problems
 94    try:
 95        version_table = Table( "migrate_version", meta, autoload=True )
 96    except NoSuchTableError:
 97        # The database exists but is not yet under migrate version control, so init with version 1
 98        log.info( "Adding version control to existing database" )
 99        try:
100            metadata_file_table = Table( "metadata_file", meta, autoload=True )
101            schema.ControlledSchema.create( engine, migrate_repository, version=2 )
102        except NoSuchTableError:
103            schema.ControlledSchema.create( engine, migrate_repository, version=1 )
104    # Verify that the code and the DB are in sync
105    db_schema = schema.ControlledSchema( engine, migrate_repository )
106    if migrate_repository.versions.latest != db_schema.version:
107        config_arg = ''
108        if os.path.abspath( os.path.join( os.getcwd(), 'universe_wsgi.ini' ) ) != galaxy_config_file:
109            config_arg = ' -c %s' % galaxy_config_file.replace( os.path.abspath( os.getcwd() ), '.' )
110        raise Exception( "Your database has version '%d' but this code expects version '%d'.  Please backup your database and then migrate the schema by running 'sh manage_db.sh%s upgrade'."
111                            % ( db_schema.version, migrate_repository.versions.latest, config_arg ) )
112    else:
113        log.info( "At database version %d" % db_schema.version )
114
115def migrate_to_current_version( engine, schema ):
116    # Changes to get to current version
117    changeset = schema.changeset( None )
118    for ver, change in changeset:
119        nextver = ver + changeset.step
120        log.info( 'Migrating %s -> %s... ' % ( ver, nextver ) )
121        old_stdout = sys.stdout
122        class FakeStdout( object ):
123            def __init__( self ):
124                self.buffer = []
125            def write( self, s ):
126                self.buffer.append( s )
127            def flush( self ):
128                pass
129        sys.stdout = FakeStdout()
130        try:
131            schema.runchange( ver, change, changeset.step )
132        finally:
133            for message in "".join( sys.stdout.buffer ).split( "\n" ):
134                log.info( message )
135            sys.stdout = old_stdout