005925c2cf4c9925b70101d870fdaac9da59b8e0
[invirt/packages/invirt-web.git] / templates / validation.py
1 #!/usr/bin/python
2
3 import getafsgroups
4 import re
5 import string
6 from sipb_xen_database import Machine, NIC
7 from webcommon import InvalidInput, g
8
9 MAX_MEMORY_TOTAL = 512
10 MAX_MEMORY_SINGLE = 256
11 MIN_MEMORY_SINGLE = 16
12 MAX_DISK_TOTAL = 50
13 MAX_DISK_SINGLE = 50
14 MIN_DISK_SINGLE = 0.1
15 MAX_VMS_TOTAL = 10
16 MAX_VMS_ACTIVE = 4
17
18 def 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.username
28     return Machine.select_by(owner=owner)
29
30 def 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     machines = getMachinesByOwner(user, machine)
43     active_machines = [x for x in machines if g.uptimes[x]]
44     mem_usage = sum([x.memory for x in active_machines if x != machine])
45     return min(MAX_MEMORY_SINGLE, MAX_MEMORY_TOTAL-mem_usage)
46
47 def maxDisk(user, machine=None):
48     machines = getMachinesByOwner(user, machine)
49     disk_usage = sum([sum([y.size for y in x.disks])
50                       for x in machines if x != machine])
51     return min(MAX_DISK_SINGLE, MAX_DISK_TOTAL-disk_usage/1024.)
52
53 def cantAddVm(user):
54     machines = getMachinesByOwner(user)
55     active_machines = [x for x in machines if g.uptimes[x]]
56     if len(machines) >= MAX_VMS_TOTAL:
57         return 'You have too many VMs to create a new one.'
58     if len(active_machines) >= MAX_VMS_ACTIVE:
59         return ('You already have the maximum number of VMs turned on.  '
60                 'To create more, turn one off.')
61     return False
62
63 def validAddVm(user):
64     reason = cantAddVm(user)
65     if reason:
66         raise InvalidInput('create', True, reason)
67     return True
68
69 def haveAccess(user, machine):
70     """Return whether a user has adminstrative access to a machine"""
71     if user.username == 'moo':
72         return True
73     if user.username in (machine.administrator, machine.owner):
74         return True
75     if getafsgroups.checkAfsGroup(user.username, machine.administrator, 
76                                   'athena.mit.edu'): #XXX Cell?
77         return True
78     if getafsgroups.checkLockerOwner(user.username, machine.owner):
79         return True
80     return owns(user, machine)
81
82 def owns(user, machine):
83     """Return whether a user owns a machine"""
84     if user.username == 'moo':
85         return True
86     return getafsgroups.checkLockerOwner(user.username, machine.owner)
87
88 def validMachineName(name):
89     """Check that name is valid for a machine name"""
90     if not name:
91         return False
92     charset = string.ascii_letters + string.digits + '-_'
93     if name[0] in '-_' or len(name) > 22:
94         return False
95     for x in name:
96         if x not in charset:
97             return False
98     return True
99
100 def validMemory(user, memory, machine=None, on=True):
101     """Parse and validate limits for memory for a given user and machine.
102
103     on is whether the memory must be valid after the machine is
104     switched on.
105     """
106     try:
107         memory = int(memory)
108         if memory < MIN_MEMORY_SINGLE:
109             raise ValueError
110     except ValueError:
111         raise InvalidInput('memory', memory, 
112                            "Minimum %s MiB" % MIN_MEMORY_SINGLE)
113     if memory > maxMemory(user, machine, on):
114         raise InvalidInput('memory', memory,
115                            'Maximum %s MiB' % maxMemory(user, machine))
116     return memory
117
118 def 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             
133 def 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
153 def testAdmin(user, admin, machine):
154     if admin in (None, machine.administrator):
155         return None
156     if admin == user.username:
157         return admin
158     if getafsgroups.checkAfsGroup(user.username, admin, 'athena.mit.edu'):
159         return admin
160     if getafsgroups.checkAfsGroup(user.username, 'system:'+admin,
161                                   'athena.mit.edu'):
162         return 'system:'+admin
163     return admin
164     
165 def testOwner(user, owner, machine):
166     if owner in (None, machine.owner):
167         return None
168     value = getafsgroups.checkLockerOwner(user.username, owner, verbose=True)
169     if not value:
170         return owner
171     raise InvalidInput('owner', owner, value)
172
173 def testContact(user, contact, machine=None):
174     if contact in (None, machine.contact):
175         return None
176     if not re.match("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$", contact, re.I):
177         raise InvalidInput('contact', contact, "Not a valid email.")
178     return contact
179
180 def testDisk(user, disksize, machine=None):
181     return disksize
182
183 def testName(user, name, machine=None):
184     if name in (None, machine.name):
185         return None
186     if not Machine.select_by(name=name):
187         return name
188     raise InvalidInput('name', name, "Name is already taken.")
189
190 def testHostname(user, hostname, machine):
191     for nic in machine.nics:
192         if hostname == nic.hostname:
193             return hostname
194     # check if doesn't already exist
195     if NIC.select_by(hostname=hostname):
196         raise InvalidInput('hostname', hostname,
197                            "Already exists")
198     if not re.match("^[A-Z0-9-]{1,22}$", hostname, re.I):
199         raise InvalidInput('hostname', hostname, "Not a valid hostname; "
200                            "must only use number, letters, and dashes.")
201     return hostname