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

Last change on this file since 587 was 587, checked in by ecprice, 16 years ago

Fix for modified status headers.

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