3 # queries a bacula database for volume names
4 # and then removed old volumes from disk that are no longer
5 # referenced in the database
7 # Copyright 2010, 2011, 2013, 2017 Peter Palfrader
9 # Permission is hereby granted, free of charge, to any person obtaining
10 # a copy of this software and associated documentation files (the
11 # "Software"), to deal in the Software without restriction, including
12 # without limitation the rights to use, copy, modify, merge, publish,
13 # distribute, sublicense, and/or sell copies of the Software, and to
14 # permit persons to whom the Software is furnished to do so, subject to
15 # the following conditions:
17 # The above copyright notice and this permission notice shall be
18 # included in all copies or substantial portions of the Software.
20 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 import psycopg2.extras
34 from datetime import datetime, timedelta
36 parser = argparse.ArgumentParser()
37 parser.add_argument('-d', '--db-connect-string', metavar='connect-string', dest='db',
38 help='Database connect string')
39 parser.add_argument('-D', '--db-connect-string-file', metavar='FILE', dest='dbfile',
40 default='/etc/dsa/bacula-reader-database',
41 help='File to read database connect string from (/etc/dsa/bacula-reader-database)')
42 parser.add_argument('-r', '--rootdir', metavar='DIR', dest='root',
43 default='/srv/bacula',
44 help='Directory to start walk in')
45 parser.add_argument('-a', '--age', metavar='DAYS', dest='age',
46 default=60.0, type=float,
47 help='Min age to delete files')
48 parser.add_argument('-v', '--verbose', dest='verbose', action='store_true')
49 parser.add_argument('-n', '--no-do', dest='nodo', action='store_true')
50 args = parser.parse_args()
52 whitelist = ['.nobackup']
54 if args.db is not None:
56 elif args.dbfile is not None:
57 args.db = open(args.dbfile).read().rstrip()
59 print >>sys.stderr, 'Need one of -d or -D.'
62 cutoff = datetime.now() - timedelta(days=args.age)
64 conn = psycopg2.connect(args.db)
65 cursor = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
70 media = set(r['volumename'] for r in cursor.fetchall())
72 for path, dirs, files in os.walk(args.root):
76 if f in media: continue
77 if f in whitelist: continue
78 full = os.path.join(path, f)
80 mtime = datetime.fromtimestamp(int(st.st_mtime))
81 if mtime >= cutoff: continue
85 print("%s %7d %s"%(mtime, st.st_size, full))
88 print("Would delete %s %7d %s"%(mtime, st.st_size, full))