Commit 8afdb5e31132beed3c0a9b5641314c33c16d3a7c

Authored by Frederik Lindenaar
1 parent 3e330c37

added Raspberry Pi CPU temperature reading

please note: CLI change: now need to specify which device to read!

Documentation updated accordingly
README.md
... ... @@ -25,7 +25,7 @@ This repository contains the following scripts:
25 25 * [check_otp](#check_otp)
26 26 plugin to monitor PrivacyIDEA (and LinOTP) OTP validation
27 27 * [check_temperature](#check_temperature)
28   - plugin to monitor the temperature of a 1-wire sensor on a RaspberryPi
  28 + plugin to monitor the CPU temperature of a RaspberryPi or that of a DS18b20 1-wire sensor on a RaspberryPi
29 29 * [nagiosstatus](#nagiosstatus)
30 30 CGI-BIN script to report the status of nagios (to monitor nagios itself)
31 31  
... ... @@ -237,15 +237,16 @@ define service {
237 237  
238 238 <a name=check_temperature>plugins/check_temperature</a>
239 239 -------------------------------------------------------
240   -Plugin (check) to monitor monitor the temperature using a sensor connected to a
241   -RaspberryPi. This implementation is specifically for the DS18B20 1-wire
242   -temperature sensor. Other methods and interfaces can be plugged in easily (just
243   -raise a request or provide a patch). For information on how to connect sensor
244   -to the RaspberryPi and to get it working please see [this Adafruit tutorial](
  240 +Plugin (check) to monitor monitor the Raspberry Pi CPU temperature or that of a
  241 +temperature sensor connected to a RaspberryPi. This implementation currently
  242 +supports the DS18B20 1-wire temperature sensor. Other methods and interfaces can
  243 +be plugged in easily (just raise a request or provide a patch). For information
  244 +on how to connect sensor to the RaspberryPi and to get it working please see
  245 +[this Adafruit tutorial](
245 246 https://learn.adafruit.com/adafruits-raspberry-pi-lesson-11-ds18b20-temperature-sensing).
246 247  
247   -To enable the 1-wire interface support on the RaspberryPi one can use the
248   -command:
  248 +No setup is required to read the CPU temperature. To enable the 1-wire interface
  249 +support on the RaspberryPi one can use the command:
249 250 ~~~
250 251 sudo raspi-config nonint do_onewire 0
251 252 ~~~
... ... @@ -256,32 +257,46 @@ Installation for is straightforward, after installing the script on the server
256 257 add the following to your Nagios `commands.cmd` configuration file:
257 258  
258 259 ~~~
  260 +# 'check_cpu_temperature' command definition to monitor CPU temperature in C
  261 +# parameters: warning (ARG1) and critical (ARG2) temperature in Celcius
  262 +define command {
  263 + command_name check_temperature
  264 + command_line [install_path]/plugins/check_temperature -w $ARG1$ -c $ARG2$ rpi_cpu
  265 +}
  266 +
  267 +# 'check_cpu_ftemperature' command definition to monitor CPU temperature in F
  268 +# parameters: warning (ARG1) and critical (ARG2) temperature in Celcius
  269 +define command {
  270 + command_name check_temperature
  271 + command_line [install_path]/plugins/check_temperature -F -w $ARG1$ -c $ARG2$ rpi_cpu
  272 +}
  273 +
259 274 # 'check_temperature' command definition to monitor a single temperature in C
260 275 # parameters: warning (ARG1) and critical (ARG2) temperature in Celcius
261 276 define command {
262 277 command_name check_temperature
263   - command_line [install_path]/plugins/check_temperature -w $ARG1$ -c $ARG2$
  278 + command_line [install_path]/plugins/check_temperature -w $ARG1$ -c $ARG2$ w1_ds18b20
264 279 }
265 280  
266 281 # 'check_ftemperature' command definition to monitor a single temperature in F
267 282 # parameters: warning (ARG1) and critical (ARG2) temperature in Farenheit
268 283 define command {
269 284 command_name check_ftemperature
270   - command_line [install_path]/plugins/check_temperature -F -w $ARG1$ -c $ARG2$
  285 + command_line [install_path]/plugins/check_temperature -F -w $ARG1$ -c $ARG2$ w1_ds18b20
271 286 }
272 287  
273 288 # 'check_temperature_sensor' command definition to monitor a single temperature in C
274 289 # parameters: sensor serial (ARG1), warning (ARG2) and critical (ARG3) temperature in Celcius
275 290 define command {
276 291 command_name check_temperature_sensor
277   - command_line [install_path]/plugins/check_temperature -s $ARG1$ -w $ARG2$ -c $ARG3$
  292 + command_line [install_path]/plugins/check_temperature -w $ARG2$ -c $ARG3$ w1_ds18b20 -s $ARG1$
278 293 }
279 294  
280 295 # 'check_ftemperature_sensor' command definition to monitor a single temperature in F
281 296 # parameters: sensor serial (ARG1), warning (ARG2) and critical (ARG3) temperature in Farenheit
282 297 define command {
283 298 command_name check_ftemperature_sensor
284   - command_line [install_path]/plugins/check_temperature -F -s $ARG1$ -w $ARG2$ -c $ARG3$
  299 + command_line [install_path]/plugins/check_temperature -F -w $ARG2$ -c $ARG3$ w1_ds18b20 -s $ARG1$
285 300 }
286 301  
287 302 ~~~
... ... @@ -290,6 +305,14 @@ Make sure to replace `[install_path]/plugins` with the location of the script.
290 305 To use the it define a service check like below:
291 306  
292 307 ~~~
  308 +# check RaspberryPi CPU temperature in Celcius
  309 +define service {
  310 + host hostname.mydomain.tld
  311 + service_description CPU Temperature
  312 + check_command check_cpu_temperature!55!75
  313 + use generic-service
  314 +}
  315 +
293 316 # check temperature in Celcius using a DS18B20 sensor connected to a RaspberryPi
294 317 define service {
295 318 host hostname.mydomain.tld
... ... @@ -298,7 +321,6 @@ define service {
298 321 use generic-service
299 322 }
300 323  
301   -
302 324 # check temperature with DS18B20 sensor 0000a31ea3de connected to a RaspberryPi
303 325 define service {
304 326 host hostname.mydomain.tld
... ... @@ -340,3 +362,4 @@ General Public License for more details.
340 362  
341 363 You should have received a copy of the GNU General Public License along with
342 364 this program. If not, download it from <http://www.gnu.org/licenses/>.
  365 +
... ...
plugins/check_temperature
... ... @@ -2,10 +2,10 @@
2 2 #
3 3 # check_temperature - Nagios temperature check for DS18B20 sensor on RaspberryPi
4 4 #
5   -# Version 1.0, latest version, documentation and bugtracker available at:
  5 +# Version 1.1, latest version, documentation and bugtracker available at:
6 6 # https://gitlab.lindenaar.net/scripts/nagios-plugins
7 7 #
8   -# Copyright (c) 2016 Frederik Lindenaar
  8 +# Copyright (c) 2017 Frederik Lindenaar
9 9 #
10 10 # This script is free software: you can redistribute and/or modify it under the
11 11 # 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, \
27 27 import logging
28 28  
29 29 # Constants (no need to change but allows for easy customization)
30   -VERSION="1.0"
  30 +VERSION="1.1"
31 31 PROG_NAME=splitext(basename(__file__))[0]
32 32 PROG_VERSION=PROG_NAME + ' ' + VERSION
33 33 SENSOR_SCALE=1000
34   -SENSOR_DEV_DIR = '/sys/bus/w1/devices/'
35   -SENSOR_DEV_PREFIX = '28-'
36   -SENSOR_DEV_SUFFIX = '/w1_slave'
37   -SENSOR_READ_RETRIES=10
  34 +W1_SENSOR_DEV_DIR = '/sys/bus/w1/devices/'
  35 +W1_SENSOR_DEV_PREFIX = '28-'
  36 +W1_SENSOR_DEV_SUFFIX = '/w1_slave'
  37 +W1_SENSOR_READ_RETRIES=10
  38 +CPU_SENSOR_DEV = '/sys/class/thermal/thermal_zone0/temp'
38 39  
39 40 LOG_FORMAT='%(levelname)s - %(message)s'
40 41 LOG_FORMAT_FILE='%(asctime)s - ' + LOG_FORMAT
... ... @@ -97,64 +98,21 @@ def isempty(string):
97 98 return string is None or len(string) == 0
98 99  
99 100  
100   -def parse_args():
101   - """Parse command line and get parameters from environment, if present"""
102   -
103   - # Setup argument parser, the workhorse gluing it all together
104   - parser = ArgumentParser(
105   - epilog='(*) by default the script will look for the first device that '
106   - 'matches %s* in %s, if multiple entries are found -s or -f must '
107   - 'be used to specify which sensor to read.' %
108   - (SENSOR_DEV_PREFIX, SENSOR_DEV_DIR),
109   - description='Nagios check plugin for 1-wire temp. sensor on RaspberryPi'
110   - )
111   - parser.add_argument('-V', '--version',action="version",version=PROG_VERSION)
112   -
113   - pgroup = parser.add_mutually_exclusive_group(required=False)
114   - pgroup.add_argument('-C', '--celcius', action='store_const',
115   - dest='converter', const=CONVERT_CELCIUS,
116   - help='measure, critical and warn values in Celcius '
117   - '(default)', default=CONVERT_CELCIUS)
118   - pgroup.add_argument('-F', '--farenheit',action='store_const',
119   - dest='converter', const=CONVERT_FARENHEIT,
120   - help='measure, critical and warn values in Farenheit')
121   -
122   - parser.add_argument('-w', '--warn', type=float,
123   - help='temperature for warning status')
124   - parser.add_argument('-c','--critical', type=float,
125   - help='temperature for critical status')
126   -
127   - parser.add_argument('-r', '--retries', type=int,default=SENSOR_READ_RETRIES,
128   - help='number of times to retry reading sensor data when'
129   - ' unstable (defaults to %d)' % SENSOR_READ_RETRIES)
130   -
131   - pgroup = parser.add_mutually_exclusive_group(required=False)
132   - pgroup.add_argument('-s', '--serial',
133   - help='(unique part of) temperature sensor serial (*)')
134   - pgroup.add_argument('-f', '--file',
135   - help='input file (or device) to obtain data from (*)')
136   -
137   - pgroup = parser.add_mutually_exclusive_group(required=False)
138   - pgroup.add_argument('-q', '--quiet', default=logging.CRITICAL,
139   - action=SetLogLevel, const=LOGGING_NONE,
140   - help='quiet (no output, only exit with exit code)')
141   - pgroup.add_argument('-v', '--verbose', help='more verbose output',
142   - action=SetLogLevel, const=logging.INFO)
143   - pgroup.add_argument('-d', '--debug', help='debug output (more verbose)',
144   - action=SetLogLevel, const=logging.DEBUG)
145   -
146   - parser.add_argument('-l', '--logfile', action=SetLogFile,
147   - help='send logging output to logfile')
148   -
149   - # parse arguments and post-process command line options
150   - args = parser.parse_args()
  101 +def read_rpi_cpu_temp(args):
  102 + """Reads CPU temperature and converts it to desired unit, returns temperature"""
  103 + with open(args.file, 'r') as f:
  104 + lines = f.readlines()
  105 + logger.debug('Temperature sensor data read from %s: %s', f.name, lines)
151 106  
152   - # if we got here all seems OK
153   - return args
  107 + temp_read = int(lines[0])
  108 + temp = args.converter[0](temp_read)
  109 + logger.debug('Temperature sensor value %d is %.2f%s', temp_read,
  110 + temp, args.converter[1])
  111 + return temp, 1
154 112  
155 113  
156   -def get_sensor_device_filename(args, dev_dir=SENSOR_DEV_DIR,
157   - prefix=SENSOR_DEV_PREFIX, suffix=SENSOR_DEV_SUFFIX):
  114 +def get_w1_sensor_device_filename(args, dev_dir=W1_SENSOR_DEV_DIR,
  115 + prefix=W1_SENSOR_DEV_PREFIX, suffix=W1_SENSOR_DEV_SUFFIX):
158 116 """Auto-determine sensor datafile name (unless args.file is set)"""
159 117 if isempty(args.file):
160 118 search_pat = dev_dir + ('/' if dev_dir[-1]!='/' else '')
... ... @@ -180,23 +138,19 @@ def get_sensor_device_filename(args, dev_dir=SENSOR_DEV_DIR,
180 138 return filename
181 139  
182 140  
183   -def read_sensor_raw(device_file):
184   - """Reads the raw data from the sensor device file, returns array of lines"""
185   - with open(device_file, 'r') as f:
186   - lines = f.readlines()
187   - logger.debug('Temperature sensor data read from %s: %s', f.name, lines)
188   - return lines
189   -
190   -
191   -def read_temp(device_file, converter=CONVERT_CELCIUS, maxretries=10):
  141 +def read_w1_temp(args):
192 142 """Reads sensor data and converts it to desired unit, returns temperature"""
193   - lines = read_sensor_raw(device_file)
194   - tries = 1
195   - while lines[0].strip()[-3:] != 'YES' and tries <= maxretries:
  143 + device_file = get_w1_sensor_device_filename(args)
  144 + lines=[ '' ]
  145 + tries = 0
  146 + while tries <= args.retries and lines[0].strip()[-3:] != 'YES':
  147 + if tries > 0:
  148 + logger.warn('Temperature sensor data not stable, reading once more')
  149 + sleep(0.2)
196 150 tries += 1
197   - sleep(0.2)
198   - logger.warn('Temperature sensor data not stable, reading once more')
199   - lines = read_temp_raw(device_file)
  151 + with open(device_file, 'r') as f:
  152 + lines = f.readlines()
  153 + logger.debug('Temperature sensor data read from %s: %s', f.name, lines)
200 154  
201 155 if lines[0].strip()[-3:] != 'YES':
202 156 errmsg = 'no stable temperature sensor data after %d tries' % tries
... ... @@ -206,15 +160,84 @@ def read_temp(device_file, converter=CONVERT_CELCIUS, maxretries=10):
206 160 errmsg = 'temperature sensor data format is not supported'
207 161 else:
208 162 temp_read = int(lines[1][equals_pos+2:])
209   - temp = converter[0](temp_read)
  163 + temp = args.converter[0](temp_read)
210 164 logger.debug('Temperature sensor value %d is %.2f%s', temp_read,
211   - temp, converter[1])
  165 + temp, args.converter[1])
212 166 return temp, tries
213 167  
214 168 logger.critical(errmsg)
215 169 raise ValueError(errmsg)
216 170  
217 171  
  172 +def parse_args():
  173 + """Parse command line and get parameters from environment, if present"""
  174 +
  175 + # Setup argument parser, the workhorse gluing it all together
  176 + parser = ArgumentParser(
  177 + epilog='(*) by default the script will look for the first device that '
  178 + 'matches %s* in %s, if multiple entries are found -s or -f must '
  179 + 'be used to specify which sensor to read.' %
  180 + (W1_SENSOR_DEV_PREFIX, W1_SENSOR_DEV_DIR),
  181 + description='Nagios check plugin for 1-wire temp. sensor on RaspberryPi'
  182 + )
  183 + parser.add_argument('-V', '--version',action="version",version=PROG_VERSION)
  184 +
  185 + pgroup = parser.add_mutually_exclusive_group(required=False)
  186 + pgroup.add_argument('-C', '--celcius', action='store_const',
  187 + dest='converter', const=CONVERT_CELCIUS,
  188 + help='measure, critical and warn values in Celcius '
  189 + '(default)', default=CONVERT_CELCIUS)
  190 + pgroup.add_argument('-F', '--farenheit',action='store_const',
  191 + dest='converter', const=CONVERT_FARENHEIT,
  192 + help='measure, critical and warn values in Farenheit')
  193 +
  194 + parser.add_argument('-w', '--warn', type=float,
  195 + help='temperature for warning status')
  196 + parser.add_argument('-c','--critical', type=float,
  197 + help='temperature for critical status')
  198 +
  199 + pgroup = parser.add_mutually_exclusive_group(required=False)
  200 + pgroup.add_argument('-q', '--quiet', default=logging.CRITICAL,
  201 + action=SetLogLevel, const=LOGGING_NONE,
  202 + help='quiet (no output, only exit with exit code)')
  203 + pgroup.add_argument('-v', '--verbose', help='more verbose output',
  204 + action=SetLogLevel, const=logging.INFO)
  205 + pgroup.add_argument('-d', '--debug', help='debug output (more verbose)',
  206 + action=SetLogLevel, const=logging.DEBUG)
  207 +
  208 + parser.add_argument('-l', '--logfile', action=SetLogFile,
  209 + help='send logging output to logfile')
  210 +
  211 + subparser = parser.add_subparsers(title='Supported temperature sensors')
  212 +
  213 + cpuparser = ArgumentParser(add_help=False)
  214 + cpuparser.add_argument('-f', '--file', default=CPU_SENSOR_DEV,
  215 + help='input file (or device) to obtain data from'
  216 + ' (defaults to %s)' % CPU_SENSOR_DEV)
  217 + cmdparser = subparser.add_parser('rpi_cpu', parents=[cpuparser],
  218 + help='read built-in Raspberry Pi CPU temperature')
  219 + cmdparser.set_defaults(func=read_rpi_cpu_temp, cmdparser=cmdparser, retries=0)
  220 +
  221 + w1parser = ArgumentParser(add_help=False)
  222 + pgroup = w1parser.add_mutually_exclusive_group(required=False)
  223 + pgroup.add_argument('-s', '--serial',
  224 + help='(unique part of) temperature sensor serial (*)')
  225 + pgroup.add_argument('-f', '--file',
  226 + help='input file (or device) to obtain data from (*)')
  227 + w1parser.add_argument('-r', '--retries', type=int,default=W1_SENSOR_READ_RETRIES,
  228 + help='number of times to retry reading sensor data when'
  229 + ' unstable (defaults to %d)' % W1_SENSOR_READ_RETRIES)
  230 + cmdparser = subparser.add_parser('w1_ds18b20', parents=[w1parser],
  231 + help='read 1-wire connected DS18b20 sensor')
  232 + cmdparser.set_defaults(func=read_w1_temp, cmdparser=cmdparser)
  233 +
  234 + # parse arguments and post-process command line options
  235 + args = parser.parse_args()
  236 +
  237 + # if we got here all seems OK
  238 + return args
  239 +
  240 +
218 241 def nagios_exit(status, message, data=None):
219 242 """exit 'nagios-style', print status and message followed by perf. data"""
220 243 if logger.isEnabledFor(logging.CRITICAL):
... ... @@ -239,8 +262,7 @@ if __name__ == &#39;__main__&#39;:
239 262  
240 263 try:
241 264 starttime = time()
242   - devicefile = get_sensor_device_filename(args)
243   - temperature, tries = read_temp(devicefile, args.converter, args.retries)
  265 + temperature, tries = args.func(args)
244 266 endtime = time()
245 267  
246 268 except (KeyboardInterrupt) as e:
... ... @@ -270,3 +292,4 @@ if __name__ == &#39;__main__&#39;:
270 292 ('retries', [ tries-1, None, args.retries, 0, None ]),
271 293 ('checktime', [ '%fs' % elapse, None, None, 0, None])
272 294 ])
  295 +
... ...