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