File: D:/bin/scripts/python/smartctl/smartctl_log.py
# (SS,24/12/14) Python script to run smartctl on primary and secondary drives and email the results
# (SS,16/10/15) modified to add third drive (we now have 2 SSDs and 1 HDD)
import os
import datetime
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.mime.text import MIMEText
from email.MIMEBase import MIMEBase
from email import Encoders
import email.utils
import re
import anydbm
def set_alert(AAlert):
global FAlert # required to modify a global
FAlert = AAlert
def get_alert():
return FAlert
def get_html_header():
return """<html>
<title>Web Server SMART Drive Check Result</title>
<style>
html, body, h1, h2, p {
font-family: Courier New, Arial, Helvetica, sans-serif;
}
img {
border: 0;
}
table {
border-collapse: collapse;
}
table, th, td {
border: 1px solid black;
}
td, th {
padding: 5px;
white-space: nowrap;
}
</style>
<body>
"""
def get_html_footer():
return """</body>
</html>"""
def get_html(AText):
return AText + "<br>"
def send_email(ASubject, AFrom, ATo, ABcc, ABody, AFiles=None):
# Open a plain text file for reading. For this example, assume that
# the text file contains only ASCII characters.
#fp = open(ABodyFileName, 'rb')
#html = fp.read()
#fp.close()
html = ABody
#msg = MIMEMultipart('alternative')
msg = MIMEMultipart()
# header
if get_alert():
msg['Subject'] = 'ALERT!!! ' + ASubject
else:
msg['Subject'] = ASubject
msg['From'] = AFrom
msg['To'] = ATo
msg['Date'] = email.utils.formatdate(localtime=True)
if get_alert():
msg['X-Priority'] = '2'
#* html = html.replace('\r\n', '<br>')
part1 = MIMEText(html, 'html')
msg.attach(part1)
# add each attached file
for file in AFiles:
part = MIMEBase('application', "octet-stream")
part.set_payload( open(file,"rb").read() )
Encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="%s"'
% os.path.basename(file))
msg.attach(part)
# send the messsage using localhost
s = smtplib.SMTP('localhost')
s.sendmail(AFrom, [ATo, ABcc], msg.as_string())
s.quit()
def get_line(ASearchIn, ASearchFor, ALabel, AIntPos):
LFound = '';
for item in ASearchIn.split("\n"):
if ASearchFor in item:
LFound = item.strip()
# get all integers in line
if LFound <> '':
LInts = map(int, re.findall(r'\d+', LFound))
if LInts:
LValue = str(LInts[AIntPos]) # nth integer as string
else:
LValue = LFound.split()[-1] # last word
LDataKey = FDataPrefix + '-' + ASearchFor
try:
LPrevValue = FData[LDataKey] # get previous value
except KeyError: # assume blank if not found
LPrevValue = ''
LChanged = LValue <> LPrevValue
if LChanged:
set_alert(True)
if LValue.isdigit() and LPrevValue.isdigit():
LChangedVal = str( int(float(LValue)) - int(float(LPrevValue)) )
else:
LChangedVal = '*'
else:
LChangedVal = ''
FData[LDataKey] = LValue # save new value
LFound = '<tr><td>' + ALabel + '</td><td style="text-align:center">' + LValue + '</td><td style="text-align:center">' + LPrevValue + '</td><td style="text-align:center;color:red">' + LChangedVal + '</td></tr>' + NL
return LFound
def get_important(ALogFile, ATitle):
fp = open(ALogFile, 'rb')
log_text = fp.read()
fp.close()
global FDataPrefix
FDataPrefix = ALogFile # used to keep the data separate for each drive
lines = '<table>' + NL
lines = lines + '<tr><th>' + ATitle + '</th><th>Current</th><th>Previous</th><th>Change</th>' + NL
lines = lines + get_line(log_text, 'SMART overall-health self-assessment test result:', 'SMART Overall Health', 0)
lines = lines + get_line(log_text, 'Reallocated_Sector_Ct', 'Reallocated Sector Count', -1)
lines = lines + get_line(log_text, 'Runtime_Bad_Block', 'Runtime Bad Blocks', -1)
lines = lines + get_line(log_text, 'End-to-End_Error', 'End-to-End Error', -1)
lines = lines + get_line(log_text, 'Reported_Uncorrect', 'Reported Uncorrectable', -1)
lines = lines + get_line(log_text, 'Current_Pending_Sector', 'Current Pending Sector Count', -1)
lines = lines + get_line(log_text, 'Offline_Uncorrectable', 'Offline Uncorrectable', -1)
lines = lines + get_line(log_text, 'UDMA_CRC_Error_Count', 'UDMA CRC Error Count', -1)
lines = lines + get_line(log_text, 'Device Error Count:', 'Device Error Count', 0)
lines = lines + '</table>' + NL
return lines
start = datetime.datetime.now()
BR = '<br>'
NL = '\r\n'
FScriptDir = os.getcwd() # without this is doesn't save logs and .dat in correct location when run by task scheduler
FData = anydbm.open(FScriptDir + '\\saved.dat', 'c')
FDataPrefix = ''
FAlert = False # global used to set alert
FPrimaryDriveLogFileName = FScriptDir + '\\log_sda.txt'
FSecondaryDriveLogFileName = FScriptDir + '\\log_sdb.txt'
FThirdDriveLogFileName = FScriptDir + '\\log_sdc.txt' # (SS,16/10/15)
os.chdir('C:\\Program Files\\smartmontools\\bin') # path required for smartctl.exe to run in task scheduler, otherwise would have had to reboot the server for path to be applied (I think)
os.system('smartctl.exe -x /dev/sda > "' + FPrimaryDriveLogFileName + '"')
os.system('smartctl.exe -x /dev/sdb > "' + FSecondaryDriveLogFileName + '"')
# (SS,16/10/15) added following for third drive (we now have 2 SSDs and 1 HDD)
os.system('smartctl.exe -x /dev/sdc > "' + FThirdDriveLogFileName + '"')
FBody = get_html_header()
FBody = FBody + get_important(FPrimaryDriveLogFileName, 'Primary Drive') + BR + NL
FBody = FBody + get_important(FSecondaryDriveLogFileName, 'Secondary Drive') + BR + NL
FBody = FBody + get_important(FThirdDriveLogFileName, 'Third Drive') # (SS,16/10/15
FBody = FBody + NL
finish = datetime.datetime.now()
time_taken = finish - start
FBody = FBody + get_html('') + get_html('Started : ' + start.strftime('%Y-%m-%d %H:%M:%S'))
FBody = FBody + get_html('Finished: ' + finish.strftime('%Y-%m-%d %H:%M:%S'))
FBody = FBody + get_html('Time taken: %0.3f seconds' % (time_taken.seconds + time_taken.microseconds / 1E6))
FBody = FBody + get_html_footer()
FData.close()
# send the file as an email
# (SS,16/10/15) added FThirdDriveLogFileName
send_email('Web Server SMART Drive Check Result', 'smartctl@itpartnership.com', 'purchases@itpartnership.com', '', FBody, [FPrimaryDriveLogFileName, FSecondaryDriveLogFileName, FThirdDriveLogFileName])