Skip to content

Commit 25fa01c

Browse files
Merge pull request #1 from bithon/master
Implemented perfdata metrics and lots of code improvements. Thanks, @bithon !
2 parents e0bff4c + c939fd4 commit 25fa01c

File tree

6 files changed

+236
-166
lines changed

6 files changed

+236
-166
lines changed

CHANGELOG.md

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Change Log
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to
6+
[Semantic Versioning](http://semver.org/).
7+
8+
## 0.2.0
9+
### Added
10+
- parsed arguments: `label`
11+
- parfdata to output (https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/3/en/perfdata.html)
12+
- exception for UnicodeDecodeError in `CheckPcMeasure.check()`
13+
### Changes
14+
- code is now more generic for NAGIOS and ICINGA
15+
- restructuring the class
16+
17+
# 0.1.0 Initial release

README.md

+41-11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
# Nagios Plugin for MessPC.de Ethernet Boxes
1+
![GitHub release](https://img.shields.io/github/release/mpibpc-mroose/nagios_plugin_pcmeasure.svg)
2+
![GitHub](https://img.shields.io/github/license/mpibpc-mroose/nagios_plugin_pcmeasure.svg?color=blue)
3+
![code coverage 100%](https://img.shields.io/badge/coverage-100%25-brightgreen.svg)
24

3-
[![Build Status](https://travis-ci.org/mpibpc-mroose/nagios_plugin_pcmeasure.svg?branch=master)](https://travis-ci.org/mpibpc-mroose/nagios_plugin_pcmeasure)
5+
# check_pcmeasure.py
6+
Nagios/Icinga2 Plugin for https://messpc.de/ and https://pcmeasure.com Ethernet Boxes written in Python 3
47

58
An implementation for Perl already exists:
69
https://exchange.nagios.org/directory/Plugins/Hardware/Environmental/MessPC--2F-pcmeasure/details
@@ -9,25 +12,52 @@ https://exchange.nagios.org/directory/Plugins/Hardware/Environmental/MessPC--2F-
912

1013
So I decided to implement it's functionality in Python.
1114

12-
There is no warranty for a 100% compatibilty to the old plugin, but
15+
There is no warranty for a 100% compatibility to the old plugin, but
1316
requests for adaptions and error reports are always welcome.
1417

1518
# How to use it
19+
## Example commandline
20+
```
21+
root@icinga-agent01:/usr/lib/nagios/plugins# ./check_pcmeasure.py -H 10.10.200.250 -S com1.1 -w 45 -c 55 -l 'celsius'
22+
OK: 27.8 celsius | celsius=27.8
23+
```
1624

17-
1. copy `check_pcmeasure.py' to your Nagios plugin folder
18-
1. define a nagios command:
25+
## Config files:
26+
- copy `check_pcmeasure.py' to your Nagios/Icinga plugin folder
27+
- define a command:
1928
```
2029
define command{
21-
command_name check_messpc
22-
command_line $USER1$/check_pcmeasure.py -H $HOSTADDRESS$ -w $ARG1$ -c $ARG2$ -S $ARG3
30+
command_name check_pcmeasure
31+
command_line $USER1$/check_pcmeasure.py -H $HOSTADDRESS$ -w $ARG1$ -c $ARG2$ -S $ARG3 -l $ARG4
2332
}
2433
```
25-
1. define a service
34+
- define a service
2635
```
2736
define service{
2837
use generic-service
29-
host_name messpc.example.com
38+
host_name pcmeasure.example.com
3039
service_description temperature sensor
31-
check_command check_messpc!28!30!com1.1
40+
check_command check_pcmeasure!28!30!com1.1!celsius
3241
}
33-
```
42+
```
43+
## Director:
44+
- Icinga Director -> Define Data Fields:
45+
46+
|Label|Field name|
47+
|-----|----------|
48+
|Critical|critical|
49+
|Warning|warning|
50+
|Etherbox address|etherbox_address|
51+
|Etherbox sensor label|etherbox_sensor_label|
52+
|Etherbox sensor name|etherbox_sensor_name|
53+
- Icinga Director -> Commands -> Templates -> Add:
54+
![screenshot](https://s3.gifyu.com/images/check_pcmeasure_command_template.png)
55+
- Click on "Fields":
56+
![screenshot](https://s3.gifyu.com/images/check_pcmeasure_command_template_fields.png)
57+
- Icinga Director -> Commands -> Commands -> Add:
58+
![screenshot](https://s3.gifyu.com/images/check_pcmeasure_command.png)
59+
- Click on "Arguments"
60+
![screenshot](https://s3.gifyu.com/images/check_pcmeasure_command_arguments.png)
61+
- Create a service as usual ...
62+
- Enjoy :)
63+
![screenshot](https://s3.gifyu.com/images/perfdata_icinga.png)

TODO.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# TODO
2+
- Create a release
3+
- Upload to NAGIOS and ICINGA exchange ...

check_pcmeasure.py

100644100755
+111-99
Original file line numberDiff line numberDiff line change
@@ -1,127 +1,139 @@
11
#!/usr/bin/env python3
22
# -*- coding: utf-8 -*-
3+
34
import argparse
5+
import os
46
import re
57
import socket
68
import sys
9+
import textwrap
10+
import time
11+
12+
13+
STATUS_OK = 0
14+
STATUS_WARNING = 1
15+
STATUS_CRITICAL = 2
16+
STATUS_UNKNOWN = 3
17+
18+
19+
class CheckPcMeasure(object):
20+
def __init__(self, host="127.0.0.1", port=4000):
21+
version = "0.2.0"
22+
if __name__ == '__main__':
23+
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
24+
description=textwrap.dedent(os.path.basename(__file__) + " " + version +
25+
" (GPLv3) 2018 - " + time.strftime("%Y")),
26+
epilog=textwrap.dedent("GitHub: https://github.com/mpibpc-mroose/"
27+
"nagios_plugin_pcmeasure"))
28+
parser.add_argument("-H", dest="host", type=str, required=True, help="hostname of the etherbox")
29+
parser.add_argument("-p", type=int, dest="port", default=4000, required=False, help="port to use")
30+
parser.add_argument("-S", dest="sensor_name", type=str, required=True, help="sensor name, e.g. com1.1")
31+
parser.add_argument("-w", dest="warning_threshold", type=float, required=True, help="warning threshold")
32+
parser.add_argument("-c", dest="critical_threshold", type=float, required=True, help="critical threshold")
33+
parser.add_argument("-l", dest="label", type=str, required=False, help="label for perfdata, e.g. 'celsius'")
734

8-
NAGIOS_OK = 0
9-
NAGIOS_WARNING = 1
10-
NAGIOS_CRITICAL = 2
11-
NAGIOS_UNKNOWN = 3
35+
self.arguments = parser.parse_args()
36+
self.critical_threshold = self.arguments.critical_threshold
37+
self.warning_threshold = self.arguments.warning_threshold
38+
self.host = self.arguments.host
39+
self.port = self.arguments.port
40+
self.sensor_name = self.arguments.sensor_name
1241

42+
if self.arguments.label:
43+
self.label_name = " " + self.arguments.label
44+
self.label_perfdata = self.arguments.label
45+
else:
46+
self.label_name = ""
47+
self.label_perfdata = "sensor_output"
1348

14-
class MessPCCheck(object):
15-
def __init__(self, host, port=4000):
16-
self.host = host
17-
self.port = port
18-
self.socket = self.connect()
49+
if self.warning_threshold > self.critical_threshold:
50+
print("UNKNOWN: warning threshold should be lower than critical threshold")
51+
sys.exit(STATUS_UNKNOWN)
52+
else:
53+
self.label_name = ""
54+
self.label_perfdata = "sensor_output"
55+
self.critical_threshold = False
56+
self.warning_threshold = False
57+
self.host = host
58+
self.port = port
59+
self.sensor_name = False
60+
self.socket = self._connect()
1961

20-
def connect(self):
62+
def _connect(self):
2163
_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2264
_socket.settimeout(0.5)
23-
_socket.connect((self.host, self.port))
65+
try:
66+
_socket.connect((self.host, self.port))
67+
except socket.timeout:
68+
print("UNKNOWN: timeout on connect to {host}:{port}".format(host=self.host,
69+
port=self.port))
70+
sys.exit(STATUS_UNKNOWN)
71+
except ConnectionRefusedError as error:
72+
print("UNKNOWN: {error} by {host}:{port}".format(error=str(error),
73+
host=self.host,
74+
port=self.port))
75+
sys.exit(STATUS_UNKNOWN)
2476
return _socket
2577

78+
def close(self):
79+
self.socket.close()
80+
2681
def __delete__(self, instance):
2782
self.socket.close()
2883

29-
def _get_value(self, sensor_name: bytes) -> float:
30-
self.socket.send(
31-
b'pcmeasure.' + sensor_name + b'\n'
32-
)
84+
def _get_value(self, sensor_name) -> float:
85+
self.socket.send(b'pcmeasure.' + bytes(sensor_name, 'utf-8') + b'\n')
3386
raw_data = self.socket.recv(2096).decode()
3487
sensor_reply_pattern = re.compile(r"value=\s(?P<value>.+);")
3588
return float(sensor_reply_pattern.findall(raw_data)[0])
3689

37-
def check(self, sensor_name: bytes, warning_threshold: float, critical_threshold: float) -> int:
90+
def check(self, sensor_name=False, warning_threshold=False, critical_threshold=False, label=False) -> int:
91+
if sensor_name:
92+
self.sensor_name = sensor_name
93+
if warning_threshold:
94+
self.warning_threshold = warning_threshold
95+
if critical_threshold:
96+
self.critical_threshold = critical_threshold
97+
if label:
98+
self.label_name = " " + label
99+
self.label_perfdata = label
38100
try:
39-
value = self._get_value(sensor_name=sensor_name)
40-
if value > critical_threshold:
41-
print("CRITICAL: {value} exceeds {threshold}".format(
42-
value=value,
43-
threshold=critical_threshold
44-
))
45-
return NAGIOS_CRITICAL
46-
elif value > warning_threshold:
47-
print("WARNING: {value} exceeds {threshold}".format(
48-
value=value,
49-
threshold=warning_threshold
50-
))
51-
return NAGIOS_WARNING
101+
value = self._get_value(sensor_name=self.sensor_name)
102+
if value > self.critical_threshold:
103+
print("CRITICAL: {value} exceeds {threshold} | "
104+
"{label_perfdata}={value};;;0".format(value=value,
105+
label=self.label_name,
106+
label_perfdata=self.label_perfdata,
107+
threshold=self.critical_threshold))
108+
return STATUS_CRITICAL
109+
elif value > self.warning_threshold:
110+
print("WARNING: {value} exceeds {threshold} | "
111+
"{label_perfdata}={value};;;0".format(value=value,
112+
label=self.label_name,
113+
label_perfdata=self.label_perfdata,
114+
threshold=self.warning_threshold))
115+
return STATUS_WARNING
52116
else:
53-
print("OK: {value}".format(
54-
value=value
55-
))
56-
return NAGIOS_OK
117+
print("OK: {value}{label} | {label_perfdata}={value};;;0".format(value=value,
118+
label_perfdata=self.label_perfdata,
119+
label=self.label_name))
120+
return STATUS_OK
121+
except UnicodeDecodeError:
122+
print("UNKNOWN: can not read from sensor '{sensor_name}'! "
123+
"use {app_name} -h for further information!".format(sensor_name=self.sensor_name,
124+
app_name=os.path.basename(__file__)))
125+
return STATUS_UNKNOWN
57126
except Exception as error:
58127
print("UNKNOWN: " + str(error))
59-
return NAGIOS_UNKNOWN
128+
return STATUS_UNKNOWN
129+
130+
def check_command(self):
131+
return_code = self.check(sensor_name=self.sensor_name,
132+
warning_threshold=self.warning_threshold,
133+
critical_threshold=self.critical_threshold)
134+
sys.exit(return_code)
60135

61136

62137
if __name__ == '__main__':
63-
parser = argparse.ArgumentParser()
64-
"""
65-
check_pcmeasure2.pl -H <host> -S <sensor>[,<sensor] [-p <port>]
66-
[-w <threshold>] [-c <threshold>]
67-
"""
68-
parser.add_argument(
69-
"-H",
70-
dest="host",
71-
type=str,
72-
required=True,
73-
help="hostname of the MessPC unit"
74-
)
75-
parser.add_argument(
76-
"-p",
77-
type=int,
78-
dest="port",
79-
default=4000,
80-
required=False,
81-
help="port to use for communication"
82-
)
83-
parser.add_argument(
84-
"-S",
85-
dest="sensor_name",
86-
type=str,
87-
required=True,
88-
help="sensor name, e.g. com1.1"
89-
)
90-
parser.add_argument(
91-
"-w",
92-
dest="warning_threshold",
93-
type=float,
94-
required=True,
95-
help="warning threshold"
96-
)
97-
parser.add_argument(
98-
"-c",
99-
dest="critical_threshold",
100-
type=float,
101-
required=True,
102-
help="critical threshold"
103-
)
104-
arguments = parser.parse_args()
105-
106-
if arguments.warning_threshold > arguments.critical_threshold:
107-
print("UNKNOWN: warning threshold should be lower than critical threshold")
108-
sys.exit(NAGIOS_UNKNOWN)
109-
110-
try:
111-
mess_pc = MessPCCheck(
112-
host=arguments.host, port=arguments.port
113-
)
114-
except TimeoutError:
115-
print(
116-
"CRITICAL: timeout on connect to {host}:{port}".format(
117-
host=arguments.host,
118-
port=arguments.port
119-
))
120-
sys.exit(NAGIOS_CRITICAL)
121-
122-
return_code = mess_pc.check(
123-
sensor_name=arguments.sensor_name.encode(),
124-
warning_threshold=arguments.warning_threshold,
125-
critical_threshold=arguments.critical_threshold
126-
)
127-
sys.exit(return_code)
138+
check_pcmeasure = CheckPcMeasure()
139+
check_pcmeasure.check_command()

setup.py

+28-9
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,43 @@
1-
import sys
2-
import os
3-
from setuptools import setup
1+
#!/usr/bin/env python3
42

5-
version = '0.1.0'
3+
import setuptools
64

7-
setup(
8-
name='nagios_plugin_pcmeasure',
5+
6+
version = '0.2.0'
7+
8+
with open("README.md", "r") as fh:
9+
long_description = fh.read()
10+
11+
setuptools.setup(
12+
name='check__pcmeasure.py',
913
version=version,
1014
author='Dr. Marco Roose',
1115
author_email='marco.roose@mpibpc.mpg.de',
1216
url='https://github.com/mpibpc-mroose/nagios_plugin_pcmeasure/',
1317
license='GPL-3.0',
14-
description='Nagios Plugin for ethernet sensor boxes from http://messpc.de',
15-
long_description='Please see our GitHub README',
18+
description='Nagios/Icinga2 Plugin for ethernet sensor boxes from http://messpc.de and http://pcmeasure.com',
19+
long_description=long_description,
20+
long_description_content_type="text/markdown",
1621
install_requires=[],
22+
python_requires='>=3.5',
23+
packages=setuptools.find_packages(),
24+
project_urls={
25+
'Source': 'https://github.com/mpibpc-mroose/nagios_plugin_pcmeasure/',
26+
'Nagios Exchange': 'https://exchange.nagios.org/',
27+
'Icinga Exchange': 'https://exchange.icinga.com',
28+
},
1729
keywords=[
1830
'Nagios',
31+
'Icinga2',
32+
'pcmeasure',
33+
'messpc',
34+
'etherbox'
1935
],
2036
classifiers=[
37+
"Development Status :: 5 - Production/Stable",
38+
"Operating System :: OS Independent",
2139
'Programming Language :: Python :: 3.5',
22-
'Programming Language :: Python :: 3.6'
40+
'Programming Language :: Python :: 3.6',
41+
'Programming Language :: Python :: 3.7'
2342
]
2443
)

0 commit comments

Comments
 (0)