#!/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.

import wiredtiger, wttest
import os, shutil
from helper import compare_files
from wtbackup import backup_base
from wtdataset import simple_key
from wtscenario import make_scenarios

# test_backup11.py
# Test cursor backup with a duplicate backup cursor.
class test_backup11(backup_base):
    conn_config= 'cache_size=1G,log=(enabled,file_max=100K)'
    dir='backup.dir'                    # Backup directory name
    pfx = 'test_backup'
    uri="table:test"

    def test_backup11(self):
        self.session.create(self.uri, "key_format=S,value_format=S")
        self.add_data(self.uri, 'key', 'value', True)
        # Open up the backup cursor. This causes a new log file to be created.
        # That log file is not part of the list returned. This is a full backup
        # primary cursor with incremental configured.
        os.mkdir(self.dir)
        config = 'incremental=(enabled,this_id="ID1")'
        bkup_c = self.session.open_cursor('backup:', None, config)

        # Add data while the backup cursor is open.
        self.add_data(self.uri, 'key', 'value', True)

        # Now make a full backup and track the log files.
        all_files = self.take_full_backup(self.dir, bkup_c)
        orig_logs = [file for file in all_files if "WiredTigerLog" in file]

        # Now open a duplicate backup cursor.
        # We *can* use a log target duplicate on an incremental primary backup so that
        # a backup process can get all the log files that occur while that primary cursor
        # is open.
        dup_logs = self.take_log_backup(bkup_c, self.dir, orig_logs)
        bkup_c.close()

        # Add more data
        self.add_data(self.uri, 'key', 'value', True)

        # Test error cases now.

        # - Incremental filename must be on duplicate, not primary.
        # Test this first because we currently do not have a primary open.
        config = 'incremental=(file=test.wt)'
        msg = "/file name can only be specified on a duplicate/"
        self.pr("Specify file on primary")
        self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
            lambda:self.assertEquals(self.session.open_cursor('backup:',
            None, config), 0), msg)

        # Open a non-incremental full backup cursor.
        # - An incremental duplicate must have an incremental primary.
        self.pr("Try to open an incremental on a non-incremental primary")
        bkup_c = self.session.open_cursor('backup:', None, None)
        msg = "/must have an incremental primary/"
        self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
            lambda:self.assertEquals(self.session.open_cursor(None,
            bkup_c, config), 0), msg)
        bkup_c.close()

        config = 'incremental=(src_id="ID1",this_id="ID2")'
        bkup_c = self.session.open_cursor('backup:', None, config)

        self.pr("Opened backup for error testing")

        # Now test all the error cases with an incremental primary open.
        # - We cannot specify consolidate on the duplicate cursor.
        config = 'incremental=(consolidate=true,file=test.wt)'
        msg = "/consolidation can only be specified on a primary/"
        self.pr("Test consolidation on a dup")
        self.pr("=========")
        # Test multiple duplicate backup cursors.
        self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
            lambda:self.assertEquals(self.session.open_cursor(None,
            bkup_c, config), 0), msg)

        # - We cannot make multiple incremental duplcate backup cursors.
        # - We cannot duplicate the duplicate backup cursor.
        config = 'incremental=(file=test.wt)'
        dupc = self.session.open_cursor(None, bkup_c, config)
        msg = "/already a duplicate backup cursor open/"
        self.pr("Test multiple dups")
        self.pr("=========")
        # Test multiple duplicate backup cursors.
        self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
            lambda:self.assertEquals(self.session.open_cursor(None,
            bkup_c, config), 0), msg)
        # Test duplicate of duplicate backup cursor.
        self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
            lambda:self.assertEquals(self.session.open_cursor(None,
            dupc, config), 0), msg)
        dupc.close()

        # - A duplicate cursor must specify incremental or log target.
        self.pr("Test dup and file target")
        self.pr("=========")
        msg = "/cannot be used for/"
        config = 'target=("file:test.wt")'
        self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
            lambda:self.assertEquals(self.session.open_cursor(None,
            bkup_c, config), 0), msg)

        # - We cannot mix block incremental with a log target on the same duplicate.
        self.pr("Test mixed targets")
        self.pr("=========")
        config = 'incremental=(file=test.wt),target=("log:")'
        msg = "/incremental backup incompatible/"
        self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
            lambda:self.assertEquals(self.session.open_cursor(None,
            bkup_c, config), 0), msg)

        # - Incremental ids must be on primary, not duplicate.
        self.pr("Test ids on dups")
        self.pr("=========")
        config = 'incremental=(src_id="ID1")'
        msg = "/specified on a primary/"
        self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
            lambda:self.assertEquals(self.session.open_cursor(None,
            bkup_c, config), 0), msg)
        config = 'incremental=(this_id="ID1")'
        self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
            lambda:self.assertEquals(self.session.open_cursor(None,
            bkup_c, config), 0), msg)

        # - Force stop must be on primary, not duplicate.
        self.pr("Test force stop")
        self.pr("=========")
        config = 'incremental=(force_stop=true)'
        self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
            lambda:self.assertEquals(self.session.open_cursor(None,
            bkup_c, config), 0), msg)

        bkup_c.close()

        # - Incremental must be opened on a primary with a source identifier.
        # Open a top level backup cursor without a source id.
        # Try to open an incremental cursor off this backup cursor.
        self.pr("Test incremental without source identifier on primary")
        self.pr("=========")
        config = 'incremental=(enabled,this_id="ID3")'
        bkup_c = self.session.open_cursor('backup:', None, config)
        ret = bkup_c.next()
        self.assertTrue(ret == 0)
        newfile = bkup_c.get_key()
        config = 'incremental=(file=' + newfile + ')'
        msg = '/known source identifier/'
        self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
            lambda: self.session.open_cursor(None, bkup_c, config), msg)
        bkup_c.close()

        # - Test opening a primary backup with an unknown source id.
        self.pr("Test incremental with unknown source identifier on primary")
        self.pr("=========")
        config = 'incremental=(enabled,src_id="ID_BAD",this_id="ID4")'
        self.assertRaises(wiredtiger.WiredTigerError,
            lambda: self.session.open_cursor('backup:', None, config))

        # - Test opening a primary backup with an id in WiredTiger namespace.
        self.pr("Test incremental with illegal src identifier using WiredTiger namespace")
        self.pr("=========")
        msg = '/name space may not/'
        config = 'incremental=(enabled,src_id="WiredTiger.0")'
        self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
            lambda: self.session.open_cursor('backup:', None, config), msg)

        # - Test opening a primary backup with an id in WiredTiger namespace.
        self.pr("Test incremental with illegal this identifier using WiredTiger namespace")
        self.pr("=========")
        config = 'incremental=(enabled,this_id="WiredTiger.ID")'
        self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
            lambda: self.session.open_cursor('backup:', None, config), msg)

        # - Test opening a primary backup with an id using illegal characters.
        self.pr("Test incremental with illegal source identifier using illegal colon character")
        self.pr("=========")
        msg = '/grouping characters/'
        config = 'incremental=(enabled,src_id="ID4:4.0")'
        self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
            lambda: self.session.open_cursor('backup:', None, config), msg)

        # - Test opening a primary backup with an id using illegal characters.
        self.pr("Test incremental with illegal this identifier using illegal colon character")
        self.pr("=========")
        config = 'incremental=(enabled,this_id="ID4:4.0")'
        self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
            lambda: self.session.open_cursor('backup:', None, config), msg)

        # - Test opening a primary backup with the same source id and this id (new id).
        self.pr("Test incremental with the same new source and this identifiers")
        self.pr("=========")
        config = 'incremental=(enabled,src_id="IDSAME",this_id="IDSAME")'
        self.assertRaises(wiredtiger.WiredTigerError,
            lambda: self.session.open_cursor('backup:', None, config))

        # - Test opening a primary backup with the same source id and this id (reusing id).
        self.pr("Test incremental with the same re-used source and this identifiers")
        self.pr("=========")
        msg = '/already in use/'
        config = 'incremental=(enabled,src_id="ID2",this_id="ID2")'
        self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
            lambda: self.session.open_cursor('backup:', None, config), msg)

        # After the full backup, open and recover the backup database.
        backup_conn = self.wiredtiger_open(self.dir)
        backup_conn.close()

if __name__ == '__main__':
    wttest.run()
