Another bare except removed.
[invirt/packages/invirt-web.git] / templates / main.py
index 8c00190..f64fe3d 100755 (executable)
@@ -13,6 +13,7 @@ import sha
 import hmac
 import datetime
 import StringIO
+import getafsgroups
 
 sys.stderr = StringIO.StringIO()
 sys.path.append('/home/ecprice/.local/lib/python2.5/site-packages')
@@ -33,7 +34,7 @@ class InvalidInput(MyException):
     the select box).
     """
     def __init__(self, err_field, err_value, expl=None):
-        super(InvalidInput, self).__init__(expl)
+        MyException.__init__(self, expl)
         self.err_field = err_field
         self.err_value = err_value
 
@@ -47,7 +48,7 @@ class Global(object):
 
     def __get_uptimes(self):
         if not hasattr(self, '_uptimes'):
-            self._uptimes = getUptimes(self.machines)
+            self._uptimes = getUptimes(Machine.select())
         return self._uptimes
     uptimes = property(__get_uptimes)
 
@@ -117,7 +118,7 @@ def haveAccess(user, machine):
     """Return whether a user has access to a machine"""
     if user.username == 'moo':
         return True
-    return machine.owner == user.username
+    return getafsgroups.checkLockerOwner(user.username,machine.owner)
 
 def error(op, user, fields, err, emsg):
     """Print an error page when a CodeError occurs"""
@@ -231,7 +232,7 @@ def parseStatus(s):
             stack[-1].extend(v.split())
     return stack[-1]
 
-def getUptimes(machines):
+def getUptimes(machines=None):
     """Return a dictionary mapping machine names to uptime strings"""
     value_string = remctl('web', 'listvms')
     lines = value_string.splitlines()
@@ -352,7 +353,7 @@ def create(user, fields):
     name = fields.getfirst('name')
     if not validMachineName(name):
         raise InvalidInput('name', name)
-    name = user.username + '_' + name.lower()
+    name = name.lower()
 
     if Machine.get_by(name=name):
         raise InvalidInput('name', name,
@@ -406,7 +407,7 @@ def listVms(user, fields):
              default_disk=min(4.0, max_disk),
              machines=machines,
              has_vnc=has_vnc,
-             uptimes=uptimes,
+             uptimes=g.uptimes,
              cdroms=CDROM.select())
     return Template(file='list.tmpl', searchList=[d, global_dict])
 
@@ -510,6 +511,7 @@ def getDiskInfo(data_dict, machine):
 
 def deleteVM(machine):
     """Delete a VM."""
+    remctl('destroy', machine.name, err=True)
     transaction = ctx.current.create_transaction()
     delete_disk_pairs = [(machine.name, d.guest_device_name) for d in machine.disks]
     try:
@@ -530,11 +532,11 @@ def deleteVM(machine):
 
 def command(user, fields):
     """Handler for running commands like boot and delete on a VM."""
-    print time.time()-start_time
+    print >> sys.stderr, time.time()-start_time
     machine = testMachineId(user, fields.getfirst('machine_id'))
     action = fields.getfirst('action')
     cdrom = fields.getfirst('cdrom')
-    print time.time()-start_time
+    print >> sys.stderr, time.time()-start_time
     if cdrom is not None and not CDROM.get(cdrom):
         raise CodeError("Invalid cdrom type '%s'" % cdrom)    
     if action not in ('Reboot', 'Power on', 'Power off', 'Shutdown', 'Delete VM'):
@@ -555,7 +557,7 @@ def command(user, fields):
         remctl('shutdown', machine.name)
     elif action == 'Delete VM':
         deleteVM(machine)
-    print time.time()-start_time
+    print >> sys.stderr, time.time()-start_time
 
     d = dict(user=user,
              command=action,
@@ -563,7 +565,7 @@ def command(user, fields):
     return Template(file="command.tmpl", searchList=[d, global_dict])
 
 def testOwner(user, owner, machine=None):
-    if owner != user.username:
+    if not getafsgroups.checkLockerOwner(user.username, owner):
         raise InvalidInput('owner', owner,
                            "Invalid")
     return owner
@@ -574,10 +576,24 @@ def testContact(user, contact, machine=None):
                            "Invalid")
     return contact
 
+def testDisk(user, disksize, machine=None):
+    return disksize
+
+def testName(user, name, machine=None):
+    if Machine.select_by(name=name) == []:
+        return name
+    if name == machine.name:
+        return name
+    raise InvalidInput('name', name,
+                       "Already taken")
+
 def testHostname(user, hostname, machine):
     for nic in machine.nics:
         if hostname == nic.hostname:
             return hostname
+    # check if doesn't already exist
+    if NIC.select_by(hostname=hostname) == []:
+        return hostname
     raise InvalidInput('hostname', hostname,
                        "Different from before")
 
@@ -585,19 +601,64 @@ def testHostname(user, hostname, machine):
 def modify(user, fields):
     """Handler for modifying attributes of a machine."""
     #XXX not written yet
-    machine = testMachineId(user, fields.getfirst('machine_id'))
-    owner = testOwner(user, fields.getfirst('owner'), machine)
-    contact = testContact(user, fields.getfirst('contact'))
-    hostname = testHostname(user, fields.getfirst('hostname'),
+
+    transaction = ctx.current.create_transaction()
+    try:
+        machine = testMachineId(user, fields.getfirst('machine_id'))
+        owner = testOwner(user, fields.getfirst('owner'), machine)
+        contact = testContact(user, fields.getfirst('contact'))
+        hostname = testHostname(owner, fields.getfirst('hostname'),
                             machine)
-    ram = fields.getfirst('memory')
-    if ram is not None:
-        ram = validMemory(user, ram, machine)
-    disk = testDisk(user, fields.getfirst('disk'))
-    if disk is not None:
-        disk = validDisk(user, disk, machine)
+        name = testName(user, fields.getfirst('name'), machine)
+        oldname = machine.name
+        command="modify"
+        olddisk = {}
+
+        memory = fields.getfirst('memory')
+        if memory is not None:
+            memory = validMemory(user, memory, machine)
+        else:
+            memory = machine.memory
+        if memory != machine.memory:
+            machine.memory = memory
+
+        disksize = testDisk(user, fields.getfirst('disk'))
+        if disksize is not None:
+            disksize = validDisk(user, disksize, machine)
+        else:
+            disksize = machine.disks[0].size
+        for disk in machine.disks:
+            olddisk[disk.guest_device_name] = disk.size
+            disk.size = disksize
+            ctx.current.save(disk)
+        
+        # XXX all NICs get same hostname on change?  Interface doesn't support more.
+        for nic in machine.nics:
+            nic.hostname = hostname
+            ctx.current.save(nic)
+
+        if owner != machine.owner:
+            machine.owner = owner
+        if name != machine.name:
+            machine.name = name
+            
+        ctx.current.save(machine)
+        transaction.commit()
+    except:
+        transaction.rollback()
+        raise
+    remctl("web", "moveregister", oldname, name)
+    for disk in machine.disks:
+        # XXX all disks get the same size on change?  Interface doesn't support more.
+        if disk.size != olddisk[disk.guest_device_name]:
+            remctl("web", "lvresize", oldname, disk.guest_device_name, str(disk.size))
+        if oldname != name:
+            remctl("web", "lvrename", oldname, disk.guest_device_name, name)
+    d = dict(user=user,
+             command=command,
+             machine=machine)
+    return Template(file="command.tmpl", searchList=[d, global_dict])    
 
-    
 
 def help(user, fields):
     """Handler for help messages."""
@@ -612,7 +673,12 @@ hope that the sipb-xen maintainers add support for serial consoles.""",
 HVM machines use the virtualization features of the processor, while
 ParaVM machines use Xen's emulation of virtualization features.  You
 want an HVM virtualized machine.""",
-                   cpu_weight="""Don't ask us!  We're as mystified as you are.""")
+                   cpu_weight="""Don't ask us!  We're as mystified as you are.""",
+                   owner="""The Owner must be the name of a locker that you are an AFS
+administrator of.  In particular, you or an AFS group you are a member
+of must have AFS rlidwka bits on the locker.  You can check see who
+administers the LOCKER locker using the command 'fs la /mit/LOCKER' on
+Athena.)""")
     
     d = dict(user=user,
              simple=simple,
@@ -630,12 +696,14 @@ def info(user, fields):
     if status is None:
         main_status = dict(name=machine.name,
                            memory=str(machine.memory))
+        uptime=None
+        cputime=None
     else:
         main_status = dict(status[1:])
-    start_time = float(main_status.get('start_time', 0))
-    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))
+        start_time = float(main_status.get('start_time', 0))
+        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))
     display_fields = """name uptime memory state cpu_weight on_reboot 
      on_poweroff on_crash on_xend_start on_xend_stop bootloader""".split()
     display_fields = [('name', 'Name'),
@@ -673,7 +741,7 @@ def info(user, fields):
     
     main_status['memory'] += ' MB'
     for field, disp in display_fields:
-        if field in ('uptime', 'cputime'):
+        if field in ('uptime', 'cputime') and locals()[field] is not None:
             fields.append((disp, locals()[field]))
         elif field in machine_info:
             fields.append((disp, machine_info[field]))
@@ -693,6 +761,7 @@ def info(user, fields):
              ram=machine.memory,
              max_mem=max_mem,
              max_disk=max_disk,
+             owner_help=helppopup("owner"),
              fields = fields)
     return Template(file='info.tmpl',
                    searchList=[d, global_dict])
@@ -722,19 +791,18 @@ if __name__ == '__main__':
         u.email = 'nobody'
     connect('postgres://sipb-xen@sipb-xen-dev/sipb_xen')
     operation = os.environ.get('PATH_INFO', '')
-    #print 'Content-Type: text/plain\n'
-    #print operation
+#    print 'Content-Type: text/plain\n'
+#    print operation
     if not operation:
         print "Status: 301 Moved Permanently"
         print 'Location: ' + os.environ['SCRIPT_NAME']+'/\n'
         sys.exit(0)
-    print 'Content-Type: text/html\n'
 
     if operation.startswith('/'):
         operation = operation[1:]
     if not operation:
         operation = 'list'
-    
+
     def badOperation(u, e):
         raise CodeError("Unknown operation")
 
@@ -747,17 +815,20 @@ if __name__ == '__main__':
         sys.stderr.seek(0)
         e = sys.stderr.read()
         if e:
-            output = output.replace('<body>', '<body><pre>'+e+'</pre>')
+            output = str(output)
+            output = output.replace('<body>', '<body><p>STDERR:</p><pre>'+e+'</pre>')
         print output
     except CodeError, err:
         print 'Content-Type: text/html\n'
         sys.stderr.seek(0)
         e = sys.stderr.read()
+        sys.stderr=sys.stdout
         print error(operation, u, fields, err, e)
     except InvalidInput, err:
         print 'Content-Type: text/html\n'
         sys.stderr.seek(0)
         e = sys.stderr.read()
+        sys.stderr=sys.stdout
         print invalidInput(operation, u, fields, err, e)
     except:
         print 'Content-Type: text/plain\n'
@@ -765,4 +836,5 @@ if __name__ == '__main__':
         e = sys.stderr.read()
         print e
         print '----'
+        sys.stderr = sys.stdout
         raise