From 8afdb5e31132beed3c0a9b5641314c33c16d3a7c Mon Sep 17 00:00:00 2001 From: Frederik Lindenaar <frederik@lindenaar.nl> Date: Sat, 6 Jan 2018 20:06:27 +0100 Subject: [PATCH] added Raspberry Pi CPU temperature reading --- README.md | 49 ++++++++++++++++++++++++++++++++++++------------- plugins/check_temperature | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------------------------------------------------------------- 2 files changed, 140 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index 28e9028..c294809 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ This repository contains the following scripts: * [check_otp](#check_otp) plugin to monitor PrivacyIDEA (and LinOTP) OTP validation * [check_temperature](#check_temperature) - plugin to monitor the temperature of a 1-wire sensor on a RaspberryPi + plugin to monitor the CPU temperature of a RaspberryPi or that of a DS18b20 1-wire sensor on a RaspberryPi * [nagiosstatus](#nagiosstatus) CGI-BIN script to report the status of nagios (to monitor nagios itself) @@ -237,15 +237,16 @@ define service { <a name=check_temperature>plugins/check_temperature</a> ------------------------------------------------------- -Plugin (check) to monitor monitor the temperature using a sensor connected to a -RaspberryPi. This implementation is specifically for the DS18B20 1-wire -temperature sensor. Other methods and interfaces can be plugged in easily (just -raise a request or provide a patch). For information on how to connect sensor -to the RaspberryPi and to get it working please see [this Adafruit tutorial]( +Plugin (check) to monitor monitor the Raspberry Pi CPU temperature or that of a +temperature sensor connected to a RaspberryPi. This implementation currently +supports the DS18B20 1-wire temperature sensor. Other methods and interfaces can +be plugged in easily (just raise a request or provide a patch). For information +on how to connect sensor to the RaspberryPi and to get it working please see +[this Adafruit tutorial]( https://learn.adafruit.com/adafruits-raspberry-pi-lesson-11-ds18b20-temperature-sensing). -To enable the 1-wire interface support on the RaspberryPi one can use the -command: +No setup is required to read the CPU temperature. To enable the 1-wire interface +support on the RaspberryPi one can use the command: ~~~ sudo raspi-config nonint do_onewire 0 ~~~ @@ -256,32 +257,46 @@ Installation for is straightforward, after installing the script on the server add the following to your Nagios `commands.cmd` configuration file: ~~~ +# 'check_cpu_temperature' command definition to monitor CPU temperature in C +# parameters: warning (ARG1) and critical (ARG2) temperature in Celcius +define command { + command_name check_temperature + command_line [install_path]/plugins/check_temperature -w $ARG1$ -c $ARG2$ rpi_cpu +} + +# 'check_cpu_ftemperature' command definition to monitor CPU temperature in F +# parameters: warning (ARG1) and critical (ARG2) temperature in Celcius +define command { + command_name check_temperature + command_line [install_path]/plugins/check_temperature -F -w $ARG1$ -c $ARG2$ rpi_cpu +} + # 'check_temperature' command definition to monitor a single temperature in C # parameters: warning (ARG1) and critical (ARG2) temperature in Celcius define command { command_name check_temperature - command_line [install_path]/plugins/check_temperature -w $ARG1$ -c $ARG2$ + command_line [install_path]/plugins/check_temperature -w $ARG1$ -c $ARG2$ w1_ds18b20 } # 'check_ftemperature' command definition to monitor a single temperature in F # parameters: warning (ARG1) and critical (ARG2) temperature in Farenheit define command { command_name check_ftemperature - command_line [install_path]/plugins/check_temperature -F -w $ARG1$ -c $ARG2$ + command_line [install_path]/plugins/check_temperature -F -w $ARG1$ -c $ARG2$ w1_ds18b20 } # 'check_temperature_sensor' command definition to monitor a single temperature in C # parameters: sensor serial (ARG1), warning (ARG2) and critical (ARG3) temperature in Celcius define command { command_name check_temperature_sensor - command_line [install_path]/plugins/check_temperature -s $ARG1$ -w $ARG2$ -c $ARG3$ + command_line [install_path]/plugins/check_temperature -w $ARG2$ -c $ARG3$ w1_ds18b20 -s $ARG1$ } # 'check_ftemperature_sensor' command definition to monitor a single temperature in F # parameters: sensor serial (ARG1), warning (ARG2) and critical (ARG3) temperature in Farenheit define command { command_name check_ftemperature_sensor - command_line [install_path]/plugins/check_temperature -F -s $ARG1$ -w $ARG2$ -c $ARG3$ + command_line [install_path]/plugins/check_temperature -F -w $ARG2$ -c $ARG3$ w1_ds18b20 -s $ARG1$ } ~~~ @@ -290,6 +305,14 @@ Make sure to replace `[install_path]/plugins` with the location of the script. To use the it define a service check like below: ~~~ +# check RaspberryPi CPU temperature in Celcius +define service { + host hostname.mydomain.tld + service_description CPU Temperature + check_command check_cpu_temperature!55!75 + use generic-service +} + # check temperature in Celcius using a DS18B20 sensor connected to a RaspberryPi define service { host hostname.mydomain.tld @@ -298,7 +321,6 @@ define service { use generic-service } - # check temperature with DS18B20 sensor 0000a31ea3de connected to a RaspberryPi define service { host hostname.mydomain.tld @@ -340,3 +362,4 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, download it from <http://www.gnu.org/licenses/>. + diff --git a/plugins/check_temperature b/plugins/check_temperature index 493de8c..34a106f 100755 --- a/plugins/check_temperature +++ b/plugins/check_temperature @@ -2,10 +2,10 @@ # # check_temperature - Nagios temperature check for DS18B20 sensor on RaspberryPi # -# Version 1.0, latest version, documentation and bugtracker available at: +# Version 1.1, latest version, documentation and bugtracker available at: # https://gitlab.lindenaar.net/scripts/nagios-plugins # -# Copyright (c) 2016 Frederik Lindenaar +# Copyright (c) 2017 Frederik Lindenaar # # This script is free software: you can redistribute and/or modify it under the # terms of version 3 of the GNU General Public License as published by the Free @@ -27,14 +27,15 @@ from argparse import ArgumentParser as StandardArgumentParser, FileType, \ import logging # Constants (no need to change but allows for easy customization) -VERSION="1.0" +VERSION="1.1" PROG_NAME=splitext(basename(__file__))[0] PROG_VERSION=PROG_NAME + ' ' + VERSION SENSOR_SCALE=1000 -SENSOR_DEV_DIR = '/sys/bus/w1/devices/' -SENSOR_DEV_PREFIX = '28-' -SENSOR_DEV_SUFFIX = '/w1_slave' -SENSOR_READ_RETRIES=10 +W1_SENSOR_DEV_DIR = '/sys/bus/w1/devices/' +W1_SENSOR_DEV_PREFIX = '28-' +W1_SENSOR_DEV_SUFFIX = '/w1_slave' +W1_SENSOR_READ_RETRIES=10 +CPU_SENSOR_DEV = '/sys/class/thermal/thermal_zone0/temp' LOG_FORMAT='%(levelname)s - %(message)s' LOG_FORMAT_FILE='%(asctime)s - ' + LOG_FORMAT @@ -97,64 +98,21 @@ def isempty(string): return string is None or len(string) == 0 -def parse_args(): - """Parse command line and get parameters from environment, if present""" - - # Setup argument parser, the workhorse gluing it all together - parser = ArgumentParser( - epilog='(*) by default the script will look for the first device that ' - 'matches %s* in %s, if multiple entries are found -s or -f must ' - 'be used to specify which sensor to read.' % - (SENSOR_DEV_PREFIX, SENSOR_DEV_DIR), - description='Nagios check plugin for 1-wire temp. sensor on RaspberryPi' - ) - parser.add_argument('-V', '--version',action="version",version=PROG_VERSION) - - pgroup = parser.add_mutually_exclusive_group(required=False) - pgroup.add_argument('-C', '--celcius', action='store_const', - dest='converter', const=CONVERT_CELCIUS, - help='measure, critical and warn values in Celcius ' - '(default)', default=CONVERT_CELCIUS) - pgroup.add_argument('-F', '--farenheit',action='store_const', - dest='converter', const=CONVERT_FARENHEIT, - help='measure, critical and warn values in Farenheit') - - parser.add_argument('-w', '--warn', type=float, - help='temperature for warning status') - parser.add_argument('-c','--critical', type=float, - help='temperature for critical status') - - parser.add_argument('-r', '--retries', type=int,default=SENSOR_READ_RETRIES, - help='number of times to retry reading sensor data when' - ' unstable (defaults to %d)' % SENSOR_READ_RETRIES) - - pgroup = parser.add_mutually_exclusive_group(required=False) - pgroup.add_argument('-s', '--serial', - help='(unique part of) temperature sensor serial (*)') - pgroup.add_argument('-f', '--file', - help='input file (or device) to obtain data from (*)') - - pgroup = parser.add_mutually_exclusive_group(required=False) - pgroup.add_argument('-q', '--quiet', default=logging.CRITICAL, - action=SetLogLevel, const=LOGGING_NONE, - help='quiet (no output, only exit with exit code)') - pgroup.add_argument('-v', '--verbose', help='more verbose output', - action=SetLogLevel, const=logging.INFO) - pgroup.add_argument('-d', '--debug', help='debug output (more verbose)', - action=SetLogLevel, const=logging.DEBUG) - - parser.add_argument('-l', '--logfile', action=SetLogFile, - help='send logging output to logfile') - - # parse arguments and post-process command line options - args = parser.parse_args() +def read_rpi_cpu_temp(args): + """Reads CPU temperature and converts it to desired unit, returns temperature""" + with open(args.file, 'r') as f: + lines = f.readlines() + logger.debug('Temperature sensor data read from %s: %s', f.name, lines) - # if we got here all seems OK - return args + temp_read = int(lines[0]) + temp = args.converter[0](temp_read) + logger.debug('Temperature sensor value %d is %.2f%s', temp_read, + temp, args.converter[1]) + return temp, 1 -def get_sensor_device_filename(args, dev_dir=SENSOR_DEV_DIR, - prefix=SENSOR_DEV_PREFIX, suffix=SENSOR_DEV_SUFFIX): +def get_w1_sensor_device_filename(args, dev_dir=W1_SENSOR_DEV_DIR, + prefix=W1_SENSOR_DEV_PREFIX, suffix=W1_SENSOR_DEV_SUFFIX): """Auto-determine sensor datafile name (unless args.file is set)""" if isempty(args.file): search_pat = dev_dir + ('/' if dev_dir[-1]!='/' else '') @@ -180,23 +138,19 @@ def get_sensor_device_filename(args, dev_dir=SENSOR_DEV_DIR, return filename -def read_sensor_raw(device_file): - """Reads the raw data from the sensor device file, returns array of lines""" - with open(device_file, 'r') as f: - lines = f.readlines() - logger.debug('Temperature sensor data read from %s: %s', f.name, lines) - return lines - - -def read_temp(device_file, converter=CONVERT_CELCIUS, maxretries=10): +def read_w1_temp(args): """Reads sensor data and converts it to desired unit, returns temperature""" - lines = read_sensor_raw(device_file) - tries = 1 - while lines[0].strip()[-3:] != 'YES' and tries <= maxretries: + device_file = get_w1_sensor_device_filename(args) + lines=[ '' ] + tries = 0 + while tries <= args.retries and lines[0].strip()[-3:] != 'YES': + if tries > 0: + logger.warn('Temperature sensor data not stable, reading once more') + sleep(0.2) tries += 1 - sleep(0.2) - logger.warn('Temperature sensor data not stable, reading once more') - lines = read_temp_raw(device_file) + with open(device_file, 'r') as f: + lines = f.readlines() + logger.debug('Temperature sensor data read from %s: %s', f.name, lines) if lines[0].strip()[-3:] != 'YES': errmsg = 'no stable temperature sensor data after %d tries' % tries @@ -206,15 +160,84 @@ def read_temp(device_file, converter=CONVERT_CELCIUS, maxretries=10): errmsg = 'temperature sensor data format is not supported' else: temp_read = int(lines[1][equals_pos+2:]) - temp = converter[0](temp_read) + temp = args.converter[0](temp_read) logger.debug('Temperature sensor value %d is %.2f%s', temp_read, - temp, converter[1]) + temp, args.converter[1]) return temp, tries logger.critical(errmsg) raise ValueError(errmsg) +def parse_args(): + """Parse command line and get parameters from environment, if present""" + + # Setup argument parser, the workhorse gluing it all together + parser = ArgumentParser( + epilog='(*) by default the script will look for the first device that ' + 'matches %s* in %s, if multiple entries are found -s or -f must ' + 'be used to specify which sensor to read.' % + (W1_SENSOR_DEV_PREFIX, W1_SENSOR_DEV_DIR), + description='Nagios check plugin for 1-wire temp. sensor on RaspberryPi' + ) + parser.add_argument('-V', '--version',action="version",version=PROG_VERSION) + + pgroup = parser.add_mutually_exclusive_group(required=False) + pgroup.add_argument('-C', '--celcius', action='store_const', + dest='converter', const=CONVERT_CELCIUS, + help='measure, critical and warn values in Celcius ' + '(default)', default=CONVERT_CELCIUS) + pgroup.add_argument('-F', '--farenheit',action='store_const', + dest='converter', const=CONVERT_FARENHEIT, + help='measure, critical and warn values in Farenheit') + + parser.add_argument('-w', '--warn', type=float, + help='temperature for warning status') + parser.add_argument('-c','--critical', type=float, + help='temperature for critical status') + + pgroup = parser.add_mutually_exclusive_group(required=False) + pgroup.add_argument('-q', '--quiet', default=logging.CRITICAL, + action=SetLogLevel, const=LOGGING_NONE, + help='quiet (no output, only exit with exit code)') + pgroup.add_argument('-v', '--verbose', help='more verbose output', + action=SetLogLevel, const=logging.INFO) + pgroup.add_argument('-d', '--debug', help='debug output (more verbose)', + action=SetLogLevel, const=logging.DEBUG) + + parser.add_argument('-l', '--logfile', action=SetLogFile, + help='send logging output to logfile') + + subparser = parser.add_subparsers(title='Supported temperature sensors') + + cpuparser = ArgumentParser(add_help=False) + cpuparser.add_argument('-f', '--file', default=CPU_SENSOR_DEV, + help='input file (or device) to obtain data from' + ' (defaults to %s)' % CPU_SENSOR_DEV) + cmdparser = subparser.add_parser('rpi_cpu', parents=[cpuparser], + help='read built-in Raspberry Pi CPU temperature') + cmdparser.set_defaults(func=read_rpi_cpu_temp, cmdparser=cmdparser, retries=0) + + w1parser = ArgumentParser(add_help=False) + pgroup = w1parser.add_mutually_exclusive_group(required=False) + pgroup.add_argument('-s', '--serial', + help='(unique part of) temperature sensor serial (*)') + pgroup.add_argument('-f', '--file', + help='input file (or device) to obtain data from (*)') + w1parser.add_argument('-r', '--retries', type=int,default=W1_SENSOR_READ_RETRIES, + help='number of times to retry reading sensor data when' + ' unstable (defaults to %d)' % W1_SENSOR_READ_RETRIES) + cmdparser = subparser.add_parser('w1_ds18b20', parents=[w1parser], + help='read 1-wire connected DS18b20 sensor') + cmdparser.set_defaults(func=read_w1_temp, cmdparser=cmdparser) + + # parse arguments and post-process command line options + args = parser.parse_args() + + # if we got here all seems OK + return args + + def nagios_exit(status, message, data=None): """exit 'nagios-style', print status and message followed by perf. data""" if logger.isEnabledFor(logging.CRITICAL): @@ -239,8 +262,7 @@ if __name__ == '__main__': try: starttime = time() - devicefile = get_sensor_device_filename(args) - temperature, tries = read_temp(devicefile, args.converter, args.retries) + temperature, tries = args.func(args) endtime = time() except (KeyboardInterrupt) as e: @@ -270,3 +292,4 @@ if __name__ == '__main__': ('retries', [ tries-1, None, args.retries, 0, None ]), ('checktime', [ '%fs' % elapse, None, None, 0, None]) ]) + -- libgit2 0.22.2