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.
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.
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.
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.
19 """The Python-based Cumulus script.
21 This implements maintenance functions and is a wrapper around the C++
22 cumulus-backup program.
25 from __future__ import division, print_function, unicode_literals
32 from cumulus import cmd_util
33 from cumulus import config
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)
49 expired_snapshots.append(snapshot)
50 # The most recent snapshot is never removed.
51 if expired_snapshots: expired_snapshots.pop()
52 print(expired_snapshots)
54 # TODO: Clean up the expiration part...
55 for snapshot in expired_snapshots:
56 store.store.delete("snapshot", "snapshot-%s.lbs" % snapshot)
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([])
65 def prune_localdb(backup_config, scheme, next_snapshot=None):
66 """Clean old snapshots out of the local database.
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.
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
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:
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())
99 print(s, s in retained)
101 evicted = [s for s in snapshots if s not in retained]
103 db.delete_snapshot(scheme, s)
108 backup_config = config.CumulusConfig(argv[1])
109 for scheme in backup_config.backup_schemes():
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))
115 if __name__ == "__main__":