--- a/pvl/verkko/hosts.py Wed Oct 24 16:04:27 2012 +0300
+++ b/pvl/verkko/hosts.py Wed Oct 24 16:04:41 2012 +0300
@@ -191,6 +191,16 @@
yield html.a(href=url(page=(page + 1)))(html("» Next"))
+ def render_filter (filter) :
+ value = filters.get(filter)
+
+ if value :
+ # XXX: multi-valued filters?
+ value = value[0]
+ else :
+ value = None
+
+ return html.input(type='text', name=filter, value=value)
table = html.table(
html.caption(title) if title else None,
@@ -209,7 +219,7 @@
),
(
html.td(class_=class_)(
- html.input(type='text', name=filter, value=filters.get(filter)) if filter else None
+ render_filter(filter) if filter else None
) for title, sort, filter, class_ in COLS
)
) if filters is not False else None
@@ -304,75 +314,103 @@
# pagination
PAGE = 10
+ # views
+ VIEWS = (
+ ("Last hour", dict(seen='1h')),
+ ("Last day", dict(seen='24h')),
+ ("All", dict()),
+ ) + tuple(
+ ("Network " + network, dict(ip=network)) for network in (
+ '194.197.235.0/24',
+ '10.1.0.0/16',
+ '10.4.0.0/16',
+ '10.5.0.0/16',
+ '10.6.0.0/16',
+ '10.10.0.0/16',
+ )
+ ) + (
+ ("Valid", dict(state=('DHCPACK', 'DHCPRELEASE'))),
+ ("Incomplete", dict(state=('DHCPDISCOVER', 'DHCPOFFER', 'DHCPREQUEST'))),
+ ("Invalid", dict(state=('DHCPNAK', ))),
+ )
+
+ def filter (self, attr, value) :
+ """
+ Return filter expression for given attr == value
+ """
+
+ if attr == 'seen' :
+ column = Host.last_seen
+
+ if value.isdigit() :
+ # specific date
+ date = datetime.datetime.strptime(value, Host.DATE_FMT).date()
+
+ return db.between(date.strftime(Host.DATE_FMT),
+ db.func.strftime(Host.DATE_FMT, Host.first_seen),
+ db.func.strftime(Host.DATE_FMT, Host.last_seen)
+ )
+ else :
+ # recent
+ timedelta = parse_timedelta(value)
+
+ return ((db.func.now() - Host.last_seen) < timedelta)
+
+ # XXX: for sqlite, pgsql should handle this natively?
+ # to seconds
+ #timeout = timedelta.days * (24 * 60 * 60) + timedelta.seconds
+
+ # WHERE strftime('%s', 'now') - strftime('%s', last_seen) < :timeout
+ #filter = (db.func.strftime('%s', 'now') - db.func.strftime('%s', Host.last_seen) < timeout)
+
+ elif attr == 'ip' :
+ column = Host.ip
+
+ # column is IPv4 string literal format...
+ if '/' in value :
+ return (db.func.inet(Host.ip).op('<<')(db.func.cidr(value)))
+ else :
+ return (db.func.inet(Host.ip) == db.func.inet(value))
+
+ else :
+ # preprocess
+ like = False
+
+ if value.endswith('*') :
+ like = value.replace('*', '%')
+
+ elif attr == 'mac' :
+ value = Host.normalize_mac(value)
+
+ # filter
+ column = self.HOST_ATTRS[attr]
+
+ if like :
+ return (column.like(like))
+ else :
+ return (column == value)
+
def process (self) :
hosts = self.query()
# filter?
- column = None
self.filters = {}
for attr in self.HOST_ATTRS :
- value = self.request.args.get(attr)
-
- if not value :
- continue
-
- if attr == 'seen' :
- column = Host.last_seen
-
- if value.isdigit() :
- # specific date
- date = datetime.datetime.strptime(value, Host.DATE_FMT).date()
+ values = self.request.args.getlist(attr)
- filter = db.between(date.strftime(Host.DATE_FMT),
- db.func.strftime(Host.DATE_FMT, Host.first_seen),
- db.func.strftime(Host.DATE_FMT, Host.last_seen)
- )
- else :
- # recent
- timedelta = parse_timedelta(value)
-
- filter = ((db.func.now() - Host.last_seen) < timedelta)
-
- # XXX: for sqlite, pgsql should handle this natively?
- # to seconds
- #timeout = timedelta.days * (24 * 60 * 60) + timedelta.seconds
-
- # WHERE strftime('%s', 'now') - strftime('%s', last_seen) < :timeout
- #filter = (db.func.strftime('%s', 'now') - db.func.strftime('%s', Host.last_seen) < timeout)
+ if not values :
+ continue
- elif attr == 'ip' :
- column = Host.ip
-
- # column is IPv4 string literal format...
- if '/' in value :
- filter = (db.func.inet(Host.ip).op('<<')(db.func.cidr(value)))
- else :
- filter = (db.func.inet(Host.ip) == db.func.inet(value))
-
- else :
- # preprocess
- like = False
-
- if value.endswith('*') :
- like = value.replace('*', '%')
+ filter = db.or_(*[self.filter(attr, value) for value in values])
- elif attr == 'mac' :
- value = Host.normalize_mac(value)
-
- # filter
- column = self.HOST_ATTRS[attr]
+ log.debug("filter %s: %s", attr, filter)
- if like :
- filter = (column.like(like))
- else :
- filter = (column == value)
-
hosts = hosts.filter(filter)
- self.filters[attr] = value
+ self.filters[attr] = values
- # sort XXX: default per filter column
- hosts = self.sort(hosts) #, column)
+ # sort XXX: default per filter column?
+ hosts = self.sort(hosts)
# page?
self.page = self.request.args.get('page')
@@ -386,7 +424,7 @@
def title (self) :
if self.filters :
- return "DHCP Hosts: {filters}".format(filters=', '.join(self.filters.itervalues()))
+ return "DHCP Hosts: {filters}".format(filters=', '.join(value for values in self.filters.itervalues() for value in values))
else :
return "DHCP Hosts"
--- a/pvl/verkko/urls.py Wed Oct 24 16:04:27 2012 +0300
+++ b/pvl/verkko/urls.py Wed Oct 24 16:04:41 2012 +0300
@@ -14,8 +14,12 @@
return (
html.ul(
- # TODO: self.url
- html.a(href=self.url(hosts.ListHandler))("DHCP Hosts"),
+ html.li(
+ "DHCP Hosts",
+ html.ul(
+ html.li(html.a(href=self.url(hosts.ListHandler, **opts))(title)) for title, opts in hosts.ListHandler.VIEWS
+ )
+ ),
)
)