Oops, reenable error mail.
[invirt/packages/invirt-web.git] / code / main.py
index d05eaca..80761ae 100755 (executable)
@@ -17,7 +17,7 @@ def revertStandardError():
     """Move stderr to stdout, and return the contents of the old stderr."""
     errio = sys.stderr
     if not isinstance(errio, StringIO):
-        return None
+        return ''
     sys.stderr = sys.stdout
     errio.seek(0)
     return errio.read()
@@ -30,7 +30,6 @@ def printError():
 if __name__ == '__main__':
     import atexit
     atexit.register(printError)
-    sys.stderr = StringIO()
 
 sys.path.append('/home/ecprice/.local/lib/python2.5/site-packages')
 
@@ -104,6 +103,7 @@ class Defaults:
     cdrom = ''
     autoinstall = ''
     name = ''
+    description = ''
     type = 'linux-hvm'
 
     def __init__(self, max_memory=None, max_disk=None, **kws):
@@ -118,12 +118,6 @@ class Defaults:
 
 DEFAULT_HEADERS = {'Content-Type': 'text/html'}
 
-def error(op, username, fields, err, emsg):
-    """Print an error page when a CodeError occurs"""
-    d = dict(op=op, user=username, errorMessage=str(err),
-             stderr=emsg)
-    return templates.error(searchList=[d])
-
 def invalidInput(op, username, fields, err, emsg):
     """Print an error page when an InvalidInput exception occurs"""
     d = dict(op=op, user=username, err_field=err.err_field,
@@ -142,9 +136,9 @@ def hasVnc(status):
     return False
 
 def parseCreate(username, state, fields):
-    kws = dict([(kw, fields.getfirst(kw)) for kw in 'name owner memory disksize vmtype cdrom clone_from'.split()])
+    kws = dict([(kw, fields.getfirst(kw)) for kw in 'name description owner memory disksize vmtype cdrom clone_from'.split()])
     validate = validation.Validate(username, state, strict=True, **kws)
-    return dict(contact=username, name=validate.name, memory=validate.memory,
+    return dict(contact=username, name=validate.name, description=validate.description, memory=validate.memory,
                 disksize=validate.disksize, owner=validate.owner, machine_type=validate.vmtype,
                 cdrom=getattr(validate, 'cdrom', None),
                 clone_from=getattr(validate, 'clone_from', None))
@@ -353,7 +347,7 @@ def modifyDict(username, state, fields):
     olddisk = {}
     transaction = ctx.current.create_transaction()
     try:
-        kws = dict([(kw, fields.getfirst(kw)) for kw in 'machine_id owner admin contact name memory vmtype disksize'.split()])
+        kws = dict([(kw, fields.getfirst(kw)) for kw in 'machine_id owner admin contact name description memory vmtype disksize'.split()])
         validate = validation.Validate(username, state, **kws)
         machine = validate.machine
         oldname = machine.name
@@ -378,6 +372,8 @@ def modifyDict(username, state, fields):
             update_acl = True
         if hasattr(validate, 'name'):
             machine.name = validate.name
+        if hasattr(validate, 'description'):
+            machine.description = validate.description
         if hasattr(validate, 'admin') and validate.admin != machine.administrator:
             machine.administrator = validate.admin
             update_acl = True
@@ -473,7 +469,7 @@ console will suffer artifacts.
 
 def badOperation(u, s, e):
     """Function called when accessing an unknown URI."""
-    raise CodeError("Unknown operation")
+    return ({'Status': '404 Not Found'}, 'Invalid operation.')
 
 def infoDict(username, state, machine):
     """Get the variables used by info.tmpl."""
@@ -495,6 +491,7 @@ def infoDict(username, state, machine):
     display_fields = """name uptime memory state cpu_weight on_reboot 
      on_poweroff on_crash on_xend_start on_xend_stop bootloader""".split()
     display_fields = [('name', 'Name'),
+                      ('description', 'Description'),
                       ('owner', 'Owner'),
                       ('administrator', 'Administrator'),
                       ('contact', 'Contact'),
@@ -516,6 +513,7 @@ def infoDict(username, state, machine):
     fields = []
     machine_info = {}
     machine_info['name'] = machine.name
+    machine_info['description'] = machine.description
     machine_info['type'] = machine.type.hvm and 'HVM' or 'ParaVM'
     machine_info['owner'] = machine.owner
     machine_info['administrator'] = machine.administrator
@@ -550,7 +548,7 @@ def infoDict(username, state, machine):
     checkpoint.checkpoint('Got mem')
     max_disk = validation.maxDisk(machine.owner, machine)
     defaults = Defaults()
-    for name in 'machine_id name administrator owner memory contact'.split():
+    for name in 'machine_id name description administrator owner memory contact'.split():
         setattr(defaults, name, getattr(machine, name))
     defaults.type = machine.type.type_id
     defaults.disk = "%0.2f" % (machine.disks[0].size/1024.)
@@ -579,6 +577,10 @@ def unauthFront(_, _2, fields):
     """Information for unauth'd users."""
     return templates.unauth(searchList=[{'simple' : True}])
 
+def throwError(_, __, ___):
+    """Throw an error, to test the error-tracing mechanisms."""
+    raise RuntimeError("test of the emergency broadcast system")
+
 mapping = dict(list=listVms,
                vnc=vnc,
                command=command,
@@ -586,7 +588,8 @@ mapping = dict(list=listVms,
                info=info,
                create=create,
                help=helpHandler,
-               unauth=unauthFront)
+               unauth=unauthFront,
+               errortest=throwError)
 
 def printHeaders(headers):
     """Print a dictionary as HTTP headers."""
@@ -594,6 +597,30 @@ def printHeaders(headers):
         print '%s: %s' % (key, value)
     print
 
+def send_error_mail(subject, body):
+    import subprocess
+
+    to = 'xvm@mit.edu'
+    mail = """To: %s
+From: root@xvm.mit.edu
+Subject: %s
+
+%s
+""" % (to, subject, body)
+    p = subprocess.Popen(['/usr/sbin/sendmail', to], stdin=subprocess.PIPE)
+    p.stdin.write(mail)
+    p.stdin.close()
+    p.wait()
+
+def show_error(op, username, fields, err, emsg, traceback):
+    """Print an error page when an exception occurs"""
+    d = dict(op=op, user=username, fields=fields,
+             errorMessage=str(err), stderr=emsg, traceback=traceback)
+    details = templates.error_raw(searchList=[d])
+    send_error_mail('xvm error on %s for %s: %s' % (op, username, err),
+                    details)
+    d['details'] = details
+    return templates.error(searchList=[d])
 
 def getUser(environ):
     """Return the current user based on the SSL environment variables"""
@@ -614,8 +641,8 @@ class App:
         self.state.environ = environ
 
     def __iter__(self):
+        sys.stderr = StringIO()
         fields = cgi.FieldStorage(fp=self.environ['wsgi.input'], environ=self.environ)
-        print >> sys.stderr, fields
         operation = self.environ.get('PATH_INFO', '')
         if not operation:
             self.start("301 Moved Permanently", [('Location',
@@ -640,7 +667,6 @@ class App:
             if isinstance(output, tuple):
                 new_headers, output = output
                 headers.update(new_headers)
-            print 'MOO2'
             e = revertStandardError()
             if e:
                 if isinstance(output, basestring):
@@ -655,26 +681,20 @@ class App:
             checkpoint.checkpoint('output as a string')
         except Exception, err:
             if not fields.has_key('js'):
-                if isinstance(err, CodeError):
-                    self.start('500 Internal Server Error', [('Content-Type', 'text/html')])
-                    e = revertStandardError()
-                    s = error(operation, self.username, fields, err, e)
-                    yield str(s)
-                    return
                 if isinstance(err, InvalidInput):
                     self.start('200 OK', [('Content-Type', 'text/html')])
                     e = revertStandardError()
-                    yield str(invalidInput(operation, self.username, fields, err, e))
+                    yield str(invalidInput(operation, self.username, fields,
+                                           err, e))
                     return
-            self.start('500 Internal Server Error', [('Content-Type', 'text/plain')])
             import traceback
-            yield '''Uh-oh!  We experienced an error.'
-Please email xvm-dev@mit.edu with the contents of this page.'
-----
-%s
-----
-%s
-----''' % (str(err), traceback.format_exc())
+            self.start('500 Internal Server Error',
+                       [('Content-Type', 'text/html')])
+            e = revertStandardError()
+            s = show_error(operation, self.username, fields,
+                           err, e, traceback.format_exc())
+            yield str(s)
+            return
         status = headers.setdefault('Status', '200 OK')
         del headers['Status']
         self.start(status, headers.items())