Commit 290f6698 authored by Tomas Hlavacek's avatar Tomas Hlavacek

Decorator machinery implemented. One test Decorator for Cisco added.

Logging implemented.
Exception handling and logging on the application level implemented.
Few minor bugfixes.
parent 1b9293a9
......@@ -34,7 +34,7 @@ default_bird_sock = '/var/run/bird.ctl'
# Template dir relative to the index.py script
template_dir = 'templates'
index_template_file = 'index.html'
display_template_file = 'display.html'
table_decorator_template_file = 'tabledecorator.html'
# Paths to external programs
bin_ssh = '/usr/bin/ssh'
......@@ -49,3 +49,7 @@ STRING_SESSION_OVERLIMIT = "<em>Limit of maximum concurrently running sessions a
STRING_ARBITRARY_ERROR = "Error encountered. Operation aborted. See log for further details."
STRING_IPADDRESS = "IP address"
STRING_IPSUBNET = "IP subnet"
# URL generator functions
def getASNURL(asn):
return 'https://apps.db.ripe.net/search/query.html?searchtext=AS%s&flags=&sources=RIPE_NCC&grssources=&inverse=&types=' % asn
<!--
<pre xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/">
<h1>DEBUG</h1>
${str(table)}
${str(table_header)}
${str(table_headline)}
</pre>
-->
<div id="resultbody" xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/">
<py:if test="defined('table_headline') and (table_headline)">
<h3>${table_headline}</h3>
</py:if>
<py:if test="defined('before') and (before)">
${before}
</py:if>
<table border="1">
<tr>
<py:for each="thidx,th in enumerate(table_header)">
<th bgcolor="#C0C0C0">${th}</th>
</py:for>
</tr>
<py:for each="tridx,tr in enumerate(table)">
<tr>
<py:for each="tdidx,td in enumerate(tr)">
<td bgcolor="${td[1]}">${td[0]}</td>
</py:for>
</tr>
</py:for>
</table>
<py:if test="defined('after') and (after)">
${after}
</py:if>
</div>
......@@ -37,7 +37,7 @@ import traceback
def testRouterCommand(router=0,command=0,params=[]):
r = config.routers[router]
try:
if(not r.runCommand(r.listCommands()[command],params,ulg.Decorator())):
if(not r.runCommand(r.listCommands()[command],params,ulg.DecoratorHelper())):
print "WARN: Outpit of test running command "+str(command)+" on router "+str(router)+" with parameters:"+str(params)+" is empty."
return False
......@@ -142,7 +142,7 @@ def testULGLock():
def testULGRunParameter(router=0,command=4,params=['91.210.16.1']):
r = config.routers[router]
try:
if(not r.runCommand(r.listCommands()[command],params,ulg.Decorator())):
if(not r.runCommand(r.listCommands()[command],params,ulg.DecoratorHelper())):
print "WARN: Output of test running command "+str(command)+" on router "+str(router)+" with parameters:"+str(params)+" is empty."
return False
......@@ -154,6 +154,16 @@ def testULGRunParameter(router=0,command=4,params=['91.210.16.1']):
print "OK: Test running command "+str(command)+" on router "+str(router)+" with parameters:"+str(params)
return True
def testULGLog(testmessage="Test message no. 1."):
try:
ulgmodel.log(testmessage)
print "OK: Logging test. Check logfile for message: "+testmessage
return True
except Exception as e:
print "FAIL: Logging test."
print traceback.format_exc()
return False
#####################################
results = []
......@@ -187,6 +197,8 @@ if __name__=="__main__":
runTest(testULGLock())
runTest(testULGLog())
reportResults()
......@@ -35,11 +35,21 @@ class ULGCron:
def __init__(self):
pass
def run(self):
def rescanRouters(self):
for r in config.routers:
r.rescanHook()
def clearSessions(self):
pass
def run(self):
ulgmodel.log("ULG cron run.")
self.rescanRouters()
self.clearSessions()
ulgmodel.log("ULG cron finished.")
# main
if __name__=="__main__":
return ULGCron().run()
sys.exit(ULGCron().run())
......@@ -30,6 +30,7 @@ import md5
import pickle
import re
import fcntl
import traceback
import config
import defaults
......@@ -88,9 +89,8 @@ class Session(object):
f = open(fn,'wb')
pickle.dump(self, f)
f.close()
finally:
# TODO log("Saving session failed: " + traceback.format_exc())
pass
except:
ulgmodel.log("Saving session failed: " + traceback.format_exc())
def getSessionId(self):
return self.sessionid
......@@ -155,7 +155,7 @@ class Session(object):
else:
return None
class Decorator:
class DecoratorHelper:
def __init__(self):
pass
......@@ -184,13 +184,25 @@ class Decorator:
return self.getURL('error',parameters)
def getRouterID(self,router):
# TODO
for ridx,r in enumerate(config.routers):
if(r == router):
return ridx
return 0
def getCommandID(self,router,command):
# TODO
for cidx,c in enumerate(router.listCommands()):
if(c == command):
return cidx
return 0
def pre(self,text):
return ('<pre>%s</pre>' % text)
def ahref(self,url,text):
return ('<a href=%s>%s</a>' % (url,text))
class ULGCgi:
def __init__(self):
self.loader=TemplateLoader(
......@@ -198,11 +210,7 @@ class ULGCgi:
auto_reload=True
)
self.decorator = Decorator()
def rescanRouters(self):
for r in config.routers:
r.rescanHook()
self.decorator_helper = DecoratorHelper()
def increaseUsage(self):
u = 0
......@@ -217,7 +225,7 @@ class ULGCgi:
# Acquire lock
fcntl.lockf(lf, fcntl.LOCK_EX)
except IOError,ValueError:
# TODO: log("Locking mechanism failure: "+str(e))
ulgmodel.log("Locking mechanism failure: "+str(e))
return False
try:
......@@ -232,7 +240,7 @@ class ULGCgi:
lf.close()
return False
except IOError as e:
# TODO: log("Locking mechanism update failure: "+str(e))
ulgmodel.log("Locking mechanism update failure: "+str(e))
return False
def decreaseUsage(self):
......@@ -248,7 +256,7 @@ class ULGCgi:
# Acquire lock
fcntl.lockf(lf, fcntl.LOCK_EX)
except IOError,ValueError:
# TODO: log("Locking mechanism failure: "+str(e))
ulgmodel.log("Locking mechanism failure: "+str(e))
return False
try:
......@@ -263,7 +271,7 @@ class ULGCgi:
lf.close()
return
except IOError as e:
# TODO: log("Locking mechanism update failure: "+str(e))
ulgmodel.log("Locking mechanism update failure: "+str(e))
pass
def stopSessionOverlimit(self,session):
......@@ -290,10 +298,10 @@ class ULGCgi:
# define trivial thread function
def commandThreadBody(session,decreaseUsageMethod):
try:
session.setResult(session.getRouter().runCommand(session.getCommand(),session.getParameters(),self.decorator))
session.setResult(session.getRouter().runCommand(session.getCommand(),session.getParameters(),self.decorator_helper))
session.setFinished()
except Exception as e:
# TODO: log("Exception occured while running a command")
ulgmodel.log("ERROR: Exception occured while running a command:" + traceback.format_exc())
session.setPreResult(traceback.format_exc())
session.setFinished()
finally:
......@@ -326,15 +334,12 @@ class ULGCgi:
def renderULGIndex(self,routerid=0,commandid=0,sessionid=None):
template = self.loader.load(defaults.index_template_file)
# rescan routers - is it a good place for rescan?
self.rescanRouters()
return template.generate(defaults=defaults,
routers=config.routers,
default_routerid=routerid,
default_commandid=commandid,
default_sessionid=sessionid,
getFormURL=self.decorator.getRuncommandURL
getFormURL=self.decorator_helper.getRuncommandURL
).render('html', doctype='html')
......@@ -357,25 +362,22 @@ class ULGCgi:
self.runCommand(session)
# redirect to the session display
return self.HTTPRedirect(self.decorator.getDisplayURL(session.getSessionId()))
return self.HTTPRedirect(self.decorator_helper.getDisplayURL(session.getSessionId()))
def renderULGResult(self,sessionid=None):
if(sessionid==None):
return self.HTTPRedirect(self.decorator.getErrorURL())
return self.HTTPRedirect(self.decorator_helper.getErrorURL())
session = Session.load(sessionid)
if(session == None):
return self.HTTPRedirect(self.decorator.getErrorURL())
return self.HTTPRedirect(self.decorator_helper.getErrorURL())
if(session.isFinished()):
refresh=None
else:
refresh = defaults.refresh_interval
# rescan routers - is it a good place for this?
self.rescanRouters()
template = self.loader.load(defaults.index_template_file)
return template.generate(defaults=defaults,
routers=config.routers,
......@@ -385,7 +387,7 @@ class ULGCgi:
default_sessionid=sessionid,
result=Markup(session.getResult()) if (session.getResult()) else None,
refresh=refresh,
getFormURL=self.decorator.getRuncommandURL
getFormURL=self.decorator_helper.getRuncommandURL
).render('html', doctype='html')
def renderULGError(self,sessionid=None,**params):
......@@ -404,7 +406,7 @@ class ULGCgi:
default_sessionid=None,
result=Markup(result_text) if(result_text) else None,
refresh=0,
getFormURL=self.decorator.getRuncommandURL
getFormURL=self.decorator_helper.getRuncommandURL
).render('html', doctype='html')
def renderULGDebug(self,**params):
......@@ -422,7 +424,7 @@ class ULGCgi:
default_sessionid=None,
result=Markup(result_text) if(result_text) else None,
refresh=0,
getFormURL=self.decorator.getRuncommandURL
getFormURL=self.decorator_helper.getRuncommandURL
).render('html', doctype='html')
def index(self, **params):
......@@ -446,25 +448,31 @@ class ULGCgi:
# main
if __name__=="__main__":
form = cgi.FieldStorage()
handler = ULGCgi()
try:
form = cgi.FieldStorage()
handler = ULGCgi()
print "Content-Type: text/html\n"
print "Content-Type: text/html\n"
action = form.getvalue('action',None)
params = dict([(k,form.getvalue(k)) for k in form.keys() if k != 'action'])
action = form.getvalue('action',None)
params = dict([(k,form.getvalue(k)) for k in form.keys() if k != 'action'])
if(action):
if(action == 'index'):
if(action):
if(action == 'index'):
handler.index(**params)
elif(action == 'runcommand'):
handler.runcommand(**params)
elif(action == 'display'):
handler.display(**params)
elif(action == 'error'):
handler.display(**params)
elif(action == 'debug'):
handler.debug(**params)
else:
ulgmodel.log('ERROR: Unknown action called: '+action+'\n')
handler.display(**params)
else:
handler.index(**params)
if(action == 'runcommand'):
handler.runcommand(**params)
if(action == 'display'):
handler.display(**params)
if(action == 'error'):
handler.display(**params)
if(action == 'debug'):
handler.debug(**params)
else:
handler.index(**params)
except Exception as e:
ulgmodel.log("ERROR in CGI: "+traceback.format_exc())
......@@ -31,6 +31,76 @@ STRING_EXPECT_SSH_NEWKEY='Are you sure you want to continue connecting'
STRING_EXPECT_PASSWORD='(P|p)assword:'
class CiscoCommandBgpIPv4Sum(ulgmodel.TextCommand):
COMMAND_TEXT='show bgp ipv4 unicast summary'
TABLE_HEADER_REGEXP='^\s*(Neighbor)\s+(V)\s+(AS)\s+(MsgRcvd)\s+(MsgSent)\s+(TblVer)\s+(InQ)\s+(OutQ)\s+(Up/Down)\s+(State/PfxRcd)\s*$'
TABLE_LINE_REGEXP='^\s*([0-9\.]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([a-z0-9:]+)\s+([a-zA-Z0-9]+|[a-zA-Z0-9]+\s\(Admin\))\s*$'
RED_STATES = ['Idle', 'Active']
YELLOW_STATES = ['Idle (Admin)',]
def __init__(self,name=None):
self.command=CiscoCommandBgpIPv4Sum.COMMAND_TEXT
self.param_specs=[]
if(name==None):
if(self.param_specs):
self.name=self.command % tuple([('<'+str(c.getName())+'>') for c in self.param_specs])
else:
self.name=self.command
else:
self.name = name
def _decorateTableLine(self,line,decorator_helper):
lrm = re.compile(CiscoCommandBgpIPv4Sum.TABLE_LINE_REGEXP).match(line)
if(lrm):
# color selection
if(lrm.group(10) in CiscoCommandBgpIPv4Sum.YELLOW_STATES):
color = ulgmodel.TableDecorator.YELLOW
elif(lrm.group(10) in CiscoCommandBgpIPv4Sum.RED_STATES):
color = ulgmodel.TableDecorator.RED
else:
color = ulgmodel.TableDecorator.GREEN
return [(lrm.group(1),color),(lrm.group(2),color),
(decorator_helper.ahref(defaults.getASNURL(lrm.group(3)),lrm.group(3)),color),(lrm.group(4),color),
(lrm.group(5),color),(lrm.group(6),color),
(lrm.group(7),color),(lrm.group(8),color),
(lrm.group(9),color),(lrm.group(10),color),]
else:
raise Exception("Can not parse line: "+l)
def decorateResult(self,result,router=None,decorator_helper=None):
if((not router) or (not decorator_helper)):
return "<pre>\n%s\n</pre>" % result
lines = str.splitlines(result)
before=''
after=''
table=[]
table_header=[]
tb = False
header_regexp = re.compile(CiscoCommandBgpIPv4Sum.TABLE_HEADER_REGEXP)
line_regexp = re.compile(CiscoCommandBgpIPv4Sum.TABLE_LINE_REGEXP)
for l in lines:
if(tb):
# inside table body
table.append(self._decorateTableLine(l,decorator_helper))
else:
# not yet in the table body
before = before + l + '\n'
# should we switch to table body?
thrm = header_regexp.match(l)
if(thrm):
tb = True
table_header = [g for g in thrm.groups()]
return ulgmodel.TableDecorator(table,table_header,'Test TableDecorator!!!',decorator_helper.pre(before)).decorate()
class CiscoRouter(ulgmodel.RemoteRouter):
RegExIPv4Subnet = '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}(/[0-9]{1,2}){0,1}]$'
RegExIPv4 = '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$'
......@@ -41,6 +111,7 @@ class CiscoRouter(ulgmodel.RemoteRouter):
ulgmodel.TextCommand('show bgp ipv4 unicast %s', [ulgmodel.TextParameter(RegExIPv4Subnet,name=defaults.STRING_IPSUBNET)]),
ulgmodel.TextCommand('show bgp ipv6 unicast %s', [ulgmodel.TextParameter(RegExIPv6Subnet,name=defaults.STRING_IPSUBNET)]),
CiscoCommandBgpIPv4Sum('show bgp ipv4 unicast summary (+DECORATOR)'),
ulgmodel.TextCommand('show bgp ipv4 unicast summary'),
ulgmodel.TextCommand('show bgp ipv6 unicast summary'),
ulgmodel.TextCommand('show bgp ipv4 unicast neighbor %s',[ulgmodel.TextParameter(RegExIPv4,name=defaults.STRING_IPADDRESS)]),
......
......@@ -21,10 +21,26 @@
# Imports
import os
import re
from time import localtime, strftime
from genshi.template import TemplateLoader
from genshi.core import Markup
import defaults
def log(*messages):
try:
with open(defaults.log_file, 'a') as l:
for m in messages:
l.write(strftime("%b %d %Y %H:%M:%S: ", localtime()) + m)
except Exception:
pass
def debug(message):
log('DEBUG:' + message)
class PersistentStorgage(object):
def __init__(self,filename=defaults.persistent_storage_file):
self.filename = filename
......@@ -54,6 +70,48 @@ class PersistentStorgage(object):
def getDict(self):
return self.data
class TableDecorator(object):
WHITE = '#FFFFFF'
RED = '#FF0000'
GREEN = '#00FF00'
BLUE = '#0000FF'
YELLOW = '#FFFF00'
BLACK = '#000000'
def __init__(self, table, table_header, table_headline=None, before=None, after=None):
self.table = table
self.table_header = table_header
self.table_headline = table_headline
self.before = before
self.after = after
self.loader=TemplateLoader(
os.path.join(os.path.dirname(__file__), defaults.template_dir),
auto_reload=True
)
def decorate(self):
def preprocessTableCell(td):
if(isinstance(td,(list,tuple))):
if(len(td) >= 2):
return (Markup(str(td[0])),Markup(str(td[1])))
elif(len(td) == 1):
return (Markup(str(td[0])),Markup(TableDecorator.WHITE))
else:
return ('',Markup(TableDecorator.WHITE))
else:
return (Markup(str(td)),Markup(TableDecorator.WHITE))
t = [[preprocessTableCell(td) for td in tr ] for tr in self.table]
template = self.loader.load(defaults.table_decorator_template_file)
return template.generate(table=t,
table_header=self.table_header,
table_headline=Markup(self.table_headline) if self.table_headline else '',
before=Markup(self.before) if self.before else '',
after=Markup(self.after) if self.after else '',
).render('html', doctype='html')
class TextParameter(object):
def __init__(self,pattern='.*',name=defaults.STRING_PARAMETER,default=''):
......@@ -138,6 +196,8 @@ class TextCommand(object):
self.name=command % tuple([('<'+str(c.getName())+'>') for c in self.param_specs])
else:
self.name=command
else:
self.name=name
def getParamSpecs(self):
return self.param_specs
......@@ -147,19 +207,16 @@ class TextCommand(object):
def checkParamsInput(self,input):
if(((not input) and (self.getParamSpecs()))or((input) and (not self.getParamSpecs()))):
# TODO log/debug
print "Failed checking parameter count to zero."
log("Failed checking parameter count to zero, input:"+str(input)+' .')
return False
if(len(input)!=len(self.getParamSpecs())):
# TODO log
print "Failed checking parameter count (nonzero)."
log("Failed checking parameter count (nonzero), input: "+str(input)+' .')
return False
for pidx,p in enumerate(self.getParamSpecs()):
if not p.checkInput(input[pidx]):
# TODO log
print "Failed checking parameter: "+input[pidx]
log("Failed checking parameter: "+str(input[pidx]))
return False
return True
......@@ -186,7 +243,7 @@ class TextCommand(object):
def rescanHook(self,router):
pass
def decorateResult(self,result,router=None,decorator=None):
def decorateResult(self,result,router=None,decorator_helper=None):
return "<pre>\n%s\n</pre>" % result
class AnyCommand(TextCommand):
......@@ -233,11 +290,11 @@ class Router(object):
r = r + ': '+error+'</em>' if error else r+'.</em>'
return r
def runCommand(self,command,parameters,decorator):
def runCommand(self,command,parameters,decorator_helper):
c = command.getCommandText(parameters)
if(c == None):
#TODO: log("Bad params encountered in command "+str(command.getName())+" : "+str(parameters))
log("Bad params encountered in command "+str(command.getName())+" : "+str(parameters))
return self.returnError(defaults.STRING_BAD_PARAMS)
r = ''
......@@ -249,7 +306,7 @@ class Router(object):
r = r + "complete command="+c+"\n" + \
"</pre><hr>"
r = r + command.decorateResult(self.runRawCommand(c),self,decorator)
r = r + command.decorateResult(self.runRawCommand(c),self,decorator_helper)
return r
def runRawCommand(self,command):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment