#!/bin/bash
# check_snmp_synology for nagios version 1.2
# 30.04.2013        Nicolas Ordonez, Switzerland
# 08.08.2020        Axel Hahn: add update, community string
# 03.05.2023  v1.2  ah  support Snmpv3 connections
#---------------------------------------------------
# this plugin check the health of your Synology NAS
# - System status (Power, Fans)
# - Disks status 
# - RAID status
# - available updates
#
# Tested with DSM 6.2 + 6.4 + 7.1
#---------------------------------------------------
#
# ah = axel.hahn@unibe.ch
#
# see docs:
# https://global.download.synology.com/download/Document/Software/DeveloperGuide/Firmware/DSM/All/enu/Synology_DiskStation_MIB_Guide.pdf
#---------------------------------------------------

. $(dirname $0)/inc_pluginfunctions

# --- basic vars

self_APPNAME=$( basename $0 | tr [:lower:] [:upper:] )
self_APPVERSION=1.2

SNMPCOMMUNITY="public"
SNMPVERSION="2c"
SNMPWALK=$(which snmpwalk)
SNMPGET=$(which snmpget)

SNMPCONFIG=/etc/icingaclient/snmp.cfg

HOSTNAME="localhost"
option_found=0
healthString=""
verbose="no"
out=""

# --- OID declarations
OID_syno="1.3.6.1.4.1.6574"
OID_model="${OID_syno}.1.5.1.0"
OID_serialNumber="${OID_syno}.1.5.2.0"
OID_DSMVersion="${OID_syno}.1.5.3.0"
OID_DSMUpdateAvailable="${OID_syno}.1.5.4.0"
OID_systemStatus="${OID_syno}.1.1.0"
OID_powerStatus="${OID_syno}.1.3.0"
OID_systemFanStatus="${OID_syno}.1.4.1.0"
OID_CPUFanStatus="${OID_syno}.1.4.2.0"
OID_temp="${OID_syno}.1.2.0"

OID_disk=""
OID_diskID="${OID_syno}.2.1.1.2"
OID_diskModel="${OID_syno}.2.1.1.3"
OID_diskStatus="${OID_syno}.2.1.1.5"
OID_diskTemp="${OID_syno}.2.1.1.6"

OID_RAID=""
OID_RAIDName="${OID_syno}.3.1.1.2"
OID_RAIDStatus="${OID_syno}.3.1.1.3"

OID_RAIDFree="${OID_syno}.3.1.1.4"
OID_RAIDSize="${OID_syno}.3.1.1.5"


# --- status arrays to show results
#                0     1          2             3               4                      5
aStatusUpgrade=( "???" Yes        "Up to date"  Connecting      Disconnected           Others)
aStatusDisk=(    "???" Normal     Initialized   NotInitialized  SystemPartitionFailed  Crashed)
aStatusRaid=(    "???" Normal     Repairing     Migrating       Expanding              Deleting Creating RaidSyncing RaidParityChecking RaidAssembling Canceling Degrade Crashed DataScrubbing RaidDeploying RaidUnDeploying RaidMountCache RaidExpandingUnfinishedSHR RaidConvertSHRToPool RaidMigrateSHR1ToSHR2 RaidUnknownStatus)

_self=$( basename $0 )
USAGE="
______________________________________________________________________

$self_APPNAME 
v$self_APPVERSION

Based on script of Nicolas Ordonez.

Institute for Medical Education - University of Bern
Licence: GNU GPL 3
______________________________________________________________________

check cpu usage and cpu wait
Cpu infos are taken from output of top command.

On higher cpu usage it can show processes that cause cpu waits and
with most cpu consumption.

SYNTAX:
    $_self [options]

OPTIONS:
    -a   authentication params for snmpwalk/ snmpget to connect to target; 
         default: \"-v2c -c public\" (Snmpv2 with community string \"public\")
    -h   host to connect as hostname or ip address
    -f FILE  
         read authentication from config file
         default: \"/etc/icingaclient/snmp.cfg\"
    -v   detailed output

CONFIG FILE:
    The config file can be multiline and has the syntax
    [hostname]:[auth parameters]
    The auth parameters set the version and all needed values to connect.
    Snmp v2 uses a community string.
    Snmp v3 is highly recommended (you should disable Snmp v2) and needs
    a user and password.

    Example:
    server-01.example.com:-v 3 -l authnoPriv -a SHA -u snmpmonitor -A password-for-server-01
    server-02.example.com:-v 3 -l authnoPriv -a SHA -u snmpmonitor -A password-for-server-02

EXAMPLE:

    $_self -h server-01.example.com -v
        Show Synology status of server-01 using connect data from /etc/icingaclient/snmp.cfg

    $_self -h server-01.example.com -v -f /opt/somewhere/snmp.conf
        Show Synology status of server-01 using connect data from custom config
"

#---------------------------------------------------
# FUNCTIONS
#---------------------------------------------------

# --- write verbose text
_wd()
{
    if [ "$verbose" = "yes" ] ; then 
        out="${out}$*
" ; fi
}

# --- get a value from SNMP output data
# param  string  mib string
_get(){
    echo "$syno" | grep "${1} " | cut -d "=" -f2 | cut -f 2- -d " "
}

# --- show usage
usage()
{
        ph.abort "$USAGE"
}

read_config(){
    SNMPAUTH="-v $SNMPVERSION -c $SNMPCOMMUNITY"

    if [ -r "$SNMPCONFIG" ]; then
        if grep "^${HOSTNAME}:" "$SNMPCONFIG" >/dev/null; then
            SNMPAUTH="$( grep "^${HOSTNAME}:" "$SNMPCONFIG" | cut -f 2- -d ':' )"
        else
            SNMPAUTH="$( grep "^DEFAULT:" "$SNMPCONFIG" | cut -f 2- -d ':' )"
        fi
    else
        ph.setStatus "unknown"
        echo "ERROR: unable to read config file [$SNMPCONFIG]."
        ph.exit
    fi
}

#---------------------------------------------------
# MAIN
#---------------------------------------------------

while getopts a:h:f:v OPTNAME; do
    case "$OPTNAME" in
        a)
            SNMPAUTH="$OPTARG"
            ;;
        f)
            SNMPCONFIG="$OPTARG"
            ;;
        h)
            HOSTNAME="$OPTARG"
            option_found=1
            ;;
        v)
            verbose="yes"
            ;;
        *)
            usage
            ;;
    esac
done


# --- read config to get the authentication params for snmp commands

test -z "$SNMPAUTH" && read_config

# --- read raid and disks to get its single OIDs
nbDisk=$($SNMPWALK -OQne -t 10 ${SNMPAUTH} $HOSTNAME $OID_diskID   2> /dev/null | wc -l )
nbRAID=$($SNMPWALK -OQne -t 10 ${SNMPAUTH} $HOSTNAME $OID_RAIDName 2> /dev/null | wc -l)

for i in $(seq 1 $nbDisk);
do
    OID_disk="$OID_disk $OID_diskID.$(($i-1)) $OID_diskModel.$(($i-1)) $OID_diskStatus.$(($i-1)) $OID_diskTemp.$(($i-1)) " 
done
for i in $(seq 1 $nbRAID);
do
    OID_RAID="$OID_RAID $OID_RAIDName.$(($i-1)) $OID_RAIDStatus.$(($i-1)) $OID_RAIDSize.$(($i-1)) $OID_RAIDFree.$(($i-1))" 
done

# --- SNPGET to all wanted oids
syno=$($SNMPGET -OQne -t 10 ${SNMPAUTH} $HOSTNAME $OID_model $OID_serialNumber $OID_DSMVersion $OID_DSMUpdateAvailable $OID_systemStatus $OID_powerStatus $OID_systemFanStatus $OID_CPUFanStatus $OID_temp $OID_disk $OID_RAID 2> /dev/null | sed 's/^[ \t]*//;s/[ \t]*$//')

if [ "$?" != "0" ] ; then
    ph.abort "Problem with SNMP request"
fi

_wd ""

# --- check fetched data
model=$(_get $OID_model)
_wd "Synology model:    $model"

serialNumber=$(_get $OID_serialNumber)
_wd "Synology s/n:      $serialNumber"

DSMVersion=$(_get $OID_DSMVersion)
_wd "DSM Version:       $DSMVersion"

healthString="Synology $model (s/n:$serialNumber, $DSMVersion)"

RAIDName=$(_get $OID_RAIDName)
RAIDStatus=$(_get $OID_RAIDStatus)


# --- check update
typeset -i DSMupdate=$(_get $OID_DSMUpdateAvailable)
_wd "Update available:  ${aStatusUpgrade[$DSMupdate]} ($DSMupdate)"
if [ $DSMupdate -eq 1 ] ; then
    ph.setStatus "warning"
    healthString="$healthString, Update available"
fi
_wd ""


# --- Check system status
systemStatus="$(_get $OID_systemStatus)"

if [ "$systemStatus" != "1" ] ; then
    if [ "$systemStatus" = "2" ] ; then      systemStatus="Failed";          fi
    ph.setStatus "critical"
    healthString="$healthString, System status: $systemStatus "
else
    systemStatus="Normal"
fi
_wd "System Status:     $systemStatus"


# --- Check power status
powerStatus="$(_get $OID_powerStatus)"

if [ "$powerStatus" != "1" ] ; then
    if [ "$powerStatus" = "2" ] ; then       powerStatus="Failed";           fi
    ph.setStatus "critical"
    healthString="$healthString, Power status: $powerStatus "
else
    powerStatus="Normal"
fi
_wd "Power Status:      $powerStatus"


# --- Check system fan status
systemFanStatus=$(_get $OID_systemFanStatus)
if [ "$systemFanStatus" != "1" ] ; then
    if [ "$systemFanStatus" = "2" ] ; then   systemFanStatus="Failed";               fi
    ph.setStatus "critical"
    healthString="$healthString, System fan status: $systemFanStatus "
else
    systemFanStatus="Normal"
fi
_wd "System Fan Status: $systemFanStatus"


# --- Check CPU fan status
CPUFanStatus=$(_get $OID_CPUFanStatus)
if [ "$CPUFanStatus" != "1" ] ; then
    if [ "$CPUFanStatus" = "2" ] ; then      CPUFanStatus="Failed";          fi
    ph.setStatus "critical"
    healthString="$healthString, CPU fan status: $CPUFanStatus "
else
    CPUFanStatus="Normal"
fi
_wd "CPU Fan Status:    $CPUFanStatus"


# --- Show temperature
DeviceTemperature=$(_get $OID_temp)
# if [ $DeviceTemperature ...  ] ; then
#     ph.setStatus "critical"
#     healthString="$healthString, CPU fan status: $CPUFanStatus °C"
# else
#     CPUFanStatus="Normal"
# fi
_wd "NAS temperature:   $DeviceTemperature °C"


# --- Check all disk status
_wd ""
_wd "Number of disks:   $nbDisk"
for i in $(seq 1 $nbDisk);
do
    diskID[$i]=$(_get $OID_diskID.$(($i-1)))
    diskModel[$i]=$(_get $OID_diskModel.$(($i-1)))
    diskTemp[$i]=$(_get $OID_diskTemp.$(($i-1)))
    idiskStatus=$(_get $OID_diskStatus.$(($i-1)))

    diskStatus[$i]=${aStatusDisk[$idiskStatus]}
   
    if [ ${idiskStatus} != "1" ] ; then
        ph.setStatus "critical"
        healthString="$healthString, problem with ${diskID[$i]} (model:${diskModel[$i]}) status:${diskStatus[$i]} temperature:${diskTemp[$i]} °C"
    fi

    _wd "${diskID[$i]} (model:${diskModel[$i]}) status: ${diskStatus[$i]} ($idiskStatus) temperature: ${diskTemp[$i]} °C"
done


# --- Check all RAID volume status
_wd ""
_wd "Number of RAID volume: $nbRAID"
for i in $(seq 1 $nbRAID);
do
    RAIDName[$i]=$(_get $OID_RAIDName.$(($i-1)))
    iRAIDStatus=$(_get $OID_RAIDStatus.$(($i-1)))

    # size in integer GB
    iRAIDSize=$(( $(_get $OID_RAIDSize.$(($i-1))) / 1024/1024/1024 ))
    iRAIDFree=$(( $(_get $OID_RAIDFree.$(($i-1))) / 1024/1024/1024 ))
    iFree=$(( iRAIDFree*100/$iRAIDSize ))

    RAIDStatus[$i]=${aStatusRaid[${iRAIDStatus}]}

    if [ $iRAIDStatus != "1" ] ; then
        ph.setStatus "critical"
        healthString="$healthString, RAID status: ($RAIDName ): $RAIDStatus[$i] "
    fi
    _wd "${RAIDName[$i]} status: ${RAIDStatus[$i]} ($iRAIDStatus) - size $iRAIDSize GB, free $iRAIDFree GB (${iFree}%)"
done

# _wd ""; _wd "DEBUG: SNMPAUTH= $( echo "$SNMPAUTH" | sed 's#\-A [^\ ]*#-A **********#g' )"

# --- output status
ph.status "$healthString"
echo "$out"

ph.exit