Statistics
| Branch: | Tag: | Revision:

root / api / handlers.py @ 9071888e

History | View | Annotate | Download (15.1 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
try:
16
    rapi = GanetiRapiClient(*settings.GANETI_CLUSTER_INFO)
17
    rapi.GetVersion()
18
except:
19
    raise fault.serviceUnavailable
20
#If we can't connect to the rapi successfully, don't do anything
21
#TODO: add logging/admin alerting
22

    
23
backend_prefix_id = settings.BACKEND_PREFIX_ID
24

    
25
VERSIONS = [
26
    {
27
        "status": "CURRENT",
28
        "id": "v1.0",
29
        "docURL" : "http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20090714.pdf ",
30
        "wadl" : "http://docs.rackspacecloud.com/servers/api/v1.0/application.wadl"
31
    },
32
    {
33
        "status": "CURRENT",
34
        "id": "v1.0grnet1",
35
        "docURL" : "None yet",
36
        "wad1" : "None yet"
37
    }
38
]
39

    
40

    
41
class VersionHandler(AnonymousBaseHandler):
42
    allowed_methods = ('GET',)
43

    
44
    def read(self, request, number=None):
45
        if number is None:
46
            versions = map(lambda v: {
47
                        "status": v["status"],
48
                        "id": v["id"],
49
                    }, VERSIONS)
50
            return { "versions": versions }
51
        else:
52
            for version in VERSIONS:
53
                if version["id"] == number:
54
                    return { "version": version }
55
            raise fault.itemNotFound
56

    
57

    
58
class ServerHandler(BaseHandler):
59
    allowed_methods = ('GET', 'POST', 'PUT', 'DELETE')
60

    
61
    def read(self, request, id=None):
62
        from time import sleep
63
        sleep(0.5)
64
        #TODO: delete the sleep once the mock objects are removed
65
        if id is None:
66
            return self.read_all(request)
67
        elif id == "detail":
68
            return self.read_all(request, detail=True)
69
        else:
70
            return self.read_one(request, id)
71

    
72
    def read_one(self, request, id):
73
        try:
74
            instance = VirtualMachine.objects.get(id=id)
75
            return { "server": instance } #FIXME
76
        except:
77
            raise fault.itemNotFound
78

    
79
    @paginator
80
    def read_all(self, request, detail=False):
81
        virtual_servers = VirtualMachine.objects.all()
82
        virtual_servers = [virtual_server for virtual_server in  virtual_servers if virtual_server.rsapi_state !="DELETED"]
83
        #get all VM's for now, FIX it to take the user's VMs only yet. also don't get deleted VM's
84

    
85
        if not detail:
86
            return { "servers": [ { "id": s.id, "name": s.name } for s in virtual_servers ] }
87
        else:
88
            virtual_servers_list = [{'status': server.rsapi_state, 
89
                                     'flavorId': server.flavor.id, 
90
                                     'name': server.name, 
91
                                     'id': server.id, 
92
                                     'imageId': server.sourceimage.id, 
93
                                     'metadata': {'Server_Label': server.description, 
94
                                                  'hostId': '9e107d9d372bb6826bd81d3542a419d6',
95
                                                  'addresses': {'public': ['67.23.10.133'],
96
                                                                'private': ['10.176.42.17'],
97
                                                                }
98
                                                  }
99
                                    } for server in virtual_servers]
100
            #pass some fake data regarding ip, since we don't have any such data
101
            return { "servers":  virtual_servers_list }                
102

    
103

    
104
    def create(self, request):
105
        print 'create machine was called'
106
        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})
107
        #TODO: replace with real data from request.POST
108
        return accepted
109

    
110
    def update(self, request, id):
111
        return noContent
112

    
113
    def delete(self, request, id):
114
        machine = 'machine-XXX' #VirtualMachine.objects.get(id=id_from_instance_name(id))
115
        print 'deleting machine %s' % machine
116
        rapi.DeleteInstance(machine.name)
117
        return accepted
118

    
119

    
120
class ServerAddressHandler(BaseHandler):
121
    allowed_methods = ('GET', 'PUT', 'DELETE')
122

    
123
    def read(self, request, id, type=None):
124
        """List IP addresses for a server"""
125

    
126
        if type is None:
127
            pass
128
        elif type == "private":
129
            pass
130
        elif type == "public":
131
            pass
132
        return {}
133

    
134
    def update(self, request, id, address):
135
        """Share an IP address to another in the group"""
136
        return accepted
137

    
138
    def delete(self, request, id, address):
139
        """Unshare an IP address"""
140
        return accepted
141

    
142

    
143
class ServerActionHandler(BaseHandler):
144
    allowed_methods = ('POST', 'DELETE', 'GET', 'PUT')
145
#TODO: remove GET/PUT
146
    
147
    def read(self, request, id):
148
        return accepted
149

    
150
    def create(self, request, id):
151
        """Reboot, rebuild, resize, confirm resized, revert resized"""
152
        try:
153
            machine = VirtualMachine.objects.get(id=id)
154
        except:
155
            return noContent
156
        #FIXME: for now make a list with only one machine. This will be a list of machines (for the list view)
157
        reboot_request = request.POST.get('reboot', None)
158
        shutdown_request = request.POST.get('shutdown', None)
159
        if reboot_request:
160
            return self.action_start([machine], 'reboot')          
161
        elif shutdown_request:
162
            return self.action_start([machine], 'shutdown')          
163
        return noContent #FIXME: when does this happen?
164

    
165

    
166
    def delete(self, request, id):
167
        """Delete an Instance"""
168
        return accepted
169

    
170
    def update(self, request, id):
171
        return noContent
172

    
173
    def action_start(self, list_of_machines, action):
174
        if action == 'reboot':
175
            try:
176
                for machine in list_of_machines:
177
                    rapi.RebootInstance(machine)
178
                return accepted
179
            except: # something bad happened. FIXME: return code
180
                return noContent
181
        if action == 'shutdown':        
182
            try:
183
                for machine in list_of_machines:
184
                    rapi.ShutdownInstance(machine)
185
                return accepted
186
            except: # something bad happened. FIXME: return code
187
                return noContent
188

    
189

    
190
#read is called on GET requests
191
#create is called on POST, and creates new objects, and should return them (or rc.CREATED.)
192
#update is called on PUT, and should update an existing product and return them (or rc.ALL_OK.)
193
#delete is called on DELETE, and should delete an existing object. Should not return anything, just rc.DELETED.'''
194

    
195

    
196
class ServerBackupHandler(BaseHandler):
197
    """ Backup Schedules are not implemented yet, return notImplemented """
198
    allowed_methods = ('GET', 'POST', 'DELETE')
199

    
200
    def read(self, request, id):
201
        raise fault.notImplemented
202

    
203
    def create(self, request, id):
204
        raise fault.notImplemented
205

    
206
    def delete(self, request, id):
207
        raise fault.notImplemented
208

    
209

    
210
class FlavorHandler(BaseHandler):
211
    allowed_methods = ('GET',)
212
    flavors = Flavor.objects.all()
213
    flavors = [ {'id': flavor.id, 'name': flavor.name, 'ram': flavor.ram, \
214
             'disk': flavor.disk} for flavor in flavors]
215

    
216
    def read(self, request, id=None):
217
        """
218
        List flavors or retrieve one
219

220
        Returns: OK
221
        Faults: cloudServersFault, serviceUnavailable, unauthorized,
222
                badRequest, itemNotFound
223
        """
224
        if id is None:
225
            simple = map(lambda v: {
226
                        "id": v['id'],
227
                        "name": v['name'],
228
                    }, self.flavors)
229
            return { "flavors": simple }
230
        elif id == "detail":
231
            return { "flavors": self.flavors }
232
        else:
233
            for flavor in self.flavors:
234
                if str(flavor['id']) == id:
235
                    return { "flavor": flavor }
236
            raise fault.itemNotFound
237

    
238

    
239
class ImageHandler(BaseHandler):
240
    allowed_methods = ('GET', 'POST')
241

    
242
    def read(self, request, id=None):
243
        """
244
        List images or retrieve one
245

246
        Returns: OK
247
        Faults: cloudServersFault, serviceUnavailable, unauthorized,
248
                badRequest, itemNotFound
249
        """
250
        images = Image.objects.all()
251
        images_list = [ {'created': image.created.isoformat(), 
252
                    'id': image.id,
253
                    'name': image.name,
254
                    'updated': image.updated.isoformat(),    
255
                    'description': image.description, 
256
                    'state': image.state, 
257
                    'vm_id': image.vm_id
258
                   } for image in images]
259
        if rapi: # Images info is stored in the DB. Ganeti is not aware of this
260
            if id == "detail":
261
                return { "images": images_list }
262
            elif id is None:
263
                return { "images": [ { "id": s['id'], "name": s['name'] } for s in images_list ] }
264
            else:
265
                try:
266
                    image = images.get(id=id)
267
                    return { "image":  {'created': image.created.isoformat(), 
268
                    'id': image.id,
269
                    'name': image.name,
270
                    'updated': image.updated.isoformat(),    
271
                    'description': image.description, 
272
                    'state': image.state, 
273
                    'vm_id': image.vm_id
274
                   } }
275
                except: 
276
                    raise fault.itemNotFound
277
        else:
278
            raise fault.serviceUnavailable
279

    
280
    def create(self, request):
281
        """Create a new image"""
282
        return accepted
283

    
284

    
285
class SharedIPGroupHandler(BaseHandler):
286
    allowed_methods = ('GET', 'POST', 'DELETE')
287

    
288
    def read(self, request, id=None):
289
        """List Shared IP Groups"""
290
        if id is None:
291
            return {}
292
        elif id == "detail":
293
            return {}
294
        else:
295
            raise fault.itemNotFound
296

    
297
    def create(self, request, id):
298
        """Creates a new Shared IP Group"""
299
        return created
300

    
301
    def delete(self, request, id):
302
        """Deletes a Shared IP Group"""
303
        return noContent
304

    
305

    
306
class VirtualMachineGroupHandler(BaseHandler):
307
    allowed_methods = ('GET', 'POST', 'DELETE')
308

    
309
    def read(self, request, id=None):
310
        """List Groups"""
311
        vmgroups = VirtualMachineGroup.objects.all() 
312
        vmgroups = [ {'id': vmgroup.id, \
313
              'name': vmgroup.name,  \
314
               'server_id': [machine.id for machine in vmgroup.machines.all()] \
315
               } for vmgroup in vmgroups]
316
        if rapi: # Group info is stored in the DB. Ganeti is not aware of this
317
            if id == "detail":
318
                return { "groups": vmgroups }
319
            elif id is None:
320
                return { "groups": [ { "id": s['id'], "name": s['name'] } for s in vmgroups ] }
321
            else:
322
                return { "groups": vmgroups[0] }
323

    
324

    
325
    def create(self, request, id):
326
        """Creates a Group"""
327
        return created
328

    
329
    def delete(self, request, id):
330
        """Deletes a  Group"""
331
        return noContent
332

    
333

    
334
class LimitHandler(BaseHandler):
335
    allowed_methods = ('GET',)
336

    
337
    # XXX: hookup with @throttle
338

    
339
    rate = [
340
        {
341
           "verb" : "POST",
342
           "URI" : "*",
343
           "regex" : ".*",
344
           "value" : 10,
345
           "remaining" : 2,
346
           "unit" : "MINUTE",
347
           "resetTime" : 1244425439
348
        },
349
        {
350
           "verb" : "POST",
351
           "URI" : "*/servers",
352
           "regex" : "^/servers",
353
           "value" : 25,
354
           "remaining" : 24,
355
           "unit" : "DAY",
356
           "resetTime" : 1244511839
357
        },
358
        {
359
           "verb" : "PUT",
360
           "URI" : "*",
361
           "regex" : ".*",
362
           "value" : 10,
363
           "remaining" : 2,
364
           "unit" : "MINUTE",
365
           "resetTime" : 1244425439
366
        },
367
        {
368
           "verb" : "GET",
369
           "URI" : "*",
370
           "regex" : ".*",
371
           "value" : 3,
372
           "remaining" : 3,
373
           "unit" : "MINUTE",
374
           "resetTime" : 1244425439
375
        },
376
        {
377
           "verb" : "DELETE",
378
           "URI" : "*",
379
           "regex" : ".*",
380
           "value" : 100,
381
           "remaining" : 100,
382
           "unit" : "MINUTE",
383
           "resetTime" : 1244425439
384
        }
385
    ]
386

    
387
    absolute = {
388
        "maxTotalRAMSize" : 51200,
389
        "maxIPGroups" : 50,
390
        "maxIPGroupMembers" : 25
391
    }
392

    
393
    def read(self, request):
394
        return { "limits": {
395
                "rate": self.rate,
396
                "absolute": self.absolute,
397
               }
398
            }
399

    
400

    
401
class DiskHandler(BaseHandler):
402
    allowed_methods = ('GET', 'POST', 'PUT', 'DELETE')
403

    
404
    def read(self, request, id=None):
405
        """List Disks"""
406
        if id is None:
407
            return self.read_all(request)
408
        elif id == "detail":
409
            return self.read_all(request, detail=True)
410
        else:
411
            return self.read_one(request, id)
412

    
413
    def read_one(self, request, id):
414
        """List one Disk with the specified id with all details"""
415
        # FIXME Get detailed info from the DB 
416
        # for the Disk with the specified id
417
        try:
418
            disk = Disk.objects.get(pk=id)
419
            disk_details = {
420
                "id" : disk.id, 
421
                "name" : disk.name, 
422
                "size" : disk.size,
423
                "created" : disk.created, 
424
                "serverId" : disk.vm.id
425
            }
426
            return { "disks" : disk_details }
427
        except:
428
            raise fault.itemNotFound
429

    
430
    @paginator
431
    def read_all(self, request, detail=False):
432
        """List all Disks. If -detail- is set list them with all details"""
433
        if not detail:
434
            disks = Disk.objects.filter(owner=SynnefoUser.objects.all()[0])
435
            return { "disks": [ { "id": disk.id, "name": disk.name } for disk in disks ] }
436
        else:
437
            disks = Disk.objects.filter(owner=SynnefoUser.objects.all()[0])
438
            disks_details = [ {
439
                "id" : disk.id, 
440
                "name" : disk.name,
441
                "size" : disk.size,
442
                "created" : disk.created, 
443
                "serverId" : disk.vm.id,
444
            } for disk in disks ]
445
            return { "disks":  disks_details }                
446

    
447
    def create(self, request):
448
        """Create a new Disk"""
449
        # FIXME Create a partial DB entry, 
450
        # then call the backend for actual creation
451
        pass
452

    
453
    def update(self, request, id):
454
        """Rename the Disk with the specified id"""
455
        # FIXME Change the Disk's name in the DB
456
        pass
457

    
458
    def delete(self, request, id):
459
        """Destroy the Disk with the specified id"""
460
        # Call the backend for actual destruction
461
        pass