import rrd
import time
"""
RRDTool graph output
"""
def timestamp () :
return time.strftime("%Y/%m/%d %H:%M:%S %Z")
def common_opts () :
"""
Common options for all views
"""
return dict(
# output
imgformat = "PNG",
# lazy = True,
color = [
# disable border
# border = 0,
"SHADEA#ffffff00",
"SHADEB#ffffff00",
# keep background transparent
"BACK#ffffff00",
"SHADEB#ffffff00",
],
# labels
vertical_label = "bits/s",
units = "si",
# use logarithmic scaling
logarithmic = True,
# smooth out lines
slope_mode = True,
)
def overview_opts () :
"""
Graph statements for a single-source overview graph
"""
return dict(
width = 600,
height = 50,
), [
"CDEF:all=in0,out0,+",
"VDEF:max=all,MAXIMUM",
"VDEF:avg=all,AVERAGE",
"VDEF:min=all,MINIMUM",
"LINE1:in0#0000FF:In",
"LINE1:out0#00CC00:Out",
"GPRINT:max:%6.2lf %Sbps max",
"GPRINT:avg:%6.2lf %Sbps avg",
"GPRINT:min:%6.2lf %Sbps min\\l",
]
def detail_opts () :
"""
Common options for a single-source detail graph
"""
return dict(
# dimensions
width = 600,
height = 200,
), [
# values
'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" % "",
"COMMENT:%11s" % "Maximum",
"COMMENT:%11s" % "Average",
"COMMENT:%11s" % "Minimum",
"COMMENT:%11s\\l" % "Current",
"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:out0#00CC00:%4s" % "Out",
'GPRINT:out_max:%6.2lf %Sbps',
'GPRINT:out_avg:%6.2lf %Sbps',
'GPRINT:out_min:%6.2lf %Sbps',
'GPRINT:out_cur:%6.2lf %Sbps\\l',
# mark
"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
"""
return dict(
# labels
x_grid = "MINUTE:15:HOUR:1:HOUR:4:0:%H:%M",
# general info
title = "Daily %s" % (title, ),
# interval
start = "-24h",
)
def weekly_opts (title) :
return dict(
# labels
# x_grid = "MINUTE:15:HOUR:1:HOUR:4:0:%H:%M",
# general info
title = "Weekly %s" % (title, ),
# interval
start = "-7d",
)
def yearly_opts (title) :
return dict(
# labels
# x_grid = "MINUTE:15:HOUR:1:HOUR:4:0:%H:%M",
# general info
title = "Yearly %s" % (title, ),
# interval
start = "-1y",
)
INTERVAL_DEFS = {
'hourly': hourly_opts,
'daily': daily_opts,
'weekly': weekly_opts,
'yearly': yearly_opts,
}
def data_defs (idx, rrd, ds_in, ds_out, bytes=True, cf='AVERAGE') :
"""
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.
"""
params = dict(
ds_in=ds_in, ds_out=ds_out, cf=cf,
idx=idx, rrd=rrd,
)
if bytes :
# data sources, bytes/s
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
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 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_defs(idx, rrd, 'rx', 'tx')
def pmacct_data (idx, rrd) :
"""
Data sources for in/out bytes from a pmacct rrd
"""
return data_defs(idx, rrd, 'in', 'out')
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 = 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)
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)