#!/usr/bin/python import getafsgroups import re import string from sipb_xen_database import Machine, NIC from webcommon import InvalidInput, g MAX_MEMORY_TOTAL = 512 MAX_MEMORY_SINGLE = 256 MIN_MEMORY_SINGLE = 16 MAX_DISK_TOTAL = 50 MAX_DISK_SINGLE = 50 MIN_DISK_SINGLE = 0.1 MAX_VMS_TOTAL = 10 MAX_VMS_ACTIVE = 4 def getMachinesByOwner(user, machine=None): """Return the machines owned by the same as a machine. If the machine is None, return the machines owned by the same user. """ if machine: owner = machine.owner else: owner = user return Machine.select_by(owner=owner) def maxMemory(user, machine=None, on=True): """Return the maximum memory for a machine or a user. If machine is None, return the memory available for a new machine. Else, return the maximum that machine can have. on is whether the machine should be turned on. If false, the max memory for the machine to change to, if it is left off, is returned. """ if not on: return MAX_MEMORY_SINGLE if machine.memory > MAX_MEMORY_SINGLE: # If they've been blessed, let them have it return machine.memory machines = getMachinesByOwner(user, machine) active_machines = [x for x in machines if g.uptimes[x]] mem_usage = sum([x.memory for x in active_machines if x != machine]) return min(MAX_MEMORY_SINGLE, MAX_MEMORY_TOTAL-mem_usage) def maxDisk(user, machine=None): machines = getMachinesByOwner(user, machine) disk_usage = sum([sum([y.size for y in x.disks]) for x in machines if x != machine]) return min(MAX_DISK_SINGLE, MAX_DISK_TOTAL-disk_usage/1024.) def cantAddVm(user): machines = getMachinesByOwner(user) active_machines = [x for x in machines if g.uptimes[x]] if len(machines) >= MAX_VMS_TOTAL: return 'You have too many VMs to create a new one.' if len(active_machines) >= MAX_VMS_ACTIVE: return ('You already have the maximum number of VMs turned on. ' 'To create more, turn one off.') return False def validAddVm(user): reason = cantAddVm(user) if reason: raise InvalidInput('create', True, reason) return True def haveAccess(user, machine): """Return whether a user has administrative access to a machine""" if user == 'moo': return True if user in (machine.administrator, machine.owner): return True if getafsgroups.checkAfsGroup(user, machine.administrator, 'athena.mit.edu'): #XXX Cell? return True if not getafsgroups.notLockerOwner(user, machine.owner): return True return owns(user, machine) def owns(user, machine): """Return whether a user owns a machine""" if user == 'moo': return True return not getafsgroups.notLockerOwner(user, machine.owner) def validMachineName(name): """Check that name is valid for a machine name""" if not name: return False charset = string.ascii_letters + string.digits + '-_' if name[0] in '-_' or len(name) > 22: return False for x in name: if x not in charset: return False return True def validMemory(user, memory, machine=None, on=True): """Parse and validate limits for memory for a given user and machine. on is whether the memory must be valid after the machine is switched on. """ try: memory = int(memory) if memory < MIN_MEMORY_SINGLE: raise ValueError except ValueError: raise InvalidInput('memory', memory, "Minimum %s MiB" % MIN_MEMORY_SINGLE) if memory > maxMemory(user, machine, on): raise InvalidInput('memory', memory, 'Maximum %s MiB for %s' % (maxMemory(user, machine), user)) return memory def validDisk(user, disk, machine=None): """Parse and validate limits for disk for a given user and machine.""" try: disk = float(disk) if disk > maxDisk(user, machine): raise InvalidInput('disk', disk, "Maximum %s G" % maxDisk(user, machine)) disk = int(disk * 1024) if disk < MIN_DISK_SINGLE * 1024: raise ValueError except ValueError: raise InvalidInput('disk', disk, "Minimum %s GiB" % MIN_DISK_SINGLE) return disk def testMachineId(user, machine_id, exists=True): """Parse, validate and check authorization for a given user and machine. If exists is False, don't check that it exists. """ if machine_id is None: raise InvalidInput('machine_id', machine_id, "Must specify a machine ID.") try: machine_id = int(machine_id) except ValueError: raise InvalidInput('machine_id', machine_id, "Must be an integer.") machine = Machine.get(machine_id) if exists and machine is None: raise InvalidInput('machine_id', machine_id, "Does not exist.") if machine is not None and not haveAccess(user, machine): raise InvalidInput('machine_id', machine_id, "You do not have access to this machine.") return machine def testAdmin(user, admin, machine): if admin in (None, machine.administrator): return None if admin == user: return admin if getafsgroups.checkAfsGroup(user, admin, 'athena.mit.edu'): return admin if getafsgroups.checkAfsGroup(user, 'system:'+admin, 'athena.mit.edu'): return 'system:'+admin return admin def testOwner(user, owner, machine=None): if owner == user or machine is not None and owner == machine.owner: return owner if owner is None: raise InvalidInput('owner', owner, "Owner must be specified") value = getafsgroups.notLockerOwner(user, owner) if not value: return owner raise InvalidInput('owner', owner, value) def testContact(user, contact, machine=None): if contact in (None, machine.contact): return None if not re.match("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$", contact, re.I): raise InvalidInput('contact', contact, "Not a valid email.") return contact def testDisk(user, disksize, machine=None): return disksize def testName(user, name, machine=None): if name in (None, machine.name): return None if not Machine.select_by(name=name): return name raise InvalidInput('name', name, "Name is already taken.") def testHostname(user, hostname, machine): for nic in machine.nics: if hostname == nic.hostname: return hostname # check if doesn't already exist if NIC.select_by(hostname=hostname): raise InvalidInput('hostname', hostname, "Already exists") if not re.match("^[A-Z0-9-]{1,22}$", hostname, re.I): raise InvalidInput('hostname', hostname, "Not a valid hostname; " "must only use number, letters, and dashes.") return hostname