--- a/bin/pvl.verkko-rrd Sun Jan 20 18:31:21 2013 +0200
+++ b/bin/pvl.verkko-rrd Sun Jan 20 18:51:51 2013 +0200
@@ -33,7 +33,10 @@
parser.add_option_group(pvl.args.parser(parser))
parser.add_option('--rrd', metavar='PATH',
- help="Path to RRD files")
+ help="Find RRD files")
+
+ parser.add_option('--cache', metavar='PATH',
+ help="Cache RRD graphs")
# parse
options, args = parser.parse_args(args)
@@ -56,7 +59,7 @@
log.error("no --rrd given")
return 2
- rrd = pvl.verkko.rrd.RRDDatabase(options.rrd)
+ rrd = pvl.verkko.rrd.RRDDatabase(options.rrd, options.cache)
# app
application = pvl.verkko.rrd.Application(rrd)
--- a/pvl/verkko/rrd.py Sun Jan 20 18:31:21 2013 +0200
+++ b/pvl/verkko/rrd.py Sun Jan 20 18:51:51 2013 +0200
@@ -12,20 +12,21 @@
import logging; log = logging.getLogger('pvl.verkko.rrd')
# Model
-import os, os.path
+import os, os.path, errno
class RRDDatabase (object) :
"""
A filesystem directory containing .rrd files.
"""
- def __init__ (self, path) :
+ def __init__ (self, path, cache=None) :
if not path :
raise ValueError("RRDDatabase: no path given")
- log.info("%s", path)
+ log.info("%s: cache=%s", path, cache)
self._path = path
+ self._cache = cache
def path (self, node=None, *subnodes) :
"""
@@ -73,10 +74,8 @@
if tree :
node = os.path.join(tree, node)
-
- node += '.rrd'
- path = self.path(node)
+ path = self.path(node) + '.rrd'
if not os.path.isfile(path) :
raise ValueError("%: Invalid rrd: %s: %s" % (self, node, path))
@@ -110,21 +109,77 @@
# return sorted lists
return sorted(dirs), sorted(rrds)
+ def _stat (self, path) :
+ """
+ os.stat or None.
+ """
+
+ try :
+ return os.stat(path)
+
+ except OSError as ex :
+ if ex.errno == errno.ENOENT :
+ return None
+ else :
+ raise
+
+ def cache (self, source, *key) :
+ """
+ Lookup given key from cache, returning (hit, file).
+ """
+
+ # output
+ if not self._cache :
+ return None, None
+
+ # cache path
+ path = os.path.join(self._cache, *key)
+
+ # create
+ dir = os.path.dirname(path)
+ if not os.path.isdir(dir) :
+ log.warn("makedirs %s", dir)
+ os.makedirs(dir)
+
+ # stats's
+ src = self._stat(source)
+ dst = self._stat(path)
+
+ if not dst:
+ log.debug("%s: %s: %s: miss", self._cache, source, path)
+ return None, path
+
+ elif dst and src.st_mtime < dst.st_mtime :
+ log.debug("%s: %s: %s: hit", self._cache, source, path)
+
+ return True, path
+
+ else :
+ log.debug("%s: %s: %s: update", self._cache, source, path)
+ return False, path
+
def graph (self, rrd, style, interval) :
"""
Graph given rrd using given style/interval, returning the opened png data file.
"""
- title = " / ".join(rrd.split('/'))
-
- log.debug("%s: %s: %s/%s", self, rrd, style, interval)
+ title = str(rrd) # " / ".join(rrd.split('/'))
- # XXX: lookup graph style..
- # XXX: collectd
- # XXX: out=None -> tempfile
- dimensions, lines, outfile = pvl.rrd.graph.collectd_ifoctets(style, interval, title, self.path(rrd), None)
+ path = self.path(rrd) + '.rrd'
- log.debug("%s: %s: %s", self, rrd, outfile)
+ cached, out = self.cache(path, style, interval, rrd + '.png')
+
+ log.debug("%s: %s: %s", self, rrd, out)
+
+ if cached :
+ # from cache
+ outfile = open(out)
+
+ else :
+ # to cache
+ # XXX: lookup graph style..
+ # XXX: collectd
+ dimensions, lines, outfile = pvl.rrd.graph.collectd_ifoctets(style, interval, title, path, out)
return outfile