3333 [default: /etc/rsnapshot.conf]
3434 -h display help
3535
36- By passing the <N> argument with hourly, you can adjust the hourly intervals. Defaults to 6 if not given, meaning every 4 hours.
37- This script will, as it is designed for now, run in systemd environments. You can use it in non systemd environments by removing or adjusting the wakeup-part.
36+ By passing the <N> argument with hourly, you can adjust the hourly intervals. Defaults
37+ to 6 if not given, meaning every 4 hours. This script will, as it is designed for now,
38+ run in systemd environments. You can use it in non systemd environments by removing or
39+ adjusting the wakeup-part.
3840"""
3941
4042import os .path
4446import datetime
4547import subprocess
4648
49+ from time import strftime
50+ from shutil import rmtree
4751from systemd import journal
4852from docopt import docopt
49- from time import strftime
5053from natsort import versorted
51- from shutil import rmtree
5254
53- def logf (logstring ,logfile = None ,prefix = "" ):
5455
56+ def logf (logstring , logfile = None , prefix = "" ):
57+ '''Logs in rsnapshot's logfile when existing'''
5558 if logfile :
5659 logstring = prefix + logstring
5760 try :
58- with open (logfile ,'a' ) as logfile :
59- logfile .write (logstring + "\n " )
61+ with open (logfile , 'a' ) as lf :
62+ lf .write (logstring + "\n " )
6063 except FileNotFoundError :
6164 logging .critical ("Specified logfile does not exist" )
6265 logging .info ("Writing to logfile: " + logstring )
6366 return True
6467
65- def logft (logstring ,logfile = None ,prefix = "" ):
66- logstring = strftime ("%Y-%m-%d-%H:%M:%S " ) + logstring
67- logf (logstring ,logfile ,prefix )
68+ def logft (logstring , logfile = None , prefix = "" ):
69+ '''Logs with timestamp'''
70+ logstring = strftime ("[%d/%b/%Y:%H:%M:%S] " ) + logstring
71+ logf (logstring , logfile , prefix )
6872 return True
6973
7074def abortlog (logfile ):
71- logft ("## BACKUP ABORTED #######################\n " ,logfile )
75+ '''Logs aborting the script'''
76+ logft ("## BACKUP ABORTED #######################\n " , logfile )
7277
7378def uptime ():
74- with open ('/proc/uptime' , 'r' ) as f :
75- uptime_seconds = float (f .readline ().split ()[0 ])
76- return uptime_seconds
79+ '''Gets the machine uptime from /proc'''
80+ with open ('/proc/uptime' , 'r' ) as procUptime :
81+ uptime_seconds = float (procUptime .readline ().split ()[0 ])
82+ return uptime_seconds
7783
78- def removepid (pidfile ,logfile = None ,prefix = "" ):
79- logft ("Removing rsnapshot-once pidfile at " + pidfile + " (CLEAN EXIT)." ,logfile ,prefix )
84+ def removepid (pidfile , logfile = None , prefix = "" ):
85+ '''removes the pidfile of rsnapshot-once'''
86+ logft ("Removing rsnapshot-once pidfile at " + pidfile + " (CLEAN EXIT)." , logfile , prefix )
8087 os .remove (pidfile )
8188
82- def parseConfig (configpath ,configfile ):
89+ def parseConfig (configpath , configfile ):
90+ '''recursively parses the rsnapshot configs'''
8391 abspath = configpath + "/" + configfile
8492 logfile = None
8593 syncFirst = None
@@ -90,10 +98,10 @@ def parseConfig(configpath,configfile):
9098 referencedpath = os .path .split (line .strip ().split ("\t " )[1 ])[0 ]
9199 referencedfile = os .path .split (line .strip ().split ("\t " )[1 ])[1 ]
92100 if not os .path .isabs (referencedpath ):
93- logft ("referenced file is given by relative path. This might lead to errors depending on where this script has been invoked from" ,LOGFILE )
101+ logft ("referenced file is given by relative path. This might lead to errors depending on where this script has been invoked from" , LOGFILE )
94102 referencedpath = configpath + "/" + referencedpath
95- logft ("referenced file: " + referencedpath + "/" + referencedfile ,LOGFILE )
96- lf ,sf ,sr = parseConfig (referencedpath ,referencedfile )
103+ logft ("referenced file: " + referencedpath + "/" + referencedfile , LOGFILE )
104+ lf , sf , sr = parseConfig (referencedpath , referencedfile )
97105 if lf != None :
98106 logfile = lf
99107 if sf != None :
@@ -117,7 +125,7 @@ def parseConfig(configpath,configfile):
117125
118126ARGS = docopt (__doc__ , version = '0.0.1-alpha' )
119127
120- logging .basicConfig (stream = sys .stdout , level = logging .DEBUG , formatter = logging .BASIC_FORMAT )
128+ logging .basicConfig (stream = sys .stdout , level = logging .INFO , formatter = logging .BASIC_FORMAT )
121129logging .debug ("Parsed arguments:\n " + str (ARGS ))
122130
123131# getting snapshotroot and logfile
@@ -129,59 +137,60 @@ def parseConfig(configpath,configfile):
129137CONFIGFILE = os .path .split (os .path .abspath (CONFIGFILE ))[1 ]
130138logft ("Configpath: " + CONFIGPATH )
131139try :
132- LOGFILE , SYNC_FIRST , SNAPSHOT_ROOT = parseConfig (CONFIGPATH ,CONFIGFILE )
140+ LOGFILE , SYNC_FIRST , SNAPSHOT_ROOT = parseConfig (CONFIGPATH , CONFIGFILE )
133141 if not LOGFILE :
134- logft ("No logfile entry in settings file. Is this intended?" ,LOGFILE )
142+ logft ("No logfile entry in settings file. Is this intended?" , LOGFILE )
135143except FileNotFoundError :
136- logft ("Specified settings file does not exist" ,LOGFILE )
144+ logft ("Specified settings file does not exist" , LOGFILE )
137145 raise SystemExit (- 1 )
138146
139147if SYNC_FIRST == "1" :
140- logft ("Rsync is configured to use sync_fist. This is not supported for now." ,LOGFILE )
148+ logft ("Rsync is configured to use sync_fist. This is not supported for now." , LOGFILE )
141149 abortlog (LOGFILE )
142150 raise SystemExit (- 1 )
143151
144152if not SNAPSHOT_ROOT :
145- logft ("No valid snapshot root entry in settings file. Aborting" ,LOGFILE )
153+ logft ("No valid snapshot root entry in settings file. Aborting" , LOGFILE )
146154 abortlog (LOGFILE )
147155 raise SystemExit (- 1 )
148156
149- logft ("## STARTING BACKUP ######################" ,LOGFILE )
157+ logft ("## STARTING BACKUP ######################" , LOGFILE )
150158
151159# get command
152160COMMAND = None
153161for k , v in ARGS .items ():
154- if v == True :
162+ if isinstance ( v , bool ) and v :
155163 COMMAND = k
156164if COMMAND != "sync" :
157165 # Check pid file
158166 PIDFILE = SNAPSHOT_ROOT + ".rsnapshot-once.pid"
159- logft ("Checking rsnapshot-once pidfile at " + PIDFILE + " ... " ,LOGFILE )
167+ logft ("Checking rsnapshot-once pidfile at " + PIDFILE + " ... " , LOGFILE )
160168 try :
161169 with open (PIDFILE ) as pf :
162170 pid = pf .read ().strip ()
163171 if os .path .isfile ("/proc/" + pid ):
164- logf ("Process " + pid + " still running. Aborting" ,LOGFILE )
165- abortlog ()
172+ logf ("Process " + pid + " still running. Aborting" , LOGFILE )
173+ abortlog (LOGFILE )
166174 raise SystemExit (- 1 )
167- logf ("PID file exists but process " + pid + " is not running. Script crashed before" ,LOGFILE )
175+ logf ("PID file exists but process " + pid + " is not running. Script crashed before" , LOGFILE )
168176 sortedBackups = []
169177 for entry in os .listdir (SNAPSHOT_ROOT ):
170178 if COMMAND in entry :
171179 sortedBackups .append (entry )
172180 # hack for sorting with natsort 3.5.1: User versorted to handle the dot
173181 sortedBackups = versorted (sortedBackups )
174- if len (sortedBackups ) == 0 :
182+ sortedBackupsCount = len (sortedBackups )
183+ if sortedBackupsCount == 0 :
175184 logft ("No previous backups found. No cleanup necessary." , LOGFILE )
176185 else :
177186 logft ("Cleaning up unfinished backup ..." , LOGFILE )
178187 firstBackup = sortedBackups .pop (0 )
179188 logft ("Deleting " + firstBackup + " ..." , LOGFILE )
180189
181190 # double check before deleting (!)
182- regExpMatches = re .search ('^(.+)\.(\d+)$' , firstBackup )
191+ regExpMatches = re .search (r '^(.+)\.(\d+)$' , firstBackup )
183192 if not os .path .isdir (SNAPSHOT_ROOT + firstBackup ) or len (firstBackup ) < 2 or not regExpMatches :
184- logf ("Script security issue. EXITING." ,LOGFILE );
193+ logf ("Script security issue. EXITING." , LOGFILE )
185194 abortlog (LOGFILE )
186195 raise SystemExit (- 1 )
187196 else :
@@ -190,36 +199,38 @@ def parseConfig(configpath,configfile):
190199 rmtree (SNAPSHOT_ROOT + firstBackup )
191200 logf ("Done" , LOGFILE )
192201 except :
193- logft ("Unexpected error " + str (sys .exc_info ()[0 ]) + " on deleting " + SNAPSHOT_ROOT + firstBackup ,LOGFILE )
202+ logft ("Unexpected error " + str (sys .exc_info ()[0 ]) + " on deleting " + SNAPSHOT_ROOT + firstBackup , LOGFILE )
194203 raise SystemExit (- 1 )
195- while len (sortedBackups ) > 0 :
204+ sortedBackupsCount = len (sortedBackups )
205+ while sortedBackupsCount > 0 :
196206 backup = sortedBackups .pop (0 )
207+ sortedBackupsCount = len (sortedBackups )
197208 #logging.debug("Backup: "+backup)
198- regExpMatches = re .search ("^(.+)\.(\d+)$" , backup )
209+ regExpMatches = re .search (r "^(.+)\.(\d+)$" , backup )
199210 if regExpMatches :
200211 #logging.debug("M: "+str(m))
201212 #logging.debug("M1: " + regExpMatches.group(1)+" M2: "+regExpMatches.group(2))
202213 #logging.debug("Prev: "+previousBackup)
203- previousBackup = regExpMatches .group (1 )+ "." + str (int (regExpMatches .group (2 ))- 1 )
214+ previousBackup = regExpMatches .group (1 )+ "." + str (int (regExpMatches .group (2 ))- 1 )
204215 logft ("Moving " + backup + " to " + previousBackup + " ... " )
205216 try :
206- os .rename (SNAPSHOT_ROOT + backup ,SNAPSHOT_ROOT + previousBackup )
207- logft ("DONE" ,LOGFILE )
217+ os .rename (SNAPSHOT_ROOT + backup , SNAPSHOT_ROOT + previousBackup )
218+ logft ("DONE" , LOGFILE )
208219 except OSError :
209220 logft ("Could not rename directory. Target already exists" )
210221 abortlog (LOGFILE )
211222 raise SystemExit (- 1 )
212223 except FileNotFoundError :
213- logf ("Does not exist. Last backup was clean." ,LOGFILE )
224+ logft ("Does not exist. Last backup was clean." , LOGFILE )
214225
215- logft ("Checking delays (minimum 15 minutes since startup/wakeup) ..." ,LOGFILE )
226+ logft ("Checking delays (minimum 15 minutes since startup/wakeup) ..." , LOGFILE )
216227 upMinutes = int (uptime ()// 60 )
217228 if upMinutes < 15 :
218- logft ("- Computer uptime is " + str (upMinutes )+ " minutes. NOT ENOUGH. EXITING." ,LOGFILE )
229+ logft ("- Computer uptime is " + str (upMinutes )+ " minutes. NOT ENOUGH. EXITING." , LOGFILE )
219230 abortlog (LOGFILE )
220231 raise SystemExit (- 1 )
221232 else :
222- logft ("- Computer uptime is " + str (upMinutes )+ " minutes. THAT'S OKAY." ,LOGFILE )
233+ logft ("- Computer uptime is " + str (upMinutes )+ " minutes. THAT'S OKAY." , LOGFILE )
223234
224235 # Get time of resume from systemd journal
225236 j = journal .Reader ()
@@ -238,30 +249,30 @@ def parseConfig(configpath,configfile):
238249 WAKEUPMINUTES = timediff .seconds // 60
239250 if WAKEUPMINUTES :
240251 if WAKEUPMINUTES < 15 :
241- logft ("- Computer wakeup time is " + str (WAKEUPMINUTES )+ " minutes. NOT ENOUGH. EXITING." ,LOGFILE )
252+ logft ("- Computer wakeup time is " + str (WAKEUPMINUTES )+ " minutes. NOT ENOUGH. EXITING." , LOGFILE )
242253 abortlog (LOGFILE )
243254 raise SystemExit (- 1 )
244255 else :
245- logft ("- Computer wakeup time is " + str (WAKEUPMINUTES )+ " minutes. THAT'S OKAY." ,LOGFILE )
256+ logft ("- Computer wakeup time is " + str (WAKEUPMINUTES )+ " minutes. THAT'S OKAY." , LOGFILE )
246257
247258 # Get date of newest folder (e.g. weekly.0, daily.0, monthly.0)
248259 # to figure out if the job needs to run
249260 NEEDSTORUN = False
250261 NEWESTBACKUP = SNAPSHOT_ROOT + COMMAND + ".0"
251262 if not os .path .isdir (NEWESTBACKUP ):
252- logft ("No backup exists for job " + COMMAND + " at " + NEWESTBACKUP ,LOGFILE )
263+ logft ("No backup exists for job " + COMMAND + " at " + NEWESTBACKUP , LOGFILE )
253264 NEEDSTORUN = True
254265 else :
255- BACKUPTIME = datetime .datetime .fromtimestamp (os .path .getmtime (NEWESTBACKUP ))
256- logft ("Last backup for job " + COMMAND + " at " + NEWESTBACKUP + " was at " + str (BACKUPTIME ),LOGFILE );
266+ BACKUPTIME = datetime .datetime .fromtimestamp (os .path .getmtime (NEWESTBACKUP ))
267+ logft ("Last backup for job " + COMMAND + " at " + NEWESTBACKUP + " was at " + str (BACKUPTIME ), LOGFILE )
257268 TIMESINCELAST = None
258269 TIMEMIN = None
259270 if COMMAND == "hourly" :
260271 intervalsPerDay = ARGS .get ("<N>" )
261- if intervalsPerDay == None :
272+ if intervalsPerDay is None :
262273 intervalsPerDay = 6 # the default
263274 if int (intervalsPerDay ) > 24 or int (intervalsPerDay ) < 2 :
264- logft ("Invalid interval for hourly given. Must be between 2 and 24. Aborting." ,LOGFILE )
275+ logft ("Invalid interval for hourly given. Must be between 2 and 24. Aborting." , LOGFILE )
265276 abortlog (LOGFILE )
266277 raise SystemExit
267278 TIMEMIN = datetime .timedelta (minutes = (24 / intervalsPerDay )* 60 - 5 )
@@ -272,7 +283,7 @@ def parseConfig(configpath,configfile):
272283 elif COMMAND == "monthly" :
273284 TIMEMIN = datetime .timedelta (days = 29 )
274285 else :
275- logft ("Error: This should not happen. ERROR." ,LOGFILE )
286+ logft ("Error: This should not happen. ERROR." , LOGFILE )
276287 abortlog (LOGFILE )
277288 raise SystemExit (- 1 )
278289
@@ -282,50 +293,50 @@ def parseConfig(configpath,configfile):
282293 logging .debug ("Mintime: " + str (TIMEMIN ))
283294
284295 if not NEEDSTORUN :
285- logft ("Job does NOT need to run. Last run is only " + str (TIMESINCELAST )+ " ago (min. is " + str (TIMEMIN )+ ") EXITING." ,LOGFILE );
296+ logft ("Job does NOT need to run. Last run is only " + str (TIMESINCELAST )+ " ago (min. is " + str (TIMEMIN )+ ") EXITING." , LOGFILE )
286297 abortlog (LOGFILE )
287298 raise SystemExit
288299 else :
289- logft ("Last run is " + str (TIMESINCELAST )+ " ago (min. is " + str (TIMEMIN )+ ")" ,LOGFILE )
300+ logft ("Last run is " + str (TIMESINCELAST )+ " ago (min. is " + str (TIMEMIN )+ ")" , LOGFILE )
290301 PID = os .getpid ()
291302 logft ("Writing rsnapshot-once pidfile (PID " + str (PID )+ ") to " + PIDFILE )
292- with open (PIDFILE ,"w" ) as pf :
303+ with open (PIDFILE , "w" ) as pf :
293304 pf .write (str (PID ))
294305 SYSCMD = "rsnapshot -c " + ARGS .get ("-c" )+ " " + COMMAND
295- logft ("NOW RUNNING JOB: " + SYSCMD + "\n " ,LOGFILE )
306+ logft ("NOW RUNNING JOB: " + SYSCMD + "\n " , LOGFILE )
296307 SYSCMD = SYSCMD .split (" " )
297308 EXITCODE = 0
298309 CONFIGERROR = False
299310 COMPLETEPROCESS = None
300311 try :
301- COMPLETEPROCESS = subprocess .check_output (SYSCMD ,universal_newlines = True ,stderr = subprocess .STDOUT )
302- except subprocess .CalledProcessError as exit :
303- EXITCODE = exit .returncode
304- COMPLETEPROCESS = exit .output
312+ COMPLETEPROCESS = subprocess .check_output (SYSCMD , universal_newlines = True , stderr = subprocess .STDOUT )
313+ except subprocess .CalledProcessError as retVal :
314+ EXITCODE = retVal .returncode
315+ COMPLETEPROCESS = retVal .output
305316
306- for line in COMPLETEPROCESS .split ("\n " ):
307- # logft (" rsnapshot says : "+line,LOGFILE )
308- if "rsnapshot encountered an error" in line :
317+ for LINE in COMPLETEPROCESS .split ("\n " ):
318+ logging . info (" rsnapshot: " + LINE )
319+ if "rsnapshot encountered an error" in LINE :
309320 CONFIGERROR = True
310321
311322 if CONFIGERROR :
312- logft ("Exiting rsnapshot-once, because error in rsnapshot config detected." ,LOGFILE ,"\n " )
313- removepid (PIDFILE ,LOGFILE )
323+ logft ("Exiting rsnapshot-once, because error in rsnapshot config detected." , LOGFILE , "\n " )
324+ removepid (PIDFILE , LOGFILE )
314325 abortlog (LOGFILE )
315326 raise SystemExit (- 1 )
316327
317328 # pidfile should NOT exist if exit was clean
318329 if EXITCODE == 1 : # 1 means 'fatal error' in rsnapshot terminology
319- logft ("No clean exit. Backup aborted. Cleanup necessary on next run (DIRTY EXIT)." ,LOGFILE ,"\n " );
330+ logft ("No clean exit. Backup aborted. Cleanup necessary on next run (DIRTY EXIT)." , LOGFILE , "\n " )
320331 abortlog (LOGFILE )
321332 raise SystemExit (- 1 )
322333 elif EXITCODE == 2 : # 2 means that warnings ocurred
323- logft ("Rsnapshot encountered warnings, this is worth a look at the log file" ,LOGFILE ,"\n " );
334+ logft ("Rsnapshot encountered warnings, this is worth a look at the log file" , LOGFILE , "\n " )
324335
325- removepid (PIDFILE ,LOGFILE ,"\n " )
326- logft ("## BACKUP COMPLETE ######################\n " ,LOGFILE )
336+ removepid (PIDFILE , LOGFILE , "\n " )
337+ logft ("## BACKUP COMPLETE ######################\n " , LOGFILE )
327338else :
328339 # sync is not supported in this context
329- logft ("Sync is not supported as command" ,LOGFILE )
340+ logft ("Sync is not supported as command" , LOGFILE )
330341 abortlog (LOGFILE )
331342 raise SystemExit (- 1 )
0 commit comments