from pvl.verkko import db, web
from pvl.html import tags as html
import socket # dns
import logging; log = logging.getLogger('pvl.verkko.hosts')
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_SORT = {
'id': Host.id,
'ip': Host.ip,
'mac': Host.mac,
'name': Host.name,
'seen': Host.last_seen,
None: 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='/')(html('«'), 'Back'),
)
from werkzeug.wrappers import Response
def respond (request, db, path) :
"""
Handle request
"""
hosts = db.query(Host)
# sort ?
sort = request.args.get('sort')
sort = HOST_SORT[sort]
log.debug("sort: %s", sort)
hosts = hosts.order_by(sort)
# index
if not path :
title = "DHCP Hosts"
html = render_hosts(hosts)
# id
elif len(path) == 1 :
id, = path
id = int(id)
host = hosts.get(id)
hosts = hosts.filter((Host.ip == host.ip) | (Host.mac == host.mac))
# render
title = "DHCP Host: {host}".format(host=unicode(host))
html = render_host(host, hosts)
# query
elif len(path) == 2 :
attr, value = path
# fake host
host = { 'ip': None, 'mac': None, 'name': None }
host[attr] = value
host = Host(**host)
# query
attr = HOST_SORT[attr]
log.debug("%s == %s", attr, value)
hosts = hosts.filter(attr == value)
# render
title = "DHCP Hosts: {value}".format(value=value)
html = render_host(host, hosts)
else :
return Response("Not Found", status=404)
# render
html = web.render_layout(title, html)
return Response(html, content_type='text/html; charset=UTF-8')