scanConfigError = listDisks_Out[0] # SCAN_OS_NOCMD, SCAN_OS_ERROR, SCAN_UNKNOWN_ERROR diskList = listDisks_Out[1] else: scanConfigError = '' diskList = diskListManual # or just use manually provided settings if diskList: getDisksTempSCT_Out = getDisksTempSCT() diskConfigError = getDisksTempSCT_Out[0] # D_OS_NOCMD, D_OS_ERROR, NOTEMPS, UNKNOWN_EXC_ERROR senderData = getDisksTempSCT_Out[1] else: diskConfigError = 'NODISKS' senderData = [] if scanConfigError != '': senderData.append(host + ' mini.disk.info[ConfigStatus] "' + scanConfigError + '"') # takes precedence elif diskConfigError != '': senderData.append(host + ' mini.disk.info[ConfigStatus] "' + diskConfigError + '"') # on mixed errors else: senderData.append(host + ' mini.disk.info[ConfigStatus] "CONFIGURED"') # signals that client host is configured for d in diskList: dR = replaceStr(d) jsonData.append({'{#DISK}':dR}) # available disks must always be populated to LLD link = r'https://github.com/nobodysu/zabbix-mini-IPMI/issues' processData(senderData, jsonData, agentConf, senderPyPath, senderPath, timeout, host, link)
def getDisksTempSCT(): '''Tries to get temperature from every disk provided in list via SCT command. Also calculates maximum temperature among all disks.''' temperatures = [] sender = [] globalError = '' # intended to change once in loop for d in diskList: dR = replaceStr(d) # sanitize the item key if not checkStandby == 'yes': if isStandby(d): sender.append(host + ' mini.disk.info[' + dR + ',DriveStatus] "STANDBY"') continue # do not try to access current disk if its in STANDBY mode p = '' localError = '' try: # take string from 'diskList', make arguments from it and append to existing command, then run it p = subprocess.check_output([ctlPath, '-l', 'scttempsts'] + split(d), universal_newlines=True) except OSError as e: if e.args[0] == 2: globalError = 'D_OS_NOCMD' else: globalError = 'D_OS_ERROR' if sys.argv[1] == 'getverb': raise break # configuration error except subprocess.CalledProcessError as e: # handle process-specific errors if not e.args: # unnecessary for python3? sender.append('%s mini.disk.info[%s,DriveStatus] "UNKNOWN_RESPONSE"' % (host, dR)) continue elif e.args[0] == 1 or e.args[0] == 2: # non-fatal disk error codes are not a concern for temperature monitoring script sender.append(host + ' mini.disk.info[' + dR + ',DriveStatus] "ERR_CODE_' + str(e.args[0]) + '"') continue # continue to the next disk on fatal error p = e.output # substitute output even on error, so it can be processed further except Exception as e: localError = 'UNKNOWN_EXC_ERROR' if sys.argv[1] == 'getverb': raise try: p = e.output except: p = '' temp = re.search(r'Current\s+Temperature:\s+(\d+)\s+Celsius', p, re.I) if temp: tempResult = temp.group(1) else: # if nothing was found - try regular SMART command getDiskTempA_Out = getDiskTempA(d) if getDiskTempA_Out: tempResult = getDiskTempA_Out else: tempResult = '' localError = 'NO_TEMP' if tempResult != '': sender.append(host + ' mini.disk.temp[' + dR + '] ' + tempResult) temperatures.append(int(tempResult)) if localError == '': sender.append(host + ' mini.disk.info[' + dR + ',DriveStatus] "PROCESSED"') # no trigger assigned, fallback value elif localError == 'NO_TEMP': sender.append(host + ' mini.disk.info[' + dR + ',DriveStatus] "NO_TEMP"') else: sender.append(host + ' mini.disk.info[' + dR + ',DriveStatus] "UNKNOWN_ERROR_ON_PROCESSING"') if temperatures: sender.append(host + ' mini.disk.temp[MAX] ' + str(max(temperatures))) elif globalError == '': # if no temperatures were discovered and globalError was not set globalError = 'NOTEMPS' return globalError, sender
def getSmart(d): #print("d:\t'%s'" % d) d = d.strip() if not diskListManual: # do not replace manual 'scsi' d = d.replace('-d scsi', '-d auto') # prevent empty results #print("dS:\t'%s'" % d) dR = replaceStr(d) # sanitize the item key dOrig = dR # save original sanitized device name #print("dR:\t'%s'" % dR) #print("dOrig:\t'%s'" % dOrig) sender = [] json = [] driveStatus = None serial = None # tracking duplicates driveHeader = [] # tracking duplicates try: p = subprocess.check_output( [ctlPath, '-a'] + split(d), universal_newlines=True ) # take string from 'diskListRe', make arguments from it and append to existing command, then run it except OSError as e: if e.args[0] == 2: fatalError = 'D_OS_NOCMD' else: fatalError = 'D_OS_ERROR' return fatalError, sender, json, (serial, driveHeader), (dR, dOrig) except subprocess.CalledProcessError as e: # handle process-specific errors ''' see 'man smartctl' for more information Bit 0 = Code 1 Bit 1 = Code 2 Bit 2 = Code 4 Bit 3 = Code 8 Bit 4 = Code 16 Bit 5 = Code 32 Bit 6 = Code 64 Bit 7 = Code 128 ''' p = e.output # substitude output even on error, so it can be processed further driveStatus = 'ERR_CODE_%s' % (str(e.args[0])) m = "SMART support is: Unavailable - Packet Interface Devices [this device: CD/DVD] don't support ATA SMART" if m in p: sender.append('%s smartctl.info[%s,DriveStatus] "CD_DVD_DRIVE"' % (host, dR)) return None, sender, json, (serial, driveHeader), (dR, dOrig) if e.args[0] == 1 or e.args[0] == 2: sender.append('%s smartctl.info[%s,DriveStatus] "%s"' % (host, dR, driveStatus)) # duplicate follows return None, sender, json, (serial, driveHeader), (dR, dOrig) except: p = e.output driveStatus = 'UNKNOWN_ERROR_ON_PROCESSING' if sys.argv[1] == 'getverb': raise else: driveStatus = 'PROCESSED' # no trigger assigned, but its needed as a fallback value # Determine disk identifier serialRe = re.search(r'^Serial Number:\s+(.+)$', p, re.M | re.I) if serialRe: if mode == 'serial': dR = replaceStr( serialRe.group(1) ) # in 'serial' mode, if serial number is found it will be used as main identifier, also sanitize it # ! 'd' becomes serial ! sender.append('%s smartctl.info[%s,serial] "%s"' % (host, dR, serialRe.group(1))) serial = serialRe.group(1) # tracking duplicates # Process disk errors when disk ID is determined if driveStatus: sender.append('%s smartctl.info[%s,DriveStatus] "%s"' % (host, dR, driveStatus)) # Adding main LLD json.append({'{#DISKID}': dR}) sender.append('%s smartctl.info[%s,device] "%s"' % (host, dR, dOrig)) # SATA and SAS info familyRe = re.search(r'^Model Family:\s+(.+)$', p, re.M | re.I) if familyRe: sender.append('%s smartctl.info[%s,family] "%s"' % (host, dR, str(familyRe.group(1)).replace('"', '\\"'))) modelRe = re.search( r'^Device Model:\s+(.+)$|^Device:\s+(.+)$|^Product:\s+(.+)$', p, re.M | re.I) if modelRe: if modelRe.group(1): modelResult = modelRe.group(1) elif modelRe.group(2): modelResult = modelRe.group(2) elif modelRe.group(3): modelResult = modelRe.group(3) else: modelResult = 'UNKNOWN' sender.append('%s smartctl.info[%s,model] "%s"' % (host, dR, modelResult)) driveHeader.append(modelRe.group(1)) # tracking duplicates capacityRe = re.search(r'User Capacity:\s+(.+)bytes', p, re.I) if capacityRe: capacityValue = re.sub('\s|\,', '', capacityRe.group(1)) sender.append('%s smartctl.info[%s,capacity] "%s"' % (host, dR, capacityValue)) driveHeader.append(capacityValue) # tracking duplicates selftestRe = re.search( r'^SMART overall-health self-assessment test result:\s+(.+)$|^SMART Health Status:\s+(.+)$', p, re.M | re.I) if selftestRe: if selftestRe.group(1): selftestResult = selftestRe.group(1) elif selftestRe.group(2): selftestResult = selftestRe.group(2) else: selftestResult = 'UNKNOWN' sender.append('%s smartctl.info[%s,selftest] "%s"' % (host, dR, selftestResult)) rpmRe = re.search(r'^Rotation Rate:\s+(\d+)\s+rpm$', p, re.M | re.I) if rpmRe: sender.append('%s smartctl.info[%s,rpm] "%s"' % (host, dR, rpmRe.group(1))) driveHeader.append(rpmRe.group(1)) # tracking duplicates formfactorRe = re.search(r'^Form Factor:\s+(.+)$', p, re.M | re.I) if formfactorRe: sender.append('%s smartctl.info[%s,formFactor] "%s"' % (host, dR, formfactorRe.group(1))) # non-SAS info sataVerRe = re.search(r'^SATA Version is:\s+(.+)$', p, re.M | re.I) if sataVerRe: sender.append('%s smartctl.info[%s,sataVersion] "%s"' % (host, dR, sataVerRe.group(1))) bandwidthMaxRe = re.search(r'^SATA Version is:\s+.+,\s+(\d+\.\d+)\s+Gb\/s', p, re.M | re.I) if bandwidthMaxRe: json.append({'{#DISKIDBANDWIDTH}': dR}) sender.append('%s smartctl.info[%s,bandwidthMax] "%s"' % (host, dR, bandwidthMaxRe.group(1))) bandwidthCurrentRe = re.search( r'^SATA Version is:\s+.+current\:\s+(\d+\.\d+)\s+Gb\/s', p, re.M | re.I) if bandwidthCurrentRe: bandwidthCurrentResult = bandwidthCurrentRe.group(1) else: bandwidthCurrentResult = bandwidthMaxRe.group(1) sender.append('%s smartctl.info[%s,bandwidthCurrent] "%s"' % (host, dR, bandwidthCurrentResult)) firmwareRe = re.search(r'^Firmware Version:\s+(.+)$', p, re.M | re.I) if firmwareRe: sender.append('%s smartctl.info[%s,firmware] "%s"' % (host, dR, firmwareRe.group(1))) driveHeader.append(firmwareRe.group(1)) # tracking duplicates vendorRe = re.search(r'^Vendor:\s+(.+)$', p, re.M | re.I) if vendorRe: sender.append('%s smartctl.info[%s,vendor] "%s"' % (host, dR, vendorRe.group(1))) # SAS-only SSD value ssdwearRe = re.search(r'^Percentage used endurance indicator:\s+(\d+)', p, re.M | re.I) if ssdwearRe: json.append({'{#DISKIDSSD}': dR}) sender.append('%s smartctl.value[%s,SSDwear] "%s"' % (host, dR, ssdwearRe.group(1))) # SATA values valuesRe = re.findall( r'^(?:\s+)?(\d+)\s+([\w-]+)\s+[\w-]+\s+\d{3}\s+\d{3}\s+\d{3}\s+[\w-]+\s+[\w-]+\s+[\w-]+\s+(\d+)', p, re.M | re.I) # catch id, name and value if valuesRe: sender.append('%s smartctl.info[%s,SmartStatus] "PRESENT_SATA"' % (host, dR)) for v in valuesRe: json.append({'{#DVALUE%s}' % v[0]: dR, '{#SMARTNAME}': v[1]}) sender.append('%s smartctl.value[%s,%s] %s' % (host, dR, v[0], v[2])) else: getSmartSAS_Out = getSmartSAS(p, dR) if not getSmartSAS_Out[0]: sender.append('%s smartctl.info[%s,SmartStatus] "PRESENT_SAS"' % (host, dR)) sender.extend(getSmartSAS_Out[1]) json.extend(getSmartSAS_Out[2]) else: sender.extend(whyNoSmart(p, dR)) return None, sender, json, (serial, driveHeader), (dR, dOrig)