#!/usr/local/bin/python3 # dinfo # Gathers info about drive errors and power cycle counts, and finds the slots the drives should be in. # Copyright 2021, Mike McPhee # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version.############ import subprocess import re import sys class drive: device = None serial = None row = None slot = None sas3ircuRow = None sas3ircuSlot = None pwrcycl = None offline = None pending = None def __init__(self, device): self.device = device #grabbing serial serialLine = subprocess.check_output("smartctl -i /dev/"+device+" | grep -i 'serial number'", shell=True, encoding='utf-8').strip('\n') serial = serialLine.split() self.serial = serial[2] infolist = subprocess.check_output("smartctl -x /dev/"+device+" | grep -i 'power_cycle_count\|offline_uncorrectable\|current_pending_sector' | awk '{print $8}'", shell=True, encoding = 'utf-8').split('\n') del infolist[len(infolist)-1] self.pwrcycl = infolist[0] self.offline = infolist[1] self.pending = infolist[2] def findSlotS3(self): #cardIOController = subprocess.check_output("lspci | grep -i 'LSI SAS'", shell=True, encoding = 'utf-8') cardIOController = subprocess.check_output("lspci | grep -i 'SAS3'", shell=True, encoding = 'utf-8') cardIOController = re.search("(SAS3224)", cardIOController).group() if cardIOController != None: controllercards = subprocess.check_output("sas3ircu list | grep -i index -A2 | awk '{print $1}' | grep '[0-9]$'", shell=True, encoding = 'utf-8').split('\n') del controllercards[len(controllercards)-1] cardnumber = None s3slot = None slotnumber = None #finding slot using sas3ircu command for y in controllercards: s3slot = subprocess.check_output("sas3ircu "+y+" display | grep -i "+self.serial+" -B10 | grep -i slot | awk '{print $4}'", shell=True, encoding = 'utf-8').strip('\n') if s3slot != "": cardnumber = int(y)+1 slotnumber = int(s3slot)+1 s3slot = None break self.sas3ircuRow = cardnumber self.sas3ircuSlot = slotnumber else: #print("Controller card model not supported. SAS 9305 required to run 'sas3ircu'.") return def findSlotCC(self, boardtype): camslot = None camrow = None cam = subprocess.check_output("camcontrol devlist | grep -v ada | grep -i "+self.device+"", shell=True, encoding = 'utf-8').strip('\n') scbus = re.search("scbus(\S+)", cam).group() target = int(float(re.search("target (\d+)", cam).group(1))) if scbus != None: if boardtype == "X10DRL-i": if scbus == "scbus0": camrow = 1 camslot = int(target)+1 if scbus == "scbus11": camrow = 2 camslot = int(target)+1 if scbus == "scbus12": camrow = 3 camslot = int(target)+1 if scbus == "scbus13": camrow = 4 camslot = int(target)+1 elif boardtype == "X11SPL-f" or boardtype == "X11SPL-F": if scbus == "scbus8": camrow = 1 camslot = int(target)+1 if scbus == "scbus9": camrow = 2 camslot = int(target)+1 if scbus == "scbus10": camrow = 3 camslot = int(target)+1 if scbus == "scbus11": camrow = 4 camslot = int(target)+1 else: camrow = None camslot = None self.row = camrow self.slot = camslot def print(self): print(f"\n*----------------------------------dinfo---------------------------------------*\nDevice: {self.device} \nsas3ircu Slot: {self.sas3ircuRow} --> {self.sas3ircuSlot}\ncamcontrol Slot: {self.row} --> {self.slot}\nSerial Number: {self.serial}\nPower Cycle Count: {self.pwrcycl}\nOffline Uncorrectable Sector Count: {self.offline}\nCurrent Pending Sector Count: {self.pending}\n\n") supportedBoards = ['X10DRL-I', 'X11SPL-F'] print("\n*----------------------------------dinfo---------------------------------------*\nGathers info about drive errors and power cycle counts, along with the drive's device identifier and physical slot.\n"); #print(sys.argv[1]) boardtype = subprocess.check_output("ipmitool fru | grep -i 'board product' | awk '{print $4}'", shell=True, encoding = 'utf-8').strip('\n') boardtype = str(boardtype).strip(" ") if supportedBoards.count(boardtype.upper())>0: print(f"Motherboard model number: {boardtype}") devlist = subprocess.check_output("ls /dev | grep -i da | grep -v 'p[0-9]$' | grep -v ada", shell =True, encoding = 'utf-8').split('\n') del devlist[len(devlist)-1] if len(sys.argv)>2: if sys.argv[1]== "list" : if sys.argv[2] == "devices": print("\nDevices Found: ") for a in devlist: print(f"{a}") if sys.argv[2] == "all": # Looping through each device found in /dev for x in devlist: currentdrive = drive(x) currentdrive.findSlotS3() currentdrive.findSlotCC(boardtype) currentdrive.print() elif devlist.count(sys.argv[2]): output = drive(sys.argv[2]) output.findSlotS3() output.findSlotCC(boardtype) output.print() else: print("\nInvalid arguments used. Device does not exist.") else: print("\nInvalid arguments used. Must use 'list all' or 'list da#'." ) else: print("\nInvalid arguments used. dinfo accepts: \n\nlist devices --> lists devices found in /dev.\n\nlist all --> list info for all devices.\n\nlist *device id (da#)*: --> lists info for specific device.\n*------------------------------------------------------------------------------*\n") else: print("\nBoard not supported. ")