X-Git-Url: http://xvm.mit.edu/gitweb/invirt/packages/invirt-web.git/blobdiff_plain/36c143099b46d2bf77569d4f1d660505ed9d37d6..2be0969e153ace507ec07b27d156b50e54e00f94:/code/main.py diff --git a/code/main.py b/code/main.py index c4eff65..86da50e 100755 --- a/code/main.py +++ b/code/main.py @@ -6,6 +6,7 @@ import cPickle import cgi import datetime import hmac +import os import random import sha import sys @@ -37,11 +38,20 @@ from invirt.common import InvalidInput, CodeError from view import View, revertStandardError + +static_dir = os.path.join(os.path.dirname(__file__), 'static') +InvirtStatic = cherrypy.tools.staticdir.handler( + root=static_dir, + dir=static_dir, + section='/static') + class InvirtUnauthWeb(View): + static = InvirtStatic + @cherrypy.expose @cherrypy.tools.mako(filename="/unauth.mako") def index(self): - return {'simple': True} + return dict(simple=True) class InvirtWeb(View): def __init__(self): @@ -53,6 +63,8 @@ class InvirtWeb(View): 'from invirt import database'] self._cp_config['request.error_response'] = self.handle_error + static = InvirtStatic + @cherrypy.expose @cherrypy.tools.mako(filename="/invalid.mako") def invalidInput(self): @@ -85,8 +97,11 @@ class InvirtWeb(View): return d def __getattr__(self, name): + # At the point __getattr__ is called, tools haven't been run. Make sure the user is logged in. + cherrypy.tools.remote_user_login.callable() + if name in ("admin", "overlord"): - if not cherrypy.request.login in getAfsGroupMembers(config.adminacl, config.authz[0].cell): + if not cherrypy.request.login in getAfsGroupMembers(config.adminacl, config.authz.afs.cells[0].cell): raise InvalidInput('username', cherrypy.request.login, 'Not in admin group %s.' % config.adminacl) cherrypy.request.state = State(cherrypy.request.login, isadmin=True) @@ -112,11 +127,9 @@ class InvirtWeb(View): @cherrypy.tools.mako(filename="/list.mako") 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 @@ -158,8 +171,6 @@ wiki, including steps to prepare an HVM guest to boot as a ParaVM
We recommend using a ParaVM when possible and an HVM when necessary.
""",
- 'CPU Weight': """
-Don't ask us! We're as mystified as you are.""",
'Owner': """
The owner field is used to determine quotas. It must be the name of a
@@ -183,6 +194,7 @@ your machine will run just fine, but the applet's display of the
console will suffer artifacts.
""",
'Windows': """
+Windows 7: The Windows 7 image is licensed for all MIT students and will automatically activate off the network; see the licensing agreement for details. The installer requires 512 MiB RAM and at least 15 GiB disk space (20 GiB or more recommended).
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, or visit http://msca.mit.edu/ if you are staff/faculty to request one.
"""
@@ -230,7 +242,7 @@ console will suffer artifacts.
d['err'] = err
if err:
for field, value in fields.items():
- setattr(d['defaults'], field, value))
+ setattr(d['defaults'], field, value)
else:
d['new_machine'] = parsed_fields['name']
return d
@@ -248,14 +260,19 @@ console will suffer artifacts.
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
- # string
-
def __getattr__(self, name):
+ """Synthesize attributes to allow RESTful URLs like
+ /machine/13/info. This is hairy. CherryPy 3.2 adds a
+ method called _cp_dispatch that allows you to explicitly
+ handle URLs that can't be mapped, and it allows you to
+ rewrite the path components and continue processing.
+
+ This function gets the next path component being resolved
+ as a string. _cp_dispatch will get an array of strings
+ representing any subsequent path components as well."""
+
try:
- machine_id = int(name)
- cherrypy.request.params['machine_id'] = machine_id
+ cherrypy.request.params['machine_id'] = int(name)
return self
except ValueError:
return None
@@ -268,7 +285,6 @@ console will suffer artifacts.
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
@@ -341,18 +357,19 @@ console will suffer artifacts.
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)
+ back = kwargs.get('back')
+ if command_name == 'delete':
+ back = 'list'
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
@@ -375,20 +392,6 @@ console will suffer artifacts.
machine = MachineView()
-class Checkpoint:
- def __init__(self):
- self.start_time = time.time()
- self.checkpoints = []
-
- def checkpoint(self, s):
- self.checkpoints.append((s, time.time()))
-
- def __str__(self):
- return ('Timing info:\n%s\n' %
- '\n'.join(['%s: %s' % (d, t - self.start_time) for
- (d, t) in self.checkpoints]))
-
-checkpoint = Checkpoint()
class Defaults:
"""Class to store default values for fields."""
@@ -422,37 +425,29 @@ def hasVnc(status):
def getListDict(username, state):
"""Gets the list of local variables used by list.tmpl."""
- checkpoint.checkpoint('Starting')
machines = state.machines
- checkpoint.checkpoint('Got my machines')
on = {}
has_vnc = {}
installing = {}
xmlist = state.xmlist
- checkpoint.checkpoint('Got uptimes')
for m in machines:
if m not in xmlist:
has_vnc[m] = 'Off'
m.uptime = None
else:
m.uptime = xmlist[m]['uptime']
+ installing[m] = bool(xmlist[m].get('autoinstall'))
if xmlist[m]['console']:
has_vnc[m] = True
elif m.type.hvm:
has_vnc[m] = "WTF?"
else:
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')
defaults = Defaults(max_memory=max_memory,
max_disk=max_disk,
owner=username)
- checkpoint.checkpoint('Got defaults')
def sortkey(machine):
return (machine.owner != username, machine.owner, machine.name)
machines = sorted(machines, key=sortkey)
@@ -463,7 +458,8 @@ def getListDict(username, state):
defaults=defaults,
machines=machines,
has_vnc=has_vnc,
- installing=installing)
+ installing=installing,
+ disable_creation=False)
return d
def getHostname(nic):
@@ -527,9 +523,9 @@ def modifyDict(username, state, machine_id, fields):
olddisk = {}
session.begin()
try:
- kws = dict([(kw, fields[kw]) for kw in
+ kws = dict((kw, fields[kw]) for kw in
'owner admin contact name description memory vmtype disksize'.split()
- if fields[kw]])
+ if fields.get(kw))
kws['machine_id'] = machine_id
validate = validation.Validate(username, state, **kws)
machine = validate.machine
@@ -541,23 +537,10 @@ def modifyDict(username, state, machine_id, fields):
if hasattr(validate, 'vmtype'):
machine.type = validate.vmtype
- if hasattr(validate, 'disksize'):
- disksize = validate.disksize
- disk = machine.disks[0]
- if disk.size != disksize:
- olddisk[disk.guest_device_name] = disksize
- disk.size = disksize
- session.save_or_update(disk)
-
update_acl = False
if hasattr(validate, 'owner') and validate.owner != machine.owner:
machine.owner = validate.owner
update_acl = True
- if hasattr(validate, 'name'):
- machine.name = validate.name
- for n in machine.nics:
- if n.hostname == oldname:
- n.hostname = validate.name
if hasattr(validate, 'description'):
machine.description = validate.description
if hasattr(validate, 'admin') and validate.admin != machine.administrator:
@@ -566,23 +549,56 @@ def modifyDict(username, state, machine_id, fields):
if hasattr(validate, 'contact'):
machine.contact = validate.contact
- session.save_or_update(machine)
- if update_acl:
- cache_acls.refreshMachine(machine)
+ session.add(machine)
+ session.commit()
+ except:
+ session.rollback()
+ raise
+
+ session.begin()
+ try:
+ if hasattr(validate, 'disksize'):
+ disksize = validate.disksize
+ disk = machine.disks[0]
+ if disk.size != disksize:
+ olddisk[disk.guest_device_name] = disksize
+ disk.size = disksize
+ session.add(disk)
+ for diskname in olddisk:
+ controls.resizeDisk(oldname, diskname, str(olddisk[diskname]))
+ session.add(machine)
+ session.commit()
+ except:
+ session.rollback()
+ raise
+
+ session.begin()
+ try:
+ if hasattr(validate, 'name'):
+ machine.name = validate.name
+ for n in machine.nics:
+ if n.hostname == oldname:
+ n.hostname = validate.name
+ if hasattr(validate, 'name'):
+ controls.renameMachine(machine, oldname, validate.name)
+ session.add(machine)
session.commit()
except:
session.rollback()
raise
- for diskname in olddisk:
- controls.resizeDisk(oldname, diskname, str(olddisk[diskname]))
- if hasattr(validate, 'name'):
- controls.renameMachine(machine, oldname, validate.name)
+
+ if update_acl:
+ cache_acls.refreshMachine(machine)
+
return dict(machine=machine)
def infoDict(username, state, machine):
"""Get the variables used by info.tmpl."""
- status = controls.statusInfo(machine)
- checkpoint.checkpoint('Getting status info')
+ try:
+ status = controls.statusInfo(machine)
+ except CodeError, e:
+ # machine was shut down in between the call to listInfoDict and this
+ status = None
has_vnc = hasVnc(status)
if status is None:
main_status = dict(name=machine.name,
@@ -592,11 +608,14 @@ def infoDict(username, state, machine):
else:
main_status = dict(status[1:])
main_status['host'] = controls.listHost(machine)
- start_time = float(main_status.get('start_time', 0))
- uptime = datetime.timedelta(seconds=int(time.time()-start_time))
+ start_time = main_status.get('start_time')
+ if start_time is None:
+ uptime = "Still booting?"
+ else:
+ start_time = float(start_time)
+ uptime = datetime.timedelta(seconds=int(time.time()-start_time))
cpu_time_float = float(main_status.get('cpu_time', 0))
cputime = datetime.timedelta(seconds=int(cpu_time_float))
- checkpoint.checkpoint('Status')
display_fields = [('name', 'Name'),
('description', 'Description'),
('owner', 'Owner'),
@@ -642,11 +661,7 @@ def infoDict(username, state, machine):
pass
#fields.append((disp, None))
- checkpoint.checkpoint('Got fields')
-
-
max_mem = validation.maxMemory(machine.owner, state, machine, False)
- checkpoint.checkpoint('Got mem')
max_disk = validation.maxDisk(machine.owner, machine)
defaults = Defaults()
for name in 'machine_id name description administrator owner memory contact'.split():
@@ -654,7 +669,6 @@ def infoDict(username, state, machine):
setattr(defaults, name, getattr(machine, name))
defaults.type = machine.type.type_id
defaults.disk = "%0.2f" % (machine.disks[0].size/1024.)
- checkpoint.checkpoint('Got defaults')
d = dict(user=username,
on=status is not None,
machine=machine,
@@ -683,4 +697,4 @@ Subject: %s
p.stdin.close()
p.wait()
-random.seed()
+random.seed() #sigh