Statistics
| Branch: | Tag: | Revision:

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
            }