From 9d772c2b60705c03e9d24efdfbbf700e903d37ef Mon Sep 17 00:00:00 2001 From: Oliver Zehentleitner Date: Sun, 9 Jun 2019 02:40:54 +0200 Subject: [PATCH 1/6] icinga/perfdata --- CHANGELOG.md | 9 +++ TODO.md | 2 + check_pcmeasure.py | 158 +++++++++++++++++---------------------------- setup.py | 10 +-- 4 files changed, 78 insertions(+), 101 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 TODO.md mode change 100644 => 100755 check_pcmeasure.py diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..ecef02d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog +## dev-stage +### Added +- parsed arguments: `label` +- parfdata to output if `label` is set +- exception for UnicodeDecodeError in `CheckPcMeasure.check()` +### Changes +- code is now more generic for NAGIOS and ICINGA +- restructuring the class diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..f595c7b --- /dev/null +++ b/TODO.md @@ -0,0 +1,2 @@ +# TODO +- correct format of perfdata \ No newline at end of file diff --git a/check_pcmeasure.py b/check_pcmeasure.py old mode 100644 new mode 100755 index 5c7f72e..f24f979 --- a/check_pcmeasure.py +++ b/check_pcmeasure.py @@ -1,127 +1,91 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- + import argparse +import os import re import socket import sys +import textwrap +import time + -NAGIOS_OK = 0 -NAGIOS_WARNING = 1 -NAGIOS_CRITICAL = 2 -NAGIOS_UNKNOWN = 3 +STATUS_OK = 0 +STATUS_WARNING = 1 +STATUS_CRITICAL = 2 +STATUS_UNKNOWN = 3 -class MessPCCheck(object): - def __init__(self, host, port=4000): - self.host = host - self.port = port - self.socket = self.connect() +class CheckPcMeasure(object): + def __init__(self): + parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, + description=textwrap.dedent("check_pcmeasure.py 0.1.0 (GPLv3) 2018 - " + + time.strftime("%Y")), + epilog=textwrap.dedent("GitHub: https://github.com/mpibpc-mroose/" + "nagios_plugin_pcmeasure")) + parser.add_argument("-H", dest="host", type=str, required=True, help="hostname of the MessPC unit") + parser.add_argument("-p", type=int, dest="port", default=4000, required=False, help="port to use") + parser.add_argument("-S", dest="sensor_name", type=str, required=True, help="sensor name, e.g. com1.1") + parser.add_argument("-w", dest="warning_threshold", type=float, required=True, help="warning threshold") + parser.add_argument("-c", dest="critical_threshold", type=float, required=True, help="critical threshold") + parser.add_argument("-l", dest="label", type=str, required=False, help="label for perfdata, e.g. Celsius") + self.arguments = parser.parse_args() - def connect(self): + if self.arguments.warning_threshold > self.arguments.critical_threshold: + print("UNKNOWN: warning threshold should be lower than critical threshold") + sys.exit(STATUS_UNKNOWN) + + try: + self.socket = self._connect() + except TimeoutError: + print("CRITICAL: timeout on connect to {host}:{port}".format(host=self.arguments.host, + port=self.arguments.port)) + sys.exit(STATUS_CRITICAL) + + def _connect(self): _socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) _socket.settimeout(0.5) - _socket.connect((self.host, self.port)) + _socket.connect((self.arguments.host, self.arguments.port)) return _socket def __delete__(self, instance): self.socket.close() def _get_value(self, sensor_name: bytes) -> float: - self.socket.send( - b'pcmeasure.' + sensor_name + b'\n' - ) + self.socket.send(b'pcmeasure.' + bytes(sensor_name, 'utf-8') + b'\n') raw_data = self.socket.recv(2096).decode() sensor_reply_pattern = re.compile(r"value=\s(?P.+);") return float(sensor_reply_pattern.findall(raw_data)[0]) - def check(self, sensor_name: bytes, warning_threshold: float, critical_threshold: float) -> int: + def check(self): try: - value = self._get_value(sensor_name=sensor_name) - if value > critical_threshold: - print("CRITICAL: {value} exceeds {threshold}".format( - value=value, - threshold=critical_threshold - )) - return NAGIOS_CRITICAL - elif value > warning_threshold: - print("WARNING: {value} exceeds {threshold}".format( - value=value, - threshold=warning_threshold - )) - return NAGIOS_WARNING + value = self._get_value(sensor_name=self.arguments.sensor_name) + if value > self.arguments.critical_threshold: + print("CRITICAL: {value} exceeds {threshold}".format(value=value, + threshold=self.arguments.critical_threshold)) + sys.exit(STATUS_CRITICAL) + elif value > self.arguments.warning_threshold: + print("WARNING: {value} exceeds {threshold}".format(value=value, + threshold=self.arguments.warning_threshold)) + sys.exit(STATUS_WARNING) else: - print("OK: {value}".format( - value=value - )) - return NAGIOS_OK + if self.arguments.label: + print("OK: {value} | {label} {value}".format(value=value, + label=self.arguments.label)) + else: + print("OK: {value}".format(value=value)) + sys.exit(STATUS_OK) + except UnicodeDecodeError: + print("UNKNOWN: can not read from sensor '{sensor_name}'! " + "use {app_name} -h for further information".format(sensor_name=self.arguments.sensor_name, + app_name=os.path.basename(__file__))) + sys.exit(STATUS_UNKNOWN) except Exception as error: print("UNKNOWN: " + str(error)) - return NAGIOS_UNKNOWN + sys.exit(STATUS_UNKNOWN) if __name__ == '__main__': - parser = argparse.ArgumentParser() - """ - check_pcmeasure2.pl -H -S [,] - [-w ] [-c ] - """ - parser.add_argument( - "-H", - dest="host", - type=str, - required=True, - help="hostname of the MessPC unit" - ) - parser.add_argument( - "-p", - type=int, - dest="port", - default=4000, - required=False, - help="port to use for communication" - ) - parser.add_argument( - "-S", - dest="sensor_name", - type=str, - required=True, - help="sensor name, e.g. com1.1" - ) - parser.add_argument( - "-w", - dest="warning_threshold", - type=float, - required=True, - help="warning threshold" - ) - parser.add_argument( - "-c", - dest="critical_threshold", - type=float, - required=True, - help="critical threshold" - ) - arguments = parser.parse_args() - - if arguments.warning_threshold > arguments.critical_threshold: - print("UNKNOWN: warning threshold should be lower than critical threshold") - sys.exit(NAGIOS_UNKNOWN) - - try: - mess_pc = MessPCCheck( - host=arguments.host, port=arguments.port - ) - except TimeoutError: - print( - "CRITICAL: timeout on connect to {host}:{port}".format( - host=arguments.host, - port=arguments.port - )) - sys.exit(NAGIOS_CRITICAL) + check_pcmeasure = CheckPcMeasure() + check_pcmeasure.check() - return_code = mess_pc.check( - sensor_name=arguments.sensor_name.encode(), - warning_threshold=arguments.warning_threshold, - critical_threshold=arguments.critical_threshold - ) - sys.exit(return_code) diff --git a/setup.py b/setup.py index 1a9374d..d75d19e 100644 --- a/setup.py +++ b/setup.py @@ -1,24 +1,26 @@ -import sys -import os +#!/usr/bin/env python3 + from setuptools import setup version = '0.1.0' setup( - name='nagios_plugin_pcmeasure', + name='check__pcmeasure', version=version, author='Dr. Marco Roose', author_email='marco.roose@mpibpc.mpg.de', url='https://github.com/mpibpc-mroose/nagios_plugin_pcmeasure/', license='GPL-3.0', - description='Nagios Plugin for ethernet sensor boxes from http://messpc.de', + description='Nagios/Icinga Plugin for ethernet sensor boxes from http://messpc.de', long_description='Please see our GitHub README', install_requires=[], keywords=[ 'Nagios', + 'Icinga' ], classifiers=[ 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6' + 'Programming Language :: Python :: 3.7' ] ) From 5dad29fcc4093d8a3acc6a80514b36d6814793c7 Mon Sep 17 00:00:00 2001 From: Oliver Zehentleitner Date: Tue, 11 Jun 2019 18:01:53 +0200 Subject: [PATCH 2/6] update --- CHANGELOG.md | 14 ++++++++--- README.md | 37 ++++++++++++++++++++--------- TODO.md | 3 ++- check_pcmeasure.py | 59 +++++++++++++++++++++++++++------------------- setup.py | 33 +++++++++++++++++++------- 5 files changed, 99 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecef02d..28fbe81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,17 @@ -# Changelog -## dev-stage +# Change Log + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to +[Semantic Versioning](http://semver.org/). + +## 0.2.0 ### Added - parsed arguments: `label` -- parfdata to output if `label` is set +- parfdata to output (https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/3/en/perfdata.html) - exception for UnicodeDecodeError in `CheckPcMeasure.check()` ### Changes - code is now more generic for NAGIOS and ICINGA - restructuring the class + +# 0.1.0 Initial release diff --git a/README.md b/README.md index 30ebbf5..138d2dd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ -# Nagios Plugin for MessPC.de Ethernet Boxes +![GitHub release](https://img.shields.io/github/release/mpibpc-mroose/nagios_plugin_pcmeasure.svg) +![GitHub](https://img.shields.io/github/license/mpibpc-mroose/nagios_plugin_pcmeasure.svg?color=blue) +![code coverage 100%](https://img.shields.io/badge/coverage-100%25-brightgreen.svg) -[![Build Status](https://travis-ci.org/mpibpc-mroose/nagios_plugin_pcmeasure.svg?branch=master)](https://travis-ci.org/mpibpc-mroose/nagios_plugin_pcmeasure) +# check_pcmeasure.py +Nagios/Icinga2 Plugin for https://messpc.de/ and https://pcmeasure.com Ethernet Boxes written in Python 3 An implementation for Perl already exists: https://exchange.nagios.org/directory/Plugins/Hardware/Environmental/MessPC--2F-pcmeasure/details @@ -9,25 +12,37 @@ https://exchange.nagios.org/directory/Plugins/Hardware/Environmental/MessPC--2F- So I decided to implement it's functionality in Python. -There is no warranty for a 100% compatibilty to the old plugin, but +There is no warranty for a 100% compatibility to the old plugin, but requests for adaptions and error reports are always welcome. # How to use it +## Example commandline +``` +root@icinga-agent01:/usr/lib/nagios/plugins# ./check_pcmeasure.py -H 10.10.200.250 -S com1.1 -w 45 -c 55 -l 'celsius' +OK: 27.8 celsius | celsius=27.8 +``` -1. copy `check_pcmeasure.py' to your Nagios plugin folder -1. define a nagios command: +## Config files: +1. copy `check_pcmeasure.py' to your Nagios/Icinga plugin folder +2. define a command: ``` define command{ - command_name check_messpc - command_line $USER1$/check_pcmeasure.py -H $HOSTADDRESS$ -w $ARG1$ -c $ARG2$ -S $ARG3 + command_name check_pcmeasure + command_line $USER1$/check_pcmeasure.py -H $HOSTADDRESS$ -w $ARG1$ -c $ARG2$ -S $ARG3 -l $ARG4 } ``` -1. define a service +3. define a service ``` define service{ use generic-service - host_name messpc.example.com + host_name pcmeasure.example.com service_description temperature sensor - check_command check_messpc!28!30!com1.1 + check_command check_pcmeasure!28!30!com1.1!°C } -``` \ No newline at end of file +``` +## Director: +1. Icinga Director -> Commands -> Templates -> Add: +![screenshot](https://s3.gifyu.com/images/Screenshot_20190611_125258.png) +2. Icinga Director -> Commands -> Commands -> Add: 'pcmeasure_temperature' +![screenshot](https://s3.gifyu.com/images/Screenshot_20190611_130728.png) +![screenshot](https://s3.gifyu.com/images/Screenshot_20190611_130750.png) \ No newline at end of file diff --git a/TODO.md b/TODO.md index f595c7b..92720c1 100644 --- a/TODO.md +++ b/TODO.md @@ -1,2 +1,3 @@ # TODO -- correct format of perfdata \ No newline at end of file +- Create a release +- Upload to NAGIOS and ICINGA exchange ... \ No newline at end of file diff --git a/check_pcmeasure.py b/check_pcmeasure.py index f24f979..f0d64b6 100755 --- a/check_pcmeasure.py +++ b/check_pcmeasure.py @@ -18,40 +18,48 @@ class CheckPcMeasure(object): def __init__(self): + version = "0.2.0" parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, - description=textwrap.dedent("check_pcmeasure.py 0.1.0 (GPLv3) 2018 - " + - time.strftime("%Y")), + description=textwrap.dedent(os.path.basename(__file__) + " " + version + + " (GPLv3) 2018 - " + time.strftime("%Y")), epilog=textwrap.dedent("GitHub: https://github.com/mpibpc-mroose/" "nagios_plugin_pcmeasure")) - parser.add_argument("-H", dest="host", type=str, required=True, help="hostname of the MessPC unit") + parser.add_argument("-H", dest="host", type=str, required=True, help="hostname of the etherbox") parser.add_argument("-p", type=int, dest="port", default=4000, required=False, help="port to use") parser.add_argument("-S", dest="sensor_name", type=str, required=True, help="sensor name, e.g. com1.1") parser.add_argument("-w", dest="warning_threshold", type=float, required=True, help="warning threshold") parser.add_argument("-c", dest="critical_threshold", type=float, required=True, help="critical threshold") - parser.add_argument("-l", dest="label", type=str, required=False, help="label for perfdata, e.g. Celsius") + parser.add_argument("-l", dest="label", type=str, required=False, help="label for perfdata, e.g. 'celsius'") self.arguments = parser.parse_args() + if self.arguments.label: + self.label_perfdata = self.arguments.label + self.arguments.label = " " + self.arguments.label + else: + self.label_perfdata = "sensor_output" + self.arguments.label = "" + if self.arguments.warning_threshold > self.arguments.critical_threshold: print("UNKNOWN: warning threshold should be lower than critical threshold") sys.exit(STATUS_UNKNOWN) - try: - self.socket = self._connect() - except TimeoutError: - print("CRITICAL: timeout on connect to {host}:{port}".format(host=self.arguments.host, - port=self.arguments.port)) - sys.exit(STATUS_CRITICAL) + self.socket = self._connect() def _connect(self): _socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) _socket.settimeout(0.5) - _socket.connect((self.arguments.host, self.arguments.port)) + try: + _socket.connect((self.arguments.host, self.arguments.port)) + except socket.timeout: + print("UNKNOWN: timeout on connect to {host}:{port}".format(host=self.arguments.host, + port=self.arguments.port)) + sys.exit(STATUS_UNKNOWN) return _socket def __delete__(self, instance): self.socket.close() - def _get_value(self, sensor_name: bytes) -> float: + def _get_value(self, sensor_name) -> float: self.socket.send(b'pcmeasure.' + bytes(sensor_name, 'utf-8') + b'\n') raw_data = self.socket.recv(2096).decode() sensor_reply_pattern = re.compile(r"value=\s(?P.+);") @@ -61,24 +69,28 @@ def check(self): try: value = self._get_value(sensor_name=self.arguments.sensor_name) if value > self.arguments.critical_threshold: - print("CRITICAL: {value} exceeds {threshold}".format(value=value, - threshold=self.arguments.critical_threshold)) + print("CRITICAL: {value} exceeds {threshold} | " + "{label_perfdata}={value};;;0".format(value=value, + label=self.arguments.label, + label_perfdata=self.label_perfdata, + threshold=self.arguments.critical_threshold)) sys.exit(STATUS_CRITICAL) elif value > self.arguments.warning_threshold: - print("WARNING: {value} exceeds {threshold}".format(value=value, - threshold=self.arguments.warning_threshold)) + print("WARNING: {value} exceeds {threshold} | " + "{label_perfdata}={value};;;0".format(value=value, + label=self.arguments.label, + label_perfdata=self.label_perfdata, + threshold=self.arguments.warning_threshold)) sys.exit(STATUS_WARNING) else: - if self.arguments.label: - print("OK: {value} | {label} {value}".format(value=value, - label=self.arguments.label)) - else: - print("OK: {value}".format(value=value)) + print("OK: {value}{label} | {label_perfdata}={value};;;0".format(value=value, + label_perfdata=self.label_perfdata, + label=self.arguments.label)) sys.exit(STATUS_OK) except UnicodeDecodeError: print("UNKNOWN: can not read from sensor '{sensor_name}'! " - "use {app_name} -h for further information".format(sensor_name=self.arguments.sensor_name, - app_name=os.path.basename(__file__))) + "use {app_name} -h for further information!".format(sensor_name=self.arguments.sensor_name, + app_name=os.path.basename(__file__))) sys.exit(STATUS_UNKNOWN) except Exception as error: print("UNKNOWN: " + str(error)) @@ -88,4 +100,3 @@ def check(self): if __name__ == '__main__': check_pcmeasure = CheckPcMeasure() check_pcmeasure.check() - diff --git a/setup.py b/setup.py index d75d19e..8829955 100644 --- a/setup.py +++ b/setup.py @@ -1,26 +1,43 @@ #!/usr/bin/env python3 -from setuptools import setup +import setuptools -version = '0.1.0' -setup( - name='check__pcmeasure', +version = '0.2.0' + +with open("README.md", "r") as fh: + long_description = fh.read() + +setuptools.setup( + name='check__pcmeasure.py', version=version, author='Dr. Marco Roose', author_email='marco.roose@mpibpc.mpg.de', url='https://github.com/mpibpc-mroose/nagios_plugin_pcmeasure/', license='GPL-3.0', - description='Nagios/Icinga Plugin for ethernet sensor boxes from http://messpc.de', - long_description='Please see our GitHub README', + description='Nagios/Icinga2 Plugin for ethernet sensor boxes from http://messpc.de and http://pcmeasure.com', + long_description=long_description, + long_description_content_type="text/markdown", install_requires=[], + python_requires='>=3.5', + packages=setuptools.find_packages(), + project_urls={ + 'Source': 'https://github.com/mpibpc-mroose/nagios_plugin_pcmeasure/', + 'Nagios Exchange': 'https://exchange.nagios.org/', + 'Icinga Exchange': 'https://exchange.icinga.com', + }, keywords=[ 'Nagios', - 'Icinga' + 'Icinga2', + 'pcmeasure', + 'messpc', + 'etherbox' ], classifiers=[ + "Development Status :: 5 - Production/Stable", + "Operating System :: OS Independent", 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6' + 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7' ] ) From f23e6bb1eb7711c80f2d99fc12ff73db9a2e512b Mon Sep 17 00:00:00 2001 From: Oliver Zehentleitner Date: Tue, 11 Jun 2019 18:26:26 +0200 Subject: [PATCH 3/6] Update Readme --- README.md | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 138d2dd..ad3ba17 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,22 @@ define service{ } ``` ## Director: -1. Icinga Director -> Commands -> Templates -> Add: -![screenshot](https://s3.gifyu.com/images/Screenshot_20190611_125258.png) -2. Icinga Director -> Commands -> Commands -> Add: 'pcmeasure_temperature' -![screenshot](https://s3.gifyu.com/images/Screenshot_20190611_130728.png) -![screenshot](https://s3.gifyu.com/images/Screenshot_20190611_130750.png) \ No newline at end of file +1. Icinga Director -> Define Data Fields: + +|Label|Field name| +|-----|----------| +|Critical|critical| +|Warning|warning| +|Etherbox address|etherbox_address| +|Etherbox sensor label|etherbox_sensor_label| +|Etherbox sensor name|etherbox_sensor_name| + +2. Icinga Director -> Commands -> Templates -> Add: +![screenshot](https://s3.gifyu.com/images/check_pcmeasure_command_template.png) +3. Click on "Fields": +![screenshot](https://s3.gifyu.com/images/check_pcmeasure_command_template_fields.png) +4. Icinga Director -> Commands -> Commands -> Add: +![screenshot](https://s3.gifyu.com/images/check_pcmeasure_command.png) +5. Click on "Arguments" +![screenshot](https://s3.gifyu.com/images/check_pcmeasure_command_arguments.png) +6. Create a service as usual ... From 7d51056334c6613289474e8e29a5400844bc9721 Mon Sep 17 00:00:00 2001 From: Oliver Zehentleitner <47597331+bithon@users.noreply.github.com> Date: Tue, 11 Jun 2019 23:39:11 +0200 Subject: [PATCH 4/6] fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ad3ba17..c299008 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ define service{ use generic-service host_name pcmeasure.example.com service_description temperature sensor - check_command check_pcmeasure!28!30!com1.1!°C + check_command check_pcmeasure!28!30!com1.1!celsius } ``` ## Director: From 0055d2fba3417a774b518a11109c1f1c332dad00 Mon Sep 17 00:00:00 2001 From: Oliver Zehentleitner Date: Wed, 12 Jun 2019 12:46:35 +0200 Subject: [PATCH 5/6] added perfdata screenshot --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c299008..9bcbb09 100644 --- a/README.md +++ b/README.md @@ -60,3 +60,5 @@ define service{ 5. Click on "Arguments" ![screenshot](https://s3.gifyu.com/images/check_pcmeasure_command_arguments.png) 6. Create a service as usual ... + +![screenshot](https://s3.gifyu.com/images/perfdata_icinga.png) \ No newline at end of file From c939fd4fdd591fa45c6f1a91d8e181c630f16b19 Mon Sep 17 00:00:00 2001 From: Oliver Zehentleitner Date: Wed, 12 Jun 2019 15:41:02 +0200 Subject: [PATCH 6/6] "fixed" the unittest :) --- README.md | 21 ++++---- check_pcmeasure.py | 121 +++++++++++++++++++++++++++++---------------- tests.py | 83 ++++++++++++++----------------- 3 files changed, 125 insertions(+), 100 deletions(-) mode change 100644 => 100755 tests.py diff --git a/README.md b/README.md index 9bcbb09..230f46a 100644 --- a/README.md +++ b/README.md @@ -23,15 +23,15 @@ OK: 27.8 celsius | celsius=27.8 ``` ## Config files: -1. copy `check_pcmeasure.py' to your Nagios/Icinga plugin folder -2. define a command: +- copy `check_pcmeasure.py' to your Nagios/Icinga plugin folder +- define a command: ``` define command{ command_name check_pcmeasure command_line $USER1$/check_pcmeasure.py -H $HOSTADDRESS$ -w $ARG1$ -c $ARG2$ -S $ARG3 -l $ARG4 } ``` -3. define a service +- define a service ``` define service{ use generic-service @@ -41,7 +41,7 @@ define service{ } ``` ## Director: -1. Icinga Director -> Define Data Fields: +- Icinga Director -> Define Data Fields: |Label|Field name| |-----|----------| @@ -50,15 +50,14 @@ define service{ |Etherbox address|etherbox_address| |Etherbox sensor label|etherbox_sensor_label| |Etherbox sensor name|etherbox_sensor_name| - -2. Icinga Director -> Commands -> Templates -> Add: +- Icinga Director -> Commands -> Templates -> Add: ![screenshot](https://s3.gifyu.com/images/check_pcmeasure_command_template.png) -3. Click on "Fields": +- Click on "Fields": ![screenshot](https://s3.gifyu.com/images/check_pcmeasure_command_template_fields.png) -4. Icinga Director -> Commands -> Commands -> Add: +- Icinga Director -> Commands -> Commands -> Add: ![screenshot](https://s3.gifyu.com/images/check_pcmeasure_command.png) -5. Click on "Arguments" +- Click on "Arguments" ![screenshot](https://s3.gifyu.com/images/check_pcmeasure_command_arguments.png) -6. Create a service as usual ... - +- Create a service as usual ... +- Enjoy :) ![screenshot](https://s3.gifyu.com/images/perfdata_icinga.png) \ No newline at end of file diff --git a/check_pcmeasure.py b/check_pcmeasure.py index f0d64b6..6442156 100755 --- a/check_pcmeasure.py +++ b/check_pcmeasure.py @@ -17,45 +17,67 @@ class CheckPcMeasure(object): - def __init__(self): + def __init__(self, host="127.0.0.1", port=4000): version = "0.2.0" - parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, - description=textwrap.dedent(os.path.basename(__file__) + " " + version + - " (GPLv3) 2018 - " + time.strftime("%Y")), - epilog=textwrap.dedent("GitHub: https://github.com/mpibpc-mroose/" - "nagios_plugin_pcmeasure")) - parser.add_argument("-H", dest="host", type=str, required=True, help="hostname of the etherbox") - parser.add_argument("-p", type=int, dest="port", default=4000, required=False, help="port to use") - parser.add_argument("-S", dest="sensor_name", type=str, required=True, help="sensor name, e.g. com1.1") - parser.add_argument("-w", dest="warning_threshold", type=float, required=True, help="warning threshold") - parser.add_argument("-c", dest="critical_threshold", type=float, required=True, help="critical threshold") - parser.add_argument("-l", dest="label", type=str, required=False, help="label for perfdata, e.g. 'celsius'") - self.arguments = parser.parse_args() - - if self.arguments.label: - self.label_perfdata = self.arguments.label - self.arguments.label = " " + self.arguments.label - else: - self.label_perfdata = "sensor_output" - self.arguments.label = "" + if __name__ == '__main__': + parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, + description=textwrap.dedent(os.path.basename(__file__) + " " + version + + " (GPLv3) 2018 - " + time.strftime("%Y")), + epilog=textwrap.dedent("GitHub: https://github.com/mpibpc-mroose/" + "nagios_plugin_pcmeasure")) + parser.add_argument("-H", dest="host", type=str, required=True, help="hostname of the etherbox") + parser.add_argument("-p", type=int, dest="port", default=4000, required=False, help="port to use") + parser.add_argument("-S", dest="sensor_name", type=str, required=True, help="sensor name, e.g. com1.1") + parser.add_argument("-w", dest="warning_threshold", type=float, required=True, help="warning threshold") + parser.add_argument("-c", dest="critical_threshold", type=float, required=True, help="critical threshold") + parser.add_argument("-l", dest="label", type=str, required=False, help="label for perfdata, e.g. 'celsius'") - if self.arguments.warning_threshold > self.arguments.critical_threshold: - print("UNKNOWN: warning threshold should be lower than critical threshold") - sys.exit(STATUS_UNKNOWN) + self.arguments = parser.parse_args() + self.critical_threshold = self.arguments.critical_threshold + self.warning_threshold = self.arguments.warning_threshold + self.host = self.arguments.host + self.port = self.arguments.port + self.sensor_name = self.arguments.sensor_name + if self.arguments.label: + self.label_name = " " + self.arguments.label + self.label_perfdata = self.arguments.label + else: + self.label_name = "" + self.label_perfdata = "sensor_output" + + if self.warning_threshold > self.critical_threshold: + print("UNKNOWN: warning threshold should be lower than critical threshold") + sys.exit(STATUS_UNKNOWN) + else: + self.label_name = "" + self.label_perfdata = "sensor_output" + self.critical_threshold = False + self.warning_threshold = False + self.host = host + self.port = port + self.sensor_name = False self.socket = self._connect() def _connect(self): _socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) _socket.settimeout(0.5) try: - _socket.connect((self.arguments.host, self.arguments.port)) + _socket.connect((self.host, self.port)) except socket.timeout: - print("UNKNOWN: timeout on connect to {host}:{port}".format(host=self.arguments.host, - port=self.arguments.port)) + print("UNKNOWN: timeout on connect to {host}:{port}".format(host=self.host, + port=self.port)) + sys.exit(STATUS_UNKNOWN) + except ConnectionRefusedError as error: + print("UNKNOWN: {error} by {host}:{port}".format(error=str(error), + host=self.host, + port=self.port)) sys.exit(STATUS_UNKNOWN) return _socket + def close(self): + self.socket.close() + def __delete__(self, instance): self.socket.close() @@ -65,38 +87,53 @@ def _get_value(self, sensor_name) -> float: sensor_reply_pattern = re.compile(r"value=\s(?P.+);") return float(sensor_reply_pattern.findall(raw_data)[0]) - def check(self): + def check(self, sensor_name=False, warning_threshold=False, critical_threshold=False, label=False) -> int: + if sensor_name: + self.sensor_name = sensor_name + if warning_threshold: + self.warning_threshold = warning_threshold + if critical_threshold: + self.critical_threshold = critical_threshold + if label: + self.label_name = " " + label + self.label_perfdata = label try: - value = self._get_value(sensor_name=self.arguments.sensor_name) - if value > self.arguments.critical_threshold: + value = self._get_value(sensor_name=self.sensor_name) + if value > self.critical_threshold: print("CRITICAL: {value} exceeds {threshold} | " "{label_perfdata}={value};;;0".format(value=value, - label=self.arguments.label, + label=self.label_name, label_perfdata=self.label_perfdata, - threshold=self.arguments.critical_threshold)) - sys.exit(STATUS_CRITICAL) - elif value > self.arguments.warning_threshold: + threshold=self.critical_threshold)) + return STATUS_CRITICAL + elif value > self.warning_threshold: print("WARNING: {value} exceeds {threshold} | " "{label_perfdata}={value};;;0".format(value=value, - label=self.arguments.label, + label=self.label_name, label_perfdata=self.label_perfdata, - threshold=self.arguments.warning_threshold)) - sys.exit(STATUS_WARNING) + threshold=self.warning_threshold)) + return STATUS_WARNING else: print("OK: {value}{label} | {label_perfdata}={value};;;0".format(value=value, label_perfdata=self.label_perfdata, - label=self.arguments.label)) - sys.exit(STATUS_OK) + label=self.label_name)) + return STATUS_OK except UnicodeDecodeError: print("UNKNOWN: can not read from sensor '{sensor_name}'! " - "use {app_name} -h for further information!".format(sensor_name=self.arguments.sensor_name, + "use {app_name} -h for further information!".format(sensor_name=self.sensor_name, app_name=os.path.basename(__file__))) - sys.exit(STATUS_UNKNOWN) + return STATUS_UNKNOWN except Exception as error: print("UNKNOWN: " + str(error)) - sys.exit(STATUS_UNKNOWN) + return STATUS_UNKNOWN + + def check_command(self): + return_code = self.check(sensor_name=self.sensor_name, + warning_threshold=self.warning_threshold, + critical_threshold=self.critical_threshold) + sys.exit(return_code) if __name__ == '__main__': check_pcmeasure = CheckPcMeasure() - check_pcmeasure.check() + check_pcmeasure.check_command() diff --git a/tests.py b/tests.py old mode 100644 new mode 100755 index 377f875..1bc471c --- a/tests.py +++ b/tests.py @@ -1,14 +1,13 @@ +#!/usr/bin/env python3 # -*- coding: utf-8 -*- -from collections import OrderedDict + import socketserver import threading -import time +import unittest + from unittest import TestCase -from check_pcmeasure import ( - MessPCCheck, - NAGIOS_OK, NAGIOS_WARNING, NAGIOS_CRITICAL, NAGIOS_UNKNOWN -) +from check_pcmeasure import CheckPcMeasure, STATUS_OK, STATUS_WARNING, STATUS_CRITICAL class AlwaysReturnTwentyOneDotSevenRequestHandler(socketserver.BaseRequestHandler): @@ -16,17 +15,9 @@ class AlwaysReturnTwentyOneDotSevenRequestHandler(socketserver.BaseRequestHandle def handle(self): self.data = self.request.recv(2096).strip() - print( - "{client_ip} wrote: {data}".format( - client_ip=self.client_address[0], - data=self.data - ) - ) - self.request.sendall( - 'valid=1;value= {expected_value};\n'.format( - expected_value=21.7 - ).encode() - ) + print("{client_ip} wrote: {data}".format(client_ip=self.client_address[0], + data=self.data)) + self.request.sendall('valid=1;value= {expected_value};\n'.format(expected_value=21.7).encode()) class TestServer(socketserver.ThreadingMixIn, socketserver.TCPServer): @@ -46,10 +37,11 @@ def start_server(self): def setUp(self): self.start_server() - self.mess_pc_check = MessPCCheck(host=self.host, port=self.port) + self.mess_pc_check = CheckPcMeasure(host=self.host, port=self.port) def tearDown(self): self.stop_server() + self.mess_pc_check.close() del self.mess_pc_check def stop_server(self): @@ -57,40 +49,37 @@ def stop_server(self): self.server.server_close() def test_01_get_value(self): - value = self.mess_pc_check._get_value(b"com1.1") - self.assertEqual( - value, - 21.7 - ) + value = self.mess_pc_check._get_value("com1.1") + self.assertEqual(value, 21.7) def threshold_test(self, parameters): - result = self.mess_pc_check.check( - sensor_name=b"com1.1", - warning_threshold=parameters["warning"], - critical_threshold=parameters["critical"], - ) - self.assertEqual( - result, - parameters["expected"] - ) + try: + label = parameters["label"] + except KeyError: + label = False + result = self.mess_pc_check.check(sensor_name="com1.1", + warning_threshold=parameters["warning"], + critical_threshold=parameters["critical"], + label=label) + self.assertEqual(result, parameters["expected"]) def test_02_check_no_thresholds_exceeded(self): - self.threshold_test({ - "warning": 22.0, - "critical": 23.0, - "expected": NAGIOS_OK - }) + self.threshold_test({"warning": 22.0, "critical": 23.0, "expected": STATUS_OK}) def test_03_check_warning_threshold_exceeded(self): - self.threshold_test({ - "warning": 21.5, - "critical": 23.0, - "expected": NAGIOS_WARNING - }) + self.threshold_test({"warning": 21.5, "critical": 23.0, "expected": STATUS_WARNING}) def test_04_check_critical_threshold_exceeded(self): - self.threshold_test({ - "warning": 20.0, - "critical": 21.0, - "expected": NAGIOS_CRITICAL - }) + self.threshold_test({"warning": 20.0, "critical": 21.0, "expected": STATUS_CRITICAL}) + + def test_05_check_no_thresholds_exceeded_with_label(self): + self.threshold_test({"warning": 22.0, "critical": 23.0, "expected": STATUS_OK, "label": "celsius"}) + + def test_06_check_warning_threshold_exceeded_with_label(self): + self.threshold_test({"warning": 21.5, "critical": 23.0, "expected": STATUS_WARNING, "label": "celsius"}) + + def test_07_check_critical_threshold_exceeded_with_label(self): + self.threshold_test({"warning": 20.0, "critical": 21.0, "expected": STATUS_CRITICAL, "label": "celsius"}) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file