# HG changeset patch # User Tero Marttila # Date 1359215560 -7200 # Node ID f9f5e669bace34f1180bbe2ece85efed0880b7e6 # Parent b21b2efe1e6c45f7cb0097132fbc8bf83ca9807d pvl.verkko: refactor into dhcp -> hosts -> web+db modules, reworking index page diff -r b21b2efe1e6c -r f9f5e669bace bin/pvl.verkko-dhcp --- a/bin/pvl.verkko-dhcp Sat Jan 26 14:40:05 2013 +0200 +++ b/bin/pvl.verkko-dhcp Sat Jan 26 17:52:40 2013 +0200 @@ -5,7 +5,8 @@ from pvl import __version__ import pvl.args import pvl.web.args -import pvl.verkko.wsgi +import pvl.verkko +import pvl.verkko.dhcp import optparse import logging; log = logging.getLogger('main') @@ -49,8 +50,11 @@ # parse cmdline options, args = parse_argv(argv, doc=__doc__) + # open + database = pvl.verkko.Database(options.database_read) + # app - application = pvl.web.args.apply(options, pvl.verkko.wsgi.Application, options.database_read) + application = pvl.web.args.apply(options, pvl.verkko.dhcp.Application, database) # wsgi wrapper run_simple('0.0.0.0', 8080, application, diff -r b21b2efe1e6c -r f9f5e669bace pvl/verkko/__init__.py --- a/pvl/verkko/__init__.py Sat Jan 26 14:40:05 2013 +0200 +++ b/pvl/verkko/__init__.py Sat Jan 26 17:52:40 2013 +0200 @@ -0,0 +1,4 @@ + +from pvl.verkko import db +from pvl.verkko.db import Database + diff -r b21b2efe1e6c -r f9f5e669bace pvl/verkko/db.py --- a/pvl/verkko/db.py Sat Jan 26 14:40:05 2013 +0200 +++ b/pvl/verkko/db.py Sat Jan 26 17:52:40 2013 +0200 @@ -1,3 +1,7 @@ +""" + SQLAlchemy Database tables model for pvl.verkko +""" + import sqlalchemy from sqlalchemy import * diff -r b21b2efe1e6c -r f9f5e669bace pvl/verkko/dhcp.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pvl/verkko/dhcp.py Sat Jan 26 17:52:40 2013 +0200 @@ -0,0 +1,72 @@ +# encoding: utf-8 +import pvl.web +import pvl.verkko.web + +from pvl.web import html, urls +from pvl.verkko import hosts + +import logging; log = logging.getLogger('pvl.verkko.dhcp') + +class Index (pvl.verkko.web.DatabaseHandler) : + TITLE = u"Päivölä Verkko" + + CSS = pvl.verkko.web.DatabaseHandler.CSS + ( + '/static/dhcp/forms.css', + ) + + def render_link (self, title, **opts) : + return html.a(href=self.url(hosts.ListHandler, **opts))(title) + + def render_links (self, attr, titlevalues) : + return html.ul( + html.li( + self.render_link(title, **{attr: value}) + ) for title, value in titlevalues + ) + + def render (self) : + return ( + html.h2("Interval"), + self.render_links('seen', ( + ("Hour", '1h'), + ("Day", '1d'), + #("Month", '30d'), + #("Year", '365d'), + )), + html.h2("State"), + self.render_links('state', ( + ("Valid", ('DHCPACK', 'DHCPRELEASE')), + ("Incomplete", ('DHCPDISCOVER', 'DHCPOFFER', 'DHCPREQUEST')), + ("Invalid", ('DHCPNAK', )), + )), + + html.h2("IP/MAC"), + html.form(action=self.url(hosts.ListHandler), method='get')( + html.fieldset( + html.ul( + html.li( + html.label(for_='ip')("IP"), + html.input(type='text', name='ip'), + ), + + html.li( + html.label(for_='mac')("MAC"), + html.input(type='text', name='mac'), + ), + + html.li( + html.input(type='submit', value="Search"), + ), + ) + ) + ), + ) + +class Application (pvl.verkko.web.Application) : + URLS = urls.Map(( + urls.rule('/', Index), + urls.rule('/hosts/', hosts.ListHandler), + urls.rule('/hosts/', hosts.ItemHandler), + urls.rule('/hosts/realtime', hosts.RealtimeHandler), + )) + diff -r b21b2efe1e6c -r f9f5e669bace pvl/verkko/hosts.py --- a/pvl/verkko/hosts.py Sat Jan 26 14:40:05 2013 +0200 +++ b/pvl/verkko/hosts.py Sat Jan 26 17:52:40 2013 +0200 @@ -1,4 +1,4 @@ -from pvl.verkko import db, web +from pvl.verkko import web, db from pvl.verkko.utils import parse_timedelta, IPv4Network from pvl.web import html @@ -10,7 +10,17 @@ import logging; log = logging.getLogger('pvl.verkko.hosts') -# XXX: this should actually be DHCPHost +## Model +import json +import time + +def dt2ts (dt) : + return int(time.mktime(dt.timetuple())) + +def ts2dt (ts) : + return datetime.datetime.fromtimestamp(ts) + +# TODO: this should be DHCPHost class Host (object) : DATE_FMT = '%Y%m%d' TIME_FMT = '%H:%M:%S' @@ -129,10 +139,19 @@ #state = db.dhcp_hosts.c., )) +## Controller +class BaseHandler (web.DatabaseHandler) : + """ + Common controller stuff for DHCP hosts + """ - - -class BaseHandler (web.Handler) : + CSS = ( + "/static/dhcp/hosts.css", + ) + JS = ( + #"/static/jquery/jquery.js" + ) + HOST_ATTRS = { 'id': Host.id, 'net': Host.gw, @@ -408,6 +427,10 @@ ) class ItemHandler (BaseHandler) : + """ + A specific DHCP host, along with a list of related hosts. + """ + def process (self, id) : self.hosts = self.query() self.host = self.hosts.get(id) @@ -456,29 +479,13 @@ class ListHandler (BaseHandler) : + """ + List of DHCP hosts for given filter. + """ + # pagination PAGE = 10 - # views - VIEWS = ( - ("Last hour", dict(seen='1h')), - ("Last day", dict(seen='24h')), - ("All", dict()), - ) + tuple( - ("Network " + network, dict(ip=network)) for network in ( - '194.197.235.0/24', - '10.1.0.0/16', - '10.4.0.0/16', - '10.5.0.0/16', - '10.6.0.0/16', - '10.10.0.0/16', - ) - ) + ( - ("Valid", dict(state=('DHCPACK', 'DHCPRELEASE'))), - ("Incomplete", dict(state=('DHCPDISCOVER', 'DHCPOFFER', 'DHCPREQUEST'))), - ("Invalid", dict(state=('DHCPNAK', ))), - ) - def process (self) : hosts = self.query() @@ -511,18 +518,9 @@ html.a(href=self.url())(html('«'), 'Back') if self.filters else None, ) -import json -import time - -def dt2ts (dt) : - return int(time.mktime(dt.timetuple())) - -def ts2dt (ts) : - return datetime.datetime.fromtimestamp(ts) - class RealtimeHandler (BaseHandler) : - TITLE = "Pseudo-Realtime hosts.." - CSS = web.Handler.CSS + ( + TITLE = "DHCP Pseudo-Realtime hosts.." + CSS = BaseHandler.CSS + ( 'http://code.jquery.com/ui/1.9.0/themes/base/jquery-ui.css', ) JS = ( diff -r b21b2efe1e6c -r f9f5e669bace pvl/verkko/urls.py --- a/pvl/verkko/urls.py Sat Jan 26 14:40:05 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -from werkzeug.routing import Map, Rule - -def rule (string, endpoint, **opts) : - return Rule(string, endpoint=endpoint, **opts) - -# URL -> Handler -from pvl.verkko import hosts - -# index page here :) -from pvl.verkko import web - -class Index (web.Handler) : - def render (self) : - html = web.html - - return ( - html.ul( - html.li( - "DHCP Hosts", - html.ul( - html.li(html.a(href=self.url(hosts.ListHandler, **opts))(title)) for title, opts in hosts.ListHandler.VIEWS - ) - ), - ) - ) - -urls = Map(( - rule('/', Index), - rule('/hosts/', hosts.ListHandler), - rule('/hosts/', hosts.ItemHandler), - rule('/hosts/realtime', hosts.RealtimeHandler), -)) - diff -r b21b2efe1e6c -r f9f5e669bace pvl/verkko/web.py --- a/pvl/verkko/web.py Sat Jan 26 14:40:05 2013 +0200 +++ b/pvl/verkko/web.py Sat Jan 26 17:52:40 2013 +0200 @@ -1,41 +1,20 @@ # encoding: utf-8 -import pvl.web.application - -# view -from pvl.web.html import tags as html +import pvl.web +from pvl.web import html, urls -from werkzeug.wrappers import Response -from werkzeug.exceptions import ( - HTTPException, - BadRequest, # 400 - NotFound, # 404 -) -from werkzeug.utils import redirect +import logging; log = logging.getLogger('pvl.verkko.web') -class Handler (pvl.web.application.Handler) : - CSS = ( - "/static/dhcp/hosts.css", - ) - JS = ( - #"/static/jquery/jquery.js" - ) +class DatabaseHandler (pvl.web.Handler) : + """ + Request handler with pvl.verkko.Database session + """ def __init__ (self, app, request, urls, params) : - super(Handler, self).__init__(app, request, urls, params) + super(DatabaseHandler, self).__init__(app, request, urls, params) # new ORM session per request self.db = app.db.session() - def title (self) : - """ - Render site/page title as text. - """ - - if self.TITLE : - return u"Päivölä Verkko :: {title}".format(title=self.TITLE) - else : - return u"Päivölä Verkko" - def cleanup (self) : """ After request processing. @@ -43,3 +22,18 @@ # XXX: SQLAlchemy doesn't automatically close these...? self.db.close() + +class Application (pvl.web.Application) : + """ + Application with pvl.verkko.Database + """ + + def __init__ (self, db, **opts) : + """ + db - pvl.verkko.Database + """ + + super(Application, self).__init__(**opts) + + self.db = db + diff -r b21b2efe1e6c -r f9f5e669bace pvl/verkko/wsgi.py --- a/pvl/verkko/wsgi.py Sat Jan 26 14:40:05 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -import pvl.web - -import logging; log = logging.getLogger('pvl.verkko.wsgi') - -from pvl.verkko import urls, db as database - -class Application (pvl.web.Application) : - URLS = urls.urls - - def __init__ (self, db, **opts) : - """ - Initialize app with db. - """ - - super(Application, self).__init__(**opts) - - self.db = database.Database(db) - diff -r b21b2efe1e6c -r f9f5e669bace pvl/web/application.py --- a/pvl/web/application.py Sat Jan 26 14:40:05 2013 +0200 +++ b/pvl/web/application.py Sat Jan 26 17:52:40 2013 +0200 @@ -1,5 +1,5 @@ """ - WSGI Application. + WSGI Application with pvl.web.urls-mapped Handlers building pvl.web.html. """ import werkzeug @@ -135,6 +135,9 @@ ) body = html(self.render()) + if not title : + raise Exception("%s: no page title!" % (self, )) + if self.app.layout : return self.app.layout.format( TITLE = unicode(title), diff -r b21b2efe1e6c -r f9f5e669bace pvl/web/html.py --- a/pvl/web/html.py Sat Jan 26 14:40:05 2013 +0200 +++ b/pvl/web/html.py Sat Jan 26 17:52:40 2013 +0200 @@ -7,6 +7,7 @@ """ # XXX: needs some refactoring for Text vs Tag now +# XXX: not all tags work in self-closing form, e.g. empty html.title() breaks badly import itertools as itertools import types as types