# -*- Mode: Python; tab-width: 4 -*- # # medusa status extension # import string import time import regex import asyncore import http_server import medusa_gif import producers START_TIME = int(time.time()) class status_extension: hit_counter = http_server.counter() def __init__ (self, objects, regexp='/status\(/.*\)?'): self.objects = objects self.regexp = regex.compile (regexp) def __repr__ (self): return '<Status Extension (%d hits) at %x>' % ( self.hits, id(self) ) def match (self, request): path = request.uri[0] return self.regexp.match (path) == len(path) def handle_request (self, request): pass # Possible Targets: # /status # /status/channel_list # /status/medusa.gif # can we have 'clickable' objects? # [yes, we can use id(x) and do a linear search] # Dynamic producers: # HTTP/1.0: we must close the channel, because it's dynamic output # HTTP/1.1: we can use the chunked transfer-encoding, and leave # it open. def handle_request (self, request): [path, params, query, fragment] = request.uri self.hit_counter.increment() if path == '/status': up_time = string.join (english_time (int(time.time()) - START_TIME)) request['Content-Type'] = 'text/html' request.push ( '<html><body>\r\n<h1>Medusa Status Reports</h1>' '<b>Up:</b> %s' % up_time ) for i in range(len(self.objects)): request.push (self.objects[i].status()) request.push ('<hr>\r\n') request.push ( '<p><a href="/status/channel_list">Channel List</a>' '<hr>' '<img src="/status/medusa.gif" align=right width=97 height=61>' '</body></html>' ) elif path == '/status/channel_list': request['Content-Type'] = 'text/html' request.push ('<html><body>') request.push(channel_list_producer()) request.push ( '<hr>' '<img src="/status/medusa.gif" align=right width=97 height=61>' '</body></html>' ) elif path == '/status/medusa.gif': request['Content-Type'] = 'image/gif' request['Content-Length'] = len(medusa_gif.data) request.push (medusa_gif.data) else: channel.send_reply (404, path) class lines_producer: def __init__ (self, lines): self.lines = lines def ready (self): return len(self.lines) def more (self): if self.lines: chunk = self.lines[:50] self.lines = self.lines[50:] return string.join (chunk, '\r\n') + '\r\n' else: return '' class channel_list_producer (lines_producer): def __init__ (self): channel_reprs = map ( lambda x: '<' + repr(x)[1:-1] + '>', asyncore.socket_map.keys() ) channel_reprs.sort() lines_producer.__init__ ( self, ['<h1>Active Channel List</h1>', '<pre>' ] + channel_reprs + [ '</pre>', '<p><a href="/status">Status Report</a>' ] ) # this really needs a full-blown quoter... def sanitize (s): if '<' in s: s = string.join (string.split (s, '<'), '<') if '>' in s: s = string.join (string.split (s, '>'), '>') return s def html_reprs (list, front='', back=''): reprs = map ( lambda x,f=front,b=back: '%s%s%s' % (f,x,b), map (lambda x: sanitize(repr(x)), list) ) reprs.sort() return reprs # for example, tera, giga, mega, kilo # p_d (n, (1024, 1024, 1024, 1024)) # smallest divider goes first - for example # minutes, hours, days # p_d (n, (60, 60, 24)) def progressive_divide (n, parts): result = [] for part in parts: n, rem = divmod (n, part) result.append (rem) result.append (n) return result # b,k,m,g,t def split_by_units (n, units, dividers, format_string): divs = progressive_divide (n, dividers) result = [] for i in range(len(units)): if divs[i]: result.append (format_string % (divs[i], units[i])) result.reverse() return result def english_bytes (n): return split_by_units ( n, ('','k','M','G','T'), (1024, 1024, 1024, 1024, 1024), '%d %sb' ) def english_time (n): return split_by_units ( n, ('secs', 'mins', 'hours', 'days', 'weeks', 'years'), ( 60, 60, 24, 7, 52), '%d %s' )