Skip to content

Commit 11ff9d1

Browse files
nicer logging and adjustments according to linter
1 parent 23130ed commit 11ff9d1

File tree

1 file changed

+82
-71
lines changed

1 file changed

+82
-71
lines changed

rsnapshot-once.py

Lines changed: 82 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@
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

4042
import os.path
@@ -44,42 +46,48 @@
4446
import datetime
4547
import subprocess
4648

49+
from time import strftime
50+
from shutil import rmtree
4751
from systemd import journal
4852
from docopt import docopt
49-
from time import strftime
5053
from 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

7074
def abortlog(logfile):
71-
logft("## BACKUP ABORTED #######################\n",logfile)
75+
'''Logs aborting the script'''
76+
logft("## BACKUP ABORTED #######################\n", logfile)
7277

7378
def 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

118126
ARGS = 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)
121129
logging.debug("Parsed arguments:\n" + str(ARGS))
122130

123131
# getting snapshotroot and logfile
@@ -129,59 +137,60 @@ def parseConfig(configpath,configfile):
129137
CONFIGFILE = os.path.split(os.path.abspath(CONFIGFILE))[1]
130138
logft("Configpath: "+CONFIGPATH)
131139
try:
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)
135143
except FileNotFoundError:
136-
logft("Specified settings file does not exist",LOGFILE)
144+
logft("Specified settings file does not exist", LOGFILE)
137145
raise SystemExit(-1)
138146

139147
if 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

144152
if 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
152160
COMMAND = None
153161
for k, v in ARGS.items():
154-
if v == True:
162+
if isinstance(v, bool) and v:
155163
COMMAND = k
156164
if 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)
327338
else:
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

Comments
 (0)