#!/usr/bin/env python # # Public Domain 2014-present MongoDB, Inc. # Public Domain 2008-2014 WiredTiger, Inc. # # This is free and unencumbered software released into the public domain. # # Anyone is free to copy, modify, publish, use, compile, sell, or # distribute this software, either in source code form or as a compiled # binary, for any purpose, commercial or non-commercial, and by any # means. # # In jurisdictions that recognize copyright laws, the author or authors # of this software dedicate any and all copyright interest in the # software to the public domain. We make this dedication for the benefit # of the public at large and to the detriment of our heirs and # successors. We intend this dedication to be an overt act of # relinquishment in perpetuity of all present and future rights to this # software under copyright law. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. # # [TEST_TAGS] # backup:correctness:full_backup # [END_TAGS] # import glob import os import shutil import string import time from wtbackup import backup_base import wiredtiger from wtdataset import SimpleDataSet, ComplexDataSet, ComplexLSMDataSet from helper import compare_files # test_backup.py # Utilities: wt backup # Test backup (both backup cursors and the wt backup command). class test_backup(backup_base): dir='backup.dir' # Backup directory name pfx = 'test_backup' objs = [ ( 'file:' + pfx + '.1', SimpleDataSet, 0), ( 'file:' + pfx + '.2', SimpleDataSet, 0), ('table:' + pfx + '.3', SimpleDataSet, 0), ('table:' + pfx + '.4', SimpleDataSet, 0), ('table:' + pfx + '.5', ComplexDataSet, 0), ('table:' + pfx + '.6', ComplexDataSet, 0), ('table:' + pfx + '.7', ComplexLSMDataSet, 1), ('table:' + pfx + '.8', ComplexLSMDataSet, 1), ] # Test simple backup cursor open/close. def test_cursor_simple(self): cursor = self.session.open_cursor('backup:', None, None) cursor.close() # Test you can't have more than one backup cursor open at a time. def test_cursor_single(self): cursor = self.session.open_cursor('backup:', None, None) msg = '/there is already a backup cursor open/' self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: self.session.open_cursor('backup:', None, None), msg) cursor.close() # Test backup of a database using the wt backup command. def test_backup_database(self): self.populate(self.objs) os.mkdir(self.dir) self.runWt(['backup', self.dir]) # Make sure all the files were copied. self.runWt(['list'], outfilename='outfile.orig') self.runWt(['-h', self.dir, 'list'], outfilename='outfile.backup') self.assertEqual( True, compare_files(self, 'outfile.orig', 'outfile.backup')) # And that the contents are the same. for i in self.objs: self.compare_backups(i[0], self.dir, './') # Backup a set of chosen tables/files using the wt backup command. def backup_table(self, l): # Remove any previous backup directories. shutil.rmtree(self.dir, ignore_errors=True) os.mkdir(self.dir) # Build a command line of objects to back up and run wt. o = 'backup' for i in range(0, len(self.objs)): if i in l: o += ' -t ' + self.objs[i][0] o += ' ' + self.dir self.runWt(o.split()) # Confirm the objects we backed up exist, with correct contents. for i in range(0, len(self.objs)): if i in l: self.compare_backups(self.objs[i][0], self.dir, './') # Confirm the other objects don't exist. for i in range(0, len(self.objs)): if i not in l: self.confirmPathDoesNotExist(self.objs[i][0], self.dir) # Test backup of database subsets. def test_backup_table(self): self.populate(self.objs) self.backup_table([0,2,4,6]) self.backup_table([1,3,5,7]) self.backup_table([0,1,2]) self.backup_table([3,4,5]) self.backup_table([5,6,7]) # Test cursor reset runs through the list twice. def test_cursor_reset(self): self.populate(self.objs) cursor = self.session.open_cursor('backup:', None, None) i = 0 while True: ret = cursor.next() if ret != 0: break i += 1 self.assertEqual(ret, wiredtiger.WT_NOTFOUND) total = i * 2 cursor.reset() while True: ret = cursor.next() if ret != 0: break i += 1 self.assertEqual(ret, wiredtiger.WT_NOTFOUND) self.assertEqual(i, total) # Test interaction between checkpoints and a backup cursor. def test_checkpoint_delete(self): # You cannot name checkpoints including LSM tables, skip those. self.populate(self.objs, False, True) # Confirm checkpoints are being deleted. self.session.checkpoint("name=one") self.session.checkpoint("name=two,drop=(one)") self.assertRaises(wiredtiger.WiredTigerError, lambda: self.session.open_cursor( self.objs[0][0], None, "checkpoint=one")) # Confirm opening a backup cursor causes checkpoint to fail if dropping # a named checkpoint created before the backup cursor, but does not stop a # default checkpoint. cursor = self.session.open_cursor('backup:', None, None) self.session.checkpoint() msg = '/checkpoints cannot be deleted during a hot backup/' self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: self.session.checkpoint("name=three,drop=(two)"), msg) self.session.checkpoint() self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: self.session.checkpoint("name=three,drop=(two)"), msg) self.session.checkpoint() # Need to pause a couple seconds; checkpoints that are assigned the same timestamp as # the backup will be pinned, even if they occur after the backup starts. time.sleep(2) # Confirm that a named checkpoint created after a backup cursor can be dropped. self.session.checkpoint("name=four") self.session.checkpoint("drop=(four)") self.assertRaises(wiredtiger.WiredTigerError, lambda: self.session.open_cursor( self.objs[0][0], None, "checkpoint=four")) # Confirm that after closing the backup cursor the original named checkpoint can # be deleted. cursor.close() self.session.checkpoint("drop=(two)") self.assertRaises(wiredtiger.WiredTigerError, lambda: self.session.open_cursor( self.objs[0][0], None, "checkpoint=two")) if __name__ == '__main__': wttest.run()