commit 6482e77af3594105129e4e47b5c853f18cfd3f0e Author: William Mantly Date: Wed Jul 1 16:11:27 2020 -0400 first diff --git a/db.py b/db.py new file mode 100644 index 0000000..a8d9c1c --- /dev/null +++ b/db.py @@ -0,0 +1,68 @@ +import sqlite3, datetime + +class SQLite(): + def __init__(self, file='sqlite.db'): + self.file=file + + def __enter__(self): + self.conn = sqlite3.connect(self.file) + self.conn.row_factory = sqlite3.Row + return self.conn.cursor() + + def __exit__(self, type, value, traceback): + self.conn.commit() + self.conn.close() + + +def create_status_table(): + with SQLite() as cur: + cur.execute(""" + CREATE TABLE `status` ( + `ID` INTEGER PRIMARY KEY AUTOINCREMENT, + `drive` TEXT, + `date` NUMERIC, + `type` TEXT, + `fast` INTEGER, + `delayed` INTEGER, + `re` INTEGER, + `total_e_cottect` INTEGER, + `correction` INTEGER, + `proc_gb` INTEGER, + `total_u_errors` INTEGER, + `Non-medium error count` INTEGER + ); + """) + +def table_exist(name): + with SQLite() as cur: + cur.execute(''' + SELECT name FROM sqlite_master WHERE type='table' AND name='{}'; + '''.format(name)) + + return True if cur.fetchone() else False + +def add_status(drive, type, status, NM_count): + now = int(datetime.datetime.now().timestamp()) + with SQLite() as cur: + cur.execute( + ''' + INSERT INTO status (drive, date, type, fast, delayed, re, total_e_cottect, correction, proc_gb, total_u_errors, "Non-medium error count") VALUES (?,?,?,?,?,?,?,?,?,?,?) + ''',(drive, now, type, *status, NM_count) + ) + +def get_status(drive): + with SQLite() as cur: + cur.execute(''' + SELECT * FROM status WHERE drive=(?) + ''',(drive,)) + return cur.fetchall() + + +if __name__ == '__main__': + if not table_exist('status'): + print("Creating table") + create_status_table() + else: + print('Table table exist!') + test = {'read': ['314277204', '9', '0', '314277213', '9', '229503.982', '0'], 'write': ['0', '0', '0', '0', '0', '47762.811', '0'], 'verify': ['64', '0', '0', '64', '0', '0.000', '0'], 'Non-medium error count': '10'} + diff --git a/parse.py b/parse.py new file mode 100644 index 0000000..241cd1e --- /dev/null +++ b/parse.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 + +import re, os +from subprocess import getoutput + +keys = [ + "ECC corrected fast", + "ECC corrected delayed", + "ECC reread/rewrites", + "Total errors corrected", + "Correction algorithm invocations", + "Gigabytes processed [10^9 bytes]", + "Total uncorrected errors", +] + +def log(type, disk, message): + print("!!LOG", type, disk, message) + +def get_all_scsi_path(): + disks = os.listdir('/dev/disk/by-id/') + out = [] + for disk in disks: + if disk.startswith('scsi') and 'part' not in disk: + out.append('/dev/disk/by-id/'+disk) + return out + +def call_smart(disks): + out = {} + for disk in disks: + content = getoutput('smartctl -a {}'.format(disk)) + content = parse_out(content, disk) + out[disk] = content + + return out + +def save_status(stats): + for disk in status: + for key in ['read', 'write', 'verify']: + if len(disk[key]) == 7: + db.add_status(disk, key, disk[key], disk['Non-medium error count']) + else: + log('missing', disk, '{} is missing values'.fotmat(key)) + + +def parse_out(content, disk=''): + out = { + 'read': {}, + 'write': {}, + 'verify': {}, + 'Non-medium error count': 0, + 'Elements in grown defect list': 0, + 'SMART Health Status': '' + } + + try: + out['SMART Health Status'] = re.search( + r'SMART Health Status:\s+(?P.+)', content + ).group('status') + except AttributeError as error: + log('missing', disk, "SMART Health Status not found") + + try: + out['Elements in grown defect list'] = re.search( + r'Elements in grown defect list:\s+(?P\d+)', content + ).group('number') + except AttributeError as error: + log('missing', disk, "Elements in grown defect list not found") + + try: + out['Non-medium error count'] = re.search( + r'Non-medium error count:\s+(?P\d+)', content + ).group('number') + except AttributeError as error: + log('missing', disk, "Non-medium error count not found") + + try: + content = content.split("Error counter log:")[1] + except IndexError: + log('failed', disk, 'Missing error information section') + return {} + + for line in content.split('\n'): + if line.startswith('read:'): + line = re.sub(r'^[a-z:\s]+', '', line) + line = re.split(r'\s+', line) + out['read'] = dict(zip(keys, line)) + continue + + if line.startswith('write:'): + line = re.sub(r'^[a-z:\s]+', '', line) + line = re.split(r'\s+', line) + out['write'] = dict(zip(keys, line)) + continue + + if line.startswith('verify:'): + line = re.sub(r'^[a-z:\s]+', '', line) + line = re.split(r'\s+', line) + out['verify'] = dict(zip(keys, line)) + continue + + return out + +if __name__ == "__main__": + import json + disks = get_all_scsi_path() + parsed = call_smart(disks) + print(json.dumps(parsed, indent=4, sort_keys=True)) + print(*[(i,parsed[i].get('SMART Health Status')) for i in parsed], sep='\n') diff --git a/smart.txt b/smart.txt new file mode 100644 index 0000000..a835c51 --- /dev/null +++ b/smart.txt @@ -0,0 +1,60 @@ +smartctl 6.5 2016-01-24 r4214 [x86_64-linux-4.4.0-165-generic] (local build) +Copyright (C) 2002-16, Bruce Allen, Christian Franke, www.smartmontools.org + +=== START OF INFORMATION SECTION === +Vendor: SEAGATE +Product: ST6000NM0034 +Revision: E005 +Compliance: SPC-4 +User Capacity: 6,001,175,126,016 bytes [6.00 TB] +Logical block size: 512 bytes +Physical block size: 4096 bytes +Formatted with type 2 protection +LB provisioning type: unreported, LBPME=0, LBPRZ=0 +Rotation Rate: 7200 rpm +Form Factor: 3.5 inches +Logical Unit id: 0x5000c50062502b8b +Serial number: Z4D0C1EV0000S440NQKX +Device type: disk +Transport protocol: SAS (SPL-3) +Local Time is: Tue Nov 5 19:56:43 2019 EST +SMART support is: Available - device has SMART capability. +SMART support is: Enabled +Temperature Warning: Enabled + +=== START OF READ SMART DATA SECTION === +SMART Health Status: OK + +Current Drive Temperature: 50 C +Drive Trip Temperature: 60 C + +Manufactured in week 36 of year 2014 +Specified cycle count over device lifetime: 10000 +Accumulated start-stop cycles: 46 +Specified load-unload count over device lifetime: 300000 +Accumulated load-unload cycles: 5971 +Elements in grown defect list: 7 + +Vendor (Seagate) cache information + Blocks sent to initiator = 3267148336 + Blocks received from initiator = 1629969808 + Blocks read from cache and sent to initiator = 3854095152 + Number of read and write commands whose size <= segment size = 300541123 + Number of read and write commands whose size > segment size = 3892 + +Vendor (Seagate/Hitachi) factory information + number of hours powered up = 28080.92 + number of minutes until next internal SMART test = 54 + +Error counter log: + Errors Corrected by Total Correction Gigabytes Total + ECC rereads/ errors algorithm processed uncorrected + fast | delayed rewrites corrected invocations [10^9 bytes] errors +read: 314277204 9 0 314277213 9 229503.982 0 +write: 0 0 0 0 0 47762.811 0 +verify: 64 0 0 64 0 0.000 0 + +Non-medium error count: 10 + +No self-tests have been logged + diff --git a/sqlite.db b/sqlite.db new file mode 100644 index 0000000..bdcb356 Binary files /dev/null and b/sqlite.db differ