def runReport(startTime): globs.log.write(1, 'rpt_bysource()') # Get header and column info nFields, fldDefs, reportOpts, rptCols, rptTits = report.initReportVars() # Print the report title msgHtml, msgText, msgCsv = report.rptTop(reportOpts, nFields) # Remove columns we don't need for this report # These are already part of the report logic processing & subheaders # We won't need to loop through them for the report fields rptCols.remove('source') # Print column titles if not printing for each section if reportOpts['repeatheaders'] is False: msgHtml, msgText, msgCsv = report.rptPrintTitles( msgHtml, msgText, msgCsv, rptCols) # Select sources from database dbCursor = globs.db.execSqlStmt( "SELECT DISTINCT source FROM backupsets ORDER BY source") srcSet = dbCursor.fetchall() globs.log.write(2, 'srcSet=[{}]'.format(srcSet)) # Loop through backupsets table and get all the potential destinations for srcKey in srcSet: # Add Source title subHead = globs.optionManager.getRcOption('report', 'subheading') if subHead is not None: # Substitute subheading keywords subHead = subHead.replace('#SOURCE#', srcKey[0]) if subHead is None or subHead == '': msgHtml += '<tr><td colspan="{}" align="center" bgcolor="{}"><b>{}:</b> {}</td></tr>\n'.format( nFields, reportOpts['subheadbg'], rptTits['source'], srcKey[0]) msgText += '***** {}: {}*****\n'.format(rptTits['source'], srcKey[0]) msgCsv += '\"***** {}: {}*****\",\n'.format( rptTits['source'], srcKey[0]) else: msgHtml += '<tr><td colspan="{}" align="center" bgcolor="{}">{}</td></tr>\n'.format( nFields, reportOpts['subheadbg'], subHead) msgText += '***** {} *****\n'.format(subHead) msgCsv += '\"***** {} *****\"\n'.format(subHead) # Print column titles if printing for each section if reportOpts['repeatheaders'] is True: msgHtml, msgText, msgCsv = report.rptPrintTitles( msgHtml, msgText, msgCsv, rptCols) sqlStmt = "SELECT destination, timestamp, examinedFiles, examinedFilesDelta, sizeOfExaminedFiles, fileSizeDelta, addedFiles, deletedFiles, modifiedFiles, filesWithError, \ parsedResult, messages, warnings, errors FROM report WHERE source=\'{}\'".format( srcKey[0]) if reportOpts['sortby'] == 'destination': sqlStmt += ' ORDER BY destination' else: sqlStmt += ' ORDER BY timestamp' dbCursor = globs.db.execSqlStmt(sqlStmt) reportRows = dbCursor.fetchall() globs.log.write(3, 'reportRows=[{}]'.format(reportRows)) # Loop through each new activity for the source/destination and add to report for destination, timestamp, examinedFiles, examinedFilesDelta, sizeOfExaminedFiles, fileSizeDelta, \ addedFiles, deletedFiles, modifiedFiles, filesWithError, parsedResult, messages, \ warnings, errors in reportRows: # Get date and time from timestamp dateStr, timeStr = drdatetime.fromTimestamp(timestamp) # Print report fields # Each field takes up one column/cell in the table msgHtml += '<tr>' # The fill list of possible fields in the report. printField() below will skip a field if it is emoved in the .rc file. titles = [ 'destination', 'date', 'time', 'files', 'filesplusminus', 'size', 'sizeplusminus', 'added', 'deleted', 'modified', 'errors', 'result' ] fields = [ destination, dateStr, timeStr, examinedFiles, examinedFilesDelta, sizeOfExaminedFiles, fileSizeDelta, addedFiles, deletedFiles, modifiedFiles, filesWithError, parsedResult ] for ttl, fld in zip(titles, fields): msgHtml += report.printField(ttl, fld, 'html') msgText += report.printField(ttl, fld, 'text') msgCsv += report.printField(ttl, fld, 'csv') msgHtml += '</tr>\n' msgText += '\n' msgCsv += '\n' fields = [messages, warnings, errors] options = ['displaymessages', 'displaywarnings', 'displayerrors'] backgrounds = ['jobmessagebg', 'jobwarningbg', 'joberrorbg'] titles = ['jobmessages', 'jobwarnings', 'joberrors'] # Print message/warning/error fields # Each of these spans all the table columns for fld, opt, bg, tit in zip(fields, options, backgrounds, titles): if ((fld != '') and (reportOpts[opt] == True)): msgHtml += '<tr><td colspan="{}" align="center" bgcolor="{}"><details><summary>{}</summary>{}</details></td></tr>\n'.format( nFields, reportOpts[bg], rptTits[tit], fld) msgText += '{}: {}\n'.format(rptTits[tit], fld) msgCsv += '\"{}: {}\",\n'.format(rptTits[tit], fld) # Show inactivity - Look for missing source/dest pairs in report dbCursor = globs.db.execSqlStmt( "SELECT destination, lastTimestamp, lastFileCount, lastFileSize FROM backupsets WHERE source = '{}' ORDER BY source" .format(srcKey[0])) missingRows = dbCursor.fetchall() for destination, lastTimestamp, lastFileCount, lastFileSize in missingRows: dbCursor = globs.db.execSqlStmt( 'SELECT count(*) FROM report WHERE source=\"{}\" AND destination=\"{}\"' .format(srcKey[0], destination)) countRows = dbCursor.fetchone() if countRows[0] == 0: # Calculate days since last activity diff = drdatetime.daysSince(lastTimestamp) lastDateStr, lastTimeStr = drdatetime.fromTimestamp( lastTimestamp) msgHtml += '<tr>' msgHtml += report.printField('destination', destination, 'html') msgHtml += '<td colspan="{}" align="center" bgcolor="{}"><i>No new activity. Last activity on {} at {} ({} days ago)</i></td>'.format( nFields - 1, report.getLastSeenColor(reportOpts, diff), lastDateStr, lastTimeStr, diff) msgHtml += '</tr>\n' msgText += report.printField('destination', destination, 'text') msgText += '{}: No new activity. Last activity on {} at {} ({} days ago)\n'.format( destination, lastDateStr, lastTimeStr, diff) msgCsv += report.printField('destination', destination, 'csv') msgCsv += '\"{}: No new activity. Last activity on {} at {} ({} days ago)\"\n'.format( destination, lastDateStr, lastTimeStr, diff) # Add report footer msgHtml, msgText, msgCsv = report.rptBottom(msgHtml, msgText, msgCsv, startTime, nFields) # Return text & HTML messages to main program. It can decide which one it wants to use. return msgHtml, msgText, msgCsv
def runReport(startTime): globs.log.write(1, 'rpt_srcdest()') # Get header and column info nFields, fldDefs, reportOpts, rptCols, rptTits = report.initReportVars() # Print the report title msgHtml, msgText, msgCsv = report.rptTop(reportOpts, nFields) # Remove columns we don't need for this report # These are already part of the report logic processing & subheaders # we won't need to loop through them for the report fields rptCols.remove('source') rptCols.remove('destination') # Print column titles if not printing for each section if reportOpts['repeatheaders'] is False: msgHtml, msgText, msgCsv = report.rptPrintTitles( msgHtml, msgText, msgCsv, rptCols) # Select source/destination pairs from database sqlStmt = "SELECT source, destination, lastTimestamp, lastFileCount, lastFileSize from backupsets" # How should report be sorted? # Options are source & destination if reportOpts['sortby'] == 'source': sqlStmt = sqlStmt + " ORDER BY source, destination" else: sqlStmt = sqlStmt + " ORDER BY destination, source" dbCursor = globs.db.execSqlStmt(sqlStmt) bkSetRows = dbCursor.fetchall() globs.log.write(2, 'bkSetRows=[{}]'.format(bkSetRows)) # Loop through backupsets table and then get latest activity for each src/dest pair for source, destination, lastTimestamp, lastFileCount, lastFileSize in bkSetRows: globs.log.write( 3, 'Src=[{}] Dest=[{}] lastTimestamp=[{}] lastFileCount=[{}] lastFileSize=[{}]' .format(source, destination, lastTimestamp, lastFileCount, lastFileSize)) # Add title for source/dest pair subHead = globs.optionManager.getRcOption('report', 'subheading') if subHead is not None: # Substitute subheading keywords subHead = subHead.replace('#SOURCE#', source).replace('#DESTINATION#', destination) if subHead is None or subHead == '': msgHtml += '<tr><td colspan="{}" align="center" bgcolor="{}"><b>{}:</b> {} <b>{}:</b> {}</td></tr>\n'.format(nFields, reportOpts['subheadbg'], \ rptTits['source'], source, rptTits['destination'], destination) msgText += '***** {}: {} {}: {} *****\n'.format( rptTits['source'], source, rptTits['destination'], destination) msgCsv += '\"***** {}: {} {}: {} *****\"\n'.format( rptTits['source'], source, rptTits['destination'], destination) else: msgHtml += '<tr><td colspan="{}" align="center" bgcolor="{}">{}</td></tr>\n'.format( nFields, reportOpts['subheadbg'], subHead) msgText += '***** {} *****\n'.format(subHead) msgCsv += '\"***** {} *****\"\n'.format(subHead) # Print column titles if printing for each section if reportOpts['repeatheaders'] is True: msgHtml, msgText, msgCsv = report.rptPrintTitles( msgHtml, msgText, msgCsv, rptCols) # Select all activity for src/dest pair since last report run sqlStmt = "SELECT timestamp, examinedFiles, examinedFilesDelta, sizeOfExaminedFiles, fileSizeDelta, \ addedFiles, deletedFiles, modifiedFiles, filesWithError, parsedResult, messages, warnings, errors \ FROM report WHERE source=\'{}\' AND destination=\'{}\' order by timestamp".format( source, destination) dbCursor = globs.db.execSqlStmt(sqlStmt) reportRows = dbCursor.fetchall() globs.log.write(3, 'reportRows=[{}]'.format(reportRows)) if not reportRows: # No rows found = no recent activity # Calculate days since last activity diff = drdatetime.daysSince(lastTimestamp) lastDateStr, lastTimeStr = drdatetime.fromTimestamp(lastTimestamp) msgHtml += '<tr><td colspan="{}" align="center" bgcolor="{}"><i>No new activity. Last activity on {} at {} ({} days ago)</i></td></tr>\n'.format( nFields, report.getLastSeenColor(reportOpts, diff), lastDateStr, lastTimeStr, diff) msgText += 'No new activity. Last activity on {} at {} ({} days ago)\n'.format( lastDateStr, lastTimeStr, diff) msgCsv += '\"No new activity. Last activity on {} at {} ({} days ago)\"\n'.format( lastDateStr, lastTimeStr, diff) else: # Loop through each new job email and report for timestamp, examinedFiles, examinedFilesDelta, sizeOfExaminedFiles, fileSizeDelta, \ addedFiles, deletedFiles, modifiedFiles, filesWithError, parsedResult, messages, \ warnings, errors in reportRows: # Get date and time from timestamp dateStr, timeStr = drdatetime.fromTimestamp(timestamp) # Print report fields # Each field takes up one column/cell in the table msgHtml += '<tr>' # The fill list of possible fields in the report. printField() below will skip a field if it is emoved in the .rc file. titles = [ 'date', 'time', 'files', 'filesplusminus', 'size', 'sizeplusminus', 'added', 'deleted', 'modified', 'errors', 'result' ] fields = [ dateStr, timeStr, examinedFiles, examinedFilesDelta, sizeOfExaminedFiles, fileSizeDelta, addedFiles, deletedFiles, modifiedFiles, filesWithError, parsedResult ] for ttl, fld in zip(titles, fields): msgHtml += report.printField(ttl, fld, 'html') msgText += report.printField(ttl, fld, 'text') msgCsv += report.printField(ttl, fld, 'csv') msgHtml += '</tr>\n' msgText += '\n' msgCsv += '\n' fields = [messages, warnings, errors] options = [ 'displaymessages', 'displaywarnings', 'displayerrors' ] backgrounds = ['jobmessagebg', 'jobwarningbg', 'joberrorbg'] titles = ['jobmessages', 'jobwarnings', 'joberrors'] # Print message/warning/error fields # Each of these spans all the table columns for fld, opt, bg, tit in zip(fields, options, backgrounds, titles): if ((fld != '') and (reportOpts[opt] == True)): msgHtml += '<tr><td colspan="{}" align="center" bgcolor="{}"><details><summary>{}</summary><p>{}</details></td></tr>\n'.format( nFields, reportOpts[bg], rptTits[tit], fld) msgText += '{}: {}\n'.format(rptTits[tit], fld) csvLine = '\"{}: {}\"\n'.format( rptTits[tit], fld ).replace('\n', ' ').replace( '\r', '' ) # Need to remove \n & \r because csv truncates after these characters msgCsv += csvLine # Add report footer msgHtml, msgText, msgCsv = report.rptBottom(msgHtml, msgText, msgCsv, startTime, nFields) # Return text & HTML messages to main program. It can decide which one(s) it wants to use. return msgHtml, msgText, msgCsv
def runReport(startTime): globs.log.write(1, 'rpt_bydate()') # Get header and column info nFields, fldDefs, reportOpts, rptCols, rptTits = report.initReportVars() # Print the report title msgHtml, msgText, msgCsv = report.rptTop(reportOpts, nFields) # Remove columns we don't need for this report # These are already part of the report logic processing & subheaders # We won't need to loop through them for the report fields rptCols.remove('date') # Print column titles if not printing for each section if reportOpts['repeatheaders'] is False: msgHtml, msgText, msgCsv = report.rptPrintTitles( msgHtml, msgText, msgCsv, rptCols) # Get earliest & latest timestamps in the report table dbCursor = globs.db.execSqlStmt("SELECT min(timestamp) FROM report" ) # Smallest timestamp in the report table currentTs = dbCursor.fetchone()[0] dbCursor = globs.db.execSqlStmt("SELECT max(timestamp) FROM report" ) # Largest timestamp in the report table highestTs = dbCursor.fetchone()[0] while currentTs <= highestTs: currentDate, currentTime = drdatetime.fromTimestamp( currentTs, dfmt=globs.opts['dateformat'], tfmt=globs.opts['timeformat']) currentDateBeginTs = drdatetime.toTimestamp( currentDate + ' 00:00:00', dfmt=globs.opts['dateformat'], tfmt=globs.opts['timeformat'] ) # Convert the string into a timestamp currentDateEndTs = drdatetime.toTimestamp( currentDate + ' 23:59:59', dfmt=globs.opts['dateformat'], tfmt=globs.opts['timeformat'] ) # Convert the string into a timestamp sqlStmt = "SELECT source, destination, timestamp, examinedFiles, examinedFilesDelta, sizeOfExaminedFiles, fileSizeDelta, \ addedFiles, deletedFiles, modifiedFiles, filesWithError, parsedResult, messages, warnings, errors \ FROM report WHERE timestamp >= {} AND timestamp <= {}".format( currentDateBeginTs, currentDateEndTs) if reportOpts['sortby'] == 'source': sqlStmt += ' ORDER BY source, destination' elif reportOpts['sortby'] == 'destination': sqlStmt += ' ORDER BY destination, source' else: sqlStmt += ' ORDER BY timestamp' dbCursor = globs.db.execSqlStmt(sqlStmt) reportRows = dbCursor.fetchall() globs.log.write(3, 'reportRows=[{}]'.format(reportRows)) if len(reportRows) != 0: subHead = globs.optionManager.getRcOption('report', 'subheading') if subHead is not None: # Substitute subheading keywords subHead = subHead.replace('#DATE#', currentDate) if subHead is None or subHead == '': msgHtml += '<tr><td colspan="{}" align="center" bgcolor="{}"><b>{}:</b> {}</td></tr>\n'.format( nFields, reportOpts['subheadbg'], rptTits['date'], currentDate) msgText += '***** {}: {} *****\n'.format( rptTits['date'], currentDate) msgCsv += '\"***** {}: {} *****\"\n'.format( rptTits['date'], currentDate) else: msgHtml += '<tr><td colspan="{}" align="center" bgcolor="{}">{}</td></tr>\n'.format( nFields, reportOpts['subheadbg'], subHead) msgText += '***** {} *****\n'.format(subHead) msgCsv += '\"***** {} *****\"\n'.format(subHead) # Print column titles if printing for each section if reportOpts['repeatheaders'] is True: msgHtml, msgText, msgCsv = report.rptPrintTitles( msgHtml, msgText, msgCsv, rptCols) for source, destination, timestamp, examinedFiles, examinedFilesDelta, sizeOfExaminedFiles, fileSizeDelta, \ addedFiles, deletedFiles, modifiedFiles, filesWithError, parsedResult, messages, \ warnings, errors in reportRows: # Get date and time from timestamp dateStr, timeStr = drdatetime.fromTimestamp(timestamp) # Print report fields # Each field takes up one column/cell in the table msgHtml += '<tr>' # The full list of possible fields in the report. printField() below will skip a field if it is emoved in the .rc file. titles = [ 'source', 'destination', 'time', 'files', 'filesplusminus', 'size', 'sizeplusminus', 'added', 'deleted', 'modified', 'errors', 'result' ] fields = [ source, destination, timeStr, examinedFiles, examinedFilesDelta, sizeOfExaminedFiles, fileSizeDelta, addedFiles, deletedFiles, modifiedFiles, filesWithError, parsedResult ] for ttl, fld in zip(titles, fields): msgHtml += report.printField(ttl, fld, 'html') msgText += report.printField(ttl, fld, 'text') msgCsv += report.printField(ttl, fld, 'csv') msgHtml += '</tr>\n' msgText += '\n' msgCsv += '\n' fields = [messages, warnings, errors] options = ['displaymessages', 'displaywarnings', 'displayerrors'] backgrounds = ['jobmessagebg', 'jobwarningbg', 'joberrorbg'] titles = ['jobmessages', 'jobwarnings', 'joberrors'] # Print message/warning/error fields # Each of these spans all the table columns for fld, opt, bg, tit in zip(fields, options, backgrounds, titles): if ((fld != '') and (reportOpts[opt] == True)): msgHtml += '<tr><td colspan="{}" align="center" bgcolor="{}"><details><summary>{}</summary>{}</details></td></tr>\n'.format( nFields, reportOpts[bg], rptTits[tit], fld) msgText += '{}: {}\n'.format(rptTits[tit], fld) msgCsv += '\"{}: {}\"\n'.format(rptTits[tit], fld) # Move current timestamp ahead 1 second currentTs = currentDateEndTs + 1 # Now see which systems didn't report in dbCursor = globs.db.execSqlStmt( "SELECT source, destination, lastTimestamp FROM backupsets ORDER BY source, destination" ) setRows = dbCursor.fetchall() globs.log.write(3, 'setRows=[{}]'.format(setRows)) # Flag to let us know if we need to print a header for missing backupsets hdrFlag = 0 for source, destination, lastTimestamp in setRows: dbCursor = globs.db.execSqlStmt( "SELECT count(*) FROM report WHERE source = \'{}\' AND destination = \'{}\'" .format(source, destination)) seenRows = dbCursor.fetchone()[0] globs.log.write(3, 'seenRows=[{}]'.format(seenRows)) if seenRows == 0: # Didn't get any rows for source/Destination pair. Add to report if hdrFlag == 0: msgHtml += '<tr><td colspan="{}" align="center" bgcolor="{}"><b>Missing Backup Sets</b></td></tr>\n'.format( nFields, reportOpts['subheadbg']) msgText += 'Missing Back Sets\n' msgCsv += '\"Missing Back Sets\"\n' hdrFlag = 1 diff = drdatetime.daysSince(lastTimestamp) lastDateStr, lastTimeStr = drdatetime.fromTimestamp(lastTimestamp) msgHtml += '<tr><td colspan="{}" align="center" bgcolor="{}">{} to {}: <i>No new activity. Last activity on {} at {} ({} days ago)</i></td></tr>\n'.format( nFields, reportOpts['noactivitybg'], source, destination, lastDateStr, lastTimeStr, diff) msgText += '{} to {}: No new activity. Last activity on {} at {} ({} days ago)\n'.format( source, destination, lastDateStr, lastTimeStr, diff) msgCsv += '\"{} to {}: No new activity. Last activity on {} at {} ({} days ago)\"\n'.format( source, destination, lastDateStr, lastTimeStr, diff) # Add report footer msgHtml, msgText, msgCsv = report.rptBottom(msgHtml, msgText, msgCsv, startTime, nFields) # Return text & HTML messages to main program. It can decide which one it wants to use. return msgHtml, msgText, msgCsv