pvl/rrd/api.py
changeset 143 fb48ba17ae3e
child 148 92fd0898188d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pvl/rrd/api.py	Sun Jan 20 15:37:59 2013 +0200
@@ -0,0 +1,111 @@
+import rrdtool
+
+import pvl.invoke
+
+import logging; log = logging.getLogger('pvl.rrd.api')
+
+"""
+    Wrapper around the rrdtool python interface
+"""
+
+def timestamp (time=None) :
+    """
+        Format datetime value for rrdtool.
+    """
+
+    if not time :
+        return None
+
+    elif isinstance(time, datetime.datetime) :
+        return int(time.mktime(dt.timetuple()))
+
+    elif isinstance(time, datetime.timedelta) :
+        raise NotImplementedError("pvl.rrd.api.timestamp: timedelta")
+
+    else :
+        # dunno
+        return str(dt)
+
+def cmd (func, pre, opts, post) :
+    """
+        Run the given rrdtool.* function, formatting the given positional arguments and options.
+
+        Returns the return value, which varies...
+    """
+    
+    log.debug("%s: %s: %s: %s", func, pre, opts, post)
+    
+    # { opt: arg } -> [ '--opt', arg ]
+    opts = pvl.invoke.optargs(**opts)
+
+    # positional arguments
+    pre = pvl.invoke.optargs(*pre)
+    post = pvl.invoke.optargs(*post)
+
+    return func(*(pre + opts + post))
+
+def graph (out=None, *args, **opts) :
+    """
+        Render a graph image and/or print a report from data stored in one or several RRDs.
+        
+            out     - None  -> tempfile
+                    - False -> stdout
+                    - path  -> write to file
+
+        Returns:
+            (width, height)         - pixel dimensions of the resulting graph image
+            report_output           - any PRINT'd output (?)
+            graph_file              - file-like object containing graph image, unless out=False -> stdout
+
+        With out=None, the returned graph_file is a tempfile which will be cleaned up by Python once close()'d!
+    """
+
+    if out is None :
+        # tempfile
+        out_file = tempfile.NamedTemporaryFile(suffix='.png', delete=True) # python2.6
+        out_path = out.name
+
+    elif out is False :
+        out_file = None
+        out_path = '-'
+
+    else :
+        # for reading
+        out_path = out
+        out_file = True # open later
+    
+    # XXX: handle tempfile close?
+    width, height, out_lines = cmd(rrdtool.graph, (out_path, ), opts, args)
+
+    if out_file is True :
+        out_file = open(out_path)
+            
+    return (width, height), out_lines, out_file
+
+def fetch (rrd, cf, **opts) :
+    """
+        Fetch values from RRD.
+
+        Returns
+            (start, end, step)          - xrange(...) for row timestamps
+            (ds, ...)                   - columns (ds name)
+            ((value, ...), ...)         - rows (data by ds)
+    """
+
+    return cmd(rrdtool.fetch, (rrd, cf), opts, ())
+
+def _fetch (rrd, cf='AVERAGE', resolution=None, start=None, end=None, **opts) :
+    """
+        Yields (timestamp, { ds: value }) for given ds-values, or all.
+    """
+
+    steps, sources, rows = fetch(rrd, cf,
+        resolution  = resolution,
+        start       = timestamp(start),
+        end         = timestamp(end),
+        **opts
+    )
+
+    for ts, row in zip(xrange(*steps), rows) :
+        yield datetime.fromtimestamp(ts), dict(zip(sources, row))
+