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

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

Improve the error infrastructure. Hopefully this works; I haven't
really tested it.

  • Property svn:executable set to *
File size: 25.8 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(self.machines)
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):
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=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    transaction = ctx.current.create_transaction()
514    delete_disk_pairs = [(machine.name, d.guest_device_name) for d in machine.disks]
515    try:
516        for nic in machine.nics:
517            nic.machine_id = None
518            nic.hostname = None
519            ctx.current.save(nic)
520        for disk in machine.disks:
521            ctx.current.delete(disk)
522        ctx.current.delete(machine)
523        transaction.commit()
524    except:
525        transaction.rollback()
526        raise
527    for mname, dname in delete_disk_pairs:
528        remctl('web', 'lvremove', mname, dname)
529    unregisterMachine(machine)
530
531def command(user, fields):
532    """Handler for running commands like boot and delete on a VM."""
533    print time.time()-start_time
534    machine = testMachineId(user, fields.getfirst('machine_id'))
535    action = fields.getfirst('action')
536    cdrom = fields.getfirst('cdrom')
537    print time.time()-start_time
538    if cdrom is not None and not CDROM.get(cdrom):
539        raise CodeError("Invalid cdrom type '%s'" % cdrom)   
540    if action not in ('Reboot', 'Power on', 'Power off', 'Shutdown', 'Delete VM'):
541        raise CodeError("Invalid action '%s'" % action)
542    if action == 'Reboot':
543        if cdrom is not None:
544            remctl('reboot', machine.name, cdrom)
545        else:
546            remctl('reboot', machine.name)
547    elif action == 'Power on':
548        if maxMemory(user) < machine.memory:
549            raise InvalidInput('action', 'Power on',
550                               "You don't have enough free RAM quota to turn on this machine")
551        bootMachine(machine, cdrom)
552    elif action == 'Power off':
553        remctl('destroy', machine.name)
554    elif action == 'Shutdown':
555        remctl('shutdown', machine.name)
556    elif action == 'Delete VM':
557        deleteVM(machine)
558    print time.time()-start_time
559
560    d = dict(user=user,
561             command=action,
562             machine=machine)
563    return Template(file="command.tmpl", searchList=[d, global_dict])
564
565def testOwner(user, owner, machine=None):
566    if owner != user.username:
567        raise InvalidInput('owner', owner,
568                           "Invalid")
569    return owner
570
571def testContact(user, contact, machine=None):
572    if contact != user.email:
573        raise InvalidInput('contact', contact,
574                           "Invalid")
575    return contact
576
577def testHostname(user, hostname, machine):
578    for nic in machine.nics:
579        if hostname == nic.hostname:
580            return hostname
581    raise InvalidInput('hostname', hostname,
582                       "Different from before")
583
584
585def modify(user, fields):
586    """Handler for modifying attributes of a machine."""
587    #XXX not written yet
588    machine = testMachineId(user, fields.getfirst('machine_id'))
589    owner = testOwner(user, fields.getfirst('owner'), machine)
590    contact = testContact(user, fields.getfirst('contact'))
591    hostname = testHostname(user, fields.getfirst('hostname'),
592                            machine)
593    ram = fields.getfirst('memory')
594    if ram is not None:
595        ram = validMemory(user, ram, machine)
596    disk = testDisk(user, fields.getfirst('disk'))
597    if disk is not None:
598        disk = validDisk(user, disk, machine)
599
600   
601
602def help(user, fields):
603    """Handler for help messages."""
604    simple = fields.getfirst('simple')
605    subjects = fields.getlist('subject')
606   
607    mapping = dict(paravm_console="""
608ParaVM machines do not support console access over VNC.  To access
609these machines, you either need to boot with a liveCD and ssh in or
610hope that the sipb-xen maintainers add support for serial consoles.""",
611                   hvm_paravm="""
612HVM machines use the virtualization features of the processor, while
613ParaVM machines use Xen's emulation of virtualization features.  You
614want an HVM virtualized machine.""",
615                   cpu_weight="""Don't ask us!  We're as mystified as you are.""")
616   
617    d = dict(user=user,
618             simple=simple,
619             subjects=subjects,
620             mapping=mapping)
621   
622    return Template(file="help.tmpl", searchList=[d, global_dict])
623   
624
625def info(user, fields):
626    """Handler for info on a single VM."""
627    machine = testMachineId(user, fields.getfirst('machine_id'))
628    status = statusInfo(machine)
629    has_vnc = hasVnc(status)
630    if status is None:
631        main_status = dict(name=machine.name,
632                           memory=str(machine.memory))
633    else:
634        main_status = dict(status[1:])
635    start_time = float(main_status.get('start_time', 0))
636    uptime = datetime.timedelta(seconds=int(time.time()-start_time))
637    cpu_time_float = float(main_status.get('cpu_time', 0))
638    cputime = datetime.timedelta(seconds=int(cpu_time_float))
639    display_fields = """name uptime memory state cpu_weight on_reboot
640     on_poweroff on_crash on_xend_start on_xend_stop bootloader""".split()
641    display_fields = [('name', 'Name'),
642                      ('owner', 'Owner'),
643                      ('contact', 'Contact'),
644                      ('type', 'Type'),
645                      'NIC_INFO',
646                      ('uptime', 'uptime'),
647                      ('cputime', 'CPU usage'),
648                      ('memory', 'RAM'),
649                      'DISK_INFO',
650                      ('state', 'state (xen format)'),
651                      ('cpu_weight', 'CPU weight'+helppopup('cpu_weight')),
652                      ('on_reboot', 'Action on VM reboot'),
653                      ('on_poweroff', 'Action on VM poweroff'),
654                      ('on_crash', 'Action on VM crash'),
655                      ('on_xend_start', 'Action on Xen start'),
656                      ('on_xend_stop', 'Action on Xen stop'),
657                      ('bootloader', 'Bootloader options'),
658                      ]
659    fields = []
660    machine_info = {}
661    machine_info['name'] = machine.name
662    machine_info['type'] = machine.type.hvm and 'HVM' or 'ParaVM'
663    machine_info['owner'] = machine.owner
664    machine_info['contact'] = machine.contact
665
666    nic_fields = getNicInfo(machine_info, machine)
667    nic_point = display_fields.index('NIC_INFO')
668    display_fields = display_fields[:nic_point] + nic_fields + display_fields[nic_point+1:]
669
670    disk_fields = getDiskInfo(machine_info, machine)
671    disk_point = display_fields.index('DISK_INFO')
672    display_fields = display_fields[:disk_point] + disk_fields + display_fields[disk_point+1:]
673   
674    main_status['memory'] += ' MB'
675    for field, disp in display_fields:
676        if field in ('uptime', 'cputime'):
677            fields.append((disp, locals()[field]))
678        elif field in machine_info:
679            fields.append((disp, machine_info[field]))
680        elif field in main_status:
681            fields.append((disp, main_status[field]))
682        else:
683            pass
684            #fields.append((disp, None))
685    max_mem = maxMemory(user, machine)
686    max_disk = maxDisk(user, machine)
687    d = dict(user=user,
688             cdroms=CDROM.select(),
689             on=status is not None,
690             machine=machine,
691             has_vnc=has_vnc,
692             uptime=str(uptime),
693             ram=machine.memory,
694             max_mem=max_mem,
695             max_disk=max_disk,
696             fields = fields)
697    return Template(file='info.tmpl',
698                   searchList=[d, global_dict])
699
700mapping = dict(list=listVms,
701               vnc=vnc,
702               command=command,
703               modify=modify,
704               info=info,
705               create=create,
706               help=help)
707
708if __name__ == '__main__':
709    start_time = time.time()
710    fields = cgi.FieldStorage()
711    class User:
712        username = "moo"
713        email = 'moo@cow.com'
714    u = User()
715    g = Global(u)
716    if 'SSL_CLIENT_S_DN_Email' in os.environ:
717        username = os.environ[ 'SSL_CLIENT_S_DN_Email'].split("@")[0]
718        u.username = username
719        u.email = os.environ[ 'SSL_CLIENT_S_DN_Email']
720    else:
721        u.username = 'moo'
722        u.email = 'nobody'
723    connect('postgres://sipb-xen@sipb-xen-dev/sipb_xen')
724    operation = os.environ.get('PATH_INFO', '')
725    #print 'Content-Type: text/plain\n'
726    #print operation
727    if not operation:
728        print "Status: 301 Moved Permanently"
729        print 'Location: ' + os.environ['SCRIPT_NAME']+'/\n'
730        sys.exit(0)
731    print 'Content-Type: text/html\n'
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 = output.replace('<body>', '<body><pre>'+e+'</pre>')
751        print output
752    except CodeError, err:
753        print 'Content-Type: text/html\n'
754        sys.stderr.seek(0)
755        e = sys.stderr.read()
756        print error(operation, u, fields, err, e)
757    except InvalidInput, err:
758        print 'Content-Type: text/html\n'
759        sys.stderr.seek(0)
760        e = sys.stderr.read()
761        print invalidInput(operation, u, fields, err, e)
762    except:
763        print 'Content-Type: text/plain\n'
764        sys.stderr.seek(0)
765        e = sys.stderr.read()
766        print e
767        print '----'
768        raise
Note: See TracBrowser for help on using the repository browser.