#!/bin/bash
# check_snmp_synology for nagios
# Version: 2.6
# 07.12.2016  Nicolas Ordonez, Switzerland
# Thanks to contributors:
# - Tobias Schenke
# Modified By:
# 04.17.2019  Corben Eastman
#---------------------------------------------------
# this plugin checks the health of your Synology NAS
# - System status (Power, Fans)
# - Disks status
# - RAID status
# - DSM update status
# - Temperature Warning and Critical
# - UPS information
# - Storage percentage of use
#
# Tested with DSM 6.2
#---------------------------------------------------
# Based on http://ukdl.synology.com/download/Document/MIBGuide/Synology_DiskStation_MIB_Guide.pdf
#---------------------------------------------------
# actual number disk limit = 52 disks per Synology
#---------------------------------------------------

SNMPWALK=$(which snmpwalk)
SNMPGET=$(which snmpget)

SNMPVersion="3"
SNMPV2Community="public"
SNMPTimeout="10"
warningTemperature="50"
criticalTemperature="60"
warningStorage="80"
criticalStorage="95"
hostname=""
healthWarningStatus=0
healthCriticalStatus=0
healthString=""
checkDSMUpdate="yes"
allowedTypes=(system version temperature power fan disk raid ups)
system=(model serialNumber systemStatus)
version=(DSMVersion DSMUpgradeAvailable)
temperature=(temperature)
power=(powerStatus)
fan=(systemFanStatus CPUFanStatus)
disk=(disk disk2)
raid=(RAID)
ups=(UpsModel UpsSN UpsStatus UpsLoad UpsBatteryCharge UpsBatteryChargeWarning)
checks=()


#OID declarations
OID_syno="1.3.6.1.4.1.6574"
#system
OID_model="1.3.6.1.4.1.6574.1.5.1.0"
OID_serialNumber="1.3.6.1.4.1.6574.1.5.2.0"
OID_systemStatus="1.3.6.1.4.1.6574.1.1.0"
#version
OID_DSMVersion="1.3.6.1.4.1.6574.1.5.3.0"
OID_DSMUpgradeAvailable="1.3.6.1.4.1.6574.1.5.4.0"
#temp
OID_temperature="1.3.6.1.4.1.6574.1.2.0"
#power
OID_powerStatus="1.3.6.1.4.1.6574.1.3.0"
#fan
OID_systemFanStatus="1.3.6.1.4.1.6574.1.4.1.0"
OID_CPUFanStatus="1.3.6.1.4.1.6574.1.4.2.0"

#disk
OID_disk=""
OID_disk2=""
OID_diskID="1.3.6.1.4.1.6574.2.1.1.2"
OID_diskModel="1.3.6.1.4.1.6574.2.1.1.3"
OID_diskStatus="1.3.6.1.4.1.6574.2.1.1.5"
OID_diskTemp="1.3.6.1.4.1.6574.2.1.1.6"

#raid
OID_RAID=""
OID_RAIDName="1.3.6.1.4.1.6574.3.1.1.2"
OID_RAIDStatus="1.3.6.1.4.1.6574.3.1.1.3"
OID_Storage="1.3.6.1.2.1.25.2.3.1"
OID_StorageDesc="1.3.6.1.2.1.25.2.3.1.3"
OID_StorageAllocationUnits="1.3.6.1.2.1.25.2.3.1.4"
OID_StorageSize="1.3.6.1.2.1.25.2.3.1.5"
OID_StorageSizeUsed="1.3.6.1.2.1.25.2.3.1.6"

#ups
OID_UpsModel="1.3.6.1.4.1.6574.4.1.1.0"
OID_UpsSN="1.3.6.1.4.1.6574.4.1.3.0"
OID_UpsStatus="1.3.6.1.4.1.6574.4.2.1.0"
OID_UpsLoad="1.3.6.1.4.1.6574.4.2.12.1.0"
OID_UpsBatteryCharge="1.3.6.1.4.1.6574.4.3.1.1.0"
OID_UpsBatteryChargeWarning="1.3.6.1.4.1.6574.4.3.1.4.0"

usage()
{
        echo "usage: ./check_snmp_synology [OPTIONS] -u [user] -p [pass] -h [hostname]"
        echo "options:"
        echo "            -u [snmp username]   	Username for SNMPv3"
        echo "            -p [snmp password]   	Password for SNMPv3"
        echo ""
        echo "            -2 [community name]	  	Use SNMPv2 (no need user/password) & define community name (ex: public)"
        echo ""
        echo "            -h [hostname or IP](:port)	Hostname or IP. You can also define a different port"
        echo ""
        echo "            -W [warning temp]		Warning temperature (for disks & synology) (default $warningTemperature)"
        echo "            -C [critical temp]		Critical temperature (for disks & synology) (default $criticalTemperature)"
        echo ""
        echo "            -w [warning %]		Warning storage usage percentage (default $warningStorage)"
        echo "            -c [critical %]		Critical storage usage percentage (default $criticalStorage)"
        echo ""
        echo "            -t [check type]	        The type of check to perform, must be one of the following: ${allowedTypes[@]}"
        echo ""
        echo "            -i   			Ignore DSM updates"
        echo ""
        echo "examples:	./check_snmp_synology -u admin -p 1234 -h nas.intranet -t temperature"
        echo "	     	./check_snmp_synology -u admin -p 1234 -h nas.intranet -v -t system"
        echo "		./check_snmp_synology -2 public -h nas.intranet -t power"
        echo "		./check_snmp_synology -2 public -h nas.intranet:10161 -t ups"
        exit 3
}

if [ "$1" == "--help" ]; then
    usage; exit 0
fi

while getopts 2:W:C:w:c:u:p:h:t:iU OPTNAME; do
        case "$OPTNAME" in
	u)	SNMPUser="$OPTARG";;
        p)	SNMPPassword="$OPTARG";;
        h)	hostname="$OPTARG";;
	2)	SNMPVersion="2"
		SNMPV2Community="$OPTARG";;
	w)	warningStorage="$OPTARG";;
        c)      criticalStorage="$OPTARG";;
	W)	warningTemperature="$OPTARG";;
	C)	criticalTemperature="$OPTARG";;
	i)	checkDSMUpdate="no";;
        t)      checkType=("$OPTARG");;
        *)      usage;;
        esac
done

if [ -z ${checkType+x} ] ; then
    echo "Missing check type";
    usage; exit 0;
fi

if [[ ! "${allowedTypes[@]}" =~ "$checkType" ]] ; then
    echo "$checkType is an invalid check type"
    usage; exit 0
fi

subst="$checkType[@]"
checks=(${!subst})

if [ "$warningTemperature" -gt "$criticalTemperature" ] ; then
    echo "Critical temperature must be higher than warning temperature"
    echo "Warning temperature: $warningTemperature"
    echo "Critical temperature: $criticalTemperature"
    echo ""
    echo "For more information:  ./${0##*/} --help"
    exit 1
fi

if [ "$warningStorage" -gt "$criticalStorage" ] ; then
    echo "The Critical storage usage percentage  must be higher than the warning storage usage percentage"
    echo "Warning: $warningStorage"
    echo "Critical: $criticalStorage"
    echo ""
    echo "For more information:  ./${0##*/} --help"
    exit 1
fi

if [ "$hostname" = "" ] || ([ "$SNMPVersion" = "3" ] && [ "$SNMPUser" = "" ]) || ([ "$SNMPVersion" = "3" ] && [ "$SNMPPassword" = "" ]) ; then
    usage
else
    if [ "$SNMPVersion" = "2" ] ; then
	SNMPArgs=" -OQne -v 2c -c $SNMPV2Community -t $SNMPTimeout"
    else
	SNMPArgs=" -OQne -v 3 -u $SNMPUser -A $SNMPPassword -l authNoPriv -a MD5 -t $SNMPTimeout"
	if [ ${#SNMPPassword} -lt "8" ] ; then
	    echo "snmpwalk:  (The supplied password is too short.)"
	    exit 1
	fi
    fi
    tmpRequest=`$SNMPWALK $SNMPArgs $hostname $OID_syno 2> /dev/null`
    if [ "$?" != "0" ] ; then
        echo "CRITICAL - Problem with SNMP request, check user/password/host"
        exit 2
    fi

    if [ "$checkType" = "disk" ] ; then
        nbDisk=$(echo "$tmpRequest" | grep $OID_diskID | wc -l)
        for i in `seq 1 $nbDisk`;
        do
            if [ $i -lt 25 ] ; then
                OID_disk="$OID_disk $OID_diskID.$(($i-1)) $OID_diskModel.$(($i-1)) $OID_diskStatus.$(($i-1)) $OID_diskTemp.$(($i-1)) "
            else
                OID_disk2="$OID_disk2 $OID_diskID.$(($i-1)) $OID_diskModel.$(($i-1)) $OID_diskStatus.$(($i-1)) $OID_diskTemp.$(($i-1)) "
            fi
        done
    fi

    if [ "$checkType" = "raid" ] ; then
        nbRAID=$(echo "$tmpRequest" | grep $OID_RAIDName | wc -l)
        for i in `seq 1 $nbRAID`;
        do
            OID_RAID="$OID_RAID $OID_RAIDName.$(($i-1)) $OID_RAIDStatus.$(($i-1))"
        done
    fi

    OIDVars=(${checks[@]/#/OID_})
    OIDs=()
    for var in "${OIDVars[@]}";
    do
        OIDs+=(${!var})
    done

    syno=`$SNMPGET $SNMPArgs $hostname ${OIDs[@]} 2> /dev/null`

    if [ "$OID_disk2" != "" ]; then
        syno2=`$SNMPGET $SNMPArgs $hostname $OID_disk2 2> /dev/null`
        syno=$(echo "$syno";echo "$syno2";)
    fi

    model=$(echo "$syno" | grep $OID_model | cut -d "=" -f2)
    serialNumber=$(echo "$syno" | grep $OID_serialNumber | cut -d "=" -f2)
    DSMVersion=$(echo "$syno" | grep $OID_DSMVersion | cut -d "=" -f2)

    healthString="Synology"

    DSMUpgradeAvailable=$(echo "$syno" | grep $OID_DSMUpgradeAvailable | cut -d "=" -f2 | sed 's/^[ \t]*//;s/[ \t]*$//')
    case $DSMUpgradeAvailable in
	"1")	DSMUpgradeAvailable="Available";	if [ "$checkDSMUpdate" = "yes" ]; then healthWarningStatus=1;		healthString="$healthString, DSM update available"; fi ;;
	"2")	DSMUpgradeAvailable="Unavailable";;
	"3")	DSMUpgradeAvailable="Connecting";;
	"4")	DSMUpgradeAvailable="Disconnected";	if [ "$checkDSMUpdate" = "yes" ]; then healthWarningStatus=1;		healthString="$healthString, DSM Update Disconnected"; fi ;;
	"5")	DSMUpgradeAvailable="Others";		if [ "$checkDSMUpdate" = "yes" ]; then healthWarningStatus=1;		healthString="$healthString, Check DSM Update"; fi ;;
    esac

    RAIDName=$(echo "$syno" | grep $OID_RAIDName | cut -d "=" -f2)
    RAIDStatus=$(echo "$syno" | grep $OID_RAIDStatus | cut -d "=" -f2 | sed 's/^[ \t]*//;s/[ \t]*$//')
    systemStatus=$(echo "$syno" | grep $OID_systemStatus | cut -d "=" -f2 | sed 's/^[ \t]*//;s/[ \t]*$//')
    temperature=$(echo "$syno" | grep $OID_temperature | cut -d "=" -f2 | sed 's/^[ \t]*//;s/[ \t]*$//')
    powerStatus=$(echo "$syno" | grep $OID_powerStatus | cut -d "=" -f2 | sed 's/^[ \t]*//;s/[ \t]*$//')
    systemFanStatus=$(echo "$syno" | grep $OID_systemFanStatus | cut -d "=" -f2 | sed 's/^[ \t]*//;s/[ \t]*$//')
    CPUFanStatus=$(echo "$syno" | grep $OID_CPUFanStatus | cut -d "=" -f2 | sed 's/^[ \t]*//;s/[ \t]*$//')


    if [ "$checkType" = "system" ] ; then
        #Check system status
        if [ "$systemStatus" = "1" ] ; then
            systemStatus="Normal"
        else
            systemStatus="Failed"
            healthCriticalStatus=1
            healthString="$healthString, System status: $systemStatus "
        fi
    fi

    if [ "$checkType" = "temperature" ] ; then
        #Check system temperature
        if [ "$temperature" -gt "$warningTemperature" ] ; then
            if [ "$temperature" -gt "$criticalTemperature" ] ; then
                    temperature="$temperature (CRITICAL)"
                    healthCriticalStatus=1
                    healthString="$healthString, temperature: $temperature "
            else
                    temperature="$temperature (WARNING)"
                    healthWarningStatus=1
                    healthString="$healthString, temperature: $temperature "
            fi
        else
            temperature="$temperature (Normal)"
        fi
    fi


    if [ "$checkType" = "power" ] ; then
    #Check power status
        if [ "$powerStatus" = "1" ] ; then
            powerStatus="Normal"
        else
            powerStatus="Failed";
            healthCriticalStatus=1
            healthString="$healthString, Power status: $powerStatus "
        fi
    fi


    if [ "$checkType" = "fan" ] ; then
        #Check system fan status
        if [ "$systemFanStatus" = "1" ] ; then
            systemFanStatus="Normal"
        else
            systemFanStatus="Failed";
            healthCriticalStatus=1
            healthString="$healthString, System fan status: $systemFanStatus "
        fi


        #Check CPU fan status
        if [ "$CPUFanStatus" = "1" ] ; then
            CPUFanStatus="Normal"
        else
            CPUFanStatus="Failed";
            healthCriticalStatus=1
            healthString="$healthString, CPU fan status: $CPUFanStatus "
        fi
    fi


    #Check all disk status
    if [ "$checkType" = "disk" ] ; then
        for i in `seq 1 $nbDisk`;
        do
            diskID[$i]=$(echo "$syno" | grep "$OID_diskID.$(($i-1)) " | cut -d "=" -f2)
            diskModel[$i]=$(echo "$syno" | grep "$OID_diskModel.$(($i-1)) " | cut -d "=" -f2 )
            diskStatus[$i]=$(echo "$syno" | grep "$OID_diskStatus.$(($i-1)) " | cut -d "=" -f2 | sed 's/^[ \t]*//;s/[ \t]*$//')
            diskTemp[$i]=$(echo "$syno" | grep "$OID_diskTemp.$(($i-1)) " | cut -d "=" -f2 | sed 's/^[ \t]*//;s/[ \t]*$//')

            case ${diskStatus[$i]} in
                    "1")	diskStatus[$i]="Normal";		;;
                    "2")	diskStatus[$i]="Initialized";		;;
                    "3")	diskStatus[$i]="NotInitialized";	;;
                    "4")	diskStatus[$i]="SystemPartitionFailed";	healthCriticalStatus=1; healthString="$healthString, problem with ${diskID[$i]} (model:${diskModel[$i]}) status:${diskStatus[$i]} temperature:${diskTemp[$i]}";;
                    "5")	diskStatus[$i]="Crashed";		healthCriticalStatus=1;	healthString="$healthString, problem with ${diskID[$i]} (model:${diskModel[$i]}) status:${diskStatus[$i]} temperature:${diskTemp[$i]}";;
            esac

            if [ "${diskTemp[$i]}" -gt "$warningTemperature" ] ; then
                if [ "${diskTemp[$i]}" -gt "$criticalTemperature" ] ; then
                    diskTemp[$i]="${diskTemp[$i]} (CRITICAL)"
                    healthCriticalStatus=1;
                    healthString="$healthString, ${diskID[$i]} temperature: ${diskTemp[$i]}"
                else
                    diskTemp[$i]="${diskTemp[$i]} (WARNING)"
                    healthWarningStatus=1;
                    healthString="$healthString, ${diskID[$i]} temperature: ${diskTemp[$i]}"
                fi
            fi
       done
    fi


    #Check all RAID volume status
    if [ "$checkType" = "raid" ] ; then
        syno_diskspace=`$SNMPWALK $SNMPArgs $hostname $OID_Storage 2> /dev/null`
        for i in `seq 1 $nbRAID`;
        do
            RAIDName[$i]=$(echo "$syno" | grep $OID_RAIDName.$(($i-1)) | cut -d "=" -f2)
            RAIDStatus[$i]=$(echo "$syno" | grep $OID_RAIDStatus.$(($i-1)) | cut -d "=" -f2 | sed 's/^[ \t]*//;s/[ \t]*$//')

            storageName[$i]=$(echo "${RAIDName[$i]}" | sed -e 's/[[:blank:]]//g' | sed -e 's/\"//g' | sed 's/.*/&/')

            # modified by Tobias Schenke
            # "timebackup" (when backup-job runs) and the "docker-feature" (since dsm 6.0, and if installed) mount volumes as a substructure of /"volume1/..." or "/.../volume1/..."
            # in this case the former grep failed with more then one result.
            # modified script to look for a line with '= "/volume1"' instead of 'volume1'
            #storageID[$i]=$(echo "$syno_diskspace" | grep ${storageName[$i]} | cut -d "=" -f1 | rev | cut -d "." -f1 | rev)
            storageID[$i]=$(echo "$syno_diskspace" | grep -i "= \"\?/${storageName[$i]}\"\?" | cut -d "=" -f1 | rev | cut -d "." -f1 | rev)

            if [ "${storageID[$i]}" != "" ] ; then
                storageSize[$i]=$(echo "$syno_diskspace" | grep "$OID_StorageSize.${storageID[$i]}" | cut -d "=" -f2 )
                storageSizeUsed[$i]=$(echo "$syno_diskspace" | grep "$OID_StorageSizeUsed.${storageID[$i]}" | cut -d "=" -f2 )
                storageAllocationUnits[$i]=$(echo "$syno_diskspace" | grep "$OID_StorageAllocationUnits.${storageID[$i]}" | cut -d "=" -f2 )
                storagePercentUsed[$i]=$((${storageSizeUsed[$i]} * 100 / ${storageSize[$i]}))
                storagePercentUsedString[$i]="${storagePercentUsed[$i]}% used"

                if [ "${storagePercentUsed[$i]}" -gt "$warningStorage" ] ; then
                    if [ "${storagePercentUsed[$i]}" -gt "$criticalStorage" ] ; then
                        healthCriticalStatus=1;
                        storagePercentUsedString[$i]="${storagePercentUsedString[$i]} CRITICAL"
                        healthString="$healthString,${RAIDName[$i]}: ${storagePercentUsedString[$i]}"
                    else
                        healthWarningStatus=1;
                        storagePercentUsedString[$i]="${storagePercentUsedString[$i]} WARNING"
                        healthString="$healthString,${RAIDName[$i]}: ${storagePercentUsedString[$i]}"
                    fi
                fi
            fi

            case ${RAIDStatus[$i]} in
                    "1")	RAIDStatus[$i]="Normal";		;;
                    "2")	RAIDStatus[$i]="Repairing";		healthWarningStatus=1;		healthString="$healthString, RAID status (${RAIDName[$i]}): ${RAIDStatus[$i]} ";;
                    "3")	RAIDStatus[$i]="Migrating";		healthWarningStatus=1;		healthString="$healthString, RAID status (${RAIDName[$i]}): ${RAIDStatus[$i]} ";;
                    "4")	RAIDStatus[$i]="Expanding";		healthWarningStatus=1;		healthString="$healthString, RAID status (${RAIDName[$i]}): ${RAIDStatus[$i]} ";;
                    "5")	RAIDStatus[$i]="Deleting";		healthWarningStatus=1;		healthString="$healthString, RAID status (${RAIDName[$i]}): ${RAIDStatus[$i]} ";;
                    "6")	RAIDStatus[$i]="Creating";		healthWarningStatus=1;		healthString="$healthString, RAID status (${RAIDName[$i]}): ${RAIDStatus[$i]} ";;
                    "7")	RAIDStatus[$i]="RaidSyncing";		healthWarningStatus=1;		healthString="$healthString, RAID status (${RAIDName[$i]}): ${RAIDStatus[$i]} ";;
                    "8")	RAIDStatus[$i]="RaidParityChecking";	healthWarningStatus=1;		healthString="$healthString, RAID status (${RAIDName[$i]}): ${RAIDStatus[$i]} ";;
                    "9")	RAIDStatus[$i]="RaidAssembling";	healthWarningStatus=1;		healthString="$healthString, RAID status (${RAIDName[$i]}): ${RAIDStatus[$i]} ";;
                    "10")	RAIDStatus[$i]="Canceling";		healthWarningStatus=1;		healthString="$healthString, RAID status (${RAIDName[$i]}): ${RAIDStatus[$i]} ";;
                    "11")	RAIDStatus[$i]="Degrade";		healthCriticalStatus=1;		healthString="$healthString, RAID status (${RAIDName[$i]}): ${RAIDStatus[$i]} ";;
                    "12")	RAIDStatus[$i]="Crashed";		healthCriticalStatus=1;		healthString="$healthString, RAID status (${RAIDName[$i]}): ${RAIDStatus[$i]} ";;
            esac
        done
    fi

    case $checkType in
        "system")         echo "Synology model:		$model" ;
                          echo "Synology s/n:		$serialNumber" ;
                          echo "System Status:		 $systemStatus" ;;
        "version")        echo "DSM Version:		$DSMVersion" ;
                          echo "DSM update:		 $DSMUpgradeAvailable" ;;
        "temperature")    echo "Temperature:		 $temperature" ;;
        "power")	  echo "Power Status:		 $powerStatus" ;;
        "fan")            echo "System Fan Status:	 $systemFanStatus" ;
                          echo "CPU Fan Status:		 $CPUFanStatus" ;;
        "disk")           echo "Number of disks:         $nbDisk" ;
                          for i in `seq 1 $nbDisk`;
                          do
                              echo " ${diskID[$i]} (model:${diskModel[$i]}) status:${diskStatus[$i]} temperature:${diskTemp[$i]}" ;
                              done ;;
        "raid")           echo "Number of RAID volume:   $nbRAID" ;
                          for i in `seq 1 $nbRAID`;
                          do
                              echo " ${RAIDName[$i]} status:${RAIDStatus[$i]} ${storagePercentUsedString[$i]}" ;
                          done ;;
    esac

	# Display UPS information
        if [ "$checkType" = "ups" ] ; then
            upsModel=$(echo "$syno" | grep $OID_UpsModel | cut -d "=" -f2)
            upsSN=$(echo "$syno" | grep $OID_UpsSN | cut -d "=" -f2)
            upsStatus=$(echo "$syno" | grep $OID_UpsStatus | cut -d "=" -f2)
            upsLoad=$(echo "$syno" | grep $OID_UpsLoad | cut -d "=" -f2)
            upsBatteryCharge=$(echo "$syno" | grep $OID_UpsBatteryCharge | cut -d "=" -f2)
            upsBatteryChargeWarning=$(echo "$syno" | grep $OID_UpsBatteryChargeWarning | cut -d "=" -f2)

	    echo "UPS:"
	    echo "  Model:		$upsModel"
	    echo "  s/n:			$upsSN"
	    echo "  Status:		$upsStatus"
	    echo "  Load:			$upsLoad"
	    echo "  Battery charge:	$upsBatteryCharge"
	    echo "  Battery charge warning:$upsBatteryChargeWarning"
	fi
	echo "";

    if [ "$healthCriticalStatus" = "1" ] ; then
	echo "CRITICAL - $healthString"
	exit 2
    fi
    if [ "$healthWarningStatus" = "1" ] ; then
	echo "WARNING - $healthString"
	exit 1
    fi
    if [ "$healthCriticalStatus" = "0" ] && [ "$healthWarningStatus" = "0" ] ; then
	echo "OK - $healthString is in good health"
	exit 0
    fi
fi
