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

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

Use owner's quota on info page, not user's quota.

  • Property svn:executable set to *
File size: 24.1 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
[209]44from webcommon import InvalidInput, CodeError, g
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
[153]122def error(op, user, fields, err, emsg):
[145]123    """Print an error page when a CodeError occurs"""
[153]124    d = dict(op=op, user=user, errorMessage=str(err),
125             stderr=emsg)
[235]126    return templates.error(searchList=[d])
[113]127
[153]128def invalidInput(op, user, fields, err, emsg):
129    """Print an error page when an InvalidInput exception occurs"""
130    d = dict(op=op, user=user, err_field=err.err_field,
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
[205]145def parseCreate(user, fields):
[134]146    name = fields.getfirst('name')
[209]147    if not validation.validMachineName(name):
[429]148        raise InvalidInput('name', name, 'You must provide a machine name.  Max 22 chars, alnum plus \'-\' and \'_\'.')
[162]149    name = name.lower()
[134]150
151    if Machine.get_by(name=name):
[153]152        raise InvalidInput('name', name,
[205]153                           "Name already exists.")
[438]154
[228]155    owner = validation.testOwner(user, fields.getfirst('owner'))
156
[134]157    memory = fields.getfirst('memory')
[266]158    memory = validation.validMemory(owner, memory, on=True)
[438]159
[243]160    disk_size = fields.getfirst('disk')
[266]161    disk_size = validation.validDisk(owner, disk_size)
[134]162
[113]163    vm_type = fields.getfirst('vmtype')
[437]164    vm_type = validation.validVmType(vm_type)
[113]165
166    cdrom = fields.getfirst('cdrom')
167    if cdrom is not None and not CDROM.get(cdrom):
[205]168        raise CodeError("Invalid cdrom type '%s'" % cdrom)
[340]169
170    clone_from = fields.getfirst('clone_from')
171    if clone_from and clone_from != 'ice3':
172        raise CodeError("Invalid clone image '%s'" % clone_from)
[438]173
[243]174    return dict(contact=user, name=name, memory=memory, disk_size=disk_size,
[437]175                owner=owner, machine_type=vm_type, cdrom=cdrom, clone_from=clone_from)
[113]176
[205]177def create(user, fields):
178    """Handler for create requests."""
179    try:
180        parsed_fields = parseCreate(user, fields)
[209]181        machine = controls.createVm(**parsed_fields)
[205]182    except InvalidInput, err:
[207]183        pass
[205]184    else:
185        err = None
186    g.clear() #Changed global state
187    d = getListDict(user)
188    d['err'] = err
189    if err:
190        for field in fields.keys():
191            setattr(d['defaults'], field, fields.getfirst(field))
192    else:
193        d['new_machine'] = parsed_fields['name']
[235]194    return templates.list(searchList=[d])
[205]195
196
197def getListDict(user):
[438]198    """Gets the list of local variables used by list.tmpl."""
[535]199    checkpoint.checkpoint('Starting')
[261]200    machines = g.machines
[235]201    checkpoint.checkpoint('Got my machines')
[133]202    on = {}
[119]203    has_vnc = {}
[535]204    xmlist = g.xmlist
[235]205    checkpoint.checkpoint('Got uptimes')
[565]206    can_clone = 'ice3' not in g.xmlist_raw
[136]207    for m in machines:
[535]208        if m not in xmlist:
[144]209            has_vnc[m] = 'Off'
[535]210            m.uptime = None
[136]211        else:
[535]212            m.uptime = xmlist[m]['uptime']
213            if xmlist[m]['console']:
214                has_vnc[m] = True
215            elif m.type.hvm:
216                has_vnc[m] = "WTF?"
217            else:
[536]218                has_vnc[m] = "ParaVM"+helppopup("ParaVM Console")
[209]219    max_memory = validation.maxMemory(user)
220    max_disk = validation.maxDisk(user)
[235]221    checkpoint.checkpoint('Got max mem/disk')
[205]222    defaults = Defaults(max_memory=max_memory,
223                        max_disk=max_disk,
[228]224                        owner=user,
[205]225                        cdrom='gutsy-i386')
[235]226    checkpoint.checkpoint('Got defaults')
[424]227    def sortkey(machine):
228        return (machine.owner != user, machine.owner, machine.name)
229    machines = sorted(machines, key=sortkey)
[113]230    d = dict(user=user,
[209]231             cant_add_vm=validation.cantAddVm(user),
[205]232             max_memory=max_memory,
[144]233             max_disk=max_disk,
[205]234             defaults=defaults,
[113]235             machines=machines,
[540]236             has_vnc=has_vnc,
237             can_clone=can_clone)
[205]238    return d
[113]239
[205]240def listVms(user, fields):
241    """Handler for list requests."""
[235]242    checkpoint.checkpoint('Getting list dict')
[205]243    d = getListDict(user)
[235]244    checkpoint.checkpoint('Got list dict')
245    return templates.list(searchList=[d])
[438]246
[113]247def vnc(user, fields):
[119]248    """VNC applet page.
249
250    Note that due to same-domain restrictions, the applet connects to
251    the webserver, which needs to forward those requests to the xen
252    server.  The Xen server runs another proxy that (1) authenticates
253    and (2) finds the correct port for the VM.
254
255    You might want iptables like:
256
[205]257    -t nat -A PREROUTING -s ! 18.181.0.60 -i eth1 -p tcp -m tcp \
[438]258      --dport 10003 -j DNAT --to-destination 18.181.0.60:10003
[205]259    -t nat -A POSTROUTING -d 18.181.0.60 -o eth1 -p tcp -m tcp \
[438]260      --dport 10003 -j SNAT --to-source 18.187.7.142
[205]261    -A FORWARD -d 18.181.0.60 -i eth1 -o eth1 -p tcp -m tcp \
262      --dport 10003 -j ACCEPT
[145]263
264    Remember to enable iptables!
265    echo 1 > /proc/sys/net/ipv4/ip_forward
[119]266    """
[209]267    machine = validation.testMachineId(user, fields.getfirst('machine_id'))
[438]268
[118]269    TOKEN_KEY = "0M6W0U1IXexThi5idy8mnkqPKEq1LtEnlK/pZSn0cDrN"
270
271    data = {}
[228]272    data["user"] = user
[205]273    data["machine"] = machine.name
274    data["expires"] = time.time()+(5*60)
275    pickled_data = cPickle.dumps(data)
[118]276    m = hmac.new(TOKEN_KEY, digestmod=sha)
[205]277    m.update(pickled_data)
278    token = {'data': pickled_data, 'digest': m.digest()}
[118]279    token = cPickle.dumps(token)
280    token = base64.urlsafe_b64encode(token)
[438]281
[209]282    status = controls.statusInfo(machine)
[152]283    has_vnc = hasVnc(status)
[438]284
[113]285    d = dict(user=user,
[152]286             on=status,
287             has_vnc=has_vnc,
[113]288             machine=machine,
[119]289             hostname=os.environ.get('SERVER_NAME', 'localhost'),
[113]290             authtoken=token)
[235]291    return templates.vnc(searchList=[d])
[113]292
[252]293def getHostname(nic):
[438]294    """Find the hostname associated with a NIC.
295
296    XXX this should be merged with the similar logic in DNS and DHCP.
297    """
[252]298    if nic.hostname and '.' in nic.hostname:
299        return nic.hostname
300    elif nic.machine:
[507]301        return nic.machine.name + '.xvm.mit.edu'
[252]302    else:
303        return None
304
305
[133]306def getNicInfo(data_dict, machine):
[145]307    """Helper function for info, get data on nics for a machine.
308
309    Modifies data_dict to include the relevant data, and returns a list
310    of (key, name) pairs to display "name: data_dict[key]" to the user.
311    """
[133]312    data_dict['num_nics'] = len(machine.nics)
[227]313    nic_fields_template = [('nic%s_hostname', 'NIC %s Hostname'),
[133]314                           ('nic%s_mac', 'NIC %s MAC Addr'),
315                           ('nic%s_ip', 'NIC %s IP'),
316                           ]
317    nic_fields = []
318    for i in range(len(machine.nics)):
319        nic_fields.extend([(x % i, y % i) for x, y in nic_fields_template])
[227]320        if not i:
[252]321            data_dict['nic%s_hostname' % i] = getHostname(machine.nics[i])
[133]322        data_dict['nic%s_mac' % i] = machine.nics[i].mac_addr
323        data_dict['nic%s_ip' % i] = machine.nics[i].ip
324    if len(machine.nics) == 1:
325        nic_fields = [(x, y.replace('NIC 0 ', '')) for x, y in nic_fields]
326    return nic_fields
327
328def getDiskInfo(data_dict, machine):
[145]329    """Helper function for info, get data on disks for a machine.
330
331    Modifies data_dict to include the relevant data, and returns a list
332    of (key, name) pairs to display "name: data_dict[key]" to the user.
333    """
[133]334    data_dict['num_disks'] = len(machine.disks)
335    disk_fields_template = [('%s_size', '%s size')]
336    disk_fields = []
337    for disk in machine.disks:
338        name = disk.guest_device_name
[438]339        disk_fields.extend([(x % name, y % name) for x, y in
[205]340                            disk_fields_template])
[211]341        data_dict['%s_size' % name] = "%0.1f GiB" % (disk.size / 1024.)
[133]342    return disk_fields
343
[205]344def command(user, fields):
345    """Handler for running commands like boot and delete on a VM."""
[207]346    back = fields.getfirst('back')
[205]347    try:
[209]348        d = controls.commandResult(user, fields)
[207]349        if d['command'] == 'Delete VM':
350            back = 'list'
[205]351    except InvalidInput, err:
[207]352        if not back:
[205]353            raise
[261]354        #print >> sys.stderr, err
355        result = err
[205]356    else:
357        result = 'Success!'
[207]358        if not back:
[235]359            return templates.command(searchList=[d])
[207]360    if back == 'list':
[205]361        g.clear() #Changed global state
362        d = getListDict(user)
[207]363        d['result'] = result
[235]364        return templates.list(searchList=[d])
[207]365    elif back == 'info':
[209]366        machine = validation.testMachineId(user, fields.getfirst('machine_id'))
[407]367        return ({'Status': '302',
368                 'Location': '/info?machine_id=%d' % machine.machine_id},
369                "You shouldn't see this message.")
[205]370    else:
[261]371        raise InvalidInput('back', back, 'Not a known back page.')
[205]372
373def modifyDict(user, fields):
[438]374    """Modify a machine as specified by CGI arguments.
375
376    Return a list of local variables for modify.tmpl.
377    """
[177]378    olddisk = {}
[161]379    transaction = ctx.current.create_transaction()
380    try:
[209]381        machine = validation.testMachineId(user, fields.getfirst('machine_id'))
382        owner = validation.testOwner(user, fields.getfirst('owner'), machine)
383        admin = validation.testAdmin(user, fields.getfirst('administrator'),
384                                     machine)
385        contact = validation.testContact(user, fields.getfirst('contact'),
386                                         machine)
387        name = validation.testName(user, fields.getfirst('name'), machine)
[161]388        oldname = machine.name
[205]389        command = "modify"
[153]390
[161]391        memory = fields.getfirst('memory')
392        if memory is not None:
[566]393            memory = validation.validMemory(owner, memory, machine, on=False)
[161]394            machine.memory = memory
[438]395
[440]396        vm_type = validation.validVmType(fields.getfirst('vmtype'))
397        if vm_type is not None:
398            machine.type = vm_type
399
[566]400        disksize = validation.testDisk(owner, fields.getfirst('disk'))
[161]401        if disksize is not None:
[566]402            disksize = validation.validDisk(owner, disksize, machine)
[177]403            disk = machine.disks[0]
404            if disk.size != disksize:
405                olddisk[disk.guest_device_name] = disksize
406                disk.size = disksize
407                ctx.current.save(disk)
[438]408
[446]409        update_acl = False
410        if owner is not None and owner != machine.owner:
[161]411            machine.owner = owner
[446]412            update_acl = True
[187]413        if name is not None:
[161]414            machine.name = name
[446]415        if admin is not None and admin != machine.administrator:
[187]416            machine.administrator = admin
[446]417            update_acl = True
[187]418        if contact is not None:
419            machine.contact = contact
[438]420
[161]421        ctx.current.save(machine)
[446]422        if update_acl:
423            cache_acls.refreshMachine(machine)
[161]424        transaction.commit()
425    except:
426        transaction.rollback()
[163]427        raise
[177]428    for diskname in olddisk:
[209]429        controls.resizeDisk(oldname, diskname, str(olddisk[diskname]))
[187]430    if name is not None:
[209]431        controls.renameMachine(machine, oldname, name)
[205]432    return dict(user=user,
433                command=command,
434                machine=machine)
[438]435
[205]436def modify(user, fields):
437    """Handler for modifying attributes of a machine."""
438    try:
439        modify_dict = modifyDict(user, fields)
440    except InvalidInput, err:
[207]441        result = None
[209]442        machine = validation.testMachineId(user, fields.getfirst('machine_id'))
[205]443    else:
444        machine = modify_dict['machine']
[209]445        result = 'Success!'
[205]446        err = None
447    info_dict = infoDict(user, machine)
448    info_dict['err'] = err
449    if err:
450        for field in fields.keys():
451            setattr(info_dict['defaults'], field, fields.getfirst(field))
[207]452    info_dict['result'] = result
[235]453    return templates.info(searchList=[info_dict])
[161]454
[438]455
[205]456def helpHandler(user, fields):
[145]457    """Handler for help messages."""
[139]458    simple = fields.getfirst('simple')
459    subjects = fields.getlist('subject')
[438]460
[536]461    help_mapping = {'ParaVM Console': """
[432]462ParaVM machines do not support local console access over VNC.  To
463access the serial console of these machines, you can SSH with Kerberos
[536]464to console.xvm.mit.edu, using the name of the machine as your
[432]465username.""",
[536]466                    'HVM/ParaVM': """
[139]467HVM machines use the virtualization features of the processor, while
468ParaVM machines use Xen's emulation of virtualization features.  You
469want an HVM virtualized machine.""",
[536]470                    'CPU Weight': """
[205]471Don't ask us!  We're as mystified as you are.""",
[536]472                    'Owner': """
[205]473The owner field is used to determine <a
[536]474href="help?subject=Quotas">quotas</a>.  It must be the name of a
[205]475locker that you are an AFS administrator of.  In particular, you or an
476AFS group you are a member of must have AFS rlidwka bits on the
[432]477locker.  You can check who administers the LOCKER locker using the
478commands 'attach LOCKER; fs la /mit/LOCKER' on Athena.)  See also <a
[536]479href="help?subject=Administrator">administrator</a>.""",
480                    'Administrator': """
[205]481The administrator field determines who can access the console and
482power on and off the machine.  This can be either a user or a moira
483group.""",
[536]484                    'Quotas': """
[408]485Quotas are determined on a per-locker basis.  Each locker may have a
[205]486maximum of 512 megabytes of active ram, 50 gigabytes of disk, and 4
[309]487active machines.""",
[536]488                    'Console': """
[309]489<strong>Framebuffer:</strong> At a Linux boot prompt in your VM, try
490setting <tt>fb=false</tt> to disable the framebuffer.  If you don't,
491your machine will run just fine, but the applet's display of the
492console will suffer artifacts.
493"""
[536]494                    }
[438]495
[187]496    if not subjects:
[205]497        subjects = sorted(help_mapping.keys())
[438]498
[139]499    d = dict(user=user,
500             simple=simple,
501             subjects=subjects,
[205]502             mapping=help_mapping)
[438]503
[235]504    return templates.help(searchList=[d])
[133]505
[438]506
[205]507def badOperation(u, e):
[438]508    """Function called when accessing an unknown URI."""
[205]509    raise CodeError("Unknown operation")
510
511def infoDict(user, machine):
[438]512    """Get the variables used by info.tmpl."""
[209]513    status = controls.statusInfo(machine)
[235]514    checkpoint.checkpoint('Getting status info')
[133]515    has_vnc = hasVnc(status)
516    if status is None:
517        main_status = dict(name=machine.name,
518                           memory=str(machine.memory))
[205]519        uptime = None
520        cputime = None
[133]521    else:
522        main_status = dict(status[1:])
[167]523        start_time = float(main_status.get('start_time', 0))
524        uptime = datetime.timedelta(seconds=int(time.time()-start_time))
525        cpu_time_float = float(main_status.get('cpu_time', 0))
526        cputime = datetime.timedelta(seconds=int(cpu_time_float))
[235]527    checkpoint.checkpoint('Status')
[133]528    display_fields = """name uptime memory state cpu_weight on_reboot
529     on_poweroff on_crash on_xend_start on_xend_stop bootloader""".split()
530    display_fields = [('name', 'Name'),
531                      ('owner', 'Owner'),
[187]532                      ('administrator', 'Administrator'),
[133]533                      ('contact', 'Contact'),
[136]534                      ('type', 'Type'),
[133]535                      'NIC_INFO',
536                      ('uptime', 'uptime'),
537                      ('cputime', 'CPU usage'),
538                      ('memory', 'RAM'),
539                      'DISK_INFO',
540                      ('state', 'state (xen format)'),
[536]541                      ('cpu_weight', 'CPU weight'+helppopup('CPU Weight')),
[133]542                      ('on_reboot', 'Action on VM reboot'),
543                      ('on_poweroff', 'Action on VM poweroff'),
544                      ('on_crash', 'Action on VM crash'),
545                      ('on_xend_start', 'Action on Xen start'),
546                      ('on_xend_stop', 'Action on Xen stop'),
547                      ('bootloader', 'Bootloader options'),
548                      ]
549    fields = []
550    machine_info = {}
[147]551    machine_info['name'] = machine.name
[136]552    machine_info['type'] = machine.type.hvm and 'HVM' or 'ParaVM'
[133]553    machine_info['owner'] = machine.owner
[187]554    machine_info['administrator'] = machine.administrator
[133]555    machine_info['contact'] = machine.contact
556
557    nic_fields = getNicInfo(machine_info, machine)
558    nic_point = display_fields.index('NIC_INFO')
[438]559    display_fields = (display_fields[:nic_point] + nic_fields +
[205]560                      display_fields[nic_point+1:])
[133]561
562    disk_fields = getDiskInfo(machine_info, machine)
563    disk_point = display_fields.index('DISK_INFO')
[438]564    display_fields = (display_fields[:disk_point] + disk_fields +
[205]565                      display_fields[disk_point+1:])
[438]566
[211]567    main_status['memory'] += ' MiB'
[133]568    for field, disp in display_fields:
[167]569        if field in ('uptime', 'cputime') and locals()[field] is not None:
[133]570            fields.append((disp, locals()[field]))
[147]571        elif field in machine_info:
572            fields.append((disp, machine_info[field]))
[133]573        elif field in main_status:
574            fields.append((disp, main_status[field]))
575        else:
576            pass
577            #fields.append((disp, None))
[235]578
579    checkpoint.checkpoint('Got fields')
580
581
[566]582    max_mem = validation.maxMemory(machine.owner, machine, False)
[235]583    checkpoint.checkpoint('Got mem')
[566]584    max_disk = validation.maxDisk(machine.owner, machine)
[209]585    defaults = Defaults()
[516]586    for name in 'machine_id name administrator owner memory contact'.split():
[205]587        setattr(defaults, name, getattr(machine, name))
[516]588    defaults.type = machine.type.type_id
[205]589    defaults.disk = "%0.2f" % (machine.disks[0].size/1024.)
[235]590    checkpoint.checkpoint('Got defaults')
[113]591    d = dict(user=user,
[133]592             on=status is not None,
593             machine=machine,
[205]594             defaults=defaults,
[133]595             has_vnc=has_vnc,
596             uptime=str(uptime),
597             ram=machine.memory,
[144]598             max_mem=max_mem,
599             max_disk=max_disk,
[536]600             owner_help=helppopup("Owner"),
[133]601             fields = fields)
[205]602    return d
[113]603
[205]604def info(user, fields):
605    """Handler for info on a single VM."""
[209]606    machine = validation.testMachineId(user, fields.getfirst('machine_id'))
[205]607    d = infoDict(user, machine)
[235]608    checkpoint.checkpoint('Got infodict')
609    return templates.info(searchList=[d])
[205]610
[510]611def unauthFront(_, fields):
612    """Information for unauth'd users."""
613    return templates.unauth(searchList=[{'simple' : True}])
614
[113]615mapping = dict(list=listVms,
616               vnc=vnc,
[133]617               command=command,
618               modify=modify,
[113]619               info=info,
[139]620               create=create,
[510]621               help=helpHandler,
622               unauth=unauthFront)
[113]623
[205]624def printHeaders(headers):
[438]625    """Print a dictionary as HTTP headers."""
[205]626    for key, value in headers.iteritems():
627        print '%s: %s' % (key, value)
628    print
629
630
631def getUser():
632    """Return the current user based on the SSL environment variables"""
[510]633    email = os.environ.get('SSL_CLIENT_S_DN_Email', None)
634    if email is None:
635        return None
636    return email.split("@")[0]
[205]637
[438]638def main(operation, user, fields):
[235]639    start_time = time.time()
[153]640    fun = mapping.get(operation, badOperation)
[205]641
642    if fun not in (helpHandler, ):
643        connect('postgres://sipb-xen@sipb-xen-dev.mit.edu/sipb_xen')
[119]644    try:
[235]645        checkpoint.checkpoint('Before')
[153]646        output = fun(u, fields)
[235]647        checkpoint.checkpoint('After')
[205]648
[209]649        headers = dict(DEFAULT_HEADERS)
[205]650        if isinstance(output, tuple):
651            new_headers, output = output
652            headers.update(new_headers)
653        e = revertStandardError()
[153]654        if e:
[205]655            output.addError(e)
656        printHeaders(headers)
[235]657        output_string =  str(output)
658        checkpoint.checkpoint('output as a string')
659        print output_string
[535]660        if fields.has_key('timedebug'):
661            print '<pre>%s</pre>' % checkpoint
[205]662    except Exception, err:
663        if not fields.has_key('js'):
664            if isinstance(err, CodeError):
665                print 'Content-Type: text/html\n'
666                e = revertStandardError()
667                print error(operation, u, fields, err, e)
668                sys.exit(1)
669            if isinstance(err, InvalidInput):
670                print 'Content-Type: text/html\n'
671                e = revertStandardError()
672                print invalidInput(operation, u, fields, err, e)
673                sys.exit(1)
[153]674        print 'Content-Type: text/plain\n'
[205]675        print 'Uh-oh!  We experienced an error.'
[536]676        print 'Please email xvm-dev@mit.edu with the contents of this page.'
[205]677        print '----'
678        e = revertStandardError()
[153]679        print e
680        print '----'
681        raise
[209]682
683if __name__ == '__main__':
684    fields = cgi.FieldStorage()
[535]685
686    if fields.has_key('sqldebug'):
687        import logging
688        logging.basicConfig()
689        logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
690        logging.getLogger('sqlalchemy.orm.unitofwork').setLevel(logging.INFO)
691
[209]692    u = getUser()
693    g.user = u
694    operation = os.environ.get('PATH_INFO', '')
695    if not operation:
696        print "Status: 301 Moved Permanently"
697        print 'Location: ' + os.environ['SCRIPT_NAME']+'/\n'
698        sys.exit(0)
699
[510]700    if u is None:
701        operation = 'unauth'
702
[209]703    if operation.startswith('/'):
704        operation = operation[1:]
705    if not operation:
706        operation = 'list'
707
[248]708    if os.getenv("SIPB_XEN_PROFILE"):
709        import profile
710        profile.run('main(operation, u, fields)', 'log-'+operation)
711    else:
712        main(operation, u, fields)
Note: See TracBrowser for help on using the repository browser.