# #
###############################################################################
+import logging
+import os
+import rrdtool
+import time
+
from threading import Thread
+from ..constants import *
from ..i18n import _
class Plugin(Thread):
+ # The name of this plugin.
+ name = None
+
+ # A description for this plugin.
+ description = None
+
+ # The schema of the RRD database.
+ rrd_schema = None
+
+ # The default interval of this plugin.
+ default_interval = 60
+
def __init__(self, collecty, **kwargs):
Thread.__init__(self)
self.collecty = collecty
- self.interval = int(kwargs.get("interval", 60))
+ # Check if this plugin was configured correctly.
+ assert self.name, "Name of the plugin is not set: %s" % self.name
+ assert self.description, "Description of the plugin is not set: %s" % self.description
+ assert self.rrd_schema
+
+ # Initialize the logger.
+ self.log = logging.getLogger("collecty.plugins.%s" % self.name)
+ self.log.propagate = 1
# Keepalive options
self.heartbeat = 2
- self.killed = False
-
- self.wakeup = self.interval / self.heartbeat
-
- self.file = kwargs.get("file", None)
- if not self.file.startswith("/"):
- self.file = os.path.join("/var/rrd", self.file)
+ self.running = True
self.data = []
+ # Create the database file.
self.create()
+
+ # Run some custom initialization.
+ self.init()
+
+ self.log.info(_("Successfully initialized."))
def __repr__(self):
- return "<Plugin %s>" % self._type
+ return "<Plugin %s>" % self.name
def __str__(self):
- return "Plugin %s %s" % (self._type, self.file)
-
- def run(self):
- self.collecty.debug("%s started..." % self)
-
- c = 0
- while True:
- if self.killed:
- self.update()
- self.collecty.debug("%s stoppped..." % self)
- return
+ return "Plugin %s %s" % (self.name, self.file)
+
+ @property
+ def interval(self):
+ """
+ Returns the interval in milliseconds, when the read method
+ should be called again.
+ """
+ # XXX read this from the settings
+
+ # Otherwise return the default.
+ return self.default_interval
+
+ @property
+ def file(self):
+ """
+ The absolute path to the RRD file of this plugin.
+ """
+ return os.path.join(DATABASE_DIR, "%s.rrd" % self.name)
- if c == 0:
- self.data.append(self.collect())
- self.collecty.debug("%s collectd: %s..." % (self, self.data[-1]))
+ def create(self):
+ """
+ Creates an empty RRD file with the desired data structures.
+ """
+ # Skip if the file does already exist.
+ if os.path.exists(self.file):
+ return
- self.update()
+ dirname = os.path.dirname(self.file)
+ if not os.path.exists(dirname):
+ os.makedirs(dirname)
- c = self.wakeup
+ rrdtool.create(self.file, *self.rrd_schema)
- c = c - 1
- time.sleep(self.heartbeat)
+ self.log.debug(_("Created RRD file %s.") % self.file)
- def shutdown(self):
- self.killed = True
+ def info(self):
+ return rrdtool.info(self.file)
- def time(self):
- return int(time.time()) # Should return time as int in UTC
+ ### Basic methods
+
+ def init(self):
+ """
+ Do some custom initialization stuff here.
+ """
+ pass
+
+ def read(self):
+ """
+ Gathers the statistical data, this plugin collects.
+ """
+ raise NotImplementedError
+
+ def submit(self):
+ """
+ Flushes the read data to disk.
+ """
+ # Do nothing in case there is no data to submit.
+ if not self.data:
+ return
+
+ self.collecty.debug(_("Saving data from %s...") % self)
+ rrdtool.update(self.file, *self.data)
+ self.data = []
- def create(self):
- if not os.path.exists(self.file):
- rrdtool.create(self.file, *self._rrd)
+ def __read(self, *args, **kwargs):
+ """
+ This method catches errors from the read() method and logs them.
+ """
+ try:
+ return self.read(*args, **kwargs)
+
+ # Catch any exceptions, so collecty does not crash.
+ except Exception, e:
+ self.log.critical(_("Unhandled exception in read()!"), exc_info=True)
+
+ def __submit(self, *args, **kwargs):
+ """
+ This method catches errors from the submit() method and logs them.
+ """
+ try:
+ return self.submit(*args, **kwargs)
- def update(self):
- if self.data:
- self.collecty.debug("%s saving data..." % self)
- rrdtool.update(self.file, *self.data)
- self.data = []
+ # Catch any exceptions, so collecty does not crash.
+ except Exception, e:
+ self.log.critical(_("Unhandled exception in submit()!"), exc_info=True)
+
+ def run(self):
+ self.log.debug(_("Started."))
- def collect(self):
- raise Exception, "Not implemented"
+ counter = 0
+ while self.running:
+ if counter == 0:
+ self.log.debug(_("Collecting..."))
+ self.__read()
+
+ self.log.debug(_("Sleeping for %.4fs.") % self.interval)
+
+ counter = self.interval / self.heartbeat
+
+ time.sleep(self.heartbeat)
+ counter -= 1
+
+ self.__submit()
+ self.log.debug(_("Stopped."))
+
+ def shutdown(self):
+ self.log.debug(_("Received shutdown signal."))
+ self.running = False
+
+ @property
+ def now(self):
+ """
+ Returns the current timestamp in the UNIX timestamp format (UTC).
+ """
+ return int(time.time())
def graph(self, file, interval=None):
args = [ "--imgformat", "PNG",
args.append(item)
rrdtool.graph(file, *args)
-
- def info(self):
- return rrdtool.info(self.file)
-