Changeset 205 for trunk


Ignore:
Timestamp:
Oct 20, 2007, 8:28:32 AM (17 years ago)
Author:
ecprice
Message:

A monster checkin, with a variety of changes to the web
infrastructure.

Adds some support for javascript and asynchronous updates.

Also added prototype.

The interface is *really* *slow*, though.

Location:
trunk/web/templates
Files:
4 added
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/web/templates/info.tmpl

    r187 r205  
    66#end def
    77
    8 
    9 #def body
    10 <h1>Info</h1>
     8#def infoTable()
    119<p>Info on ${machine.name}:</p>
    1210<table>
     
    1513  #end for
    1614</table>
     15#end def
    1716
    18 <p>Commands:</p>
     17#def commands()
     18<script>
     19function commandButton(elt, action){
     20  form = elt.up('', 4);
     21  cdrom = Form.serialize(form, true).cdrom;
     22  new Ajax.Request('command', {method: 'post',
     23  parameters: 'machine_id=$machine.machine_id&js=info&cdrom='+cdrom+'&action='+action,
     24  onSuccess: replaceFunc
     25  });
     26  return false;
     27}
     28</script>
    1929<form action="command" action="POST">
    2030  <input type="hidden" name="machine_id" value="$machine.machine_id"/>
     
    3444      <tr>
    3545        #if $on
    36         <td><input type="submit" class="button" name="action" value="Power off"/></td>
    37         <td><input type="submit" class="button" name="action" value="Shutdown"/></td>
    38         <td><input type="submit" class="button" name="action" value="Reboot"/></td>
     46        <td><input type="submit" class="button" name="action" value="Power off" onclick="return commandButton(this, 'Power off');"/></td>
     47        <td><input type="submit" class="button" name="action" value="Shutdown" onclick="return commandButton(this, 'Shutdown');"/></td>
     48        <td><input type="submit" class="button" name="action" value="Reboot" onclick="return commandButton(this, 'Reboot');"/></td>
    3949        #else
    40         <td><input type="submit" class="button" name="action" value="Power on"/></td>
     50        <td><input type="submit" class="button" name="action" value="Power on" onclick="return commandButton(this, 'Power on');"/></td>
    4151        #end if
    4252      <td>Boot CD:</td>
    43       <td><select name="cdrom">
    44           <option selected value="">None</option>
    45           #for $cdrom in $cdroms
    46           <option value="$cdrom.cdrom_id">
    47             $cdrom.description
    48           </option>
    49           #end for
    50       </select></td>
     53      <td>$cdromList($cdroms)</td>
    5154  </tr>
    5255    <tr>
    53       <td><input type="submit" class="button" name="action" value="Delete VM"/></td>
     56      <td><input type="submit" class="button" name="action" value="Delete VM" onclick="return confirm('Are you sure that you want to delete this VM?');"/></td>
    5457    </tr>
    5558  </table>
    5659</form>
    57 <p>Change settings:
     60#end def
     61
     62#def modifyForm()
     63#if $err
     64<p class="error">We had a problem with your request:</p>
     65#else if $varExists('new_machine')
     66<p>Successfully modified.</p>
     67#end if
    5868#if $on
    5969(To edit ram, disk size, or machine name, turn off the machine first.)
    6070#end if
    6171</p>
    62 <form action="modify" method="POST">
    63   <input type="hidden" name="machine_id" value="$machine.machine_id"/>
     72<form action="modify" method="POST" onsubmit="return jsFormSubmit('modify', this);">
     73  <input type="hidden" name="machine_id" value="$defaults.machine_id"/>
    6474  <table>
    65     <tr><td>Owner${helppopup("owner")}:</td><td><input type="text" name="owner", value="$machine.owner"/></td></tr>
    66     <tr><td>Administrator${helppopup("administrator")}:</td><td><input type="text" name="administrator", value="$machine.administrator"/></td></tr>
    67     <tr><td>Contact email:</td><td><input type="text" name="contact" value="$machine.contact"/></td></tr>
     75    <tr><td>Owner${helppopup("owner")}:</td><td><input type="text" name="owner", value="$defaults.owner"/></td></tr>
     76$errorRow('owner', $err)
     77    <tr><td>Administrator${helppopup("administrator")}:</td><td><input type="text" name="administrator", value="$defaults.administrator"/></td></tr>
     78$errorRow('administrator', $err)
     79    <tr><td>Contact email:</td><td><input type="text" name="contact" value="$defaults.contact"/></td></tr>
     80$errorRow('contact', $err)
    6881#if $machine.nics
    69     <tr><td>Hostname:</td><td><input type="text" name="hostname" value="$machine.nics[0].hostname"/>.servers.csail.mit.edu</td></tr>
     82    <tr><td>Hostname:</td><td><input type="text" name="hostname" value="$defaults.hostname"/>.servers.csail.mit.edu</td></tr>
    7083#end if
     84$errorRow('hostname', $err)
    7185#if not $on
    72     <tr><td>Machine Name:</td><td><input type="text" name="name" value="$machine.name"/></td></tr>
    73     <tr><td>Ram:</td><td><input type="text" size=3 name="memory" value="$machine.memory"/>MB (max $max_mem)</td></tr>
    74     <tr><td>Disk:</td><td><input type="text" size=3 name="disk" value="${machine.disks[0].size/1024.}"/>GB (max $max_disk)</td><td>WARNING: Modifying disk size may corrupt your data.</td></tr>
     86    <tr><td>Machine Name:</td><td><input type="text" name="name" value="$defaults.name"/></td></tr>
     87$errorRow('name', $err)
     88    <tr><td>Ram:</td><td><input type="text" size=3 name="memory" value="$defaults.memory"/>MB (max $max_mem)</td></tr>
     89$errorRow('memory', $err)
     90    <tr><td>Disk:</td><td><input type="text" size=3 name="disk" value="$defaults.disk"/>GB (max $max_disk)</td><td>WARNING: Modifying disk size may corrupt your data.</td></tr>
     91$errorRow('disk', $err)
     92#else
     93$errorRow('name', $err)
     94$errorRow('memory', $err)
     95$errorRow('disk', $err)
    7596#end if
    7697    <tr><td><input type="submit" class="button" name="action" value="Change"/></td></tr>
    7798  </table>
    7899</form>
     100#end def
    79101
     102#def body
     103<h1>Info</h1>
     104<div id="info">
     105  $infoTable()
     106</div>
     107
     108<p>Commands:</p>
     109<div id="commands">
     110  $commands()
     111</div>
     112<p>Change settings:
     113<div id="modify">
     114  $modifyForm()
     115</div>
    80116#end def
  • trunk/web/templates/list.tmpl

    r186 r205  
    11#from skeleton import skeleton
    22#extends skeleton
     3
    34
    45#def title
     
    67#end def
    78
    8 #def body
    9 #if not $machines
    10    
     9#def createTable()
     10#if $cant_add_vm
     11<p>$cant_add_vm</p>
    1112#else
    12     <p>You have the following VMs:</p>
    13     <table>
    14       <tr>
    15         <td>Name</td>
    16         <td>Memory</td>
    17         <td>owner</td>
    18         <td>IP</td>
    19         <td>Hostname</td>
    20         <td>Uptime</td>
    21         <td>VNC</td>
    22         <td></td>
    23       #for $machine in $machines:
     13<p>Create a new VM:</p>
     14#if $err
     15<p class="error">We had a problem with your request:</p>
     16#else if $varExists('new_machine')
     17<p>Congratulations! You successfully created a new VM called $new_machine.</p>
     18#end if
     19    <form action="create" method="POST" onsubmit="return jsFormSubmit('create', this);">
     20      <table>
     21      $errorRow('create', $err)
     22        <tr>
     23          <td>Name</td>
     24          <td><input type="text" name="name" value="$defaults.name"/></td>
     25        </tr>
     26$errorRow('name', $err)
     27        <tr>
     28          <td>Memory</td>
     29          <td><input type="text" name="memory" value="$defaults.memory" size=3/> megabytes ($max_memory max)</td>
     30        </tr>
     31$errorRow('memory', $err)
     32        <tr>
     33          <td>Disk</td>
     34          <td><input type="text" name="disk" value="$defaults.disk" size=3/> gigabytes (${"%0.1f" % ($max_disk-0.05)} max)</td>
     35        </tr>
     36$errorRow('disk', $err)
     37        <tr>
     38          <td>HVM/ParaVM$helppopup('hvm_paravm')</td>
     39          <td>
     40#for $value, $name in (('hvm', 'HVM'), ('paravm', 'ParaVM'))
     41   <input #slurp
     42#if $defaults.vmtype == $value then 'checked' else ''
     43 type="radio" name="vmtype" value="$value">$name</input>
     44#end for
     45          </td>
     46        </tr>
     47$errorRow('vmtype', $err)
     48        <tr>
     49          <td>Boot CD</td>
     50          <td>$cdromList($cdroms, $defaults.cdrom)</td>
     51        </tr>
     52$errorRow('cdrom', $err)
     53      </table>
     54      <input type="submit" class="button" value="Create it!"/>
     55    </form>
     56#end if
     57#end def
     58
     59#def machineRow($machine)
    2460      <tr>
    2561        <td><a href="info?machine_id=$machine.machine_id">$machine.name</a></td>
     
    3571#end if
    3672<td>#slurp
    37 #if $uptimes[$machine]
    38 $uptimes[$machine]#slurp
     73#if $machine.uptime
     74$machine.uptime#slurp
    3975#else
    4076Off#slurp
     
    4985</td>
    5086        <td>
    51           <form action="command">
     87          <form action="command" method="post" onsubmit="return rowFormSubmit(this, 'list');">
    5288            <input type="hidden" name="machine_id"
    5389                   value="$machine.machine_id"/>
    54 #if $uptimes[$machine]
    55             <input type="submit" class="button" name="action" value="Shutdown"/>
    56 #else
    57             <input type="submit" class="button" name="action" value="Power on"/>
    58 #end if
     90<input type="submit" class="button" name="action" value="#slurp
     91#if $machine.uptime then 'Shutdown' else 'Power on'
     92"/>
    5993          </form>
    6094        </td>
    6195      </tr>
     96#end def
     97
     98#def machineList($machines)
     99    <table>
     100      <tr>
     101        <td>Name</td>
     102        <td>Memory</td>
     103        <td>owner</td>
     104        <td>IP</td>
     105        <td>Hostname</td>
     106        <td>Uptime</td>
     107        <td>VNC</td>
     108        <td></td>
     109      #for $machine in $machines:
     110        $machineRow($machine)
    62111      #end for
    63112    </table>
     113#end def
     114
     115
     116#def body
     117#if not $machines
     118<p>You don't currently control any VMs.</p>   
     119#else
     120    <p>You have the following VMs:</p>
    64121#end if
    65 #if $can_add_vm
    66     <p>Create a new VM:</p>
    67     <form action="create" method="POST">
    68       <table>
    69         <tr>
    70           <td>Name</td>
    71           <td><input type="text" name="name" value=""/></td>
    72         </tr>
    73         <tr>
    74           <td>Memory</td>
    75           <td><input type="text" name="memory" value="$default_mem" size=3/> megabytes ($max_mem max)</td>
    76         </tr>
    77         <tr>
    78           <td>Disk</td>
    79           <td><input type="text" name="disk" value="$default_disk" size=3/> gigabytes (${"%0.1f" % ($max_disk-0.05)} max)</td>
    80         </tr>
    81         <tr>
    82           <td>HVM/ParaVM$helppopup('hvm_paravm')</td>
    83           <td>
    84             <input checked type="radio" name="vmtype" value="hvm">HVM</input>
    85             <input type="radio" name="vmtype" value="paravm">ParaVM</input>
    86           </td>
    87         </tr>
    88         <tr>
    89           <td>Boot CD</td>
    90           <td>
    91             <select name="cdrom">
    92               <option selected value="">None</option>
    93               #for $cdrom in $cdroms
    94               <option value="$cdrom.cdrom_id">
    95                 $cdrom.description
    96               </option>
    97               #end for
    98             </select>
    99           </td>
    100         </tr>
    101       </table>
    102       <input type="submit" class="button" value="Create it!"/>
    103     </form>
    104 #else
    105 <p>You are at the maximum number of VMs.</p>
    106 #end if
     122    <p><a href="list" onclick="new Ajax.Updater('machinelist', 'list?js=machinelist', {method: 'get' });return false">refresh</a></p>
     123    <div id="machinelist">
     124    $machineList($machines)
     125    </div>
     126<div id="createtable">
     127$createTable()
     128</div>
    107129#end def
  • trunk/web/templates/main.py

    r203 r205  
    11#!/usr/bin/python
    2 
    3 import sys
     2"""Main CGI script for web interface"""
     3
     4import base64
     5import cPickle
    46import cgi
     7import datetime
     8import getafsgroups
     9import hmac
    510import os
     11import random
     12import re
     13import sha
     14import simplejson
    615import string
    716import subprocess
    8 import re
     17import sys
    918import time
    10 import cPickle
    11 import base64
    12 import sha
    13 import hmac
    14 import datetime
    15 import StringIO
    16 import getafsgroups
    17 
    18 errio = StringIO.StringIO()
    19 sys.stderr = errio
     19from StringIO import StringIO
     20
     21
     22def revertStandardError():
     23    """Move stderr to stdout, and return the contents of the old stderr."""
     24    errio = sys.stderr
     25    if not isinstance(errio, StringIO):
     26        return None
     27    sys.stderr = sys.stdout
     28    errio.seek(0)
     29    return errio.read()
     30
     31def printError():
     32    """Revert stderr to stdout, and print the contents of stderr"""
     33    if isinstance(sys.stderr, StringIO):
     34        print revertStandardError()
     35
     36if __name__ == '__main__':
     37    import atexit
     38    atexit.register(printError)
     39    sys.stderr = StringIO()
     40
    2041sys.path.append('/home/ecprice/.local/lib/python2.5/site-packages')
    2142
    2243from Cheetah.Template import Template
    2344from sipb_xen_database import *
    24 import random
    2545
    2646class MyException(Exception):
     
    4464    pass
    4565
     66def helppopup(subj):
     67    """Return HTML code for a (?) link to a specified help topic"""
     68    return ('<span class="helplink"><a href="help?subject=' + subj +
     69            '&amp;simple=true" target="_blank" ' +
     70            'onclick="return helppopup(\'' + subj + '\')">(?)</a></span>')
     71
    4672class Global(object):
     73    """Global state of the system, to avoid duplicate remctls to get state"""
    4774    def __init__(self, user):
    4875        self.user = user
     
    5481    uptimes = property(__get_uptimes)
    5582
     83    def clear(self):
     84        """Clear the state so future accesses reload it."""
     85        for attr in ('_uptimes', ):
     86            if hasattr(self, attr):
     87                delattr(self, attr)
     88
    5689g = None
    5790
    58 def helppopup(subj):
    59     """Return HTML code for a (?) link to a specified help topic"""
    60     return '<span class="helplink"><a href="help?subject='+subj+'&amp;simple=true" target="_blank" onclick="return helppopup(\''+subj+'\')">(?)</a></span>'
    61 
    62 
    63 global_dict = {}
    64 global_dict['helppopup'] = helppopup
    65 
     91class User:
     92    """User class (sort of useless, I admit)"""
     93    def __init__(self, username, email):
     94        self.username = username
     95        self.email = email
     96
     97def makeErrorPre(old, addition):
     98    if addition is None:
     99        return
     100    if old:
     101        return old[:-6]  + '\n----\n' + str(addition) + '</pre>'
     102    else:
     103        return '<p>STDERR:</p><pre>' + str(addition) + '</pre>'
     104
     105Template.helppopup = staticmethod(helppopup)
     106Template.err = None
     107
     108class JsonDict:
     109    """Class to store a dictionary that will be converted to JSON"""
     110    def __init__(self, **kws):
     111        self.data = kws
     112        if 'err' in kws:
     113            err = kws['err']
     114            del kws['err']
     115            self.addError(err)
     116
     117    def __str__(self):
     118        return simplejson.dumps(self.data)
     119
     120    def addError(self, text):
     121        """Add stderr text to be displayed on the website."""
     122        self.data['err'] = \
     123            makeErrorPre(self.data.get('err'), text)
     124
     125class Defaults:
     126    """Class to store default values for fields."""
     127    memory = 256
     128    disk = 4.0
     129    cdrom = ''
     130    name = ''
     131    vmtype = 'hvm'
     132    def __init__(self, max_memory=None, max_disk=None, **kws):
     133        if max_memory is not None:
     134            self.memory = min(self.memory, max_memory)
     135        if max_disk is not None:
     136            self.max_disk = min(self.disk, max_disk)
     137        for key in kws:
     138            setattr(self, key, kws[key])
     139
     140
     141
     142default_headers = {'Content-Type': 'text/html'}
    66143
    67144# ... and stolen from xend/uuid.py
     
    120197    return min(MAX_DISK_SINGLE, MAX_DISK_TOTAL-disk_usage/1024.)
    121198
    122 def canAddVm(user):
     199def cantAddVm(user):
    123200    machines = getMachinesByOwner(user)
    124201    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)
     202    if len(machines) >= MAX_VMS_TOTAL:
     203        return 'You have too many VMs to create a new one.'
     204    if len(active_machines) >= MAX_VMS_ACTIVE:
     205        return ('You already have the maximum number of VMs turned on.  '
     206                'To create more, turn one off.')
     207    return False
    127208
    128209def haveAccess(user, machine):
     
    132213    if user.username in (machine.administrator, machine.owner):
    133214        return True
    134     if getafsgroups.checkAfsGroup(user.username, machine.administrator, 'athena.mit.edu'): #XXX Cell?
     215    if getafsgroups.checkAfsGroup(user.username, machine.administrator,
     216                                  'athena.mit.edu'): #XXX Cell?
    135217        return True
    136218    if getafsgroups.checkLockerOwner(user.username, machine.owner):
     
    148230    d = dict(op=op, user=user, errorMessage=str(err),
    149231             stderr=emsg)
    150     return Template(file='error.tmpl', searchList=[d, global_dict]);
     232    return Template(file='error.tmpl', searchList=[d]);
    151233
    152234def invalidInput(op, user, fields, err, emsg):
     
    155237             err_value=str(err.err_value), stderr=emsg,
    156238             errorMessage=str(err))
    157     return Template(file='invalid.tmpl', searchList=[d, global_dict]);
     239    return Template(file='invalid.tmpl', searchList=[d]);
    158240
    159241def validMachineName(name):
     
    194276                         stdout=subprocess.PIPE,
    195277                         stderr=subprocess.PIPE)
     278    v = p.wait()
    196279    if kws.get('err'):
    197         p.wait()
    198280        return p.stdout.read(), p.stderr.read()
    199     if p.wait():
    200         print >> sys.stderr, 'Error on remctl', args, ':'
     281    if v:
     282        print >> sys.stderr, 'Error', v, 'on remctl', args, ':'
    201283        print >> sys.stderr, p.stderr.read()
    202284        raise CodeError('ERROR on remctl')
     
    277359    Gets and parses xm list --long
    278360    """
    279     value_string, err_string = remctl('control', machine.name, 'list-long', err=True)
     361    value_string, err_string = remctl('control', machine.name, 'list-long',
     362                                      err=True)
    280363    if 'Unknown command' in err_string:
    281         raise CodeError("ERROR in remctl list-long %s is not registered" % (machine.name,))
     364        raise CodeError("ERROR in remctl list-long %s is not registered" %
     365                        (machine.name,))
    282366    elif 'does not exist' in err_string:
    283367        return None
    284368    elif err_string:
    285         raise CodeError("ERROR in remctl list-long %s:  %s" % (machine.name, err_string))
     369        raise CodeError("ERROR in remctl list-long %s:  %s" %
     370                        (machine.name, err_string))
    286371    status = parseStatus(value_string)
    287372    return status
     
    308393            raise InvalidInput('disk', disk,
    309394                               "Max %s" % maxDisk(user))
    310         if not canAddVm(user):
    311             raise InvalidInput('create', True, 'Unable to create more VMs')
    312         res = meta.engine.execute('select nextval(\'"machines_machine_id_seq"\')')
     395        reason = cantAddVm(user)
     396        if reason:
     397            raise InvalidInput('create', True, reason)
     398        res = meta.engine.execute('select nextval('
     399                                  '\'"machines_machine_id_seq"\')')
    313400        id = res.fetchone()[0]
    314401        machine = Machine()
     
    326413        disk = Disk(machine.machine_id,
    327414                    'hda', disk)
    328         open = NIC.select_by(machine_id=None)
    329         if not open: #No IPs left!
    330             raise CodeError("No IP addresses left!  Contact sipb-xen-dev@mit.edu")
    331         nic = open[0]
     415        open_nics = NIC.select_by(machine_id=None)
     416        if not open_nics: #No IPs left!
     417            raise CodeError("No IP addresses left!  "
     418                            "Contact sipb-xen-dev@mit.edu")
     419        nic = open_nics[0]
    332420        nic.machine_id = machine.machine_id
    333421        nic.hostname = name
     
    378466    return disk
    379467
    380 def create(user, fields):
    381     """Handler for create requests."""
     468def parseCreate(user, fields):
    382469    name = fields.getfirst('name')
    383470    if not validMachineName(name):
    384         raise InvalidInput('name', name)
     471        raise InvalidInput('name', name, 'You must provide a machine name.')
    385472    name = name.lower()
    386473
    387474    if Machine.get_by(name=name):
    388475        raise InvalidInput('name', name,
    389                            "Already exists")
     476                           "Name already exists.")
    390477   
    391478    memory = fields.getfirst('memory')
     
    402489    cdrom = fields.getfirst('cdrom')
    403490    if cdrom is not None and not CDROM.get(cdrom):
    404         raise CodeError("Invalid cdrom type '%s'" % cdrom)   
    405    
    406     machine = createVm(user, name, memory, disk, is_hvm, cdrom)
    407     d = dict(user=user,
    408              machine=machine)
    409     return Template(file='create.tmpl',
    410                    searchList=[d, global_dict]);
    411 
    412 def listVms(user, fields):
    413     """Handler for list requests."""
     491        raise CodeError("Invalid cdrom type '%s'" % cdrom)
     492    return dict(user=user, name=name, memory=memory, disk=disk,
     493                is_hvm=is_hvm, cdrom=cdrom)
     494
     495def create(user, fields):
     496    """Handler for create requests."""
     497    js = fields.getfirst('js')
     498    try:
     499        parsed_fields = parseCreate(user, fields)
     500        machine = createVm(**parsed_fields)
     501    except InvalidInput, err:
     502        if not js:
     503            raise
     504    else:
     505        err = None
     506        if not js:
     507            d = dict(user=user,
     508                     machine=machine)
     509            return Template(file='create.tmpl', searchList=[d])
     510    g.clear() #Changed global state
     511    d = getListDict(user)
     512    d['err'] = err
     513    if err:
     514        for field in fields.keys():
     515            setattr(d['defaults'], field, fields.getfirst(field))
     516    else:
     517        d['new_machine'] = parsed_fields['name']
     518    t = Template(file='list.tmpl', searchList=[d])
     519    return JsonDict(createtable=t.createTable(),
     520                    machinelist=t.machineList(d['machines']))
     521
     522
     523def getListDict(user):
    414524    machines = [m for m in Machine.select() if haveAccess(user, m)]   
    415525    on = {}
     
    417527    on = g.uptimes
    418528    for m in machines:
     529        m.uptime = g.uptimes.get(m)
    419530        if not on[m]:
    420531            has_vnc[m] = 'Off'
     
    427538    #         on[m.name] = status is not None
    428539    #         has_vnc[m.name] = hasVnc(status)
    429     max_mem=maxMemory(user)
    430     max_disk=maxDisk(user)
     540    max_memory = maxMemory(user)
     541    max_disk = maxDisk(user)
     542    defaults = Defaults(max_memory=max_memory,
     543                        max_disk=max_disk,
     544                        cdrom='gutsy-i386')
    431545    d = dict(user=user,
    432              can_add_vm=canAddVm(user),
    433              max_mem=max_mem,
     546             cant_add_vm=cantAddVm(user),
     547             max_memory=max_memory,
    434548             max_disk=max_disk,
    435              default_mem=max_mem,
    436              default_disk=min(4.0, max_disk),
     549             defaults=defaults,
    437550             machines=machines,
    438551             has_vnc=has_vnc,
    439552             uptimes=g.uptimes,
    440553             cdroms=CDROM.select())
    441     return Template(file='list.tmpl', searchList=[d, global_dict])
    442 
     554    return d
     555
     556def listVms(user, fields):
     557    """Handler for list requests."""
     558    d = getListDict(user)
     559    t = Template(file='list.tmpl', searchList=[d])
     560    js = fields.getfirst('js')
     561    if not js:
     562        return t
     563    if js == 'machinelist':
     564        return t.machineList(d['machines'])
     565    elif js.startswith('machinerow-'):
     566        request_machine_id = int(js.split('-')[1])
     567        m = [x for x in d['machines'] if x.id == request_machine_id]
     568        return t.machineRow(m)
     569    elif js == 'createtable':
     570        return t.createTable()
     571           
    443572def testMachineId(user, machineId, exists=True):
    444573    """Parse, validate and check authorization for a given machineId.
     
    469598    You might want iptables like:
    470599
    471     -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
    472     -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
    473     -A FORWARD -d 18.181.0.60 -i eth1 -o eth1 -p tcp -m tcp --dport 10003 -j ACCEPT
     600    -t nat -A PREROUTING -s ! 18.181.0.60 -i eth1 -p tcp -m tcp \
     601      --dport 10003 -j DNAT --to-destination 18.181.0.60:10003
     602    -t nat -A POSTROUTING -d 18.181.0.60 -o eth1 -p tcp -m tcp \
     603      --dport 10003 -j SNAT --to-source 18.187.7.142
     604    -A FORWARD -d 18.181.0.60 -i eth1 -o eth1 -p tcp -m tcp \
     605      --dport 10003 -j ACCEPT
    474606
    475607    Remember to enable iptables!
     
    482614    data = {}
    483615    data["user"] = user.username
    484     data["machine"]=machine.name
    485     data["expires"]=time.time()+(5*60)
    486     pickledData = cPickle.dumps(data)
     616    data["machine"] = machine.name
     617    data["expires"] = time.time()+(5*60)
     618    pickled_data = cPickle.dumps(data)
    487619    m = hmac.new(TOKEN_KEY, digestmod=sha)
    488     m.update(pickledData)
    489     token = {'data': pickledData, 'digest': m.digest()}
     620    m.update(pickled_data)
     621    token = {'data': pickled_data, 'digest': m.digest()}
    490622    token = cPickle.dumps(token)
    491623    token = base64.urlsafe_b64encode(token)
     
    500632             hostname=os.environ.get('SERVER_NAME', 'localhost'),
    501633             authtoken=token)
    502     return Template(file='vnc.tmpl',
    503                    searchList=[d, global_dict])
     634    return Template(file='vnc.tmpl', searchList=[d])
    504635
    505636def getNicInfo(data_dict, machine):
     
    517648    for i in range(len(machine.nics)):
    518649        nic_fields.extend([(x % i, y % i) for x, y in nic_fields_template])
    519         data_dict['nic%s_hostname' % i] = machine.nics[i].hostname + '.servers.csail.mit.edu'
     650        data_dict['nic%s_hostname' % i] = (machine.nics[i].hostname +
     651                                           '.servers.csail.mit.edu')
    520652        data_dict['nic%s_mac' % i] = machine.nics[i].mac_addr
    521653        data_dict['nic%s_ip' % i] = machine.nics[i].ip
     
    535667    for disk in machine.disks:
    536668        name = disk.guest_device_name
    537         disk_fields.extend([(x % name, y % name) for x, y in disk_fields_template])
     669        disk_fields.extend([(x % name, y % name) for x, y in
     670                            disk_fields_template])
    538671        data_dict['%s_size' % name] = "%0.1f GB" % (disk.size / 1024.)
    539672    return disk_fields
     
    543676    remctl('control', machine.name, 'destroy', err=True)
    544677    transaction = ctx.current.create_transaction()
    545     delete_disk_pairs = [(machine.name, d.guest_device_name) for d in machine.disks]
     678    delete_disk_pairs = [(machine.name, d.guest_device_name)
     679                         for d in machine.disks]
    546680    try:
    547681        for nic in machine.nics:
     
    560694    unregisterMachine(machine)
    561695
    562 def command(user, fields):
    563     """Handler for running commands like boot and delete on a VM."""
     696def commandResult(user, fields):
    564697    print >> sys.stderr, time.time()-start_time
    565698    machine = testMachineId(user, fields.getfirst('machine_id'))
     
    569702    if cdrom is not None and not CDROM.get(cdrom):
    570703        raise CodeError("Invalid cdrom type '%s'" % cdrom)   
    571     if action not in ('Reboot', 'Power on', 'Power off', 'Shutdown', 'Delete VM'):
     704    if action not in ('Reboot', 'Power on', 'Power off', 'Shutdown',
     705                      'Delete VM'):
    572706        raise CodeError("Invalid action '%s'" % action)
    573707    if action == 'Reboot':
    574708        if cdrom is not None:
    575             remctl('control', machine.name, 'reboot', cdrom)
     709            out, err = remctl('control', machine.name, 'reboot', cdrom,
     710                              err=True)
    576711        else:
    577             remctl('control', machine.name, 'reboot')
     712            out, err = remctl('control', machine.name, 'reboot',
     713                              err=True)
     714        if err:
     715            if re.match("Error: Domain '.*' does not exist.", err):
     716                raise InvalidInput("action", "reboot",
     717                                   "Machine is not on")
     718            else:
     719                print >> sys.stderr, 'Error on reboot:'
     720                print >> sys.stderr, err
     721                raise CodeError('ERROR on remctl')
     722               
    578723    elif action == 'Power on':
    579724        if maxMemory(user) < machine.memory:
    580725            raise InvalidInput('action', 'Power on',
    581                                "You don't have enough free RAM quota to turn on this machine")
     726                               "You don't have enough free RAM quota "
     727                               "to turn on this machine.")
    582728        bootMachine(machine, cdrom)
    583729    elif action == 'Power off':
    584         remctl('control', machine.name, 'destroy')
     730        out, err = remctl('control', machine.name, 'destroy', err=True)
     731        if err:
     732            if re.match("Error: Domain '.*' does not exist.", err):
     733                raise InvalidInput("action", "Power off",
     734                                   "Machine is not on.")
     735            else:
     736                print >> sys.stderr, 'Error on power off:'
     737                print >> sys.stderr, err
     738                raise CodeError('ERROR on remctl')
    585739    elif action == 'Shutdown':
    586         remctl('control', machine.name, 'shutdown')
     740        out, err = remctl('control', machine.name, 'shutdown', err=True)
     741        if err:
     742            if re.match("Error: Domain '.*' does not exist.", err):
     743                raise InvalidInput("action", "Shutdown",
     744                                   "Machine is not on.")
     745            else:
     746                print >> sys.stderr, 'Error on Shutdown:'
     747                print >> sys.stderr, err
     748                raise CodeError('ERROR on remctl')
    587749    elif action == 'Delete VM':
    588750        deleteVM(machine)
     
    592754             command=action,
    593755             machine=machine)
    594     return Template(file="command.tmpl", searchList=[d, global_dict])
     756    return d
     757
     758def command(user, fields):
     759    """Handler for running commands like boot and delete on a VM."""
     760    js = fields.getfirst('js')
     761    try:
     762        d = commandResult(user, fields)
     763    except InvalidInput, err:
     764        if not js:
     765            raise
     766        result = None
     767    else:
     768        err = None
     769        result = 'Success!'
     770        if not js:
     771            return Template(file='command.tmpl', searchList=[d])
     772    if js == 'list':
     773        g.clear() #Changed global state
     774        d = getListDict(user)
     775        t = Template(file='list.tmpl', searchList=[d])
     776        return JsonDict(createtable=t.createTable(),
     777                        machinelist=t.machineList(d['machines']),
     778                        result=result,
     779                        err=err)
     780    elif js == 'info':
     781        machine = testMachineId(user, fields.getfirst('machine_id'))
     782        d = infoDict(user, machine)
     783        t = Template(file='info.tmpl', searchList=[d])
     784        return JsonDict(info=t.infoTable(),
     785                        commands=t.commands(),
     786                        modify=t.modifyForm(),
     787                        result=result,
     788                        err=err)
     789    else:
     790        raise InvalidInput('js', js, 'Not a known js type.')
    595791
    596792def testAdmin(user, admin, machine):
     
    601797    if getafsgroups.checkAfsGroup(user.username, admin, 'athena.mit.edu'):
    602798        return admin
    603     if getafsgroups.checkAfsGroup(user.username, 'system:'+admin, 'athena.mit.edu'):
     799    if getafsgroups.checkAfsGroup(user.username, 'system:'+admin,
     800                                  'athena.mit.edu'):
    604801        return 'system:'+admin
    605     raise InvalidInput('admin', admin,
    606                        'You must control the group you move it to')
     802    raise InvalidInput('administrator', admin,
     803                       'You must control the group you move it to.')
    607804   
    608805def testOwner(user, owner, machine):
    609806    if owner in (None, machine.owner):
    610807        return None
    611     #XXX should you be able to transfer ownership if you don't already own it?
    612     #if not owns(user, machine):
    613     #    raise InvalidInput('owner', owner, "You don't own this machine, so you can't  transfer ownership")
    614808    value = getafsgroups.checkLockerOwner(user.username, owner, verbose=True)
    615809    if value == True:
     
    621815        return None
    622816    if not re.match("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$", contact, re.I):
    623         raise InvalidInput('contact', contact, "Not a valid email")
     817        raise InvalidInput('contact', contact, "Not a valid email.")
    624818    return contact
    625819
     
    632826    if not Machine.select_by(name=name):
    633827        return name
    634     raise InvalidInput('name', name, "Already taken")
     828    raise InvalidInput('name', name, "Name is already taken.")
    635829
    636830def testHostname(user, hostname, machine):
     
    643837                           "Already exists")
    644838    if not re.match("^[A-Z0-9-]{1,22}$", hostname, re.I):
    645         raise InvalidInput('hostname', hostname, "Not a valid hostname; must only use number, letters, and dashes.")
     839        raise InvalidInput('hostname', hostname, "Not a valid hostname; "
     840                           "must only use number, letters, and dashes.")
    646841    return hostname
    647842
    648 def modify(user, fields):
    649     """Handler for modifying attributes of a machine."""
    650 
     843def modifyDict(user, fields):
    651844    olddisk = {}
    652845    transaction = ctx.current.create_transaction()
     
    659852        name = testName(user, fields.getfirst('name'), machine)
    660853        oldname = machine.name
    661         command="modify"
     854        command = "modify"
    662855
    663856        memory = fields.getfirst('memory')
     
    675868                ctx.current.save(disk)
    676869       
    677         # XXX first NIC gets hostname on change?  Interface doesn't support more.
     870        # XXX first NIC gets hostname on change? 
     871        # Interface doesn't support more.
    678872        for nic in machine.nics[:1]:
    679873            nic.hostname = hostname
     
    700894            remctl("web", "lvrename", oldname, disk.guest_device_name, name)
    701895        remctl("web", "moveregister", oldname, name)
    702     d = dict(user=user,
    703              command=command,
    704              machine=machine)
    705     return Template(file="command.tmpl", searchList=[d, global_dict])   
    706 
    707 
    708 def help(user, fields):
     896    return dict(user=user,
     897                command=command,
     898                machine=machine)
     899   
     900def modify(user, fields):
     901    """Handler for modifying attributes of a machine."""
     902    js = fields.getfirst('js')
     903    try:
     904        modify_dict = modifyDict(user, fields)
     905    except InvalidInput, err:
     906        if not js:
     907            raise
     908        result = ''
     909        machine = testMachineId(user, fields.getfirst('machine_id'))
     910    else:
     911        machine = modify_dict['machine']
     912        result='Success!'
     913        err = None
     914        if not js:
     915            return Template(file='command.tmpl', searchList=[modify_dict])
     916    info_dict = infoDict(user, machine)
     917    info_dict['err'] = err
     918    if err:
     919        for field in fields.keys():
     920            setattr(info_dict['defaults'], field, fields.getfirst(field))
     921    t = Template(file='info.tmpl', searchList=[info_dict])
     922    return JsonDict(info=t.infoTable(),
     923                    commands=t.commands(),
     924                    modify=t.modifyForm(),
     925                    result=result,
     926                    err=err)
     927   
     928
     929def helpHandler(user, fields):
    709930    """Handler for help messages."""
    710931    simple = fields.getfirst('simple')
    711932    subjects = fields.getlist('subject')
    712933   
    713     mapping = dict(paravm_console="""
     934    help_mapping = dict(paravm_console="""
    714935ParaVM machines do not support console access over VNC.  To access
    715936these machines, you either need to boot with a liveCD and ssh in or
    716937hope that the sipb-xen maintainers add support for serial consoles.""",
    717                    hvm_paravm="""
     938                        hvm_paravm="""
    718939HVM machines use the virtualization features of the processor, while
    719940ParaVM machines use Xen's emulation of virtualization features.  You
    720941want an HVM virtualized machine.""",
    721                    cpu_weight="""Don't ask us!  We're as mystified as you are.""",
    722                    owner="""The owner field is used to determine <a href="help?subject=quotas">quotas</a>.  It must be the name
    723 of a locker that you are an AFS administrator of.  In particular, you
    724 or an AFS group you are a member of must have AFS rlidwka bits on the
     942                        cpu_weight="""
     943Don't ask us!  We're as mystified as you are.""",
     944                        owner="""
     945The owner field is used to determine <a
     946href="help?subject=quotas">quotas</a>.  It must be the name of a
     947locker that you are an AFS administrator of.  In particular, you or an
     948AFS group you are a member of must have AFS rlidwka bits on the
    725949locker.  You can check see who administers the LOCKER locker using the
    726 command 'fs la /mit/LOCKER' on Athena.)  See also <a href="help?subject=administrator">administrator</a>.""",
    727                    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.""",
    728                    quotas="""Quotas are determined on a per-locker basis.  Each
    729 quota may have a maximum of 512 megabytes of active ram, 50 gigabytes of disk, and 4 active machines."""
    730 
     950command 'fs la /mit/LOCKER' on Athena.)  See also <a
     951href="help?subject=administrator">administrator</a>.""",
     952                        administrator="""
     953The administrator field determines who can access the console and
     954power on and off the machine.  This can be either a user or a moira
     955group.""",
     956                        quotas="""
     957Quotas are determined on a per-locker basis.  Each quota may have a
     958maximum of 512 megabytes of active ram, 50 gigabytes of disk, and 4
     959active machines."""
    731960                   )
    732961   
    733962    if not subjects:
    734         subjects = sorted(mapping.keys())
     963        subjects = sorted(help_mapping.keys())
    735964       
    736965    d = dict(user=user,
    737966             simple=simple,
    738967             subjects=subjects,
    739              mapping=mapping)
    740    
    741     return Template(file="help.tmpl", searchList=[d, global_dict])
    742    
    743 
    744 def info(user, fields):
    745     """Handler for info on a single VM."""
    746     machine = testMachineId(user, fields.getfirst('machine_id'))
     968             mapping=help_mapping)
     969   
     970    return Template(file="help.tmpl", searchList=[d])
     971   
     972
     973def badOperation(u, e):
     974    raise CodeError("Unknown operation")
     975
     976def infoDict(user, machine):
    747977    status = statusInfo(machine)
    748978    has_vnc = hasVnc(status)
     
    750980        main_status = dict(name=machine.name,
    751981                           memory=str(machine.memory))
    752         uptime=None
    753         cputime=None
     982        uptime = None
     983        cputime = None
    754984    else:
    755985        main_status = dict(status[1:])
     
    7891019    nic_fields = getNicInfo(machine_info, machine)
    7901020    nic_point = display_fields.index('NIC_INFO')
    791     display_fields = display_fields[:nic_point] + nic_fields + display_fields[nic_point+1:]
     1021    display_fields = (display_fields[:nic_point] + nic_fields +
     1022                      display_fields[nic_point+1:])
    7921023
    7931024    disk_fields = getDiskInfo(machine_info, machine)
    7941025    disk_point = display_fields.index('DISK_INFO')
    795     display_fields = display_fields[:disk_point] + disk_fields + display_fields[disk_point+1:]
     1026    display_fields = (display_fields[:disk_point] + disk_fields +
     1027                      display_fields[disk_point+1:])
    7961028   
    7971029    main_status['memory'] += ' MB'
     
    8081040    max_mem = maxMemory(user, machine)
    8091041    max_disk = maxDisk(user, machine)
     1042    defaults=Defaults()
     1043    for name in 'machine_id name administrator owner memory contact'.split():
     1044        setattr(defaults, name, getattr(machine, name))
     1045    if machine.nics:
     1046        defaults.hostname = machine.nics[0].hostname
     1047    defaults.disk = "%0.2f" % (machine.disks[0].size/1024.)
    8101048    d = dict(user=user,
    8111049             cdroms=CDROM.select(),
    8121050             on=status is not None,
    8131051             machine=machine,
     1052             defaults=defaults,
    8141053             has_vnc=has_vnc,
    8151054             uptime=str(uptime),
     
    8191058             owner_help=helppopup("owner"),
    8201059             fields = fields)
    821     return Template(file='info.tmpl',
    822                    searchList=[d, global_dict])
     1060    return d
     1061
     1062def info(user, fields):
     1063    """Handler for info on a single VM."""
     1064    machine = testMachineId(user, fields.getfirst('machine_id'))
     1065    d = infoDict(user, machine)
     1066    return Template(file='info.tmpl', searchList=[d])
    8231067
    8241068mapping = dict(list=listVms,
     
    8281072               info=info,
    8291073               create=create,
    830                help=help)
     1074               help=helpHandler)
     1075
     1076def printHeaders(headers):
     1077    for key, value in headers.iteritems():
     1078        print '%s: %s' % (key, value)
     1079    print
     1080
     1081
     1082def getUser():
     1083    """Return the current user based on the SSL environment variables"""
     1084    if 'SSL_CLIENT_S_DN_Email' in os.environ:
     1085        username = os.environ['SSL_CLIENT_S_DN_Email'].split("@")[0]
     1086        return User(username, os.environ['SSL_CLIENT_S_DN_Email'])
     1087    else:
     1088        return User('moo', 'nobody')
    8311089
    8321090if __name__ == '__main__':
    8331091    start_time = time.time()
    8341092    fields = cgi.FieldStorage()
    835     class User:
    836         username = "moo"
    837         email = 'moo@cow.com'
    838     u = User()
     1093    u = getUser()
    8391094    g = Global(u)
    840     if 'SSL_CLIENT_S_DN_Email' in os.environ:
    841         username = os.environ[ 'SSL_CLIENT_S_DN_Email'].split("@")[0]
    842         u.username = username
    843         u.email = os.environ[ 'SSL_CLIENT_S_DN_Email']
    844     else:
    845         u.username = 'moo'
    846         u.email = 'nobody'
    847     connect('postgres://sipb-xen@sipb-xen-dev/sipb_xen')
    8481095    operation = os.environ.get('PATH_INFO', '')
    8491096    if not operation:
     
    8571104        operation = 'list'
    8581105
    859     def badOperation(u, e):
    860         raise CodeError("Unknown operation")
     1106
    8611107
    8621108    fun = mapping.get(operation, badOperation)
    863     if fun not in (help, ):
    864         connect('postgres://sipb-xen@sipb-xen-dev/sipb_xen')
     1109
     1110    if fun not in (helpHandler, ):
     1111        connect('postgres://sipb-xen@sipb-xen-dev.mit.edu/sipb_xen')
    8651112    try:
    8661113        output = fun(u, fields)
    867         print 'Content-Type: text/html\n'
    868         sys.stderr=sys.stdout
    869         errio.seek(0)
    870         e = errio.read()
     1114
     1115        headers = dict(default_headers)
     1116        if isinstance(output, tuple):
     1117            new_headers, output = output
     1118            headers.update(new_headers)
     1119
     1120        e = revertStandardError()
    8711121        if e:
    872             output = str(output)
    873             output = output.replace('<body>', '<body><p>STDERR:</p><pre>'+e+'</pre>')
     1122            output.addError(e)
     1123        printHeaders(headers)
    8741124        print output
    875     except CodeError, err:
    876         print 'Content-Type: text/html\n'
    877         sys.stderr=sys.stdout
    878         errio.seek(0)
    879         e = errio.read()
    880         print error(operation, u, fields, err, e)
    881     except InvalidInput, err:
    882         print 'Content-Type: text/html\n'
    883         sys.stderr=sys.stdout
    884         errio.seek(0)
    885         e = errio.read()
    886         print invalidInput(operation, u, fields, err, e)
    887     except:
     1125    except Exception, err:
     1126        if not fields.has_key('js'):
     1127            if isinstance(err, CodeError):
     1128                print 'Content-Type: text/html\n'
     1129                e = revertStandardError()
     1130                print error(operation, u, fields, err, e)
     1131                sys.exit(1)
     1132            if isinstance(err, InvalidInput):
     1133                print 'Content-Type: text/html\n'
     1134                e = revertStandardError()
     1135                print invalidInput(operation, u, fields, err, e)
     1136                sys.exit(1)
    8881137        print 'Content-Type: text/plain\n'
    889         sys.stderr=sys.stdout
    890         errio.seek(0)
    891         e = errio.read()
     1138        print 'Uh-oh!  We experienced an error.'
     1139        print 'Please email sipb-xen@mit.edu with the contents of this page.'
     1140        print '----'
     1141        e = revertStandardError()
    8921142        print e
    8931143        print '----'
  • trunk/web/templates/skeleton.py

    r187 r205  
    2121import Cheetah.Filters as Filters
    2222import Cheetah.ErrorCatchers as ErrorCatchers
     23from functions import functions
    2324
    2425##################################################
     
    3435__CHEETAH_version__ = '2.0rc8'
    3536__CHEETAH_versionTuple__ = (2, 0, 0, 'candidate', 8)
    36 __CHEETAH_genTime__ = 1192082083.444865
    37 __CHEETAH_genTimestamp__ = 'Thu Oct 11 01:54:43 2007'
     37__CHEETAH_genTime__ = 1192883107.2947011
     38__CHEETAH_genTimestamp__ = 'Sat Oct 20 08:25:07 2007'
    3839__CHEETAH_src__ = 'skeleton.tmpl'
    39 __CHEETAH_srcLastModified__ = 'Thu Oct 11 01:54:41 2007'
     40__CHEETAH_srcLastModified__ = 'Sat Oct 20 08:22:11 2007'
    4041__CHEETAH_docstring__ = 'Autogenerated by CHEETAH: The Python-Powered Template Engine'
    4142
     
    4950## CLASSES
    5051
    51 class skeleton(Template):
     52class skeleton(functions):
    5253
    5354    ##################################################
     
    5758    def __init__(self, *args, **KWs):
    5859
    59         Template.__init__(self, *args, **KWs)
     60        functions.__init__(self, *args, **KWs)
    6061        if not self._CHEETAH__instanceInitialized:
    6162            cheetahKWArgs = {}
     
    6667       
    6768
    68     def respond(self, trans=None):
    69 
    70 
    71 
    72         ## CHEETAH: main method generated for this template
     69    def full_body(self, **KWS):
     70
     71
     72
     73        ## CHEETAH: generated from #def full_body at line 4, col 1.
     74        trans = KWS.get("trans")
    7375        if (not trans and not self._CHEETAH__isBuffering and not callable(self.transaction)):
    7476            trans = self.transaction # is None unless self.awake() was called
     
    8486        ## START - generated method body
    8587       
    86         write('''<html>
     88        write('''<!DOCTYPE html
     89PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
     90"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
     91<html>
    8792<head><title>''')
    88         _v = VFFSL(SL,"title",True) # '$title' on line 2, col 14
    89         if _v is not None: write(_filter(_v, rawExpr='$title')) # from line 2, col 14.
     93        _v = VFFSL(SL,"title",True) # '$title' on line 9, col 14
     94        if _v is not None: write(_filter(_v, rawExpr='$title')) # from line 9, col 14.
    9095        write('''</title>
    9196  <link href="static/favicon.ico" type="image/x-icon" rel="shortcut icon">
    9297  <link rel="stylesheet" href="static/style.css" type="text/css" />
    9398  <link rel="stylesheet" href="static/layout.css" type="text/css" media="screen" />
     99  <script type="text/javascript" src="static/prototype.js"></script>
    94100  <script type="text/javascript">
    95101var helpWin = null;
     
    108114   return false;
    109115}
     116
     117Ajax.Responders.register({
     118  onCreate: function(){
     119    if (Ajax.activeRequestCount > 0) {
     120       document.getElementById("loadingnotice").style.display = \'block\';
     121    }
     122  },
     123  onComplete: function(){
     124    if (Ajax.activeRequestCount == 0) {
     125      document.getElementById("loadingnotice").style.display = \'none\';
     126    }
     127  }
     128});
     129function replaceFunc(transport) {
     130  try {
     131    d = transport.responseText.evalJSON();
     132  } catch (e) {
     133    $(\'body\').innerHTML = \'<pre>\'+transport.responseText+\'</pre>\'
     134    return;
     135  }
     136  for(key in d) {
     137    $(key).innerHTML = d[key];
     138  }
     139}
     140
     141function jsFormSubmit(location, elt){
     142  new Ajax.Request(location, {method: \'post\',
     143  parameters: Form.serialize(elt)+\'&js=true\',
     144  onSuccess: replaceFunc,
     145  onComplete: function() {Form.enable(elt);}
     146  });
     147  Form.disable(elt);
     148  return false;
     149}
     150
     151function rowFormSubmit(elt, retpage){
     152  new Ajax.Request(\'command\', {method: \'post\',
     153  parameters: Form.serialize(elt)+\'&js=\'+retpage,
     154  onSuccess: replaceFunc
     155  });
     156  return false;
     157}
     158
     159window.onload = {
     160  //Fix bug with disabled elements
     161  $''')
     162        _v = 'form' # "$('form')" on line 75, col 4
     163        if _v is not None: write(_filter(_v, rawExpr="$('form')")) # from line 75, col 4.
     164        write('''.each(Form.enable);
     165}
     166
    110167</script>
    111168</head>
    112 <body>
    113 ''')
    114         if not VFFSL(SL,"varExists",False)('simple') or not VFFSL(SL,"simple",True): # generated from line 25, col 1
     169<body id="body">
     170
     171<div id="err">
     172''')
     173        if VFFSL(SL,"varExists",False)('error_text'): # generated from line 83, col 1
     174            write('''<p>STDERR:</p><pre>''')
     175            _v = VFFSL(SL,"error_text",True) # '$error_text' on line 84, col 20
     176            if _v is not None: write(_filter(_v, rawExpr='$error_text')) # from line 84, col 20.
     177            write('''</pre>
     178''')
     179        write('''</div>
     180
     181''')
     182        if not VFFSL(SL,"varExists",False)('simple') or not VFFSL(SL,"simple",True): # generated from line 88, col 1
    115183            write('''<p>[You are logged in as ''')
    116             _v = VFFSL(SL,"user.username",True) # '$user.username' on line 26, col 26
    117             if _v is not None: write(_filter(_v, rawExpr='$user.username')) # from line 26, col 26.
     184            _v = VFFSL(SL,"user.username",True) # '$user.username' on line 89, col 26
     185            if _v is not None: write(_filter(_v, rawExpr='$user.username')) # from line 89, col 26.
    118186            write('''.]</p>
     187
     188<div class="navigation">
    119189<p><a href="list">List</a>
    120190''')
    121             if VFFSL(SL,"varExists",False)('machine'): # generated from line 28, col 1
     191            if VFFSL(SL,"varExists",False)('machine'): # generated from line 93, col 1
    122192                write('''<a href="info?machine_id=''')
    123                 _v = VFFSL(SL,"machine.machine_id",True) # '$machine.machine_id' on line 29, col 26
    124                 if _v is not None: write(_filter(_v, rawExpr='$machine.machine_id')) # from line 29, col 26.
     193                _v = VFFSL(SL,"machine.machine_id",True) # '$machine.machine_id' on line 94, col 26
     194                if _v is not None: write(_filter(_v, rawExpr='$machine.machine_id')) # from line 94, col 26.
    125195                write('''">Info</a>
    126196<a href="vnc?machine_id=''')
    127                 _v = VFFSL(SL,"machine.machine_id",True) # '$machine.machine_id' on line 30, col 25
    128                 if _v is not None: write(_filter(_v, rawExpr='$machine.machine_id')) # from line 30, col 25.
     197                _v = VFFSL(SL,"machine.machine_id",True) # '$machine.machine_id' on line 95, col 25
     198                if _v is not None: write(_filter(_v, rawExpr='$machine.machine_id')) # from line 95, col 25.
    129199                write('''">Console</a>
    130200''')
    131201            write('''<a href="help">Help</a></p>
    132202''')
    133         _v = VFFSL(SL,"body",True) # '$body' on line 34, col 1
    134         if _v is not None: write(_filter(_v, rawExpr='$body')) # from line 34, col 1.
     203        write('''</div>
     204
     205<div id="loadingnotice" class="loadingnotice">LOADING</div>
     206<div id="result" class="result"></div>
     207
     208''')
     209        _v = VFFSL(SL,"body",True) # '$body' on line 104, col 1
     210        if _v is not None: write(_filter(_v, rawExpr='$body')) # from line 104, col 1.
    135211        write('''
    136212''')
    137         if not VFFSL(SL,"varExists",False)('simple') or not VFFSL(SL,"simple",True): # generated from line 35, col 1
     213        if not VFFSL(SL,"varExists",False)('simple') or not VFFSL(SL,"simple",True): # generated from line 105, col 1
    138214            write('''<hr />
    139215Questions? Contact <a href="mailto:sipb-xen-dev@mit.edu">sipb-xen-dev@mit.edu</a>.
     
    148224        return _dummyTrans and trans.response().getvalue() or ""
    149225       
     226
     227    def writeBody(self, **KWS):
     228
     229
     230
     231        ## CHEETAH: main method generated for this template
     232        trans = KWS.get("trans")
     233        if (not trans and not self._CHEETAH__isBuffering and not callable(self.transaction)):
     234            trans = self.transaction # is None unless self.awake() was called
     235        if not trans:
     236            trans = DummyTransaction()
     237            _dummyTrans = True
     238        else: _dummyTrans = False
     239        write = trans.response().write
     240        SL = self._CHEETAH__searchList
     241        _filter = self._CHEETAH__currentFilter
     242       
     243        ########################################
     244        ## START - generated method body
     245       
     246        write('''
     247''')
     248       
     249        ########################################
     250        ## END - generated method body
     251       
     252        return _dummyTrans and trans.response().getvalue() or ""
     253       
    150254    ##################################################
    151255    ## CHEETAH GENERATED ATTRIBUTES
     
    166270    _CHEETAH_srcLastModified = __CHEETAH_srcLastModified__
    167271
    168     _mainCheetahMethod_for_skeleton= 'respond'
     272    _mainCheetahMethod_for_skeleton= 'writeBody'
    169273
    170274## END CLASS DEFINITION
  • trunk/web/templates/skeleton.tmpl

    r187 r205  
     1#from functions import functions
     2#extends functions
     3
     4#def full_body
     5<!DOCTYPE html
     6PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
     7"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    18<html>
    29<head><title>$title</title>
     
    411  <link rel="stylesheet" href="static/style.css" type="text/css" />
    512  <link rel="stylesheet" href="static/layout.css" type="text/css" media="screen" />
     13  <script type="text/javascript" src="static/prototype.js"></script>
    614  <script type="text/javascript">
    715var helpWin = null;
     
    2028   return false;
    2129}
     30
     31Ajax.Responders.register({
     32  onCreate: function(){
     33    if (Ajax.activeRequestCount > 0) {
     34       document.getElementById("loadingnotice").style.display = 'block';
     35    }
     36  },
     37  onComplete: function(){
     38    if (Ajax.activeRequestCount == 0) {
     39      document.getElementById("loadingnotice").style.display = 'none';
     40    }
     41  }
     42});
     43function replaceFunc(transport) {
     44  try {
     45    d = transport.responseText.evalJSON();
     46  } catch (e) {
     47    \$('body').innerHTML = '<pre>'+transport.responseText+'</pre>'
     48    return;
     49  }
     50  for(key in d) {
     51    \$(key).innerHTML = d[key];
     52  }
     53}
     54
     55function jsFormSubmit(location, elt){
     56  new Ajax.Request(location, {method: 'post',
     57  parameters: Form.serialize(elt)+'&js=true',
     58  onSuccess: replaceFunc,
     59  onComplete: function() {Form.enable(elt);}
     60  });
     61  Form.disable(elt);
     62  return false;
     63}
     64
     65function rowFormSubmit(elt, retpage){
     66  new Ajax.Request('command', {method: 'post',
     67  parameters: Form.serialize(elt)+'&js='+retpage,
     68  onSuccess: replaceFunc
     69  });
     70  return false;
     71}
     72
     73window.onload = {
     74  //Fix bug with disabled elements
     75  $$('form').each(Form.enable);
     76}
     77
    2278</script>
    2379</head>
    24 <body>
     80<body id="body">
     81
     82<div id="err">
     83#if $varExists('error_text')
     84<p>STDERR:</p><pre>$error_text</pre>
     85#end if
     86</div>
     87
    2588#if not $varExists('simple') or not $simple
    2689<p>[You are logged in as $user.username.]</p>
     90
     91<div class="navigation">
    2792<p><a href="list">List</a>
    2893#if $varExists('machine')
     
    3297<a href="help">Help</a></p>
    3398#end if
     99</div>
     100
     101<div id="loadingnotice" class="loadingnotice">LOADING</div>
     102<div id="result" class="result"></div>
     103
    34104$body
    35105#if not $varExists('simple') or not $simple
     
    39109</body>
    40110</html>
     111#end def
  • trunk/web/templates/static/style.css

    r182 r205  
    6767    font-family: "Bitstream Vera Sans Mono", "Luxi Mono", "Courier New", monospace;
    6868}
     69
     70#loadingnotice {
     71    padding: 2px;
     72    display: none;
     73    color: #FFFFFF;
     74    background: #CC2200;
     75    position: fixed;
     76    right: 10px;
     77    top: 5px;
     78}
     79
     80.error {
     81  color: #FF0000;
     82  padding: 0.25em;
     83}
     84
     85td.error {
     86  border: 1px solid red;
     87}
Note: See TracChangeset for help on using the changeset viewer.