root / api / handlers.py @ d08a5f6f
History | View | Annotate | Download (12.4 kB)
1 |
# vim: ts=4 sts=4 et ai sw=4 fileencoding=utf-8
|
---|---|
2 |
#
|
3 |
# Copyright © 2010 Greek Research and Technology Network
|
4 |
#
|
5 |
|
6 |
from django.conf import settings |
7 |
from piston.handler import BaseHandler, AnonymousBaseHandler |
8 |
from synnefo.api.faults import fault, noContent, accepted, created |
9 |
from synnefo.api.helpers import instance_to_server, paginator |
10 |
from synnefo.util.rapi import GanetiRapiClient, GanetiApiError |
11 |
from synnefo.db.models import * |
12 |
from util.rapi import GanetiRapiClient |
13 |
|
14 |
|
15 |
if settings.GANETI_CLUSTER_INFO:
|
16 |
rapi = GanetiRapiClient(*settings.GANETI_CLUSTER_INFO) |
17 |
else:
|
18 |
rapi = None
|
19 |
|
20 |
backend_prefix_id = settings.BACKEND_PREFIX_ID |
21 |
|
22 |
VERSIONS = [ |
23 |
{ |
24 |
"status": "CURRENT", |
25 |
"id": "v1.0", |
26 |
"docURL" : "http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20090714.pdf ", |
27 |
"wadl" : "http://docs.rackspacecloud.com/servers/api/v1.0/application.wadl" |
28 |
}, |
29 |
] |
30 |
|
31 |
|
32 |
class VersionHandler(AnonymousBaseHandler): |
33 |
allowed_methods = ('GET',)
|
34 |
|
35 |
def read(self, request, number=None): |
36 |
if number is None: |
37 |
versions = map(lambda v: { |
38 |
"status": v["status"], |
39 |
"id": v["id"], |
40 |
}, VERSIONS) |
41 |
return { "versions": versions } |
42 |
else:
|
43 |
for version in VERSIONS: |
44 |
if version["id"] == number: |
45 |
return { "version": version } |
46 |
raise fault.itemNotFound
|
47 |
|
48 |
|
49 |
class ServerHandler(BaseHandler): |
50 |
allowed_methods = ('GET', 'POST', 'PUT', 'DELETE') |
51 |
|
52 |
def read(self, request, id=None): |
53 |
from time import sleep |
54 |
sleep(1)
|
55 |
#TODO: delete the sleep once the mock objects are removed
|
56 |
if id is None: |
57 |
return self.read_all(request) |
58 |
elif id == "detail": |
59 |
return self.read_all(request, detail=True) |
60 |
else:
|
61 |
return self.read_one(request, id) |
62 |
|
63 |
def read_one(self, request, id): |
64 |
if not rapi: # No ganeti backend. Return mock objects |
65 |
servers = VirtualMachine.objects.all()[0]
|
66 |
return { "server": servers[0] } |
67 |
try:
|
68 |
instance = rapi.GetInstance(id)
|
69 |
servers = VirtualMachine.objects.all()[0]
|
70 |
return { "server": instance_to_server(instance) } |
71 |
except GanetiApiError:
|
72 |
raise fault.itemNotFound
|
73 |
|
74 |
@paginator
|
75 |
def read_all(self, request, detail=False): |
76 |
if not rapi: # No ganeti backend. Return mock objects |
77 |
if detail:
|
78 |
virtual_servers = VirtualMachine.objects.filter(owner=User.objects.all()[0])
|
79 |
#get the first user, since we don't have any user data yet
|
80 |
virtual_servers_list = [{'status': server.state, 'flavorId': server.flavor, \ |
81 |
'name': server.name, 'id': server.id, 'imageId': server.imageid, |
82 |
'metadata': {'Server_Label': server.description, \ |
83 |
'hostId': '9e107d9d372bb6826bd81d3542a419d6', \ |
84 |
'addresses': {'public': ['67.23.10.133'], 'private': ['10.176.42.17']}} \ |
85 |
for server in virtual_servers] |
86 |
#pass some fake data regarding ip, since we don't have any such data
|
87 |
return { "servers": virtual_servers_list } |
88 |
else:
|
89 |
virtual_servers = VirtualMachine.objects.filter(owner=User.objects.all()[0])
|
90 |
return { "servers": [ { "id": s.id, "name": s.name } for s in virtual_servers ] } |
91 |
|
92 |
#ganeti can't ask for instances of user X. The DB is responsible for this
|
93 |
#also here we ask for the instances of the first user, since the user system is not ready
|
94 |
if not detail: |
95 |
virtual_servers = VirtualMachine.objects.filter(owner=User.objects.all()[0])
|
96 |
return { "servers": [ { "id": s.id, "name": s.name } for s in virtual_servers ] } |
97 |
else:
|
98 |
virtual_servers = VirtualMachine.objects.filter(owner=User.objects.all()[0])
|
99 |
virtual_servers_list = [{'status': server.state, 'flavorId': server.flavor, \ |
100 |
'name': server.name, 'id': server.id, 'imageId': server.imageid, |
101 |
'metadata': {'Server_Label': server.description, \ |
102 |
'hostId': '9e107d9d372bb6826bd81d3542a419d6', \ |
103 |
'addresses': {'public': ['67.23.10.133'], 'private': ['10.176.42.17']}} \ |
104 |
for server in virtual_servers] |
105 |
#pass some fake data regarding ip, since we don't have any such data
|
106 |
return { "servers": virtual_servers_list } |
107 |
|
108 |
|
109 |
def create(self, request): |
110 |
print 'create machine was called' |
111 |
rapi.CreateInstance('create', 'machine-unwebXYZ', 'plain', [{"size": 5120}], [{}], os='debootstrap+default', ip_check=False, name_check=False,pnode="store68", beparams={'auto_balance': True, 'vcpus': 2, 'memory': 1024}) |
112 |
#TODO: replace with real data from request.POST
|
113 |
return accepted
|
114 |
|
115 |
def update(self, request, id): |
116 |
return noContent
|
117 |
|
118 |
def delete(self, request, id): |
119 |
machine = 'machine-XXX' #VirtualMachine.objects.get(id=id_from_instance_name(id)) |
120 |
print 'deleting machine %s' % machine |
121 |
rapi.DeleteInstance(machine.name) |
122 |
return accepted
|
123 |
|
124 |
|
125 |
class ServerAddressHandler(BaseHandler): |
126 |
allowed_methods = ('GET', 'PUT', 'DELETE') |
127 |
|
128 |
def read(self, request, id, type=None): |
129 |
"""List IP addresses for a server"""
|
130 |
|
131 |
if type is None: |
132 |
pass
|
133 |
elif type == "private": |
134 |
pass
|
135 |
elif type == "public": |
136 |
pass
|
137 |
return {}
|
138 |
|
139 |
def update(self, request, id, address): |
140 |
"""Share an IP address to another in the group"""
|
141 |
return accepted
|
142 |
|
143 |
def delete(self, request, id, address): |
144 |
"""Unshare an IP address"""
|
145 |
return accepted
|
146 |
|
147 |
|
148 |
class ServerActionHandler(BaseHandler): |
149 |
allowed_methods = ('POST', 'DELETE', 'GET', 'PUT') |
150 |
#TODO: remove GET/PUT
|
151 |
|
152 |
def read(self, request, id): |
153 |
return accepted
|
154 |
|
155 |
def create(self, request, id): |
156 |
"""Reboot, rebuild, resize, confirm resized, revert resized"""
|
157 |
machine = 'machine-XXX' #VirtualMachine.objects.get(id=id_from_instance_name(id)) |
158 |
reboot_request = request.POST.get('reboot', None) |
159 |
shutdown_request = request.POST.get('shutdown', None) |
160 |
if reboot_request:
|
161 |
print 'reboot was asked, with options: %s' % reboot_request |
162 |
rapi.RebootInstance(machine) |
163 |
elif shutdown_request:
|
164 |
print 'shutdown was asked, with options: %s' % shutdown_request |
165 |
rapi.ShutdownInstance(machine) |
166 |
return accepted
|
167 |
|
168 |
|
169 |
def delete(self, request, id): |
170 |
"""Delete an Instance"""
|
171 |
return accepted
|
172 |
|
173 |
def update(self, request, id): |
174 |
return noContent
|
175 |
|
176 |
|
177 |
|
178 |
#read is called on GET requests
|
179 |
#create is called on POST, and creates new objects, and should return them (or rc.CREATED.)
|
180 |
#update is called on PUT, and should update an existing product and return them (or rc.ALL_OK.)
|
181 |
#delete is called on DELETE, and should delete an existing object. Should not return anything, just rc.DELETED.'''
|
182 |
|
183 |
|
184 |
class ServerBackupHandler(BaseHandler): |
185 |
""" Backup Schedules are not implemented yet, return notImplemented """
|
186 |
allowed_methods = ('GET', 'POST', 'DELETE') |
187 |
|
188 |
def read(self, request, id): |
189 |
raise fault.notImplemented
|
190 |
|
191 |
def create(self, request, id): |
192 |
raise fault.notImplemented
|
193 |
|
194 |
def delete(self, request, id): |
195 |
raise fault.notImplemented
|
196 |
|
197 |
|
198 |
class FlavorHandler(BaseHandler): |
199 |
allowed_methods = ('GET',)
|
200 |
flavors = Flavor.objects.all() |
201 |
flavors = [ {'id': flavor.id, 'name': flavor.name, 'ram': flavor.ram, \ |
202 |
'disk': flavor.disk} for flavor in flavors] |
203 |
|
204 |
def read(self, request, id=None): |
205 |
"""
|
206 |
List flavors or retrieve one
|
207 |
|
208 |
Returns: OK
|
209 |
Faults: cloudServersFault, serviceUnavailable, unauthorized,
|
210 |
badRequest, itemNotFound
|
211 |
"""
|
212 |
if id is None: |
213 |
simple = map(lambda v: { |
214 |
"id": v['id'], |
215 |
"name": v['name'], |
216 |
}, self.flavors)
|
217 |
return { "flavors": simple } |
218 |
elif id == "detail": |
219 |
return { "flavors": self.flavors } |
220 |
else:
|
221 |
for flavor in self.flavors: |
222 |
if str(flavor['id']) == id: |
223 |
return { "flavor": flavor } |
224 |
raise fault.itemNotFound
|
225 |
|
226 |
|
227 |
class ImageHandler(BaseHandler): |
228 |
allowed_methods = ('GET', 'POST') |
229 |
|
230 |
def read(self, request, id=None): |
231 |
"""
|
232 |
List images or retrieve one
|
233 |
|
234 |
Returns: OK
|
235 |
Faults: cloudServersFault, serviceUnavailable, unauthorized,
|
236 |
badRequest, itemNotFound
|
237 |
"""
|
238 |
images = Image.objects.all() |
239 |
images = [ {'created': image.created.isoformat(), 'id': image.id, \ |
240 |
'name': image.name, 'updated': image.updated.isoformat(), \ |
241 |
'description': image.description, 'state': image.state, 'serverid': image.serverid, \ |
242 |
'vm_id': image.vm_id} for image in images] |
243 |
|
244 |
if rapi: # Images info is stored in the DB. Ganeti is not aware of this |
245 |
if id == "detail": |
246 |
return { "images": images } |
247 |
elif id is None: |
248 |
return { "images": [ { "id": s['id'], "name": s['name'] } for s in images ] } |
249 |
else:
|
250 |
return { "image": images[0] } |
251 |
# if id is None:
|
252 |
# return {}
|
253 |
# elif id == "detail":
|
254 |
# return {}
|
255 |
# else:
|
256 |
# raise fault.itemNotFound
|
257 |
|
258 |
def create(self, request): |
259 |
"""Create a new image"""
|
260 |
return accepted
|
261 |
|
262 |
|
263 |
class SharedIPGroupHandler(BaseHandler): |
264 |
allowed_methods = ('GET', 'POST', 'DELETE') |
265 |
|
266 |
def read(self, request, id=None): |
267 |
"""List Shared IP Groups"""
|
268 |
if id is None: |
269 |
return {}
|
270 |
elif id == "detail": |
271 |
return {}
|
272 |
else:
|
273 |
raise fault.itemNotFound
|
274 |
|
275 |
def create(self, request, id): |
276 |
"""Creates a new Shared IP Group"""
|
277 |
return created
|
278 |
|
279 |
def delete(self, request, id): |
280 |
"""Deletes a Shared IP Group"""
|
281 |
return noContent
|
282 |
|
283 |
|
284 |
class VirtualMachineGroupHandler(BaseHandler): |
285 |
allowed_methods = ('GET', 'POST', 'DELETE') |
286 |
|
287 |
def read(self, request, id=None): |
288 |
"""List Groups"""
|
289 |
vmgroups = VirtualMachineGroup.objects.all() |
290 |
vmgroups = [ {'id': vmgroup.id, \
|
291 |
'name': vmgroup.name, \
|
292 |
'server_id': [machine.id for machine in vmgroup.machines.all()] \ |
293 |
} for vmgroup in vmgroups] |
294 |
if rapi: # Group info is stored in the DB. Ganeti is not aware of this |
295 |
if id == "detail": |
296 |
return { "groups": vmgroups } |
297 |
elif id is None: |
298 |
return { "groups": [ { "id": s['id'], "name": s['name'] } for s in vmgroups ] } |
299 |
else:
|
300 |
return { "groups": vmgroups[0] } |
301 |
|
302 |
|
303 |
def create(self, request, id): |
304 |
"""Creates a Group"""
|
305 |
return created
|
306 |
|
307 |
def delete(self, request, id): |
308 |
"""Deletes a Group"""
|
309 |
return noContent
|
310 |
|
311 |
|
312 |
|
313 |
class LimitHandler(BaseHandler): |
314 |
allowed_methods = ('GET',)
|
315 |
|
316 |
# XXX: hookup with @throttle
|
317 |
|
318 |
rate = [ |
319 |
{ |
320 |
"verb" : "POST", |
321 |
"URI" : "*", |
322 |
"regex" : ".*", |
323 |
"value" : 10, |
324 |
"remaining" : 2, |
325 |
"unit" : "MINUTE", |
326 |
"resetTime" : 1244425439 |
327 |
}, |
328 |
{ |
329 |
"verb" : "POST", |
330 |
"URI" : "*/servers", |
331 |
"regex" : "^/servers", |
332 |
"value" : 25, |
333 |
"remaining" : 24, |
334 |
"unit" : "DAY", |
335 |
"resetTime" : 1244511839 |
336 |
}, |
337 |
{ |
338 |
"verb" : "PUT", |
339 |
"URI" : "*", |
340 |
"regex" : ".*", |
341 |
"value" : 10, |
342 |
"remaining" : 2, |
343 |
"unit" : "MINUTE", |
344 |
"resetTime" : 1244425439 |
345 |
}, |
346 |
{ |
347 |
"verb" : "GET", |
348 |
"URI" : "*", |
349 |
"regex" : ".*", |
350 |
"value" : 3, |
351 |
"remaining" : 3, |
352 |
"unit" : "MINUTE", |
353 |
"resetTime" : 1244425439 |
354 |
}, |
355 |
{ |
356 |
"verb" : "DELETE", |
357 |
"URI" : "*", |
358 |
"regex" : ".*", |
359 |
"value" : 100, |
360 |
"remaining" : 100, |
361 |
"unit" : "MINUTE", |
362 |
"resetTime" : 1244425439 |
363 |
} |
364 |
] |
365 |
|
366 |
absolute = { |
367 |
"maxTotalRAMSize" : 51200, |
368 |
"maxIPGroups" : 50, |
369 |
"maxIPGroupMembers" : 25 |
370 |
} |
371 |
|
372 |
def read(self, request): |
373 |
return { "limits": { |
374 |
"rate": self.rate, |
375 |
"absolute": self.absolute, |
376 |
} |
377 |
} |