+import sys
+import traceback
+
+from sqlalchemy import MetaData, Table
+from sqlalchemy.exc import NoSuchTableError
+
+from migrate.versioning.api import version, db_version, version_control, upgrade
+
+from sfa.util.sfalogging import logger
+from sfa.storage.model import init_tables
+
+### this script will upgrade from a pre-2.1 db
+# * 1.0 and up to 1.1-4: ('very old')
+# was piggybacking the planetlab5 database
+# this is kind of out of our scope here, we don't have the credentials
+# to connect to planetlab5, but this is documented in
+# https://svn.planet-lab.org/wiki/SFATutorialConfigureSFA#Upgradingnotes
+# and essentially this is seamless to users
+# * from 1.1-5 up to 2.0-x: ('old')
+# uses the 'sfa' db and essentially the 'records' table,
+# as well as record_types
+# together with an 'sfa_db_version' table (version, subversion)
+# * from 2.1:
+# we have an 'records' table, plus 'users' and the like
+# and once migrate has kicked in there is a table named
+# migrate_db_version (repository_id, repository_path, version)
+####
+# An initial attempt to run this as a 001_*.py migrate script
+# did not quite work out (essentially we need to set the current version
+# number out of the migrations logic)
+# also this approach has less stuff in the initscript, which seems just right
+
+class DBSchema:
+
+ header="Upgrading to 2.1 or higher"
+
+ def __init__ (self):
+ from sfa.storage.alchemy import alchemy
+ self.url=alchemy.url
+ self.engine=alchemy.engine
+ self.repository="/usr/share/sfa/migrations"
+ self.meta=MetaData (bind=self.engine)
+
+ def current_version (self):
+ try:
+ return db_version (self.url, self.repository)
+ except:
+ return None
+
+ def table_exists (self, tablename):
+ try:
+ table=Table (tablename, self.meta, autoload=True)
+ return True
+ except NoSuchTableError:
+ return False
+
+ def drop_table (self, tablename):
+ if self.table_exists (tablename):
+ print >>sys.stderr, "%s: Dropping table %s"%(DBSchema.header,tablename)
+ self.engine.execute ("drop table %s cascade"%tablename)
+ else:
+ print >>sys.stderr, "%s: no need to drop table %s"%(DBSchema.header,tablename)
+
+ def handle_old_releases (self):
+ try:
+ # try to find out which old version this can be
+ if not self.table_exists ('records'):
+ # this likely means we've just created the db, so it's either a fresh install
+ # or we come from a 'very old' depl.
+ # in either case, an import is required but there's nothing to clean up
+ print >> sys.stderr,"%s: make sure to run import"%(DBSchema.header,)
+ elif self.table_exists ('sfa_db_version'):
+ # we come from an 'old' version
+ self.drop_table ('records')
+ self.drop_table ('record_types')
+ self.drop_table ('sfa_db_version')
+ else:
+ # we should be good here
+ pass
+ except:
+ print >> sys.stderr, "%s: unknown exception"%(DBSchema.header,)
+ traceback.print_exc ()
+
+ # after this call the db schema and the version as known by migrate should
+ # reflect the current data model and the latest known version
+ def init_or_upgrade (self):
+ # check if under version control, and initialize it otherwise
+ if self.current_version() is None:
+ before="Unknown"
+ # can be either a very old version, or a fresh install
+ # for very old versions:
+ self.handle_old_releases()
+ # in any case, initialize db from current code and reflect in migrate
+ init_tables(self.engine)
+ code_version = version (self.repository)
+ version_control (self.url, self.repository, code_version)
+ else:
+ # use migrate in the usual way
+ before="%s"%self.current_version()
+ upgrade (self.url, self.repository)
+ after="%s"%self.current_version()
+ if before != after:
+ logger.info("DBSchema : upgraded from %s to %s"%(before,after))
+
+ # this call will trash the db altogether
+ def nuke (self):
+ drop_tables(self.engine)
+
+if __name__ == '__main__':
+ DBSchema().init_or_upgrade()