Statistics
| Branch: | Tag: | Revision:

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
            }