]> git.ipfire.org Git - collecty.git/blobdiff - src/collecty/plugins/latency.py
latency: Rewrite latency module
[collecty.git] / src / collecty / plugins / latency.py
index df6710216181145252772b0cf4e86c24554e1cc1..a219240fbfd3eb4a955867244d1f7559096ef1e8 100644 (file)
@@ -19,8 +19,9 @@
 #                                                                             #
 ###############################################################################
 
-import collecty.ping
+import socket
 
+import collecty._collecty
 from . import base
 
 from ..i18n import _
@@ -37,86 +38,124 @@ class GraphTemplateLatency(base.GraphTemplate):
        @property
        def rrd_graph(self):
                return [
-                       "DEF:latency=%(file)s:latency:AVERAGE",
-                       "DEF:latency_loss=%(file)s:latency_loss:AVERAGE",
-                       "DEF:latency_stddev=%(file)s:latency_stddev:AVERAGE",
-
-                       # Compute loss in percentage.
-                       "CDEF:latency_ploss=latency_loss,100,*",
-
-                       # Compute standard deviation.
-                       "CDEF:stddev1=latency,latency_stddev,+",
-                       "CDEF:stddev2=latency,latency_stddev,-",
-
-                       "CDEF:l005=latency_ploss,0,5,LIMIT,UN,UNKN,INF,IF",
-                       "CDEF:l010=latency_ploss,5,10,LIMIT,UN,UNKN,INF,IF",
-                       "CDEF:l025=latency_ploss,10,25,LIMIT,UN,UNKN,INF,IF",
-                       "CDEF:l050=latency_ploss,25,50,LIMIT,UN,UNKN,INF,IF",
-                       "CDEF:l100=latency_ploss,50,100,LIMIT,UN,UNKN,INF,IF",
-
+                       "DEF:latency6=%(file)s:latency6:AVERAGE",
+                       "DEF:loss6=%(file)s:loss6:AVERAGE",
+                       "DEF:stddev6=%(file)s:stddev6:AVERAGE",
+
+                       "DEF:latency4=%(file)s:latency4:AVERAGE",
+                       "DEF:loss4=%(file)s:loss4:AVERAGE",
+                       "DEF:stddev4=%(file)s:stddev4:AVERAGE",
+
+                       # Compute the biggest loss and convert into percentage
+                       "CDEF:ploss=loss6,loss4,MAX,100,*",
+
+                       # Compute standard deviation
+                       "CDEF:stddevarea6=stddev6,2,*",
+                       "CDEF:spacer6=latency6,stddev6,-",
+                       "CDEF:stddevarea4=stddev4,2,*",
+                       "CDEF:spacer4=latency4,stddev4,-",
+
+                       "CDEF:l005=ploss,0,5,LIMIT,UN,UNKN,INF,IF",
+                       "CDEF:l010=ploss,5,10,LIMIT,UN,UNKN,INF,IF",
+                       "CDEF:l025=ploss,10,25,LIMIT,UN,UNKN,INF,IF",
+                       "CDEF:l050=ploss,25,50,LIMIT,UN,UNKN,INF,IF",
+                       "CDEF:l100=ploss,50,100,LIMIT,UN,UNKN,INF,IF",
+
+                       "VDEF:latency6min=latency6,MINIMUM",
+                       "VDEF:latency6max=latency6,MAXIMUM",
+                       "VDEF:latency6avg=latency6,AVERAGE",
+                       "VDEF:latency4min=latency4,MINIMUM",
+                       "VDEF:latency4max=latency4,MAXIMUM",
+                       "VDEF:latency4avg=latency4,AVERAGE",
+
+                       "LINE1:latency6avg#00ff0066:%s" % _("Average latency (IPv6)"),
+                       "LINE1:latency4avg#ff000066:%s\\r" % _("Average latency (IPv4)"),
+
+                       "COMMENT:%s" % _("Packet Loss"),
                        "AREA:l005#ffffff:%s" % _("0-5%%"),
-                       "AREA:l010#000000:%s" % _("5-10%%"),
-                       "AREA:l025#ff0000:%s" % _("10-25%%"),
-                       "AREA:l050#00ff00:%s" % _("25-50%%"),
-                       "AREA:l100#0000ff:%s" % _("50-100%%") + "\\n",
-
-                       "LINE1:stddev1#00660088",
-                       "LINE1:stddev2#00660088",
-
-                       "LINE3:latency#ff0000:%s" % _("Latency"),
-                       "VDEF:latencymin=latency,MINIMUM",
-                       "VDEF:latencymax=latency,MAXIMUM",
-                       "VDEF:latencyavg=latency,AVERAGE",
-                       "GPRINT:latencymax:%12s\:" % _("Maximum") + " %6.2lf",
-                       "GPRINT:latencymin:%12s\:" % _("Minimum") + " %6.2lf",
-                       "GPRINT:latencyavg:%12s\:" % _("Average") + " %6.2lf\\n",
-
-                       "LINE1:latencyavg#000000:%s" % _("Average latency"),
+                       "AREA:l010#cccccc:%s" % _("5-10%%"),
+                       "AREA:l025#999999:%s" % _("10-25%%"),
+                       "AREA:l050#666666:%s" % _("25-50%%"),
+                       "AREA:l100#333333:%s" % _("50-100%%") + "\\r",
+
+                       "COMMENT: \\n", # empty line
+
+                       "AREA:spacer4",
+                       "AREA:stddevarea4#ff000033:STACK",
+                       "LINE2:latency4#ff0000:%s" % _("Latency (IPv4)"),
+                       "GPRINT:latency4max:%12s\:" % _("Maximum") + " %6.2lf",
+                       "GPRINT:latency4min:%12s\:" % _("Minimum") + " %6.2lf",
+                       "GPRINT:latency4avg:%12s\:" % _("Average") + " %6.2lf\\n",
+
+                       "AREA:spacer6",
+                       "AREA:stddevarea6#00ff0033:STACK",
+                       "LINE2:latency6#00ff00:%s" % _("Latency (IPv6)"),
+                       "GPRINT:latency6max:%12s\:" % _("Maximum") + " %6.2lf",
+                       "GPRINT:latency6min:%12s\:" % _("Minimum") + " %6.2lf",
+                       "GPRINT:latency6avg:%12s\:" % _("Average") + " %6.2lf\\n",
                ]
 
        @property
        def graph_title(self):
-               return _("Latency to %(host)s")
+               return _("Latency to %s") % self.object.hostname
 
        @property
        def graph_vertical_label(self):
                return _("Milliseconds")
 
+       @property
+       def rrd_graph_args(self):
+               return [
+                       "--legend-direction=bottomup",
+               ]
+
 
 class LatencyObject(base.Object):
        rrd_schema = [
-               "DS:latency:GAUGE:0:U",
-               "DS:latency_loss:GAUGE:0:100",
-               "DS:latency_stddev:GAUGE:0:U",
+               "DS:latency6:GAUGE:0:U",
+               "DS:stddev6:GAUGE:0:U",
+               "DS:loss6:GAUGE:0:100",
+               "DS:latency4:GAUGE:0:U",
+               "DS:stddev4:GAUGE:0:U",
+               "DS:loss4:GAUGE:0:100",
        ]
 
        def __repr__(self):
                return "<%s %s>" % (self.__class__.__name__, self.hostname)
 
-       def init(self, hostname, deadline=None):
+       def init(self, hostname):
                self.hostname = hostname
-               self.deadline = deadline
 
        @property
        def id(self):
                return self.hostname
 
        def collect(self):
-               # Send up to five ICMP echo requests.
-               try:
-                       ping = collecty.ping.Ping(destination=self.hostname, timeout=20000)
-                       ping.run(count=5, deadline=self.deadline)
+               result = []
+
+               for family in (socket.AF_INET6, socket.AF_INET):
+                       try:
+                               p = collecty._collecty.Ping(self.hostname, family=family)
+                               p.ping(count=5, deadline=10)
+
+                               result += (p.average, p.stddev, p.loss)
+
+                       except collecty._collecty.PingAddHostError as e:
+                               self.log.debug(_("Could not add host %(host)s for family %(family)s") \
+                                       % { "host" : self.hostname, "family" : family })
 
-               except collecty.ping.PingError as e:
-                       self.log.warning(_("Could not run latency check for %(host)s: %(msg)s") \
-                               % { "host" : self.hostname, "msg" : e.msg })
-                       return
+                               # No data available
+                               result += (None, None, None)
+                               continue
 
-               return (
-                       "%.10f" % ping.avg_time,
-                       "%.10f" % ping.loss,
-                       "%.10f" % ping.stddev,
-               )
+                       except collecty._collecty.PingError as e:
+                               self.log.warning(_("Could not run latency check for %(host)s: %(msg)s") \
+                                       % { "host" : self.hostname, "msg" : e })
+
+                               # A hundred percent loss
+                               result += (None, None, 1)
+
+               return result
 
 
 class LatencyPlugin(base.Plugin):
@@ -127,7 +166,5 @@ class LatencyPlugin(base.Plugin):
 
        @property
        def objects(self):
-               deadline = self.interval / len(PING_HOSTS)
-
                for hostname in PING_HOSTS:
-                       yield LatencyObject(self, hostname, deadline=deadline)
+                       yield LatencyObject(self, hostname)