blob: ee448cf24aec61ad9d1716cd83c9acef59cafa15 [file] [log] [blame]
mblighfa29a2a2008-05-16 22:48:09 +00001#!/usr/bin/python -u
mblighe8819cd2008-02-15 16:48:40 +00002
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -08003import os, sys, re, tempfile
mblighb090f142008-02-27 21:33:46 +00004from optparse import OptionParser
mbligh9b907d62008-05-13 17:56:24 +00005import common
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -08006from autotest_lib.client.common_lib import utils
showard0e73c852008-10-03 10:15:50 +00007from autotest_lib.database import database_connection
mblighe8819cd2008-02-15 16:48:40 +00008
9MIGRATE_TABLE = 'migrate_info'
showard50c0e712008-09-22 16:20:37 +000010
11_AUTODIR = os.path.join(os.path.dirname(__file__), '..')
12_MIGRATIONS_DIRS = {
Dan Shi7f0c1832014-10-27 16:05:57 -070013 'AUTOTEST_WEB': os.path.join(_AUTODIR, 'frontend', 'migrations'),
14 'TKO': os.path.join(_AUTODIR, 'tko', 'migrations'),
15 'AUTOTEST_SERVER_DB': os.path.join(_AUTODIR, 'database',
16 'server_db_migrations'),
showard50c0e712008-09-22 16:20:37 +000017}
18_DEFAULT_MIGRATIONS_DIR = 'migrations' # use CWD
mblighe8819cd2008-02-15 16:48:40 +000019
mblighe8819cd2008-02-15 16:48:40 +000020class Migration(object):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -080021 """Represents a database migration."""
showardeca1f212009-05-13 20:28:12 +000022 _UP_ATTRIBUTES = ('migrate_up', 'UP_SQL')
23 _DOWN_ATTRIBUTES = ('migrate_down', 'DOWN_SQL')
24
25 def __init__(self, name, version, module):
26 self.name = name
27 self.version = version
28 self.module = module
29 self._check_attributes(self._UP_ATTRIBUTES)
30 self._check_attributes(self._DOWN_ATTRIBUTES)
31
32
33 @classmethod
34 def from_file(cls, filename):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -080035 """Instantiates a Migration from a file.
36
37 @param filename: Name of a migration file.
38
39 @return An instantiated Migration object.
40
41 """
showardeca1f212009-05-13 20:28:12 +000042 version = int(filename[:3])
43 name = filename[:-3]
44 module = __import__(name, globals(), locals(), [])
45 return cls(name, version, module)
46
47
48 def _check_attributes(self, attributes):
49 method_name, sql_name = attributes
50 assert (hasattr(self.module, method_name) or
51 hasattr(self.module, sql_name))
52
53
54 def _execute_migration(self, attributes, manager):
55 method_name, sql_name = attributes
56 method = getattr(self.module, method_name, None)
57 if method:
58 assert callable(method)
59 method(manager)
60 else:
61 sql = getattr(self.module, sql_name)
62 assert isinstance(sql, basestring)
63 manager.execute_script(sql)
showarddecbe502008-03-28 16:31:10 +000064
65
jadmanski0afbb632008-06-06 21:10:57 +000066 def migrate_up(self, manager):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -080067 """Performs an up migration (to a newer version).
68
69 @param manager: A MigrationManager object.
70
71 """
showardeca1f212009-05-13 20:28:12 +000072 self._execute_migration(self._UP_ATTRIBUTES, manager)
showarddecbe502008-03-28 16:31:10 +000073
74
jadmanski0afbb632008-06-06 21:10:57 +000075 def migrate_down(self, manager):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -080076 """Performs a down migration (to an older version).
77
78 @param manager: A MigrationManager object.
79
80 """
showardeca1f212009-05-13 20:28:12 +000081 self._execute_migration(self._DOWN_ATTRIBUTES, manager)
mblighe8819cd2008-02-15 16:48:40 +000082
83
84class MigrationManager(object):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -080085 """Managest database migrations."""
jadmanski0afbb632008-06-06 21:10:57 +000086 connection = None
87 cursor = None
88 migrations_dir = None
mblighe8819cd2008-02-15 16:48:40 +000089
showard0e73c852008-10-03 10:15:50 +000090 def __init__(self, database_connection, migrations_dir=None, force=False):
91 self._database = database_connection
showard50c0e712008-09-22 16:20:37 +000092 self.force = force
showard12454c62010-01-15 19:15:14 +000093 # A boolean, this will only be set to True if this migration should be
94 # simulated rather than actually taken. For use with migrations that
95 # may make destructive queries
96 self.simulate = False
showard0e73c852008-10-03 10:15:50 +000097 self._set_migrations_dir(migrations_dir)
98
99
100 def _set_migrations_dir(self, migrations_dir=None):
jamesren92afa562010-03-23 00:19:26 +0000101 config_section = self._config_section()
jadmanski0afbb632008-06-06 21:10:57 +0000102 if migrations_dir is None:
showard50c0e712008-09-22 16:20:37 +0000103 migrations_dir = os.path.abspath(
showard0e73c852008-10-03 10:15:50 +0000104 _MIGRATIONS_DIRS.get(config_section, _DEFAULT_MIGRATIONS_DIR))
jadmanski0afbb632008-06-06 21:10:57 +0000105 self.migrations_dir = migrations_dir
106 sys.path.append(migrations_dir)
showard0e73c852008-10-03 10:15:50 +0000107 assert os.path.exists(migrations_dir), migrations_dir + " doesn't exist"
mblighe8819cd2008-02-15 16:48:40 +0000108
109
jamesren92afa562010-03-23 00:19:26 +0000110 def _config_section(self):
111 return self._database.global_config_section
112
113
showardeab66ce2009-12-23 00:03:56 +0000114 def get_db_name(self):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -0800115 """Gets the database name."""
showard0e73c852008-10-03 10:15:50 +0000116 return self._database.get_database_info()['db_name']
mblighe8819cd2008-02-15 16:48:40 +0000117
118
jadmanski0afbb632008-06-06 21:10:57 +0000119 def execute(self, query, *parameters):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -0800120 """Executes a database query.
121
122 @param query: The query to execute.
123 @param parameters: Associated parameters for the query.
124
125 @return The result of the query.
126
127 """
showard0e73c852008-10-03 10:15:50 +0000128 return self._database.execute(query, parameters)
mblighe8819cd2008-02-15 16:48:40 +0000129
130
jadmanski0afbb632008-06-06 21:10:57 +0000131 def execute_script(self, script):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -0800132 """Executes a set of database queries.
133
134 @param script: A string of semicolon-separated queries.
135
136 """
showardb1e51872008-10-07 11:08:18 +0000137 sql_statements = [statement.strip()
138 for statement in script.split(';')
139 if statement.strip()]
jadmanski0afbb632008-06-06 21:10:57 +0000140 for statement in sql_statements:
showardb1e51872008-10-07 11:08:18 +0000141 self.execute(statement)
mblighe8819cd2008-02-15 16:48:40 +0000142
143
jadmanski0afbb632008-06-06 21:10:57 +0000144 def check_migrate_table_exists(self):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -0800145 """Checks whether the migration table exists."""
jadmanski0afbb632008-06-06 21:10:57 +0000146 try:
147 self.execute("SELECT * FROM %s" % MIGRATE_TABLE)
148 return True
showard0e73c852008-10-03 10:15:50 +0000149 except self._database.DatabaseError, exc:
150 # we can't check for more specifics due to differences between DB
151 # backends (we can't even check for a subclass of DatabaseError)
152 return False
mblighe8819cd2008-02-15 16:48:40 +0000153
154
jadmanski0afbb632008-06-06 21:10:57 +0000155 def create_migrate_table(self):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -0800156 """Creates the migration table."""
jadmanski0afbb632008-06-06 21:10:57 +0000157 if not self.check_migrate_table_exists():
158 self.execute("CREATE TABLE %s (`version` integer)" %
159 MIGRATE_TABLE)
160 else:
161 self.execute("DELETE FROM %s" % MIGRATE_TABLE)
162 self.execute("INSERT INTO %s VALUES (0)" % MIGRATE_TABLE)
showard0e73c852008-10-03 10:15:50 +0000163 assert self._database.rowcount == 1
mblighe8819cd2008-02-15 16:48:40 +0000164
165
jadmanski0afbb632008-06-06 21:10:57 +0000166 def set_db_version(self, version):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -0800167 """Sets the database version.
168
169 @param version: The version to which to set the database.
170
171 """
jadmanski0afbb632008-06-06 21:10:57 +0000172 assert isinstance(version, int)
173 self.execute("UPDATE %s SET version=%%s" % MIGRATE_TABLE,
174 version)
showard0e73c852008-10-03 10:15:50 +0000175 assert self._database.rowcount == 1
mblighe8819cd2008-02-15 16:48:40 +0000176
177
jadmanski0afbb632008-06-06 21:10:57 +0000178 def get_db_version(self):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -0800179 """Gets the database version.
180
181 @return The database version.
182
183 """
jadmanski0afbb632008-06-06 21:10:57 +0000184 if not self.check_migrate_table_exists():
185 return 0
showard0e73c852008-10-03 10:15:50 +0000186 rows = self.execute("SELECT * FROM %s" % MIGRATE_TABLE)
jadmanski0afbb632008-06-06 21:10:57 +0000187 if len(rows) == 0:
188 return 0
189 assert len(rows) == 1 and len(rows[0]) == 1
190 return rows[0][0]
mblighe8819cd2008-02-15 16:48:40 +0000191
192
jadmanski0afbb632008-06-06 21:10:57 +0000193 def get_migrations(self, minimum_version=None, maximum_version=None):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -0800194 """Gets the list of migrations to perform.
195
196 @param minimum_version: The minimum database version.
197 @param maximum_version: The maximum database version.
198
199 @return A list of Migration objects.
200
201 """
jadmanski0afbb632008-06-06 21:10:57 +0000202 migrate_files = [filename for filename
203 in os.listdir(self.migrations_dir)
204 if re.match(r'^\d\d\d_.*\.py$', filename)]
205 migrate_files.sort()
showardeca1f212009-05-13 20:28:12 +0000206 migrations = [Migration.from_file(filename)
207 for filename in migrate_files]
jadmanski0afbb632008-06-06 21:10:57 +0000208 if minimum_version is not None:
209 migrations = [migration for migration in migrations
210 if migration.version >= minimum_version]
211 if maximum_version is not None:
212 migrations = [migration for migration in migrations
213 if migration.version <= maximum_version]
214 return migrations
mblighe8819cd2008-02-15 16:48:40 +0000215
216
jadmanski0afbb632008-06-06 21:10:57 +0000217 def do_migration(self, migration, migrate_up=True):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -0800218 """Performs a migration.
219
220 @param migration: The Migration to perform.
221 @param migrate_up: Whether to migrate up (if not, then migrates down).
222
223 """
jadmanski0afbb632008-06-06 21:10:57 +0000224 print 'Applying migration %s' % migration.name, # no newline
225 if migrate_up:
226 print 'up'
227 assert self.get_db_version() == migration.version - 1
228 migration.migrate_up(self)
229 new_version = migration.version
230 else:
231 print 'down'
232 assert self.get_db_version() == migration.version
233 migration.migrate_down(self)
234 new_version = migration.version - 1
235 self.set_db_version(new_version)
mblighe8819cd2008-02-15 16:48:40 +0000236
237
jadmanski0afbb632008-06-06 21:10:57 +0000238 def migrate_to_version(self, version):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -0800239 """Performs a migration to a specified version.
240
241 @param version: The version to which to migrate the database.
242
243 """
jadmanski0afbb632008-06-06 21:10:57 +0000244 current_version = self.get_db_version()
jamesren92afa562010-03-23 00:19:26 +0000245 if current_version == 0 and self._config_section() == 'AUTOTEST_WEB':
246 self._migrate_from_base()
247 current_version = self.get_db_version()
248
jadmanski0afbb632008-06-06 21:10:57 +0000249 if current_version < version:
250 lower, upper = current_version, version
251 migrate_up = True
252 else:
253 lower, upper = version, current_version
254 migrate_up = False
mblighe8819cd2008-02-15 16:48:40 +0000255
jadmanski0afbb632008-06-06 21:10:57 +0000256 migrations = self.get_migrations(lower + 1, upper)
257 if not migrate_up:
258 migrations.reverse()
259 for migration in migrations:
260 self.do_migration(migration, migrate_up)
mblighe8819cd2008-02-15 16:48:40 +0000261
jadmanski0afbb632008-06-06 21:10:57 +0000262 assert self.get_db_version() == version
263 print 'At version', version
mblighe8819cd2008-02-15 16:48:40 +0000264
265
jamesren92afa562010-03-23 00:19:26 +0000266 def _migrate_from_base(self):
Dan Shi7f0c1832014-10-27 16:05:57 -0700267 """Initialize the AFE database.
268 """
jamesren92afa562010-03-23 00:19:26 +0000269 self.confirm_initialization()
270
271 migration_script = utils.read_file(
Keith Haddow6dbcdf52018-06-07 14:13:52 -0700272 os.path.join(os.path.dirname(__file__), 'schema_129.sql'))
jamesrenef692982010-04-05 18:14:07 +0000273 migration_script = migration_script % (
274 dict(username=self._database.get_database_info()['username']))
jamesren92afa562010-03-23 00:19:26 +0000275 self.execute_script(migration_script)
276
277 self.create_migrate_table()
Keith Haddow6dbcdf52018-06-07 14:13:52 -0700278 self.set_db_version(129)
jamesren92afa562010-03-23 00:19:26 +0000279
280
281 def confirm_initialization(self):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -0800282 """Confirms with the user that we should initialize the database.
283
284 @raises Exception, if the user chooses to abort the migration.
285
286 """
jamesren92afa562010-03-23 00:19:26 +0000287 if not self.force:
288 response = raw_input(
289 'Your %s database does not appear to be initialized. Do you '
290 'want to recreate it (this will result in loss of any existing '
291 'data) (yes/No)? ' % self.get_db_name())
292 if response != 'yes':
293 raise Exception('User has chosen to abort migration')
294
295
jadmanski0afbb632008-06-06 21:10:57 +0000296 def get_latest_version(self):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -0800297 """Gets the latest database version."""
jadmanski0afbb632008-06-06 21:10:57 +0000298 migrations = self.get_migrations()
299 return migrations[-1].version
showardd2d4e2c2008-03-12 21:32:46 +0000300
301
jadmanski0afbb632008-06-06 21:10:57 +0000302 def migrate_to_latest(self):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -0800303 """Migrates the database to the latest version."""
jadmanski0afbb632008-06-06 21:10:57 +0000304 latest_version = self.get_latest_version()
305 self.migrate_to_version(latest_version)
mblighe8819cd2008-02-15 16:48:40 +0000306
307
jadmanski0afbb632008-06-06 21:10:57 +0000308 def initialize_test_db(self):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -0800309 """Initializes a test database."""
showardeab66ce2009-12-23 00:03:56 +0000310 db_name = self.get_db_name()
showard0e73c852008-10-03 10:15:50 +0000311 test_db_name = 'test_' + db_name
jadmanski0afbb632008-06-06 21:10:57 +0000312 # first, connect to no DB so we can create a test DB
showard0e73c852008-10-03 10:15:50 +0000313 self._database.connect(db_name='')
jadmanski0afbb632008-06-06 21:10:57 +0000314 print 'Creating test DB', test_db_name
315 self.execute('CREATE DATABASE ' + test_db_name)
showard0e73c852008-10-03 10:15:50 +0000316 self._database.disconnect()
jadmanski0afbb632008-06-06 21:10:57 +0000317 # now connect to the test DB
showard0e73c852008-10-03 10:15:50 +0000318 self._database.connect(db_name=test_db_name)
mblighe8819cd2008-02-15 16:48:40 +0000319
320
jadmanski0afbb632008-06-06 21:10:57 +0000321 def remove_test_db(self):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -0800322 """Removes a test database."""
jadmanski0afbb632008-06-06 21:10:57 +0000323 print 'Removing test DB'
showardeab66ce2009-12-23 00:03:56 +0000324 self.execute('DROP DATABASE ' + self.get_db_name())
showard0e73c852008-10-03 10:15:50 +0000325 # reset connection back to real DB
326 self._database.disconnect()
327 self._database.connect()
mblighe8819cd2008-02-15 16:48:40 +0000328
329
jadmanski0afbb632008-06-06 21:10:57 +0000330 def get_mysql_args(self):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -0800331 """Returns the mysql arguments as a string."""
showard0e73c852008-10-03 10:15:50 +0000332 return ('-u %(username)s -p%(password)s -h %(host)s %(db_name)s' %
333 self._database.get_database_info())
mblighe8819cd2008-02-15 16:48:40 +0000334
335
jadmanski0afbb632008-06-06 21:10:57 +0000336 def migrate_to_version_or_latest(self, version):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -0800337 """Migrates to either a specified version, or the latest version.
338
339 @param version: The version to which to migrate the database,
340 or None in order to migrate to the latest version.
341
342 """
jadmanski0afbb632008-06-06 21:10:57 +0000343 if version is None:
344 self.migrate_to_latest()
345 else:
346 self.migrate_to_version(version)
mblighaa383b72008-03-12 20:11:56 +0000347
348
jadmanski0afbb632008-06-06 21:10:57 +0000349 def do_sync_db(self, version=None):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -0800350 """Migrates the database.
351
352 @param version: The version to which to migrate the database.
353
354 """
showardeab66ce2009-12-23 00:03:56 +0000355 print 'Migration starting for database', self.get_db_name()
jadmanski0afbb632008-06-06 21:10:57 +0000356 self.migrate_to_version_or_latest(version)
357 print 'Migration complete'
mblighe8819cd2008-02-15 16:48:40 +0000358
359
jadmanski0afbb632008-06-06 21:10:57 +0000360 def test_sync_db(self, version=None):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -0800361 """Create a fresh database and run all migrations on it.
362
363 @param version: The version to which to migrate the database.
364
jadmanski0afbb632008-06-06 21:10:57 +0000365 """
366 self.initialize_test_db()
367 try:
showardeab66ce2009-12-23 00:03:56 +0000368 print 'Starting migration test on DB', self.get_db_name()
jadmanski0afbb632008-06-06 21:10:57 +0000369 self.migrate_to_version_or_latest(version)
370 # show schema to the user
371 os.system('mysqldump %s --no-data=true '
372 '--add-drop-table=false' %
373 self.get_mysql_args())
374 finally:
375 self.remove_test_db()
376 print 'Test finished successfully'
mblighe8819cd2008-02-15 16:48:40 +0000377
378
jadmanski0afbb632008-06-06 21:10:57 +0000379 def simulate_sync_db(self, version=None):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -0800380 """Creates a fresh DB, copies existing DB to it, then synchronizes it.
381
382 @param version: The version to which to migrate the database.
383
jadmanski0afbb632008-06-06 21:10:57 +0000384 """
jadmanski0afbb632008-06-06 21:10:57 +0000385 db_version = self.get_db_version()
jadmanski0afbb632008-06-06 21:10:57 +0000386 # don't do anything if we're already at the latest version
387 if db_version == self.get_latest_version():
388 print 'Skipping simulation, already at latest version'
389 return
390 # get existing data
showard12454c62010-01-15 19:15:14 +0000391 self.initialize_and_fill_test_db()
392 try:
393 print 'Starting migration test on DB', self.get_db_name()
394 self.migrate_to_version_or_latest(version)
395 finally:
396 self.remove_test_db()
397 print 'Test finished successfully'
398
399
400 def initialize_and_fill_test_db(self):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -0800401 """Initializes and fills up a test database."""
jadmanski0afbb632008-06-06 21:10:57 +0000402 print 'Dumping existing data'
403 dump_fd, dump_file = tempfile.mkstemp('.migrate_dump')
jadmanski0afbb632008-06-06 21:10:57 +0000404 os.system('mysqldump %s >%s' %
405 (self.get_mysql_args(), dump_file))
406 # fill in test DB
407 self.initialize_test_db()
408 print 'Filling in test DB'
409 os.system('mysql %s <%s' % (self.get_mysql_args(), dump_file))
showard0e73c852008-10-03 10:15:50 +0000410 os.close(dump_fd)
jadmanski0afbb632008-06-06 21:10:57 +0000411 os.remove(dump_file)
mblighe8819cd2008-02-15 16:48:40 +0000412
413
mblighc2f24452008-03-31 16:46:13 +0000414USAGE = """\
415%s [options] sync|test|simulate|safesync [version]
416Options:
417 -d --database Which database to act on
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -0800418 -f --force Don't ask for confirmation
419 --debug Print all DB queries"""\
mblighc2f24452008-03-31 16:46:13 +0000420 % sys.argv[0]
mblighe8819cd2008-02-15 16:48:40 +0000421
422
423def main():
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -0800424 """Main function for the migration script."""
jadmanski0afbb632008-06-06 21:10:57 +0000425 parser = OptionParser()
426 parser.add_option("-d", "--database",
427 help="which database to act on",
jamesren92afa562010-03-23 00:19:26 +0000428 dest="database",
429 default="AUTOTEST_WEB")
showard50c0e712008-09-22 16:20:37 +0000430 parser.add_option("-f", "--force", help="don't ask for confirmation",
431 action="store_true")
showard049b0fd2009-05-29 18:39:07 +0000432 parser.add_option('--debug', help='print all DB queries',
433 action='store_true')
jadmanski0afbb632008-06-06 21:10:57 +0000434 (options, args) = parser.parse_args()
showard250d84d2010-01-12 21:59:48 +0000435 manager = get_migration_manager(db_name=options.database,
436 debug=options.debug, force=options.force)
jadmanski0afbb632008-06-06 21:10:57 +0000437
438 if len(args) > 0:
439 if len(args) > 1:
440 version = int(args[1])
441 else:
442 version = None
443 if args[0] == 'sync':
444 manager.do_sync_db(version)
445 elif args[0] == 'test':
showard12454c62010-01-15 19:15:14 +0000446 manager.simulate=True
jadmanski0afbb632008-06-06 21:10:57 +0000447 manager.test_sync_db(version)
448 elif args[0] == 'simulate':
showard12454c62010-01-15 19:15:14 +0000449 manager.simulate=True
jadmanski0afbb632008-06-06 21:10:57 +0000450 manager.simulate_sync_db(version)
451 elif args[0] == 'safesync':
452 print 'Simluating migration'
showard12454c62010-01-15 19:15:14 +0000453 manager.simulate=True
jadmanski0afbb632008-06-06 21:10:57 +0000454 manager.simulate_sync_db(version)
455 print 'Performing real migration'
showard12454c62010-01-15 19:15:14 +0000456 manager.simulate=False
jadmanski0afbb632008-06-06 21:10:57 +0000457 manager.do_sync_db(version)
458 else:
459 print USAGE
460 return
461
462 print USAGE
mblighe8819cd2008-02-15 16:48:40 +0000463
464
showard250d84d2010-01-12 21:59:48 +0000465def get_migration_manager(db_name, debug, force):
Dennis Jeffreyd65bbd12013-03-05 10:59:18 -0800466 """Creates a MigrationManager object.
467
468 @param db_name: The database name.
469 @param debug: Whether to print debug messages.
470 @param force: Whether to force migration without asking for confirmation.
471
472 @return A created MigrationManager object.
473
474 """
showard250d84d2010-01-12 21:59:48 +0000475 database = database_connection.DatabaseConnection(db_name)
476 database.debug = debug
477 database.reconnect_enabled = False
478 database.connect()
479 return MigrationManager(database, force=force)
480
481
mblighe8819cd2008-02-15 16:48:40 +0000482if __name__ == '__main__':
jadmanski0afbb632008-06-06 21:10:57 +0000483 main()