pvl/verkko/hosts.py
author Tero Marttila <terom@paivola.fi>
Wed, 10 Oct 2012 23:16:25 +0300
changeset 4 b09436772d46
parent 3 5990b188c54b
child 5 91970ce3fc6b
permissions -rw-r--r--
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,
))

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) :

    def title (self) :
        pass
    
    def index (self) :
        return render_hosts(self.hosts)

    def detail (self) :
        return render_host(self.host, self.hosts)
    
    def list (self) :
        return render_host(self.host, self.hosts)
    
    def process (self, id=None, attr=None, value=None) :
        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)
        
        # lookup host
        if id :
            self.host = hosts.get(id)
            
            if not self.host :
                raise web.NotFound("No such host: {id}".format(id=id))

            self.hosts = hosts.filter((Host.ip == self.host.ip) | (Host.mac == self.host.mac))
            self.render = self.detail
            self.title = "DHCP Host: {host}".format(host=unicode(self.host))
        
        # lookup hosts
        elif attr and 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

            self.host = Host(**host)

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

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

        # list
        else :
            self.hosts = hosts
            self.render = self.index
            self.title = "DHCP Hosts"