source: trunk/packages/sipb-xen-www/code/main.py @ 580

Last change on this file since 580 was 579, checked in by ecprice, 17 years ago

Use fcgi

  • Property svn:executable set to *
File size: 24.2 KB
RevLine 
[113]1#!/usr/bin/python
[205]2"""Main CGI script for web interface"""
[113]3
[205]4import base64
5import cPickle
[113]6import cgi
[205]7import datetime
8import hmac
[113]9import os
[205]10import sha
11import simplejson
12import sys
[118]13import time
[447]14import urllib
[205]15from StringIO import StringIO
[113]16
[205]17def revertStandardError():
18    """Move stderr to stdout, and return the contents of the old stderr."""
19    errio = sys.stderr
20    if not isinstance(errio, StringIO):
21        return None
22    sys.stderr = sys.stdout
23    errio.seek(0)
24    return errio.read()
25
26def printError():
27    """Revert stderr to stdout, and print the contents of stderr"""
28    if isinstance(sys.stderr, StringIO):
29        print revertStandardError()
30
31if __name__ == '__main__':
32    import atexit
33    atexit.register(printError)
34    sys.stderr = StringIO()
35
[113]36sys.path.append('/home/ecprice/.local/lib/python2.5/site-packages')
37
[235]38import templates
[113]39from Cheetah.Template import Template
[440]40import sipb_xen_database
[443]41from sipb_xen_database import Machine, CDROM, ctx, connect, MachineAccess, Type, Autoinstall
[209]42import validation
[446]43import cache_acls
[578]44from webcommon import InvalidInput, CodeError, State
[209]45import controls
[113]46
[235]47class Checkpoint:
48    def __init__(self):
49        self.start_time = time.time()
50        self.checkpoints = []
51
52    def checkpoint(self, s):
53        self.checkpoints.append((s, time.time()))
54
55    def __str__(self):
56        return ('Timing info:\n%s\n' %
57                '\n'.join(['%s: %s' % (d, t - self.start_time) for
58                           (d, t) in self.checkpoints]))
59
60checkpoint = Checkpoint()
61
[447]62def jquote(string):
63    return "'" + string.replace('\\', '\\\\').replace("'", "\\'").replace('\n', '\\n') + "'"
[235]64
[205]65def helppopup(subj):
66    """Return HTML code for a (?) link to a specified help topic"""
[447]67    return ('<span class="helplink"><a href="help?' +
68            cgi.escape(urllib.urlencode(dict(subject=subj, simple='true')))
69            +'" target="_blank" ' +
70            'onclick="return helppopup(' + cgi.escape(jquote(subj)) + ')">(?)</a></span>')
[205]71
72def makeErrorPre(old, addition):
73    if addition is None:
74        return
75    if old:
76        return old[:-6]  + '\n----\n' + str(addition) + '</pre>'
77    else:
78        return '<p>STDERR:</p><pre>' + str(addition) + '</pre>'
[139]79
[440]80Template.sipb_xen_database = sipb_xen_database
[205]81Template.helppopup = staticmethod(helppopup)
82Template.err = None
[139]83
[205]84class JsonDict:
85    """Class to store a dictionary that will be converted to JSON"""
86    def __init__(self, **kws):
87        self.data = kws
88        if 'err' in kws:
89            err = kws['err']
90            del kws['err']
91            self.addError(err)
[139]92
[205]93    def __str__(self):
94        return simplejson.dumps(self.data)
95
96    def addError(self, text):
97        """Add stderr text to be displayed on the website."""
98        self.data['err'] = \
99            makeErrorPre(self.data.get('err'), text)
100
101class Defaults:
102    """Class to store default values for fields."""
103    memory = 256
104    disk = 4.0
105    cdrom = ''
[443]106    autoinstall = ''
[205]107    name = ''
[515]108    type = 'linux-hvm'
109
[205]110    def __init__(self, max_memory=None, max_disk=None, **kws):
111        if max_memory is not None:
112            self.memory = min(self.memory, max_memory)
113        if max_disk is not None:
114            self.max_disk = min(self.disk, max_disk)
115        for key in kws:
116            setattr(self, key, kws[key])
117
118
119
[209]120DEFAULT_HEADERS = {'Content-Type': 'text/html'}
[205]121
[572]122def error(op, username, fields, err, emsg):
[145]123    """Print an error page when a CodeError occurs"""
[572]124    d = dict(op=op, user=username, errorMessage=str(err),
[153]125             stderr=emsg)
[235]126    return templates.error(searchList=[d])
[113]127
[572]128def invalidInput(op, username, fields, err, emsg):
[153]129    """Print an error page when an InvalidInput exception occurs"""
[572]130    d = dict(op=op, user=username, err_field=err.err_field,
[153]131             err_value=str(err.err_value), stderr=emsg,
132             errorMessage=str(err))
[235]133    return templates.invalid(searchList=[d])
[153]134
[119]135def hasVnc(status):
[133]136    """Does the machine with a given status list support VNC?"""
[119]137    if status is None:
138        return False
139    for l in status:
140        if l[0] == 'device' and l[1][0] == 'vfb':
141            d = dict(l[1][1:])
142            return 'location' in d
143    return False
144
[572]145def parseCreate(username, state, fields):
146    kws = dict([(kw, fields.getfirst(kw)) for kw in 'name owner memory disksize vmtype cdrom clone_from'.split()])
[577]147    validate = validation.Validate(username, state, strict=True, **kws)
[572]148    return dict(contact=username, name=validate.name, memory=validate.memory,
149                disksize=validate.disksize, owner=validate.owner, machine_type=validate.vmtype,
150                cdrom=getattr(validate, 'cdrom', None),
151                clone_from=getattr(validate, 'clone_from', None))
[134]152
[572]153def create(username, state, fields):
[205]154    """Handler for create requests."""
155    try:
[572]156        parsed_fields = parseCreate(username, state, fields)
[577]157        machine = controls.createVm(username, state, **parsed_fields)
[205]158    except InvalidInput, err:
[207]159        pass
[205]160    else:
161        err = None
[572]162    state.clear() #Changed global state
[576]163    d = getListDict(username, state)
[205]164    d['err'] = err
165    if err:
166        for field in fields.keys():
167            setattr(d['defaults'], field, fields.getfirst(field))
168    else:
169        d['new_machine'] = parsed_fields['name']
[235]170    return templates.list(searchList=[d])
[205]171
172
[572]173def getListDict(username, state):
[438]174    """Gets the list of local variables used by list.tmpl."""
[535]175    checkpoint.checkpoint('Starting')
[572]176    machines = state.machines
[235]177    checkpoint.checkpoint('Got my machines')
[133]178    on = {}
[119]179    has_vnc = {}
[572]180    xmlist = state.xmlist
[235]181    checkpoint.checkpoint('Got uptimes')
[572]182    can_clone = 'ice3' not in state.xmlist_raw
[136]183    for m in machines:
[535]184        if m not in xmlist:
[144]185            has_vnc[m] = 'Off'
[535]186            m.uptime = None
[136]187        else:
[535]188            m.uptime = xmlist[m]['uptime']
189            if xmlist[m]['console']:
190                has_vnc[m] = True
191            elif m.type.hvm:
192                has_vnc[m] = "WTF?"
193            else:
[536]194                has_vnc[m] = "ParaVM"+helppopup("ParaVM Console")
[572]195    max_memory = validation.maxMemory(username, state)
196    max_disk = validation.maxDisk(username)
[235]197    checkpoint.checkpoint('Got max mem/disk')
[205]198    defaults = Defaults(max_memory=max_memory,
199                        max_disk=max_disk,
[572]200                        owner=username,
[205]201                        cdrom='gutsy-i386')
[235]202    checkpoint.checkpoint('Got defaults')
[424]203    def sortkey(machine):
[572]204        return (machine.owner != username, machine.owner, machine.name)
[424]205    machines = sorted(machines, key=sortkey)
[572]206    d = dict(user=username,
207             cant_add_vm=validation.cantAddVm(username, state),
[205]208             max_memory=max_memory,
[144]209             max_disk=max_disk,
[205]210             defaults=defaults,
[113]211             machines=machines,
[540]212             has_vnc=has_vnc,
213             can_clone=can_clone)
[205]214    return d
[113]215
[572]216def listVms(username, state, fields):
[205]217    """Handler for list requests."""
[235]218    checkpoint.checkpoint('Getting list dict')
[572]219    d = getListDict(username, state)
[235]220    checkpoint.checkpoint('Got list dict')
221    return templates.list(searchList=[d])
[438]222
[572]223def vnc(username, state, fields):
[119]224    """VNC applet page.
225
226    Note that due to same-domain restrictions, the applet connects to
227    the webserver, which needs to forward those requests to the xen
228    server.  The Xen server runs another proxy that (1) authenticates
229    and (2) finds the correct port for the VM.
230
231    You might want iptables like:
232
[205]233    -t nat -A PREROUTING -s ! 18.181.0.60 -i eth1 -p tcp -m tcp \
[438]234      --dport 10003 -j DNAT --to-destination 18.181.0.60:10003
[205]235    -t nat -A POSTROUTING -d 18.181.0.60 -o eth1 -p tcp -m tcp \
[438]236      --dport 10003 -j SNAT --to-source 18.187.7.142
[205]237    -A FORWARD -d 18.181.0.60 -i eth1 -o eth1 -p tcp -m tcp \
238      --dport 10003 -j ACCEPT
[145]239
240    Remember to enable iptables!
241    echo 1 > /proc/sys/net/ipv4/ip_forward
[119]242    """
[572]243    machine = validation.Validate(username, state, machine_id=fields.getfirst('machine_id')).machine
[438]244
[118]245    TOKEN_KEY = "0M6W0U1IXexThi5idy8mnkqPKEq1LtEnlK/pZSn0cDrN"
246
247    data = {}
[572]248    data["user"] = username
[205]249    data["machine"] = machine.name
250    data["expires"] = time.time()+(5*60)
251    pickled_data = cPickle.dumps(data)
[118]252    m = hmac.new(TOKEN_KEY, digestmod=sha)
[205]253    m.update(pickled_data)
254    token = {'data': pickled_data, 'digest': m.digest()}
[118]255    token = cPickle.dumps(token)
256    token = base64.urlsafe_b64encode(token)
[438]257
[209]258    status = controls.statusInfo(machine)
[152]259    has_vnc = hasVnc(status)
[438]260
[572]261    d = dict(user=username,
[152]262             on=status,
263             has_vnc=has_vnc,
[113]264             machine=machine,
[119]265             hostname=os.environ.get('SERVER_NAME', 'localhost'),
[113]266             authtoken=token)
[235]267    return templates.vnc(searchList=[d])
[113]268
[252]269def getHostname(nic):
[438]270    """Find the hostname associated with a NIC.
271
272    XXX this should be merged with the similar logic in DNS and DHCP.
273    """
[252]274    if nic.hostname and '.' in nic.hostname:
275        return nic.hostname
276    elif nic.machine:
[507]277        return nic.machine.name + '.xvm.mit.edu'
[252]278    else:
279        return None
280
281
[133]282def getNicInfo(data_dict, machine):
[145]283    """Helper function for info, get data on nics for a machine.
284
285    Modifies data_dict to include the relevant data, and returns a list
286    of (key, name) pairs to display "name: data_dict[key]" to the user.
287    """
[133]288    data_dict['num_nics'] = len(machine.nics)
[227]289    nic_fields_template = [('nic%s_hostname', 'NIC %s Hostname'),
[133]290                           ('nic%s_mac', 'NIC %s MAC Addr'),
291                           ('nic%s_ip', 'NIC %s IP'),
292                           ]
293    nic_fields = []
294    for i in range(len(machine.nics)):
295        nic_fields.extend([(x % i, y % i) for x, y in nic_fields_template])
[227]296        if not i:
[252]297            data_dict['nic%s_hostname' % i] = getHostname(machine.nics[i])
[133]298        data_dict['nic%s_mac' % i] = machine.nics[i].mac_addr
299        data_dict['nic%s_ip' % i] = machine.nics[i].ip
300    if len(machine.nics) == 1:
301        nic_fields = [(x, y.replace('NIC 0 ', '')) for x, y in nic_fields]
302    return nic_fields
303
304def getDiskInfo(data_dict, machine):
[145]305    """Helper function for info, get data on disks for a machine.
306
307    Modifies data_dict to include the relevant data, and returns a list
308    of (key, name) pairs to display "name: data_dict[key]" to the user.
309    """
[133]310    data_dict['num_disks'] = len(machine.disks)
311    disk_fields_template = [('%s_size', '%s size')]
312    disk_fields = []
313    for disk in machine.disks:
314        name = disk.guest_device_name
[438]315        disk_fields.extend([(x % name, y % name) for x, y in
[205]316                            disk_fields_template])
[211]317        data_dict['%s_size' % name] = "%0.1f GiB" % (disk.size / 1024.)
[133]318    return disk_fields
319
[572]320def command(username, state, fields):
[205]321    """Handler for running commands like boot and delete on a VM."""
[207]322    back = fields.getfirst('back')
[205]323    try:
[572]324        d = controls.commandResult(username, state, fields)
[207]325        if d['command'] == 'Delete VM':
326            back = 'list'
[205]327    except InvalidInput, err:
[207]328        if not back:
[205]329            raise
[572]330        print >> sys.stderr, err
[261]331        result = err
[205]332    else:
333        result = 'Success!'
[207]334        if not back:
[235]335            return templates.command(searchList=[d])
[207]336    if back == 'list':
[572]337        state.clear() #Changed global state
[576]338        d = getListDict(username, state)
[207]339        d['result'] = result
[235]340        return templates.list(searchList=[d])
[207]341    elif back == 'info':
[572]342        machine = validation.Validate(username, state, machine_id=fields.getfirst('machine_id')).machine
[407]343        return ({'Status': '302',
344                 'Location': '/info?machine_id=%d' % machine.machine_id},
345                "You shouldn't see this message.")
[205]346    else:
[261]347        raise InvalidInput('back', back, 'Not a known back page.')
[205]348
[572]349def modifyDict(username, state, fields):
[438]350    """Modify a machine as specified by CGI arguments.
351
352    Return a list of local variables for modify.tmpl.
353    """
[177]354    olddisk = {}
[161]355    transaction = ctx.current.create_transaction()
356    try:
[572]357        kws = dict([(kw, fields.getfirst(kw)) for kw in 'machine_id owner admin contact name memory vmtype disksize'.split()])
358        validate = validation.Validate(username, state, **kws)
359        machine = validate.machine
[161]360        oldname = machine.name
[153]361
[572]362        if hasattr(validate, 'memory'):
363            machine.memory = validate.memory
[438]364
[572]365        if hasattr(validate, 'vmtype'):
366            machine.type = validate.vmtype
[440]367
[572]368        if hasattr(validate, 'disksize'):
369            disksize = validate.disksize
[177]370            disk = machine.disks[0]
371            if disk.size != disksize:
372                olddisk[disk.guest_device_name] = disksize
373                disk.size = disksize
374                ctx.current.save(disk)
[438]375
[446]376        update_acl = False
[572]377        if hasattr(validate, 'owner') and validate.owner != machine.owner:
378            machine.owner = validate.owner
[446]379            update_acl = True
[572]380        if hasattr(validate, 'name'):
[161]381            machine.name = name
[572]382        if hasattr(validate, 'admin') and validate.admin != machine.administrator:
383            machine.administrator = validate.admin
[446]384            update_acl = True
[572]385        if hasattr(validate, 'contact'):
386            machine.contact = validate.contact
[438]387
[161]388        ctx.current.save(machine)
[446]389        if update_acl:
[572]390            print >> sys.stderr, machine, machine.administrator
[446]391            cache_acls.refreshMachine(machine)
[161]392        transaction.commit()
393    except:
394        transaction.rollback()
[163]395        raise
[177]396    for diskname in olddisk:
[209]397        controls.resizeDisk(oldname, diskname, str(olddisk[diskname]))
[572]398    if hasattr(validate, 'name'):
399        controls.renameMachine(machine, oldname, validate.name)
400    return dict(user=username,
401                command="modify",
[205]402                machine=machine)
[438]403
[572]404def modify(username, state, fields):
[205]405    """Handler for modifying attributes of a machine."""
406    try:
[572]407        modify_dict = modifyDict(username, state, fields)
[205]408    except InvalidInput, err:
[207]409        result = None
[572]410        machine = validation.Validate(username, state, machine_id=fields.getfirst('machine_id')).machine
[205]411    else:
412        machine = modify_dict['machine']
[209]413        result = 'Success!'
[205]414        err = None
[572]415    info_dict = infoDict(username, machine)
[205]416    info_dict['err'] = err
417    if err:
418        for field in fields.keys():
419            setattr(info_dict['defaults'], field, fields.getfirst(field))
[207]420    info_dict['result'] = result
[235]421    return templates.info(searchList=[info_dict])
[161]422
[438]423
[572]424def helpHandler(username, state, fields):
[145]425    """Handler for help messages."""
[139]426    simple = fields.getfirst('simple')
427    subjects = fields.getlist('subject')
[438]428
[536]429    help_mapping = {'ParaVM Console': """
[432]430ParaVM machines do not support local console access over VNC.  To
431access the serial console of these machines, you can SSH with Kerberos
[536]432to console.xvm.mit.edu, using the name of the machine as your
[432]433username.""",
[536]434                    'HVM/ParaVM': """
[139]435HVM machines use the virtualization features of the processor, while
436ParaVM machines use Xen's emulation of virtualization features.  You
437want an HVM virtualized machine.""",
[536]438                    'CPU Weight': """
[205]439Don't ask us!  We're as mystified as you are.""",
[536]440                    'Owner': """
[205]441The owner field is used to determine <a
[536]442href="help?subject=Quotas">quotas</a>.  It must be the name of a
[205]443locker that you are an AFS administrator of.  In particular, you or an
444AFS group you are a member of must have AFS rlidwka bits on the
[432]445locker.  You can check who administers the LOCKER locker using the
446commands 'attach LOCKER; fs la /mit/LOCKER' on Athena.)  See also <a
[536]447href="help?subject=Administrator">administrator</a>.""",
448                    'Administrator': """
[205]449The administrator field determines who can access the console and
450power on and off the machine.  This can be either a user or a moira
451group.""",
[536]452                    'Quotas': """
[408]453Quotas are determined on a per-locker basis.  Each locker may have a
[205]454maximum of 512 megabytes of active ram, 50 gigabytes of disk, and 4
[309]455active machines.""",
[536]456                    'Console': """
[309]457<strong>Framebuffer:</strong> At a Linux boot prompt in your VM, try
458setting <tt>fb=false</tt> to disable the framebuffer.  If you don't,
459your machine will run just fine, but the applet's display of the
460console will suffer artifacts.
461"""
[536]462                    }
[438]463
[187]464    if not subjects:
[205]465        subjects = sorted(help_mapping.keys())
[438]466
[572]467    d = dict(user=username,
[139]468             simple=simple,
469             subjects=subjects,
[205]470             mapping=help_mapping)
[438]471
[235]472    return templates.help(searchList=[d])
[133]473
[438]474
[579]475def badOperation(u, s, e):
[438]476    """Function called when accessing an unknown URI."""
[205]477    raise CodeError("Unknown operation")
478
[579]479def infoDict(username, state, machine):
[438]480    """Get the variables used by info.tmpl."""
[209]481    status = controls.statusInfo(machine)
[235]482    checkpoint.checkpoint('Getting status info')
[133]483    has_vnc = hasVnc(status)
484    if status is None:
485        main_status = dict(name=machine.name,
486                           memory=str(machine.memory))
[205]487        uptime = None
488        cputime = None
[133]489    else:
490        main_status = dict(status[1:])
[167]491        start_time = float(main_status.get('start_time', 0))
492        uptime = datetime.timedelta(seconds=int(time.time()-start_time))
493        cpu_time_float = float(main_status.get('cpu_time', 0))
494        cputime = datetime.timedelta(seconds=int(cpu_time_float))
[235]495    checkpoint.checkpoint('Status')
[133]496    display_fields = """name uptime memory state cpu_weight on_reboot
497     on_poweroff on_crash on_xend_start on_xend_stop bootloader""".split()
498    display_fields = [('name', 'Name'),
499                      ('owner', 'Owner'),
[187]500                      ('administrator', 'Administrator'),
[133]501                      ('contact', 'Contact'),
[136]502                      ('type', 'Type'),
[133]503                      'NIC_INFO',
504                      ('uptime', 'uptime'),
505                      ('cputime', 'CPU usage'),
506                      ('memory', 'RAM'),
507                      'DISK_INFO',
508                      ('state', 'state (xen format)'),
[536]509                      ('cpu_weight', 'CPU weight'+helppopup('CPU Weight')),
[133]510                      ('on_reboot', 'Action on VM reboot'),
511                      ('on_poweroff', 'Action on VM poweroff'),
512                      ('on_crash', 'Action on VM crash'),
513                      ('on_xend_start', 'Action on Xen start'),
514                      ('on_xend_stop', 'Action on Xen stop'),
515                      ('bootloader', 'Bootloader options'),
516                      ]
517    fields = []
518    machine_info = {}
[147]519    machine_info['name'] = machine.name
[136]520    machine_info['type'] = machine.type.hvm and 'HVM' or 'ParaVM'
[133]521    machine_info['owner'] = machine.owner
[187]522    machine_info['administrator'] = machine.administrator
[133]523    machine_info['contact'] = machine.contact
524
525    nic_fields = getNicInfo(machine_info, machine)
526    nic_point = display_fields.index('NIC_INFO')
[438]527    display_fields = (display_fields[:nic_point] + nic_fields +
[205]528                      display_fields[nic_point+1:])
[133]529
530    disk_fields = getDiskInfo(machine_info, machine)
531    disk_point = display_fields.index('DISK_INFO')
[438]532    display_fields = (display_fields[:disk_point] + disk_fields +
[205]533                      display_fields[disk_point+1:])
[438]534
[211]535    main_status['memory'] += ' MiB'
[133]536    for field, disp in display_fields:
[167]537        if field in ('uptime', 'cputime') and locals()[field] is not None:
[133]538            fields.append((disp, locals()[field]))
[147]539        elif field in machine_info:
540            fields.append((disp, machine_info[field]))
[133]541        elif field in main_status:
542            fields.append((disp, main_status[field]))
543        else:
544            pass
545            #fields.append((disp, None))
[235]546
547    checkpoint.checkpoint('Got fields')
548
549
[572]550    max_mem = validation.maxMemory(machine.owner, state, machine, False)
[235]551    checkpoint.checkpoint('Got mem')
[566]552    max_disk = validation.maxDisk(machine.owner, machine)
[209]553    defaults = Defaults()
[516]554    for name in 'machine_id name administrator owner memory contact'.split():
[205]555        setattr(defaults, name, getattr(machine, name))
[516]556    defaults.type = machine.type.type_id
[205]557    defaults.disk = "%0.2f" % (machine.disks[0].size/1024.)
[235]558    checkpoint.checkpoint('Got defaults')
[572]559    d = dict(user=username,
[133]560             on=status is not None,
561             machine=machine,
[205]562             defaults=defaults,
[133]563             has_vnc=has_vnc,
564             uptime=str(uptime),
565             ram=machine.memory,
[144]566             max_mem=max_mem,
567             max_disk=max_disk,
[536]568             owner_help=helppopup("Owner"),
[133]569             fields = fields)
[205]570    return d
[113]571
[572]572def info(username, state, fields):
[205]573    """Handler for info on a single VM."""
[572]574    machine = validation.Validate(username, state, machine_id=fields.getfirst('machine_id')).machine
[579]575    d = infoDict(username, state, machine)
[235]576    checkpoint.checkpoint('Got infodict')
577    return templates.info(searchList=[d])
[205]578
[572]579def unauthFront(_, _2, fields):
[510]580    """Information for unauth'd users."""
581    return templates.unauth(searchList=[{'simple' : True}])
582
[113]583mapping = dict(list=listVms,
584               vnc=vnc,
[133]585               command=command,
586               modify=modify,
[113]587               info=info,
[139]588               create=create,
[510]589               help=helpHandler,
590               unauth=unauthFront)
[113]591
[205]592def printHeaders(headers):
[438]593    """Print a dictionary as HTTP headers."""
[205]594    for key, value in headers.iteritems():
595        print '%s: %s' % (key, value)
596    print
597
598
[572]599def getUser(environ):
[205]600    """Return the current user based on the SSL environment variables"""
[572]601    email = environ.get('SSL_CLIENT_S_DN_Email', None)
[510]602    if email is None:
603        return None
[572]604    if not email.endswith('@MIT.EDU'):
605        return None
606    return email[:-8]
[205]607
[579]608class App:
609    def __init__(self, environ, start_response):
610        self.environ = environ
611        self.start = start_response
[205]612
[579]613        self.username = getUser(environ)
614        self.state = State(self.username)
[205]615
[579]616    def __iter__(self):
617        fields = cgi.FieldStorage(fp=self.environ['wsgi.input'], environ=self.environ)
618        print >> sys.stderr, fields
619        operation = self.environ.get('PATH_INFO', '')
620        if not operation:
621            self.start("301 Moved Permanently", [('Location',
622                                                  os.environ['SCRIPT_NAME']+'/')])
623            return
624        if self.username is None:
625            operation = 'unauth'
626        if operation.startswith('/'):
627            operation = operation[1:]
628        if not operation:
629            operation = 'list'
630        print 'Starting', operation
631
632        start_time = time.time()
633        fun = mapping.get(operation, badOperation)
634        try:
635            checkpoint.checkpoint('Before')
636            output = fun(self.username, self.state, fields)
637            checkpoint.checkpoint('After')
638
639            headers = dict(DEFAULT_HEADERS)
640            if isinstance(output, tuple):
641                new_headers, output = output
642                headers.update(new_headers)
643            print 'MOO2'
644            e = revertStandardError()
645            if e:
646                if isinstance(output, basestring):
647                    sys.stderr = StringIO()
648                    x = str(output)
649                    print >> sys.stderr, x
650                    print >> sys.stderr, 'XXX'
651                    print >> sys.stderr, e
652                    raise Exception()
653                output.addError(e)
654            output_string =  str(output)
655            checkpoint.checkpoint('output as a string')
656        except Exception, err:
657            if not fields.has_key('js'):
658                if isinstance(err, CodeError):
659                    self.start('500 Internal Server Error', [('Content-Type', 'text/html')])
660                    e = revertStandardError()
661                    s = error(operation, self.username, fields, err, e)
662                    yield str(s)
663                    return
664                if isinstance(err, InvalidInput):
665                    self.start('200 OK', [('Content-Type', 'text/html')])
666                    e = revertStandardError()
667                    yield str(invalidInput(operation, self.username, fields, err, e))
668                    return
669            self.start('500 Internal Server Error', [('Content-Type', 'text/plain')])
670            import traceback
671            yield '''Uh-oh!  We experienced an error.'
672Please email xvm-dev@mit.edu with the contents of this page.'
673----
674%s
675----
676%s
677----''' % (str(err), traceback.format_exc())
678        self.start('200 OK', headers.items())
679        yield output_string
[535]680        if fields.has_key('timedebug'):
[579]681            yield '<pre>%s</pre>' % cgi.escape(str(checkpoint))
[209]682
[579]683def constructor():
684    connect('postgres://sipb-xen@sipb-xen-dev.mit.edu/sipb_xen')
685    return App
[535]686
[579]687def main():
688    from flup.server.fcgi_fork import WSGIServer
689    WSGIServer(constructor()).run()
[535]690
[579]691if __name__ == '__main__':
692    main()
Note: See TracBrowser for help on using the repository browser.