source: trunk/packages/sipb-xen-www/code/validation.py @ 411

Last change on this file since 411 was 411, checked in by ecprice, 17 years ago

Fix the bug jbarnold reported, where the real-time access control didn't match the cached version.

File size: 7.2 KB
Line 
1#!/usr/bin/python
2
3import cache_acls
4import getafsgroups
5import re
6import string
7from sipb_xen_database import Machine, NIC
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.uptimes.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    machines = getMachinesByOwner(user, machine)
53    disk_usage = sum([sum([y.size for y in x.disks])
54                      for x in machines if x != machine])
55    return min(MAX_DISK_SINGLE, MAX_DISK_TOTAL-disk_usage/1024.)
56
57def cantAddVm(user):
58    machines = getMachinesByOwner(user)
59    active_machines = [x for x in machines if g.uptimes.get(x)]
60    if len(machines) >= MAX_VMS_TOTAL:
61        return 'You have too many VMs to create a new one.'
62    if len(active_machines) >= MAX_VMS_ACTIVE:
63        return ('You already have the maximum number of VMs turned on.  '
64                'To create more, turn one off.')
65    return False
66
67def validAddVm(user):
68    reason = cantAddVm(user)
69    if reason:
70        raise InvalidInput('create', True, reason)
71    return True
72
73def haveAccess(user, machine):
74    """Return whether a user has administrative access to a machine"""
75    return user in cache_acls.accessList(machine)
76
77def owns(user, machine):
78    """Return whether a user owns a machine"""
79    return user in expandLocker(machine.owner)
80
81def validMachineName(name):
82    """Check that name is valid for a machine name"""
83    if not name:
84        return False
85    charset = string.ascii_letters + string.digits + '-_'
86    if name[0] in '-_' or len(name) > 22:
87        return False
88    for x in name:
89        if x not in charset:
90            return False
91    return True
92
93def validMemory(user, memory, machine=None, on=True):
94    """Parse and validate limits for memory for a given user and machine.
95
96    on is whether the memory must be valid after the machine is
97    switched on.
98    """
99    try:
100        memory = int(memory)
101        if memory < MIN_MEMORY_SINGLE:
102            raise ValueError
103    except ValueError:
104        raise InvalidInput('memory', memory, 
105                           "Minimum %s MiB" % MIN_MEMORY_SINGLE)
106    if memory > maxMemory(user, machine, on):
107        raise InvalidInput('memory', memory,
108                           'Maximum %s MiB for %s' % (maxMemory(user, machine),
109                                                      user))
110    return memory
111
112def validDisk(user, disk, machine=None):
113    """Parse and validate limits for disk for a given user and machine."""
114    try:
115        disk = float(disk)
116        if disk > maxDisk(user, machine):
117            raise InvalidInput('disk', disk,
118                               "Maximum %s G" % maxDisk(user, machine))
119        disk = int(disk * 1024)
120        if disk < MIN_DISK_SINGLE * 1024:
121            raise ValueError
122    except ValueError:
123        raise InvalidInput('disk', disk,
124                           "Minimum %s GiB" % MIN_DISK_SINGLE)
125    return disk
126           
127def testMachineId(user, machine_id, exists=True):
128    """Parse, validate and check authorization for a given user and machine.
129
130    If exists is False, don't check that it exists.
131    """
132    if machine_id is None:
133        raise InvalidInput('machine_id', machine_id, 
134                           "Must specify a machine ID.")
135    try:
136        machine_id = int(machine_id)
137    except ValueError:
138        raise InvalidInput('machine_id', machine_id, "Must be an integer.")
139    machine = Machine.get(machine_id)
140    if exists and machine is None:
141        raise InvalidInput('machine_id', machine_id, "Does not exist.")
142    if machine is not None and not haveAccess(user, machine):
143        raise InvalidInput('machine_id', machine_id,
144                           "You do not have access to this machine.")
145    return machine
146
147def testAdmin(user, admin, machine):
148    """Determine whether a user can set the admin of a machine to this value.
149
150    Return the value to set the admin field to (possibly 'system:' +
151    admin).  XXX is modifying this a good idea?
152    """
153    if admin in (None, machine.administrator):
154        return None
155    if admin == user:
156        return admin
157    if ':' not in admin:
158        if cache_acls.isUser(admin):
159            return admin
160        admin = 'system:' + admin
161    if getafsgroups.checkAfsGroup(user, admin, 'athena.mit.edu'):
162        return admin
163    #XXX Should we require that user is in cache_acls.expandName(admin)?
164    return admin
165   
166def testOwner(user, owner, machine=None):
167    """Determine whether a user can set the owner of a machine to this value.
168
169    If machine is None, this is the owner of a new machine.
170    """
171    if owner == user or machine is not None and owner == machine.owner:
172        return owner
173    if owner is None:
174        raise InvalidInput('owner', owner, "Owner must be specified")
175    try:
176        if user not in cache_acls.expandLocker(owner):
177            raise InvalidInput('owner', owner, 'You do not have access to the '
178                               + owner + ' locker')
179    except getafsgroups.AfsProcessError, e:
180        raise InvalidInput('owner', owner, str(e))
181    return owner
182
183def testContact(user, contact, machine=None):
184    if contact in (None, machine.contact):
185        return None
186    if not re.match("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$", contact, re.I):
187        raise InvalidInput('contact', contact, "Not a valid email.")
188    return contact
189
190def testDisk(user, disksize, machine=None):
191    return disksize
192
193def testName(user, name, machine=None):
194    if name in (None, machine.name):
195        return None
196    if not Machine.select_by(name=name):
197        return name
198    raise InvalidInput('name', name, "Name is already taken.")
199
200def testHostname(user, hostname, machine):
201    for nic in machine.nics:
202        if hostname == nic.hostname:
203            return hostname
204    # check if doesn't already exist
205    if NIC.select_by(hostname=hostname):
206        raise InvalidInput('hostname', hostname,
207                           "Already exists")
208    if not re.match("^[A-Z0-9-]{1,22}$", hostname, re.I):
209        raise InvalidInput('hostname', hostname, "Not a valid hostname; "
210                           "must only use number, letters, and dashes.")
211    return hostname
Note: See TracBrowser for help on using the repository browser.