rrdweb/graph.py
changeset 32 47e977c23ba2
parent 28 89a4d9879171
equal deleted inserted replaced
31:cd9ca8068b09 32:47e977c23ba2
    40         slope_mode          = True,
    40         slope_mode          = True,
    41     )
    41     )
    42 
    42 
    43 def overview_opts () :
    43 def overview_opts () :
    44     """
    44     """
    45         Common options for the overview graph
    45         Graph statements for a single-source overview graph
    46     """
    46     """
    47 
    47 
    48     return dict(
    48     return dict(
    49         width               = 600,
    49         width               = 600,
    50         height              = 50,
    50         height              = 50,
    51     ), [
    51     ), [
    52         "CDEF:all=in,out,+",
    52         "CDEF:all=in0,out0,+",
    53 
    53 
    54         "VDEF:max=all,MAXIMUM",
    54         "VDEF:max=all,MAXIMUM",
    55         "VDEF:avg=all,AVERAGE",
    55         "VDEF:avg=all,AVERAGE",
    56         "VDEF:min=all,MINIMUM",
    56         "VDEF:min=all,MINIMUM",
    57 
    57 
    58         "LINE1:in#0000FF:In",
    58         "LINE1:in0#0000FF:In",
    59         "LINE1:out#00CC00:Out",
    59         "LINE1:out0#00CC00:Out",
    60 
    60 
    61         "GPRINT:max:%6.2lf %Sbps max",
    61         "GPRINT:max:%6.2lf %Sbps max",
    62         "GPRINT:avg:%6.2lf %Sbps avg",
    62         "GPRINT:avg:%6.2lf %Sbps avg",
    63         "GPRINT:min:%6.2lf %Sbps min\\l",
    63         "GPRINT:min:%6.2lf %Sbps min\\l",
    64     ]
    64     ]
    65 
    65 
    66 def detail_opts () :
    66 def detail_opts () :
    67     """
    67     """
    68         Common options for the detail graph
    68         Common options for a single-source detail graph
    69     """
    69     """
    70 
    70 
    71     return dict(
    71     return dict(
    72         # dimensions
    72         # dimensions
    73         width               = 600,
    73         width               = 600,
    74         height              = 200,
    74         height              = 200,
    75     ), [
    75     ), [
    76         # values
    76         # values
    77         'VDEF:in_max=in,MAXIMUM',
    77         'VDEF:in_max=in0,MAXIMUM',
    78         'VDEF:in_avg=in,AVERAGE',
    78         'VDEF:in_avg=in0,AVERAGE',
    79         'VDEF:in_min=in,MINIMUM',
    79         'VDEF:in_min=in0,MINIMUM',
    80         'VDEF:in_cur=in,LAST',
    80         'VDEF:in_cur=in0,LAST',
    81         'VDEF:out_max=out,MAXIMUM',
    81         'VDEF:out_max=out0,MAXIMUM',
    82         'VDEF:out_avg=out,AVERAGE',
    82         'VDEF:out_avg=out0,AVERAGE',
    83         'VDEF:out_min=out,MINIMUM',
    83         'VDEF:out_min=out0,MINIMUM',
    84         'VDEF:out_cur=out,LAST',
    84         'VDEF:out_cur=out0,LAST',
    85 
    85 
    86         # legend/graph
    86         # legend/graph
    87         "COMMENT:%4s" % "",
    87         "COMMENT:%4s" % "",
    88         "COMMENT:%11s" % "Maximum",
    88         "COMMENT:%11s" % "Maximum",
    89         "COMMENT:%11s" % "Average",
    89         "COMMENT:%11s" % "Average",
    90         "COMMENT:%11s" % "Minimum",
    90         "COMMENT:%11s" % "Minimum",
    91         "COMMENT:%11s\\l" % "Current",
    91         "COMMENT:%11s\\l" % "Current",
    92 
    92 
    93         "LINE1:in#0000FF:%4s" % "In",
    93         "LINE1:in0#0000FF:%4s" % "In",
    94         'GPRINT:in_max:%6.2lf %Sbps',
    94         'GPRINT:in_max:%6.2lf %Sbps',
    95         'GPRINT:in_avg:%6.2lf %Sbps',
    95         'GPRINT:in_avg:%6.2lf %Sbps',
    96         'GPRINT:in_min:%6.2lf %Sbps',
    96         'GPRINT:in_min:%6.2lf %Sbps',
    97         'GPRINT:in_cur:%6.2lf %Sbps\\l',
    97         'GPRINT:in_cur:%6.2lf %Sbps\\l',
    98 
    98 
    99         "LINE1:out#00CC00:%4s" % "Out",
    99         "LINE1:out0#00CC00:%4s" % "Out",
   100         'GPRINT:out_max:%6.2lf %Sbps',
   100         'GPRINT:out_max:%6.2lf %Sbps',
   101         'GPRINT:out_avg:%6.2lf %Sbps',
   101         'GPRINT:out_avg:%6.2lf %Sbps',
   102         'GPRINT:out_min:%6.2lf %Sbps',
   102         'GPRINT:out_min:%6.2lf %Sbps',
   103         'GPRINT:out_cur:%6.2lf %Sbps\\l',
   103         'GPRINT:out_cur:%6.2lf %Sbps\\l',
   104         
   104         
   105         # mark
   105         # mark
   106         "COMMENT:Generated %s\\r" % timestamp().replace(':', '\\:'),
   106         "COMMENT:Generated %s\\r" % timestamp().replace(':', '\\:'),
   107     ]
   107     ]
   108 
   108 
       
   109 # set of line colors used for a multi-source graph, in rgb hex form (no prefix)
       
   110 MULTI_COLORS = (
       
   111     '00cc00',
       
   112     '0000ff',
       
   113     'cc0000',
       
   114 )
       
   115 
       
   116 def multi_graph_source_defs (idx, name, color) :
       
   117     """
       
   118         Render graph and legend summary for a single source in a multi-source graph.
       
   119     """
       
   120 
       
   121     params = dict(
       
   122         idx = idx, name = name, color = color
       
   123     )
       
   124 
       
   125     return [stmt.format(**params) for stmt in (
       
   126         "CDEF:all{idx}=in{idx},out{idx},+",
       
   127 
       
   128         "VDEF:max{idx}=all{idx},MAXIMUM",
       
   129         "VDEF:avg{idx}=all{idx},AVERAGE",
       
   130         "VDEF:cur{idx}=all{idx},LAST",
       
   131 
       
   132         "LINE1:all{idx}#{color}: ",
       
   133 
       
   134         "GPRINT:max{idx}:%6.2lf %Sbps",
       
   135         "GPRINT:avg{idx}:%6.2lf %Sbps",
       
   136         "GPRINT:cur{idx}:%6.2lf %Sbps",
       
   137         "COMMENT:\t{name}\\l",
       
   138     )]
       
   139 
       
   140 
       
   141 def multi_graph (sources) :
       
   142     """
       
   143         Graph definition for a multi-source overview graph.
       
   144 
       
   145         Uses combined in/out totals, giving a single line per source.
       
   146     """
       
   147 
       
   148     # defs for each source
       
   149     sources_defs = [multi_graph_source_defs(idx, name, color) for (idx, name), color in zip(sources, MULTI_COLORS)]
       
   150 
       
   151     return dict(
       
   152         # dimensions
       
   153         width               = 600,
       
   154         height              = 200,
       
   155 
       
   156     ), [
       
   157         # legend header
       
   158         "COMMENT: ",
       
   159         "COMMENT:%11s" % "Maximum",
       
   160         "COMMENT:%11s" % "Average",
       
   161         "COMMENT:%11s" % "Current",
       
   162         "COMMENT:\t%s\\l" % "",
       
   163             
       
   164     ] + [
       
   165         # each source
       
   166         stmt for defs in sources_defs for stmt in defs
       
   167     ]
       
   168 
   109 STYLE_DEFS = {
   169 STYLE_DEFS = {
   110     'overview': overview_opts,
   170     'overview': overview_opts,
   111     'detail':   detail_opts,
   171     'detail':   detail_opts,
   112 }
   172 }
   113 
   173 
       
   174 def hourly_opts (title) :
       
   175 
       
   176     return dict(
       
   177         # labels
       
   178         x_grid              = None,
       
   179 
       
   180         # general info
       
   181         title               = "Hourly %s" % (title, ),
       
   182 
       
   183         # interval
       
   184         start               = "-1h",
       
   185     )
       
   186 
   114 def daily_opts (title) :
   187 def daily_opts (title) :
   115     """
   188     """
   116         Common options for the 'daily' view
   189         Common options for the 'daily' view
   117     """
   190     """
   118 
   191 
   151         start               = "-1y",
   224         start               = "-1y",
   152     )
   225     )
   153 
   226 
   154 
   227 
   155 INTERVAL_DEFS = {
   228 INTERVAL_DEFS = {
       
   229     'hourly':   hourly_opts,
   156     'daily':    daily_opts,
   230     'daily':    daily_opts,
   157     'weekly':   weekly_opts,
   231     'weekly':   weekly_opts,
   158     'yearly':   yearly_opts,
   232     'yearly':   yearly_opts,
   159 }
   233 }
   160 
   234 
   161 def mrtg_data (rrd_path) :
   235 def data_defs (idx, rrd, ds_in, ds_out, bytes=True, cf='AVERAGE') :
   162     """
   236     """
   163         Data sources for network in/out from an MRTG rrd
   237         Generate the DEF/CDEF statements for the in{idx}/out{idx} data sources for the given RRD and DS names.
   164     """
   238 
   165 
   239         If bytes is given, convert the value into bits.
   166     return [
   240     """
       
   241 
       
   242     params = dict(
       
   243         ds_in=ds_in, ds_out=ds_out, cf=cf,
       
   244         idx=idx, rrd=rrd, 
       
   245     )
       
   246 
       
   247     if bytes :
   167         # data sources, bytes/s
   248         # data sources, bytes/s
   168         r'DEF:in0=%s:ds0:AVERAGE' % rrd_path,
   249         yield 'DEF:in_raw{idx}={rrd}:{ds_in}:{cf}'.format(**params)
   169         r'DEF:out0=%s:ds1:AVERAGE' % rrd_path,
   250         yield 'DEF:out_raw{idx}={rrd}:{ds_out}:{cf}'.format(**params)
   170 
   251     
   171         # data, bits/s
   252         # data, bits/s
   172         'CDEF:in=in0,8,*',
   253         yield 'CDEF:in{idx}=in_raw{idx},8,*'.format(**params)
   173         'CDEF:out=out0,8,*',
   254         yield 'CDEF:out{idx}=out_raw{idx},8,*'.format(**params)
   174 
   255     
   175     ]        
   256     else :
   176 
   257         # data sources, bits/s
   177 def collectd_data (rrd_path) :
   258         yield 'DEF:in{idx}={rrd}:{ds_in}:{cf}'.format(**params)
       
   259         yield 'DEF:out{idx}={rrd}:{ds_out}:{cf}'.format(**params)
       
   260 
       
   261 
       
   262 def mrtg_data (idx, rrd) :
       
   263     """
       
   264         Generate the in{idx}/out{idx} data sources fro the given MRTG rrd.
       
   265     """
       
   266 
       
   267     return data_defs(idx, rrd, 'ds0', 'ds1')
       
   268 
       
   269 def collectd_data (idx, rrd) :
   178     """
   270     """
   179         Data sources for if_octets from a collectd rrd
   271         Data sources for if_octets from a collectd rrd
   180     """
   272     """
   181 
   273     
   182     return [
   274     return data_defs(idx, rrd, 'rx', 'tx')
   183         # data sources, bytes/s
   275 
   184         r'DEF:in0=%s:rx:AVERAGE' % rrd_path,
   276 def pmacct_data (idx, rrd) :
   185         r'DEF:out0=%s:tx:AVERAGE' % rrd_path,
   277     """
   186 
   278         Data sources for in/out bytes from a pmacct rrd
   187         # data, bits/s
   279     """
   188         'CDEF:in=in0,8,*',
   280     
   189         'CDEF:out=out0,8,*',
   281     return data_defs(idx, rrd, 'in', 'out')
   190 
   282 
   191     ]        
   283 def graph_single (style, interval, title, data_func, rrd_path, out_path) :
   192     
   284     """
   193 def _graph (style, interval, title, data_func, rrd_path, out_path) :
   285         Render graph.
       
   286 
       
   287         Returns a (width, height, print_lines, graph_file) tuple.
       
   288     """
       
   289 
   194     style_opts, style_vars = STYLE_DEFS[style]()
   290     style_opts, style_vars = STYLE_DEFS[style]()
   195     interval_opts = INTERVAL_DEFS[interval](title)
   291     interval_opts = INTERVAL_DEFS[interval](title)
   196 
   292 
   197     opts = rrd.merge_opts(common_opts(), style_opts, interval_opts)
   293     opts = rrd.merge_opts(common_opts(), style_opts, interval_opts)
   198     data = data_func(rrd_path) + style_vars
   294     data = list(data_func(0, rrd_path)) + style_vars
   199 
   295 
   200     return rrd.graph(out_path, *data, **opts)
   296     return rrd.graph(out_path, *data, **opts)
   201    
   297    
       
   298 def graph_multi (interval, title, rrd_list, data_func, out) :
       
   299     """
       
   300         Render a multi-source graph.
       
   301 
       
   302             interval        - the name of the time interval to use
       
   303             title           - graph title
       
   304             rrd_list        - sequence of (rrd, name) tuples for each source to draw
       
   305             data_func       - the data source definition to use
       
   306             out             - output path for graph
       
   307     """
       
   308 
       
   309     # data sources
       
   310     data_defs = [stmt for idx, (path, name) in enumerate(rrd_list) for stmt in data_func(idx, path)]
       
   311     
       
   312     # options
       
   313     graph_opts, graph_defs = multi_graph([(idx, name) for idx, (path, name) in enumerate(rrd_list)])
       
   314     interval_opts = INTERVAL_DEFS[interval](title)
       
   315     
       
   316     # combine
       
   317     opts = rrd.merge_opts(common_opts(), graph_opts, interval_opts)
       
   318     defs = data_defs + graph_defs
       
   319     
       
   320     # graph
       
   321     return rrd.graph(out, *defs, **opts)
   202 
   322 
   203 def mrtg (style, interval, title, rrd_path, out_path) :
   323 def mrtg (style, interval, title, rrd_path, out_path) :
   204     return _graph(style, interval, title, mrtg_data, rrd_path, out_path)
   324     return _graph(style, interval, title, mrtg_data, rrd_path, out_path)
   205 
   325 
   206 def collectd_ifoctets (style, interval, title, rrd_path, out_path) :
   326 def collectd_ifoctets (style, interval, title, rrd_path, out_path) :
   207     return _graph(style, interval, title, collectd_data, rrd_path, out_path)
   327     return _graph(style, interval, title, collectd_data, rrd_path, out_path)
   208 
   328 
       
   329 def pmacct_bytes (style, interval, title, rrd_path, out_path) :
       
   330     return _graph(style, interval, title, pmacct_data, rrd_path, out_path)
       
   331 
       
   332