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

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

Fix bug resulting from partial uptimes list from r261

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 machine is not None and machine.memory > MAX_MEMORY_SINGLE:
41        # If they've been blessed, let them have it
42        return machine.memory
43    if not on:
44        return MAX_MEMORY_SINGLE
45    machines = getMachinesByOwner(user, machine)
46    active_machines = [x for x in machines if g.uptimes.get(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.get(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 in (machine.administrator, machine.owner):
75        return True
76    if getafsgroups.checkAfsGroup(user, machine.administrator, 
77                                  'athena.mit.edu'): #XXX Cell?
78        return True
79    if not getafsgroups.notLockerOwner(user, machine.owner):
80        return True
81    return owns(user, machine)
82
83def owns(user, machine):
84    """Return whether a user owns a machine"""
85    return not getafsgroups.notLockerOwner(user, machine.owner)
86
87def validMachineName(name):
88    """Check that name is valid for a machine name"""
89    if not name:
90        return False
91    charset = string.ascii_letters + string.digits + '-_'
92    if name[0] in '-_' or len(name) > 22:
93        return False
94    for x in name:
95        if x not in charset:
96            return False
97    return True
98
99def validMemory(user, memory, machine=None, on=True):
100    """Parse and validate limits for memory for a given user and machine.
101
102    on is whether the memory must be valid after the machine is
103    switched on.
104    """
105    try:
106        memory = int(memory)
107        if memory < MIN_MEMORY_SINGLE:
108            raise ValueError
109    except ValueError:
110        raise InvalidInput('memory', memory, 
111                           "Minimum %s MiB" % MIN_MEMORY_SINGLE)
112    if memory > maxMemory(user, machine, on):
113        raise InvalidInput('memory', memory,
114                           'Maximum %s MiB for %s' % (maxMemory(user, machine),
115                                                      user))
116    return memory
117
118def validDisk(user, disk, machine=None):
119    """Parse and validate limits for disk for a given user and machine."""
120    try:
121        disk = float(disk)
122        if disk > maxDisk(user, machine):
123            raise InvalidInput('disk', disk,
124                               "Maximum %s G" % maxDisk(user, machine))
125        disk = int(disk * 1024)
126        if disk < MIN_DISK_SINGLE * 1024:
127            raise ValueError
128    except ValueError:
129        raise InvalidInput('disk', disk,
130                           "Minimum %s GiB" % MIN_DISK_SINGLE)
131    return disk
132           
133def testMachineId(user, machine_id, exists=True):
134    """Parse, validate and check authorization for a given user and machine.
135
136    If exists is False, don't check that it exists.
137    """
138    if machine_id is None:
139        raise InvalidInput('machine_id', machine_id, 
140                           "Must specify a machine ID.")
141    try:
142        machine_id = int(machine_id)
143    except ValueError:
144        raise InvalidInput('machine_id', machine_id, "Must be an integer.")
145    machine = Machine.get(machine_id)
146    if exists and machine is None:
147        raise InvalidInput('machine_id', machine_id, "Does not exist.")
148    if machine is not None and not haveAccess(user, machine):
149        raise InvalidInput('machine_id', machine_id,
150                           "You do not have access to this machine.")
151    return machine
152
153def testAdmin(user, admin, machine):
154    if admin in (None, machine.administrator):
155        return None
156    if admin == user:
157        return admin
158    if getafsgroups.checkAfsGroup(user, admin, 'athena.mit.edu'):
159        return admin
160    if getafsgroups.checkAfsGroup(user, 'system:'+admin,
161                                  'athena.mit.edu'):
162        return 'system:'+admin
163    return admin
164   
165def testOwner(user, owner, machine=None):
166    if owner == user or machine is not None and owner == machine.owner:
167        return owner
168    if owner is None:
169        raise InvalidInput('owner', owner, "Owner must be specified")
170    value = getafsgroups.notLockerOwner(user, owner)
171    if not value:
172        return owner
173    raise InvalidInput('owner', owner, value)
174
175def testContact(user, contact, machine=None):
176    if contact in (None, machine.contact):
177        return None
178    if not re.match("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$", contact, re.I):
179        raise InvalidInput('contact', contact, "Not a valid email.")
180    return contact
181
182def testDisk(user, disksize, machine=None):
183    return disksize
184
185def testName(user, name, machine=None):
186    if name in (None, machine.name):
187        return None
188    if not Machine.select_by(name=name):
189        return name
190    raise InvalidInput('name', name, "Name is already taken.")
191
192def testHostname(user, hostname, machine):
193    for nic in machine.nics:
194        if hostname == nic.hostname:
195            return hostname
196    # check if doesn't already exist
197    if NIC.select_by(hostname=hostname):
198        raise InvalidInput('hostname', hostname,
199                           "Already exists")
200    if not re.match("^[A-Z0-9-]{1,22}$", hostname, re.I):
201        raise InvalidInput('hostname', hostname, "Not a valid hostname; "
202                           "must only use number, letters, and dashes.")
203    return hostname
Note: See TracBrowser for help on using the repository browser.