7 from sipb_xen_database import Machine, NIC, Type, Disk
8 from webcommon import InvalidInput, g
10 MAX_MEMORY_TOTAL = 512
11 MAX_MEMORY_SINGLE = 256
12 MIN_MEMORY_SINGLE = 16
19 def getMachinesByOwner(user, machine=None):
20 """Return the machines owned by the same as a machine.
22 If the machine is None, return the machines owned by the same
29 return Machine.select_by(owner=owner)
31 def maxMemory(user, machine=None, on=True):
32 """Return the maximum memory for a machine or a user.
34 If machine is None, return the memory available for a new
35 machine. Else, return the maximum that machine can have.
37 on is whether the machine should be turned on. If false, the max
38 memory for the machine to change to, if it is left off, is
41 if machine is not None and machine.memory > MAX_MEMORY_SINGLE:
42 # If they've been blessed, let them have it
45 return MAX_MEMORY_SINGLE
46 machines = getMachinesByOwner(user, machine)
47 active_machines = [x for x in machines if g.xmlist.get(x)]
48 mem_usage = sum([x.memory for x in active_machines if x != machine])
49 return min(MAX_MEMORY_SINGLE, MAX_MEMORY_TOTAL-mem_usage)
51 def maxDisk(user, machine=None):
52 """Return the maximum disk that a machine can reach.
54 If machine is None, the maximum disk for a new machine. Otherwise,
55 return the maximum that a given machine can be changed to.
57 if machine is not None:
58 machine_id = machine.machine_id
61 disk_usage = Disk.query().filter_by(Disk.c.machine_id != machine_id,
62 owner=user).sum(Disk.c.size)
63 return min(MAX_DISK_SINGLE, MAX_DISK_TOTAL-disk_usage/1024.)
66 machines = getMachinesByOwner(user)
67 active_machines = [x for x in machines if g.xmlist.get(x)]
68 if len(machines) >= MAX_VMS_TOTAL:
69 return 'You have too many VMs to create a new one.'
70 if len(active_machines) >= MAX_VMS_ACTIVE:
71 return ('You already have the maximum number of VMs turned on. '
72 'To create more, turn one off.')
76 reason = cantAddVm(user)
78 raise InvalidInput('create', True, reason)
81 def haveAccess(user, machine):
82 """Return whether a user has administrative access to a machine"""
83 return user in cache_acls.accessList(machine)
85 def owns(user, machine):
86 """Return whether a user owns a machine"""
87 return user in expandLocker(machine.owner)
89 def validMachineName(name):
90 """Check that name is valid for a machine name"""
93 charset = string.ascii_letters + string.digits + '-_'
94 if name[0] in '-_' or len(name) > 22:
101 def validMemory(user, memory, machine=None, on=True):
102 """Parse and validate limits for memory for a given user and machine.
104 on is whether the memory must be valid after the machine is
109 if memory < MIN_MEMORY_SINGLE:
112 raise InvalidInput('memory', memory,
113 "Minimum %s MiB" % MIN_MEMORY_SINGLE)
114 if memory > maxMemory(user, machine, on):
115 raise InvalidInput('memory', memory,
116 'Maximum %s MiB for %s' % (maxMemory(user, machine),
120 def validDisk(user, disk, machine=None):
121 """Parse and validate limits for disk for a given user and machine."""
124 if disk > maxDisk(user, machine):
125 raise InvalidInput('disk', disk,
126 "Maximum %s G" % maxDisk(user, machine))
127 disk = int(disk * 1024)
128 if disk < MIN_DISK_SINGLE * 1024:
131 raise InvalidInput('disk', disk,
132 "Minimum %s GiB" % MIN_DISK_SINGLE)
135 def validVmType(vm_type):
138 t = Type.get(vm_type)
140 raise CodeError("Invalid vm type '%s'" % vm_type)
143 def testMachineId(user, machine_id, exists=True):
144 """Parse, validate and check authorization for a given user and machine.
146 If exists is False, don't check that it exists.
148 if machine_id is None:
149 raise InvalidInput('machine_id', machine_id,
150 "Must specify a machine ID.")
152 machine_id = int(machine_id)
154 raise InvalidInput('machine_id', machine_id, "Must be an integer.")
155 machine = Machine.get(machine_id)
156 if exists and machine is None:
157 raise InvalidInput('machine_id', machine_id, "Does not exist.")
158 if machine is not None and not haveAccess(user, machine):
159 raise InvalidInput('machine_id', machine_id,
160 "You do not have access to this machine.")
163 def testAdmin(user, admin, machine):
164 """Determine whether a user can set the admin of a machine to this value.
166 Return the value to set the admin field to (possibly 'system:' +
167 admin). XXX is modifying this a good idea?
169 if admin in (None, machine.administrator):
174 if cache_acls.isUser(admin):
176 admin = 'system:' + admin
178 if user in getafsgroups.getAfsGroupMembers(admin, 'athena.mit.edu'):
180 except getafsgroups.AfsProcessError, e:
182 if errmsg.startswith("pts: User or group doesn't exist"):
183 errmsg = 'The group "%s" does not exist.' % admin
184 raise InvalidInput('administrator', admin, errmsg)
185 #XXX Should we require that user is in the admin group?
188 def testOwner(user, owner, machine=None):
189 """Determine whether a user can set the owner of a machine to this value.
191 If machine is None, this is the owner of a new machine.
193 if owner == user or machine is not None and owner == machine.owner:
196 raise InvalidInput('owner', owner, "Owner must be specified")
198 if user not in cache_acls.expandLocker(owner):
199 raise InvalidInput('owner', owner, 'You do not have access to the '
201 except getafsgroups.AfsProcessError, e:
202 raise InvalidInput('owner', owner, str(e))
205 def testContact(user, contact, machine=None):
206 if contact in (None, machine.contact):
208 if not re.match("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$", contact, re.I):
209 raise InvalidInput('contact', contact, "Not a valid email.")
212 def testDisk(user, disksize, machine=None):
215 def testName(user, name, machine=None):
216 if name in (None, machine.name):
218 if not Machine.select_by(name=name):
220 raise InvalidInput('name', name, "Name is already taken.")
222 def testHostname(user, hostname, machine):
223 for nic in machine.nics:
224 if hostname == nic.hostname:
226 # check if doesn't already exist
227 if NIC.select_by(hostname=hostname):
228 raise InvalidInput('hostname', hostname,
230 if not re.match("^[A-Z0-9-]{1,22}$", hostname, re.I):
231 raise InvalidInput('hostname', hostname, "Not a valid hostname; "
232 "must only use number, letters, and dashes.")