rrdweb/graph.py
changeset 32 47e977c23ba2
parent 28 89a4d9879171
--- a/rrdweb/graph.py	Tue Jan 25 01:19:40 2011 +0200
+++ b/rrdweb/graph.py	Tue Jan 25 01:28:06 2011 +0200
@@ -42,21 +42,21 @@
 
 def overview_opts () :
     """
-        Common options for the overview graph
+        Graph statements for a single-source overview graph
     """
 
     return dict(
         width               = 600,
         height              = 50,
     ), [
-        "CDEF:all=in,out,+",
+        "CDEF:all=in0,out0,+",
 
         "VDEF:max=all,MAXIMUM",
         "VDEF:avg=all,AVERAGE",
         "VDEF:min=all,MINIMUM",
 
-        "LINE1:in#0000FF:In",
-        "LINE1:out#00CC00:Out",
+        "LINE1:in0#0000FF:In",
+        "LINE1:out0#00CC00:Out",
 
         "GPRINT:max:%6.2lf %Sbps max",
         "GPRINT:avg:%6.2lf %Sbps avg",
@@ -65,7 +65,7 @@
 
 def detail_opts () :
     """
-        Common options for the detail graph
+        Common options for a single-source detail graph
     """
 
     return dict(
@@ -74,14 +74,14 @@
         height              = 200,
     ), [
         # values
-        'VDEF:in_max=in,MAXIMUM',
-        'VDEF:in_avg=in,AVERAGE',
-        'VDEF:in_min=in,MINIMUM',
-        'VDEF:in_cur=in,LAST',
-        'VDEF:out_max=out,MAXIMUM',
-        'VDEF:out_avg=out,AVERAGE',
-        'VDEF:out_min=out,MINIMUM',
-        'VDEF:out_cur=out,LAST',
+        'VDEF:in_max=in0,MAXIMUM',
+        'VDEF:in_avg=in0,AVERAGE',
+        'VDEF:in_min=in0,MINIMUM',
+        'VDEF:in_cur=in0,LAST',
+        'VDEF:out_max=out0,MAXIMUM',
+        'VDEF:out_avg=out0,AVERAGE',
+        'VDEF:out_min=out0,MINIMUM',
+        'VDEF:out_cur=out0,LAST',
 
         # legend/graph
         "COMMENT:%4s" % "",
@@ -90,13 +90,13 @@
         "COMMENT:%11s" % "Minimum",
         "COMMENT:%11s\\l" % "Current",
 
-        "LINE1:in#0000FF:%4s" % "In",
+        "LINE1:in0#0000FF:%4s" % "In",
         'GPRINT:in_max:%6.2lf %Sbps',
         'GPRINT:in_avg:%6.2lf %Sbps',
         'GPRINT:in_min:%6.2lf %Sbps',
         'GPRINT:in_cur:%6.2lf %Sbps\\l',
 
-        "LINE1:out#00CC00:%4s" % "Out",
+        "LINE1:out0#00CC00:%4s" % "Out",
         'GPRINT:out_max:%6.2lf %Sbps',
         'GPRINT:out_avg:%6.2lf %Sbps',
         'GPRINT:out_min:%6.2lf %Sbps',
@@ -106,11 +106,84 @@
         "COMMENT:Generated %s\\r" % timestamp().replace(':', '\\:'),
     ]
 
+# set of line colors used for a multi-source graph, in rgb hex form (no prefix)
+MULTI_COLORS = (
+    '00cc00',
+    '0000ff',
+    'cc0000',
+)
+
+def multi_graph_source_defs (idx, name, color) :
+    """
+        Render graph and legend summary for a single source in a multi-source graph.
+    """
+
+    params = dict(
+        idx = idx, name = name, color = color
+    )
+
+    return [stmt.format(**params) for stmt in (
+        "CDEF:all{idx}=in{idx},out{idx},+",
+
+        "VDEF:max{idx}=all{idx},MAXIMUM",
+        "VDEF:avg{idx}=all{idx},AVERAGE",
+        "VDEF:cur{idx}=all{idx},LAST",
+
+        "LINE1:all{idx}#{color}: ",
+
+        "GPRINT:max{idx}:%6.2lf %Sbps",
+        "GPRINT:avg{idx}:%6.2lf %Sbps",
+        "GPRINT:cur{idx}:%6.2lf %Sbps",
+        "COMMENT:\t{name}\\l",
+    )]
+
+
+def multi_graph (sources) :
+    """
+        Graph definition for a multi-source overview graph.
+
+        Uses combined in/out totals, giving a single line per source.
+    """
+
+    # defs for each source
+    sources_defs = [multi_graph_source_defs(idx, name, color) for (idx, name), color in zip(sources, MULTI_COLORS)]
+
+    return dict(
+        # dimensions
+        width               = 600,
+        height              = 200,
+
+    ), [
+        # legend header
+        "COMMENT: ",
+        "COMMENT:%11s" % "Maximum",
+        "COMMENT:%11s" % "Average",
+        "COMMENT:%11s" % "Current",
+        "COMMENT:\t%s\\l" % "",
+            
+    ] + [
+        # each source
+        stmt for defs in sources_defs for stmt in defs
+    ]
+
 STYLE_DEFS = {
     'overview': overview_opts,
     'detail':   detail_opts,
 }
 
+def hourly_opts (title) :
+
+    return dict(
+        # labels
+        x_grid              = None,
+
+        # general info
+        title               = "Hourly %s" % (title, ),
+
+        # interval
+        start               = "-1h",
+    )
+
 def daily_opts (title) :
     """
         Common options for the 'daily' view
@@ -153,52 +226,99 @@
 
 
 INTERVAL_DEFS = {
+    'hourly':   hourly_opts,
     'daily':    daily_opts,
     'weekly':   weekly_opts,
     'yearly':   yearly_opts,
 }
 
-def mrtg_data (rrd_path) :
+def data_defs (idx, rrd, ds_in, ds_out, bytes=True, cf='AVERAGE') :
     """
-        Data sources for network in/out from an MRTG rrd
+        Generate the DEF/CDEF statements for the in{idx}/out{idx} data sources for the given RRD and DS names.
+
+        If bytes is given, convert the value into bits.
     """
 
-    return [
+    params = dict(
+        ds_in=ds_in, ds_out=ds_out, cf=cf,
+        idx=idx, rrd=rrd, 
+    )
+
+    if bytes :
         # data sources, bytes/s
-        r'DEF:in0=%s:ds0:AVERAGE' % rrd_path,
-        r'DEF:out0=%s:ds1:AVERAGE' % rrd_path,
-
+        yield 'DEF:in_raw{idx}={rrd}:{ds_in}:{cf}'.format(**params)
+        yield 'DEF:out_raw{idx}={rrd}:{ds_out}:{cf}'.format(**params)
+    
         # data, bits/s
-        'CDEF:in=in0,8,*',
-        'CDEF:out=out0,8,*',
+        yield 'CDEF:in{idx}=in_raw{idx},8,*'.format(**params)
+        yield 'CDEF:out{idx}=out_raw{idx},8,*'.format(**params)
+    
+    else :
+        # data sources, bits/s
+        yield 'DEF:in{idx}={rrd}:{ds_in}:{cf}'.format(**params)
+        yield 'DEF:out{idx}={rrd}:{ds_out}:{cf}'.format(**params)
 
-    ]        
 
-def collectd_data (rrd_path) :
+def mrtg_data (idx, rrd) :
+    """
+        Generate the in{idx}/out{idx} data sources fro the given MRTG rrd.
+    """
+
+    return data_defs(idx, rrd, 'ds0', 'ds1')
+
+def collectd_data (idx, rrd) :
     """
         Data sources for if_octets from a collectd rrd
     """
-
-    return [
-        # data sources, bytes/s
-        r'DEF:in0=%s:rx:AVERAGE' % rrd_path,
-        r'DEF:out0=%s:tx:AVERAGE' % rrd_path,
+    
+    return data_defs(idx, rrd, 'rx', 'tx')
 
-        # data, bits/s
-        'CDEF:in=in0,8,*',
-        'CDEF:out=out0,8,*',
+def pmacct_data (idx, rrd) :
+    """
+        Data sources for in/out bytes from a pmacct rrd
+    """
+    
+    return data_defs(idx, rrd, 'in', 'out')
 
-    ]        
-    
-def _graph (style, interval, title, data_func, rrd_path, out_path) :
+def graph_single (style, interval, title, data_func, rrd_path, out_path) :
+    """
+        Render graph.
+
+        Returns a (width, height, print_lines, graph_file) tuple.
+    """
+
     style_opts, style_vars = STYLE_DEFS[style]()
     interval_opts = INTERVAL_DEFS[interval](title)
 
     opts = rrd.merge_opts(common_opts(), style_opts, interval_opts)
-    data = data_func(rrd_path) + style_vars
+    data = list(data_func(0, rrd_path)) + style_vars
 
     return rrd.graph(out_path, *data, **opts)
    
+def graph_multi (interval, title, rrd_list, data_func, out) :
+    """
+        Render a multi-source graph.
+
+            interval        - the name of the time interval to use
+            title           - graph title
+            rrd_list        - sequence of (rrd, name) tuples for each source to draw
+            data_func       - the data source definition to use
+            out             - output path for graph
+    """
+
+    # data sources
+    data_defs = [stmt for idx, (path, name) in enumerate(rrd_list) for stmt in data_func(idx, path)]
+    
+    # options
+    graph_opts, graph_defs = multi_graph([(idx, name) for idx, (path, name) in enumerate(rrd_list)])
+    interval_opts = INTERVAL_DEFS[interval](title)
+    
+    # combine
+    opts = rrd.merge_opts(common_opts(), graph_opts, interval_opts)
+    defs = data_defs + graph_defs
+    
+    # graph
+    return rrd.graph(out, *defs, **opts)
 
 def mrtg (style, interval, title, rrd_path, out_path) :
     return _graph(style, interval, title, mrtg_data, rrd_path, out_path)
@@ -206,3 +326,7 @@
 def collectd_ifoctets (style, interval, title, rrd_path, out_path) :
     return _graph(style, interval, title, collectd_data, rrd_path, out_path)
 
+def pmacct_bytes (style, interval, title, rrd_path, out_path) :
+    return _graph(style, interval, title, pmacct_data, rrd_path, out_path)
+
+