Current File : //lib/check_mk_agent/plugins/smart
#!/bin/bash
# Copyright (C) 2019 tribe29 GmbH - License: GNU General Public License v2
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
# conditions defined in the file COPYING, which is part of this source code package.

# Reason for this no-op: shellcheck disable=... before the first command disables the error for the
# entire script.
:

# Disable unused variable error (needed to keep track of version)
# shellcheck disable=SC2034
CMK_VERSION="2.0.0p23"

# Function to replace "if type [somecmd]" idiom
# 'command -v' tends to be more robust vs 'which' and 'type' based tests
inpath() {
    command -v "${1:?No command to test}" >/dev/null 2>&1
}

# This will be called on LSI based raidcontrollers and accesses
# the SMART data of SATA disks attached to a SAS Raid HBA via
# SCSI protocol interface.
megaraid_info()
{
    #PDINFO=$(MegaCli -PDlist -a0)
    if [ -z "$1" ]; then
        PDINFO=$(megacli -PDlist -a0 -NoLog)
    else
        PDINFO=$($1 -PDlist -a0 -NoLog)
    fi

    echo "$PDINFO" | \
    while read -r line ; do
        case "$line" in
           # FIRST LINE
           "Enclosure Device ID"*) #Enclosure Device ID: 252
            ENC=$( echo "$line" | awk '{print $4}')
                  unset SLOT LOG_DEV_ID VEND MODEL
        ;;
        "Slot Number"*)  #Slot Number: 7
            SLOT=$( echo "$line" | awk '{print $3}')
        ;;
        # Identify the logical device ID. smartctl needs it to access the disk.
        "Device Id"*)    #Device Id: 19
            LOG_DEV_ID=$( echo "$line" | awk '{print $3}')
        ;;
        "PD Type"*)      #PD Type: SATA
            VEND=$( echo "$line" | awk '{print $3}')
        ;;
        # This is the last value, generate output here
        "Inquiry Data"*)
         #Inquiry Data: WD-WCC1T1035197WDC WD20EZRX-00DC0B0 80.00A80
            # $4 seems to be better for some vendors... wont be possible to get this perfect.
            MODEL=$( echo "$line" | awk '{print $3}')

            # /dev/sdc ATA SAMSUNG_SSD_830   5 Reallocated_Sector_Ct   0x0033   100   100   010    Pre-fail  Always       -
            smartctl -d megaraid,"${LOG_DEV_ID}" -v 9,raw48 -A /dev/sg0 | \
              grep Always  | grep -E -v '^190(.*)Temperature(.*)'       | \
              sed "s|^|Enc${ENC}/Slot${SLOT} $VEND $MODEL |"
            ;;
            esac
       done
}


# Only handle always updated values, add device path and vendor/model
if which smartctl > /dev/null 2>&1 ; then
    #
    # if the 3ware-utility is found
    # get the serials for all disks on the controller
    #
    if which tw_cli > /dev/null 2>&1 ; then
        # support for only one controller at the moment
        TWAC=$(tw_cli show | awk 'NR < 4 { next } { print $1 }' | head -n 1)

        # - add a trailing zero to handle case of unused slot
        #   trailing zeros are part of the device links in /dev/disk/by-id/... anyway
        # - only the last 9 chars seem to be relevant
        # (hopefully all this doesn't change with new kernels...)
        eval "$(tw_cli /"$TWAC" show drivestatus | grep -E '^p[0-9]' | awk '{print $1 " " $7 "0"}' | while read -r twaminor serial ; do
            twaminor=${twaminor#p}
            serial=${serial:(-9)}
            serial=AMCC_${serial}00000000000
            echo "$serial=$twaminor"
        done)"
    else
        echo "tw_cli not found" >&2
    fi

    INPATH_UDEVADM=$(inpath udevadm && echo "udevadm_present")

    echo '<<<smart>>>'
    SEEN=
    for D in /dev/disk/by-id/{scsi,ata,nvme}-*; do
        [ "$D" != "${D%scsi-\*}" ] && continue
        [ "$D" != "${D%ata-\*}" ] && continue
        [ "$D" != "${D%nvme-\*}" ] && continue
        [ "$D" != "${D%-part*}" ] && continue
        N=$(readlink "$D")
        N=${N##*/}
        if [ -r /sys/block/"$N"/device/vendor ]; then
            VEND=$(tr -d ' ' < /sys/block/"$N"/device/vendor)
        elif [ -r /sys/block/"$N"/device/device/vendor ]; then
            VEND=NVME
        else
            # 2012-01-25 Stefan Kaerst CDJ - in case $N does not exist
            VEND=ATA
        fi
        if [ -r /sys/block/"$N"/device/model ]; then
            MODEL=$(sed -e 's/ /_/g' -e 's/_*$//g' < /sys/block/"$N"/device/model)
        else
            MODEL=$(smartctl -a "$D" | grep -i "device model" | sed -e "s/.*:[ ]*//g" -e "s/\ /_/g")
        fi
        # Excluded disk models for SAN arrays or certain RAID luns that are also not usable..
        if [ "$MODEL" = "iSCSI_Disk" ] || [ "$MODEL" = "LOGICAL_VOLUME" ]; then
            continue
        fi

        # Exclude everything which can read a CD
        if [ "$INPATH_UDEVADM" == "udevadm_present" ] && (udevadm info /dev/"$N" | grep -Fxq 'E: ID_CDROM_CD=1'); then
            continue
        fi

        # Avoid duplicate entries for same device
        if [ "${SEEN//.$N./}" != "$SEEN" ] ; then
            continue
        fi
        SEEN="$SEEN.$N."

        # strip device name for final output
        DNAME=${D#/dev/disk/by-id/scsi-}
        DNAME=${DNAME#/dev/disk/by-id/ata-}
        # 2012-01-25 Stefan Kaerst CDJ - special option in case vendor is AMCC
        CMD=
        if [ "$VEND" == "AMCC" ] && [ -n "$TWAC" ]; then
            DNAME=${DNAME#1}
            [ -z "${!DNAME}" ] && continue
            CMD="smartctl -d 3ware,${!DNAME} -v 9,raw48 -A /dev/twa0"
            # create nice device name including model
            MODEL=$(tw_cli /"$TWAC"/p"${!DNAME}" show model | head -n 1 | awk -F= '{ print $2 }')
            MODEL=${MODEL## }
            MODEL=${MODEL// /-}
            DNAME=${DNAME#AMCC_}
            DNAME="AMCC_${MODEL}_${DNAME%000000000000}"
        elif [ "$VEND" != "ATA" ] ; then
            if [ "$VEND" == "NVME" ] ; then
                DNAME="/dev/$N"
                CMD="smartctl -d nvme -A $DNAME"
            else
                TEMP=
                # create temperature output as expected by checks/smart
                # this is a hack, TODO: change checks/smart to support SCSI-disks
                eval "$(smartctl -d scsi -i -A "$D" | while read -r a b c d _ ; do
                    [ "$a" == Serial ] && echo SN="$c"
                    [ "$a" == Current ] && [ "$b" == Drive ] && [ "$c" == Temperature: ] && echo TEMP="$d"
                done)"
                [ -n "$TEMP" ] && CMD="echo 194 Temperature_Celsius 0x0000 000 000 000 Old_age Always - $TEMP (0 0 0 0)"
                DNAME="${VEND}_${MODEL}_${SN}"
            fi
        else
            CMD="smartctl -d ata -v 9,raw48 -A $D"
        fi

            if [ $VEND == "NVME" ]; then
                echo "$DNAME $VEND $MODEL"
                [ -n "$CMD" ] && $CMD | sed -e '1,5d; /^$/d'
            else
                [ -n "$CMD" ] && $CMD | grep Always | grep -E -v '^190(.*)Temperature(.*)' | sed "s|^|$DNAME $VEND $MODEL |"
            fi
    done 2>/dev/null


    # Call MegaRaid submodule if conditions are met
    if type MegaCli >/dev/null 2>&1; then
        MegaCli_bin="MegaCli"
    elif type MegaCli64 >/dev/null 2>&1; then
        MegaCli_bin="MegaCli64"
    elif type megacli >/dev/null 2>&1; then
        MegaCli_bin="megacli"
    else
        MegaCli_bin="unknown"
    fi

    if [ "$MegaCli_bin" != "unknown" ]; then
        megaraid_info "$MegaCli_bin"
    fi
else
    echo "ERROR: smartctl not found" >&2
fi