Fix more bugs
[invirt/packages/invirt-web.git] / code / validation.py
index c0e3aeb..db3a365 100644 (file)
@@ -5,7 +5,7 @@ import getafsgroups
 import re
 import string
 from sipb_xen_database import Machine, NIC, Type, Disk
 import re
 import string
 from sipb_xen_database import Machine, NIC, Type, Disk
-from webcommon import InvalidInput, g
+from webcommon import InvalidInput
 
 MAX_MEMORY_TOTAL = 512
 MAX_MEMORY_SINGLE = 256
 
 MAX_MEMORY_TOTAL = 512
 MAX_MEMORY_SINGLE = 256
@@ -16,7 +16,57 @@ MIN_DISK_SINGLE = 0.1
 MAX_VMS_TOTAL = 10
 MAX_VMS_ACTIVE = 4
 
 MAX_VMS_TOTAL = 10
 MAX_VMS_ACTIVE = 4
 
-def getMachinesByOwner(user, machine=None):
+class Validate:
+    def __init__(self, username, state, machine_id=None, name=None, owner=None,
+                 admin=None, contact=None, memory=None, disksize=None,
+                 vmtype=None, cdrom=None, clone_from=None, strict=False):
+        # XXX Successive quota checks aren't a good idea, since you
+        # can't necessarily change the locker and disk size at the
+        # same time.
+        created_new = (machine_id is None)
+
+        if strict:
+            if name is None:
+                raise InvalidInput('name', name, "You must provide a machine name.")
+            if memory is None:
+                raise InvalidInput('memory', memory, "You must provide a memory size.")
+            if disksize is None:
+                raise InvalidInput('disk', disksize, "You must provide a disk size.")
+
+        if machine_id is not None:
+            self.machine = testMachineId(username, machine_id)
+        machine = getattr(self, 'machine', None)
+
+        owner = testOwner(username, owner, machine)
+        if owner is not None:
+            self.owner = owner
+        admin = testAdmin(username, admin, machine)
+        if admin is not None:
+            self.admin = admin
+        contact = testContact(username, contact, machine)
+        if contact is not None:
+            self.contact = contact
+        name = testName(username, name, machine)
+        if name is not None:
+            self.name = name
+        if memory is not None:
+            self.memory = validMemory(self.owner, state, memory, machine,
+                                      on=not created_new)
+        if disksize is not None:
+            self.disksize = validDisk(self.owner, disksize, machine)
+        if vmtype is not None:
+            self.vmtype = validVmType(vmtype)
+        if cdrom is not None:
+            if not CDROM.get(cdrom):
+                raise CodeError("Invalid cdrom type '%s'" % cdrom)
+            self.cdrom = cdrom
+        if clone_from is not None:
+            if clone_from not in ('ice3', ):
+                raise CodeError("Invalid clone image '%s'" % clone_from)
+            self.clone_from = clone_from
+
+
+def getMachinesByOwner(owner, machine=None):
     """Return the machines owned by the same as a machine.
 
     If the machine is None, return the machines owned by the same
     """Return the machines owned by the same as a machine.
 
     If the machine is None, return the machines owned by the same
@@ -24,11 +74,9 @@ def getMachinesByOwner(user, machine=None):
     """
     if machine:
         owner = machine.owner
     """
     if machine:
         owner = machine.owner
-    else:
-        owner = user
     return Machine.select_by(owner=owner)
 
     return Machine.select_by(owner=owner)
 
-def maxMemory(user, machine=None, on=True):
+def maxMemory(owner, g, machine=None, on=True):
     """Return the maximum memory for a machine or a user.
 
     If machine is None, return the memory available for a new
     """Return the maximum memory for a machine or a user.
 
     If machine is None, return the memory available for a new
@@ -43,12 +91,12 @@ def maxMemory(user, machine=None, on=True):
         return machine.memory
     if not on:
         return MAX_MEMORY_SINGLE
         return machine.memory
     if not on:
         return MAX_MEMORY_SINGLE
-    machines = getMachinesByOwner(user, machine)
-    active_machines = [x for x in machines if g.xmlist.get(x)]
+    machines = getMachinesByOwner(owner, machine)
+    active_machines = [m for m in machines if m.name in g.xmlist_raw]
     mem_usage = sum([x.memory for x in active_machines if x != machine])
     return min(MAX_MEMORY_SINGLE, MAX_MEMORY_TOTAL-mem_usage)
 
     mem_usage = sum([x.memory for x in active_machines if x != machine])
     return min(MAX_MEMORY_SINGLE, MAX_MEMORY_TOTAL-mem_usage)
 
-def maxDisk(user, machine=None):
+def maxDisk(owner, machine=None):
     """Return the maximum disk that a machine can reach.
 
     If machine is None, the maximum disk for a new machine. Otherwise,
     """Return the maximum disk that a machine can reach.
 
     If machine is None, the maximum disk for a new machine. Otherwise,
@@ -59,12 +107,12 @@ def maxDisk(user, machine=None):
     else:
         machine_id = None
     disk_usage = Disk.query().filter_by(Disk.c.machine_id != machine_id,
     else:
         machine_id = None
     disk_usage = Disk.query().filter_by(Disk.c.machine_id != machine_id,
-                                        owner=user).sum(Disk.c.size) or 0
+                                        owner=owner).sum(Disk.c.size) or 0
     return min(MAX_DISK_SINGLE, MAX_DISK_TOTAL-disk_usage/1024.)
 
     return min(MAX_DISK_SINGLE, MAX_DISK_TOTAL-disk_usage/1024.)
 
-def cantAddVm(user):
-    machines = getMachinesByOwner(user)
-    active_machines = [x for x in machines if g.xmlist.get(x)]
+def cantAddVm(owner, g):
+    machines = getMachinesByOwner(owner)
+    active_machines = [m for m in machines if m.name in g.xmlist_raw]
     if len(machines) >= MAX_VMS_TOTAL:
         return 'You have too many VMs to create a new one.'
     if len(active_machines) >= MAX_VMS_ACTIVE:
     if len(machines) >= MAX_VMS_TOTAL:
         return 'You have too many VMs to create a new one.'
     if len(active_machines) >= MAX_VMS_ACTIVE:
@@ -72,12 +120,6 @@ def cantAddVm(user):
                 'To create more, turn one off.')
     return False
 
                 'To create more, turn one off.')
     return False
 
-def validAddVm(user):
-    reason = cantAddVm(user)
-    if reason:
-        raise InvalidInput('create', True, reason)
-    return True
-
 def haveAccess(user, machine):
     """Return whether a user has administrative access to a machine"""
     return user in cache_acls.accessList(machine)
 def haveAccess(user, machine):
     """Return whether a user has administrative access to a machine"""
     return user in cache_acls.accessList(machine)
@@ -98,8 +140,8 @@ def validMachineName(name):
             return False
     return True
 
             return False
     return True
 
-def validMemory(user, memory, machine=None, on=True):
-    """Parse and validate limits for memory for a given user and machine.
+def validMemory(owner, g, memory, machine=None, on=True):
+    """Parse and validate limits for memory for a given owner and machine.
 
     on is whether the memory must be valid after the machine is
     switched on.
 
     on is whether the memory must be valid after the machine is
     switched on.
@@ -111,19 +153,19 @@ def validMemory(user, memory, machine=None, on=True):
     except ValueError:
         raise InvalidInput('memory', memory,
                            "Minimum %s MiB" % MIN_MEMORY_SINGLE)
     except ValueError:
         raise InvalidInput('memory', memory,
                            "Minimum %s MiB" % MIN_MEMORY_SINGLE)
-    if memory > maxMemory(user, machine, on):
+    max_val = maxMemory(owner, g, machine, on)
+    if memory > max_val:
         raise InvalidInput('memory', memory,
         raise InvalidInput('memory', memory,
-                           'Maximum %s MiB for %s' % (maxMemory(user, machine),
-                                                      user))
+                           'Maximum %s MiB for %s' % (max_val, owner))
     return memory
 
     return memory
 
-def validDisk(user, disk, machine=None):
-    """Parse and validate limits for disk for a given user and machine."""
+def validDisk(owner, disk, machine=None):
+    """Parse and validate limits for disk for a given owner and machine."""
     try:
         disk = float(disk)
     try:
         disk = float(disk)
-        if disk > maxDisk(user, machine):
+        if disk > maxDisk(owner, machine):
             raise InvalidInput('disk', disk,
             raise InvalidInput('disk', disk,
-                               "Maximum %s G" % maxDisk(user, machine))
+                               "Maximum %s G" % maxDisk(owner, machine))
         disk = int(disk * 1024)
         if disk < MIN_DISK_SINGLE * 1024:
             raise ValueError
         disk = int(disk * 1024)
         if disk < MIN_DISK_SINGLE * 1024:
             raise ValueError
@@ -166,7 +208,9 @@ def testAdmin(user, admin, machine):
     Return the value to set the admin field to (possibly 'system:' +
     admin).  XXX is modifying this a good idea?
     """
     Return the value to set the admin field to (possibly 'system:' +
     admin).  XXX is modifying this a good idea?
     """
-    if admin in (None, machine.administrator):
+    if admin is None:
+        return None
+    if machine is not None and admin == machine.administrator:
         return None
     if admin == user:
         return admin
         return None
     if admin == user:
         return admin
@@ -190,8 +234,10 @@ def testOwner(user, owner, machine=None):
 
     If machine is None, this is the owner of a new machine.
     """
 
     If machine is None, this is the owner of a new machine.
     """
-    if owner == user or machine is not None and owner == machine.owner:
+    if owner == user:
         return owner
         return owner
+    if machine is not None and owner in (machine.owner, None):
+        return machine.owner
     if owner is None:
         raise InvalidInput('owner', owner, "Owner must be specified")
     try:
     if owner is None:
         raise InvalidInput('owner', owner, "Owner must be specified")
     try:
@@ -203,7 +249,7 @@ def testOwner(user, owner, machine=None):
     return owner
 
 def testContact(user, contact, machine=None):
     return owner
 
 def testContact(user, contact, machine=None):
-    if contact in (None, machine.contact):
+    if contact is None or (machine is not None and contact == machine.contact):
         return None
     if not re.match("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$", contact, re.I):
         raise InvalidInput('contact', contact, "Not a valid email.")
         return None
     if not re.match("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$", contact, re.I):
         raise InvalidInput('contact', contact, "Not a valid email.")
@@ -213,9 +259,13 @@ def testDisk(user, disksize, machine=None):
     return disksize
 
 def testName(user, name, machine=None):
     return disksize
 
 def testName(user, name, machine=None):
-    if name in (None, machine.name):
+    if name is None:
+        return None
+    if machine is not None and name == machine.name:
         return None
     if not Machine.select_by(name=name):
         return None
     if not Machine.select_by(name=name):
+        if not validMachineName(name):
+            raise InvalidInput('name', name, 'You must provide a machine name.  Max 22 chars, alnum plus \'-\' and \'_\'.')
         return name
     raise InvalidInput('name', name, "Name is already taken.")
 
         return name
     raise InvalidInput('name', name, "Name is already taken.")