@cryptotaxi247 / netdata-1 / commits / b2fb7dd2c

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