From: Eric Price Date: Tue, 9 Oct 2007 12:09:47 +0000 (-0400) Subject: Improve the error infrastructure. Hopefully this works; I haven't X-Git-Tag: sipb-xen-www/1~77 X-Git-Url: http://xvm.mit.edu/gitweb/invirt/packages/invirt-web.git/commitdiff_plain/b0592519fc047639e1cdf092dae91fbf316b37c9 Improve the error infrastructure. Hopefully this works; I haven't really tested it. svn path=/trunk/web/; revision=153 --- diff --git a/templates/error.tmpl b/templates/error.tmpl index a4f9fda..3a53f49 100644 --- a/templates/error.tmpl +++ b/templates/error.tmpl @@ -7,6 +7,8 @@ ERROR! #def body

ERROR

-

$errorMessage in operation $op

+

$errorMessage in operation $op. This shouldn't happen! Please +email sipb-xen@mit.edu to explain how it happened. Stderr:

+
$stderr
#end def diff --git a/templates/info.tmpl b/templates/info.tmpl index 46767b2..ca98ff9 100644 --- a/templates/info.tmpl +++ b/templates/info.tmpl @@ -62,14 +62,14 @@ Info on $machine.name
- - + + #if $machine.nics - + #end if #if not $on - - + + #end if
Owner:
Contact email:
Owner:
Contact email:
Hostname:.servers.csail.mit.edu
Hostname:.servers.csail.mit.edu
Ram:MB (max $max_mem)
Disk:GB (max $max_disk)WARNING: Modifying disk size may corrupt your data.
Ram:MB (max $max_mem)
Disk:GB (max $max_disk)WARNING: Modifying disk size may corrupt your data.
diff --git a/templates/invalid.tmpl b/templates/invalid.tmpl new file mode 100644 index 0000000..c0d8f77 --- /dev/null +++ b/templates/invalid.tmpl @@ -0,0 +1,19 @@ +#from skeleton import skeleton +#extends skeleton + +#def title +Invalid input +#end def + +#def body +

Invalid Input

+

Your input was bad:

+ + + +#if $stderr +

Printed to standard error:

+
$stderr
+#end if +
operationFieldvaluereason
$op$err_field$err_value$errorMessage
+#end def diff --git a/templates/main.py b/templates/main.py index 8de8a37..8c00190 100755 --- a/templates/main.py +++ b/templates/main.py @@ -12,8 +12,9 @@ import base64 import sha import hmac import datetime +import StringIO -sys.stderr = sys.stdout +sys.stderr = StringIO.StringIO() sys.path.append('/home/ecprice/.local/lib/python2.5/site-packages') from Cheetah.Template import Template @@ -31,7 +32,10 @@ class InvalidInput(MyException): typo) but not setting an invalid boot CD (which requires bypassing the select box). """ - pass + def __init__(self, err_field, err_value, expl=None): + super(InvalidInput, self).__init__(expl) + self.err_field = err_field + self.err_value = err_value class CodeError(MyException): """Exception for internal errors or bad faith input.""" @@ -115,10 +119,18 @@ def haveAccess(user, machine): return True return machine.owner == user.username -def error(op, user, fields, err): +def error(op, user, fields, err, emsg): """Print an error page when a CodeError occurs""" - d = dict(op=op, user=user, errorMessage=str(err)) - print Template(file='error.tmpl', searchList=[d, global_dict]); + d = dict(op=op, user=user, errorMessage=str(err), + stderr=emsg) + return Template(file='error.tmpl', searchList=[d, global_dict]); + +def invalidInput(op, user, fields, err, emsg): + """Print an error page when an InvalidInput exception occurs""" + d = dict(op=op, user=user, err_field=err.err_field, + err_value=str(err.err_value), stderr=emsg, + errorMessage=str(err)) + return Template(file='invalid.tmpl', searchList=[d, global_dict]); def validMachineName(name): """Check that name is valid for a machine name""" @@ -265,11 +277,13 @@ def createVm(user, name, memory, disk, is_hvm, cdrom): transaction = ctx.current.create_transaction() try: if memory > maxMemory(user): - raise InvalidInput("Too much memory requested") + raise InvalidInput('memory', memory, + "Max %s" % maxMemory(user)) if disk > maxDisk(user) * 1024: - raise InvalidInput("Too much disk requested") + raise InvalidInput('disk', disk, + "Max %s" % maxDisk(user)) if not canAddVm(user): - raise InvalidInput("Too many VMs requested") + raise InvalidInput('create', True, 'Unable to create more VMs') res = meta.engine.execute('select nextval(\'"machines_machine_id_seq"\')') id = res.fetchone()[0] machine = Machine() @@ -311,10 +325,11 @@ def validMemory(user, memory, machine=None): if memory < MIN_MEMORY_SINGLE: raise ValueError except ValueError: - raise InvalidInput("Invalid memory amount; must be at least %s MB" % - MIN_MEMORY_SINGLE) + raise InvalidInput('memory', memory, + "Minimum %s MB" % MIN_MEMORY_SINGLE) if memory > maxMemory(user, machine): - raise InvalidInput("Too much memory requested") + raise InvalidInput('memory', memory, + 'Maximum %s MB' % maxMemory(user, machine)) return memory def validDisk(user, disk, machine=None): @@ -322,24 +337,26 @@ def validDisk(user, disk, machine=None): try: disk = float(disk) if disk > maxDisk(user, machine): - raise InvalidInput("Too much disk requested") + raise InvalidInput('disk', disk, + "Maximum %s G" % maxDisk(user, machine)) disk = int(disk * 1024) if disk < MIN_DISK_SINGLE * 1024: raise ValueError except ValueError: - raise InvalidInput("Invalid disk amount; minimum is %s GB" % - MIN_DISK_SINGLE) + raise InvalidInput('disk', disk, + "Minimum %s GB" % MIN_DISK_SINGLE) return disk def create(user, fields): """Handler for create requests.""" name = fields.getfirst('name') if not validMachineName(name): - raise InvalidInput("Invalid name '%s'" % name) + raise InvalidInput('name', name) name = user.username + '_' + name.lower() if Machine.get_by(name=name): - raise InvalidInput("A machine named '%s' already exists" % name) + raise InvalidInput('name', name, + "Already exists") memory = fields.getfirst('memory') memory = validMemory(user, memory) @@ -359,7 +376,7 @@ def create(user, fields): machine = createVm(user, name, memory, disk, is_hvm, cdrom) d = dict(user=user, machine=machine) - print Template(file='create.tmpl', + return Template(file='create.tmpl', searchList=[d, global_dict]); def listVms(user, fields): @@ -391,7 +408,7 @@ def listVms(user, fields): has_vnc=has_vnc, uptimes=uptimes, cdroms=CDROM.select()) - print Template(file='list.tmpl', searchList=[d, global_dict]) + return Template(file='list.tmpl', searchList=[d, global_dict]) def testMachineId(user, machineId, exists=True): """Parse, validate and check authorization for a given machineId. @@ -452,7 +469,7 @@ def vnc(user, fields): machine=machine, hostname=os.environ.get('SERVER_NAME', 'localhost'), authtoken=token) - print Template(file='vnc.tmpl', + return Template(file='vnc.tmpl', searchList=[d, global_dict]) def getNicInfo(data_dict, machine): @@ -529,7 +546,8 @@ def command(user, fields): remctl('reboot', machine.name) elif action == 'Power on': if maxMemory(user) < machine.memory: - raise InvalidInput("You don't have enough free RAM quota") + raise InvalidInput('action', 'Power on', + "You don't have enough free RAM quota to turn on this machine") bootMachine(machine, cdrom) elif action == 'Power off': remctl('destroy', machine.name) @@ -542,13 +560,45 @@ def command(user, fields): d = dict(user=user, command=action, machine=machine) - print Template(file="command.tmpl", searchList=[d, global_dict]) - + return Template(file="command.tmpl", searchList=[d, global_dict]) + +def testOwner(user, owner, machine=None): + if owner != user.username: + raise InvalidInput('owner', owner, + "Invalid") + return owner + +def testContact(user, contact, machine=None): + if contact != user.email: + raise InvalidInput('contact', contact, + "Invalid") + return contact + +def testHostname(user, hostname, machine): + for nic in machine.nics: + if hostname == nic.hostname: + return hostname + raise InvalidInput('hostname', hostname, + "Different from before") + + def modify(user, fields): """Handler for modifying attributes of a machine.""" #XXX not written yet machine = testMachineId(user, fields.getfirst('machine_id')) + owner = testOwner(user, fields.getfirst('owner'), machine) + contact = testContact(user, fields.getfirst('contact')) + hostname = testHostname(user, fields.getfirst('hostname'), + machine) + ram = fields.getfirst('memory') + if ram is not None: + ram = validMemory(user, ram, machine) + disk = testDisk(user, fields.getfirst('disk')) + if disk is not None: + disk = validDisk(user, disk, machine) + + def help(user, fields): """Handler for help messages.""" simple = fields.getfirst('simple') @@ -569,7 +619,7 @@ want an HVM virtualized machine.""", subjects=subjects, mapping=mapping) - print Template(file="help.tmpl", searchList=[d, global_dict]) + return Template(file="help.tmpl", searchList=[d, global_dict]) def info(user, fields): @@ -644,7 +694,7 @@ def info(user, fields): max_mem=max_mem, max_disk=max_disk, fields = fields) - print Template(file='info.tmpl', + return Template(file='info.tmpl', searchList=[d, global_dict]) mapping = dict(list=listVms, @@ -685,15 +735,34 @@ if __name__ == '__main__': if not operation: operation = 'list' - fun = mapping.get(operation, - lambda u, e: - error(operation, u, e, - "Invalid operation '%s'" % operation)) + def badOperation(u, e): + raise CodeError("Unknown operation") + + fun = mapping.get(operation, badOperation) if fun not in (help, ): connect('postgres://sipb-xen@sipb-xen-dev/sipb_xen') try: - fun(u, fields) + output = fun(u, fields) + print 'Content-Type: text/html\n' + sys.stderr.seek(0) + e = sys.stderr.read() + if e: + output = output.replace('', '
'+e+'
') + print output except CodeError, err: - error(operation, u, fields, err) + print 'Content-Type: text/html\n' + sys.stderr.seek(0) + e = sys.stderr.read() + print error(operation, u, fields, err, e) except InvalidInput, err: - error(operation, u, fields, err) + print 'Content-Type: text/html\n' + sys.stderr.seek(0) + e = sys.stderr.read() + print invalidInput(operation, u, fields, err, e) + except: + print 'Content-Type: text/plain\n' + sys.stderr.seek(0) + e = sys.stderr.read() + print e + print '----' + raise diff --git a/templates/skeleton.tmpl b/templates/skeleton.tmpl index 3a5b8e2..1ccea2e 100644 --- a/templates/skeleton.tmpl +++ b/templates/skeleton.tmpl @@ -15,7 +15,7 @@ function closeWin(){ function helppopup(name){ closeWin() - helpWin = window.open("help?simple=true&subject="+encodeURIComponent(name), "HMMTHelp", + helpWin = window.open("help?simple=true&subject="+encodeURIComponent(name), "Help", "status, height = 300, width = 400"); if (window.focus){helpWin.focus();} return false;