Revision e646ebe5 api/handlers.py

b/api/handlers.py
7 7
from piston.handler import BaseHandler, AnonymousBaseHandler
8 8
from synnefo.api.faults import fault, noContent, accepted, created, notModified
9 9
from synnefo.api.helpers import instance_to_server, paginator
10
from synnefo.util.rapi import GanetiRapiClient, GanetiApiError, CertificateError
10
from synnefo.util.rapi import GanetiRapiClient, GanetiApiError
11
from synnefo.util.rapi import CertificateError
11 12
from synnefo.db.models import *
12 13
import random
13 14
import string
......
30 31
    {
31 32
        "status": "CURRENT",
32 33
        "id": "v1.0",
33
        "docURL" : "http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf",
34
        "wadl" : "http://docs.rackspacecloud.com/servers/api/v1.0/application.wadl"
34
        "docURL":
35
    "http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20110112.pdf",
36
        "wadl":
37
            "http://docs.rackspacecloud.com/servers/api/v1.0/application.wadl"
35 38
    },
36 39
    {
37 40
        "status": "CURRENT",
38 41
        "id": "v1.1",
39
        "docURL" : "http://docs.openstack.org/openstack-compute/developer/content/",
40
        "wadl" : "None yet"
42
        "docURL":
43
            "http://docs.openstack.org/openstack-compute/developer/content/",
44
        "wadl": "None yet"
41 45
    },
42 46
    {
43 47
        "status": "CURRENT",
44 48
        "id": "v1.0grnet1",
45
        "docURL" : "None yet",
46
        "wad1" : "None yet"
49
        "docURL": "None yet",
50
        "wad1": "None yet"
47 51
    }
48 52
]
49 53

  
......
63 67
                            "status": v["status"],
64 68
                            "id": v["id"],
65 69
                        }, VERSIONS)
66
                return { "versions": versions }
70
                return {"versions": versions}
67 71
            else:
68 72
                for version in VERSIONS:
69 73
                    if version["id"] == number:
70
                        return { "version": version }
74
                        return {"version": version}
71 75
                raise fault.itemNotFound
72 76
        except Exception, e:
73 77
            log.exception('Unexpected error: %s' % e)
......
81 85

  
82 86
     @HTTP methods: POST, DELETE, PUT, GET
83 87
     @Parameters: POST data with the create data (cpu, ram, etc)
84
     @Responses: HTTP 200 if successfully call rapi, 304 if not modified, itemNotFound or serviceUnavailable otherwise
88
     @Responses: HTTP 200 if successfully call rapi, 304 if not modified,
89
                 itemNotFound or serviceUnavailable otherwise
85 90

  
86 91
    """
87 92
    allowed_methods = ('GET', 'POST', 'PUT', 'DELETE')
......
101 106
        try:
102 107
            server = VirtualMachine.objects.get(id=id)
103 108

  
104
            server = {'status': server.rsapi_state, 
105
                     'flavorRef': server.flavor.id, 
106
                     'name': server.name, 
107
                     'id': server.id, 
109
            server = {'status': server.rsapi_state,
110
                     'flavorRef': server.flavor.id,
111
                     'name': server.name,
112
                     'id': server.id,
108 113
                     'imageRef': server.sourceimage.id,
109
                     'created': server.created, 
114
                     'created': server.created,
110 115
                     'updated': server.updated,
111
                     'hostId': server.hostid, 
112
                     'progress': server.rsapi_state == 'ACTIVE' and 100 or 0, 
116
                     'hostId': server.hostid,
117
                     'progress': server.rsapi_state == 'ACTIVE' and 100 or 0,
113 118
                     #'metadata': {'Server_Label': server.description },
114
                     'metadata':[{'meta': { 'key': {metadata.meta_key: metadata.meta_value}}} for metadata in server.virtualmachinemetadata_set.all()],                                    
115
                     'addresses': {'public': { 'ip': {'addr': server.ipfour}, 'ip6': {'addr': server.ipsix}},'private': ''},      
119
                     'metadata': [{'meta': {
120
                                    'key': {
121
                                     metadata.meta_key: metadata.meta_value
122
                                    }
123
                                   }
124
                        }
125
                            for metadata in
126
                            server.virtualmachinemetadata_set.all()],
127
                     'addresses': {'public': {
128
                                    'ip': {'addr': server.ipfour},
129
                                    'ip6': {'addr': server.ipsix}},
130
                                            'private': ''},
116 131
                    }
117
            return { "server": server } 
132
            return {"server": server}
118 133
        except VirtualMachine.DoesNotExist:
119 134
            raise fault.itemNotFound
120 135
        except VirtualMachine.MultipleObjectsReturned:
......
123 138
            log.exception('Unexpected error: %s' % e)
124 139
            raise fault.serviceUnavailable
125 140

  
126

  
127 141
    @paginator
128 142
    def read_all(self, request, detail=False):
129 143
        #changes_since should be on ISO 8601 format
130 144
        try:
131 145
            changes_since = request.GET.get("changes-since", 0)
132 146
            if changes_since:
133
                last_update = datetime.strptime(changes_since, "%Y-%m-%dT%H:%M:%S" )
147
                last_update = datetime.strptime(changes_since,
148
                                                "%Y-%m-%dT%H:%M:%S")
134 149
                #return a badRequest if the changes_since is older than a limit
135
                if datetime.now() - last_update > timedelta(seconds=settings.POLL_LIMIT):
136
                    raise fault.badRequest        
137
                virtual_servers = VirtualMachine.objects.filter(updated__gt=last_update)
150
                if (datetime.now() - last_update >
151
                    timedelta(seconds=settings.POLL_LIMIT)):
152
                    raise fault.badRequest
153
                virtual_servers = VirtualMachine.objects.filter(
154
                    updated__gt=last_update)
138 155
                if not len(virtual_servers):
139 156
                    return notModified
140 157
            else:
141 158
                virtual_servers = VirtualMachine.objects.filter(deleted=False)
142
            #get all VM's for now, FIX it to take the user's VMs only yet. also don't get deleted VM's
159
            # get all VM's for now, FIX it to take the user's VMs only yet.
160
            # also don't get deleted VM's
143 161
        except Exception, e:
144
            raise fault.badRequest        
162
            raise fault.badRequest
145 163
        try:
146 164
            if not detail:
147
                return { "servers": [ { "id": s.id, "name": s.name } for s in virtual_servers ] }
165
                return {"servers": [{
166
                    "id": s.id, "name": s.name} for s in virtual_servers]}
148 167
            else:
149
                virtual_servers_list = [{'status': server.rsapi_state, 
150
                                         'flavorRef': server.flavor.id, 
151
                                         'name': server.name, 
152
                                         'id': server.id, 
153
                                         'created': server.created, 
154
                                         'updated': server.updated,
155
                                         'imageRef': server.sourceimage.id, 
156
                                         'hostId': server.hostid, 
157
                                         'progress': server.rsapi_state == 'ACTIVE' and 100 or 0, 
158
                                         #'metadata': {'Server_Label': server.description },
159
                                         'metadata':[{'meta': { 'key': {metadata.meta_key: metadata.meta_value}}} for metadata in server.virtualmachinemetadata_set.all()],                                    
160
                                         'addresses': {'public': { 'ip': {'addr': server.ipfour}, 'ip6': {'addr': server.ipsix}},'private': ''},      
161

  
162
                                        } for server in virtual_servers]
163
                #pass some fake data regarding ip, since we don't have any such data            
164
                return { "servers":  virtual_servers_list }                
168
                virtual_servers_list = [
169
                    {'status': server.rsapi_state,
170
                     'flavorRef': server.flavor.id,
171
                    'name': server.name,
172
                    'id': server.id,
173
                    'created': server.created,
174
                    'updated': server.updated,
175
                    'imageRef': server.sourceimage.id,
176
                    'hostId': server.hostid,
177
                    'progress': (server.rsapi_state == 'ACTIVE' and 100 or 0),
178
                     #'metadata': {'Server_Label': server.description },
179
                    'metadata':[
180
                        {'meta':
181
                            {'key': {metadata.meta_key: metadata.meta_value}}}
182
                                for metadata in
183
                                server.virtualmachinemetadata_set.all()
184
                        ],
185
                    'addresses': {
186
                        'public': {
187
                            'ip': {'addr': server.ipfour},
188
                            'ip6': {'addr': server.ipsix}
189
                        },
190
                        'private': ''},
191
                    } for server in virtual_servers]
192
                #pass fake data regarding ip, since we don't have it yet
193
                return {"servers":  virtual_servers_list}
165 194
        except Exception, e:
166 195
            log.exception('Unexpected error: %s' % e)
167 196
            raise fault.serviceUnavailable
168 197

  
169

  
170 198
    def create(self, request):
171 199
        """ Parse RackSpace API create request to generate rapi create request
172
        
173 200
            TODO: auto generate and set password
174 201
        """
175
        # Check if we have all the necessary data in the JSON request       
202
        # Check if we have all the necessary data in the JSON request
176 203
        try:
177 204
            server = json.loads(request.raw_post_data)['server']
178 205
            name = server['name']
......
187 214
        except (Flavor.MultipleObjectsReturned, Image.MultipleObjectsReturned):
188 215
            raise fault.serviceUnavailable
189 216
        except Exception as e:
190
            log.exception('Malformed create request: %s - %s' % (e, request.raw_post_data))    
217
            log.exception('Malformed create request: %s - %s' %
218
                          (e, request.raw_post_data))
191 219
            raise fault.badRequest
192 220

  
193 221
        # TODO: Proper Authn, Authz
194 222
        # Everything belongs to a single SynnefoUser for now.
195
        try:  	
223
        try:
196 224
            owner = SynnefoUser.objects.all()[0]
197 225
        except Exception as e:
198
            log.exception('Cannot find a single SynnefoUser in the DB: %s' % (e));
226
            log.exception('Cannot find a single SynnefoUser in the DB: %s' %
227
                          (e))
199 228
            raise fault.unauthorized
200 229

  
201 230
        # add the new VM to the local db
202 231
        try:
203
            vm = VirtualMachine.objects.create(sourceimage=image, ipfour='0.0.0.0', ipsix='::1', flavor=flavor, owner=owner)
232
            vm = VirtualMachine.objects.create(sourceimage=image,
233
                                               ipfour='0.0.0.0',
234
                                               ipsix='::1',
235
                                               flavor=flavor,
236
                                               owner=owner)
204 237
        except Exception as e:
205 238
            log.exception("Can't save vm: %s" % e)
206 239
            raise fault.serviceUnavailable
......
208 241
        try:
209 242
            vm.name = name
210 243
            #vm.description = descr
211
            vm.save()            
244
            vm.save()
212 245
            jobId = rapi.CreateInstance(
213 246
                'create',
214
                request.META['SERVER_NAME'] == 'testserver' and 'test-server' or vm.backend_id,
247
                (request.META['SERVER_NAME'] == 'testserver' and
248
                'test-server' or vm.backend_id),
215 249
                'plain',
216
                # disk field of Flavor object is in GB, value specified here is in MB
217
                # FIXME: Always ask for a 2GB disk, current LVM physical groups are too small:
250
                # disk field of Flavor object is in GB,
251
                # value specified here is in MB
252
                # FIXME: Always ask for a 2GB disk,
253
                # current LVM physical groups are too small:
218 254
                # [{"size": flavor.disk * 1000}],
219 255
                [{"size": 2000}],
220 256
                [{}],
......
223 259
                ip_check=False,
224 260
                name_check=False,
225 261
                #TODO: verify if this is necessary
226
                pnode = rapi.GetNodes()[0],
262
                pnode=rapi.GetNodes()[0],
227 263
                # Dry run when called by unit tests
228
                dry_run = request.META['SERVER_NAME'] == 'testserver',
264
                dry_run=request.META['SERVER_NAME'] == 'testserver',
229 265
                beparams={
230 266
                            'auto_balance': True,
231 267
                            'vcpus': flavor.cpu,
232 268
                            'memory': flavor.ram,
233 269
                        },
234 270
                )
235
            log.info('created vm with %s cpus, %s ram and %s storage' % (flavor.cpu, flavor.ram, flavor.disk))
271
            log.info('created vm with %s cpus, %s ram and %s storage' %
272
                     (flavor.cpu, flavor.ram, flavor.disk))
236 273
        except (GanetiApiError, CertificateError) as e:
237 274
            log.exception('CreateInstance failed: %s' % e)
238 275
            vm.deleted = True
......
242 279
            log.exception('Unexpected error: %s' % e)
243 280
            vm.deleted = True
244 281
            vm.save()
245
            raise fault.serviceUnavailable            
246
        
282
            raise fault.serviceUnavailable
247 283

  
248 284
        ret = {'server': {
249
                'id' : vm.id,
250
                'name' : vm.name,
251
                "imageRef" : imageRef,
252
                "flavorRef" : flavorRef,
253
                "hostId" : vm.hostid,
254
                "progress" : 0,
255
                "status" : 'BUILD',
256
                "adminPass" : self.random_password(),
257
                "metadata" : {"My Server Name" : vm.name},
258
                "addresses" : {
259
                    "public" : [  ],
260
                    "private" : [  ],
285
                'id': vm.id,
286
                'name': vm.name,
287
                "imageRef": imageRef,
288
                "flavorRef": flavorRef,
289
                "hostId": vm.hostid,
290
                "progress": 0,
291
                "status": 'BUILD',
292
                "adminPass": self.random_password(),
293
                "metadata": {"My Server Name": vm.name},
294
                "addresses": {
295
                    "public": [],
296
                    "private": [],
261 297
                    },
262 298
                },
263 299
        }
264
        return HttpResponse(json.dumps(ret), mimetype="application/json", status=202)
265

  
300
        return HttpResponse(json.dumps(ret),
301
                            mimetype="application/json", status=202)
266 302

  
267 303
    def random_password(self):
268 304
        "return random password"
269 305
        number_of_chars = 8
270
        possible_chars = string.ascii_uppercase + string.ascii_lowercase + string.digits
271
        return ''.join(random.choice(possible_chars) for x in range(number_of_chars))
272

  
306
        possible_chars = string.ascii_uppercase + string.ascii_lowercase + \
307
                         string.digits
308
        return ''.join(random.choice(possible_chars) \
309
                       for x in range(number_of_chars))
273 310

  
274 311
    def update(self, request, id):
275
        """Sets and updates Virtual Machine Metadata. 
276
 
312
        """Sets and updates Virtual Machine Metadata.
313

  
277 314
        """
278 315
        try:
279 316
            metadata_request = json.loads(request.raw_post_data)['metadata']
280 317
            metadata_key = metadata_request.get('metadata_key')
281 318
            metadata_value = metadata_request.get('metadata_value')
282
 
319

  
283 320
            vm = VirtualMachine.objects.get(id=id)
284 321
            #we only update virtual machine's name atm
285 322
            if metadata_key == 'name':
......
296 333

  
297 334
        raise fault.itemNotFound
298 335

  
299

  
300 336
    def delete(self, request, id):
301 337
        try:
302 338
            vm = VirtualMachine.objects.get(id=id)
303 339
            #TODO: set the status to DESTROYED
304 340
            vm.start_action('DESTROY')
305 341
            rapi.DeleteInstance(vm.backend_id)
306
            return accepted        
342
            return accepted
307 343
        except VirtualMachine.DoesNotExist:
308 344
            raise fault.itemNotFound
309 345
        except VirtualMachine.MultipleObjectsReturned:
......
315 351
            raise fault.serviceUnavailable
316 352

  
317 353

  
318

  
319 354
class ServerAddressHandler(BaseHandler):
320 355
    """Handler responsible for Server Addresses
321 356

  
322
     handles Reboot, Shutdown and Start actions. 
357
     handles Reboot, Shutdown and Start actions.
323 358

  
324 359
     @HTTP methods: GET
325 360
     @Parameters: Id of server and networkID (eg public, private)
326
     @Responses: HTTP 200 if successfully call rapi, itemNotFound, serviceUnavailable otherwise
361
     @Responses: HTTP 200 if successfully call rapi, itemNotFound,
362
     serviceUnavailable otherwise
327 363

  
328 364
    """
329 365
    allowed_methods = ('GET',)
......
333 369

  
334 370
        try:
335 371
            server = VirtualMachine.objects.get(id=id)
336
            address =  {'public': { 'ip': {'addr': server.ipfour}, 'ip6': {'addr': server.ipsix}},'private': ''}                                          
372
            address = {'public': {'ip': {'addr': server.ipfour}, \
373
                'ip6': {'addr': server.ipsix}}, 'private': ''}
337 374
        except VirtualMachine.DoesNotExist:
338 375
            raise fault.itemNotFound
339 376
        except VirtualMachine.MultipleObjectsReturned:
......
343 380
            raise fault.serviceUnavailable
344 381

  
345 382
        if networkID == "public":
346
            address = {'public': { 'ip': {'addr': server.ipfour}, 'ip6': {'addr': server.ipsix}}}                            
383
            address = {'public': {'ip': {'addr': server.ipfour}, \
384
                'ip6': {'addr': server.ipsix}}}
347 385
        elif networkID == "private":
348
            address = {'private': ''}    
386
            address = {'private': ''}
349 387
        elif networkID != None:
350 388
            raise fault.badRequest
351
        return { "addresses": address } 
352

  
389
        return {"addresses": address}
353 390

  
354 391

  
355 392
class ServerActionHandler(BaseHandler):
356 393
    """Handler responsible for Server Actions
357 394

  
358
     handles Reboot, Shutdown and Start actions. 
395
     handles Reboot, Shutdown and Start actions.
359 396

  
360 397
     @HTTP methods: POST, DELETE, PUT
361 398
     @Parameters: POST data with the action (reboot, shutdown, start)
362
     @Responses: HTTP 202 if successfully call rapi, itemNotFound, serviceUnavailable otherwise
399
     @Responses: HTTP 202 if successfully call rapi, itemNotFound,
400
     serviceUnavailable otherwise
363 401

  
364 402
    """
365 403

  
......
367 405

  
368 406
    def create(self, request, id):
369 407
        """Reboot, Shutdown, Start virtual machine"""
370
        
408

  
371 409
        try:
372 410
            requested_action = json.loads(request.raw_post_data)
373 411
            reboot_request = requested_action.get('reboot', None)
374 412
            shutdown_request = requested_action.get('shutdown', None)
375 413
            start_request = requested_action.get('start', None)
376 414
            #action not implemented
377
            action = reboot_request and 'REBOOT' or shutdown_request and 'STOP' or start_request and 'START'
415
            action = reboot_request and 'REBOOT' or shutdown_request \
416
                     and 'STOP' or start_request and 'START'
378 417

  
379 418
            if not action:
380
                raise fault.notImplemented 
419
                raise fault.notImplemented
381 420
            #test if we can get the vm
382 421
            vm = VirtualMachine.objects.get(id=id)
383 422
            vm.start_action(action)
......
446 485
            return {
447 486
                "metadata": {
448 487
                    "values": [
449
                        {m.meta_key: m.meta_value} for m in server.virtualmachinemetadata_set.all()
488
                        {m.meta_key: m.meta_value} \
489
                        for m in server.virtualmachinemetadata_set.all()
450 490
                    ]
451 491
                }
452 492
            }
......
457 497
        except Exception, e:
458 498
            log.exception('Unexpected error: %s' % e)
459 499
            raise fault.serviceUnavailable
460
        
500

  
461 501
    def read_onekey(self, request, id, key):
462 502
        """Returns the specified metadata key of the specified server"""
463 503
        try:
......
465 505
            return {
466 506
                "metadata": {
467 507
                    "values": [
468
                        {m.meta_key: m.meta_value} for m in server.virtualmachinemetadata_set.filter(meta_key=key)
508
                        {m.meta_key: m.meta_value} for m in
509
                        server.virtualmachinemetadata_set.filter(meta_key=key)
469 510
                    ]
470 511
                }
471 512
            }
472 513
        except VirtualMachineMetadata.DoesNotExist:
473
            raise fault.itemNotFound            
514
            raise fault.itemNotFound
474 515
        except VirtualMachine.DoesNotExist:
475 516
            raise fault.itemNotFound
476 517
        except VirtualMachine.MultipleObjectsReturned:
......
487 528
        try:
488 529
            metadata = json.loads(request.raw_post_data)['metadata']
489 530
        except Exception as e:
490
            log.exception('Malformed create request: %s - %s' % (e, request.raw_post_data))
531
            log.exception('Malformed create request: %s - %s' \
532
                          % (e, request.raw_post_data))
491 533
            raise fault.badRequest
492 534

  
493 535
        try:
494 536
            vm = VirtualMachine.objects.get(pk=id)
495 537
            for x in metadata.keys():
496
                vm_meta, created = vm.virtualmachinemetadata_set.get_or_create(meta_key=x)
497
                vm_meta.meta_value = metadata[x] 
538
                vm_meta, created = (vm.virtualmachinemetadata_set.
539
                                    get_or_create(meta_key=x))
540
                vm_meta.meta_value = metadata[x]
498 541
                vm_meta.save()
499 542
            return {
500 543
                "metadata": [{
501
                    "meta": { 
502
                        "key": {m.meta_key: m.meta_value}}} for m in vm.virtualmachinemetadata_set.all()]
503
            }        
544
                    "meta": {
545
                        "key": {m.meta_key: m.meta_value}}} \
546
                    for m in vm.virtualmachinemetadata_set.all()]
547
            }
504 548
        except VirtualMachine.DoesNotExist:
505 549
            raise fault.itemNotFound
506 550
        except VirtualMachine.MultipleObjectsReturned:
......
522 566
            metadata = json.loads(request.raw_post_data)['meta']
523 567
            metadata_value = metadata[key]
524 568
        except Exception as e:
525
            log.exception('Malformed create request: %s - %s' % (e, request.raw_post_data))
569
            log.exception('Malformed create request: %s - %s' \
570
                          % (e, request.raw_post_data))
526 571
            raise fault.badRequest
527 572

  
528 573
        try:
529 574
            server = VirtualMachine.objects.get(pk=id)
530
            vm_meta, created = server.virtualmachinemetadata_set.get_or_create(meta_key=key)
531
            vm_meta.meta_value = metadata_value 
575
            vm_meta, created = (server.virtualmachinemetadata_set.
576
                                get_or_create(meta_key=key))
577
            vm_meta.meta_value = metadata_value
532 578
            vm_meta.save()
533 579
            return {"meta": {vm_meta.meta_key: vm_meta.meta_value}}
534
        
580

  
535 581
        except VirtualMachine.DoesNotExist:
536 582
            raise fault.itemNotFound
537 583
        except VirtualMachine.MultipleObjectsReturned:
......
581 627
        """
582 628
        try:
583 629
            flavors = Flavor.objects.all()
584
            flavors = [ {'id': flavor.id, 'name': flavor.name, 'ram': flavor.ram, \
585
                     'disk': flavor.disk, 'cpu': flavor.cpu} for flavor in flavors]
630
            flavors = [{'id': flavor.id,
631
                        'name': flavor.name,
632
                        'ram': flavor.ram,
633
                        'disk': flavor.disk,
634
                        'cpu': flavor.cpu}
635
                        for flavor in flavors]
586 636

  
587 637
            if id is None:
588 638
                simple = map(lambda v: {
589 639
                            "id": v['id'],
590 640
                            "name": v['name'],
591 641
                        }, flavors)
592
                return { "flavors": simple }
642
                return {"flavors": simple}
593 643
            elif id == "detail":
594
                return { "flavors": flavors }
644
                return {"flavors": flavors}
595 645
            else:
596 646
                flavor = Flavor.objects.get(id=id)
597
                return { "flavor":  {
647
                return {"flavor":  {
598 648
                    'id': flavor.id,
599 649
                    'name': flavor.name,
600 650
                    'ram': flavor.ram,
601
                    'disk': flavor.disk,  
602
                    'cpu': flavor.cpu,  
603
                   } }
651
                    'disk': flavor.disk,
652
                    'cpu': flavor.cpu,
653
                   }}
604 654

  
605 655
        except Flavor.DoesNotExist:
606 656
            raise fault.itemNotFound
......
614 664
class ImageHandler(BaseHandler):
615 665
    """Handler responsible for Images
616 666

  
617
     handles the listing, creation and delete of Images. 
667
     handles the listing, creation and delete of Images.
618 668

  
619 669
     @HTTP methods: GET, POST
620
     @Parameters: POST data 
621
     @Responses: HTTP 202 if successfully create Image or get the Images list, itemNotFound, serviceUnavailable otherwise
670
     @Parameters: POST data
671
     @Responses: HTTP 202 if successfully create Image or get the Images list,
672
     itemNotFound, serviceUnavailable otherwise
622 673

  
623 674
    """
624 675

  
625

  
626 676
    allowed_methods = ('GET', 'POST')
627 677

  
628 678
    def read(self, request, id=None):
......
638 688
        try:
639 689
            changes_since = request.GET.get("changes-since", 0)
640 690
            if changes_since:
641
                last_update = datetime.strptime(changes_since, "%Y-%m-%dT%H:%M:%S" )
691
                last_update = datetime.strptime(changes_since,
692
                                                "%Y-%m-%dT%H:%M:%S")
642 693
                #return a badRequest if the changes_since is older than a limit
643
                if datetime.now() - last_update > timedelta(seconds=settings.POLL_LIMIT):
644
                    raise fault.badRequest        
694
                if datetime.now() - last_update > timedelta(
695
                    seconds=settings.POLL_LIMIT):
696
                    raise fault.badRequest
645 697
                images = Image.objects.filter(updated__gt=last_update)
646 698
                if not len(images):
647 699
                    return notModified
648 700
            else:
649 701
                images = Image.objects.all()
650 702
        except Exception, e:
651
            raise fault.badRequest        
703
            raise fault.badRequest
652 704
        try:
653
            images_list = [ {'created': image.created.isoformat(), 
705
            images_list = [{'created': image.created.isoformat(),
654 706
                        'id': image.id,
655 707
                        'name': image.name,
656
                        'updated': image.updated.isoformat(),    
657
                        'status': image.state, 
658
                        'progress': image.state == 'ACTIVE' and 100 or 0, 
659
                        'size': image.size, 
708
                        'updated': image.updated.isoformat(),
709
                        'status': image.state,
710
                        'progress': image.state == 'ACTIVE' and 100 or 0,
711
                        'size': image.size,
660 712
                        'serverId': image.sourcevm and image.sourcevm.id or "",
661
                        #'metadata':[{'meta': { 'key': {metadata.meta_key: metadata.meta_value}}} for metadata in image.imagemetadata_set.all()]
662
                        'metadata':{'meta': { 'key': {'description': image.description}}},
713
                        #'metadata':[{'meta':
714
                        #{ 'key': {metadata.meta_key: metadata.meta_value}}}
715
                        #for metadata in image.imagemetadata_set.all()]
716
                        'metadata':{'meta':
717
                            {'key': {'description': image.description}}},
663 718
                       } for image in images]
664 719
            # Images info is stored in the DB. Ganeti is not aware of this
665 720
            if id == "detail":
666
                return { "images": images_list }
721
                return {"images": images_list}
667 722
            elif id is None:
668
                return { "images": [ { "id": s['id'], "name": s['name'] } for s in images_list ] }
669
            else:        
723
                return {"images": [{"id": s['id'], "name": s['name']} \
724
                    for s in images_list]}
725
            else:
670 726
                image = images.get(id=id)
671
                return { "image":  {'created': image.created.isoformat(), 
727
                return {"image":  {'created': image.created.isoformat(),
672 728
                    'id': image.id,
673 729
                    'name': image.name,
674
                    'updated': image.updated.isoformat(),    
675
                    'description': image.description, 
676
                    'status': image.state, 
677
                    'progress': image.state == 'ACTIVE' and 100 or 0, 
678
                    'size': image.size, 
730
                    'updated': image.updated.isoformat(),
731
                    'description': image.description,
732
                    'status': image.state,
733
                    'progress': image.state == 'ACTIVE' and 100 or 0,
734
                    'size': image.size,
679 735
                    'serverId': image.sourcevm and image.sourcevm.id or "",
680
                    #'metadata':[{'meta': { 'key': {metadata.meta_key: metadata.meta_value}}} for metadata in image.imagemetadata_set.all()]
681
                    'metadata':{'meta': { 'key': {'description': image.description}}},
682
                   } }
736
                    #'metadata':[{'meta': { 'key':
737
                    #{metadata.meta_key: metadata.meta_value}}}
738
                    #for metadata in image.imagemetadata_set.all()]
739
                    'metadata': {
740
                        'meta': {'key': {'description': image.description}}},
741
                   }}
683 742
        except Image.DoesNotExist:
684 743
                    raise fault.itemNotFound
685 744
        except Image.MultipleObjectsReturned:
......
717 776
            image = Image.objects.get(pk=id)
718 777
            return {
719 778
                "metadata": [{
720
                    "meta": { 
721
                        "key": {m.meta_key: m.meta_value}}} for m in image.imagemetadata_set.all()]
779
                    "meta": {
780
                        "key": {m.meta_key: m.meta_value}}} \
781
                    for m in image.imagemetadata_set.all()]
722 782
            }
723 783
        except Image.DoesNotExist:
724 784
            raise fault.itemNotFound
......
727 787
        except Exception, e:
728 788
            log.exception('Unexpected error: %s' % e)
729 789
            raise fault.serviceUnavailable
730
        
790

  
731 791
    def read_onekey(self, request, id, key):
732 792
        """Returns the specified metadata key of the specified server"""
733 793
        try:
......
735 795
            return {
736 796
                "metadata": {
737 797
                    "values": [
738
                        {m.meta_key: m.meta_value} for m in image.imagemetadata_set.filter(meta_key=key)
798
                        {m.meta_key: m.meta_value} \
799
                        for m in image.imagemetadata_set.filter(meta_key=key)
739 800
                    ]
740 801
                }
741 802
            }
742 803
        except ImageMetadata.DoesNotExist:
743
            raise fault.itemNotFound            
804
            raise fault.itemNotFound
744 805
        except Image.DoesNotExist:
745 806
            raise fault.itemNotFound
746 807
        except Image.MultipleObjectsReturned:
......
757 818
        try:
758 819
            metadata = json.loads(request.raw_post_data)['metadata']
759 820
        except Exception as e:
760
            log.exception('Malformed create request: %s - %s' % (e, request.raw_post_data))
821
            log.exception('Malformed create request: %s - %s' \
822
                          % (e, request.raw_post_data))
761 823
            raise fault.badRequest
762 824

  
763 825
        try:
764 826
            image = Image.objects.get(pk=id)
765 827
            for x in metadata.keys():
766
                img_meta, created = image.imagemetadata_set.get_or_create(meta_key=x)
767
                img_meta.meta_value = metadata[x] 
828
                img_meta, created = (image.imagemetadata_set.
829
                                     get_or_create(meta_key=x))
830
                img_meta.meta_value = metadata[x]
768 831
                img_meta.save()
769 832
            return {
770 833
                "metadata": [{
771
                    "meta": { 
772
                        "key": {m.meta_key: m.meta_value}}} for m in image.imagemetadata_set.all()]
773
            }        
834
                    "meta": {
835
                        "key": {m.meta_key: m.meta_value}}} \
836
                    for m in image.imagemetadata_set.all()]
837
            }
774 838
        except Image.DoesNotExist:
775 839
            raise fault.itemNotFound
776 840
        except Image.MultipleObjectsReturned:
......
784 848
            raise fault.serviceUnavailable
785 849

  
786 850
    def update(self, request, id, key=None):
787
        """Update or Create the specified metadata key for the specified Image"""
851
        """Update or Create the specified metadata key for the
852
        specified Image"""
788 853
        if key is None:
789 854
            log.exception('No metadata key specified in URL')
790 855
            raise fault.badRequest
......
792 857
            metadata = json.loads(request.raw_post_data)['meta']
793 858
            metadata_value = metadata[key]
794 859
        except Exception as e:
795
            log.exception('Malformed create request: %s - %s' % (e, request.raw_post_data))
860
            log.exception('Malformed create request: %s - %s' \
861
                          % (e, request.raw_post_data))
796 862
            raise fault.badRequest
797 863

  
798 864
        try:
799 865
            image = Image.objects.get(pk=id)
800
            img_meta, created = image.imagemetadata_set.get_or_create(meta_key=key)
801
            img_meta.meta_value = metadata_value 
866
            img_meta, created = (image.imagemetadata_set.
867
                                 get_or_create(meta_key=key))
868
            img_meta.meta_value = metadata_value
802 869
            img_meta.save()
803 870
            return {"meta": {img_meta.meta_key: img_meta.meta_value}}
804
        
871

  
805 872
        except Image.DoesNotExist:
806 873
            raise fault.itemNotFound
807 874
        except Image.MultipleObjectsReturned:
......
862 929
     creates, lists, deletes virtual machine groups
863 930

  
864 931
     @HTTP methods: GET, POST, DELETE
865
     @Parameters: POST data 
866
     @Responses: HTTP 202 if successfully get the Groups list, itemNotFound, serviceUnavailable otherwise
932
     @Parameters: POST data
933
     @Responses: HTTP 202 if successfully get the Groups list,
934
     itemNotFound, serviceUnavailable otherwise
867 935

  
868 936
    """
869 937

  
......
872 940
    def read(self, request, id=None):
873 941
        """List Groups"""
874 942
        try:
875
            vmgroups = VirtualMachineGroup.objects.all() 
876
            vmgroups_list = [ {'id': vmgroup.id, \
943
            vmgroups = VirtualMachineGroup.objects.all()
944
            vmgroups_list = [{'id': vmgroup.id, \
877 945
                  'name': vmgroup.name,  \
878
                   'server_id': [machine.id for machine in vmgroup.machines.all()] \
946
                   'server_id':
947
                    [machine.id for machine in vmgroup.machines.all()]
879 948
                   } for vmgroup in vmgroups]
880 949
            # Group info is stored in the DB. Ganeti is not aware of this
881 950
            if id == "detail":
882
                return { "groups": vmgroups_list }
951
                return {"groups": vmgroups_list}
883 952
            elif id is None:
884
                return { "groups": [ { "id": s['id'], "name": s['name'] } for s in vmgroups_list ] }
953
                return {"groups": [{"id": s['id'],
954
                                    "name": s['name']} for s in vmgroups_list]}
885 955
            else:
886 956
                vmgroup = vmgroups.get(id=id)
887 957

  
888
                return { "group":  {'id': vmgroup.id, \
889
                  'name': vmgroup.name,  \
890
                   'server_id': [machine.id for machine in vmgroup.machines.all()] \
891
                   } }
892

  
958
                return {"group":  {'id': vmgroup.id,
959
                  'name': vmgroup.name,
960
                   'server_id':
961
                    [machine.id for machine in vmgroup.machines.all()]
962
                   }}
893 963

  
894 964
        except VirtualMachineGroup.DoesNotExist:
895 965
                    raise fault.itemNotFound
......
899 969
                    log.exception('Unexpected error: %s' % e)
900 970
                    raise fault.serviceUnavailable
901 971

  
902

  
903

  
904 972
    def create(self, request, id):
905 973
        """Creates a Group"""
906 974
        return created
......
917 985

  
918 986
    rate = [
919 987
        {
920
           "verb" : "POST",
921
           "URI" : "*",
922
           "regex" : ".*",
923
           "value" : 10,
924
           "remaining" : 2,
925
           "unit" : "MINUTE",
926
           "resetTime" : 1244425439
988
           "verb": "POST",
989
           "URI": "*",
990
           "regex": ".*",
991
           "value": 10,
992
           "remaining": 2,
993
           "unit": "MINUTE",
994
           "resetTime": 1244425439
927 995
        },
928 996
        {
929
           "verb" : "POST",
930
           "URI" : "*/servers",
931
           "regex" : "^/servers",
932
           "value" : 25,
933
           "remaining" : 24,
934
           "unit" : "DAY",
935
           "resetTime" : 1244511839
997
           "verb": "POST",
998
           "URI": "*/servers",
999
           "regex": "^/servers",
1000
           "value": 25,
1001
           "remaining": 24,
1002
           "unit": "DAY",
1003
           "resetTime": 1244511839
936 1004
        },
937 1005
        {
938
           "verb" : "PUT",
939
           "URI" : "*",
940
           "regex" : ".*",
941
           "value" : 10,
942
           "remaining" : 2,
943
           "unit" : "MINUTE",
944
           "resetTime" : 1244425439
1006
           "verb": "PUT",
1007
           "URI": "*",
1008
           "regex": ".*",
1009
           "value": 10,
1010
           "remaining": 2,
1011
           "unit": "MINUTE",
1012
           "resetTime": 1244425439
945 1013
        },
946 1014
        {
947
           "verb" : "GET",
948
           "URI" : "*",
949
           "regex" : ".*",
950
           "value" : 3,
951
           "remaining" : 3,
952
           "unit" : "MINUTE",
953
           "resetTime" : 1244425439
1015
           "verb": "GET",
1016
           "URI": "*",
1017
           "regex": ".*",
1018
           "value": 3,
1019
           "remaining": 3,
1020
           "unit": "MINUTE",
1021
           "resetTime": 1244425439
954 1022
        },
955 1023
        {
956
           "verb" : "DELETE",
957
           "URI" : "*",
958
           "regex" : ".*",
959
           "value" : 100,
960
           "remaining" : 100,
961
           "unit" : "MINUTE",
962
           "resetTime" : 1244425439
1024
           "verb": "DELETE",
1025
           "URI": "*",
1026
           "regex": ".*",
1027
           "value": 100,
1028
           "remaining": 100,
1029
           "unit": "MINUTE",
1030
           "resetTime": 1244425439
963 1031
        }
964 1032
    ]
965 1033

  
966 1034
    absolute = {
967
        "maxTotalRAMSize" : 51200,
968
        "maxIPGroups" : 50,
969
        "maxIPGroupMembers" : 25
1035
        "maxTotalRAMSize": 51200,
1036
        "maxIPGroups": 50,
1037
        "maxIPGroupMembers": 25
970 1038
    }
971 1039

  
972 1040
    def read(self, request):
973
        return { "limits": {
1041
        return {"limits": {
974 1042
                "rate": self.rate,
975 1043
                "absolute": self.absolute,
976 1044
               }
......
991 1059

  
992 1060
    def read_one(self, request, id):
993 1061
        """List one Disk with the specified id with all details"""
994
        # FIXME Get detailed info from the DB 
1062
        # FIXME Get detailed info from the DB
995 1063
        # for the Disk with the specified id
996 1064
        try:
997 1065
            disk = Disk.objects.get(pk=id)
998 1066
            disk_details = {
999
                "id" : disk.id, 
1000
                "name" : disk.name, 
1001
                "size" : disk.size,
1002
                "created" : disk.created, 
1003
                "serverId" : disk.vm.id
1067
                "id": disk.id,
1068
                "name": disk.name,
1069
                "size": disk.size,
1070
                "created": disk.created,
1071
                "serverId": disk.vm.id
1004 1072
            }
1005
            return { "disks" : disk_details }
1073
            return {"disks": disk_details}
1006 1074
        except:
1007 1075
            raise fault.itemNotFound
1008 1076

  
......
1011 1079
        """List all Disks. If -detail- is set list them with all details"""
1012 1080
        if not detail:
1013 1081
            disks = Disk.objects.filter(owner=SynnefoUser.objects.all()[0])
1014
            return { "disks": [ { "id": disk.id, "name": disk.name } for disk in disks ] }
1082
            return {"disks": [{"id": disk.id, "name": disk.name} \
1083
                for disk in disks]}
1015 1084
        else:
1016 1085
            disks = Disk.objects.filter(owner=SynnefoUser.objects.all()[0])
1017
            disks_details = [ {
1018
                "id" : disk.id, 
1019
                "name" : disk.name,
1020
                "size" : disk.size,
1021
                "created" : disk.created, 
1022
                "serverId" : disk.vm.id,
1023
            } for disk in disks ]
1024
            return { "disks":  disks_details }                
1086
            disks_details = [{
1087
                "id": disk.id,
1088
                "name": disk.name,
1089
                "size": disk.size,
1090
                "created": disk.created,
1091
                "serverId": disk.vm.id,
1092
            } for disk in disks]
1093
            return {"disks":  disks_details}
1025 1094

  
1026 1095
    def create(self, request):
1027 1096
        """Create a new Disk"""
1028
        # FIXME Create a partial DB entry, 
1097
        # FIXME Create a partial DB entry,
1029 1098
        # then call the backend for actual creation
1030 1099
        pass
1031 1100

  

Also available in: Unified diff