]> arthur.barton.de Git - netdata.git/commitdiff
New `NetSocketService` class + squid.chart.py + fix infinite retries loop
authorpaulfantom <paulfantom@gmail.com>
Mon, 4 Jul 2016 13:28:32 +0000 (15:28 +0200)
committerpaulfantom <paulfantom@gmail.com>
Mon, 4 Jul 2016 13:28:32 +0000 (15:28 +0200)
python.d/Makefile.am
python.d/hddtemp.chart.py
python.d/python_modules/base.py
python.d/squid.chart.py [new file with mode: 0644]

index 2d6d70c18d8d8f8c677bda3cf00ffba81592e6a9..553e56d0760f2388b7713705fa3c672b8b64316d 100644 (file)
@@ -11,6 +11,8 @@ dist_python_SCRIPTS = \
        apache.chart.py \
        nginx.chart.py \
        apache_cache.chart.py \
+       hddtemp.chart.py \
+       squid.chart.py \
        python-modules-installer.sh \
        $(NULL)
 
index 0b9947d3459f709bb5f34bcb5e8cce6dcbfe6b35..1cf8a5fbbfa45c90047d066a9b8fd7e784a0e21e 100644 (file)
@@ -2,8 +2,7 @@
 # Description: hddtemp netdata python.d plugin
 # Author: Pawel Krupa (paulfantom)
 
-from base import SimpleService
-import socket
+from base import NetSocketService
 
 # default module values (can be overridden per job in `config`)
 #update_every = 2
@@ -30,11 +29,12 @@ CHARTS = {
 }
 
 
-class Service(SimpleService):
+class Service(NetSocketService):
     def __init__(self, configuration=None, name=None):
-        self.host = "localhost"
+        NetSocketService.__init__(self, configuration=configuration, name=name)
+        self.request = ""
+        self.host = "127.0.0.1"
         self.port = 7634
-        SimpleService.__init__(self, configuration=configuration, name=name)
         self.order = ORDER
         self.definitions = CHARTS
 
@@ -43,14 +43,7 @@ class Service(SimpleService):
         Get data from TCP/IP socket
         :return: dict
         """
-        try:
-            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-            s.connect((self.host, self.port))
-            raw = s.recv(4096).split("|")[:-1]
-            s.close()
-        except Exception:
-            return None
-
+        raw = self._get_raw_data().split("|")[:-1]
         data = {}
         for i in range(len(raw) // 5):
             try:
@@ -61,18 +54,7 @@ class Service(SimpleService):
         return data
 
     def check(self):
-        if self.name is not None or self.name != str(None):
-            self.name = ""
-        else:
-            self.name = str(self.name)
-        try:
-            self.host = str(self.configuration['host'])
-        except (KeyError, TypeError):
-            self.error("No host specified. Using: '" + self.host + "'")
-        try:
-            self.port = int(self.configuration['port'])
-        except (KeyError, TypeError):
-            self.error("No port specified. Using: '" + str(self.port) + "'")
+        self._parse_config()
 
         data = self._get_data()
         if data is None:
index 7b338c972c2f566cdb60b4435990f6a08e9c51e8..0cc393ca2add03783a49f93d972bf18fcb513889 100644 (file)
@@ -5,6 +5,7 @@
 import time
 import sys
 import os
+import socket
 try:
     from urllib.request import urlopen
 except ImportError:
@@ -132,7 +133,7 @@ class BaseService(threading.Thread):
                 time.sleep(self.timetable['next'] - time.time())
                 self.retries_left = self.retries
             else:
-                self.retries -= 1
+                self.retries_left -= 1
                 if self.retries_left <= 0:
                     msg.error("no more retries. Exiting")
                     return
@@ -411,6 +412,82 @@ class UrlService(SimpleService):
             return False
 
 
+class NetSocketService(SimpleService):
+    def __init__(self, configuration=None, name=None):
+        # definitions are created dynamically in create() method based on 'charts' dictionary. format:
+        # definitions = {
+        #     'chart_name_in_netdata' : [ charts['chart_name_in_netdata']['lines']['name'] ]
+        # }
+        self.host = "localhost"
+        self.port = None
+        self.sock = None
+        self.request = ""
+        SimpleService.__init__(self, configuration=configuration, name=name)
+
+    def _get_raw_data(self):
+        """
+        Get raw data with low-level "socket" module.
+        :return: str
+        """
+        if self.sock is None:
+            try:
+                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+                sock.settimeout(self.update_every)
+                sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+                sock.connect((self.host, self.port))
+            except Exception as e:
+                self.sock = None
+                return None
+
+        try:
+            sock.send(self.request)
+        except Exception:
+            try:
+                sock.shutdown(1)
+                sock.close()
+            except:
+                pass
+            self.sock = None
+            return None
+
+        data = sock.recv(1024)
+        try:
+            while True:
+                buf = sock.recv(1024)
+                if not buf:
+                    break
+                else:
+                    data += buf
+        except:
+            sock.close()
+            return None
+
+        return data.decode()
+
+    def _parse_config(self):
+        """
+        Format configuration data and try to connect to server
+        :return: boolean
+        """
+        if self.name is not None or self.name != str(None):
+            self.name = ""
+        else:
+            self.name = str(self.name)
+        try:
+            self.host = str(self.configuration['host'])
+        except (KeyError, TypeError):
+            self.error("No host specified. Using: '" + self.host + "'")
+        try:
+            self.port = int(self.configuration['port'])
+        except (KeyError, TypeError):
+            self.error("No port specified. Using: '" + str(self.port) + "'")
+        try:
+            self.port = int(self.configuration['request'])
+        except (KeyError, TypeError):
+            self.error("No request specified. Using: '" + str(self.request) + "'")
+        self.request = self.request.encode()
+
+
 class LogService(SimpleService):
     def __init__(self, configuration=None, name=None):
         # definitions are created dynamically in create() method based on 'charts' dictionary. format:
diff --git a/python.d/squid.chart.py b/python.d/squid.chart.py
new file mode 100644 (file)
index 0000000..c1e568f
--- /dev/null
@@ -0,0 +1,115 @@
+# -*- coding: utf-8 -*-
+# Description: squid netdata python.d plugin
+# Author: Pawel Krupa (paulfantom)
+
+from base import NetSocketService
+
+# default module values (can be overridden per job in `config`)
+# update_every = 2
+priority = 60000
+retries = 5
+
+# default job configuration (overridden by python.d.plugin)
+# config = {'local': {
+#             'update_every': update_every,
+#             'retries': retries,
+#             'priority': priority,
+#             'url': 'http://localhost/stub_status'
+#          }}
+
+# charts order (can be overridden if you want less charts, or different order)
+ORDER = ['clients_net', 'clients_requests', 'servers_net', 'servers_requests']
+
+CHARTS = {
+    'clients_net': {
+        'options': [None, "Squid Client Bandwidth", "kilobits / sec", "clients", "squid.clients.net" "area"],
+        'lines': [
+            ["client_http_kbytes_in", "in", "incremental", 8, 1],
+            ["client_http_kbytes_out", "out", "incremental", -8, 1],
+            ["client_http_hit_kbytes_out", "hits", "incremental", -8, 1]
+        ]},
+    'clients_requests': {
+        'options': [None, "Squid Client Requests", "requests / sec", "clients", "squid.clients.requests", 'line'],
+        'lines': [
+            ["client_http_requests", "requests"],
+            ["client_http_hits", "hits"],
+            ["client_http_errors", "errors", "incremental", -1, 1]
+        ]},
+    'servers_net': {
+        'options': [None, "Squid Server Bandwidth", "kilobits / sec", "servers", "squid.servers.net" "area"],
+        'lines': [
+            ["server_all_kbytes_in", "in", "incremental", 8, 1],
+            ["server_all_kbytes_out", "out", "incremental", -8, 1]
+        ]},
+    'servers_requests': {
+        'options': [None, "Squid Server Requests", "requests / sec", "servers", "squid.servers.requests", 'line'],
+        'lines': [
+            ["server_all_requests", "requests"],
+            ["server_all_errors", "errors", "incremental", -1, 1]
+        ]}
+}
+
+
+class Service(NetSocketService):
+    def __init__(self, configuration=None, name=None):
+        NetSocketService.__init__(self, configuration=configuration, name=name)
+        self.request = ""
+        self.host = "localhost"
+        self.port = (3128, 8080)
+        self.order = ORDER
+        self.definitions = CHARTS
+
+    def _get_data(self):
+        """
+        Get data from http request
+        :return: dict
+        """
+        try:
+            raw = self._get_raw_data().split('\n')
+            if "200 OK" not in raw[0]:
+                return None
+            data = {}
+            for row in raw:
+                if row.startswith(("client", "server.all")):
+                    tmp = row.split("=")
+                    data[tmp[0].replace('.', '_').strip(' ')] = int(tmp[1])
+
+            return data
+        except (ValueError, AttributeError):
+            return None
+
+    def check(self):
+        self._parse_config()
+        # format request
+        self.request.decode()
+        if not self.request.startswith("GET"):
+            self.request = "GET " + self.request
+        if not self.request.endswith(" HTTP/1.0\r\n\r\n"):
+            self.request += " HTTP/1.0\r\n\r\n"
+        self.request = self.request.encode()
+
+        # autodetect squid
+        if type(self.port) is tuple:
+            ports = self.port
+            for port in ports:
+                self.port = port
+                urls = ["cache_object://" + self.host + ":" + str(port) + "/counters",
+                        "/squid-internal-mgr/counters"]
+                for url in urls:
+                    tmp = "GET " + url + " HTTP/1.0\r\n\r\n"
+                    self.request = tmp.encode()
+                    if self._get_data() is not None:
+                        return True
+        else:
+            if self._get_data() is not None:
+                return True
+            else:
+                return False
+            
+                
+                
+                
+            
+                
+            
+