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

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

Don't fail to delete the VM because it wasn't running

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