Fixes for queries on cherrypy compat
[invirt/packages/invirt-web.git] / code / validation.py
old mode 100644 (file)
new mode 100755 (executable)
index 5f2a3ff..d288a69
@@ -5,18 +5,13 @@ import getafsgroups
 import re
 import string
 import dns.resolver
 import re
 import string
 import dns.resolver
-from invirt.database import Machine, NIC, Type, Disk, CDROM, Autoinstall
+from invirt import authz
+from invirt.database import Machine, NIC, Type, Disk, CDROM, Autoinstall, Owner
 from invirt.config import structs as config
 from invirt.common import InvalidInput, CodeError
 
 from invirt.config import structs as config
 from invirt.common import InvalidInput, CodeError
 
-MAX_MEMORY_TOTAL = 512
-MAX_MEMORY_SINGLE = 512
 MIN_MEMORY_SINGLE = 16
 MIN_MEMORY_SINGLE = 16
-MAX_DISK_TOTAL = 50
-MAX_DISK_SINGLE = 50
 MIN_DISK_SINGLE = 0.1
 MIN_DISK_SINGLE = 0.1
-MAX_VMS_TOTAL = 10
-MAX_VMS_ACTIVE = 4
 
 class Validate:
     def __init__(self, username, state, machine_id=None, name=None, description=None, owner=None,
 
 class Validate:
     def __init__(self, username, state, machine_id=None, name=None, description=None, owner=None,
@@ -30,7 +25,7 @@ class Validate:
         if strict:
             if name is None:
                 raise InvalidInput('name', name, "You must provide a machine name.")
         if strict:
             if name is None:
                 raise InvalidInput('name', name, "You must provide a machine name.")
-            if description is None:
+            if description is None or description.strip() == '':
                 raise InvalidInput('description', description, "You must provide a description.")
             if memory is None:
                 raise InvalidInput('memory', memory, "You must provide a memory size.")
                 raise InvalidInput('description', description, "You must provide a description.")
             if memory is None:
                 raise InvalidInput('memory', memory, "You must provide a memory size.")
@@ -91,15 +86,14 @@ def maxMemory(owner, g, machine=None, on=True):
     memory for the machine to change to, if it is left off, is
     returned.
     """
     memory for the machine to change to, if it is left off, is
     returned.
     """
-    if machine is not None and machine.memory > MAX_MEMORY_SINGLE:
-        # If they've been blessed, let them have it
-        return machine.memory
+    (quota_total, quota_single) = Owner.getMemoryQuotas(machine.owner if machine else owner)
+
     if not on:
     if not on:
-        return MAX_MEMORY_SINGLE
+        return quota_single
     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])
     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)
+    return min(quota_single, quota_total-mem_usage)
 
 def maxDisk(owner, machine=None):
     """Return the maximum disk that a machine can reach.
 
 def maxDisk(owner, machine=None):
     """Return the maximum disk that a machine can reach.
@@ -107,6 +101,8 @@ def maxDisk(owner, machine=None):
     If machine is None, the maximum disk for a new machine. Otherwise,
     return the maximum that a given machine can be changed to.
     """
     If machine is None, the maximum disk for a new machine. Otherwise,
     return the maximum that a given machine can be changed to.
     """
+    (quota_total, quota_single) = Owner.getDiskQuotas(machine.owner if machine else owner)
+
     if machine is not None:
         machine_id = machine.machine_id
     else:
     if machine is not None:
         machine_id = machine.machine_id
     else:
@@ -114,14 +110,15 @@ def maxDisk(owner, machine=None):
     disk_usage = Disk.query().filter(Disk.c.machine_id != machine_id).\
                      join('machine').\
                      filter_by(owner=owner).sum(Disk.c.size) or 0
     disk_usage = Disk.query().filter(Disk.c.machine_id != machine_id).\
                      join('machine').\
                      filter_by(owner=owner).sum(Disk.c.size) or 0
-    return min(MAX_DISK_SINGLE, MAX_DISK_TOTAL-disk_usage/1024.)
+    return min(quota_single, quota_total-disk_usage/1024.)
 
 def cantAddVm(owner, g):
     machines = getMachinesByOwner(owner)
     active_machines = [m for m in machines if m.name in g.xmlist_raw]
 
 def cantAddVm(owner, g):
     machines = getMachinesByOwner(owner)
     active_machines = [m for m in machines if m.name in g.xmlist_raw]
-    if machines.count() >= MAX_VMS_TOTAL:
+    (quota_total, quota_active) = Owner.getVMQuotas(owner)
+    if machines.count() >= quota_total:
         return 'You have too many VMs to create a new one.'
         return 'You have too many VMs to create a new one.'
-    if len(active_machines) >= MAX_VMS_ACTIVE:
+    if len(active_machines) >= quota_active:
         return ('You already have the maximum number of VMs turned on.  '
                 'To create more, turn one off.')
     return False
         return ('You already have the maximum number of VMs turned on.  '
                 'To create more, turn one off.')
     return False
@@ -133,7 +130,7 @@ def haveAccess(user, state, machine):
 
 def owns(user, machine):
     """Return whether a user owns a machine"""
 
 def owns(user, machine):
     """Return whether a user owns a machine"""
-    return user in expandLocker(machine.owner)
+    return user in authz.expandOwner(machine.owner)
 
 def validMachineName(name):
     """Check that name is valid for a machine name"""
 
 def validMachineName(name):
     """Check that name is valid for a machine name"""
@@ -212,8 +209,7 @@ def testMachineId(user, state, machine_id, exists=True):
 def testAdmin(user, admin, machine):
     """Determine whether a user can set the admin of a machine to this value.
 
 def testAdmin(user, admin, machine):
     """Determine whether a user can set the admin of a machine to this value.
 
-    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).
     """
     if admin is None:
         return None
     """
     if admin is None:
         return None
@@ -221,20 +217,17 @@ def testAdmin(user, admin, machine):
         return admin
     if admin == user:
         return admin
         return admin
     if admin == user:
         return admin
+    # we do not require that the user be in the admin group;
+    # just that it is a non-empty set
+    if authz.expandAdmin(admin):
+        return admin
     if ':' not in admin:
     if ':' not in admin:
-        if cache_acls.isUser(admin):
-            return admin
-        admin = 'system:' + admin
-    try:
-        if user in getafsgroups.getAfsGroupMembers(admin, config.authz[0].cell):
-            return admin
-    except getafsgroups.AfsProcessError, e:
-        errmsg = str(e)
-        if errmsg.startswith("pts: User or group doesn't exist"):
-            errmsg = 'The group "%s" does not exist.' % admin
-        raise InvalidInput('administrator', admin, errmsg)
-    #XXX Should we require that user is in the admin group?
-    return admin
+        if authz.expandAdmin('system:' + admin):
+            return 'system:' + admin
+        errmsg = 'No user "%s" or non-empty group "system:%s" found.' % (admin, admin)
+    else:
+        errmsg = 'No non-empty group "%s" found.' % (admin,)
+    raise InvalidInput('administrator', admin, errmsg)
 
 def testOwner(user, owner, machine=None):
     """Determine whether a user can set the owner of a machine to this value.
 
 def testOwner(user, owner, machine=None):
     """Determine whether a user can set the owner of a machine to this value.
@@ -245,8 +238,10 @@ def testOwner(user, owner, machine=None):
         return machine.owner
     if owner is None:
         raise InvalidInput('owner', owner, "Owner must be specified")
         return machine.owner
     if owner is None:
         raise InvalidInput('owner', owner, "Owner must be specified")
+    if '@' in owner:
+        raise InvalidInput('owner', owner, "No cross-realm Hesiod lockers allowed")
     try:
     try:
-        if user not in cache_acls.expandLocker(owner):
+        if user not in authz.expandOwner(owner):
             raise InvalidInput('owner', owner, 'You do not have access to the '
                                + owner + ' locker')
     except getafsgroups.AfsProcessError, e:
             raise InvalidInput('owner', owner, 'You do not have access to the '
                                + owner + ' locker')
     except getafsgroups.AfsProcessError, e:
@@ -260,9 +255,6 @@ def testContact(user, contact, machine=None):
         raise InvalidInput('contact', contact, "Not a valid email.")
     return contact
 
         raise InvalidInput('contact', contact, "Not a valid email.")
     return contact
 
-def testDisk(user, disksize, machine=None):
-    return disksize
-
 def testName(user, name, machine=None):
     if name is None:
         return None
 def testName(user, name, machine=None):
     if name is None:
         return None