Add portcheck python plugin
Chris committed
Feb 19, 2018 at 23:56 UTC
b2fb7dd2c70020a16df657570726d221b83950c7
1 file changed
+138
python.d/portcheck.chart.py
new
+138
@@ -0,0 +1,138 @@
1
+# -*- coding: utf-8 -*-
2
+# Description: simple port check netdata python.d module
3
+# Original Author: ccremer (github.com/ccremer)
4
+
5
+import socket
6
+import time
7
+from bases.FrameworkServices.SimpleService import SimpleService
8
+
9
+# default module values (can be overridden per job in `config`)
10
+priority = 60000
11
+retries = 60
12
+
13
+ORDER = ['latency', 'error']
14
+
15
+CHARTS = {
16
+ 'latency': {
17
+ 'options': [None, 'TCP connect latency', 'ms', 'latency', 'portcheck.latency', 'line'],
18
+ 'lines': [
19
+ ['tcp_connect_time', 'connect', 'absolute', 100, 1000]
20
+ ]},
21
+ 'error': {
22
+ 'options': [None, 'Portcheck error code', 'code', 'error', 'portcheck.error', 'line'],
23
+ 'lines': [
24
+ ['tcp_error', 'error', 'absolute']
25
+ ]}
26
+}
27
+
28
+SOCKET_FAILED = -1
29
+CONNECTION_FAILED = -2
30
+CONNECTION_TIMED_OUT = -3
31
+
32
+
33
+class Service(SimpleService):
34
+ def __init__(self, configuration=None, name=None):
35
+ SimpleService.__init__(self, configuration=configuration, name=name)
36
+ self.order = ORDER
37
+ self.definitions = CHARTS
38
+ self.chart_name = ""
39
+ self._sock = None
40
+ self.__socket_config = None
41
+ self.host = self.configuration.get('host', None)
42
+ self.port = self.configuration.get('port', None)
43
+ self.update_every = self.configuration.get('update every', 3)
44
+ self.timeout = self.configuration.get('timeout', 5)
45
+
46
+ def check(self):
47
+ """
48
+ Parse configuration, check if configuration is available, and dynamically create chart lines data
49
+ :return: boolean
50
+ """
51
+ if self.host is None or self.port is None:
52
+ self.error("Host and/or port missing")
53
+ return False
54
+ else:
55
+ self.debug("Enabled portcheck: {host}:{port}, update every {update}s, timeout: {timeout}s".format(
56
+ host=self.host, port=self.port, update=self.update_every, timeout=self.timeout
57
+ ))
58
+ # We will accept any configuration, even if initial connection fails (a service might be down from
59
+ # the beginning)
60
+ return True
61
+
62
+ def _get_data(self):
63
+ """
64
+ Get data from socket
65
+ :return: dict
66
+ """
67
+ data = dict()
68
+
69
+ for res in socket.getaddrinfo(self.host, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM):
70
+ # use first working socket
71
+ if self._create_socket(res):
72
+ self._connect2socket(data)
73
+ self._disconnect()
74
+ return data
75
+
76
+ # We could not connect
77
+ data['tcp_connect_time'] = 0
78
+ data['tcp_error'] = SOCKET_FAILED
79
+ return data
80
+
81
+ def _create_socket(self, res=None):
82
+ af, sock_type, proto, canon_name, sa = res
83
+ try:
84
+ self.debug('Creating socket to "{address}", port {port}'.format(address=sa[0], port=sa[1]))
85
+ self._sock = socket.socket(af, sock_type, proto)
86
+ self.__socket_config = res
87
+ self._sock.settimeout(self.timeout)
88
+ return True
89
+ except socket.error as error:
90
+ self.error('Failed to create socket "{address}", port {port}, error: {error}'.format(
91
+ address=sa[0], port=sa[1], error=error
92
+ ))
93
+ return False
94
+
95
+ def _connect2socket(self, data):
96
+ """
97
+ Connect to a socket, passing the result of getaddrinfo()
98
+ :return: dict
99
+ """
100
+ data['tcp_connect_time'] = 0
101
+ data['tcp_error'] = 0
102
+
103
+ af, sock_type, proto, canon_name, sa = self.__socket_config
104
+ try:
105
+ self.debug('connecting socket to "{address}", port {port}'.format(address=sa[0], port=sa[1]))
106
+ start = time.time()
107
+ self._sock.connect(sa)
108
+ diff = time.time() - start
109
+ self.debug('connected to "{address}", port {port}, latency {latency}'.format(
110
+ address=sa[0], port=sa[1], latency=diff
111
+ ))
112
+ # we will set it at least 0.1 ms. 0.0 would mean failed connection (handy for 3rd-party-APIs)
113
+ data['tcp_connect_time'] = max(round(diff * 10000), 1)
114
+
115
+ except socket.timeout as error:
116
+ self.error('Socket timed out on "{address}", port {port}, error: {error}'.format(
117
+ address=sa[0], port=sa[1], error=error
118
+ ))
119
+ data['tcp_error'] = CONNECTION_TIMED_OUT
120
+
121
+ except socket.error as error:
122
+ self.error('Failed to connect to "{address}", port {port}, error: {error}'.format(
123
+ address=sa[0], port=sa[1], error=error
124
+ ))
125
+ data['tcp_error'] = CONNECTION_FAILED
126
+
127
+ def _disconnect(self):
128
+ """
129
+ Close socket connection
130
+ :return:
131
+ """
132
+ if self._sock is not None:
133
+ try:
134
+ self.debug('closing socket')
135
+ self._sock.shutdown(2) # 0 - read, 1 - write, 2 - all
136
+ self._sock.close()
137
+ except Exception:
138
+ pass