hack hack hack some HTML output
authorTero Marttila <terom@fixme.fi>
Tue, 08 Dec 2009 20:41:16 +0200
changeset 5 e716718482c3
parent 4 8cd81ed8fadd
child 6 36b9aa4a7697
hack hack hack some HTML output
etc/generate.py
etc/targets.py
etc/templates/layout.html
etc/templates/overview-target.html
etc/templates/overview.html
etc/templates/target.html
mrtg.py
rrd/armo.switches.pvl_atk-luokka.rrd
rrdweb/graph.py
rrdweb/html.py
rrdweb/rrd.py
test.py
--- /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",
 )