Changeset 119
- Timestamp:
- Oct 7, 2007, 5:32:53 PM (17 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/web/templates/main.py
r118 r119 6 6 import string 7 7 import subprocess 8 import re 8 9 import time 9 10 import cPickle 10 11 import base64 12 import sha 13 import hmac 11 14 12 15 print 'Content-Type: text/html\n' … … 18 21 import random 19 22 23 class MyException(Exception): 24 pass 25 20 26 # ... and stolen from xend/uuid.py 21 27 def randomUUID(): … … 28 34 "%02x" * 6]) % tuple(u) 29 35 30 31 36 def maxMemory(user): 32 37 return 256 33 38 39 def maxDisk(user): 40 return 10.0 41 34 42 def haveAccess(user, machine): 35 43 return True 36 44 37 38 def error(op, user, fields, errorMessage): 39 d = dict(op=op, 40 user=user, 41 errorMessage=errorMessage) 42 print Template(file='error.tmpl', 43 searchList=d); 45 def error(op, user, fields, err): 46 d = dict(op=op, user=user, errorMessage=str(err)) 47 print Template(file='error.tmpl', searchList=d); 44 48 45 49 def validMachineName(name): 50 """Check that name is valid for a machine name""" 46 51 if not name: 47 52 return False 48 charset = string.ascii_letters + string.digits + '- '49 if name[0] == '-' or len(name) > 22:53 charset = string.ascii_letters + string.digits + '-_' 54 if name[0] in '-_' or len(name) > 22: 50 55 return False 51 56 return all(x in charset for x in name) 52 57 53 def kinit(): 54 keytab = '/etc/tabbott.keytab' 55 username = 'tabbott/extra' 56 p = subprocess.Popen(['kinit', "-k", "-t", keytab, 57 username]) 58 p.wait() 58 def kinit(username = 'tabbott/extra', keytab = '/etc/tabbott.keytab'): 59 """Kinit with a given username and keytab""" 60 61 p = subprocess.Popen(['kinit', "-k", "-t", keytab, username]) 62 e = p.wait() 63 if e: 64 raise MyException("Error %s in kinit" % e) 59 65 60 66 def checkKinit(): 67 """If we lack tickets, kinit.""" 61 68 p = subprocess.Popen(['klist', '-s']) 62 69 if p.wait(): 63 70 kinit() 64 71 65 def remctl(*args): 72 def remctl(*args, **kws): 73 """Perform a remctl and return the output. 74 75 kinits if necessary, and outputs errors to stderr. 76 """ 66 77 checkKinit() 67 78 p = subprocess.Popen(['remctl', 'black-mesa.mit.edu'] … … 69 80 stdout=subprocess.PIPE, 70 81 stderr=subprocess.PIPE) 82 if kws.get('err'): 83 return p.stdout.read(), p.stderr.read() 71 84 if p.wait(): 72 85 print >> sys.stderr, 'ERROR on remctl ', args 73 86 print >> sys.stderr, p.stderr.read() 87 return p.stdout.read() 74 88 75 89 def makeDisks(): 76 remctl('lvcreate','all') 90 """Update the lvm partitions to include all disks in the database.""" 91 remctl('web', 'lvcreate') 77 92 78 93 def bootMachine(machine, cdtype): 94 """Boot a machine with a given boot CD. 95 96 If cdtype is None, give no boot cd. Otherwise, it is the string 97 id of the CD (e.g. 'gutsy_i386') 98 """ 79 99 if cdtype is not None: 80 remctl(' vmboot', 'cdrom', str(machine.name),100 remctl('web', 'vmboot', machine.name, 81 101 cdtype) 82 102 else: 83 remctl('vmboot', 'cdrom', str(machine.name)) 103 remctl('web', 'vmboot', machine.name) 104 105 def registerMachine(machine): 106 """Register a machine to be controlled by the web interface""" 107 remctl('web', 'register', machine.name) 108 109 def parseStatus(s): 110 """Parse a status string into nested tuples of strings. 111 112 s = output of xm list --long <machine_name> 113 """ 114 values = re.split('([()])', s) 115 stack = [[]] 116 for v in values[2:-2]: #remove initial and final '()' 117 if not v: 118 continue 119 v = v.strip() 120 if v == '(': 121 stack.append([]) 122 elif v == ')': 123 stack[-2].append(stack[-1]) 124 stack.pop() 125 else: 126 if not v: 127 continue 128 stack[-1].extend(v.split()) 129 return stack[-1] 130 131 def statusInfo(machine): 132 value_string, err_string = remctl('list-long', machine.name, err=True) 133 if 'Unknown command' in err_string: 134 raise MyException("ERROR in remctl list-long %s is not registered" % (machine.name,)) 135 elif 'does not exist' in err_string: 136 return None 137 elif err_string: 138 raise MyException("ERROR in remctl list-long %s: %s" % (machine.name, err_string)) 139 status = parseStatus(value_string) 140 return status 141 142 def hasVnc(status): 143 if status is None: 144 return False 145 for l in status: 146 if l[0] == 'device' and l[1][0] == 'vfb': 147 d = dict(l[1][1:]) 148 return 'location' in d 149 return False 84 150 85 151 def createVm(user, name, memory, disk, is_hvm, cdrom): … … 115 181 raise 116 182 makeDisks() 183 registerMachine(machine) 117 184 # tell it to boot with cdrom 118 185 bootMachine(machine, cdrom) … … 123 190 name = fields.getfirst('name') 124 191 if not validMachineName(name): 125 return error('create', user, fields, 126 "Invalid name '%s'" % name) 192 raise MyException("Invalid name '%s'" % name) 127 193 name = name.lower() 128 194 129 195 if Machine.get_by(name=name): 130 return error('create', user, fields, 131 "A machine named '%s' already exists" % name) 196 raise MyException("A machine named '%s' already exists" % name) 132 197 133 198 memory = fields.getfirst('memory') … … 137 202 raise ValueError 138 203 except ValueError: 139 return error('create', user, fields, 140 "Invalid memory amount") 204 raise MyException("Invalid memory amount") 141 205 if memory > maxMemory(user): 142 return error('create', user, fields, 143 "Too much memory requested") 206 raise MyException("Too much memory requested") 144 207 145 208 disk = fields.getfirst('disk') … … 150 213 raise ValueError 151 214 except ValueError: 152 return error('create', user, fields, 153 "Invalid disk amount") 215 raise MyException("Invalid disk amount") 216 if disk > maxDisk(user): 217 raise MyException("Too much disk requested") 154 218 155 219 vm_type = fields.getfirst('vmtype') 156 220 if vm_type not in ('hvm', 'paravm'): 157 return error('create', user, fields, 158 "Invalid vm type '%s'" % vm_type) 221 raise MyException("Invalid vm type '%s'" % vm_type) 159 222 is_hvm = (vm_type == 'hvm') 160 223 161 224 cdrom = fields.getfirst('cdrom') 162 225 if cdrom is not None and not CDROM.get(cdrom): 163 return error('create', user, fields, 164 "Invalid cdrom type '%s'" % cdrom) 226 raise MyException("Invalid cdrom type '%s'" % cdrom) 165 227 166 228 machine = createVm(user, name, memory, disk, is_hvm, cdrom) 167 229 if isinstance(machine, basestring): 168 return error('create', user, fields, 169 machine) 230 raise MyException(machine) 170 231 d = dict(user=user, 171 232 machine=machine) … … 175 236 def listVms(user, fields): 176 237 machines = Machine.select() 238 status = statusInfo(machines) 239 has_vnc = {} 240 for m in machines: 241 on[m.name] = status[m.name] is not None 242 has_vnc[m.name] = hasVnc(status[m.name]) 177 243 d = dict(user=user, 244 maxmem=maxMemory(user), 245 maxdisk=maxDisk(user), 178 246 machines=machines, 247 status=status, 248 has_vnc=has_vnc, 179 249 cdroms=CDROM.select()) 180 181 250 print Template(file='list.tmpl', searchList=d) 182 251 183 252 def testMachineId(user, machineId, exists=True): 184 253 if machineId is None: 185 error('vnc', user, fields, 186 "No machine ID specified") 187 return False 254 raise MyException("No machine ID specified") 188 255 try: 189 256 machineId = int(machineId) 190 257 except ValueError: 191 error('vnc', user, fields, 192 "Invalid machine ID '%s'" 193 % machineId) 194 return False 258 raise MyException("Invalid machine ID '%s'" % machineId) 195 259 machine = Machine.get(machineId) 196 260 if exists and machine is None: 197 error('vnc', user, fields, 198 "No such machine ID '%s'" 199 % machineId) 200 return False 261 raise MyException("No such machine ID '%s'" % machineId) 201 262 if not haveAccess(user, machine): 202 error('vnc', user, fields, 203 "No access to machine ID '%s'" 204 % machineId) 205 return False 263 raise MyException("No access to machine ID '%s'" % machineId) 206 264 return machine 207 265 208 266 def vnc(user, fields): 267 """VNC applet page. 268 269 Note that due to same-domain restrictions, the applet connects to 270 the webserver, which needs to forward those requests to the xen 271 server. The Xen server runs another proxy that (1) authenticates 272 and (2) finds the correct port for the VM. 273 274 You might want iptables like: 275 276 -t nat -A PREROUTING -s ! 18.181.0.60 -i eth1 -p tcp -m tcp --dport 10003 -j DNAT --to-destination 18.181.0.60:10003 277 -t nat -A POSTROUTING -d 18.181.0.60 -o eth1 -p tcp -m tcp --dport 10003 -j SNAT --to-source 18.187.7.142 278 -A FORWARD -d 18.181.0.60 -i eth1 -o eth1 -p tcp -m tcp --dport 10003 -j ACCEPT 279 """ 209 280 machine = testMachineId(user, fields.getfirst('machine_id')) 210 if machine is None: #gave error page already 211 return 281 #XXX fix 212 282 213 283 TOKEN_KEY = "0M6W0U1IXexThi5idy8mnkqPKEq1LtEnlK/pZSn0cDrN" … … 226 296 d = dict(user=user, 227 297 machine=machine, 228 hostname= 'localhost',298 hostname=os.environ.get('SERVER_NAME', 'localhost'), 229 299 authtoken=token) 230 300 print Template(file='vnc.tmpl', … … 233 303 def info(user, fields): 234 304 machine = testMachineId(user, fields.getfirst('machine_id')) 235 if machine is None: #gave error page already236 return237 238 305 d = dict(user=user, 239 306 machine=machine) … … 254 321 connect('postgres://sipb-xen@sipb-xen-dev/sipb_xen') 255 322 operation = os.environ.get('PATH_INFO', '') 323 if not operation: 324 pass 325 #XXX do redirect 326 256 327 if operation.startswith('/'): 257 328 operation = operation[1:] … … 263 334 error(operation, u, e, 264 335 "Invalid operation '%'" % operation)) 265 fun(u, fields) 336 try: 337 fun(u, fields) 338 except MyException, err: 339 error(operation, u, fields, err)
Note: See TracChangeset
for help on using the changeset viewer.