]> git.ipfire.org Git - collecty.git/blob - src/collecty/plugins/latency.py
Introduce a colour scheme and fix design of the graphs
[collecty.git] / src / collecty / plugins / latency.py
1 #!/usr/bin/python3
2 ###############################################################################
3 # #
4 # collecty - A system statistics collection daemon for IPFire #
5 # Copyright (C) 2012 IPFire development team #
6 # #
7 # This program is free software: you can redistribute it and/or modify #
8 # it under the terms of the GNU General Public License as published by #
9 # the Free Software Foundation, either version 3 of the License, or #
10 # (at your option) any later version. #
11 # #
12 # This program is distributed in the hope that it will be useful, #
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15 # GNU General Public License for more details. #
16 # #
17 # You should have received a copy of the GNU General Public License #
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
19 # #
20 ###############################################################################
21
22 import socket
23
24 import collecty._collecty
25 from . import base
26
27 from .. import util
28 from ..colours import *
29 from ..i18n import _
30
31 PING_HOSTS = [
32 "ping.ipfire.org",
33 ]
34
35 class GraphTemplateLatency(base.GraphTemplate):
36 name = "latency"
37
38 lower_limit = 0
39
40 @property
41 def rrd_graph(self):
42 _ = self.locale.translate
43
44 colour_bg = AMBER
45 return [
46 # Compute the biggest loss and convert into percentage
47 "CDEF:ploss=loss6,loss4,MAX,100,*",
48
49 # Compute standard deviation
50 "CDEF:stddevarea6=stddev6,2,*",
51 "CDEF:spacer6=latency6,stddev6,-",
52 "CDEF:stddevarea4=stddev4,2,*",
53 "CDEF:spacer4=latency4,stddev4,-",
54
55 "CDEF:l005=ploss,0,5,LIMIT,UN,UNKN,INF,IF",
56 "CDEF:l010=ploss,5,10,LIMIT,UN,UNKN,INF,IF",
57 "CDEF:l025=ploss,10,25,LIMIT,UN,UNKN,INF,IF",
58 "CDEF:l050=ploss,25,50,LIMIT,UN,UNKN,INF,IF",
59 "CDEF:l099=ploss,50,99,LIMIT,UN,UNKN,INF,IF",
60
61 "LINE2:latency6_avg%s:%s" % (
62 util.transparency(COLOUR_IPV6, .5),
63 _("Average latency (IPv6)"),
64 ),
65 "LINE2:latency4_avg%s:%s\\r" % (
66 util.transparency(COLOUR_IPV4, .5),
67 _("Average latency (IPv4)"),
68 ),
69
70 "COMMENT:%s" % _("Packet Loss"),
71 "AREA:l005%s:%s" % (
72 util.transparency(colour_bg, .2), _("0-5%"),
73 ),
74 "AREA:l010%s:%s" % (
75 util.transparency(colour_bg, .4), _("5-10%"),
76 ),
77 "AREA:l025%s:%s" % (
78 util.transparency(colour_bg, .6), _("10-25%"),
79 ),
80 "AREA:l050%s:%s" % (
81 util.transparency(colour_bg, .8), _("25-50%"),
82 ),
83 "AREA:l099%s:%s\\r" % (colour_bg, _("50-99%")),
84
85 "COMMENT: \\n", # empty line
86
87 "AREA:spacer4",
88 "AREA:stddevarea4%s:STACK" % util.lighten(COLOUR_IPV4, STDDEV_OPACITY),
89 "LINE2:latency4%s:%s" % (COLOUR_IPV4, _("Latency (IPv4)")),
90 "GPRINT:latency4_max:%12s\:" % _("Maximum") + " %6.2lf",
91 "GPRINT:latency4_min:%12s\:" % _("Minimum") + " %6.2lf",
92 "GPRINT:latency4_avg:%12s\:" % _("Average") + " %6.2lf\\n",
93
94 "AREA:spacer6",
95 "AREA:stddevarea6%s:STACK" % util.lighten(COLOUR_IPV6, STDDEV_OPACITY),
96 "LINE2:latency6%s:%s" % (COLOUR_IPV6, _("Latency (IPv6)")),
97 "GPRINT:latency6_max:%12s\:" % _("Maximum") + " %6.2lf",
98 "GPRINT:latency6_min:%12s\:" % _("Minimum") + " %6.2lf",
99 "GPRINT:latency6_avg:%12s\:" % _("Average") + " %6.2lf\\n",
100 ]
101
102 @property
103 def graph_title(self):
104 _ = self.locale.translate
105 return _("Latency to %s") % self.object.hostname
106
107 @property
108 def graph_vertical_label(self):
109 _ = self.locale.translate
110 return _("Milliseconds")
111
112 @property
113 def rrd_graph_args(self):
114 return [
115 "--legend-direction=bottomup",
116 ]
117
118
119 class LatencyObject(base.Object):
120 rrd_schema = [
121 "DS:latency6:GAUGE:0:U",
122 "DS:stddev6:GAUGE:0:U",
123 "DS:loss6:GAUGE:0:100",
124 "DS:latency4:GAUGE:0:U",
125 "DS:stddev4:GAUGE:0:U",
126 "DS:loss4:GAUGE:0:100",
127 ]
128
129 def __repr__(self):
130 return "<%s %s>" % (self.__class__.__name__, self.hostname)
131
132 def init(self, hostname):
133 self.hostname = hostname
134
135 @property
136 def id(self):
137 return self.hostname
138
139 def collect(self):
140 result = []
141
142 for family in (socket.AF_INET6, socket.AF_INET):
143 try:
144 p = collecty._collecty.Ping(self.hostname, family=family)
145 p.ping(count=5, deadline=10)
146
147 result += (p.average, p.stddev, p.loss)
148
149 except collecty._collecty.PingAddHostError as e:
150 self.log.debug(_("Could not add host %(host)s for family %(family)s") \
151 % { "host" : self.hostname, "family" : family })
152
153 # No data available
154 result += (None, None, None)
155 continue
156
157 except collecty._collecty.PingError as e:
158 self.log.warning(_("Could not run latency check for %(host)s: %(msg)s") \
159 % { "host" : self.hostname, "msg" : e })
160
161 # A hundred percent loss
162 result += (None, None, 1)
163
164 return result
165
166
167 class LatencyPlugin(base.Plugin):
168 name = "latency"
169 description = "Latency (ICMP ping) Plugin"
170
171 templates = [GraphTemplateLatency]
172
173 @property
174 def objects(self):
175 for hostname in PING_HOSTS:
176 yield LatencyObject(self, hostname)