--- a/rrdweb/graph.py Sat Jan 30 23:44:28 2010 +0200
+++ b/rrdweb/graph.py Sun Jan 31 01:32:48 2010 +0200
@@ -8,182 +8,309 @@
def timestamp () :
return time.strftime("%Y/%m/%d %H:%M:%S %Z")
-def common_opts () :
- """
- Common options for all views
+class BaseGraph (object) :
"""
-
- 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 () :
- """
- Common options for the overview graph
+ Construct the data layout and parameters for rendering the rrdtool.graph.
"""
- return dict(
- width = 600,
- height = 50,
- ), [
- "CDEF:all=in,out,+",
-
- "VDEF:max=all,MAXIMUM",
- "VDEF:avg=all,AVERAGE",
- "VDEF:min=all,MINIMUM",
+ # the unit label, used in both the Y scale and overview labels
+ UNIT_LABEL = None
- "LINE1:in#0000FF:In",
- "LINE1:out#00CC00:Out",
+ def __init__ (self, rrd_path, title) :
+ """
+ rrd_path - path to the .rrd file
+ title - target name to use for the graph title
+ """
- "GPRINT:max:%6.2lf %Sbps max",
- "GPRINT:avg:%6.2lf %Sbps avg",
- "GPRINT:min:%6.2lf %Sbps min\\l",
- ]
+ self.rrd_path = rrd_path
+ self.title = title
-def detail_opts () :
+
+ def data_sources (self, label_width=0) :
+ """
+ Yield the data sources to define as a series of
+ (id, statements)
+ tuples.
+ """
+
+ pass
+
+
+ def overview_data (self) :
+ """
+ Yield the data lines required to create a single summary data series called 'all'
+ """
+
+ ids, statements = zip(self.data_sources())
+
+ # draw lines
+ yield statements
+
+ # summarized data
+ assert len(ids) == 2
+
+ yield "CDEF:all=%s,+" % (ids.join(','))
+
+
+ def detail_data (self, label_width=4) :
+ """
+ Yield the data statements required to generate output for each line, as
+ (identifier, statements)
+ tuples
+ """
+
+ return data_sources(label_width=label_width)
+
+ def data (self) :
+ """
+ Yield the data statements to be used for the rrdtool graph
+ """
+
+ return []
+
+ def options (self) :
+ """
+ Yield the options to be used for the rrdtool graph as a dict
+ """
+
+ 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 = self.UNIT_LABEL,
+ units = "si",
+
+ # use logarithmic scaling
+ logarithmic = True,
+
+ # smooth out lines
+ slope_mode = True,
+ )
+
+
+ def generate (self, out_path) :
+ """
+ Generate the output PNG file to the given path, using this object's params/data
+ """
+
+ return rrd.graph(out_path, *self.data(), **self.options())
+
+class OverviewGraph (BaseGraph) :
"""
- Common options for the detail graph
+ A small 600x50 px graph, suitable for showing as part of a long list of targets.
+
+ May include some unlabed graph lines, and then a single summarized row of output.
+ """
+
+ def options (self) :
+ yield super(OverviewGraph, self).options()
+
+ yield 'width', 600
+ yield 'height', 50
+
+ def data (self) :
+ """
+ A single summary line
+ """
+
+ # recurse
+ yield super(OverviewGraph, self).data()
+
+ # have a summarized data series?
+ summarized = self.overview_data()
+
+ if summarized :
+ # summarize -> all
+ yield summarized
+
+ # calculate min/max/avg of the summary value
+ yield "VDEF:min=all,MINIMUM"
+ yield "VDEF:max=all,MAXIMUM"
+ yield "VDEF:avg=all,AVERAGE"
+
+ # display one line
+ yield "GPRINT:max:%%6.2lf %%S%s max" % self.UNIT_LABEL
+ yield "GPRINT:avg:%%6.2lf %%S%s avg" % self.UNIT_LABEL
+ yield "GPRINT:min:%%6.2lf %%S%s min\\l" % self.UNIT_LABEL
+
+class DetailGraph (BaseGraph) :
+ """
+ A large 600x200 px graph, suitable for showing for a specfic taget.
+
+ Can include any number of graph lines, and then a row of summarized data for each.
"""
- return dict(
- # dimensions
- width = 600,
- height = 200,
- ), [
- # values
- 'VDEF:in_max=in,MAXIMUM',
- 'VDEF:in_avg=in,AVERAGE',
- 'VDEF:in_min=in,MINIMUM',
- 'VDEF:out_max=out,MAXIMUM',
- 'VDEF:out_avg=out,AVERAGE',
- 'VDEF:out_min=out,MINIMUM',
+ def options (self) :
+ yield super(OverviewGraph, self).options()
- # legend/graph
- "COMMENT:%4s" % "",
- "COMMENT:%11s" % "Maximum",
- "COMMENT:%11s" % "Average",
- "COMMENT:%11s\\l" % "Minimum",
+ yield 'width', 600
+ yield 'height', 200
- "LINE1:in#0000FF:%4s" % "In",
- 'GPRINT:in_max:%6.2lf %Sbps',
- 'GPRINT:in_avg:%6.2lf %Sbps',
- 'GPRINT:in_min:%6.2lf %Sbps\\l',
+ def data (self) :
+ """
+ A complex breakdown into min/max/avg by line
+ """
+
+ # column titles
+ yield "COMMENT:%4s" % ""
+ yield "COMMENT:%11s" % "Minimum"
+ yield "COMMENT:%11s" % "Maximum"
+ yield "COMMENT:%11s\\l" % "Average"
+
+ # each row
+ # label isn't wide enough?
+ for id, data in self.detail_data(label_width=4) :
+ # the defs
+ yield data
- "LINE1:out#00CC00:%4s" % "Out",
- 'GPRINT:out_max:%6.2lf %Sbps',
- 'GPRINT:out_avg:%6.2lf %Sbps',
- 'GPRINT:out_min:%6.2lf %Sbps\\l',
-
- # mark
- "COMMENT:Generated %s\\r" % timestamp().replace(':', '\\:'),
- ]
+ # summarize data
+ yield 'VDEF:%s_min=%s,MINIMUM' % (id, id)
+ yield 'VDEF:%s_max=%s,MAXIMUM' % (id, id)
+ yield 'VDEF:%s_avg=%s,AVERAGE' % (id, id)
+
+ # the output
+ 'GPRINT:%s_min:%%6.2lf %%S%s' % (id, self.UNIT_LABEL)
+ 'GPRINT:%s_max:%%6.2lf %%S%s' % (id, self.UNIT_LABEL)
+ 'GPRINT:%s_avg:%%6.2lf %%S%s\\l' % (id, self.UNIT_LABEL)
+
+ # timestamp
+ yield "COMMENT:Generated %s\\r" % timestamp().replace(':', '\\:')
+
STYLE_DEFS = {
- 'overview': overview_opts,
- 'detail': detail_opts,
+ 'overview' : OverviewGraph,
+ 'detail' : DetailGraph,
}
-def daily_opts (title) :
+class DailyGraph (BaseGraph) :
"""
- Common options for the 'daily' view
+ Provides a grid/interval/title suitable for a 24-hour graph
"""
- return dict(
+ def options (self) :
+ yield super(DailyGraph, self).options()
+
+
# labels
- x_grid = "MINUTE:15:HOUR:1:HOUR:4:0:%H:%M",
+ yield 'x_grid', "MINUTE:15:HOUR:1:HOUR:4:0:%H:%M"
- # general info
- title = "Daily %s" % (title, ),
+ # title
+ yield 'title', "Daily %s" % (self.target_title, )
# interval
- start = "-24h",
- )
+ yield 'start', "-24h"
-def weekly_opts (title) :
- return dict(
+
+class WeeklyGraph (BaseGraph) :
+ """
+ Provides a grid/interval/title suitable for a 7-day graph
+ """
+
+ def options (self) :
+ yield super(DailyGraph, self).options()
+
# labels
# x_grid = "MINUTE:15:HOUR:1:HOUR:4:0:%H:%M",
# general info
- title = "Weekly %s" % (title, ),
+ yield 'title', "Weekly %s" % (self.target_title, )
# interval
- start = "-7d",
- )
+ yield 'start', "-7d"
-def yearly_opts (title) :
- return dict(
+
+class YearlyGraph (BaseGraph) :
+ """
+ Provides a grid/interval/title suitable for a 1-year graph
+ """
+
+ def options (self) :
+ yield super(DailyGraph, self).option()
+
# labels
# x_grid = "MINUTE:15:HOUR:1:HOUR:4:0:%H:%M",
# general info
- title = "Yearly %s" % (title, ),
+ yield 'title' "Yearly %s" % (self.target_title, )
# interval
- start = "-1y",
- )
+ yield 'start', "-1y"
INTERVAL_DEFS = {
- 'daily': daily_opts,
- 'weekly': weekly_opts,
- 'yearly': yearly_opts,
+ 'daily' : DailyGraph,
+ 'weekly' : WeeklyGraph,
+ 'yearly' : YearlyGraph,
}
-def mrtg_data (rrd_path) :
- """
- Data sources for network in/out from an MRTG rrd
+class IfOctetGraph (BaseGraph) :
"""
-
- return [
- # data sources, bytes/s
- r'DEF:in0=%s:ds0:AVERAGE' % rrd_path,
- r'DEF:out0=%s:ds1:AVERAGE' % rrd_path,
-
- # data, bits/s
- 'CDEF:in=in0,8,*',
- 'CDEF:out=out0,8,*',
+ Defines the data sources for a single bidirectional octet counter
+ """
+
+ # bits/s
+ UNIT_LABEL = "bps"
+
+ # the names for the in/out DS's
+ RRD_DS_RX = None
+ RRD_DS_TX = None
- ]
-
-def collectd_data (rrd_path) :
- """
- Data sources for if_octets from a collectd rrd
- """
+ def data_source (self, id, ds, color, label_width, label) :
+ # data source, bytes/s
+ yield "DEF:%s0=%s:%s:AVERAGE" % (id, self.rrd_path, ds)
- return [
- # data sources, bytes/s
- r'DEF:in0=%s:rx:AVERAGE' % rrd_path,
- r'DEF:out0=%s:tx:AVERAGE' % rrd_path,
+ # convert to bits/s
+ yield "CDEF:%s=%s0,8,*" % (id, id)
+
+ # draw line with label
+ yield "LINE1:%s%s:%*s" % (id, color, label_width, label)
+
- # data, bits/s
- 'CDEF:in=in0,8,*',
- 'CDEF:out=out0,8,*',
+ def data_sources (self, label_width=0) :
+ """
+ Yield the data sources to define as a series of
+ (id, line-statements)
+ tuples.
+ """
+
+ # yield the table of values
+ for id, ds, color, label in (
+ ('rx', self.RRD_DS_RX, "#0000FF", "In" ),
+ ('tx', self.RRD_DS_TX, "#00CC00", "Out" ),
+ ) :
+ yield id, self.data_source(id, ds, color, label_width, label)
- ]
+class MRTGGraph (IfOctetGraph) :
+ """
+ Data sources for an MRTG .rrd
+ """
+
+ # the DS names
+ RRD_DS_RX = 'ds0'
+ RRD_DS_TX = 'ds1'
+
+class CollectdIfOctetGraph (IfOctetGraph) :
+ """
+ Data sources for an if_octets .rrd from collectd
+ """
+
+ # the DS names
+ RRD_DS_RX = 'rx'
+ RRD_DS_TX = 'tx'
+
def _graph (style, interval, title, data_func, rrd_path, out_path) :
style_opts, style_vars = STYLE_DEFS[style]()