fbf715ab150dea49b1cf2749211bdadd42a0a685
[collecty.git] / src / collecty / plugins / interface.py
1 #!/usr/bin/python
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 from __future__ import division
23
24 import os
25
26 import base
27
28 from ..i18n import _
29
30 SYS_CLASS_NET = "/sys/class/net"
31
32 COLOUR_RX = "B22222"
33 COLOUR_RX_AREA = "%sAA" % COLOUR_RX
34 COLOUR_TX = "228B22"
35 COLOUR_TX_AREA = "%sAA" % COLOUR_TX
36
37 class GraphTemplateInterfaceBits(base.GraphTemplate):
38         name = "interface-bits"
39
40         @property
41         def rrd_graph(self):
42                 return [
43                         "DEF:bytes_rx=%(file)s:bytes_rx:AVERAGE",
44                         "DEF:bytes_tx=%(file)s:bytes_tx:AVERAGE",
45
46                         # Convert everything into bits.
47                         "CDEF:bits_rx=bytes_rx,8,*",
48                         "CDEF:bits_tx=bytes_tx,8,*",
49
50                         # Compute 95% lines.
51                         "VDEF:bits_rx_95p=bits_rx,95,PERCENT",
52                         "VDEF:bits_tx_95p=bits_tx,95,PERCENT",
53
54                         # Draw the received area.
55                         "AREA:bits_rx#%s:%-15s" % (COLOUR_RX_AREA, _("Received")),
56                         "VDEF:bits_rx_min=bits_rx,MINIMUM",
57                         "VDEF:bits_rx_max=bits_rx,MAXIMUM",
58                         "VDEF:bits_rx_avg=bits_rx,AVERAGE",
59                         "GPRINT:bits_rx_max:%12s\: " % _("Maximum") + _("%8.2lf %sbps"),
60                         "GPRINT:bits_rx_min:%12s\: " % _("Minimum") + _("%8.2lf %sbps"),
61                         "GPRINT:bits_rx_avg:%12s\: " % _("Average") + _("%8.2lf %sbps") + "\\n",
62
63                         # Draw the transmitted area.
64                         "AREA:bits_tx#%s:%-15s" % (COLOUR_TX_AREA, _("Transmitted")),
65                         "VDEF:bits_tx_min=bits_tx,MINIMUM",
66                         "VDEF:bits_tx_max=bits_tx,MAXIMUM",
67                         "VDEF:bits_tx_avg=bits_tx,AVERAGE",
68                         "GPRINT:bits_tx_max:%12s\: " % _("Maximum") + _("%8.2lf %sbps"),
69                         "GPRINT:bits_tx_min:%12s\: " % _("Minimum") + _("%8.2lf %sbps"),
70                         "GPRINT:bits_tx_avg:%12s\: " % _("Average") + _("%8.2lf %sbps") + "\\n",
71
72                         # Draw outlines.
73                         "LINE1:bits_rx#%s" % COLOUR_RX,
74                         "LINE1:bits_tx#%s" % COLOUR_TX,
75
76                         # Draw the 95% lines.
77                         "COMMENT:--- %s ---\\n" % _("95th percentile"),
78                         "LINE2:bits_rx_95p#%s:%-15s" % (COLOUR_RX, _("Received")),
79                         "GPRINT:bits_rx_95p:%s" % _("%8.2lf %sbps") + "\\n",
80                         "LINE2:bits_tx_95p#%s:%-15s" % (COLOUR_TX, _("Transmitted")),
81                         "GPRINT:bits_tx_95p:%s" % _("%8.2lf %sbps") + "\\n",
82                 ]
83
84         @property
85         def rrd_graph_args(self):
86                 return [
87                         "--title", _("Bandwidth usage on %(interface)s"),
88                         "--vertical-label", _("Bit/s"),
89                 ]
90
91
92 class GraphTemplateInterfacePackets(base.GraphTemplate):
93         name = "interface-packets"
94
95         @property
96         def rrd_graph(self):
97                 return [
98                         "DEF:packets_rx=%(file)s:packets_rx:AVERAGE",
99                         "DEF:packets_tx=%(file)s:packets_tx:AVERAGE",
100
101                         # Draw the received area.
102                         "AREA:packets_rx#%s:%-15s" % (COLOUR_RX_AREA, _("Received")),
103                         "VDEF:packets_rx_min=packets_rx,MINIMUM",
104                         "VDEF:packets_rx_max=packets_rx,MAXIMUM",
105                         "VDEF:packets_rx_avg=packets_rx,AVERAGE",
106                         "GPRINT:packets_rx_max:%12s\: " % _("Maximum") + _("%8.0lf %spps"),
107                         "GPRINT:packets_rx_min:%12s\: " % _("Minimum") + _("%8.0lf %spps"),
108                         "GPRINT:packets_rx_avg:%12s\: " % _("Average") + _("%8.2lf %spps") + "\\n",
109
110                         # Draw the transmitted area.
111                         "AREA:packets_tx#%s:%-15s" % (COLOUR_TX_AREA, _("Transmitted")),
112                         "VDEF:packets_tx_min=packets_tx,MINIMUM",
113                         "VDEF:packets_tx_max=packets_tx,MAXIMUM",
114                         "VDEF:packets_tx_avg=packets_tx,AVERAGE",
115                         "GPRINT:packets_tx_max:%12s\: " % _("Maximum") + _("%8.0lf %spps"),
116                         "GPRINT:packets_tx_min:%12s\: " % _("Minimum") + _("%8.0lf %spps"),
117                         "GPRINT:packets_tx_avg:%12s\: " % _("Average") + _("%8.2lf %spps") + "\\n",
118
119                         # Draw outlines of the areas on top.
120                         "LINE1:packets_rx#%s" % COLOUR_RX,
121                         "LINE1:packets_tx#%s" % COLOUR_TX,
122                 ]
123
124         @property
125         def rrd_graph_args(self):
126                 return [
127                         "--title", _("Transferred packets on %(interface)s"),
128                         "--vertical-label", _("Packets/s"),
129                 ]
130
131
132 class GraphTemplateInterfaceErrors(base.GraphTemplate):
133         name = "interface-errors"
134
135         @property
136         def rrd_graph(self):
137                 return [
138                         "DEF:errors_rx=%(file)s:errors_rx:AVERAGE",
139                         "DEF:errors_tx=%(file)s:errors_tx:AVERAGE",
140                         "DEF:dropped_rx=%(file)s:dropped_rx:AVERAGE",
141                         "DEF:dropped_tx=%(file)s:dropped_tx:AVERAGE",
142                         "DEF:collisions=%(file)s:collisions:AVERAGE",
143
144                         # Invert the transmitted packets to create upside down graph.
145                         "CDEF:errors_tx_inv=errors_tx,-1,*",
146                         "CDEF:dropped_tx_inv=dropped_tx,-1,*",
147
148                         # Draw the receive errors.
149                         "AREA:errors_rx#228B2277:%-15s" % _("Receive errors"),
150                         "VDEF:errors_rx_min=errors_rx,MINIMUM",
151                         "VDEF:errors_rx_max=errors_rx,MAXIMUM",
152                         "VDEF:errors_rx_avg=errors_rx,AVERAGE",
153                         "GPRINT:errors_rx_max:%12s\: " % _("Maximum") + _("%8.0lf %spps"),
154                         "GPRINT:errors_rx_min:%12s\: " % _("Minimum") + _("%8.0lf %spps"),
155                         "GPRINT:errors_rx_avg:%12s\: " % _("Average") + _("%8.2lf %spps") + "\\n",
156                         "LINE1:errors_rx#228B22",
157
158                         # Draw the transmit errors.
159                         "AREA:errors_tx_inv#B2222277:%-15s" % _("Transmit errors"),
160                         "VDEF:errors_tx_min=errors_tx,MINIMUM",
161                         "VDEF:errors_tx_max=errors_tx,MAXIMUM",
162                         "VDEF:errors_tx_avg=errors_tx,AVERAGE",
163                         "GPRINT:errors_tx_max:%12s\: " % _("Maximum") + _("%8.0lf %spps"),
164                         "GPRINT:errors_tx_min:%12s\: " % _("Minimum") + _("%8.0lf %spps"),
165                         "GPRINT:errors_tx_avg:%12s\: " % _("Average") + _("%8.2lf %spps") + "\\n",
166                         "LINE1:errors_tx_inv#B22222",
167
168                         # Draw the receive drops.
169                         "LINE2:dropped_rx#228B22:%-15s" % _("Receive drops"),
170                         "VDEF:dropped_rx_min=dropped_rx,MINIMUM",
171                         "VDEF:dropped_rx_max=dropped_rx,MAXIMUM",
172                         "VDEF:dropped_rx_avg=dropped_rx,AVERAGE",
173                         "GPRINT:dropped_rx_max:%12s\: " % _("Maximum") + _("%8.0lf %spps"),
174                         "GPRINT:dropped_rx_min:%12s\: " % _("Minimum") + _("%8.0lf %spps"),
175                         "GPRINT:dropped_rx_avg:%12s\: " % _("Average") + _("%8.2lf %spps") + "\\n",
176                         "LINE1:dropped_rx#228B22",
177
178                         # Draw the transmit drops.
179                         "LINE2:dropped_tx#B22222:%-15s" % _("Transmit drops"),
180                         "VDEF:dropped_tx_min=dropped_tx,MINIMUM",
181                         "VDEF:dropped_tx_max=dropped_tx,MAXIMUM",
182                         "VDEF:dropped_tx_avg=dropped_tx,AVERAGE",
183                         "GPRINT:dropped_tx_max:%12s\: " % _("Maximum") + _("%8.0lf %spps"),
184                         "GPRINT:dropped_tx_min:%12s\: " % _("Minimum") + _("%8.0lf %spps"),
185                         "GPRINT:dropped_tx_avg:%12s\: " % _("Average") + _("%8.2lf %spps") + "\\n",
186                         "LINE1:dropped_tx#B22222",
187
188                         # Draw the collisions as a line.
189                         "LINE3:collisions#8B0000:%-15s" % _("Collisions") + "\\n",
190                 ]
191
192         @property
193         def rrd_graph_args(self):
194                 return [
195                         "--title", _("Errors/dropped packets on %(interface)s"),
196                         "--vertical-label", _("Packets/s"),
197                 ]
198
199
200 class InterfacePlugin(base.Plugin):
201         name = "interface"
202         description = "Interface Statistics Data Source"
203
204         templates = [
205                 GraphTemplateInterfaceBits,
206                 GraphTemplateInterfacePackets,
207                 GraphTemplateInterfaceErrors,
208         ]
209
210         rrd_schema = [
211                 "DS:bytes_rx:DERIVE:0:U",
212                 "DS:bytes_tx:DERIVE:0:U",
213                 "DS:collisions:DERIVE:0:U",
214                 "DS:dropped_rx:DERIVE:0:U",
215                 "DS:dropped_tx:DERIVE:0:U",
216                 "DS:errors_rx:DERIVE:0:U",
217                 "DS:errors_tx:DERIVE:0:U",
218                 "DS:multicast:DERIVE:0:U",
219                 "DS:packets_rx:DERIVE:0:U",
220                 "DS:packets_tx:DERIVE:0:U",
221         ]
222
223         @classmethod
224         def autocreate(cls, collecty, **kwargs):
225                 if not os.path.exists(SYS_CLASS_NET):
226                         return
227
228                 instances = []
229                 for interface in os.listdir(SYS_CLASS_NET):
230                         # Skip some unwanted interfaces.
231                         if interface == "lo" or interface.startswith("mon."):
232                                 continue
233
234                         path = os.path.join(SYS_CLASS_NET, interface)
235                         if not os.path.isdir(path):
236                                 continue
237
238                         instance = cls(collecty, interface=interface)
239                         instances.append(instance)
240
241                 return instances
242
243         def init(self, **kwargs):
244                 self.interface = kwargs.get("interface")
245
246         @property
247         def id(self):
248                 return "-".join((self.name, self.interface))
249
250         def read(self):
251                 interface_path = os.path.join(SYS_CLASS_NET, self.interface)
252
253                 # Check if the interface exists.
254                 if not os.path.exists(interface_path):
255                         self.log.debug(_("Interface %s does not exists. Cannot collect.") \
256                                 % self.interface)
257                         return
258
259                 files = (
260                         "rx_bytes", "tx_bytes",
261                         "collisions",
262                         "rx_dropped", "tx_dropped",
263                         "rx_errors", "tx_errors",
264                         "multicast",
265                         "rx_packets", "tx_packets",
266                 )
267                 ret = []
268
269                 for file in files:
270                         path = os.path.join(interface_path, "statistics", file)
271
272                         # Open file and read it's content.
273                         f = None
274                         try:
275                                 f = open(path)
276
277                                 line = f.readline()
278                                 line = line.strip()
279                                 ret.append(line)
280                         except:
281                                 ret.append("0")
282                                 raise
283
284                         finally:
285                                 if f:
286                                         f.close()
287
288                 return ":".join(ret)