18 errio = StringIO.StringIO()
20 sys.path.append('/home/ecprice/.local/lib/python2.5/site-packages')
22 from Cheetah.Template import Template
23 from sipb_xen_database import *
26 class MyException(Exception):
27 """Base class for my exceptions"""
30 class InvalidInput(MyException):
31 """Exception for user-provided input is invalid but maybe in good faith.
33 This would include setting memory to negative (which might be a
34 typo) but not setting an invalid boot CD (which requires bypassing
37 def __init__(self, err_field, err_value, expl=None):
38 MyException.__init__(self, expl)
39 self.err_field = err_field
40 self.err_value = err_value
42 class CodeError(MyException):
43 """Exception for internal errors or bad faith input."""
47 def __init__(self, user):
50 def __get_uptimes(self):
51 if not hasattr(self, '_uptimes'):
52 self._uptimes = getUptimes(Machine.select())
54 uptimes = property(__get_uptimes)
59 """Return HTML code for a (?) link to a specified help topic"""
60 return '<span class="helplink"><a href="help?subject='+subj+'&simple=true" target="_blank" onclick="return helppopup(\''+subj+'\')">(?)</a></span>'
64 global_dict['helppopup'] = helppopup
67 # ... and stolen from xend/uuid.py
69 """Generate a random UUID."""
71 return [ random.randint(0, 255) for _ in range(0, 16) ]
74 """Turn a numeric UUID to a hyphen-seperated one."""
75 return "-".join(["%02x" * 4, "%02x" * 2, "%02x" * 2, "%02x" * 2,
76 "%02x" * 6]) % tuple(u)
78 MAX_MEMORY_TOTAL = 512
79 MAX_MEMORY_SINGLE = 256
80 MIN_MEMORY_SINGLE = 16
87 def getMachinesByOwner(user, machine=None):
88 """Return the machines owned by the same as a machine.
90 If the machine is None, return the machines owned by the same
97 return Machine.select_by(owner=owner)
99 def maxMemory(user, machine=None, on=True):
100 """Return the maximum memory for a machine or a user.
102 If machine is None, return the memory available for a new
103 machine. Else, return the maximum that machine can have.
105 on is whether the machine should be turned on. If false, the max
106 memory for the machine to change to, if it is left off, is
110 return MAX_MEMORY_SINGLE
111 machines = getMachinesByOwner(user, machine)
112 active_machines = [x for x in machines if g.uptimes[x]]
113 mem_usage = sum([x.memory for x in active_machines if x != machine])
114 return min(MAX_MEMORY_SINGLE, MAX_MEMORY_TOTAL-mem_usage)
116 def maxDisk(user, machine=None):
117 machines = getMachinesByOwner(user, machine)
118 disk_usage = sum([sum([y.size for y in x.disks])
119 for x in machines if x != machine])
120 return min(MAX_DISK_SINGLE, MAX_DISK_TOTAL-disk_usage/1024.)
123 machines = getMachinesByOwner(user)
124 active_machines = [x for x in machines if g.uptimes[x]]
125 return (len(machines) < MAX_VMS_TOTAL and
126 len(active_machines) < MAX_VMS_ACTIVE)
128 def haveAccess(user, machine):
129 """Return whether a user has adminstrative access to a machine"""
130 if user.username == 'moo':
132 if user.username in (machine.administrator, machine.owner):
134 if getafsgroups.checkAfsGroup(user, machine.administrator, 'athena.mit.edu'): #XXX Cell?
136 return owns(user, machine)
138 def owns(user, machine):
139 """Return whether a user owns a machine"""
140 if user.username == 'moo':
142 return getafsgroups.checkLockerOwner(user.username, machine.owner)
144 def error(op, user, fields, err, emsg):
145 """Print an error page when a CodeError occurs"""
146 d = dict(op=op, user=user, errorMessage=str(err),
148 return Template(file='error.tmpl', searchList=[d, global_dict]);
150 def invalidInput(op, user, fields, err, emsg):
151 """Print an error page when an InvalidInput exception occurs"""
152 d = dict(op=op, user=user, err_field=err.err_field,
153 err_value=str(err.err_value), stderr=emsg,
154 errorMessage=str(err))
155 return Template(file='invalid.tmpl', searchList=[d, global_dict]);
157 def validMachineName(name):
158 """Check that name is valid for a machine name"""
161 charset = string.ascii_letters + string.digits + '-_'
162 if name[0] in '-_' or len(name) > 22:
169 def kinit(username = 'tabbott/extra', keytab = '/etc/tabbott.keytab'):
170 """Kinit with a given username and keytab"""
172 p = subprocess.Popen(['kinit', "-k", "-t", keytab, username],
173 stderr=subprocess.PIPE)
176 raise CodeError("Error %s in kinit: %s" % (e, p.stderr.read()))
179 """If we lack tickets, kinit."""
180 p = subprocess.Popen(['klist', '-s'])
184 def remctl(*args, **kws):
185 """Perform a remctl and return the output.
187 kinits if necessary, and outputs errors to stderr.
190 p = subprocess.Popen(['remctl', 'black-mesa.mit.edu']
192 stdout=subprocess.PIPE,
193 stderr=subprocess.PIPE)
196 return p.stdout.read(), p.stderr.read()
198 print >> sys.stderr, 'Error on remctl', args, ':'
199 print >> sys.stderr, p.stderr.read()
200 raise CodeError('ERROR on remctl')
201 return p.stdout.read()
203 def lvcreate(machine, disk):
204 """Create a single disk for a machine"""
205 remctl('web', 'lvcreate', machine.name,
206 disk.guest_device_name, str(disk.size))
208 def makeDisks(machine):
209 """Update the lvm partitions to add a disk."""
210 for disk in machine.disks:
211 lvcreate(machine, disk)
213 def bootMachine(machine, cdtype):
214 """Boot a machine with a given boot CD.
216 If cdtype is None, give no boot cd. Otherwise, it is the string
217 id of the CD (e.g. 'gutsy_i386')
219 if cdtype is not None:
220 remctl('control', machine.name, 'create',
223 remctl('control', machine.name, 'create')
225 def registerMachine(machine):
226 """Register a machine to be controlled by the web interface"""
227 remctl('web', 'register', machine.name)
229 def unregisterMachine(machine):
230 """Unregister a machine to not be controlled by the web interface"""
231 remctl('web', 'unregister', machine.name)
234 """Parse a status string into nested tuples of strings.
236 s = output of xm list --long <machine_name>
238 values = re.split('([()])', s)
240 for v in values[2:-2]: #remove initial and final '()'
247 if len(stack[-1]) == 1:
249 stack[-2].append(stack[-1])
254 stack[-1].extend(v.split())
257 def getUptimes(machines=None):
258 """Return a dictionary mapping machine names to uptime strings"""
259 value_string = remctl('web', 'listvms')
260 lines = value_string.splitlines()
265 uptime = ' '.join(lst[2:])
269 ans[m] = d.get(m.name)
272 def statusInfo(machine):
273 """Return the status list for a given machine.
275 Gets and parses xm list --long
277 value_string, err_string = remctl('control', machine.name, 'list-long', err=True)
278 if 'Unknown command' in err_string:
279 raise CodeError("ERROR in remctl list-long %s is not registered" % (machine.name,))
280 elif 'does not exist' in err_string:
283 raise CodeError("ERROR in remctl list-long %s: %s" % (machine.name, err_string))
284 status = parseStatus(value_string)
288 """Does the machine with a given status list support VNC?"""
292 if l[0] == 'device' and l[1][0] == 'vfb':
294 return 'location' in d
297 def createVm(user, name, memory, disk, is_hvm, cdrom):
298 """Create a VM and put it in the database"""
299 # put stuff in the table
300 transaction = ctx.current.create_transaction()
302 if memory > maxMemory(user):
303 raise InvalidInput('memory', memory,
304 "Max %s" % maxMemory(user))
305 if disk > maxDisk(user) * 1024:
306 raise InvalidInput('disk', disk,
307 "Max %s" % maxDisk(user))
308 if not canAddVm(user):
309 raise InvalidInput('create', True, 'Unable to create more VMs')
310 res = meta.engine.execute('select nextval(\'"machines_machine_id_seq"\')')
311 id = res.fetchone()[0]
313 machine.machine_id = id
315 machine.memory = memory
316 machine.owner = user.username
317 machine.administrator = user.username
318 machine.contact = user.email
319 machine.uuid = uuidToString(randomUUID())
320 machine.boot_off_cd = True
321 machine_type = Type.get_by(hvm=is_hvm)
322 machine.type_id = machine_type.type_id
323 ctx.current.save(machine)
324 disk = Disk(machine.machine_id,
326 open = NIC.select_by(machine_id=None)
327 if not open: #No IPs left!
328 raise CodeError("No IP addresses left! Contact sipb-xen-dev@mit.edu")
330 nic.machine_id = machine.machine_id
332 ctx.current.save(nic)
333 ctx.current.save(disk)
336 transaction.rollback()
338 registerMachine(machine)
340 # tell it to boot with cdrom
341 bootMachine(machine, cdrom)
345 def validMemory(user, memory, machine=None, on=True):
346 """Parse and validate limits for memory for a given user and machine.
348 on is whether the memory must be valid after the machine is
353 if memory < MIN_MEMORY_SINGLE:
356 raise InvalidInput('memory', memory,
357 "Minimum %s MB" % MIN_MEMORY_SINGLE)
358 if memory > maxMemory(user, machine, on):
359 raise InvalidInput('memory', memory,
360 'Maximum %s MB' % maxMemory(user, machine))
363 def validDisk(user, disk, machine=None):
364 """Parse and validate limits for disk for a given user and machine."""
367 if disk > maxDisk(user, machine):
368 raise InvalidInput('disk', disk,
369 "Maximum %s G" % maxDisk(user, machine))
370 disk = int(disk * 1024)
371 if disk < MIN_DISK_SINGLE * 1024:
374 raise InvalidInput('disk', disk,
375 "Minimum %s GB" % MIN_DISK_SINGLE)
378 def create(user, fields):
379 """Handler for create requests."""
380 name = fields.getfirst('name')
381 if not validMachineName(name):
382 raise InvalidInput('name', name)
385 if Machine.get_by(name=name):
386 raise InvalidInput('name', name,
389 memory = fields.getfirst('memory')
390 memory = validMemory(user, memory, on=True)
392 disk = fields.getfirst('disk')
393 disk = validDisk(user, disk)
395 vm_type = fields.getfirst('vmtype')
396 if vm_type not in ('hvm', 'paravm'):
397 raise CodeError("Invalid vm type '%s'" % vm_type)
398 is_hvm = (vm_type == 'hvm')
400 cdrom = fields.getfirst('cdrom')
401 if cdrom is not None and not CDROM.get(cdrom):
402 raise CodeError("Invalid cdrom type '%s'" % cdrom)
404 machine = createVm(user, name, memory, disk, is_hvm, cdrom)
407 return Template(file='create.tmpl',
408 searchList=[d, global_dict]);
410 def listVms(user, fields):
411 """Handler for list requests."""
412 machines = [m for m in Machine.select() if haveAccess(user, m)]
422 has_vnc[m] = "ParaVM"+helppopup("paravm_console")
424 # status = statusInfo(m)
425 # on[m.name] = status is not None
426 # has_vnc[m.name] = hasVnc(status)
427 max_mem=maxMemory(user)
428 max_disk=maxDisk(user)
430 can_add_vm=canAddVm(user),
434 default_disk=min(4.0, max_disk),
438 cdroms=CDROM.select())
439 return Template(file='list.tmpl', searchList=[d, global_dict])
441 def testMachineId(user, machineId, exists=True):
442 """Parse, validate and check authorization for a given machineId.
444 If exists is False, don't check that it exists.
446 if machineId is None:
447 raise CodeError("No machine ID specified")
449 machineId = int(machineId)
451 raise CodeError("Invalid machine ID '%s'" % machineId)
452 machine = Machine.get(machineId)
453 if exists and machine is None:
454 raise CodeError("No such machine ID '%s'" % machineId)
455 if machine is not None and not haveAccess(user, machine):
456 raise CodeError("No access to machine ID '%s'" % machineId)
459 def vnc(user, fields):
462 Note that due to same-domain restrictions, the applet connects to
463 the webserver, which needs to forward those requests to the xen
464 server. The Xen server runs another proxy that (1) authenticates
465 and (2) finds the correct port for the VM.
467 You might want iptables like:
469 -t nat -A PREROUTING -s ! 18.181.0.60 -i eth1 -p tcp -m tcp --dport 10003 -j DNAT --to-destination 18.181.0.60:10003
470 -t nat -A POSTROUTING -d 18.181.0.60 -o eth1 -p tcp -m tcp --dport 10003 -j SNAT --to-source 18.187.7.142
471 -A FORWARD -d 18.181.0.60 -i eth1 -o eth1 -p tcp -m tcp --dport 10003 -j ACCEPT
473 Remember to enable iptables!
474 echo 1 > /proc/sys/net/ipv4/ip_forward
476 machine = testMachineId(user, fields.getfirst('machine_id'))
478 TOKEN_KEY = "0M6W0U1IXexThi5idy8mnkqPKEq1LtEnlK/pZSn0cDrN"
481 data["user"] = user.username
482 data["machine"]=machine.name
483 data["expires"]=time.time()+(5*60)
484 pickledData = cPickle.dumps(data)
485 m = hmac.new(TOKEN_KEY, digestmod=sha)
486 m.update(pickledData)
487 token = {'data': pickledData, 'digest': m.digest()}
488 token = cPickle.dumps(token)
489 token = base64.urlsafe_b64encode(token)
491 status = statusInfo(machine)
492 has_vnc = hasVnc(status)
498 hostname=os.environ.get('SERVER_NAME', 'localhost'),
500 return Template(file='vnc.tmpl',
501 searchList=[d, global_dict])
503 def getNicInfo(data_dict, machine):
504 """Helper function for info, get data on nics for a machine.
506 Modifies data_dict to include the relevant data, and returns a list
507 of (key, name) pairs to display "name: data_dict[key]" to the user.
509 data_dict['num_nics'] = len(machine.nics)
510 nic_fields_template = [('nic%s_hostname', 'NIC %s hostname'),
511 ('nic%s_mac', 'NIC %s MAC Addr'),
512 ('nic%s_ip', 'NIC %s IP'),
515 for i in range(len(machine.nics)):
516 nic_fields.extend([(x % i, y % i) for x, y in nic_fields_template])
517 data_dict['nic%s_hostname' % i] = machine.nics[i].hostname + '.servers.csail.mit.edu'
518 data_dict['nic%s_mac' % i] = machine.nics[i].mac_addr
519 data_dict['nic%s_ip' % i] = machine.nics[i].ip
520 if len(machine.nics) == 1:
521 nic_fields = [(x, y.replace('NIC 0 ', '')) for x, y in nic_fields]
524 def getDiskInfo(data_dict, machine):
525 """Helper function for info, get data on disks for a machine.
527 Modifies data_dict to include the relevant data, and returns a list
528 of (key, name) pairs to display "name: data_dict[key]" to the user.
530 data_dict['num_disks'] = len(machine.disks)
531 disk_fields_template = [('%s_size', '%s size')]
533 for disk in machine.disks:
534 name = disk.guest_device_name
535 disk_fields.extend([(x % name, y % name) for x, y in disk_fields_template])
536 data_dict['%s_size' % name] = "%0.1f GB" % (disk.size / 1024.)
539 def deleteVM(machine):
541 remctl('control', machine.name, 'destroy', err=True)
542 transaction = ctx.current.create_transaction()
543 delete_disk_pairs = [(machine.name, d.guest_device_name) for d in machine.disks]
545 for nic in machine.nics:
546 nic.machine_id = None
548 ctx.current.save(nic)
549 for disk in machine.disks:
550 ctx.current.delete(disk)
551 ctx.current.delete(machine)
554 transaction.rollback()
556 for mname, dname in delete_disk_pairs:
557 remctl('web', 'lvremove', mname, dname)
558 unregisterMachine(machine)
560 def command(user, fields):
561 """Handler for running commands like boot and delete on a VM."""
562 print >> sys.stderr, time.time()-start_time
563 machine = testMachineId(user, fields.getfirst('machine_id'))
564 action = fields.getfirst('action')
565 cdrom = fields.getfirst('cdrom')
566 print >> sys.stderr, time.time()-start_time
567 if cdrom is not None and not CDROM.get(cdrom):
568 raise CodeError("Invalid cdrom type '%s'" % cdrom)
569 if action not in ('Reboot', 'Power on', 'Power off', 'Shutdown', 'Delete VM'):
570 raise CodeError("Invalid action '%s'" % action)
571 if action == 'Reboot':
572 if cdrom is not None:
573 remctl('control', machine.name, 'reboot', cdrom)
575 remctl('control', machine.name, 'reboot')
576 elif action == 'Power on':
577 if maxMemory(user) < machine.memory:
578 raise InvalidInput('action', 'Power on',
579 "You don't have enough free RAM quota to turn on this machine")
580 bootMachine(machine, cdrom)
581 elif action == 'Power off':
582 remctl('control', machine.name, 'destroy')
583 elif action == 'Shutdown':
584 remctl('control', machine.name, 'shutdown')
585 elif action == 'Delete VM':
587 print >> sys.stderr, time.time()-start_time
592 return Template(file="command.tmpl", searchList=[d, global_dict])
594 def testAdmin(user, admin, machine):
595 if admin in (None, machine.administrator):
597 if admin == user.username:
599 if getafsgroups.checkAfsGroup(user, admin, 'athena.mit.edu'):
601 if getafsgroups.checkAfsGroup(user, 'system:'+admin, 'athena.mit.edu'):
602 return 'system:'+admin
603 raise InvalidInput('admin', admin,
604 'You must control the group you move it to')
606 def testOwner(user, owner, machine):
607 if owner in (None, machine.owner):
609 #XXX should you be able to transfer ownership if you don't already own it?
610 #if not owns(user, machine):
611 # raise InvalidInput('owner', owner, "You don't own this machine, so you can't transfer ownership")
612 value = getafsgroups.checkLockerOwner(user.username, owner, verbose=True)
615 raise InvalidInput('owner', owner, value)
617 def testContact(user, contact, machine=None):
618 if contact in (None, machine.contact):
620 if not re.match("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$", contact, re.I):
621 raise InvalidInput('contact', contact, "Not a valid email")
624 def testDisk(user, disksize, machine=None):
627 def testName(user, name, machine=None):
628 if name in (None, machine.name):
630 if not Machine.select_by(name=name):
632 raise InvalidInput('name', name, "Already taken")
634 def testHostname(user, hostname, machine):
635 for nic in machine.nics:
636 if hostname == nic.hostname:
638 # check if doesn't already exist
639 if NIC.select_by(hostname=hostname):
640 raise InvalidInput('hostname', hostname,
642 if not re.match("^[A-Z0-9-]{1,22}$", hostname, re.I):
643 raise InvalidInput('hostname', hostname, "Not a valid hostname; must only use number, letters, and dashes.")
646 def modify(user, fields):
647 """Handler for modifying attributes of a machine."""
650 transaction = ctx.current.create_transaction()
652 machine = testMachineId(user, fields.getfirst('machine_id'))
653 owner = testOwner(user, fields.getfirst('owner'), machine)
654 admin = testAdmin(user, fields.getfirst('administrator'), machine)
655 contact = testContact(user, fields.getfirst('contact'), machine)
656 hostname = testHostname(owner, fields.getfirst('hostname'), machine)
657 name = testName(user, fields.getfirst('name'), machine)
658 oldname = machine.name
661 memory = fields.getfirst('memory')
662 if memory is not None:
663 memory = validMemory(user, memory, machine, on=False)
664 machine.memory = memory
666 disksize = testDisk(user, fields.getfirst('disk'))
667 if disksize is not None:
668 disksize = validDisk(user, disksize, machine)
669 disk = machine.disks[0]
670 if disk.size != disksize:
671 olddisk[disk.guest_device_name] = disksize
673 ctx.current.save(disk)
675 # XXX first NIC gets hostname on change? Interface doesn't support more.
676 for nic in machine.nics[:1]:
677 nic.hostname = hostname
678 ctx.current.save(nic)
680 if owner is not None:
681 machine.owner = owner
684 if admin is not None:
685 machine.administrator = admin
686 if contact is not None:
687 machine.contact = contact
689 ctx.current.save(machine)
692 transaction.rollback()
694 for diskname in olddisk:
695 remctl("web", "lvresize", oldname, diskname, str(olddisk[diskname]))
697 for disk in machine.disks:
698 remctl("web", "lvrename", oldname, disk.guest_device_name, name)
699 remctl("web", "moveregister", oldname, name)
703 return Template(file="command.tmpl", searchList=[d, global_dict])
706 def help(user, fields):
707 """Handler for help messages."""
708 simple = fields.getfirst('simple')
709 subjects = fields.getlist('subject')
711 mapping = dict(paravm_console="""
712 ParaVM machines do not support console access over VNC. To access
713 these machines, you either need to boot with a liveCD and ssh in or
714 hope that the sipb-xen maintainers add support for serial consoles.""",
716 HVM machines use the virtualization features of the processor, while
717 ParaVM machines use Xen's emulation of virtualization features. You
718 want an HVM virtualized machine.""",
719 cpu_weight="""Don't ask us! We're as mystified as you are.""",
720 owner="""The owner field is used to determine <a href="help?subject=quotas">quotas</a>. It must be the name
721 of a locker that you are an AFS administrator of. In particular, you
722 or an AFS group you are a member of must have AFS rlidwka bits on the
723 locker. You can check see who administers the LOCKER locker using the
724 command 'fs la /mit/LOCKER' on Athena.) See also <a href="help?subject=administrator">administrator</a>.""",
725 administrator="""The administrator field determines who can access the console and power on and off the machine. This can be either a user or a moira group.""",
726 quotas="""Quotas are determined on a per-locker basis. Each
727 quota may have a maximum of 512 megabytes of active ram, 50 gigabytes of disk, and 4 active machines."""
732 subjects = sorted(mapping.keys())
739 return Template(file="help.tmpl", searchList=[d, global_dict])
742 def info(user, fields):
743 """Handler for info on a single VM."""
744 machine = testMachineId(user, fields.getfirst('machine_id'))
745 status = statusInfo(machine)
746 has_vnc = hasVnc(status)
748 main_status = dict(name=machine.name,
749 memory=str(machine.memory))
753 main_status = dict(status[1:])
754 start_time = float(main_status.get('start_time', 0))
755 uptime = datetime.timedelta(seconds=int(time.time()-start_time))
756 cpu_time_float = float(main_status.get('cpu_time', 0))
757 cputime = datetime.timedelta(seconds=int(cpu_time_float))
758 display_fields = """name uptime memory state cpu_weight on_reboot
759 on_poweroff on_crash on_xend_start on_xend_stop bootloader""".split()
760 display_fields = [('name', 'Name'),
762 ('administrator', 'Administrator'),
763 ('contact', 'Contact'),
766 ('uptime', 'uptime'),
767 ('cputime', 'CPU usage'),
770 ('state', 'state (xen format)'),
771 ('cpu_weight', 'CPU weight'+helppopup('cpu_weight')),
772 ('on_reboot', 'Action on VM reboot'),
773 ('on_poweroff', 'Action on VM poweroff'),
774 ('on_crash', 'Action on VM crash'),
775 ('on_xend_start', 'Action on Xen start'),
776 ('on_xend_stop', 'Action on Xen stop'),
777 ('bootloader', 'Bootloader options'),
781 machine_info['name'] = machine.name
782 machine_info['type'] = machine.type.hvm and 'HVM' or 'ParaVM'
783 machine_info['owner'] = machine.owner
784 machine_info['administrator'] = machine.administrator
785 machine_info['contact'] = machine.contact
787 nic_fields = getNicInfo(machine_info, machine)
788 nic_point = display_fields.index('NIC_INFO')
789 display_fields = display_fields[:nic_point] + nic_fields + display_fields[nic_point+1:]
791 disk_fields = getDiskInfo(machine_info, machine)
792 disk_point = display_fields.index('DISK_INFO')
793 display_fields = display_fields[:disk_point] + disk_fields + display_fields[disk_point+1:]
795 main_status['memory'] += ' MB'
796 for field, disp in display_fields:
797 if field in ('uptime', 'cputime') and locals()[field] is not None:
798 fields.append((disp, locals()[field]))
799 elif field in machine_info:
800 fields.append((disp, machine_info[field]))
801 elif field in main_status:
802 fields.append((disp, main_status[field]))
805 #fields.append((disp, None))
806 max_mem = maxMemory(user, machine)
807 max_disk = maxDisk(user, machine)
809 cdroms=CDROM.select(),
810 on=status is not None,
817 owner_help=helppopup("owner"),
819 return Template(file='info.tmpl',
820 searchList=[d, global_dict])
822 mapping = dict(list=listVms,
830 if __name__ == '__main__':
831 start_time = time.time()
832 fields = cgi.FieldStorage()
835 email = 'moo@cow.com'
838 if 'SSL_CLIENT_S_DN_Email' in os.environ:
839 username = os.environ[ 'SSL_CLIENT_S_DN_Email'].split("@")[0]
840 u.username = username
841 u.email = os.environ[ 'SSL_CLIENT_S_DN_Email']
845 connect('postgres://sipb-xen@sipb-xen-dev/sipb_xen')
846 operation = os.environ.get('PATH_INFO', '')
848 print "Status: 301 Moved Permanently"
849 print 'Location: ' + os.environ['SCRIPT_NAME']+'/\n'
852 if operation.startswith('/'):
853 operation = operation[1:]
857 def badOperation(u, e):
858 raise CodeError("Unknown operation")
860 fun = mapping.get(operation, badOperation)
861 if fun not in (help, ):
862 connect('postgres://sipb-xen@sipb-xen-dev/sipb_xen')
864 output = fun(u, fields)
865 print 'Content-Type: text/html\n'
866 sys.stderr=sys.stdout
871 output = output.replace('<body>', '<body><p>STDERR:</p><pre>'+e+'</pre>')
873 except CodeError, err:
874 print 'Content-Type: text/html\n'
875 sys.stderr=sys.stdout
878 print error(operation, u, fields, err, e)
879 except InvalidInput, err:
880 print 'Content-Type: text/html\n'
881 sys.stderr=sys.stdout
884 print invalidInput(operation, u, fields, err, e)
886 print 'Content-Type: text/plain\n'
887 sys.stderr=sys.stdout