## tests ##
###############################################################################
-class RecoverTest (BaseTest):
+class DefectiveTest (BaseTest):
"""
Disaster recovery: restore corrupt backups.
"""
os.system("rm -rf source_dir source_dir2 backup_dir*")
+class RecoverTest (DefectiveTest):
+ """
+ Recover: restore corrupt backups from index file information.
+ """
+
def test_recover_corrupt (self):
"""
Perform various damaging actions that cause unreadable objects.
shutil.rmtree (self.dst_path)
+class RescueTest (DefectiveTest):
+ """
+ Rescue: restore corrupt backups from backup set that is damaged to a degree
+ that the index file is worthless.
+ """
+
+ def test_rescue_corrupt (self):
+ """
+ Perform various damaging actions that cause unreadable objects, then
+ attempt to extract objects regardless.
+ """
+ mode = self.COMPRESSION or "#"
+ bak_path = "backup_dir"
+ backup_file = "the_full_backup_%0.2d.tar"
+ backup_full = ("%s/%s" % (bak_path, backup_file)) % 0
+ index_file = "the_full_index"
+
+ if self.COMPRESSION is not None:
+ backup_file += ".gz"
+ backup_full += ".gz"
+ index_file += ".gz"
+
+ if self.PASSWORD is not None:
+ backup_file = "%s.%s" % (backup_file, deltatar.PDTCRYPT_EXTENSION)
+ backup_full = "%s.%s" % (backup_full, deltatar.PDTCRYPT_EXTENSION)
+ index_file = "%s.%s" % (index_file , deltatar.PDTCRYPT_EXTENSION)
+
+ if self.VOLUMES > 1:
+ # add n files for one nth the volume size each, corrected
+ # for metadata and tar block overhead
+ fsiz = int ( ( TEST_VOLSIZ
+ / (TEST_FILESPERVOL * VOLUME_OVERHEAD))
+ * 1024 * 1024)
+ fcnt = (self.VOLUMES - 1) * TEST_FILESPERVOL
+ for i in range (fcnt):
+ nvol, invol = divmod(i, TEST_FILESPERVOL)
+ f = "dummy_vol_%d_n_%0.2d" % (nvol, invol)
+ self.hash [f] = self.create_file ("%s/%s"
+ % (self.src_path, f),
+ fsiz,
+ random=True)
+
+ def vname (_x, _y, n, *a, **kwa):
+ return backup_file % n
+
+ dtar = deltatar.DeltaTar (mode=mode,
+ logger=None,
+ password=self.PASSWORD,
+ index_name_func=lambda _: index_file,
+ volume_name_func=vname)
+
+ dtar.create_full_backup \
+ (source_path=self.src_path, backup_path=bak_path,
+ max_volume_size=1)
+
+ if self.PASSWORD is not None:
+ # ensure all files are at least superficially in PDT format
+ for f in os.listdir (bak_path):
+ assert is_pdt_encrypted ("%s/%s" % (bak_path, f))
+
+ # first restore must succeed
+ dtar.restore_backup(target_path=self.dst_path,
+ backup_indexes_paths=[
+ "%s/%s" % (bak_path, index_file)
+ ])
+ for key, value in self.hash.items ():
+ f = "%s/%s" % (self.dst_path, key)
+ assert os.path.exists (f)
+ assert value == self.md5sum (f)
+ shutil.rmtree (self.dst_path)
+ shutil.rmtree (self.src_path)
+
+ self.CORRUPT (backup_full,
+ self.COMPRESSION is not None,
+ self.PASSWORD is not None)
+
+ # normal restore must fail
+ try:
+ dtar.restore_backup(target_path=self.dst_path,
+ backup_tar_path=backup_full)
+ except tarfile.CompressionError:
+ if self.PASSWORD is not None or self.COMPRESSION is not None:
+ pass
+ else:
+ raise
+ except tarfile.ReadError:
+ # can happen with all three modes
+ pass
+ except tarfile.DecryptionError:
+ if self.PASSWORD is not None:
+ pass
+ else:
+ raise
+
+ os.chdir (self.pwd) # not restored due to the error above
+ # but recover will succeed
+ failed = dtar.rescue_backup(target_path=self.dst_path,
+ backup_indexes_paths=[
+ "%s/%s" % (bak_path, index_file)
+ ])
+
+ assert len (failed) == self.FAILURES
+
+ # with one file missing
+ missing = []
+ mismatch = []
+ for key, value in self.hash.items ():
+ kkey = "%s/%s" % (self.dst_path, key)
+ if os.path.exists (kkey):
+ if value != self.md5sum (kkey):
+ mismatch.append (key)
+ else:
+ missing.append (key)
+
+ ssert len (missing) == (self.MISSING if self.MISSING is not None
+ else self.FAILURES)
+ assert len (mismatch) == self.MISMATCHES
+
+ shutil.rmtree (self.dst_path)
+
+
class RecoverCorruptPayloadTestBase (RecoverTest):
COMPRESSION = None
PASSWORD = None