pvl.verkko: refactor into dhcp -> hosts -> web+db modules, reworking index page
--- 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,
--- 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
+
--- 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 *
--- /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/<int:id>', hosts.ItemHandler),
+ urls.rule('/hosts/realtime', hosts.RealtimeHandler),
+ ))
+
--- 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 = (
--- 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/<int:id>', hosts.ItemHandler),
- rule('/hosts/realtime', hosts.RealtimeHandler),
-))
-
--- 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
+
--- 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)
-
--- 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),
--- 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