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

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

Cleaner HVM/paravm validation

File size: 7.7 KB
Line 
1#!/usr/bin/python
2
3import cache_acls
4import getafsgroups
5import re
6import string
7from sipb_xen_database import Machine, NIC, Type
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 validVmType(vm_type):
128    if vm_type == 'hvm':
129        return Type.get('linux-hvm')
130    elif vm_type == 'paravm':
131        return Type.get('linux')
132    else:
133        raise CodeError("Invalid vm type '%s'"  % vm_type)
134
135def testMachineId(user, machine_id, exists=True):
136    """Parse, validate and check authorization for a given user and machine.
137
138    If exists is False, don't check that it exists.
139    """
140    if machine_id is None:
141        raise InvalidInput('machine_id', machine_id, 
142                           "Must specify a machine ID.")
143    try:
144        machine_id = int(machine_id)
145    except ValueError:
146        raise InvalidInput('machine_id', machine_id, "Must be an integer.")
147    machine = Machine.get(machine_id)
148    if exists and machine is None:
149        raise InvalidInput('machine_id', machine_id, "Does not exist.")
150    if machine is not None and not haveAccess(user, machine):
151        raise InvalidInput('machine_id', machine_id,
152                           "You do not have access to this machine.")
153    return machine
154
155def testAdmin(user, admin, machine):
156    """Determine whether a user can set the admin of a machine to this value.
157
158    Return the value to set the admin field to (possibly 'system:' +
159    admin).  XXX is modifying this a good idea?
160    """
161    if admin in (None, machine.administrator):
162        return None
163    if admin == user:
164        return admin
165    if ':' not in admin:
166        if cache_acls.isUser(admin):
167            return admin
168        admin = 'system:' + admin
169    try:
170        if user in getafsgroups.getAfsGroupMembers(admin, 'athena.mit.edu'):
171            return admin
172    except getafsgroups.AfsProcessError, e:
173        errmsg = str(e)
174        if errmsg.startswith("pts: User or group doesn't exist"):
175            errmsg = 'The group "%s" does not exist.' % admin
176        raise InvalidInput('administrator', admin, errmsg)
177    #XXX Should we require that user is in the admin group?
178    return admin
179   
180def testOwner(user, owner, machine=None):
181    """Determine whether a user can set the owner of a machine to this value.
182
183    If machine is None, this is the owner of a new machine.
184    """
185    if owner == user or machine is not None and owner == machine.owner:
186        return owner
187    if owner is None:
188        raise InvalidInput('owner', owner, "Owner must be specified")
189    try:
190        if user not in cache_acls.expandLocker(owner):
191            raise InvalidInput('owner', owner, 'You do not have access to the '
192                               + owner + ' locker')
193    except getafsgroups.AfsProcessError, e:
194        raise InvalidInput('owner', owner, str(e))
195    return owner
196
197def testContact(user, contact, machine=None):
198    if contact in (None, machine.contact):
199        return None
200    if not re.match("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$", contact, re.I):
201        raise InvalidInput('contact', contact, "Not a valid email.")
202    return contact
203
204def testDisk(user, disksize, machine=None):
205    return disksize
206
207def testName(user, name, machine=None):
208    if name in (None, machine.name):
209        return None
210    if not Machine.select_by(name=name):
211        return name
212    raise InvalidInput('name', name, "Name is already taken.")
213
214def testHostname(user, hostname, machine):
215    for nic in machine.nics:
216        if hostname == nic.hostname:
217            return hostname
218    # check if doesn't already exist
219    if NIC.select_by(hostname=hostname):
220        raise InvalidInput('hostname', hostname,
221                           "Already exists")
222    if not re.match("^[A-Z0-9-]{1,22}$", hostname, re.I):
223        raise InvalidInput('hostname', hostname, "Not a valid hostname; "
224                           "must only use number, letters, and dashes.")
225    return hostname
Note: See TracBrowser for help on using the repository browser.