Improve tracking of segments and segment utilization.
[cumulus.git] / python / cumulus / main.py
1 # Cumulus: Efficient Filesystem Backup to the Cloud
2 # Copyright (C) 2012 The Cumulus Developers
3 # See the AUTHORS file for a list of contributors.
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License along
16 # with this program; if not, write to the Free Software Foundation, Inc.,
17 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
19 """The Python-based Cumulus script.
20
21 This implements maintenance functions and is a wrapper around the C++
22 cumulus-backup program.
23 """
24
25 import datetime
26 import re
27 import sys
28
29 import cumulus
30 from cumulus import cmd_util
31 from cumulus import config
32
33 class FakeOptions:
34     pass
35
36 def prune_backups(backup_config, scheme):
37     store = cumulus.LowlevelDataStore(backup_config.get_global("dest"))
38     snapshot_re = re.compile(r"^(.*)-(.*)$")
39     retention = backup_config.get_retention_for_scheme(scheme)
40     expired_snapshots = []
41     for snapshot in sorted(store.list_snapshots()):
42         m = snapshot_re.match(snapshot)
43         if m.group(1) != scheme: continue
44         timestamp = m.group(2)
45         keep = retention.consider_snapshot(timestamp)
46         if not keep:
47             expired_snapshots.append(snapshot)
48     # The most recent snapshot is never removed.
49     if expired_snapshots: expired_snapshots.pop()
50     print expired_snapshots
51
52     # TODO: Clean up the expiration part...
53     for snapshot in expired_snapshots:
54         store.store.delete("snapshot", "snapshot-%s.lbs" % snapshot)
55
56     print "Collecting garbage..."
57     options = FakeOptions()
58     options.store = backup_config.get_global("dest")
59     options.dry_run = False
60     cmd_util.options = options
61     cmd_util.cmd_garbage_collect([])
62
63 def prune_localdb(backup_config, scheme, next_snapshot=None):
64     """Clean old snapshots out of the local database.
65
66     Clear old snapshots out of the local database, possibly in preparation for
67     running a new backup.  One snapshot of each configured retention period is
68     kept (i.e., one weekly and one daily), and the most recent snapshot is
69     always retained.  If next_snapshot is not None, it should be the timestamp
70     when (approximately) the next snapshot will be taken; if that snapshot
71     would be a daily, weekly, etc. snapshot, then it may result in the previous
72     snapshot of the same duration being evicted from the local database.
73
74     Note that in this sense, "evict" merely refers to tracking the snapshots in
75     the local database; this function does not delete backups from the backup
76     storage.
77     """
78     # Fetch the list of existing snapshots in the local database.  Pruning only
79     # makes sense if there are more than one snapshots present.
80     db = cumulus.LocalDatabase(backup_config.get_global("localdb"))
81     snapshots = sorted(db.list_snapshots(scheme))
82     if len(snapshots) <= 1:
83         return
84
85     # Classify the snapshots (daily, weekly, etc.) and keep the most recent one
86     # of each category.  Also ensure that the most recent snapshot is retained.
87     retention = backup_config.get_retention_for_scheme(scheme)
88     for snapshot in snapshots:
89         retention.consider_snapshot(snapshot)
90     if next_snapshot is not None:
91         retention.consider_snapshot(next_snapshot)
92     retained = set(retention.last_snapshots().values())
93     retained.add(snapshots[-1])
94     print retention.last_snapshots()
95     print retained
96     for s in snapshots:
97         print s, s in retained
98
99     evicted = [s for s in snapshots if s not in retained]
100     for s in evicted:
101         db.delete_snapshot(scheme, s)
102     db.garbage_collect()
103     db.commit()
104
105 def main(argv):
106     backup_config = config.CumulusConfig(argv[1])
107     for scheme in backup_config.backup_schemes():
108         print scheme
109         #prune_backups(backup_config, scheme)
110         prune_localdb(backup_config, scheme, datetime.datetime.utcnow())
111         #prune_localdb(backup_config, scheme, datetime.datetime(2013, 1, 1))
112
113 if __name__ == "__main__":
114     main(sys.argv)