root / api / handlers.py @ e107ee57
History | View | Annotate | Download (11.6 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 VirtualMachine, Flavor, Image, User, id_from_instance_name |
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.server_label, \ |
83 |
'Image_Version': server.image_version}, \
|
84 |
'hostId': '9e107d9d372bb6826bd81d3542a419d6', \ |
85 |
'addresses': {'public': ['67.23.10.133'], 'private': ['10.176.42.17']}} \ |
86 |
for server in virtual_servers] |
87 |
#pass some fake data regarding ip, since we don't have any such data
|
88 |
return { "servers": virtual_servers_list } |
89 |
else:
|
90 |
virtual_servers = VirtualMachine.objects.filter(owner=User.objects.all()[0])
|
91 |
return { "servers": [ { "id": s.id, "name": s.name } for s in virtual_servers ] } |
92 |
|
93 |
#ganeti can't ask for instances of user X. The DB is responsible for this
|
94 |
#also here we ask for the instances of the first user, since the user system is not ready
|
95 |
if not detail: |
96 |
virtual_servers = VirtualMachine.objects.filter(owner=User.objects.all()[0])
|
97 |
return { "servers": [ { "id": s.id, "name": s.name } for s in virtual_servers ] } |
98 |
else:
|
99 |
virtual_servers = VirtualMachine.objects.filter(owner=User.objects.all()[0])
|
100 |
virtual_servers_list = [{'status': server.state, 'flavorId': server.flavor, \ |
101 |
'name': server.name, 'id': server.id, 'imageId': server.imageid, |
102 |
'metadata': {'Server_Label': server.server_label, \ |
103 |
'Image_Version': server.image_version}, \
|
104 |
'hostId': '9e107d9d372bb6826bd81d3542a419d6', \ |
105 |
'addresses': {'public': ['67.23.10.133'], 'private': ['10.176.42.17']}} \ |
106 |
for server in virtual_servers] |
107 |
#pass some fake data regarding ip, since we don't have any such data
|
108 |
return { "servers": virtual_servers_list } |
109 |
|
110 |
|
111 |
def create(self, request): |
112 |
print 'create machine was called' |
113 |
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}) |
114 |
#TODO: replace with real data from request.POST
|
115 |
return accepted
|
116 |
|
117 |
def update(self, request, id): |
118 |
return noContent
|
119 |
|
120 |
def delete(self, request, id): |
121 |
machine = 'machine-XXX' #VirtualMachine.objects.get(id=id_from_instance_name(id)) |
122 |
print 'deleting machine %s' % machine |
123 |
rapi.DeleteInstance(machine.name) |
124 |
return accepted
|
125 |
|
126 |
|
127 |
class ServerAddressHandler(BaseHandler): |
128 |
allowed_methods = ('GET', 'PUT', 'DELETE') |
129 |
|
130 |
def read(self, request, id, type=None): |
131 |
"""List IP addresses for a server"""
|
132 |
|
133 |
if type is None: |
134 |
pass
|
135 |
elif type == "private": |
136 |
pass
|
137 |
elif type == "public": |
138 |
pass
|
139 |
return {}
|
140 |
|
141 |
def update(self, request, id, address): |
142 |
"""Share an IP address to another in the group"""
|
143 |
return accepted
|
144 |
|
145 |
def delete(self, request, id, address): |
146 |
"""Unshare an IP address"""
|
147 |
return accepted
|
148 |
|
149 |
|
150 |
class ServerActionHandler(BaseHandler): |
151 |
allowed_methods = ('POST', 'DELETE', 'GET', 'PUT') |
152 |
#TODO: remove GET/PUT
|
153 |
|
154 |
def read(self, request, id): |
155 |
return accepted
|
156 |
|
157 |
def create(self, request, id): |
158 |
"""Reboot, rebuild, resize, confirm resized, revert resized"""
|
159 |
machine = 'machine-XXX' #VirtualMachine.objects.get(id=id_from_instance_name(id)) |
160 |
reboot_request = request.POST.get('reboot', None) |
161 |
shutdown_request = request.POST.get('shutdown', None) |
162 |
if reboot_request:
|
163 |
print 'reboot was asked, with options: %s' % reboot_request |
164 |
rapi.RebootInstance(machine) |
165 |
elif shutdown_request:
|
166 |
print 'shutdown was asked, with options: %s' % shutdown_request |
167 |
rapi.ShutdownInstance(machine) |
168 |
return accepted
|
169 |
|
170 |
|
171 |
def delete(self, request, id): |
172 |
"""Delete an Instance"""
|
173 |
return accepted
|
174 |
|
175 |
def update(self, request, id): |
176 |
return noContent
|
177 |
|
178 |
|
179 |
|
180 |
#read is called on GET requests
|
181 |
#create is called on POST, and creates new objects, and should return them (or rc.CREATED.)
|
182 |
#update is called on PUT, and should update an existing product and return them (or rc.ALL_OK.)
|
183 |
#delete is called on DELETE, and should delete an existing object. Should not return anything, just rc.DELETED.'''
|
184 |
|
185 |
|
186 |
class ServerBackupHandler(BaseHandler): |
187 |
""" Backup Schedules are not implemented yet, return notImplemented """
|
188 |
allowed_methods = ('GET', 'POST', 'DELETE') |
189 |
|
190 |
def read(self, request, id): |
191 |
raise fault.notImplemented
|
192 |
|
193 |
def create(self, request, id): |
194 |
raise fault.notImplemented
|
195 |
|
196 |
def delete(self, request, id): |
197 |
raise fault.notImplemented
|
198 |
|
199 |
|
200 |
class FlavorHandler(BaseHandler): |
201 |
allowed_methods = ('GET',)
|
202 |
flavors = Flavor.objects.all() |
203 |
flavors = [ {'id': flavor.id, 'name': flavor.name, 'ram': flavor.ram, \ |
204 |
'disk': flavor.disk} for flavor in flavors] |
205 |
|
206 |
def read(self, request, id=None): |
207 |
"""
|
208 |
List flavors or retrieve one
|
209 |
|
210 |
Returns: OK
|
211 |
Faults: cloudServersFault, serviceUnavailable, unauthorized,
|
212 |
badRequest, itemNotFound
|
213 |
"""
|
214 |
if id is None: |
215 |
simple = map(lambda v: { |
216 |
"id": v['id'], |
217 |
"name": v['name'], |
218 |
}, self.flavors)
|
219 |
return { "flavors": simple } |
220 |
elif id == "detail": |
221 |
return { "flavors": self.flavors } |
222 |
else:
|
223 |
for flavor in self.flavors: |
224 |
if str(flavor['id']) == id: |
225 |
return { "flavor": flavor } |
226 |
raise fault.itemNotFound
|
227 |
|
228 |
|
229 |
class ImageHandler(BaseHandler): |
230 |
allowed_methods = ('GET', 'POST') |
231 |
|
232 |
def read(self, request, id=None): |
233 |
"""
|
234 |
List images or retrieve one
|
235 |
|
236 |
Returns: OK
|
237 |
Faults: cloudServersFault, serviceUnavailable, unauthorized,
|
238 |
badRequest, itemNotFound
|
239 |
"""
|
240 |
images = Image.objects.all() |
241 |
images = [ {'created': image.created.isoformat(), 'id': image.id, \ |
242 |
'name': image.name, 'updated': image.updated.isoformat(), \ |
243 |
'description': image.description, 'state': image.state, 'serverid': image.serverid, \ |
244 |
'vm_id': image.vm_id} for image in images] |
245 |
|
246 |
if rapi: # Images info is stored in the DB. Ganeti is not aware of this |
247 |
if id == "detail": |
248 |
return { "images": images } |
249 |
elif id is None: |
250 |
return { "images": [ { "id": s['id'], "name": s['name'] } for s in images ] } |
251 |
else:
|
252 |
return { "image": images[0] } |
253 |
# if id is None:
|
254 |
# return {}
|
255 |
# elif id == "detail":
|
256 |
# return {}
|
257 |
# else:
|
258 |
# raise fault.itemNotFound
|
259 |
|
260 |
def create(self, request): |
261 |
"""Create a new image"""
|
262 |
return accepted
|
263 |
|
264 |
|
265 |
class SharedIPGroupHandler(BaseHandler): |
266 |
allowed_methods = ('GET', 'POST', 'DELETE') |
267 |
|
268 |
def read(self, request, id=None): |
269 |
"""List Shared IP Groups"""
|
270 |
if id is None: |
271 |
return {}
|
272 |
elif id == "detail": |
273 |
return {}
|
274 |
else:
|
275 |
raise fault.itemNotFound
|
276 |
|
277 |
def create(self, request, id): |
278 |
"""Creates a new Shared IP Group"""
|
279 |
return created
|
280 |
|
281 |
def delete(self, request, id): |
282 |
"""Deletes a Shared IP Group"""
|
283 |
return noContent
|
284 |
|
285 |
|
286 |
class LimitHandler(BaseHandler): |
287 |
allowed_methods = ('GET',)
|
288 |
|
289 |
# XXX: hookup with @throttle
|
290 |
|
291 |
rate = [ |
292 |
{ |
293 |
"verb" : "POST", |
294 |
"URI" : "*", |
295 |
"regex" : ".*", |
296 |
"value" : 10, |
297 |
"remaining" : 2, |
298 |
"unit" : "MINUTE", |
299 |
"resetTime" : 1244425439 |
300 |
}, |
301 |
{ |
302 |
"verb" : "POST", |
303 |
"URI" : "*/servers", |
304 |
"regex" : "^/servers", |
305 |
"value" : 25, |
306 |
"remaining" : 24, |
307 |
"unit" : "DAY", |
308 |
"resetTime" : 1244511839 |
309 |
}, |
310 |
{ |
311 |
"verb" : "PUT", |
312 |
"URI" : "*", |
313 |
"regex" : ".*", |
314 |
"value" : 10, |
315 |
"remaining" : 2, |
316 |
"unit" : "MINUTE", |
317 |
"resetTime" : 1244425439 |
318 |
}, |
319 |
{ |
320 |
"verb" : "GET", |
321 |
"URI" : "*", |
322 |
"regex" : ".*", |
323 |
"value" : 3, |
324 |
"remaining" : 3, |
325 |
"unit" : "MINUTE", |
326 |
"resetTime" : 1244425439 |
327 |
}, |
328 |
{ |
329 |
"verb" : "DELETE", |
330 |
"URI" : "*", |
331 |
"regex" : ".*", |
332 |
"value" : 100, |
333 |
"remaining" : 100, |
334 |
"unit" : "MINUTE", |
335 |
"resetTime" : 1244425439 |
336 |
} |
337 |
] |
338 |
|
339 |
absolute = { |
340 |
"maxTotalRAMSize" : 51200, |
341 |
"maxIPGroups" : 50, |
342 |
"maxIPGroupMembers" : 25 |
343 |
} |
344 |
|
345 |
def read(self, request): |
346 |
return { "limits": { |
347 |
"rate": self.rate, |
348 |
"absolute": self.absolute, |
349 |
} |
350 |
} |