source: trunk/web/templates/main.py @ 158

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

destroy machines before trying to delete them.

  • Property svn:executable set to *
File size: 26.0 KB
Line 
1#!/usr/bin/python
2
3import sys
4import cgi
5import os
6import string
7import subprocess
8import re
9import time
10import cPickle
11import base64
12import sha
13import hmac
14import datetime
15import StringIO
16
17sys.stderr = StringIO.StringIO()
18sys.path.append('/home/ecprice/.local/lib/python2.5/site-packages')
19
20from Cheetah.Template import Template
21from sipb_xen_database import *
22import random
23
24class MyException(Exception):
25    """Base class for my exceptions"""
26    pass
27
28class InvalidInput(MyException):
29    """Exception for user-provided input is invalid but maybe in good faith.
30
31    This would include setting memory to negative (which might be a
32    typo) but not setting an invalid boot CD (which requires bypassing
33    the select box).
34    """
35    def __init__(self, err_field, err_value, expl=None):
36        super(InvalidInput, self).__init__(expl)
37        self.err_field = err_field
38        self.err_value = err_value
39
40class CodeError(MyException):
41    """Exception for internal errors or bad faith input."""
42    pass
43
44class Global(object):
45    def __init__(self, user):
46        self.user = user
47
48    def __get_uptimes(self):
49        if not hasattr(self, '_uptimes'):
50            self._uptimes = getUptimes(Machine.select())
51        return self._uptimes
52    uptimes = property(__get_uptimes)
53
54g = None
55
56def helppopup(subj):
57    """Return HTML code for a (?) link to a specified help topic"""
58    return '<span class="helplink"><a href="help?subject='+subj+'&amp;simple=true" target="_blank" onclick="return helppopup(\''+subj+'\')">(?)</a></span>'
59
60
61global_dict = {}
62global_dict['helppopup'] = helppopup
63
64
65# ... and stolen from xend/uuid.py
66def randomUUID():
67    """Generate a random UUID."""
68
69    return [ random.randint(0, 255) for _ in range(0, 16) ]
70
71def uuidToString(u):
72    """Turn a numeric UUID to a hyphen-seperated one."""
73    return "-".join(["%02x" * 4, "%02x" * 2, "%02x" * 2, "%02x" * 2,
74                     "%02x" * 6]) % tuple(u)
75
76MAX_MEMORY_TOTAL = 512
77MAX_MEMORY_SINGLE = 256
78MIN_MEMORY_SINGLE = 16
79MAX_DISK_TOTAL = 50
80MAX_DISK_SINGLE = 50
81MIN_DISK_SINGLE = 0.1
82MAX_VMS_TOTAL = 10
83MAX_VMS_ACTIVE = 4
84
85def getMachinesByOwner(owner):
86    """Return the machines owned by a given owner."""
87    return Machine.select_by(owner=owner)
88
89def maxMemory(user, machine=None):
90    """Return the maximum memory for a machine or a user.
91
92    If machine is None, return the memory available for a new
93    machine.  Else, return the maximum that machine can have.
94
95    on is a dictionary from machines to booleans, whether a machine is
96    on.  If None, it is recomputed. XXX make this global?
97    """
98
99    machines = getMachinesByOwner(user.username)
100    active_machines = [x for x in machines if g.uptimes[x]]
101    mem_usage = sum([x.memory for x in active_machines if x != machine])
102    return min(MAX_MEMORY_SINGLE, MAX_MEMORY_TOTAL-mem_usage)
103
104def maxDisk(user, machine=None):
105    machines = getMachinesByOwner(user.username)
106    disk_usage = sum([sum([y.size for y in x.disks])
107                      for x in machines if x != machine])
108    return min(MAX_DISK_SINGLE, MAX_DISK_TOTAL-disk_usage/1024.)
109
110def canAddVm(user):
111    machines = getMachinesByOwner(user.username)
112    active_machines = [x for x in machines if g.uptimes[x]]
113    return (len(machines) < MAX_VMS_TOTAL and
114            len(active_machines) < MAX_VMS_ACTIVE)
115
116def haveAccess(user, machine):
117    """Return whether a user has access to a machine"""
118    if user.username == 'moo':
119        return True
120    return machine.owner == user.username
121
122def error(op, user, fields, err, emsg):
123    """Print an error page when a CodeError occurs"""
124    d = dict(op=op, user=user, errorMessage=str(err),
125             stderr=emsg)
126    return Template(file='error.tmpl', searchList=[d, global_dict]);
127
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))
133    return Template(file='invalid.tmpl', searchList=[d, global_dict]);
134
135def validMachineName(name):
136    """Check that name is valid for a machine name"""
137    if not name:
138        return False
139    charset = string.ascii_letters + string.digits + '-_'
140    if name[0] in '-_' or len(name) > 22:
141        return False
142    for x in name:
143        if x not in charset:
144            return False
145    return True
146
147def kinit(username = 'tabbott/extra', keytab = '/etc/tabbott.keytab'):
148    """Kinit with a given username and keytab"""
149
150    p = subprocess.Popen(['kinit', "-k", "-t", keytab, username],
151                         stderr=subprocess.PIPE)
152    e = p.wait()
153    if e:
154        raise CodeError("Error %s in kinit: %s" % (e, p.stderr.read()))
155
156def checkKinit():
157    """If we lack tickets, kinit."""
158    p = subprocess.Popen(['klist', '-s'])
159    if p.wait():
160        kinit()
161
162def remctl(*args, **kws):
163    """Perform a remctl and return the output.
164
165    kinits if necessary, and outputs errors to stderr.
166    """
167    checkKinit()
168    p = subprocess.Popen(['remctl', 'black-mesa.mit.edu']
169                         + list(args),
170                         stdout=subprocess.PIPE,
171                         stderr=subprocess.PIPE)
172    if kws.get('err'):
173        p.wait()
174        return p.stdout.read(), p.stderr.read()
175    if p.wait():
176        raise CodeError('ERROR on remctl %s: %s' %
177                          (args, p.stderr.read()))
178    return p.stdout.read()
179
180def lvcreate(machine, disk):
181    """Create a single disk for a machine"""
182    remctl('web', 'lvcreate', machine.name,
183           disk.guest_device_name, str(disk.size))
184   
185def makeDisks(machine):
186    """Update the lvm partitions to add a disk."""
187    for disk in machine.disks:
188        lvcreate(machine, disk)
189
190def bootMachine(machine, cdtype):
191    """Boot a machine with a given boot CD.
192
193    If cdtype is None, give no boot cd.  Otherwise, it is the string
194    id of the CD (e.g. 'gutsy_i386')
195    """
196    if cdtype is not None:
197        remctl('web', 'vmboot', machine.name,
198               cdtype)
199    else:
200        remctl('web', 'vmboot', machine.name)
201
202def registerMachine(machine):
203    """Register a machine to be controlled by the web interface"""
204    remctl('web', 'register', machine.name)
205
206def unregisterMachine(machine):
207    """Unregister a machine to not be controlled by the web interface"""
208    remctl('web', 'unregister', machine.name)
209
210def parseStatus(s):
211    """Parse a status string into nested tuples of strings.
212
213    s = output of xm list --long <machine_name>
214    """
215    values = re.split('([()])', s)
216    stack = [[]]
217    for v in values[2:-2]: #remove initial and final '()'
218        if not v:
219            continue
220        v = v.strip()
221        if v == '(':
222            stack.append([])
223        elif v == ')':
224            if len(stack[-1]) == 1:
225                stack[-1].append('')
226            stack[-2].append(stack[-1])
227            stack.pop()
228        else:
229            if not v:
230                continue
231            stack[-1].extend(v.split())
232    return stack[-1]
233
234def getUptimes(machines=None):
235    """Return a dictionary mapping machine names to uptime strings"""
236    value_string = remctl('web', 'listvms')
237    lines = value_string.splitlines()
238    d = {}
239    for line in lines:
240        lst = line.split()
241        name, id = lst[:2]
242        uptime = ' '.join(lst[2:])
243        d[name] = uptime
244    ans = {}
245    for m in machines:
246        ans[m] = d.get(m.name)
247    return ans
248
249def statusInfo(machine):
250    """Return the status list for a given machine.
251
252    Gets and parses xm list --long
253    """
254    value_string, err_string = remctl('list-long', machine.name, err=True)
255    if 'Unknown command' in err_string:
256        raise CodeError("ERROR in remctl list-long %s is not registered" % (machine.name,))
257    elif 'does not exist' in err_string:
258        return None
259    elif err_string:
260        raise CodeError("ERROR in remctl list-long %s%s" % (machine.name, err_string))
261    status = parseStatus(value_string)
262    return status
263
264def hasVnc(status):
265    """Does the machine with a given status list support VNC?"""
266    if status is None:
267        return False
268    for l in status:
269        if l[0] == 'device' and l[1][0] == 'vfb':
270            d = dict(l[1][1:])
271            return 'location' in d
272    return False
273
274def createVm(user, name, memory, disk, is_hvm, cdrom):
275    """Create a VM and put it in the database"""
276    # put stuff in the table
277    transaction = ctx.current.create_transaction()
278    try:
279        if memory > maxMemory(user):
280            raise InvalidInput('memory', memory,
281                               "Max %s" % maxMemory(user))
282        if disk > maxDisk(user) * 1024:
283            raise InvalidInput('disk', disk,
284                               "Max %s" % maxDisk(user))
285        if not canAddVm(user):
286            raise InvalidInput('create', True, 'Unable to create more VMs')
287        res = meta.engine.execute('select nextval(\'"machines_machine_id_seq"\')')
288        id = res.fetchone()[0]
289        machine = Machine()
290        machine.machine_id = id
291        machine.name = name
292        machine.memory = memory
293        machine.owner = user.username
294        machine.contact = user.email
295        machine.uuid = uuidToString(randomUUID())
296        machine.boot_off_cd = True
297        machine_type = Type.get_by(hvm=is_hvm)
298        machine.type_id = machine_type.type_id
299        ctx.current.save(machine)
300        disk = Disk(machine.machine_id, 
301                    'hda', disk)
302        open = NIC.select_by(machine_id=None)
303        if not open: #No IPs left!
304            raise CodeError("No IP addresses left!  Contact sipb-xen-dev@mit.edu")
305        nic = open[0]
306        nic.machine_id = machine.machine_id
307        nic.hostname = name
308        ctx.current.save(nic)   
309        ctx.current.save(disk)
310        transaction.commit()
311    except:
312        transaction.rollback()
313        raise
314    registerMachine(machine)
315    makeDisks(machine)
316    # tell it to boot with cdrom
317    bootMachine(machine, cdrom)
318
319    return machine
320
321def validMemory(user, memory, machine=None):
322    """Parse and validate limits for memory for a given user and machine."""
323    try:
324        memory = int(memory)
325        if memory < MIN_MEMORY_SINGLE:
326            raise ValueError
327    except ValueError:
328        raise InvalidInput('memory', memory, 
329                           "Minimum %s MB" % MIN_MEMORY_SINGLE)
330    if memory > maxMemory(user, machine):
331        raise InvalidInput('memory', memory,
332                           'Maximum %s MB' % maxMemory(user, machine))
333    return memory
334
335def validDisk(user, disk, machine=None):
336    """Parse and validate limits for disk for a given user and machine."""
337    try:
338        disk = float(disk)
339        if disk > maxDisk(user, machine):
340            raise InvalidInput('disk', disk,
341                               "Maximum %s G" % maxDisk(user, machine))
342        disk = int(disk * 1024)
343        if disk < MIN_DISK_SINGLE * 1024:
344            raise ValueError
345    except ValueError:
346        raise InvalidInput('disk', disk,
347                           "Minimum %s GB" % MIN_DISK_SINGLE)
348    return disk
349
350def create(user, fields):
351    """Handler for create requests."""
352    name = fields.getfirst('name')
353    if not validMachineName(name):
354        raise InvalidInput('name', name)
355    name = user.username + '_' + name.lower()
356
357    if Machine.get_by(name=name):
358        raise InvalidInput('name', name,
359                           "Already exists")
360   
361    memory = fields.getfirst('memory')
362    memory = validMemory(user, memory)
363   
364    disk = fields.getfirst('disk')
365    disk = validDisk(user, disk)
366
367    vm_type = fields.getfirst('vmtype')
368    if vm_type not in ('hvm', 'paravm'):
369        raise CodeError("Invalid vm type '%s'"  % vm_type)   
370    is_hvm = (vm_type == 'hvm')
371
372    cdrom = fields.getfirst('cdrom')
373    if cdrom is not None and not CDROM.get(cdrom):
374        raise CodeError("Invalid cdrom type '%s'" % cdrom)   
375   
376    machine = createVm(user, name, memory, disk, is_hvm, cdrom)
377    d = dict(user=user,
378             machine=machine)
379    return Template(file='create.tmpl',
380                   searchList=[d, global_dict]);
381
382def listVms(user, fields):
383    """Handler for list requests."""
384    machines = [m for m in Machine.select() if haveAccess(user, m)]   
385    on = {}
386    has_vnc = {}
387    on = g.uptimes
388    for m in machines:
389        if not on[m]:
390            has_vnc[m] = 'Off'
391        elif m.type.hvm:
392            has_vnc[m] = True
393        else:
394            has_vnc[m] = "ParaVM"+helppopup("paravm_console")
395    #     for m in machines:
396    #         status = statusInfo(m)
397    #         on[m.name] = status is not None
398    #         has_vnc[m.name] = hasVnc(status)
399    max_mem=maxMemory(user)
400    max_disk=maxDisk(user)
401    d = dict(user=user,
402             can_add_vm=canAddVm(user),
403             max_mem=max_mem,
404             max_disk=max_disk,
405             default_mem=max_mem,
406             default_disk=min(4.0, max_disk),
407             machines=machines,
408             has_vnc=has_vnc,
409             uptimes=g.uptimes,
410             cdroms=CDROM.select())
411    return Template(file='list.tmpl', searchList=[d, global_dict])
412
413def testMachineId(user, machineId, exists=True):
414    """Parse, validate and check authorization for a given machineId.
415
416    If exists is False, don't check that it exists.
417    """
418    if machineId is None:
419        raise CodeError("No machine ID specified")
420    try:
421        machineId = int(machineId)
422    except ValueError:
423        raise CodeError("Invalid machine ID '%s'" % machineId)
424    machine = Machine.get(machineId)
425    if exists and machine is None:
426        raise CodeError("No such machine ID '%s'" % machineId)
427    if machine is not None and not haveAccess(user, machine):
428        raise CodeError("No access to machine ID '%s'" % machineId)
429    return machine
430
431def vnc(user, fields):
432    """VNC applet page.
433
434    Note that due to same-domain restrictions, the applet connects to
435    the webserver, which needs to forward those requests to the xen
436    server.  The Xen server runs another proxy that (1) authenticates
437    and (2) finds the correct port for the VM.
438
439    You might want iptables like:
440
441    -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
442    -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
443    -A FORWARD -d 18.181.0.60 -i eth1 -o eth1 -p tcp -m tcp --dport 10003 -j ACCEPT
444
445    Remember to enable iptables!
446    echo 1 > /proc/sys/net/ipv4/ip_forward
447    """
448    machine = testMachineId(user, fields.getfirst('machine_id'))
449   
450    TOKEN_KEY = "0M6W0U1IXexThi5idy8mnkqPKEq1LtEnlK/pZSn0cDrN"
451
452    data = {}
453    data["user"] = user.username
454    data["machine"]=machine.name
455    data["expires"]=time.time()+(5*60)
456    pickledData = cPickle.dumps(data)
457    m = hmac.new(TOKEN_KEY, digestmod=sha)
458    m.update(pickledData)
459    token = {'data': pickledData, 'digest': m.digest()}
460    token = cPickle.dumps(token)
461    token = base64.urlsafe_b64encode(token)
462   
463    status = statusInfo(machine)
464    has_vnc = hasVnc(status)
465   
466    d = dict(user=user,
467             on=status,
468             has_vnc=has_vnc,
469             machine=machine,
470             hostname=os.environ.get('SERVER_NAME', 'localhost'),
471             authtoken=token)
472    return Template(file='vnc.tmpl',
473                   searchList=[d, global_dict])
474
475def getNicInfo(data_dict, machine):
476    """Helper function for info, get data on nics for a machine.
477
478    Modifies data_dict to include the relevant data, and returns a list
479    of (key, name) pairs to display "name: data_dict[key]" to the user.
480    """
481    data_dict['num_nics'] = len(machine.nics)
482    nic_fields_template = [('nic%s_hostname', 'NIC %s hostname'),
483                           ('nic%s_mac', 'NIC %s MAC Addr'),
484                           ('nic%s_ip', 'NIC %s IP'),
485                           ]
486    nic_fields = []
487    for i in range(len(machine.nics)):
488        nic_fields.extend([(x % i, y % i) for x, y in nic_fields_template])
489        data_dict['nic%s_hostname' % i] = machine.nics[i].hostname + '.servers.csail.mit.edu'
490        data_dict['nic%s_mac' % i] = machine.nics[i].mac_addr
491        data_dict['nic%s_ip' % i] = machine.nics[i].ip
492    if len(machine.nics) == 1:
493        nic_fields = [(x, y.replace('NIC 0 ', '')) for x, y in nic_fields]
494    return nic_fields
495
496def getDiskInfo(data_dict, machine):
497    """Helper function for info, get data on disks for a machine.
498
499    Modifies data_dict to include the relevant data, and returns a list
500    of (key, name) pairs to display "name: data_dict[key]" to the user.
501    """
502    data_dict['num_disks'] = len(machine.disks)
503    disk_fields_template = [('%s_size', '%s size')]
504    disk_fields = []
505    for disk in machine.disks:
506        name = disk.guest_device_name
507        disk_fields.extend([(x % name, y % name) for x, y in disk_fields_template])
508        data_dict['%s_size' % name] = "%0.1f GB" % (disk.size / 1024.)
509    return disk_fields
510
511def deleteVM(machine):
512    """Delete a VM."""
513    remctl('destroy', machine.name)
514    transaction = ctx.current.create_transaction()
515    delete_disk_pairs = [(machine.name, d.guest_device_name) for d in machine.disks]
516    try:
517        for nic in machine.nics:
518            nic.machine_id = None
519            nic.hostname = None
520            ctx.current.save(nic)
521        for disk in machine.disks:
522            ctx.current.delete(disk)
523        ctx.current.delete(machine)
524        transaction.commit()
525    except:
526        transaction.rollback()
527        raise
528    for mname, dname in delete_disk_pairs:
529        remctl('web', 'lvremove', mname, dname)
530    unregisterMachine(machine)
531
532def command(user, fields):
533    """Handler for running commands like boot and delete on a VM."""
534    print >> sys.stderr, time.time()-start_time
535    machine = testMachineId(user, fields.getfirst('machine_id'))
536    action = fields.getfirst('action')
537    cdrom = fields.getfirst('cdrom')
538    print >> sys.stderr, time.time()-start_time
539    if cdrom is not None and not CDROM.get(cdrom):
540        raise CodeError("Invalid cdrom type '%s'" % cdrom)   
541    if action not in ('Reboot', 'Power on', 'Power off', 'Shutdown', 'Delete VM'):
542        raise CodeError("Invalid action '%s'" % action)
543    if action == 'Reboot':
544        if cdrom is not None:
545            remctl('reboot', machine.name, cdrom)
546        else:
547            remctl('reboot', machine.name)
548    elif action == 'Power on':
549        if maxMemory(user) < machine.memory:
550            raise InvalidInput('action', 'Power on',
551                               "You don't have enough free RAM quota to turn on this machine")
552        bootMachine(machine, cdrom)
553    elif action == 'Power off':
554        remctl('destroy', machine.name)
555    elif action == 'Shutdown':
556        remctl('shutdown', machine.name)
557    elif action == 'Delete VM':
558        deleteVM(machine)
559    print >> sys.stderr, time.time()-start_time
560
561    d = dict(user=user,
562             command=action,
563             machine=machine)
564    return Template(file="command.tmpl", searchList=[d, global_dict])
565
566def testOwner(user, owner, machine=None):
567    if owner != user.username:
568        raise InvalidInput('owner', owner,
569                           "Invalid")
570    return owner
571
572def testContact(user, contact, machine=None):
573    if contact != user.email:
574        raise InvalidInput('contact', contact,
575                           "Invalid")
576    return contact
577
578def testHostname(user, hostname, machine):
579    for nic in machine.nics:
580        if hostname == nic.hostname:
581            return hostname
582    raise InvalidInput('hostname', hostname,
583                       "Different from before")
584
585
586def modify(user, fields):
587    """Handler for modifying attributes of a machine."""
588    #XXX not written yet
589    machine = testMachineId(user, fields.getfirst('machine_id'))
590    owner = testOwner(user, fields.getfirst('owner'), machine)
591    contact = testContact(user, fields.getfirst('contact'))
592    hostname = testHostname(user, fields.getfirst('hostname'),
593                            machine)
594    ram = fields.getfirst('memory')
595    if ram is not None:
596        ram = validMemory(user, ram, machine)
597    disk = testDisk(user, fields.getfirst('disk'))
598    if disk is not None:
599        disk = validDisk(user, disk, machine)
600
601   
602
603def help(user, fields):
604    """Handler for help messages."""
605    simple = fields.getfirst('simple')
606    subjects = fields.getlist('subject')
607   
608    mapping = dict(paravm_console="""
609ParaVM machines do not support console access over VNC.  To access
610these machines, you either need to boot with a liveCD and ssh in or
611hope that the sipb-xen maintainers add support for serial consoles.""",
612                   hvm_paravm="""
613HVM machines use the virtualization features of the processor, while
614ParaVM machines use Xen's emulation of virtualization features.  You
615want an HVM virtualized machine.""",
616                   cpu_weight="""Don't ask us!  We're as mystified as you are.""")
617   
618    d = dict(user=user,
619             simple=simple,
620             subjects=subjects,
621             mapping=mapping)
622   
623    return Template(file="help.tmpl", searchList=[d, global_dict])
624   
625
626def info(user, fields):
627    """Handler for info on a single VM."""
628    machine = testMachineId(user, fields.getfirst('machine_id'))
629    status = statusInfo(machine)
630    has_vnc = hasVnc(status)
631    if status is None:
632        main_status = dict(name=machine.name,
633                           memory=str(machine.memory))
634    else:
635        main_status = dict(status[1:])
636    start_time = float(main_status.get('start_time', 0))
637    uptime = datetime.timedelta(seconds=int(time.time()-start_time))
638    cpu_time_float = float(main_status.get('cpu_time', 0))
639    cputime = datetime.timedelta(seconds=int(cpu_time_float))
640    display_fields = """name uptime memory state cpu_weight on_reboot
641     on_poweroff on_crash on_xend_start on_xend_stop bootloader""".split()
642    display_fields = [('name', 'Name'),
643                      ('owner', 'Owner'),
644                      ('contact', 'Contact'),
645                      ('type', 'Type'),
646                      'NIC_INFO',
647                      ('uptime', 'uptime'),
648                      ('cputime', 'CPU usage'),
649                      ('memory', 'RAM'),
650                      'DISK_INFO',
651                      ('state', 'state (xen format)'),
652                      ('cpu_weight', 'CPU weight'+helppopup('cpu_weight')),
653                      ('on_reboot', 'Action on VM reboot'),
654                      ('on_poweroff', 'Action on VM poweroff'),
655                      ('on_crash', 'Action on VM crash'),
656                      ('on_xend_start', 'Action on Xen start'),
657                      ('on_xend_stop', 'Action on Xen stop'),
658                      ('bootloader', 'Bootloader options'),
659                      ]
660    fields = []
661    machine_info = {}
662    machine_info['name'] = machine.name
663    machine_info['type'] = machine.type.hvm and 'HVM' or 'ParaVM'
664    machine_info['owner'] = machine.owner
665    machine_info['contact'] = machine.contact
666
667    nic_fields = getNicInfo(machine_info, machine)
668    nic_point = display_fields.index('NIC_INFO')
669    display_fields = display_fields[:nic_point] + nic_fields + display_fields[nic_point+1:]
670
671    disk_fields = getDiskInfo(machine_info, machine)
672    disk_point = display_fields.index('DISK_INFO')
673    display_fields = display_fields[:disk_point] + disk_fields + display_fields[disk_point+1:]
674   
675    main_status['memory'] += ' MB'
676    for field, disp in display_fields:
677        if field in ('uptime', 'cputime'):
678            fields.append((disp, locals()[field]))
679        elif field in machine_info:
680            fields.append((disp, machine_info[field]))
681        elif field in main_status:
682            fields.append((disp, main_status[field]))
683        else:
684            pass
685            #fields.append((disp, None))
686    max_mem = maxMemory(user, machine)
687    max_disk = maxDisk(user, machine)
688    d = dict(user=user,
689             cdroms=CDROM.select(),
690             on=status is not None,
691             machine=machine,
692             has_vnc=has_vnc,
693             uptime=str(uptime),
694             ram=machine.memory,
695             max_mem=max_mem,
696             max_disk=max_disk,
697             fields = fields)
698    return Template(file='info.tmpl',
699                   searchList=[d, global_dict])
700
701mapping = dict(list=listVms,
702               vnc=vnc,
703               command=command,
704               modify=modify,
705               info=info,
706               create=create,
707               help=help)
708
709if __name__ == '__main__':
710    start_time = time.time()
711    fields = cgi.FieldStorage()
712    class User:
713        username = "moo"
714        email = 'moo@cow.com'
715    u = User()
716    g = Global(u)
717    if 'SSL_CLIENT_S_DN_Email' in os.environ:
718        username = os.environ[ 'SSL_CLIENT_S_DN_Email'].split("@")[0]
719        u.username = username
720        u.email = os.environ[ 'SSL_CLIENT_S_DN_Email']
721    else:
722        u.username = 'moo'
723        u.email = 'nobody'
724    connect('postgres://sipb-xen@sipb-xen-dev/sipb_xen')
725    operation = os.environ.get('PATH_INFO', '')
726#    print 'Content-Type: text/plain\n'
727#    print operation
728    if not operation:
729        print "Status: 301 Moved Permanently"
730        print 'Location: ' + os.environ['SCRIPT_NAME']+'/\n'
731        sys.exit(0)
732
733    if operation.startswith('/'):
734        operation = operation[1:]
735    if not operation:
736        operation = 'list'
737
738    def badOperation(u, e):
739        raise CodeError("Unknown operation")
740
741    fun = mapping.get(operation, badOperation)
742    if fun not in (help, ):
743        connect('postgres://sipb-xen@sipb-xen-dev/sipb_xen')
744    try:
745        output = fun(u, fields)
746        print 'Content-Type: text/html\n'
747        sys.stderr.seek(0)
748        e = sys.stderr.read()
749        if e:
750            output = str(output)
751            output = output.replace('<body>', '<body><p>STDERR:</p><pre>'+e+'</pre>')
752        print output
753    except CodeError, err:
754        print 'Content-Type: text/html\n'
755        sys.stderr.seek(0)
756        e = sys.stderr.read()
757        sys.stderr=sys.stdout
758        print error(operation, u, fields, err, e)
759    except InvalidInput, err:
760        print 'Content-Type: text/html\n'
761        sys.stderr.seek(0)
762        e = sys.stderr.read()
763        sys.stderr=sys.stdout
764        print invalidInput(operation, u, fields, err, e)
765    except:
766        print 'Content-Type: text/plain\n'
767        sys.stderr.seek(0)
768        e = sys.stderr.read()
769        print e
770        print '----'
771        sys.stderr = sys.stdout
772        raise
Note: See TracBrowser for help on using the repository browser.