Index: trunk/packages/invirt-base/debian/changelog
===================================================================
--- trunk/packages/invirt-base/debian/changelog	(revision 2130)
+++ trunk/packages/invirt-base/debian/changelog	(revision 2132)
@@ -1,2 +1,8 @@
+invirt-base (0.0.21) unstable; urgency=low
+
+  * added invirt-setquota script (self-documenting)
+
+ -- Peter A. Iannucci <iannucci@mit.edu>  Mon, 16 Feb 2009 23:48:25 -0500
+
 invirt-base (0.0.20) unstable; urgency=low
 
Index: trunk/packages/invirt-base/scripts/invirt-setquota
===================================================================
--- trunk/packages/invirt-base/scripts/invirt-setquota	(revision 2132)
+++ trunk/packages/invirt-base/scripts/invirt-setquota	(revision 2132)
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+
+"""
+invirt-setquota allows an administrator to set the RAM quotas for an owner.
+Invoking with only an owner name returns the current quotas for that owner.
+Setting a parameter to -1 restores the default.
+
+Examples:
+
+    invirt-setquota joeuser -t 512 -s None
+"""
+
+from invirt.database import *
+from sys import argv, exit, stderr, stdout
+from optparse import OptionParser
+
+class invirt_exception(Exception): pass
+
+def main(argv):
+    try:
+        parser = OptionParser(usage = '%prog owner [options]',
+                description = __doc__.strip().split('\n\n')[0])
+        parser.add_option('-t', '--total',
+                type = 'int',
+                dest = 'total',
+                help = 'set the total concurrent RAM quota')
+        parser.add_option('-s', '--single',
+                type = 'int',
+                dest = 'single',
+                help = 'set the single VM RAM quota')
+        opts, args = parser.parse_args()
+
+        if len(args) != 1:
+            raise invirt_exception(__doc__.strip())
+        owner = args[0]
+        connect()
+        session.begin()
+        
+        x = Owner.query().filter_by(owner_id=owner).first()
+
+        edited = False
+        if opts.total != None:
+            total = int(opts.total)
+            if total == -1:
+                x.ram_quota_total = None
+            else:
+                x.ram_quota_total = total
+            edited = True
+
+        if opts.single != None:
+            single = int(opts.single)
+            if single == -1:
+                x.ram_quota_single = None
+            else:
+                x.ram_quota_single = single
+            edited = True
+
+        if edited:
+            session.commit()
+        print str(x)
+
+    except invirt_exception, ex:
+        print >> stderr, ex
+        return 1
+
+if __name__ == '__main__':
+    exit(main(argv))
Index: trunk/packages/invirt-database/debian/changelog
===================================================================
--- trunk/packages/invirt-database/debian/changelog	(revision 2130)
+++ trunk/packages/invirt-database/debian/changelog	(revision 2132)
@@ -1,2 +1,9 @@
+invirt-database (0.1.3) unstable; urgency=low
+
+  * Added owner table with ram quotas.
+  * Refactored Owner class into separate sourcefile
+
+ -- Peter A. Iannucci <iannucci@mit.edu>  Mon, 16 Feb 2009 23:49:39 -0500
+
 invirt-database (0.1.2) unstable; urgency=low
 
Index: trunk/packages/invirt-database/python/database/models.py
===================================================================
--- trunk/packages/invirt-database/python/database/models.py	(revision 2130)
+++ trunk/packages/invirt-database/python/database/models.py	(revision 2132)
@@ -17,4 +17,5 @@
            'mirrors_table',
            'autoinstalls_table',
+           'owners_table',
            'Machine',
            'MachineAccess',
@@ -25,4 +26,5 @@
            'Mirror',
            'Autoinstall',
+           'Owner',
            'or_',
            ]
@@ -83,4 +85,9 @@
        Column('arch', String, nullable=False))
 
+owners_table = Table('owners', meta,
+       Column('owner_id', String, primary_key=True, nullable=False),
+       Column('ram_quota_total', Integer, nullable=True),
+       Column('ram_quota_single', Integer, nullable=True))
+
 machine_access_table = Table('machine_access', meta,
        Column('machine_id', Integer, ForeignKey('machines.machine_id', ondelete='CASCADE'), nullable=False, index=True),
@@ -120,4 +127,6 @@
         return "<Autoinstall %s: %s (%s)>" % (self.autoinstall_id, self.description, self.type.type_id)
 
+from owner import Owner
+
 session.mapper(Machine, machine_table,
               properties={'nics': relation(NIC, backref="machine"),
@@ -133,4 +142,5 @@
                properties={'mirror': relation(Mirror, backref="cdroms")})
 session.mapper(Autoinstall, autoinstalls_table)
+session.mapper(Owner, owners_table)
 
 def clear_cache():
Index: trunk/packages/invirt-database/python/database/owner.py
===================================================================
--- trunk/packages/invirt-database/python/database/owner.py	(revision 2132)
+++ trunk/packages/invirt-database/python/database/owner.py	(revision 2132)
@@ -0,0 +1,19 @@
+MAX_MEMORY_TOTAL = 512
+MAX_MEMORY_SINGLE = 512
+class Owner(object):
+    def __repr__(self):
+        return "<Owner %s: ram_quota_total=%s MB ram_quota_single=%s MB>" % (self.owner_id, self.ram_quota_total, self.ram_quota_single)
+    def getQuotas(owner):
+        owner_info = Owner.query().filter_by(owner_id=owner).first()
+        if owner_info != None:
+            quota_total = owner_info.ram_quota_total
+            if quota_total == None:
+                quota_total = MAX_MEMORY_TOTAL
+            quota_single = owner_info.ram_quota_single
+            if quota_single == None:
+                quota_single = MAX_MEMORY_SINGLE
+        else:
+            quota_total = MAX_MEMORY_TOTAL
+            quota_single = MAX_MEMORY_SINGLE
+        return (quota_total, quota_single)
+    getQuotas = staticmethod(getQuotas)
Index: trunk/packages/invirt-remote/debian/changelog
===================================================================
--- trunk/packages/invirt-remote/debian/changelog	(revision 2130)
+++ trunk/packages/invirt-remote/debian/changelog	(revision 2132)
@@ -2,8 +2,10 @@
 
   * modified host/usr/sbin/invirt-availability and invirt-vmcontrol to stat
-    /etc/invirt/nocreate; if it exists, they advertise zero free memory and 
+    /etc/invirt/nocreate; if it exists, they advertise zero free memory and
     refuse to create VMs
-
- -- Peter A. Iannucci <iannucci@mit.edu>  Sat, 14 Feb 2009 18:10:54 -0500
+  * added memory quota validation to invirt-remote-create
+  * added owner table to database with ram_quota_total and ram_quota_single
+
+ -- Peter A. Iannucci <iannucci@mit.edu>  Mon, 16 Feb 2009 23:49:14 -0500
 
 invirt-remote (0.3.3) unstable; urgency=low
Index: trunk/packages/invirt-remote/server/usr/sbin/invirt-remote-create
===================================================================
--- trunk/packages/invirt-remote/server/usr/sbin/invirt-remote-create	(revision 2130)
+++ trunk/packages/invirt-remote/server/usr/sbin/invirt-remote-create	(revision 2132)
@@ -13,4 +13,16 @@
 import sys
 import yaml
+import invirt.database
+
+def maxMemory(owner, xmlist):
+    """
+    Return the memory available for a new machine.
+    """
+    machines = invirt.database.Machine.query().filter_by(owner=owner)
+    (quota_total, quota_single) = invirt.database.Owner.getQuotas(owner)
+
+    active_machines = [m for m in machines if m.name in xmlist]
+    mem_usage = sum([x.memory for x in active_machines])
+    return min(quota_single, quota_total-mem_usage)
 
 def choose_host():
@@ -50,4 +62,16 @@
         return 1
 
+    if operation == "create":
+        invirt.database.connect()
+        machine = invirt.database.Machine.query().filter_by(name=machine_name).first()
+
+        owner = machine.owner
+        vm_memory = machine.memory
+
+        max_memory = maxMemory(owner, vms.keys())
+        if vm_memory > max_memory:
+            print >>sys.stderr, "owner %s requested %d MB of memory for vm %s; %d MB allowed" % (owner, vm_memory, machine_name, max_memory)
+            return 1
+
     host = choose_host()
     print 'Creating on host %s...' % host
Index: trunk/packages/invirt-web/code/validation.py
===================================================================
--- trunk/packages/invirt-web/code/validation.py	(revision 2130)
+++ trunk/packages/invirt-web/code/validation.py	(revision 2132)
@@ -6,10 +6,8 @@
 import string
 import dns.resolver
-from invirt.database import Machine, NIC, Type, Disk, CDROM, Autoinstall
+from invirt.database import Machine, NIC, Type, Disk, CDROM, Autoinstall, Owner
 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
 MAX_DISK_TOTAL = 50
@@ -92,13 +90,12 @@
     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.getQuotas(machine.owner if machine else owner)
+
     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])
-    return min(MAX_MEMORY_SINGLE, MAX_MEMORY_TOTAL-mem_usage)
+    return min(quota_single, quota_total-mem_usage)
 
 def maxDisk(owner, machine=None):
Index: trunk/packages/invirt-web/debian/changelog
===================================================================
--- trunk/packages/invirt-web/debian/changelog	(revision 2130)
+++ trunk/packages/invirt-web/debian/changelog	(revision 2132)
@@ -1,2 +1,8 @@
+invirt-web (0.0.19) unstable; urgency=low
+
+  * modified quota checking to refer to invirt.database.Owner for quotas and defaults
+
+ -- Peter A. Iannucci <iannucci@mit.edu>  Mon, 16 Feb 2009 23:49:21 -0500
+
 invirt-web (0.0.18) unstable; urgency=low
 
