X-Git-Url: http://git.vrable.net/?a=blobdiff_plain;f=python%2Fcumulus%2F__init__.py;h=d8b6814e8c2fc034ccaaf18d060a30b14a1b4a78;hb=fbe7425ae37564a99eb49133561eea5f1a6c7877;hp=2ac9be4e9c9229f82361165c5ab1eb17ef69c7a4;hpb=76ce8210bee6f9c2dbefab56873a9b2847d92a13;p=cumulus.git diff --git a/python/cumulus/__init__.py b/python/cumulus/__init__.py index 2ac9be4..d8b6814 100644 --- a/python/cumulus/__init__.py +++ b/python/cumulus/__init__.py @@ -1,3 +1,21 @@ +# Cumulus: Efficient Filesystem Backup to the Cloud +# Copyright (C) 2008-2009, 2012 The Cumulus Developers +# See the AUTHORS file for a list of contributors. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + """High-level interface for working with Cumulus archives. This module provides an easy interface for reading from and manipulating @@ -128,7 +146,7 @@ class LowlevelDataStore: """Return a file-like object for reading data from the given file.""" (type, filename) = self._classify(filename) - return self.store.get(type, filename) + return self.store.get(type + "/" + filename) def lowlevel_stat(self, filename): """Return a dictionary of information about the given file. @@ -138,7 +156,7 @@ class LowlevelDataStore: """ (type, filename) = self._classify(filename) - return self.store.stat(type, filename) + return self.store.stat(type + "/" + filename) # Slightly higher-level list methods. def list_snapshots(self): @@ -173,7 +191,7 @@ class ObjectStore: def parse_ref(refstr): m = re.match(r"^zero\[(\d+)\]$", refstr) if m: - return ("zero", None, None, (0, int(m.group(1)))) + return ("zero", None, None, (0, int(m.group(1)), False)) m = re.match(r"^([-0-9a-f]+)\/([0-9a-f]+)(\(\S+\))?(\[(((\d+)\+)?(\d+)|=(\d+))\])?$", refstr) if not m: return @@ -230,7 +248,7 @@ class ObjectStore: yield (path[1], data_obj.read()) def load_snapshot(self, snapshot): - file = self.store.lowlevel_open("snapshot-" + snapshot + ".lbs") + file = self.store.lowlevel_open("snapshot-" + snapshot + ".cumulus") return file.read().splitlines(True) def extract_segment(self, segment): @@ -514,7 +532,26 @@ class LocalDatabase: schemes.sort() return schemes - def garbage_collect(self, scheme, intent=1.0): + def list_snapshots(self, scheme): + """Return a list of snapshots for the given scheme.""" + cur = self.cursor() + cur.execute("select name from snapshots") + snapshots = [row[0] for row in cur.fetchall()] + snapshots.sort() + return snapshots + + def delete_snapshot(self, scheme, name): + """Remove the specified snapshot from the database. + + Warning: This does not garbage collect all dependent data in the + database, so it must be followed by a call to garbage_collect() to make + the database consistent. + """ + cur = self.cursor() + cur.execute("delete from snapshots where scheme = ? and name = ?", + (scheme, name)) + + def prune_old_snapshots(self, scheme, intent=1.0): """Delete entries from old snapshots from the database. Only snapshots with the specified scheme name will be deleted. If @@ -561,6 +598,16 @@ class LocalDatabase: first = False max_intent = max(max_intent, snap_intent) + self.garbage_collect() + + def garbage_collect(self): + """Garbage-collect unreachable segment and object data. + + Remove all segments and checksums which is not reachable from the + current set of snapshots stored in the local database. + """ + cur = self.cursor() + # Delete entries in the segments_used table which are for non-existent # snapshots. cur.execute("""delete from segments_used @@ -572,16 +619,10 @@ class LocalDatabase: cur.execute("""delete from segments where segmentid not in (select segmentid from segments_used)""") - # Delete unused objects in the block_index table. By "unused", we mean - # any object which was stored in a segment which has been deleted, and - # any object in a segment which was marked for cleaning and has had - # cleaning performed already (the expired time is less than the current - # largest snapshot id). + # Delete dangling objects in the block_index table. cur.execute("""delete from block_index - where segmentid not in (select segmentid from segments) - or segmentid in (select segmentid from segments - where expire_time < ?)""", - (last_snapshotid,)) + where segmentid not in + (select segmentid from segments)""") # Remove sub-block signatures for deleted objects. cur.execute("""delete from subblock_signatures