pvl.verkko.leases: implement ui for dhcp_leases table
authorTero Marttila <terom@paivola.fi>
Sat, 26 Jan 2013 22:18:40 +0200
changeset 184 eef756d892e9
parent 183 8fbaaf0564dc
child 185 2e5e1a130070
pvl.verkko.leases: implement ui for dhcp_leases table
pvl/verkko/db.py
pvl/verkko/dhcp.py
pvl/verkko/leases.py
pvl/verkko/utils.py
static/dhcp/hosts.css
--- a/pvl/verkko/db.py	Sat Jan 26 22:18:16 2013 +0200
+++ b/pvl/verkko/db.py	Sat Jan 26 22:18:40 2013 +0200
@@ -42,6 +42,7 @@
 
     Column('starts',        DateTime,   nullable=False),
     Column('ends',          DateTime,   nullable=True),  # never
+    # TODO: renewed
 
     Column('state',         String,     nullable=True),
     Column('next',          String,     nullable=True),
--- a/pvl/verkko/dhcp.py	Sat Jan 26 22:18:16 2013 +0200
+++ b/pvl/verkko/dhcp.py	Sat Jan 26 22:18:40 2013 +0200
@@ -3,7 +3,7 @@
 import pvl.verkko.web
 
 from pvl.web import html, urls
-from pvl.verkko import hosts
+from pvl.verkko import hosts, leases
 
 import logging; log = logging.getLogger('pvl.verkko.dhcp')
 
@@ -68,5 +68,6 @@
         urls.rule('/hosts/',                 hosts.ListHandler),
         urls.rule('/hosts/<int:id>',         hosts.ItemHandler),
         urls.rule('/hosts/realtime',         hosts.RealtimeHandler),
+        urls.rule('/leases/',                leases.ListHandler),
     ))
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pvl/verkko/leases.py	Sat Jan 26 22:18:40 2013 +0200
@@ -0,0 +1,149 @@
+from pvl.verkko import web, db, table
+from pvl.verkko.utils import parse_timedelta, format_timedelta
+
+from pvl.web import html
+
+import datetime
+
+import logging; log = logging.getLogger('pvl.verkko.leases')
+
+class DHCPLease (object) :
+    """
+        A DHCP lease with ip/mac and starts/ends
+    """
+
+    DATE_FMT = '%Y%m%d %H:%M'
+    TIME_FMT = '%H:%M:%S'
+
+    @classmethod
+    def format_datetime (cls, dt) :
+        if dt.date() == datetime.date.today() :
+            return dt.strftime(cls.TIME_FMT)
+        else :
+            return dt.strftime(cls.DATE_FMT)
+    
+    def render_starts (self) :
+        return self.format_datetime(self.starts)
+
+    def render_ends (self) :
+        if self.ends :
+            return self.format_datetime(self.ends)
+        else :
+            return None
+    
+    def ends_class (self) :
+        if self.ends > datetime.datetime.now() :
+            return 'active'
+        else :
+            return None
+
+    @property
+    def length (self) :
+        if self.ends :
+            return self.ends - self.starts
+        else :
+            return datetime.datetime.now() - self.starts
+
+    def render_length (self) :
+        return format_timedelta(self.length)
+
+db.mapper(DHCPLease, db.dhcp_leases, properties=dict(
+
+))
+
+class LeasesTable (table.Table) :
+    """
+        Table of leases.
+    """
+
+    ITEMS   = "Leases"
+    COLUMNS = (
+        table.Column('ip',          "IP",       DHCPLease.ip,        
+            rowfilter   = True,
+        ),
+        table.Column('mac',         "MAC",      DHCPLease.mac,
+            rowfilter   = True,
+        ),
+        table.Column('hostname',    "Hostname", DHCPLease.hostname),
+        table.Column('starts',      "Starts",   DHCPLease.starts,       DHCPLease.render_starts),
+        table.Column('ends',        "Ends",     DHCPLease.ends,         DHCPLease.render_ends,
+            rowcss  = DHCPLease.ends_class
+        ),
+        table.Column('length',      "Lease",    DHCPLease.ends - DHCPLease.starts,  DHCPLease.render_length),
+    )
+    
+    # XXX: have to set again
+    ATTRS = dict((col.attr, col) for col in COLUMNS)
+
+    # default
+    SORT = DHCPLease.starts.desc()
+    PAGE = 20
+
+class LeasesHandler (table.TableHandler, web.DatabaseHandler) :
+    """
+        Combined database + <table>
+    """
+
+    CSS = web.DatabaseHandler.CSS + table.TableHandler.CSS + (
+        "/static/dhcp/hosts.css", 
+    )
+    
+    # model
+    TABLE = LeasesTable
+
+    def query (self) :
+        """
+            Database SELECT query.
+        """
+
+        return self.db.query(DHCPLease)
+
+    def filter_starts (self, value) :
+        return ((db.func.now() - DHCPLease.starts) < parse_timedelta(value))
+
+    def filter_ends (self, value) :
+        return ((DHCPLease.ends - db.func.now()) > parse_timedelta(value))
+
+    def filter_length (self, value) :
+        """
+            Filter by leases valid on given date.
+        """
+
+        dt = datetime.datetime.strptime(value, DHCPLease.DATE_FMT)
+
+        return db.between(dt, DHCPLease.starts, DHCPLease.ends)
+
+    def filter_ip (self, value) :
+        # column is IPv4 string literal format...
+        if '/' in value :
+            return (db.func.inet(DHCPLease.ip).op('<<')(db.func.cidr(value)))
+        else :
+            return (db.func.inet(DHCPLease.ip) == db.func.inet(value))
+
+
+class ListHandler (LeasesHandler) :
+    """
+        List of DHCP leases, using table.TableHandler -> LeasesTable.
+    """
+
+    #TABLE_ITEM_URL = ItemHandler
+
+    def process (self) :
+        # super
+        table.TableHandler.process(self)
+ 
+    def title (self) :
+        if self.filters :
+            return "DHCP Leases: {filters}".format(filters=self.filters_title())
+        else :
+            return "DHCP Leases"
+
+    def render (self) :
+        return (
+            self.render_table(self.query, filters=self.filters, sort=self.sorts, page=self.page),
+
+            #html.a(href=self.url())(html('&laquo;'), 'Back') if self.filters else None,
+        )
+
+
+
--- a/pvl/verkko/utils.py	Sat Jan 26 22:18:16 2013 +0200
+++ b/pvl/verkko/utils.py	Sat Jan 26 22:18:40 2013 +0200
@@ -41,9 +41,9 @@
     
     return timedelta(**what)
 
-def timedelta_str (td):
+def format_timedelta (td):
     """
-        datetime.timedelta -> short str
+        datetime.timedelta -> str
 
         >>> print timedelta_str(timedelta(days=1))
         1d
--- a/static/dhcp/hosts.css	Sat Jan 26 22:18:16 2013 +0200
+++ b/static/dhcp/hosts.css	Sat Jan 26 22:18:40 2013 +0200
@@ -7,7 +7,7 @@
 }
 
 /*
- * Details
+ * Host details
  */
 div.info {
     width: 80%;
@@ -69,3 +69,9 @@
 .dhcp-release {     background-color: #335533; }
 .dhcp-error {       background-color: #ff4444; }
 
+/*
+ * Leases 
+ */
+.ends.active {
+    font-weight: bold;
+}