Clarify that staff can receive Windows license keys from MIT
[invirt/packages/invirt-web.git] / code / main.py
index 47f9b01..7ab52b5 100755 (executable)
@@ -58,10 +58,12 @@ class InvirtWeb(View):
 
     @cherrypy.expose
     @cherrypy.tools.mako(filename="/list.mako")
-    def list(self):
+    def list(self, result=None):
         """Handler for list requests."""
         checkpoint.checkpoint('Getting list dict')
         d = getListDict(cherrypy.request.login, cherrypy.request.state)
+        if result is not None:
+            d['result'] = result
         checkpoint.checkpoint('Got list dict')
         return d
     index=list
@@ -130,7 +132,7 @@ console will suffer artifacts.
 """,
             'Windows': """
 <strong>Windows Vista:</strong> The Vista image is licensed for all MIT students and will automatically activate off the network; see <a href="/static/msca-email.txt">the licensing confirmation e-mail</a> for details. The installer requires 512 MiB RAM and at least 7.5 GiB disk space (15 GiB or more recommended).<br>
-<strong>Windows XP:</strong> This is the volume license CD image. You will need your own volume license key to complete the install. We do not have these available for the general MIT community; ask your department if they have one.
+<strong>Windows XP:</strong> This is the volume license CD image. You will need your own volume license key to complete the install. We do not have these available for the general MIT community; ask your department if they have one, or visit <a href="http://msca.mit.edu/">http://msca.mit.edu/</a> if you are staff/faculty to request one.
 """
             }
 
@@ -144,12 +146,135 @@ console will suffer artifacts.
                     mapping=help_mapping)
     help._cp_config['tools.require_login.on'] = False
 
+    def parseCreate(self, fields):
+        kws = dict([(kw, fields.get(kw)) for kw in 'name description owner memory disksize vmtype cdrom autoinstall'.split()])
+        validate = validation.Validate(cherrypy.request.login, cherrypy.request.state, strict=True, **kws)
+        return dict(contact=cherrypy.request.login, name=validate.name, description=validate.description, memory=validate.memory,
+                    disksize=validate.disksize, owner=validate.owner, machine_type=getattr(validate, 'vmtype', Defaults.type),
+                    cdrom=getattr(validate, 'cdrom', None),
+                    autoinstall=getattr(validate, 'autoinstall', None))
+
+    @cherrypy.expose
+    @cherrypy.tools.mako(filename="/list.mako")
+    @cherrypy.tools.require_POST()
+    def create(self, **fields):
+        """Handler for create requests."""
+        try:
+            parsed_fields = self.parseCreate(fields)
+            machine = controls.createVm(cherrypy.request.login, cherrypy.request.state, **parsed_fields)
+        except InvalidInput, err:
+            pass
+        else:
+            err = None
+        cherrypy.request.state.clear() #Changed global state
+        d = getListDict(cherrypy.request.login, cherrypy.request.state)
+        d['err'] = err
+        if err:
+            for field in fields.keys():
+                setattr(d['defaults'], field, fields.get(field))
+        else:
+            d['new_machine'] = parsed_fields['name']
+        return d
+
     @cherrypy.expose
     @cherrypy.tools.mako(filename="/helloworld.mako")
     def helloworld(self, **kwargs):
         return {'request': cherrypy.request, 'kwargs': kwargs}
     helloworld._cp_config['tools.require_login.on'] = False
 
+    class MachineView(View):
+        # This is hairy. Fix when CherryPy 3.2 is out. (rename to
+        # _cp_dispatch, and parse the argument as a list instead of
+        # string
+
+        def __getattr__(self, name):
+            try:
+                machine_id = int(name)
+                cherrypy.request.params['machine_id'] = machine_id
+                return self
+            except ValueError:
+                return None
+
+        @cherrypy.expose
+        @cherrypy.tools.mako(filename="/info.mako")
+        def info(self, machine_id):
+            """Handler for info on a single VM."""
+            machine = validation.Validate(cherrypy.request.login, cherrypy.request.state, machine_id=machine_id).machine
+            d = infoDict(cherrypy.request.login, cherrypy.request.state, machine)
+            checkpoint.checkpoint('Got infodict')
+            return d
+        index = info
+
+        @cherrypy.expose
+        @cherrypy.tools.mako(filename="/vnc.mako")
+        def vnc(self, machine_id):
+            """VNC applet page.
+
+            Note that due to same-domain restrictions, the applet connects to
+            the webserver, which needs to forward those requests to the xen
+            server.  The Xen server runs another proxy that (1) authenticates
+            and (2) finds the correct port for the VM.
+
+            You might want iptables like:
+
+            -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
+            -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
+            -A FORWARD -d 18.181.0.60 -i eth1 -o eth1 -p tcp -m tcp \
+            --dport 10003 -j ACCEPT
+
+            Remember to enable iptables!
+            echo 1 > /proc/sys/net/ipv4/ip_forward
+            """
+            machine = validation.Validate(cherrypy.request.login, cherrypy.request.state, machine_id=machine_id).machine
+
+            token = controls.vnctoken(machine)
+            host = controls.listHost(machine)
+            if host:
+                port = 10003 + [h.hostname for h in config.hosts].index(host)
+            else:
+                port = 5900 # dummy
+
+            status = controls.statusInfo(machine)
+            has_vnc = hasVnc(status)
+
+            d = dict(on=status,
+                     has_vnc=has_vnc,
+                     machine=machine,
+                     hostname=cherrypy.request.local.name,
+                     port=port,
+                     authtoken=token)
+            return d
+        @cherrypy.expose
+        @cherrypy.tools.mako(filename="/command.mako")
+        @cherrypy.tools.require_POST()
+        def command(self, command_name, machine_id, **kwargs):
+            """Handler for running commands like boot and delete on a VM."""
+            back = kwargs.get('back', None)
+            try:
+                d = controls.commandResult(cherrypy.request.login, cherrypy.request.state, command_name, machine_id, kwargs)
+                if d['command'] == 'Delete VM':
+                    back = 'list'
+            except InvalidInput, err:
+                if not back:
+                    raise
+                print >> sys.stderr, err
+                result = err
+            else:
+                result = 'Success!'
+                if not back:
+                    return d
+            if back == 'list':
+                cherrypy.request.state.clear() #Changed global state
+                raise cherrypy.InternalRedirect('/list?result=%s' % urllib.quote(result))
+            elif back == 'info':
+                raise cherrypy.HTTPRedirect(cherrypy.request.base + '/machine/%d/' % machine_id, status=303)
+            else:
+                raise InvalidInput('back', back, 'Not a known back page.')
+
+    machine = MachineView()
+
 def pathSplit(path):
     if path.startswith('/'):
         path = path[1:]
@@ -241,33 +366,6 @@ def hasVnc(status):
             return 'location' in d
     return False
 
-def parseCreate(username, state, fields):
-    kws = dict([(kw, fields.getfirst(kw)) for kw in 'name description owner memory disksize vmtype cdrom autoinstall'.split()])
-    validate = validation.Validate(username, state, strict=True, **kws)
-    return dict(contact=username, name=validate.name, description=validate.description, memory=validate.memory,
-                disksize=validate.disksize, owner=validate.owner, machine_type=getattr(validate, 'vmtype', Defaults.type),
-                cdrom=getattr(validate, 'cdrom', None),
-                autoinstall=getattr(validate, 'autoinstall', None))
-
-def create(username, state, path, fields):
-    """Handler for create requests."""
-    try:
-        parsed_fields = parseCreate(username, state, fields)
-        machine = controls.createVm(username, state, **parsed_fields)
-    except InvalidInput, err:
-        pass
-    else:
-        err = None
-    state.clear() #Changed global state
-    d = getListDict(username, state)
-    d['err'] = err
-    if err:
-        for field in fields.keys():
-            setattr(d['defaults'], field, fields.getfirst(field))
-    else:
-        d['new_machine'] = parsed_fields['name']
-    return templates.list(searchList=[d])
-
 
 def getListDict(username, state):
     """Gets the list of local variables used by list.tmpl."""
@@ -290,7 +388,7 @@ def getListDict(username, state):
             elif m.type.hvm:
                 has_vnc[m] = "WTF?"
             else:
-                has_vnc[m] = "ParaVM"+helppopup("ParaVM Console")
+                has_vnc[m] = "ParaVM"
     max_memory = validation.maxMemory(username, state)
     max_disk = validation.maxDisk(username)
     checkpoint.checkpoint('Got max mem/disk')
@@ -311,47 +409,6 @@ def getListDict(username, state):
              can_clone=can_clone)
     return d
 
-def vnc(username, state, path, fields):
-    """VNC applet page.
-
-    Note that due to same-domain restrictions, the applet connects to
-    the webserver, which needs to forward those requests to the xen
-    server.  The Xen server runs another proxy that (1) authenticates
-    and (2) finds the correct port for the VM.
-
-    You might want iptables like:
-
-    -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
-    -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
-    -A FORWARD -d 18.181.0.60 -i eth1 -o eth1 -p tcp -m tcp \
-      --dport 10003 -j ACCEPT
-
-    Remember to enable iptables!
-    echo 1 > /proc/sys/net/ipv4/ip_forward
-    """
-    machine = validation.Validate(username, state, machine_id=fields.getfirst('machine_id')).machine
-
-    token = controls.vnctoken(machine)
-    host = controls.listHost(machine)
-    if host:
-        port = 10003 + [h.hostname for h in config.hosts].index(host)
-    else:
-        port = 5900 # dummy
-
-    status = controls.statusInfo(machine)
-    has_vnc = hasVnc(status)
-
-    d = dict(user=username,
-             on=status,
-             has_vnc=has_vnc,
-             machine=machine,
-             hostname=state.environ.get('SERVER_NAME', 'localhost'),
-             port=port,
-             authtoken=token)
-    return templates.vnc(searchList=[d])
-
 def getHostname(nic):
     """Find the hostname associated with a NIC.
 
@@ -405,35 +462,6 @@ def getDiskInfo(data_dict, machine):
         data_dict['%s_size' % name] = "%0.1f GiB" % (disk.size / 1024.)
     return disk_fields
 
-def command(username, state, path, fields):
-    """Handler for running commands like boot and delete on a VM."""
-    back = fields.getfirst('back')
-    try:
-        d = controls.commandResult(username, state, fields)
-        if d['command'] == 'Delete VM':
-            back = 'list'
-    except InvalidInput, err:
-        if not back:
-            raise
-        print >> sys.stderr, err
-        result = err
-    else:
-        result = 'Success!'
-        if not back:
-            return templates.command(searchList=[d])
-    if back == 'list':
-        state.clear() #Changed global state
-        d = getListDict(username, state)
-        d['result'] = result
-        return templates.list(searchList=[d])
-    elif back == 'info':
-        machine = validation.Validate(username, state, machine_id=fields.getfirst('machine_id')).machine
-        return ({'Status': '303 See Other',
-                 'Location': 'info?machine_id=%d' % machine.machine_id},
-                "You shouldn't see this message.")
-    else:
-        raise InvalidInput('back', back, 'Not a known back page.')
-
 def modifyDict(username, state, fields):
     """Modify a machine as specified by CGI arguments.
 
@@ -547,7 +575,6 @@ def infoDict(username, state, machine):
                       ('memory', 'RAM'),
                       'DISK_INFO',
                       ('state', 'state (xen format)'),
-                      ('cpu_weight', 'CPU weight'+helppopup('CPU Weight')),
                       ]
     fields = []
     machine_info = {}
@@ -601,17 +628,9 @@ def infoDict(username, state, machine):
              ram=machine.memory,
              max_mem=max_mem,
              max_disk=max_disk,
-             owner_help=helppopup("Owner"),
              fields = fields)
     return d
 
-def info(username, state, path, fields):
-    """Handler for info on a single VM."""
-    machine = validation.Validate(username, state, machine_id=fields.getfirst('machine_id')).machine
-    d = infoDict(username, state, machine)
-    checkpoint.checkpoint('Got infodict')
-    return templates.info(searchList=[d])
-
 def unauthFront(_, _2, _3, fields):
     """Information for unauth'd users."""
     return templates.unauth(searchList=[{'simple' : True, 
@@ -633,11 +652,8 @@ def throwError(_, __, ___, ____):
     """Throw an error, to test the error-tracing mechanisms."""
     raise RuntimeError("test of the emergency broadcast system")
 
-mapping = dict(vnc=vnc,
-               command=command,
+mapping = dict(
                modify=modify,
-               info=info,
-               create=create,
                unauth=unauthFront,
                admin=admin,
                overlord=admin,