10
10
__metaclass__ = type
12
12
# pylint: disable-msg=W0403
13
import _pythonpath # Sort PYTHONPATH
13
import _pythonpath # Sort PYTHONPATH
15
15
from cStringIO import StringIO
18
18
from optparse import OptionParser
20
21
from tempfile import NamedTemporaryFile
21
22
from textwrap import dedent
101
102
def to_seconds(td):
102
103
"""Convert a timedelta to seconds."""
103
return td.days * (24 * 60 * 60) + td.seconds + td.microseconds / 1000000.0
104
return td.days * (24*60*60) + td.seconds + td.microseconds/1000000.0
106
107
def report_patch_times(con, todays_patches):
178
179
# The first script applies the DB patches to all nodes.
180
181
# First make sure the cluster is synced.
181
log.info("Waiting for cluster to sync, pre-update.")
182
log.info("Waiting for cluster to sync.")
182
183
replication.helpers.sync(timeout=600)
184
185
outf = StringIO()
248
249
# Execute the script with slonik.
249
log.info("slonik(1) schema upgrade script generated. Invoking.")
250
250
if not replication.helpers.execute_slonik(outf.getvalue()):
251
251
log.fatal("Aborting.")
253
log.info("slonik(1) schema upgrade script completed.")
255
253
# Cleanup our temporary files - they applied successfully.
256
254
for temporary_file in temporary_files:
258
256
del temporary_files
260
258
# Wait for replication to sync.
261
log.info("Waiting for patches to apply to slaves and cluster to sync.")
262
259
replication.helpers.sync(timeout=0)
264
261
# The db patches have now been applied to all nodes, and we are now
351
348
print >> outf, dedent("""\
352
349
echo 'Subscribing holding set to @node%d_node.';
354
id=@holding_set, provider=@master_node,
355
receiver=@node%d_node, forward=yes);
357
origin=@master_node, confirmed=all,
358
wait on=@master_node, timeout=0);
352
provider=@master_node, receiver=@node%d_node, forward=yes);
359
353
echo 'Waiting for sync';
360
354
sync (id=@master_node);
362
356
origin=@master_node, confirmed=ALL,
363
wait on=@master_node, timeout=0);
357
wait on=@master_node, timeout=0
364
359
""" % (slave_node.node_id, slave_node.node_id))
366
361
print >> outf, dedent("""\
367
362
echo 'Merging holding set to lpmain';
369
id=@lpmain_set, add id=@holding_set, origin=@master_node);
364
id=@lpmain_set, add id=@holding_set, origin=@master_node
372
368
# Execute the script and sync.
374
"Generated slonik(1) script to replicate new objects. Invoking.")
375
369
if not replication.helpers.execute_slonik(outf.getvalue()):
376
370
log.fatal("Aborting.")
378
"slonik(1) script to replicate new objects completed.")
379
log.info("Waiting for sync.")
380
371
replication.helpers.sync(timeout=0)
382
log.info("No new tables or sequences to replicate.")
384
373
# We also scan for tables and sequences we want to drop and do so using
385
374
# a final slonik script. Instead of dropping tables in the DB patch,
425
log.info("Generated slonik(1) script to drop tables. Invoking.")
426
414
if not replication.helpers.execute_slonik(sk.getvalue()):
427
415
log.fatal("Aborting.")
428
log.info("slonik(1) script to drop tables completed.")
431
418
# Now drop sequences. We don't do this at the same time as the tables,
471
log.info("Generated slonik(1) script to drop sequences. Invoking.")
472
458
if not replication.helpers.execute_slonik(sk.getvalue()):
473
459
log.fatal("Aborting.")
474
log.info("slonik(1) script to drop sequences completed.")
475
log.info("Waiting for final sync.")
476
460
replication.helpers.sync(timeout=0)
491
475
m = re.search('patch-(\d+)-(\d+)-(\d).sql$', patch_file)
493
477
log.fatal('Invalid patch filename %s' % repr(patch_file))
496
480
major, minor, patch = [int(i) for i in m.groups()]
497
481
if (major, minor, patch) in dbpatches:
498
continue # This patch has already been applied
482
continue # This patch has already been applied
499
483
log.debug("Found patch %d.%d.%d -- %s" % (
500
484
major, minor, patch, patch_file
520
504
if (major, minor, patch) not in applied_patches(con):
521
505
log.fatal("%s failed to update LaunchpadDatabaseRevision correctly"
525
509
# Commit changes if we allow partial updates.
526
510
if options.commit and options.partial: