"""
RRD file on filesystem.
"""
import os, os.path, errno
import logging; log = logging.getLogger('pvl.rrd.rrds')
class RRDDatabase (object) :
"""
A filesystem directory containing .rrd files.
"""
def __init__ (self, path, graph_type, cache=None) :
"""
path - path to rrd dirs
graph_type - pvl.rrd.graph.InterfaceGraph type
cache - optional RRDCache
"""
if not path :
raise ValueError("RRDDatabase: no path given")
log.info("%s: graph_type=%s, cache=%s", path, graph_type, cache)
self.graph_type = graph_type
self._path = path
self.cache = cache
def path (self, *nodes) :
"""
Lookup and full filesystem path to the given relative RRD/dir path.
Raises ValueError if invalid path.
"""
# relative dir (no leading slash) -> absolute path
path = os.path.normpath(os.path.join(self._path, *nodes))
log.debug("%s: %s -> %s", self, nodes, path)
# check inside base path
if not path.startswith(self._path.rstrip('/')) :
# mask
raise ValueError("%s: Invalid path: %s" % (self, nodes))
# ok
return path
def tree (self, node=None) :
"""
Lookup and return XXX:RRDTree for given node, or root tree.
Raises ValueError if invalid path, or no such tree.
"""
# lookup fs path
if node :
path = self.path(node)
else :
path = self.path()
# found?
if not os.path.isdir(path) :
raise ValueError("%s: Invalid tree: %s: %s" % (self, node, path))
if node :
return node
else :
return '' # equivalent
def rrd (self, node, tree=None) :
"""
Lookup and return RRD for given node.
"""
if tree :
node = os.path.join(tree, node)
path = self.path(node) + '.rrd'
if not os.path.isfile(path) :
raise ValueError("%: Invalid rrd: %s: %s" % (self, node, path))
return RRD(self, node, self.graph_type)
def list (self, tree=None) :
"""
List (trees, rrds) under given tree.
"""
dirs = []
rrds = []
path = self.path(tree)
for name in os.listdir(path) :
if name.startswith('.') :
continue
path = self.path(tree, name)
basename, extname = os.path.splitext(name)
log.debug("%s: %s: %s: %s", self, tree, name, path)
if os.path.isdir(path) :
dirs.append(name)
elif extname == '.rrd' :
# without the .rrd
rrds.append(basename)
# return sorted lists
return sorted(dirs), sorted(rrds)
def graph (self, rrd, style, interval, cache=True) :
"""
Cached RRD.graph(), returning outfile.
Uses self.cache if cache, otherwise graphs to tempfile.
"""
path = rrd.path()
if self.cache and cache :
# path in cache
out = self.cache.path(style, interval, str(rrd))
# hit/miss?
cache = self.cache.lookup(path, out)
else :
# tempfile
out = None
cache = None
log.debug("%s: %s: %s", self, rrd, out)
if cache :
# from cache
outfile = open(out)
else :
# to cache/tempfile
dimensions, lines, outfile = rrd.graph(style, interval).graph(out)
return outfile
def __str__ (self) :
return self._path
class RRD (object) :
"""
An .rrd file on the filesystem, graphed using pvl.rrd.graph.Interface.
"""
def __init__ (self, db, rrd, graph_type) :
self.db = db
self.rrd = rrd
self.graph_type = graph_type
def path (self) :
return self.db.path(self.rrd) + '.rrd'
def graph (self, style, interval) :
"""
Build Graph given rrd using given style/interval.
"""
path = self.path()
title = str(self) # " / ".join(rrd.split('/'))
return self.graph_type.build(title, path, style, interval)
def __str__ (self) :
return self.rrd
class RRDCache (object) :
"""
Cache graph output in the filesystem.
"""
def __init__ (self, path) :
log.info("%s", path)
self._path = path
def path (self, *key) :
"""
Return cache path for key.
"""
return os.path.join(self._path, *key) + '.png'
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 lookup (self, source, path) :
"""
Return hit from cache.
"""
# 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 dst and src.st_mtime < dst.st_mtime :
log.debug("%s: %s: %s: hit", self, source, path)
return True
elif not dst:
log.debug("%s: %s: %s: miss", self, source, path)
return False
else :
log.debug("%s: %s: %s: update", self, source, path)
return None
def __str__ (self) :
return self._path