X-Git-Url: http://xvm.mit.edu/gitweb/invirt/packages/invirt-web.git/blobdiff_plain/d5c709a5a9d53bbff6fae1e3eab6e0a4acf294bf..5b6a2d94cebef6d12c2eaf32c010e3a0a8fbf329:/code/main.py diff --git a/code/main.py b/code/main.py index 656c209..473647f 100755 --- a/code/main.py +++ b/code/main.py @@ -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': """ Windows Vista: The Vista image is licensed for all MIT students and will automatically activate off the network; see the licensing confirmation e-mail for details. The installer requires 512 MiB RAM and at least 7.5 GiB disk space (15 GiB or more recommended).
-Windows XP: 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. +Windows XP: 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 http://msca.mit.edu/ if you are staff/faculty to request one. """ } @@ -144,12 +146,47 @@ 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() if fields.get(kw)]) + 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 + @cherrypy.expose + def errortest(self): + """Throw an error, to test the error-tracing mechanisms.""" + raise RuntimeError("test of the emergency broadcast system") + 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 @@ -173,6 +210,74 @@ console will suffer artifacts. 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): @@ -266,33 +371,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.""" @@ -301,9 +379,9 @@ def getListDict(username, state): checkpoint.checkpoint('Got my machines') on = {} has_vnc = {} + installing = {} xmlist = state.xmlist checkpoint.checkpoint('Got uptimes') - can_clone = 'ice3' not in state.xmlist_raw for m in machines: if m not in xmlist: has_vnc[m] = 'Off' @@ -315,7 +393,11 @@ def getListDict(username, state): elif m.type.hvm: has_vnc[m] = "WTF?" else: - has_vnc[m] = "ParaVM"+helppopup("ParaVM Console") + has_vnc[m] = "ParaVM" + if xmlist[m].get('autoinstall'): + installing[m] = True + else: + installing[m] = False max_memory = validation.maxMemory(username, state) max_disk = validation.maxDisk(username) checkpoint.checkpoint('Got max mem/disk') @@ -333,50 +415,9 @@ def getListDict(username, state): defaults=defaults, machines=machines, has_vnc=has_vnc, - can_clone=can_clone) + installing=installing) 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. @@ -430,35 +471,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. @@ -572,7 +584,6 @@ def infoDict(username, state, machine): ('memory', 'RAM'), 'DISK_INFO', ('state', 'state (xen format)'), - ('cpu_weight', 'CPU weight'+helppopup('CPU Weight')), ] fields = [] machine_info = {} @@ -626,7 +637,6 @@ def infoDict(username, state, machine): ram=machine.memory, max_mem=max_mem, max_disk=max_disk, - owner_help=helppopup("Owner"), fields = fields) return d @@ -647,18 +657,11 @@ def admin(username, state, path, fields): newstate.environ = state.environ return handler(username, newstate, path, fields) -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, - create=create, unauth=unauthFront, admin=admin, - overlord=admin, - errortest=throwError) + overlord=admin) def printHeaders(headers): """Print a dictionary as HTTP headers."""