7a6ec8b711c7a897a97b969dd54d1a7d8d7388d6
[invirt/packages/invirt-web.git] / templates / main.py
1 #!/usr/bin/python
2
3 import sys
4 import cgi
5 import os
6 import string
7 import subprocess
8 import time
9 import cPickle
10 import base64
11
12 print 'Content-Type: text/html\n'
13 sys.stderr = sys.stdout
14 sys.path.append('/home/ecprice/.local/lib/python2.5/site-packages')
15
16 from Cheetah.Template import Template
17 from sipb_xen_database import *
18 import random
19
20 # ... and stolen from xend/uuid.py
21 def randomUUID():
22     """Generate a random UUID."""
23
24     return [ random.randint(0, 255) for _ in range(0, 16) ]
25
26 def uuidToString(u):
27     return "-".join(["%02x" * 4, "%02x" * 2, "%02x" * 2, "%02x" * 2,
28                      "%02x" * 6]) % tuple(u)
29
30
31 def maxMemory(user):
32     return 256
33
34 def haveAccess(user, machine):
35     return True
36
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);
44
45 def validMachineName(name):
46     if not name:
47         return False
48     charset = string.ascii_letters + string.digits + '-'
49     if name[0] == '-' or len(name) > 22:
50         return False
51     return all(x in charset for x in name)
52
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()
59
60 def checkKinit():
61     p = subprocess.Popen(['klist', '-s'])
62     if p.wait():
63         kinit()
64
65 def remctl(*args):
66     checkKinit()
67     p = subprocess.Popen(['remctl', 'black-mesa.mit.edu']
68                          + list(args),
69                          stdout=subprocess.PIPE,
70                          stderr=subprocess.PIPE)
71     if p.wait():
72         print >> sys.stderr, 'ERROR on remctl ', args
73         print >> sys.stderr, p.stderr.read()
74
75 def makeDisks():
76     remctl('lvcreate','all')
77
78 def bootMachine(machine, cdtype):
79     if cdtype is not None:
80         remctl('vmboot', 'cdrom', str(machine.name),
81                cdtype)
82     else:
83         remctl('vmboot', 'cdrom', str(machine.name))
84
85 def createVm(user, name, memory, disk, is_hvm, cdrom):
86     # put stuff in the table
87     transaction = ctx.current.create_transaction()
88     try:
89         res = meta.engine.execute('select nextval(\'"machines_machine_id_seq"\')')
90         id = res.fetchone()[0]
91         machine = Machine()
92         machine.machine_id = id
93         machine.name = name
94         machine.memory = memory
95         machine.owner = user.username
96         machine.contact = user.email
97         machine.uuid = uuidToString(randomUUID())
98         machine.boot_off_cd = True
99         machine_type = Type.get_by(hvm=is_hvm)
100         machine.type_id = machine_type.type_id
101         ctx.current.save(machine)
102         disk = Disk(machine.machine_id, 
103                     'hda', disk)
104         open = NIC.select_by(machine_id=None)
105         if not open: #No IPs left!
106             return "No IP addresses left!  Contact sipb-xen-dev@mit.edu"
107         nic = open[0]
108         nic.machine_id = machine.machine_id
109         nic.hostname = name
110         ctx.current.save(nic)    
111         ctx.current.save(disk)
112         transaction.commit()
113     except:
114         transaction.rollback()
115         raise
116     makeDisks()
117     # tell it to boot with cdrom
118     bootMachine(machine, cdrom)
119
120     return machine
121
122 def create(user, fields):
123     name = fields.getfirst('name')
124     if not validMachineName(name):
125         return error('create', user, fields,
126                      "Invalid name '%s'" % name)
127     name = name.lower()
128
129     if Machine.get_by(name=name):
130         return error('create', user, fields,
131                      "A machine named '%s' already exists" % name)
132     
133     memory = fields.getfirst('memory')
134     try:
135         memory = int(memory)
136         if memory <= 0:
137             raise ValueError
138     except ValueError:
139         return error('create', user, fields,
140                      "Invalid memory amount")
141     if memory > maxMemory(user):
142         return error('create', user, fields,
143                      "Too much memory requested")
144     
145     disk = fields.getfirst('disk')
146     try:
147         disk = float(disk)
148         disk = int(disk * 1024)
149         if disk <= 0:
150             raise ValueError
151     except ValueError:
152         return error('create', user, fields,
153                      "Invalid disk amount")
154     
155     vm_type = fields.getfirst('vmtype')
156     if vm_type not in ('hvm', 'paravm'):
157         return error('create', user, fields,
158                      "Invalid vm type '%s'"  % vm_type)    
159     is_hvm = (vm_type == 'hvm')
160
161     cdrom = fields.getfirst('cdrom')
162     if cdrom is not None and not CDROM.get(cdrom):
163         return error('create', user, fields,
164                      "Invalid cdrom type '%s'" % cdrom)    
165     
166     machine = createVm(user, name, memory, disk, is_hvm, cdrom)
167     if isinstance(machine, basestring):
168         return error('create', user, fields,
169                      machine)
170     d = dict(user=user,
171              machine=machine)
172     print Template(file='create.tmpl',
173                    searchList=d);
174
175 def listVms(user, fields):
176     machines = Machine.select()
177     d = dict(user=user,
178              machines=machines,
179              cdroms=CDROM.select())
180
181     print Template(file='list.tmpl', searchList=d)
182
183 def testMachineId(user, machineId, exists=True):
184     if machineId is None:
185         error('vnc', user, fields,
186               "No machine ID specified")
187         return False
188     try:
189         machineId = int(machineId)
190     except ValueError:
191         error('vnc', user, fields,
192               "Invalid machine ID '%s'" 
193               % machineId)
194         return False
195     machine = Machine.get(machineId)
196     if exists and machine is None:
197         error('vnc', user, fields,
198               "No such machine ID '%s'" 
199               % machineId)
200         return False
201     if not haveAccess(user, machine):
202         error('vnc', user, fields,
203               "No access to machine ID '%s'" 
204               % machineId)
205         return False
206     return machine
207
208 def vnc(user, fields):
209     machine = testMachineId(user, fields.getfirst('machine_id'))
210     if machine is None: #gave error page already
211         return
212     
213     TOKEN_KEY = "0M6W0U1IXexThi5idy8mnkqPKEq1LtEnlK/pZSn0cDrN"
214
215     data = {}
216     data["user"] = user
217     data["machine"]=machine
218     data["expires"]=time.time()+(5*60)
219     pickledData = cPickle.dumps(data)
220     m = hmac.new(TOKEN_KEY, digestmod=sha)
221     m.update(pickledData)
222     token = {'data': pickledData, 'digest': m.digest()}
223     token = cPickle.dumps(token)
224     token = base64.urlsafe_b64encode(token)
225     
226     d = dict(user=user,
227              machine=machine,
228              hostname='localhost',
229              authtoken=token)
230     print Template(file='vnc.tmpl',
231                    searchList=d)
232
233 def info(user, fields):
234     machine = testMachineId(user, fields.getfirst('machine_id'))
235     if machine is None: #gave error page already
236         return
237     
238     d = dict(user=user,
239              machine=machine)
240     print Template(file='info.tmpl',
241                    searchList=d)
242
243 mapping = dict(list=listVms,
244                vnc=vnc,
245                info=info,
246                create=create)
247
248 if __name__ == '__main__':
249     fields = cgi.FieldStorage()
250     class C:
251         username = "moo"
252         email = 'moo@cow.com'
253     u = C()
254     connect('postgres://sipb-xen@sipb-xen-dev/sipb_xen')
255     operation = os.environ.get('PATH_INFO', '')
256     if operation.startswith('/'):
257         operation = operation[1:]
258     if not operation:
259         operation = 'list'
260     
261     fun = mapping.get(operation, 
262                       lambda u, e:
263                           error(operation, u, e,
264                                 "Invalid operation '%'" % operation))
265     fun(u, fields)