source: branches/wsgi/packages/sipb-xen-www/code/validation.py @ 2530

Last change on this file since 2530 was 537, checked in by ecprice, 16 years ago

Fix jis's bug

File size: 8.0 KB
Line 
1#!/usr/bin/python
2
3import cache_acls
4import getafsgroups
5import re
6import string
7from sipb_xen_database import Machine, NIC, Type, Disk
8from webcommon import InvalidInput, g
9
10MAX_MEMORY_TOTAL = 512
11MAX_MEMORY_SINGLE = 256
12MIN_MEMORY_SINGLE = 16
13MAX_DISK_TOTAL = 50
14MAX_DISK_SINGLE = 50
15MIN_DISK_SINGLE = 0.1
16MAX_VMS_TOTAL = 10
17MAX_VMS_ACTIVE = 4
18
19def getMachinesByOwner(user, machine=None):
20    """Return the machines owned by the same as a machine.
21
22    If the machine is None, return the machines owned by the same
23    user.
24    """
25    if machine:
26        owner = machine.owner
27    else:
28        owner = user
29    return Machine.select_by(owner=owner)
30
31def maxMemory(user, machine=None, on=True):
32    """Return the maximum memory for a machine or a user.
33
34    If machine is None, return the memory available for a new
35    machine.  Else, return the maximum that machine can have.
36
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
39    returned.
40    """
41    if machine is not None and machine.memory > MAX_MEMORY_SINGLE:
42        # If they've been blessed, let them have it
43        return machine.memory
44    if not on:
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)
50
51def maxDisk(user, machine=None):
52    """Return the maximum disk that a machine can reach.
53
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.
56    """
57    if machine is not None:
58        machine_id = machine.machine_id
59    else:
60        machine_id = None
61    disk_usage = Disk.query().filter_by(Disk.c.machine_id != machine_id,
62                                        owner=user).sum(Disk.c.size) or 0
63    return min(MAX_DISK_SINGLE, MAX_DISK_TOTAL-disk_usage/1024.)
64
65def cantAddVm(user):
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.')
73    return False
74
75def validAddVm(user):
76    reason = cantAddVm(user)
77    if reason:
78        raise InvalidInput('create', True, reason)
79    return True
80
81def haveAccess(user, machine):
82    """Return whether a user has administrative access to a machine"""
83    return user in cache_acls.accessList(machine)
84
85def owns(user, machine):
86    """Return whether a user owns a machine"""
87    return user in expandLocker(machine.owner)
88
89def validMachineName(name):
90    """Check that name is valid for a machine name"""
91    if not name:
92        return False
93    charset = string.ascii_letters + string.digits + '-_'
94    if name[0] in '-_' or len(name) > 22:
95        return False
96    for x in name:
97        if x not in charset:
98            return False
99    return True
100
101def validMemory(user, memory, machine=None, on=True):
102    """Parse and validate limits for memory for a given user and machine.
103
104    on is whether the memory must be valid after the machine is
105    switched on.
106    """
107    try:
108        memory = int(memory)
109        if memory < MIN_MEMORY_SINGLE:
110            raise ValueError
111    except ValueError:
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),
117                                                      user))
118    return memory
119
120def validDisk(user, disk, machine=None):
121    """Parse and validate limits for disk for a given user and machine."""
122    try:
123        disk = float(disk)
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:
129            raise ValueError
130    except ValueError:
131        raise InvalidInput('disk', disk,
132                           "Minimum %s GiB" % MIN_DISK_SINGLE)
133    return disk
134
135def validVmType(vm_type):
136    if vm_type is None:
137        return None
138    t = Type.get(vm_type)
139    if t is None:
140        raise CodeError("Invalid vm type '%s'"  % vm_type)
141    return t
142
143def testMachineId(user, machine_id, exists=True):
144    """Parse, validate and check authorization for a given user and machine.
145
146    If exists is False, don't check that it exists.
147    """
148    if machine_id is None:
149        raise InvalidInput('machine_id', machine_id,
150                           "Must specify a machine ID.")
151    try:
152        machine_id = int(machine_id)
153    except ValueError:
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.")
161    return machine
162
163def testAdmin(user, admin, machine):
164    """Determine whether a user can set the admin of a machine to this value.
165
166    Return the value to set the admin field to (possibly 'system:' +
167    admin).  XXX is modifying this a good idea?
168    """
169    if admin in (None, machine.administrator):
170        return None
171    if admin == user:
172        return admin
173    if ':' not in admin:
174        if cache_acls.isUser(admin):
175            return admin
176        admin = 'system:' + admin
177    try:
178        if user in getafsgroups.getAfsGroupMembers(admin, 'athena.mit.edu'):
179            return admin
180    except getafsgroups.AfsProcessError, e:
181        errmsg = str(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?
186    return admin
187
188def testOwner(user, owner, machine=None):
189    """Determine whether a user can set the owner of a machine to this value.
190
191    If machine is None, this is the owner of a new machine.
192    """
193    if owner == user or machine is not None and owner == machine.owner:
194        return owner
195    if owner is None:
196        raise InvalidInput('owner', owner, "Owner must be specified")
197    try:
198        if user not in cache_acls.expandLocker(owner):
199            raise InvalidInput('owner', owner, 'You do not have access to the '
200                               + owner + ' locker')
201    except getafsgroups.AfsProcessError, e:
202        raise InvalidInput('owner', owner, str(e))
203    return owner
204
205def testContact(user, contact, machine=None):
206    if contact in (None, machine.contact):
207        return None
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.")
210    return contact
211
212def testDisk(user, disksize, machine=None):
213    return disksize
214
215def testName(user, name, machine=None):
216    if name in (None, machine.name):
217        return None
218    if not Machine.select_by(name=name):
219        return name
220    raise InvalidInput('name', name, "Name is already taken.")
221
222def testHostname(user, hostname, machine):
223    for nic in machine.nics:
224        if hostname == nic.hostname:
225            return hostname
226    # check if doesn't already exist
227    if NIC.select_by(hostname=hostname):
228        raise InvalidInput('hostname', hostname,
229                           "Already exists")
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.")
233    return hostname
Note: See TracBrowser for help on using the repository browser.