pvl/verkko/hosts.py
changeset 0 91c739202f06
child 1 731d2df704f0
equal deleted inserted replaced
-1:000000000000 0:91c739202f06
       
     1 from pvl.verkko import db, web
       
     2 
       
     3 from pvl.html import tags as html
       
     4 
       
     5 import socket # dns
       
     6 
       
     7 import logging; log = logging.getLogger('pvl.verkko.hosts')
       
     8 
       
     9 class Host (object) :
       
    10     DATE_FMT = '%Y%m%d'
       
    11 
       
    12     def __init__ (self, ip, mac, name=None) :
       
    13         self.ip = ip
       
    14         self.mac = mac
       
    15         self.name = name
       
    16    
       
    17     def render_mac (self) :
       
    18         if not self.mac :
       
    19             return None
       
    20 
       
    21         elif len(self.mac) > (6 * 2 + 5) :
       
    22             return u'???'
       
    23 
       
    24         else :
       
    25             return unicode(self.mac)
       
    26 
       
    27     def render_name (self) :
       
    28         if self.name :
       
    29             return self.name.decode('ascii', 'replace')
       
    30         else :
       
    31             return None
       
    32 
       
    33     def when (self) :
       
    34         return '{frm} - {to}'.format(
       
    35                 frm = self.first_seen.strftime(self.DATE_FMT),
       
    36                 to  = self.last_seen.strftime(self.DATE_FMT),
       
    37         )
       
    38 
       
    39     def dns (self) :
       
    40         """
       
    41             Reverse-DNS lookup.
       
    42         """
       
    43 
       
    44         if not self.ip :
       
    45             return None
       
    46         
       
    47         sockaddrs = set(sockaddr for family, socktype, proto, canonname, sockaddr in socket.getaddrinfo(self.ip, 0, 0, 0, 0, socket.AI_NUMERICHOST))
       
    48 
       
    49         for sockaddr in sockaddrs :
       
    50             try :
       
    51                 host, port = socket.getnameinfo(sockaddr, socket.NI_NAMEREQD)
       
    52             except socket.gaierror :
       
    53                 continue
       
    54 
       
    55             return host
       
    56 
       
    57     def __unicode__ (self) :
       
    58         return u"{host.ip} ({host.mac})".format(host=self)
       
    59 
       
    60 db.mapper(Host, db.dhcp_hosts, properties=dict(
       
    61     id      = db.dhcp_hosts.c.rowid,
       
    62     #_mac    = db.dhcp_hosts.c.mac,
       
    63     #_name   = db.dhcp_hosts.c.name,
       
    64 ))
       
    65 
       
    66 HOST_SORT = {
       
    67     'id':   Host.id,
       
    68     'ip':   Host.ip,
       
    69     'mac':  Host.mac,
       
    70     'name': Host.name,
       
    71     'seen': Host.last_seen,
       
    72     None:   Host.last_seen.desc(),
       
    73 }
       
    74 
       
    75 def render_hosts (hosts, title=None) :
       
    76     COLS = (
       
    77         #title          sort
       
    78         ('#',           None),
       
    79         ('IP',          'ip'),
       
    80         ('MAC',         'mac'),
       
    81         ('Hostname',    'name'),
       
    82         ('Seen',        'seen'),
       
    83     )
       
    84 
       
    85     return html.table(
       
    86         html.caption(title) if title else None,
       
    87         html.thead(
       
    88             html.tr(
       
    89                 html.th(
       
    90                     html.a(href='?sort={name}'.format(name=sort))(title) if sort else (title)
       
    91                 ) for title, sort in COLS
       
    92             )
       
    93         ),
       
    94         html.tbody(
       
    95             html.tr(class_=('alternate' if i % 2 else None), id=host.id)(
       
    96                 html.td(class_='id')(
       
    97                     html.a(href='/hosts/{host.id}'.format(host=host))(
       
    98                         '#' #host['rowid'])
       
    99                     )
       
   100                 ),
       
   101                 html.td(class_='ip')(
       
   102                     html.a(href='/hosts/ip/{host.ip}'.format(host=host))(host.ip)
       
   103                 ),
       
   104                 html.td(class_='mac')(
       
   105                     html.a(href='/hosts/mac/{host.mac}'.format(host=host))(
       
   106                         host.render_mac()
       
   107                     )
       
   108                 ),
       
   109                 html.td(host.render_name()),
       
   110                 html.td(host.when()),
       
   111             ) for i, host in enumerate(hosts)
       
   112         )
       
   113     )
       
   114 
       
   115 def render_host (host, hosts) :
       
   116     title = 'DHCP Host: {host}'.format(host=host)
       
   117     
       
   118     attrs = (
       
   119             ('IP',          host.ip),
       
   120             ('MAC',         host.mac),
       
   121             ('Hostname',    host.name),
       
   122             ('DNS',         host.dns()),
       
   123     )
       
   124 
       
   125     return (
       
   126         html.h2('Host'),
       
   127         html.dl(
       
   128             (html.dt(title), html.dd(value)) for title, value in attrs
       
   129         ),
       
   130 
       
   131         html.h2('Related'),
       
   132         render_hosts(hosts),
       
   133 
       
   134         html.a(href='/')(html('«'), 'Back'),
       
   135     )
       
   136 
       
   137 from werkzeug.wrappers import Response
       
   138 
       
   139 def respond (request, db, path) :
       
   140     """
       
   141         Handle request
       
   142     """
       
   143 
       
   144     hosts = db.query(Host)
       
   145 
       
   146     # sort ?
       
   147     sort = request.args.get('sort')
       
   148     sort = HOST_SORT[sort]
       
   149     log.debug("sort: %s", sort)
       
   150 
       
   151     hosts = hosts.order_by(sort)
       
   152     
       
   153     # index
       
   154     if not path :
       
   155         title = "DHCP Hosts"
       
   156         html = render_hosts(hosts)
       
   157     
       
   158     # id
       
   159     elif len(path) == 1 :
       
   160         id, = path
       
   161         id = int(id)
       
   162 
       
   163         host = hosts.get(id)
       
   164         hosts = hosts.filter((Host.ip == host.ip) | (Host.mac == host.mac))
       
   165         
       
   166         # render
       
   167         title = "DHCP Host: {host}".format(host=unicode(host))
       
   168         html = render_host(host, hosts)
       
   169     
       
   170     # query
       
   171     elif len(path) == 2 :
       
   172         attr, value = path
       
   173         
       
   174         # fake host
       
   175         host = { 'ip': None, 'mac': None, 'name': None }
       
   176         host[attr] = value
       
   177 
       
   178         host = Host(**host)
       
   179 
       
   180         # query
       
   181         attr = HOST_SORT[attr]
       
   182         log.debug("%s == %s", attr, value)
       
   183 
       
   184         hosts = hosts.filter(attr == value)
       
   185         
       
   186         # render
       
   187         title = "DHCP Hosts: {value}".format(value=value)
       
   188         html = render_host(host, hosts)
       
   189 
       
   190     else :
       
   191         return Response("Not Found", status=404)
       
   192 
       
   193     # render
       
   194     html = web.render_layout(title, html)
       
   195 
       
   196     return Response(html, content_type='text/html; charset=UTF-8')
       
   197