1*a1a3b679SAndreas Boehler#!/usr/bin/env python 2*a1a3b679SAndreas Boehler 3*a1a3b679SAndreas Boehler# 4*a1a3b679SAndreas Boehler# Copyright (c) 2009-2010 Evert Pot 5*a1a3b679SAndreas Boehler# All rights reserved. 6*a1a3b679SAndreas Boehler# http://www.rooftopsolutions.nl/ 7*a1a3b679SAndreas Boehler# 8*a1a3b679SAndreas Boehler# This utility is distributed along with SabreDAV 9*a1a3b679SAndreas Boehler# license: http://sabre.io/license/ Modified BSD License 10*a1a3b679SAndreas Boehler 11*a1a3b679SAndreas Boehlerimport os 12*a1a3b679SAndreas Boehlerfrom optparse import OptionParser 13*a1a3b679SAndreas Boehlerimport time 14*a1a3b679SAndreas Boehler 15*a1a3b679SAndreas Boehlerdef getfreespace(path): 16*a1a3b679SAndreas Boehler stat = os.statvfs(path) 17*a1a3b679SAndreas Boehler return stat.f_frsize * stat.f_bavail 18*a1a3b679SAndreas Boehler 19*a1a3b679SAndreas Boehlerdef getbytesleft(path,threshold): 20*a1a3b679SAndreas Boehler return getfreespace(path)-threshold 21*a1a3b679SAndreas Boehler 22*a1a3b679SAndreas Boehlerdef run(cacheDir, threshold, sleep=5, simulate=False, min_erase = 0): 23*a1a3b679SAndreas Boehler 24*a1a3b679SAndreas Boehler bytes = getbytesleft(cacheDir,threshold) 25*a1a3b679SAndreas Boehler if (bytes>0): 26*a1a3b679SAndreas Boehler print "Bytes to go before we hit threshold:", bytes 27*a1a3b679SAndreas Boehler else: 28*a1a3b679SAndreas Boehler print "Threshold exceeded with:", -bytes, "bytes" 29*a1a3b679SAndreas Boehler dir = os.listdir(cacheDir) 30*a1a3b679SAndreas Boehler dir2 = [] 31*a1a3b679SAndreas Boehler for file in dir: 32*a1a3b679SAndreas Boehler path = cacheDir + '/' + file 33*a1a3b679SAndreas Boehler dir2.append({ 34*a1a3b679SAndreas Boehler "path" : path, 35*a1a3b679SAndreas Boehler "atime": os.stat(path).st_atime, 36*a1a3b679SAndreas Boehler "size" : os.stat(path).st_size 37*a1a3b679SAndreas Boehler }) 38*a1a3b679SAndreas Boehler 39*a1a3b679SAndreas Boehler dir2.sort(lambda x,y: int(x["atime"]-y["atime"])) 40*a1a3b679SAndreas Boehler 41*a1a3b679SAndreas Boehler filesunlinked = 0 42*a1a3b679SAndreas Boehler gainedspace = 0 43*a1a3b679SAndreas Boehler 44*a1a3b679SAndreas Boehler # Left is the amount of bytes that need to be freed up 45*a1a3b679SAndreas Boehler # The default is the 'min_erase setting' 46*a1a3b679SAndreas Boehler left = min_erase 47*a1a3b679SAndreas Boehler 48*a1a3b679SAndreas Boehler # If the min_erase setting is lower than the amount of bytes over 49*a1a3b679SAndreas Boehler # the threshold, we use that number instead. 50*a1a3b679SAndreas Boehler if left < -bytes : 51*a1a3b679SAndreas Boehler left = -bytes 52*a1a3b679SAndreas Boehler 53*a1a3b679SAndreas Boehler print "Need to delete at least:", left; 54*a1a3b679SAndreas Boehler 55*a1a3b679SAndreas Boehler for file in dir2: 56*a1a3b679SAndreas Boehler 57*a1a3b679SAndreas Boehler # Only deleting files if we're not simulating 58*a1a3b679SAndreas Boehler if not simulate: os.unlink(file["path"]) 59*a1a3b679SAndreas Boehler left = int(left - file["size"]) 60*a1a3b679SAndreas Boehler gainedspace = gainedspace + file["size"] 61*a1a3b679SAndreas Boehler filesunlinked = filesunlinked + 1 62*a1a3b679SAndreas Boehler 63*a1a3b679SAndreas Boehler if(left<0): 64*a1a3b679SAndreas Boehler break 65*a1a3b679SAndreas Boehler 66*a1a3b679SAndreas Boehler print "%d files deleted (%d bytes)" % (filesunlinked, gainedspace) 67*a1a3b679SAndreas Boehler 68*a1a3b679SAndreas Boehler 69*a1a3b679SAndreas Boehler time.sleep(sleep) 70*a1a3b679SAndreas Boehler 71*a1a3b679SAndreas Boehler 72*a1a3b679SAndreas Boehler 73*a1a3b679SAndreas Boehlerdef main(): 74*a1a3b679SAndreas Boehler parser = OptionParser( 75*a1a3b679SAndreas Boehler version="naturalselection v0.3", 76*a1a3b679SAndreas Boehler description="Cache directory manager. Deletes cache entries based on accesstime and free space thresholds.\n" + 77*a1a3b679SAndreas Boehler "This utility is distributed alongside SabreDAV.", 78*a1a3b679SAndreas Boehler usage="usage: %prog [options] cacheDirectory", 79*a1a3b679SAndreas Boehler ) 80*a1a3b679SAndreas Boehler parser.add_option( 81*a1a3b679SAndreas Boehler '-s', 82*a1a3b679SAndreas Boehler dest="simulate", 83*a1a3b679SAndreas Boehler action="store_true", 84*a1a3b679SAndreas Boehler help="Don't actually make changes, but just simulate the behaviour", 85*a1a3b679SAndreas Boehler ) 86*a1a3b679SAndreas Boehler parser.add_option( 87*a1a3b679SAndreas Boehler '-r','--runs', 88*a1a3b679SAndreas Boehler help="How many times to check before exiting. -1 is infinite, which is the default", 89*a1a3b679SAndreas Boehler type="int", 90*a1a3b679SAndreas Boehler dest="runs", 91*a1a3b679SAndreas Boehler default=-1 92*a1a3b679SAndreas Boehler ) 93*a1a3b679SAndreas Boehler parser.add_option( 94*a1a3b679SAndreas Boehler '-n','--interval', 95*a1a3b679SAndreas Boehler help="Sleep time in seconds (default = 5)", 96*a1a3b679SAndreas Boehler type="int", 97*a1a3b679SAndreas Boehler dest="sleep", 98*a1a3b679SAndreas Boehler default=5 99*a1a3b679SAndreas Boehler ) 100*a1a3b679SAndreas Boehler parser.add_option( 101*a1a3b679SAndreas Boehler '-l','--threshold', 102*a1a3b679SAndreas Boehler help="Threshold in bytes (default = 10737418240, which is 10GB)", 103*a1a3b679SAndreas Boehler type="int", 104*a1a3b679SAndreas Boehler dest="threshold", 105*a1a3b679SAndreas Boehler default=10737418240 106*a1a3b679SAndreas Boehler ) 107*a1a3b679SAndreas Boehler parser.add_option( 108*a1a3b679SAndreas Boehler '-m', '--min-erase', 109*a1a3b679SAndreas Boehler help="Minimum number of bytes to erase when the threshold is reached. " + 110*a1a3b679SAndreas Boehler "Setting this option higher will reduce the amount of times the cache directory will need to be scanned. " + 111*a1a3b679SAndreas Boehler "(the default is 1073741824, which is 1GB.)", 112*a1a3b679SAndreas Boehler type="int", 113*a1a3b679SAndreas Boehler dest="min_erase", 114*a1a3b679SAndreas Boehler default=1073741824 115*a1a3b679SAndreas Boehler ) 116*a1a3b679SAndreas Boehler 117*a1a3b679SAndreas Boehler options,args = parser.parse_args() 118*a1a3b679SAndreas Boehler if len(args)<1: 119*a1a3b679SAndreas Boehler parser.error("This utility requires at least 1 argument") 120*a1a3b679SAndreas Boehler cacheDir = args[0] 121*a1a3b679SAndreas Boehler 122*a1a3b679SAndreas Boehler print "Natural Selection" 123*a1a3b679SAndreas Boehler print "Cache directory:", cacheDir 124*a1a3b679SAndreas Boehler free = getfreespace(cacheDir); 125*a1a3b679SAndreas Boehler print "Current free disk space:", free 126*a1a3b679SAndreas Boehler 127*a1a3b679SAndreas Boehler runs = options.runs; 128*a1a3b679SAndreas Boehler while runs!=0 : 129*a1a3b679SAndreas Boehler run( 130*a1a3b679SAndreas Boehler cacheDir, 131*a1a3b679SAndreas Boehler sleep=options.sleep, 132*a1a3b679SAndreas Boehler simulate=options.simulate, 133*a1a3b679SAndreas Boehler threshold=options.threshold, 134*a1a3b679SAndreas Boehler min_erase=options.min_erase 135*a1a3b679SAndreas Boehler ) 136*a1a3b679SAndreas Boehler if runs>0: 137*a1a3b679SAndreas Boehler runs = runs - 1 138*a1a3b679SAndreas Boehler 139*a1a3b679SAndreas Boehlerif __name__ == '__main__' : 140*a1a3b679SAndreas Boehler main() 141