8d4156c398910bd1d48f3f3c9dd45a8651f7b192
[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 from __future__ import division, print_function, unicode_literals
26
27 import datetime
28 import re
29 import sys
30
31 import cumulus
32 from cumulus import cmd_util
33 from cumulus import config
34
35 class FakeOptions:
36     pass
37
38 def prune_backups(backup_config, scheme):
39     store = cumulus.LowlevelDataStore(backup_config.get_global("dest"))
40     snapshot_re = re.compile(r"^(.*)-(.*)$")
41     retention = backup_config.get_retention_for_scheme(scheme)
42     expired_snapshots = []
43     for snapshot in sorted(store.list_snapshots()):
44         m = snapshot_re.match(snapshot)
45         if m.group(1) != scheme: continue
46         timestamp = m.group(2)
47         keep = retention.consider_snapshot(timestamp)
48         if not keep:
49             expired_snapshots.append(snapshot)
50     # The most recent snapshot is never removed.
51     if expired_snapshots: expired_snapshots.pop()
52     print(expired_snapshots)
53
54     # TODO: Clean up the expiration part...
55     for snapshot in expired_snapshots:
56         store.store.delete("snapshot", "snapshot-%s.lbs" % snapshot)
57
58     print("Collecting garbage...")
59     options = FakeOptions()
60     options.store = backup_config.get_global("dest")
61     options.dry_run = False
62     cmd_util.options = options
63     cmd_util.cmd_garbage_collect([])
64
65 def prune_localdb(backup_config, scheme, next_snapshot=None):
66     """Clean old snapshots out of the local database.
67
68     Clear old snapshots out of the local database, possibly in preparation for
69     running a new backup.  One snapshot of each configured retention period is
70     kept (i.e., one weekly and one daily), and the most recent snapshot is
71     always retained.  If next_snapshot is not None, it should be the timestamp
72     when (approximately) the next snapshot will be taken; if that snapshot
73     would be a daily, weekly, etc. snapshot, then it may result in the previous
74     snapshot of the same duration being evicted from the local database.
75
76     Note that in this sense, "evict" merely refers to tracking the snapshots in
77     the local database; this function does not delete backups from the backup
78     storage.
79     """
80     # Fetch the list of existing snapshots in the local database.  Pruning only
81     # makes sense if there are more than one snapshots present.
82     db = cumulus.LocalDatabase(backup_config.get_global("localdb"))
83     snapshots = sorted(db.list_snapshots(scheme))
84     if len(snapshots) <= 1:
85         return
86
87     # Classify the snapshots (daily, weekly, etc.) and keep the most recent one
88     # of each category.  Also ensure that the most recent snapshot is retained.
89     retention = backup_config.get_retention_for_scheme(scheme)
90     for snapshot in snapshots:
91         retention.consider_snapshot(snapshot)
92     if next_snapshot is not None:
93         retention.consider_snapshot(next_snapshot)
94     retained = set(retention.last_snapshots().values())
95     retained.add(snapshots[-1])
96     print(retention.last_snapshots())
97     print(retained)
98     for s in snapshots:
99         print(s, s in retained)
100
101     evicted = [s for s in snapshots if s not in retained]
102     for s in evicted:
103         db.delete_snapshot(scheme, s)
104     db.garbage_collect()
105     db.commit()
106
107 def main(argv):
108     backup_config = config.CumulusConfig(argv[1])
109     for scheme in backup_config.backup_schemes():
110         print(scheme)
111         #prune_backups(backup_config, scheme)
112         prune_localdb(backup_config, scheme, datetime.datetime.utcnow())
113         #prune_localdb(backup_config, scheme, datetime.datetime(2013, 1, 1))
114
115 if __name__ == "__main__":
116     main(sys.argv)