HEX
Server: Microsoft-IIS/10.0
System: Windows NT ITPWINWEBSVR22 10.0 build 20348 (Windows Server 2022) AMD64
User: www.conferencesearch.co.uk (0)
PHP: 8.3.30
Disabled: NONE
Upload Files
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])