]>
git.ipfire.org Git - collecty.git/blob - src/collecty/plugins/base.py
2 ###############################################################################
4 # collecty - A system statistics collection daemon for IPFire #
5 # Copyright (C) 2012 IPFire development team #
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. #
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. #
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/>. #
20 ###############################################################################
22 from __future__
import division
31 from ..constants
import *
38 Returns a list with all automatically registered plugins.
40 return _plugins
.values()
43 def __init__(self
, timeout
, heartbeat
=1):
44 self
.timeout
= timeout
45 self
.heartbeat
= heartbeat
51 def reset(self
, delay
=0):
53 self
.start
= time
.time()
57 # Has this timer been killed?
62 return time
.time() - self
.start
- self
.delay
68 while self
.elapsed
< self
.timeout
and not self
.killed
:
69 time
.sleep(self
.heartbeat
)
71 return self
.elapsed
> self
.timeout
74 class Plugin(threading
.Thread
):
75 # The name of this plugin.
78 # A description for this plugin.
81 # Templates which can be used to generate a graph out of
82 # the data from this data source.
85 # The schema of the RRD database.
89 rra_types
= ["AVERAGE", "MIN", "MAX"]
90 rra_timespans
= [3600, 86400, 604800, 2678400, 31622400]
93 # The default interval of this plugin.
96 # Automatically register all providers.
97 class __metaclass__(type):
98 def __init__(plugin
, name
, bases
, dict):
99 type.__init
__(plugin
, name
, bases
, dict)
101 # The main class from which is inherited is not registered
106 if not all((plugin
.name
, plugin
.description
)):
107 raise RuntimeError(_("Plugin is not properly configured: %s") \
110 _plugins
[plugin
.name
] = plugin
112 def __init__(self
, collecty
, **kwargs
):
113 threading
.Thread
.__init
__(self
, name
=self
.description
)
116 self
.collecty
= collecty
118 # Check if this plugin was configured correctly.
119 assert self
.name
, "Name of the plugin is not set: %s" % self
.name
120 assert self
.description
, "Description of the plugin is not set: %s" % self
.description
121 assert self
.rrd_schema
123 # Initialize the logger.
124 self
.log
= logging
.getLogger("collecty.plugins.%s" % self
.name
)
125 self
.log
.propagate
= 1
129 # Run some custom initialization.
132 # Create the database file.
137 self
.timer
= Timer(self
.interval
)
139 self
.log
.info(_("Successfully initialized (%s).") % self
.id)
142 return "<%s %s>" % (self
.__class
__.__name
__, self
.id)
147 A unique ID of the plugin instance.
154 Returns the interval in milliseconds, when the read method
155 should be called again.
157 # XXX read this from the settings
159 # Otherwise return the default.
160 return self
.default_interval
168 return self
.stepsize
* 2
173 The absolute path to the RRD file of this plugin.
175 return os
.path
.join(DATABASE_DIR
, "%s.rrd" % self
.id)
179 Creates an empty RRD file with the desired data structures.
181 # Skip if the file does already exist.
182 if os
.path
.exists(self
.file):
185 dirname
= os
.path
.dirname(self
.file)
186 if not os
.path
.exists(dirname
):
189 # Create argument list.
190 args
= self
.get_rrd_schema()
192 rrdtool
.create(self
.file, *args
)
194 self
.log
.debug(_("Created RRD file %s.") % self
.file)
196 self
.log
.debug(" %s" % arg
)
198 def get_rrd_schema(self
):
200 "--step", "%s" % self
.stepsize
,
202 for line
in self
.rrd_schema
:
203 if line
.startswith("DS:"):
205 (prefix
, name
, type, lower_limit
, upper_limit
) = line
.split(":")
211 "%s" % self
.heartbeat
,
223 for rra_timespan
in self
.rra_timespans
:
224 if (rra_timespan
/ self
.stepsize
) < self
.rra_rows
:
225 rra_timespan
= self
.stepsize
* self
.rra_rows
230 cdp_length
= rra_timespan
// (self
.rra_rows
* self
.stepsize
)
232 cdp_number
= math
.ceil(rra_timespan
/ (cdp_length
* self
.stepsize
))
234 for rra_type
in self
.rra_types
:
235 schema
.append("RRA:%s:%.10f:%d:%d" % \
236 (rra_type
, xff
, cdp_length
, cdp_number
))
241 return rrdtool
.info(self
.file)
245 def init(self
, **kwargs
):
247 Do some custom initialization stuff here.
253 Gathers the statistical data, this plugin collects.
255 raise NotImplementedError
259 Flushes the read data to disk.
261 # Do nothing in case there is no data to submit.
265 self
.log
.debug(_("Submitting data to database. %d entries.") % len(self
.data
))
266 for data
in self
.data
:
267 self
.log
.debug(" %s" % data
)
269 # Create the RRD files (if they don't exist yet or
270 # have vanished for some reason).
273 rrdtool
.update(self
.file, *self
.data
)
276 def _read(self
, *args
, **kwargs
):
278 This method catches errors from the read() method and logs them.
280 start_time
= time
.time()
283 data
= self
.read(*args
, **kwargs
)
285 self
.log
.warning(_("Received empty data."))
287 self
.data
.append("%d:%s" % (start_time
, data
))
289 # Catch any exceptions, so collecty does not crash.
291 self
.log
.critical(_("Unhandled exception in read()!"), exc_info
=True)
293 # Return the elapsed time since _read() has been called.
294 return (time
.time() - start_time
)
296 def _submit(self
, *args
, **kwargs
):
298 This method catches errors from the submit() method and logs them.
301 return self
.submit(*args
, **kwargs
)
303 # Catch any exceptions, so collecty does not crash.
305 self
.log
.critical(_("Unhandled exception in submit()!"), exc_info
=True)
308 self
.log
.debug(_("Started."))
314 # Wait until the timer has successfully elapsed.
315 if self
.timer
.wait():
316 self
.log
.debug(_("Collecting..."))
319 self
.timer
.reset(delay
)
322 self
.log
.debug(_("Stopped."))
325 self
.log
.debug(_("Received shutdown signal."))
328 # Kill any running timers.
333 class GraphTemplate(object):
334 # A unique name to identify this graph template.
337 # Instructions how to create the graph.
340 # Extra arguments passed to rrdgraph.
343 def __init__(self
, ds
):
348 return self
.ds
.collecty
350 def graph(self
, file, interval
=None,
351 width
=GRAPH_DEFAULT_WIDTH
, height
=GRAPH_DEFAULT_HEIGHT
):
353 "--width", "%d" % width
,
354 "--height", "%d" % height
,
356 args
+= self
.collecty
.graph_default_arguments
357 args
+= self
.rrd_graph_args
367 args
.append("--start")
369 args
.append(intervals
[interval
])
371 args
.append(interval
)
373 info
= { "file" : self
.ds
.file }
374 for item
in self
.rrd_graph
:
376 args
.append(item
% info
)
380 rrdtool
.graph(file, *args
)