pvl/verkko/hosts.py
author Tero Marttila <terom@paivola.fi>
Wed, 10 Oct 2012 23:36:23 +0300
changeset 6 0f243c59d5d1
parent 5 91970ce3fc6b
child 8 f64c44640b15
permissions -rw-r--r--
build urls
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,
))

class BaseHandler (web.Handler) :
    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 query (self) :
        hosts = self.db.query(Host)

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

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

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

        hosts = hosts.order_by(sort)

        # k
        return hosts
    
    def render_hosts (self, 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=self.url(sort=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=self.url(ItemHandler, id=host.id))(
                            '#' #host['rowid'])
                        )
                    ),
                    html.td(class_='ip')(
                        html.a(href=self.url(ListHandler, attr='ip', value=host.ip))(
                            host.ip
                        )
                    ),
                    html.td(class_='mac')(
                        html.a(href=self.url(ListHandler, attr='mac', value=host.mac))(
                            host.render_mac()
                        )
                    ),
                    html.td(host.render_name()),
                    html.td(host.when()),
                ) for i, host in enumerate(hosts)
            )
        )

    def render_host (self, host, hosts) :
        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'),
            self.render_hosts(hosts),

            html.a(href=self.url(IndexHandler))(html('&laquo;'), 'Back'),
        )

class IndexHandler (BaseHandler) :
    def process (self) :
        self.hosts = self.query()
    
    def title (self) :
        return "DHCP Hosts"

    def render (self) :
        return self.render_hosts(self.hosts)

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

        self.hosts = self.hosts.filter((Host.ip == self.host.ip) | (Host.mac == self.host.mac))
    
    def title (self) :
        return u"DHCP Host: {self.host}".format(self=self)

    def render (self) :
        return self.render_host(self.host, self.hosts)

class ListHandler (BaseHandler) :
    def process (self, attr, value) :
        # fake host
        _host = { 'ip': None, 'mac': None, 'name': None }

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

        _host[attr] = value

        self.host = Host(**_host)
        self.expression = "{attr}: {value}".format(attr=attr, value=value)

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

    def title (self) :
        return "DHCP Hosts: {self.expression}".format(self=self)
    
    def render (self) :
        return self.render_host(self.host, self.hosts)