69 lines
2.1 KiB
Python
Executable File
69 lines
2.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Compare two Bongo PAX backups while ignoring generated timestamps."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import hashlib
|
|
import tarfile
|
|
|
|
|
|
GENERATED_TIMESTAMP_PROPERTIES = {
|
|
"BONGO.nmap.created",
|
|
"BONGO.nmap.lastmodified",
|
|
}
|
|
|
|
|
|
def read_archive(path):
|
|
result = {}
|
|
with tarfile.open(path, "r:*") as archive:
|
|
for member in archive.getmembers():
|
|
payload = b""
|
|
if member.isfile():
|
|
source = archive.extractfile(member)
|
|
if source is None:
|
|
raise ValueError("cannot read %s from %s" % (
|
|
member.name, path))
|
|
payload = source.read()
|
|
metadata = {
|
|
key: value for key, value in member.pax_headers.items()
|
|
if key not in GENERATED_TIMESTAMP_PROPERTIES
|
|
}
|
|
result[member.name] = (
|
|
member.isdir(), member.isfile(), member.size, member.mtime,
|
|
hashlib.sha256(payload).hexdigest(), metadata)
|
|
return result
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("source", help="backup made before restore")
|
|
parser.add_argument("restored", help="backup made after restore")
|
|
args = parser.parse_args()
|
|
|
|
source = read_archive(args.source)
|
|
restored = read_archive(args.restored)
|
|
if source != restored:
|
|
missing = sorted(set(source) - set(restored))
|
|
extra = sorted(set(restored) - set(source))
|
|
changed = sorted(
|
|
name for name in set(source) & set(restored)
|
|
if source[name] != restored[name])
|
|
if missing:
|
|
print("Missing:", ", ".join(missing))
|
|
if extra:
|
|
print("Extra:", ", ".join(extra))
|
|
if changed:
|
|
print("Changed:", ", ".join(changed))
|
|
return 1
|
|
|
|
file_count = sum(1 for item in source.values() if item[1])
|
|
directory_count = sum(1 for item in source.values() if item[0])
|
|
print("PASS: %d files and %d collections are equivalent" % (
|
|
file_count, directory_count))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|