source: trunk/web/validation.py @ 250

Last change on this file since 250 was 250, checked in by quentin, 16 years ago

Allow service maintainers to bump memory

File size: 7.0 KB
Line 
1#!/usr/bin/python
2
3import getafsgroups
4import re
5import string
6from sipb_xen_database import Machine, NIC
7from webcommon import InvalidInput, g
8
9MAX_MEMORY_TOTAL = 512
10MAX_MEMORY_SINGLE = 256
11MIN_MEMORY_SINGLE = 16
12MAX_DISK_TOTAL = 50
13MAX_DISK_SINGLE = 50
14MIN_DISK_SINGLE = 0.1
15MAX_VMS_TOTAL = 10
16MAX_VMS_ACTIVE = 4
17
18def getMachinesByOwner(user, machine=None):
19    """Return the machines owned by the same as a machine.
20   
21    If the machine is None, return the machines owned by the same
22    user.
23    """
24    if machine:
25        owner = machine.owner
26    else:
27        owner = user
28    return Machine.select_by(owner=owner)
29
30def maxMemory(user, machine=None, on=True):
31    """Return the maximum memory for a machine or a user.
32
33    If machine is None, return the memory available for a new
34    machine.  Else, return the maximum that machine can have.
35
36    on is whether the machine should be turned on.  If false, the max
37    memory for the machine to change to, if it is left off, is
38    returned.
39    """
40    if not on:
41        return MAX_MEMORY_SINGLE
42    if machine.memory > MAX_MEMORY_SINGLE:
43        # If they've been blessed, let them have it
44        return machine.memory
45    machines = getMachinesByOwner(user, machine)
46    active_machines = [x for x in machines if g.uptimes[x]]
47    mem_usage = sum([x.memory for x in active_machines if x != machine])
48    return min(MAX_MEMORY_SINGLE, MAX_MEMORY_TOTAL-mem_usage)
49
50def maxDisk(user, machine=None):
51    machines = getMachinesByOwner(user, machine)
52    disk_usage = sum([sum([y.size for y in x.disks])
53                      for x in machines if x != machine])
54    return min(MAX_DISK_SINGLE, MAX_DISK_TOTAL-disk_usage/1024.)
55
56def cantAddVm(user):
57    machines = getMachinesByOwner(user)
58    active_machines = [x for x in machines if g.uptimes[x]]
59    if len(machines) >= MAX_VMS_TOTAL:
60        return 'You have too many VMs to create a new one.'
61    if len(active_machines) >= MAX_VMS_ACTIVE:
62        return ('You already have the maximum number of VMs turned on.  '
63                'To create more, turn one off.')
64    return False
65
66def validAddVm(user):
67    reason = cantAddVm(user)
68    if reason:
69        raise InvalidInput('create', True, reason)
70    return True
71
72def haveAccess(user, machine):
73    """Return whether a user has administrative access to a machine"""
74    if user == 'moo':
75        return True
76    if user in (machine.administrator, machine.owner):
77        return True
78    if getafsgroups.checkAfsGroup(user, machine.administrator, 
79                                  'athena.mit.edu'): #XXX Cell?
80        return True
81    if not getafsgroups.notLockerOwner(user, machine.owner):
82        return True
83    return owns(user, machine)
84
85def owns(user, machine):
86    """Return whether a user owns a machine"""
87    if user == 'moo':
88        return True
89    return not getafsgroups.notLockerOwner(user, machine.owner)
90
91def validMachineName(name):
92    """Check that name is valid for a machine name"""
93    if not name:
94        return False
95    charset = string.ascii_letters + string.digits + '-_'
96    if name[0] in '-_' or len(name) > 22:
97        return False
98    for x in name:
99        if x not in charset:
100            return False
101    return True
102
103def validMemory(user, memory, machine=None, on=True):
104    """Parse and validate limits for memory for a given user and machine.
105
106    on is whether the memory must be valid after the machine is
107    switched on.
108    """
109    try:
110        memory = int(memory)
111        if memory < MIN_MEMORY_SINGLE:
112            raise ValueError
113    except ValueError:
114        raise InvalidInput('memory', memory, 
115                           "Minimum %s MiB" % MIN_MEMORY_SINGLE)
116    if memory > maxMemory(user, machine, on):
117        raise InvalidInput('memory', memory,
118                           'Maximum %s MiB for %s' % (maxMemory(user, machine),
119                                                      user))
120    return memory
121
122def validDisk(user, disk, machine=None):
123    """Parse and validate limits for disk for a given user and machine."""
124    try:
125        disk = float(disk)
126        if disk > maxDisk(user, machine):
127            raise InvalidInput('disk', disk,
128                               "Maximum %s G" % maxDisk(user, machine))
129        disk = int(disk * 1024)
130        if disk < MIN_DISK_SINGLE * 1024:
131            raise ValueError
132    except ValueError:
133        raise InvalidInput('disk', disk,
134                           "Minimum %s GiB" % MIN_DISK_SINGLE)
135    return disk
136           
137def testMachineId(user, machine_id, exists=True):
138    """Parse, validate and check authorization for a given user and machine.
139
140    If exists is False, don't check that it exists.
141    """
142    if machine_id is None:
143        raise InvalidInput('machine_id', machine_id, 
144                           "Must specify a machine ID.")
145    try:
146        machine_id = int(machine_id)
147    except ValueError:
148        raise InvalidInput('machine_id', machine_id, "Must be an integer.")
149    machine = Machine.get(machine_id)
150    if exists and machine is None:
151        raise InvalidInput('machine_id', machine_id, "Does not exist.")
152    if machine is not None and not haveAccess(user, machine):
153        raise InvalidInput('machine_id', machine_id,
154                           "You do not have access to this machine.")
155    return machine
156
157def testAdmin(user, admin, machine):
158    if admin in (None, machine.administrator):
159        return None
160    if admin == user:
161        return admin
162    if getafsgroups.checkAfsGroup(user, admin, 'athena.mit.edu'):
163        return admin
164    if getafsgroups.checkAfsGroup(user, 'system:'+admin,
165                                  'athena.mit.edu'):
166        return 'system:'+admin
167    return admin
168   
169def testOwner(user, owner, machine=None):
170    if owner == user or machine is not None and owner == machine.owner:
171        return owner
172    if owner is None:
173        raise InvalidInput('owner', owner, "Owner must be specified")
174    value = getafsgroups.notLockerOwner(user, owner)
175    if not value:
176        return owner
177    raise InvalidInput('owner', owner, value)
178
179def testContact(user, contact, machine=None):
180    if contact in (None, machine.contact):
181        return None
182    if not re.match("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$", contact, re.I):
183        raise InvalidInput('contact', contact, "Not a valid email.")
184    return contact
185
186def testDisk(user, disksize, machine=None):
187    return disksize
188
189def testName(user, name, machine=None):
190    if name in (None, machine.name):
191        return None
192    if not Machine.select_by(name=name):
193        return name
194    raise InvalidInput('name', name, "Name is already taken.")
195
196def testHostname(user, hostname, machine):
197    for nic in machine.nics:
198        if hostname == nic.hostname:
199            return hostname
200    # check if doesn't already exist
201    if NIC.select_by(hostname=hostname):
202        raise InvalidInput('hostname', hostname,
203                           "Already exists")
204    if not re.match("^[A-Z0-9-]{1,22}$", hostname, re.I):
205        raise InvalidInput('hostname', hostname, "Not a valid hostname; "
206                           "must only use number, letters, and dashes.")
207    return hostname
Note: See TracBrowser for help on using the repository browser.