--- a/pvl/verkko/hosts.py Wed Oct 24 20:27:39 2012 +0300
+++ b/pvl/verkko/hosts.py Wed Oct 24 20:46:17 2012 +0300
@@ -150,20 +150,108 @@
return self.db.query(Host)
def sort (self, hosts, default=HOST_SORT) :
- self.sort = self.request.args.get('sort')
+ sort = self.request.args.get('sort')
- if self.sort :
- sort = self.HOST_ATTRS[self.sort]
+ if sort :
+ order_by = self.HOST_ATTRS[sort]
else :
- sort = default
+ order_by = default
- log.debug("sort: %s", sort)
+ log.debug("sort: %s", order_by)
- hosts = hosts.order_by(sort)
+ hosts = hosts.order_by(order_by)
# k
- return hosts
+ return sort, hosts
+ def filter_attr (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 filter (self, hosts) :
+ """
+ Apply filters from request.args against given hosts.
+
+ Returns (filters, hosts).
+ """
+
+ # filter?
+ filters = {}
+
+ for attr in self.HOST_ATTRS :
+ values = self.request.args.getlist(attr)
+
+ if not values :
+ continue
+
+ filter = db.or_(*[self.filter_attr(attr, value) for value in values])
+
+ log.debug("filter %s: %s", attr, filter)
+
+ hosts = hosts.filter(filter)
+ filters[attr] = values
+
+ return filters, hosts
+
+ def filters_title (self) :
+ """
+ Return a string representing the applied filters.
+ """
+
+ return ', '.join(value for values in self.filters.itervalues() for value in values)
+
def render_hosts (self, hosts, title=None, filters=False, page=None, hilight=None) :
COLS = (
#title sort filter class
@@ -286,7 +374,7 @@
return table
else :
return html.form(method='get', action=self.url())(
- html.input(type='hidden', name='sort', value=self.sort),
+ html.input(type='hidden', name='sort', value=self.sorts),
table,
)
@@ -297,8 +385,8 @@
if not self.host :
raise web.NotFound("No such host: {id}".format(id=id))
-
- self.hosts = self.sort(self.hosts.filter((Host.ip == self.host.ip) | (Host.mac == self.host.mac)))
+
+ self.sorts, self.hosts = self.sort(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)
@@ -362,83 +450,14 @@
("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?
- self.filters = {}
-
- for attr in self.HOST_ATTRS :
- values = self.request.args.getlist(attr)
-
- if not values :
- continue
-
- filter = db.or_(*[self.filter(attr, value) for value in values])
-
- log.debug("filter %s: %s", attr, filter)
-
- hosts = hosts.filter(filter)
- self.filters[attr] = values
+ # filter
+ self.filters, hosts = self.filter(hosts)
# sort XXX: default per filter column?
- hosts = self.sort(hosts)
+ self.sorts, hosts = self.sort(hosts)
# page?
self.page = self.request.args.get('page')
@@ -449,10 +468,10 @@
hosts = hosts.offset(self.page * self.PAGE).limit(self.PAGE)
self.hosts = hosts
-
+
def title (self) :
if self.filters :
- return "DHCP Hosts: {filters}".format(filters=', '.join(value for values in self.filters.itervalues() for value in values))
+ return "DHCP Hosts: {filters}".format(filters=self.filters_title())
else :
return "DHCP Hosts"
@@ -472,7 +491,7 @@
def ts2dt (ts) :
return datetime.datetime.fromtimestamp(ts)
-class RealtimeHandler (web.Handler) :
+class RealtimeHandler (BaseHandler) :
TITLE = "Pseudo-Realtime hosts.."
CSS = web.Handler.CSS + (
'http://code.jquery.com/ui/1.9.0/themes/base/jquery-ui.css',
@@ -494,10 +513,19 @@
( 'state', 'State', lambda host: host.state ),
)
-
def process (self) :
- hosts = self.db.query(Host).order_by(Host.last_seen.desc())
+ """
+ Either return JSON (if ?t=...), or fetch hosts/t for rendering.
+ """
+
+ hosts = self.db.query(Host)
t = self.request.args.get('t')
+
+ # always sorted by last_seen
+ hosts = hosts.order_by(Host.last_seen.desc())
+
+ # filter
+ self.filters, hosts = self.filter(hosts)
def host_params (host) :
yield 'id', host.id
@@ -545,7 +573,17 @@
self.t = self.hosts[0].last_seen
+ def title (self) :
+ if self.filters :
+ return "{title}: {filters}".format(title=self.TITLE, filters=self.filters_title())
+ else :
+ return self.TITLE
+
def render (self) :
+ """
+ Render page HTML and initial <table>, along with bootstrap JS (t0, configuration).
+ """
+
def column (name, title, fvalue, host) :
cls = name
@@ -556,6 +594,7 @@
params = dict(
url = self.url(),
+ filters = self.filters,
t = dt2ts(self.t),
host = self.url(ItemHandler, id='0'),
columns = [name for name, title, fvalue in self.COLUMNS]