# #
###############################################################################
-import datetime
import logging
import os
import re
import time
import unicodedata
-from .. import locales
from .. import util
from ..constants import *
from ..i18n import _
Sets the correct environment for rrdtool to create
localised graphs and graphs in the correct timezone.
"""
- def __init__(self, timezone, locale):
+ def __init__(self, timezone="UTC", locale="en_US.utf-8"):
# Build the new environment
self.new_environment = {
- "TZ" : timezone or DEFAULT_TIMEZONE,
+ "LANGUAGE" : locale,
+ "LC_ALL" : locale,
+ "TZ" : timezone,
}
- for k in ("LANG", "LC_ALL"):
- self.new_environment[k] = locale or DEFAULT_LOCALE
-
def __enter__(self):
# Save the current environment
self.old_environment = {}
+
for k in self.new_environment:
+ # Store the old value
self.old_environment[k] = os.environ.get(k, None)
- # Apply the new one
- os.environ.update(self.new_environment)
+ # Apply the new one
+ if self.new_environment[k]:
+ os.environ[k] = self.new_environment[k]
def __exit__(self, type, value, traceback):
# Roll back to the previous environment
time_start = time.time()
# Run through all objects of this plugin and call the collect method.
- for o in self.objects:
- now = datetime.datetime.utcnow()
+ for object in self.objects:
+ # Run collection
try:
- result = o.collect()
+ result = object.collect()
- result = self._format_result(result)
- except:
- self.log.warning(_("Unhandled exception in %s.collect()") % o, exc_info=True)
+ # Catch any unhandled exceptions
+ except Exception as e:
+ self.log.warning(_("Unhandled exception in %s.collect()") % object, exc_info=True)
continue
if not result:
- self.log.warning(_("Received empty result: %s") % o)
+ self.log.warning(_("Received empty result: %s") % object)
continue
- self.log.debug(_("Collected %s: %s") % (o, result))
-
# Add the object to the write queue so that the data is written
# to the databases later.
- self.collecty.write_queue.add(o, now, result)
+ result = self.collecty.write_queue.submit(object, result)
+
+ self.log.debug(_("Collected %s: %s") % (object, result))
# Returns the time this function took to complete.
delay = time.time() - time_start
# Log some warning when a collect method takes too long to return some data
if delay >= 60:
self.log.warning(_("A worker thread was stalled for %.4fs") % delay)
-
- @staticmethod
- def _format_result(result):
- if not isinstance(result, tuple) and not isinstance(result, list):
- return result
-
- # Replace all Nones by UNKNOWN
- s = []
-
- for e in result:
- if e is None:
- e = "U"
-
- s.append("%s" % e)
-
- return ":".join(s)
+ else:
+ self.log.debug(_("Collection finished in %.2fms") % (delay * 1000))
def get_object(self, id):
for object in self.objects:
time_start = time.time()
- graph = template.generate_graph(**kwargs)
+ with Environment(timezone=timezone, locale=locale):
+ graph = template.generate_graph(**kwargs)
duration = time.time() - time_start
self.log.debug(_("Generated graph %s in %.1fms") \
def __init__(self, plugin, *args, **kwargs):
self.plugin = plugin
- # Indicates if this object has collected its data
- self.collected = False
-
# Initialise this object
self.init(*args, **kwargs)
x, y, vals = rrdtool.graph("/dev/null", *args)
return dict(zip(self.rrd_schema_names, vals))
- def execute(self):
- if self.collected:
- raise RuntimeError("This object has already collected its data")
-
- self.collected = True
- self.now = datetime.datetime.utcnow()
-
- # Call the collect
- result = self.collect()
-
def commit(self):
"""
Will commit the collected data to the database.
"""
filename = os.path.join(*args)
- with open(filename) as f:
- value = f.read()
+ try:
+ with open(filename) as f:
+ value = f.read()
+ except FileNotFoundError as e:
+ return None
# Strip any excess whitespace
if strip:
try:
return int(value)
- except ValueError:
+ except (TypeError, ValueError):
return None
def read_proc_stat(self):
return ret
+ def read_proc_meminfo(self):
+ ret = {}
+
+ with open("/proc/meminfo") as f:
+ for line in f:
+ # Split the key from the rest of the line
+ key, line = line.split(":", 1)
+
+ # Remove any whitespace
+ line = line.strip()
+
+ # Remove any trailing kB
+ if line.endswith(" kB"):
+ line = line[:-3]
+
+ # Try to convert to integer
+ try:
+ line = int(line)
+ except (TypeError, ValueError):
+ continue
+
+ ret[key] = line
+
+ return ret
+
class GraphTemplate(object):
# A unique name to identify this graph template.
# Extra arguments passed to rrdgraph.
rrd_graph_args = []
- # Default dimensions for this graph
- height = GRAPH_DEFAULT_HEIGHT
- width = GRAPH_DEFAULT_WIDTH
-
def __init__(self, plugin, object_id, locale=None, timezone=None):
self.plugin = plugin
# Save localisation parameters
- self.locale = locales.get(locale)
+ self.locale = locale
self.timezone = timezone
# Get all required RRD objects
def _make_command_line(self, interval, format=DEFAULT_IMAGE_FORMAT,
width=None, height=None, with_title=True, thumbnail=False):
- args = [e for e in GRAPH_DEFAULT_ARGUMENTS]
+ args = [
+ # Change the background colour
+ "--color", "BACK#FFFFFFFF",
+
+ # Disable the border around the image
+ "--border", "0",
+
+ # Let's width and height define the size of the entire image
+ "--full-size-mode",
+
+ # Gives the curves a more organic look
+ "--slope-mode",
+
+ # Show nicer labels
+ "--dynamic-labels",
+
+ # Brand all generated graphs
+ "--watermark", _("Created by collecty"),
+ ]
# Set the default dimensions
- default_height, default_width = GRAPH_DEFAULT_HEIGHT, GRAPH_DEFAULT_WIDTH
+ default_width, default_height = 960, 480
# A thumbnail doesn't have a legend and other labels
if thumbnail:
args.append("--only-graph")
- default_height = THUMBNAIL_DEFAULT_HEIGHT
- default_width = THUMBNAIL_DEFAULT_WIDTH
+ default_width, default_height = 80, 20
args += [
"--imgformat", format,
for arg in args:
self.log.debug(" %s" % arg)
- with Environment(self.timezone, self.locale.lang):
- graph = rrdtool.graphv("-", *args)
+ graph = rrdtool.graphv("-", *args)
return {
"image" : graph.get("image"),