--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/etc/generate.py Tue Dec 08 20:41:16 2009 +0200
@@ -0,0 +1,53 @@
+from rrdweb import graph, html
+
+# list of targets
+from targets import targets
+
+import os.path, fnmatch
+
+rrd_dir = "rrd"
+img_dir = "img"
+web_dir = "web"
+
+def target_graph (target, style, interval) :
+ # compose paths
+ rrd_path = os.path.join(rrd_dir, target.rrd_name())
+ out_path = os.path.join(img_dir, style, interval, target.img_name())
+
+ # graph
+ graph.mrtg(style, interval, target.title, rrd_path, out_path)
+
+
+def main (targets, filters, style='detail', interval='daily'):
+ if filters :
+ # filter targets
+ targets = [target for target in targets if any(fnmatch.fnmatch(target.name, filter) for filter in filters)]
+
+ # overview
+ html_fmt = html.Formatter(
+ url_prefix = "/~terom/rrdweb",
+ img_url = "%(prefix)s/img/%(style)s/%(interval)s/%(target)s",
+ target_url = "%(prefix)s/web/%(target)s.html",
+ )
+
+ # overview page
+ overview_path = os.path.join(web_dir, "index.html")
+ open(overview_path, 'w').write(html_fmt.overview(targets))
+
+ for target in targets :
+ print target.name
+
+ target_graph(target, 'overview', 'daily')
+
+ for interval in ('daily', 'weekly', 'yearly') :
+ target_graph(target, 'detail', interval)
+
+ # html
+ html_path = os.path.join(web_dir, target.name + '.html')
+ open(html_path, 'w').write(html_fmt.target(target))
+
+if __name__ == '__main__' :
+ import sys
+
+ main(targets, sys.argv[1:])
+
--- a/etc/targets.py Sun Dec 06 02:25:30 2009 +0200
+++ b/etc/targets.py Tue Dec 08 20:41:16 2009 +0200
@@ -1,36 +1,33 @@
from mrtg import *
+# port = 1xx
class Sw3com3300 (Host) :
- """
- 3com switches have port numbers 100+n
- """
-
def port (self, port_name, mbps, port) :
return super(Sw3com3300, self).port(port_name, mbps, port=port+100, port_title="#%d" % port)
-
# switches
armo = Sw3com3300('armo', 'armo.switches.pvl', 'geuGo6eecu')
kt_aula = Host('aula.kivitalo', 'aula.kivitalo.switches.pvl', 'chooyaizoizeimoi')
kt_atk = Host('atk-luokka.kivitalo', 'atk-luokka.kivitalo.switches.pvl', 'voichahphahqueej')
vt_atk = Host('atk-luokka.veturi', 'atk-luokka.veturi.switches.pvl', 'kuegenohthoogeex')
+# XXX: add missing hosts
targets = [
- armo.port('ranssila', 100, 17),
- armo.port('wlan', 100, 18),
- armo.port('jeriko', 100, 19),
- armo.port('olohuone', 100, 20),
- armo.port('puffy', 100, 21),
- armo.port('veturi-backup', 10, 22),
- armo.port('ranssi', 100, 23),
- armo.port('ylakerta', 100, 24),
- armo.port('atk-luokka', 1000, 25),
- armo.port('aula', 1000, 26),
+ armo.port('ranssila', 100, 17),
+ armo.port('wlan', 100, 18),
+ armo.port('jeriko', 100, 19),
+ armo.port('olohuone', 100, 20),
+ armo.port('puffy', 100, 21),
+ armo.port('veturi-backup', 10, 22),
+ armo.port('ranssi', 100, 23),
+ armo.port('ylakerta', 100, 24),
+ armo.port('atk-luokka', 1000, 25),
+ armo.port('aula', 1000, 26),
- kt_aula.port('ranssi', 1000, 25),
- kt_aula.port('armo', 1000, 26),
- kt_aula.port('kellari', 100, 27),
+ kt_aula.port('ranssi', 1000, 25),
+ kt_aula.port('armo', 1000, 26),
+ kt_aula.port('kellari', 100, 27),
kt_atk.port('luokka', 100, 17),
kt_atk.port('atk-luokka.veturi', 1000, 25),
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/etc/templates/layout.html Tue Dec 08 20:41:16 2009 +0200
@@ -0,0 +1,32 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html>
+ <head>
+ <title>Päivölä Network</title>
+ <link rel="Stylesheet" type="text/css" href="/static/style.css" />
+ <link rel="Stylesheet" type="text/css" href="/static/rrdweb.css" />
+ </head>
+ <body>
+ <div id="header">
+ Päivölä Network
+ </div>
+
+ <div id="menu">
+ <ul>
+ <li class="open"><a href="/">Main</a></li>
+ <li><a href="/issues/">Issues</a></li>
+ <li><a href="/weathermap/">Traffic</a></li>
+ <li class="open"><a href="/mrtg/">MRTG</a></li>
+ <li><a href="/smokeping/">Smokeping</a></li>
+ </ul>
+ </div>
+
+ <div id="content">
+ %(content)s
+ </div>
+
+ <div id="footer">
+ tech@paivola.fi
+ </div>
+ </body>
+</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/etc/templates/overview-target.html Tue Dec 08 20:41:16 2009 +0200
@@ -0,0 +1,6 @@
+ <li>
+ <h2>%(title)s</h2>
+ <a href="%(target_url)s">
+ <img src="%(daily_overview_img)s" />
+ </a>
+ </li>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/etc/templates/overview.html Tue Dec 08 20:41:16 2009 +0200
@@ -0,0 +1,6 @@
+<h1>Daily Overview</h1>
+
+<ul id='overview-graphs'>
+ %(overview_graphs)s
+</ul>
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/etc/templates/target.html Tue Dec 08 20:41:16 2009 +0200
@@ -0,0 +1,11 @@
+<h1>%(title)s</h1>
+
+<h2>Daily</h2>
+<img src="%(daily_img)s" />
+
+<h2>Weekly</h2>
+<img src="%(weekly_img)s" />
+
+<h2>Yearly</h2>
+<img src="%(yearly_img)s" />
+
--- a/mrtg.py Sun Dec 06 02:25:30 2009 +0200
+++ b/mrtg.py Tue Dec 08 20:41:16 2009 +0200
@@ -23,6 +23,20 @@
self.title = title
self.comment = comment
+ def rrd_name (self) :
+ """
+ Return the relative path to the .rrd file for this target
+ """
+
+ return "%s.rrd" % (self.name, )
+
+ def img_name (self) :
+ """
+ Return the relative path to the .png image for this target
+ """
+
+ return "%s.png" % (self.name, )
+
def statement (self, name, value) :
"""
Yield the line for a single stanza:
Binary file rrd/armo.switches.pvl_atk-luokka.rrd has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rrdweb/graph.py Tue Dec 08 20:41:16 2009 +0200
@@ -0,0 +1,170 @@
+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 () :
+ """
+ Common options for the overview graph
+ """
+
+ return dict(
+ width = 600,
+ height = 50,
+ ), [
+ "LINE1:in#0000FF:%4s" % "In",
+ "LINE1:out#00CC00:%4s" % "Out",
+ ]
+
+def detail_opts () :
+ """
+ Common options for the detail graph
+ """
+
+ 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',
+
+ # legend/graph
+ "COMMENT:%4s" % "",
+ "COMMENT:%11s" % "Maximum",
+ "COMMENT:%11s" % "Average",
+ "COMMENT:%11s\\l" % "Minimum",
+
+ "LINE1:in#0000FF:%4s" % "In",
+ 'GPRINT:in_max:%6.2lf %Sbps',
+ 'GPRINT:in_avg:%6.2lf %Sbps',
+ 'GPRINT:in_min:%6.2lf %Sbps\\l',
+
+ "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(':', '\\:'),
+ ]
+
+STYLE_DEFS = {
+ 'overview': overview_opts,
+ 'detail': detail_opts,
+}
+
+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 = {
+ 'daily': daily_opts,
+ 'weekly': weekly_opts,
+ 'yearly': yearly_opts,
+}
+
+def mrtg_data (rrd_path) :
+ """
+ Data sources for network in/out from an MRTG rrd
+ """
+
+ 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,*',
+
+ ]
+
+def mrtg (style, interval, title, rrd_path, out_path) :
+ style_opts, style_vars = STYLE_DEFS[style]()
+ interval_opts = INTERVAL_DEFS[interval](title)
+
+ opts = rrd.merge_opts(common_opts(), style_opts, interval_opts)
+ data = mrtg_data(rrd_path) + style_vars
+
+ return rrd.graph(out_path, *data, **opts)
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rrdweb/html.py Tue Dec 08 20:41:16 2009 +0200
@@ -0,0 +1,82 @@
+
+"""
+ HTML output
+"""
+
+import os.path
+
+class Formatter (object) :
+ TEMPLATE_DIR = 'etc/templates'
+
+ IMG_URL = '%(prefix)s?t=%(target)s&s=%(style)&i=%(interval)s'
+ TARGET_URL = '%(prefix)s?t=%(target)s'
+
+ def __init__ (self, template_dir=TEMPLATE_DIR, url_prefix='', img_url=IMG_URL, target_url=TARGET_URL) :
+ self.template_dir = template_dir
+ self.url_prefix = url_prefix
+ self.img_url = img_url
+ self.target_url = target_url
+
+ def tpl (self, name, **vars) :
+ """
+ Format and return given template
+ """
+
+ path = os.path.join(self.template_dir, name) + '.html'
+
+ # read contents
+ data = open(path).read()
+
+ # format
+ return data % vars
+
+ def page (self, content) :
+ """
+ Format page contents
+ """
+
+ return self.tpl('layout', content=content)
+
+ def fmt_img_url (self, style, interval, target) :
+ return self.img_url % dict(
+ prefix = self.url_prefix,
+ target = target.name,
+ style = style,
+ interval = interval,
+ )
+
+ def fmt_target_url (self, target) :
+ return self.target_url % dict(
+ prefix = self.url_prefix,
+ target = target.name,
+ )
+
+ def overview (self, targets) :
+ """
+ Format target listing
+ """
+
+ return self.page(self.tpl('overview',
+ overview_graphs = '\n'.join(
+ self.tpl('overview-target',
+ title = target.title,
+ daily_overview_img = self.fmt_img_url('overview', 'daily', target),
+ target_url = self.fmt_target_url(target),
+
+ ) for target in targets
+ )
+ ))
+
+ def target (self, target) :
+ """
+ Format a specific target
+ """
+
+ return self.page(self.tpl('target',
+ title = target.title,
+
+ daily_img = self.fmt_img_url('detail', 'daily', target),
+ weekly_img = self.fmt_img_url('detail', 'weekly', target),
+ yearly_img = self.fmt_img_url('detail', 'yearly', target),
+ ))
+
--- a/rrdweb/rrd.py Sun Dec 06 02:25:30 2009 +0200
+++ b/rrdweb/rrd.py Tue Dec 08 20:41:16 2009 +0200
@@ -50,6 +50,19 @@
# option value
return (key, str(value))
+def merge_opts (*all_opts) :
+ """
+ Merge the given series of opt dicts
+ """
+
+ out = dict()
+
+ for opts in all_opts :
+ # XXX: not strictly true, merge lists
+ out.update(opts)
+
+ return out
+
def run_cmd (func, pre_args, opts, post_args) :
"""
Run the given rrdtool.* function, formatting the given positional arguments and options.
--- a/test.py Sun Dec 06 02:25:30 2009 +0200
+++ b/test.py Tue Dec 08 20:41:16 2009 +0200
@@ -5,6 +5,52 @@
detail_size = (600, 200)
+# common options for all output
+detail_opts = dict(
+ # output
+ imgformat = "PNG",
+ lazy = True,
+
+ # dimensions
+ width = detail_size[0],
+ height = detail_size[1],
+
+ 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,
+)
+
+graph_opts = dict(
+ daily = dict(
+ # labels
+ x_grid = "MINUTE:15:HOUR:1:HOUR:4:0:%H:%M",
+
+ # general info
+ title = "Daily Traffic @ armo.switches.pvl -> atk-luokka",
+
+ # interval
+ start = "-24h",
+ ),
+)
+
+
rrd.graph(out_path,
# data sources, bytes/s
@@ -39,39 +85,5 @@
'GPRINT:out_avg:%6.2lf %Sbps',
'GPRINT:out_min:%6.2lf %Sbps\\l',
- # output
- imgformat = "PNG",
- lazy = True,
-
- # dimensions
- width = detail_size[0],
- height = detail_size[1],
-
- color = [
- # disable border
- # border = 0,
- "SHADEA#ffffff00",
- "SHADEB#ffffff00",
- # keep background transparent
- "BACK#ffffff00",
- "SHADEB#ffffff00",
- ],
-
- # labels
- vertical_label = "bits/s",
- units = "si",
- x_grid = "MINUTE:15:HOUR:1:HOUR:4:0:%H:%M",
-
- # use logarithmic scaling
- logarithmic = True,
-
- # smooth out lines
- slope_mode = True,
-
- # general info
- title = "Daily Traffic @ armo.switches.pvl -> atk-luokka",
-
- # interval
- start = "-24h",
)