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
예제 #3
0
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)