]>
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
32 from ..constants
import *
39 Returns a list with all automatically registered plugins.
41 return _plugins
.values()
44 def __init__(self
, timeout
, heartbeat
=1):
45 self
.timeout
= timeout
46 self
.heartbeat
= heartbeat
52 def reset(self
, delay
=0):
54 self
.start
= time
.time()
58 # Has this timer been killed?
63 return time
.time() - self
.start
- self
.delay
69 while self
.elapsed
< self
.timeout
and not self
.killed
:
70 time
.sleep(self
.heartbeat
)
72 return self
.elapsed
> self
.timeout
75 class Plugin(threading
.Thread
):
76 # The name of this plugin.
79 # A description for this plugin.
82 # Templates which can be used to generate a graph out of
83 # the data from this data source.
86 # The default interval for all plugins
89 # Automatically register all providers.
90 class __metaclass__(type):
91 def __init__(plugin
, name
, bases
, dict):
92 type.__init
__(plugin
, name
, bases
, dict)
94 # The main class from which is inherited is not registered
99 if not all((plugin
.name
, plugin
.description
)):
100 raise RuntimeError(_("Plugin is not properly configured: %s") \
103 _plugins
[plugin
.name
] = plugin
105 def __init__(self
, collecty
, **kwargs
):
106 threading
.Thread
.__init
__(self
, name
=self
.description
)
109 self
.collecty
= collecty
111 # Check if this plugin was configured correctly.
112 assert self
.name
, "Name of the plugin is not set: %s" % self
.name
113 assert self
.description
, "Description of the plugin is not set: %s" % self
.description
115 # Initialize the logger.
116 self
.log
= logging
.getLogger("collecty.plugins.%s" % self
.name
)
117 self
.log
.propagate
= 1
121 # Run some custom initialization.
126 self
.timer
= Timer(self
.interval
)
128 self
.log
.info(_("Successfully initialized %s") % self
.__class
__.__name
__)
133 Returns the name of the sub directory in which all RRD files
134 for this plugin should be stored in.
140 def init(self
, **kwargs
):
142 Do some custom initialization stuff here.
148 Gathers the statistical data, this plugin collects.
150 time_start
= time
.time()
152 # Run through all objects of this plugin and call the collect method.
153 for o
in self
.objects
:
154 now
= datetime
.datetime
.utcnow()
158 self
.log
.warning(_("Unhandled exception in %s.collect()") % o
, exc_info
=True)
162 self
.log
.warning(_("Received empty result: %s") % o
)
165 self
.log
.debug(_("Collected %s: %s") % (o
, result
))
167 # Add the object to the write queue so that the data is written
168 # to the databases later.
169 self
.collecty
.write_queue
.add(o
, now
, result
)
171 # Returns the time this function took to complete.
172 return (time
.time() - time_start
)
175 self
.log
.debug(_("%s plugin has started") % self
.name
)
177 # Initially collect everything
184 # Wait until the timer has successfully elapsed.
185 if self
.timer
.wait():
186 delay
= self
.collect()
187 self
.timer
.reset(delay
)
189 self
.log
.debug(_("%s plugin has stopped") % self
.name
)
192 self
.log
.debug(_("Received shutdown signal."))
195 # Kill any running timers.
200 class Object(object):
201 # The schema of the RRD database.
205 rra_types
= ["AVERAGE", "MIN", "MAX"]
206 rra_timespans
= [3600, 86400, 604800, 2678400, 31622400]
209 def __init__(self
, plugin
, *args
, **kwargs
):
212 # Indicates if this object has collected its data
213 self
.collected
= False
215 # Initialise this object
216 self
.init(*args
, **kwargs
)
218 # Create the database file.
222 return "<%s>" % self
.__class
__.__name
__
226 return self
.plugin
.collecty
230 return self
.plugin
.log
235 Returns a UNIQUE identifier for this object. As this is incorporated
236 into the path of RRD file, it must only contain ASCII characters.
238 raise NotImplementedError
243 The absolute path to the RRD file of this plugin.
245 return os
.path
.join(DATABASE_DIR
, self
.plugin
.path
, "%s.rrd" % self
.id)
249 def init(self
, *args
, **kwargs
):
251 Do some custom initialization stuff here.
257 Creates an empty RRD file with the desired data structures.
259 # Skip if the file does already exist.
260 if os
.path
.exists(self
.file):
263 dirname
= os
.path
.dirname(self
.file)
264 if not os
.path
.exists(dirname
):
267 # Create argument list.
268 args
= self
.get_rrd_schema()
270 rrdtool
.create(self
.file, *args
)
272 self
.log
.debug(_("Created RRD file %s.") % self
.file)
274 self
.log
.debug(" %s" % arg
)
277 return rrdtool
.info(self
.file)
281 return self
.plugin
.interval
285 return self
.stepsize
* 2
287 def get_rrd_schema(self
):
289 "--step", "%s" % self
.stepsize
,
291 for line
in self
.rrd_schema
:
292 if line
.startswith("DS:"):
294 (prefix
, name
, type, lower_limit
, upper_limit
) = line
.split(":")
300 "%s" % self
.heartbeat
,
312 for rra_timespan
in self
.rra_timespans
:
313 if (rra_timespan
/ self
.stepsize
) < self
.rra_rows
:
314 rra_timespan
= self
.stepsize
* self
.rra_rows
319 cdp_length
= rra_timespan
// (self
.rra_rows
* self
.stepsize
)
321 cdp_number
= math
.ceil(rra_timespan
/ (cdp_length
* self
.stepsize
))
323 for rra_type
in self
.rra_types
:
324 schema
.append("RRA:%s:%.10f:%d:%d" % \
325 (rra_type
, xff
, cdp_length
, cdp_number
))
331 raise RuntimeError("This object has already collected its data")
333 self
.collected
= True
334 self
.now
= datetime
.datetime
.utcnow()
337 result
= self
.collect()
341 Will commit the collected data to the database.
343 # Make sure that the RRD database has been created
347 class GraphTemplate(object):
348 # A unique name to identify this graph template.
351 # Instructions how to create the graph.
354 # Extra arguments passed to rrdgraph.
357 def __init__(self
, ds
):
362 return self
.ds
.collecty
364 def graph(self
, file, interval
=None,
365 width
=GRAPH_DEFAULT_WIDTH
, height
=GRAPH_DEFAULT_HEIGHT
):
367 "--width", "%d" % width
,
368 "--height", "%d" % height
,
370 args
+= self
.collecty
.graph_default_arguments
371 args
+= self
.rrd_graph_args
381 args
.append("--start")
383 args
.append(intervals
[interval
])
385 args
.append(interval
)
387 info
= { "file" : self
.ds
.file }
388 for item
in self
.rrd_graph
:
390 args
.append(item
% info
)
394 rrdtool
.graph(file, *args
)