#!/usr/bin/env python # # Public Domain 2014-2018 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. import glob import os import shutil import string from suite_subprocess import suite_subprocess import wiredtiger, wttest 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(wttest.WiredTigerTestCase, suite_subprocess): 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), ] # Populate a set of objects. def populate(self, skiplsm): for i in self.objs: if i[2]: if skiplsm: continue i[1](self, i[0], 100).populate() # Compare the original and backed-up files using the wt dump command. def compare(self, uri): self.runWt(['dump', uri], outfilename='orig') self.runWt(['-h', self.dir, 'dump', uri], outfilename='backup') self.assertEqual(True, compare_files(self, 'orig', 'backup')) # 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(0) 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(i[0]) # Check that a URI doesn't exist, both the meta-data and the file names. def confirmPathDoesNotExist(self, uri): conn = self.wiredtiger_open(self.dir) session = conn.open_session() self.assertRaises(wiredtiger.WiredTigerError, lambda: session.open_cursor(uri, None, None)) conn.close() self.assertEqual( glob.glob(self.dir + '*' + uri.split(":")[1] + '*'), [], 'confirmPathDoesNotExist: URI exists, file name matching \"' + uri.split(":")[1] + '\" found') # 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(self.objs[i][0]) # 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]) # Test backup of database subsets. def test_backup_table(self): self.populate(0) 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(0) 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 that named checkpoints can't be deleted while backup cursors are # open, but that normal checkpoints continue to work. def test_checkpoint_delete(self): # You cannot name checkpoints including LSM tables, skip those. self.populate(1) # 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, 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() cursor.close() if __name__ == '__main__': wttest.run()