# encoding: utf-8
"""
http://verkko.paivola.fi/rrd
"""
from pvl import web
from pvl.web import urls
from pvl.web.html import tags as html
import pvl.web.response
import pvl.rrd
import logging; log = logging.getLogger('pvl.verkko.rrd')
# errors
import os.path
class RRDNotFound (pvl.web.response.NotFound) :
"""
404 Not Found for tree/rrd.
"""
def __init__ (self, tree, rrd=None) :
if tree and rrd :
path = os.path.join(tree, rrd)
elif tree :
path = tree
elif rrd :
path = rrd
else :
path = ''
pvl.web.response.NotFound.__init__(self, path)
# View/Controller
class Handler (web.Handler) :
CSS = (
"/static/rrd/rrd.css",
)
def title (self) :
return u"Network RRD Traffic Graphs"
def breadcrumb (self, _tree, target=None) :
"""
Yield (title, url) navigation breadcrumbs
"""
yield '/', self.url(Index)
if _tree :
tree = ''
for part in _tree.split('/') :
tree = urls.join(tree, part)
yield part, self.url(Index, tree=tree)
if target :
# Target
yield target, self.url(Target, tree=tree, target=self.target)
def render_breadcrumb (self, tree, target=None) :
"""
Render breadcrumb -> html.div
"""
return html.div(id='breadcrumb')(html(" » ".join(
str(html.a(href=url)(node)) for node, url in self.breadcrumb(tree, target)))
)
class Index (Handler) :
"""
Browse trees, show overview graphs for targets.
"""
def _title (self) :
if self.tree :
return html(" » ".join(self.tree.split('/')))
else :
return ""
def url_tree (self, node) :
"""
Return url for given sub-node.
"""
if self.tree :
path = urls.join(self.tree, node)
else :
path = node
return self.url(tree=path)
def process (self, tree=None) :
"""
Lookup path -> self.tree.
"""
if tree :
try :
# XXX: unicode?
self.tree = self.app.rrd.tree(tree)
except ValueError as ex :
# mask
raise RRDNotFound(tree)
else :
# root
self.tree = self.app.rrd.tree()
def render_list (self, items) :
return (
html.li(class_=('odd' if idx % 2 else 'even'))(item) for idx, item in enumerate(items)
)
def render_rrd (self, rrd) :
"""
Render overview link/image for given rrd.
"""
target_url = self.url(Target, tree=self.tree, target=rrd)
graph_url = self.url(Graph, tree=self.tree, target=rrd)
return html.a(href=target_url)(
html.h3(rrd),
html.img(src=graph_url),
)
def render (self) :
"""
Render list of trees/rrds.
"""
trees, rrds = self.app.rrd.list(self.tree)
return self.render_breadcrumb(self.tree), html.div(id='overview')(
html.ul(id='tree-list')(
self.render_list(
html.a(href=self.url_tree(subtree))(subtree)
for subtree in trees)
) if trees else None,
html.hr() if trees and rrds else None,
html.ul(id='rrd-list')(
self.render_list(
self.render_rrd(rrd)
for rrd in rrds)
) if rrds else None,
)
class Target (Handler) :
"""
Show graphs for RRD file.
"""
def _title (self) :
return html(" » ".join(self.rrd.split('/')))
def process (self, target, tree=None) :
"""
Lookup tree/target -> self.target
"""
try :
self.tree = self.app.rrd.tree(tree)
self.rrd = self.app.rrd.rrd(target, self.tree)
self.target = target
except ValueError as ex :
raise RRDNotFound(tree, target)
def render_interval (self, interval, style='detail') :
"""
Render detail link/image.
"""
graph_url = self.url(Graph, tree=self.tree, target=self.target, style=style, interval=interval)
return (
html.h2(interval.title()),
html.img(src=graph_url)
)
INTERVALS = ('daily', 'weekly', 'yearly')
def render (self) :
return self.render_breadcrumb(self.tree, self.target), html.div(id='detail')(
self.render_interval(interval) for interval in self.INTERVALS
)
from pvl.invoke import merge # XXX
import werkzeug # wrap_file
class Graph (Handler) :
"""
Render graph for RRD.
"""
ARGS = { 'interval': 'daily', 'style': 'overview' }
def process (self, tree, target, style, interval) :
"""
Return Graph for given options.
"""
try :
self.tree = self.app.rrd.tree(tree)
self.rrd = self.app.rrd.rrd(target, self.tree)
except ValueError as ex :
raise RRDNotFound(tree, target)
self.style = style
self.interval = interval
def render_png (self) :
"""
Return PNG data as a file-like object for our graph.
"""
return self.app.rrd.graph(self.rrd, self.style, self.interval)
def respond (self) :
"""
Return Response for our request.
"""
# process params+args -> self.graph
process = merge(self.params, dict((arg, self.request.args.get(arg, default)) for arg, default in self.ARGS.iteritems()))
response = self.process(**process)
if response :
return response
# PNG output
file = self.render_png()
# respond with file wrapper
return pvl.web.response.image(self.response_file(file), type='png')
# WSGI
class Application (web.Application) :
# dispatch
URLS = urls.Map((
urls.rule('/', Index),
urls.rule('/<target>', Target),
urls.rule('/<path:tree>/', Index),
urls.rule('/<path:tree>/<target>', Target),
urls.rule('/<path:tree>/<target>.png', Graph),
))
def __init__ (self, rrd, **opts) :
"""
rrd - pvl.rrd.RRDDatabase
"""
super(Application, self).__init__(**opts)
self.rrd = rrd