pvl/verkko/hosts.py
author Tero Marttila <terom@paivola.fi>
Wed, 10 Oct 2012 22:45:50 +0300
changeset 3 5990b188c54b
parent 1 731d2df704f0
child 4 b09436772d46
permissions -rw-r--r--
web.Handler
from pvl.verkko import db, web

from pvl.html import tags as html

import socket # dns

import logging; log = logging.getLogger('pvl.verkko.hosts')

# XXX: this should actually be DHCPHost
class Host (object) :
    DATE_FMT = '%Y%m%d'

    def __init__ (self, ip, mac, name=None) :
        self.ip = ip
        self.mac = mac
        self.name = name
   
    def render_mac (self) :
        if not self.mac :
            return None

        elif len(self.mac) > (6 * 2 + 5) :
            return u'???'

        else :
            return unicode(self.mac)

    def render_name (self) :
        if self.name :
            return self.name.decode('ascii', 'replace')
        else :
            return None

    def when (self) :
        return '{frm} - {to}'.format(
                frm = self.first_seen.strftime(self.DATE_FMT),
                to  = self.last_seen.strftime(self.DATE_FMT),
        )

    def dns (self) :
        """
            Reverse-DNS lookup.
        """

        if not self.ip :
            return None
        
        sockaddrs = set(sockaddr for family, socktype, proto, canonname, sockaddr in socket.getaddrinfo(self.ip, 0, 0, 0, 0, socket.AI_NUMERICHOST))

        for sockaddr in sockaddrs :
            try :
                host, port = socket.getnameinfo(sockaddr, socket.NI_NAMEREQD)
            except socket.gaierror :
                continue

            return host

    def __unicode__ (self) :
        return u"{host.ip} ({host.mac})".format(host=self)

db.mapper(Host, db.dhcp_hosts, properties=dict(
    id      = db.dhcp_hosts.c.rowid,
    #_mac    = db.dhcp_hosts.c.mac,
    #_name   = db.dhcp_hosts.c.name,
))

HOST_ATTRS = {
    'id':   Host.id,
    'ip':   Host.ip,
    'mac':  Host.mac,
    'name': Host.name,
    'seen': Host.last_seen,
}
HOST_SORT = Host.last_seen.desc()

def render_hosts (hosts, title=None) :
    COLS = (
        #title          sort
        ('#',           None),
        ('IP',          'ip'),
        ('MAC',         'mac'),
        ('Hostname',    'name'),
        ('Seen',        'seen'),
    )

    return html.table(
        html.caption(title) if title else None,
        html.thead(
            html.tr(
                html.th(
                    html.a(href='?sort={name}'.format(name=sort))(title) if sort else (title)
                ) for title, sort in COLS
            )
        ),
        html.tbody(
            html.tr(class_=('alternate' if i % 2 else None), id=host.id)(
                html.td(class_='id')(
                    html.a(href='/hosts/{host.id}'.format(host=host))(
                        '#' #host['rowid'])
                    )
                ),
                html.td(class_='ip')(
                    html.a(href='/hosts/ip/{host.ip}'.format(host=host))(host.ip)
                ),
                html.td(class_='mac')(
                    html.a(href='/hosts/mac/{host.mac}'.format(host=host))(
                        host.render_mac()
                    )
                ),
                html.td(host.render_name()),
                html.td(host.when()),
            ) for i, host in enumerate(hosts)
        )
    )

def render_host (host, hosts) :
    title = 'DHCP Host: {host}'.format(host=host)
    
    attrs = (
            ('IP',          host.ip),
            ('MAC',         host.mac),
            ('Hostname',    host.name),
            ('DNS',         host.dns()),
    )

    return (
        html.h2('Host'),
        html.dl(
            (html.dt(title), html.dd(value)) for title, value in attrs
        ),

        html.h2('Related'),
        render_hosts(hosts),

        html.a(href='/hosts')(html('&laquo;'), 'Back'),
    )

class Handler (web.Handler) :
    TITLE = "DHCP Hosts"
    
    def index (self) :
        return render_hosts(self.hosts)

    def host (self, id) :
        host = self.hosts.get(id)
        
        if not host :
            raise web.NotFound("No such host: {id}".format(id=id))

        hosts = self.hosts.filter((Host.ip == host.ip) | (Host.mac == host.mac))
        
        # XXX
        #self.title = "DHCP Host: {host}".format(host=unicode(host))

        return render_host(host, hosts)
    
    def list (self, attr, value) :
        # fake host
        host = { 'ip': None, 'mac': None, 'name': None }

        if attr not in HOST_ATTRS :
            raise web.BadRequest("Invalid attribute: {attr}".format(attr=attr))

        host[attr] = value

        host = Host(**host)

        # query
        attr = HOST_ATTRS[attr]
        log.debug("%s == %s", attr, value)

        hosts = self.hosts.filter(attr == value)
        
        # XXX
        #self.title = "DHCP Hosts: {value}".format(value=value)

        return render_host(host, hosts)
    
    def process (self) :
        hosts = self.db.query(Host)

        # sort ?
        sort = self.request.args.get('sort')

        if sort :
            sort = HOST_ATTRS[sort]
        else :
            sort = HOST_SORT

        log.debug("sort: %s", sort)

        hosts = hosts.order_by(sort)
        
        # store
        self.hosts = hosts
    
    def render (self) :
        # index
        if not self.path :
            return self.index()
        
        # id
        elif len(self.path) == 1 :
            try :
                id, = self.path
                id = int(id)
            except ValueError as ex :
                raise web.BadRequest("Invalid host ID: {id}: {ex}".format(id=id, ex=ex))

            return self.host(id)

        # query
        elif len(self.path) == 2 :
            attr, value = self.path
            
            return self.list(attr, value)

        else :
            raise web.NotFound