Minor fix to segment cleaning.
[cumulus.git] / lbs-util
index 5c12438..da89060 100755 (executable)
--- a/lbs-util
+++ b/lbs-util
@@ -6,9 +6,9 @@ import getpass, os, stat, sys, time
 from optparse import OptionParser
 import lbs
 
-# We support up to "LBS Snapshot v0.2" formats, but are also limited by the lbs
+# We support up to "LBS Snapshot v0.6" formats, but are also limited by the lbs
 # module.
-FORMAT_VERSION = min(lbs.FORMAT_VERSION, (0, 2))
+FORMAT_VERSION = min(lbs.FORMAT_VERSION, (0, 6))
 
 def check_version(format):
     ver = lbs.parse_metadata_version(format)
@@ -22,6 +22,8 @@ parser.add_option("--store", dest="store",
                   help="specify path to backup data store")
 parser.add_option("--localdb", dest="localdb",
                   help="specify path to local database")
+parser.add_option("--intent", dest="intent", default=1.0,
+                  help="give expected next snapshot type when cleaning")
 (options, args) = parser.parse_args(sys.argv[1:])
 
 # Read a passphrase from the user and store it in the LBS_GPG_PASSPHRASE
@@ -38,8 +40,8 @@ def cmd_prune_db():
     db = lbs.LocalDatabase(options.localdb)
 
     # Delete old snapshots from the local database.
-    db.garbage_collect()
-    db.commit()
+    #db.garbage_collect()
+    #db.commit()
 
 # Run the segment cleaner.
 # Syntax: $0 --localdb=LOCALDB clean
@@ -47,7 +49,9 @@ def cmd_clean(clean_threshold=7.0):
     db = lbs.LocalDatabase(options.localdb)
 
     # Delete old snapshots from the local database.
-    db.garbage_collect()
+    intent = float(options.intent)
+    for s in db.list_schemes():
+        db.garbage_collect(s, intent)
 
     # Expire segments which are poorly-utilized.
     for s in db.get_segment_cleaning_list():
@@ -132,13 +136,14 @@ def cmd_verify_snapshots(snapshots):
     lowlevel = lbs.LowlevelDataStore(options.store)
     store = lbs.ObjectStore(lowlevel)
     for s in snapshots:
+        lbs.accessed_segments.clear()
         print "#### Snapshot", s
         d = lbs.parse_full(store.load_snapshot(s))
         check_version(d['Format'])
         print "## Root:", d['Root']
         metadata = lbs.iterate_metadata(store, d['Root'])
         for m in metadata:
-            if m.fields['type'] != '-': continue
+            if m.fields['type'] not in ('-', 'f'): continue
             print "%s [%d bytes]" % (m.fields['name'], int(m.fields['size']))
             verifier = lbs.ChecksumVerifier(m.fields['checksum'])
             size = 0
@@ -150,6 +155,17 @@ def cmd_verify_snapshots(snapshots):
                 raise ValueError("File size does not match!")
             if not verifier.valid():
                 raise ValueError("Bad checksum found")
+
+        # Verify that the list of segments included with the snapshot was
+        # actually accurate: covered all segments that were really read, and
+        # doesn't contain duplicates.
+        listed_segments = set(d['Segments'].split())
+        if lbs.accessed_segments - listed_segments:
+            print "Error: Some segments not listed in descriptor!"
+            print sorted(list(lbs.accessed_segments - listed_segments))
+        if listed_segments - lbs.accessed_segments :
+            print "Warning: Extra unused segments listed in descriptor!"
+            print sorted(list(listed_segments - lbs.accessed_segments))
     store.cleanup()
 
 # Restore a snapshot, or some subset of files from it
@@ -181,7 +197,7 @@ def cmd_restore_snapshot(args):
             if not os.path.isdir(path):
                 os.makedirs(path)
 
-            if m.items.type == '-':
+            if m.items.type in ('-', 'f'):
                 file = open(destpath, 'wb')
                 verifier = lbs.ChecksumVerifier(m.items.checksum)
                 size = 0
@@ -199,7 +215,12 @@ def cmd_restore_snapshot(args):
                 if filename != '.':
                     os.mkdir(destpath)
             elif m.items.type == 'l':
-                os.symlink(m.items.contents, destpath)
+                try:
+                    target = m.items.target
+                except:
+                    # Old (v0.2 format) name for 'target'
+                    target = m.items.contents
+                os.symlink(target, destpath)
             elif m.items.type == 'p':
                 os.mkfifo(destpath)
             elif m.items.type in ('c', 'b'):