Statistics
| Branch: | Tag: | Revision:

root / api / tests.py @ aa519091

History | View | Annotate | Download (32.2 kB)

1
#
2
# Copyright (c) 2010 Greek Research and Technology Network
3
#
4

    
5
from __future__ import with_statement
6

    
7
from collections import defaultdict
8
from email.utils import parsedate
9
from random import choice, randint, sample
10
from time import mktime
11

    
12
from django.utils import simplejson as json
13
from django.test import TestCase
14
from django.test.client import Client
15

    
16
from synnefo.db.models import *
17
from synnefo.logic.utils import get_rsapi_state
18

    
19

    
20
class AaiClient(Client):
21
    def request(self, **request):
22
        request['HTTP_X_AUTH_TOKEN'] = '46e427d657b20defe352804f0eb6f8a2'
23
        return super(AaiClient, self).request(**request)
24

    
25

    
26
class APITestCase(TestCase):
27
    fixtures = ['api_test_data']
28
    test_server_id = 1001
29
    test_image_id = 1
30
    test_flavor_id = 1
31
    test_group_id = 1
32
    test_wrong_server_id = 99999999
33
    test_wrong_image_id = 99999999
34
    test_wrong_flavor_id = 99999999
35
    test_wrong_group_id = 99999999
36
    #make the testing with these id's
37

    
38
    def setUp(self):
39
        self.client = AaiClient()
40

    
41
    def test_api_version(self):
42
        """Check API version."""
43
        
44
        response = self.client.get('/api/v1.1/')
45
        self.assertEqual(response.status_code, 200)
46
        api_version = json.loads(response.content)['version']
47
        self.assertEqual(api_version['id'], 'v1.1')
48
        self.assertEqual(api_version['status'], 'CURRENT')
49

    
50
    def test_server_list(self):
51
        """Test if the expected list of servers is returned."""
52
        
53
        response = self.client.get('/api/v1.1/servers')
54
        vms_from_api = json.loads(response.content)['servers']['values']
55
        vms_from_db = VirtualMachine.objects.filter(deleted=False)
56
        self.assertEqual(len(vms_from_api), len(vms_from_db))
57
        self.assertTrue(response.status_code in [200, 203])
58
        for vm_from_api in vms_from_api:
59
            vm_from_db = VirtualMachine.objects.get(id=vm_from_api['id'])
60
            self.assertEqual(vm_from_api['id'], vm_from_db.id)
61
            self.assertEqual(vm_from_api['name'], vm_from_db.name)
62

    
63
    def test_server_details(self):
64
        """Test if the expected server is returned."""
65
        
66
        response = self.client.get('/api/v1.1/servers/%d' % self.test_server_id)
67
        vm_from_api = json.loads(response.content)['server']
68
        vm_from_db = VirtualMachine.objects.get(id=self.test_server_id)
69
        self.assertEqual(vm_from_api['flavorRef'], vm_from_db.flavor.id)
70
        self.assertEqual(vm_from_api['hostId'], vm_from_db.hostid)
71
        self.assertEqual(vm_from_api['id'], vm_from_db.id)
72
        self.assertEqual(vm_from_api['imageRef'], vm_from_db.flavor.id)
73
        self.assertEqual(vm_from_api['name'], vm_from_db.name)
74
        self.assertEqual(vm_from_api['status'], get_rsapi_state(vm_from_db))
75
        self.assertTrue(response.status_code in [200, 203])
76

    
77
    def test_servers_details(self):
78
        """Test if the servers details are returned."""
79
        
80
        response = self.client.get('/api/v1.1/servers/detail')
81

    
82
        # Make sure both DB and API responses are sorted by id,
83
        # to allow for 1-1 comparisons
84
        vms_from_db = VirtualMachine.objects.filter(deleted=False).order_by('id')
85
        vms_from_api = json.loads(response.content)['servers']['values']
86
        vms_from_api = sorted(vms_from_api, key=lambda vm: vm['id'])
87
        self.assertEqual(len(vms_from_db), len(vms_from_api))
88

    
89
        id_list = [vm.id for vm in vms_from_db]
90
        number = 0
91
        for vm_id in id_list:
92
            vm_from_api = vms_from_api[number]
93
            vm_from_db = VirtualMachine.objects.get(id=vm_id)
94
            self.assertEqual(vm_from_api['flavorRef'], vm_from_db.flavor.id)
95
            self.assertEqual(vm_from_api['hostId'], vm_from_db.hostid)
96
            self.assertEqual(vm_from_api['id'], vm_from_db.id)
97
            self.assertEqual(vm_from_api['imageRef'], vm_from_db.flavor.id)
98
            self.assertEqual(vm_from_api['name'], vm_from_db.name)
99
            self.assertEqual(vm_from_api['status'], get_rsapi_state(vm_from_db))
100
            number += 1
101
        for vm_from_api in vms_from_api:
102
            vm_from_db = VirtualMachine.objects.get(id=vm_from_api['id'])
103
            self.assertEqual(vm_from_api['flavorRef'], vm_from_db.flavor.id)
104
            self.assertEqual(vm_from_api['hostId'], vm_from_db.hostid)
105
            self.assertEqual(vm_from_api['id'], vm_from_db.id)
106
            self.assertEqual(vm_from_api['imageRef'], vm_from_db.flavor.id)
107
            self.assertEqual(vm_from_api['name'], vm_from_db.name)
108
            self.assertEqual(vm_from_api['status'], get_rsapi_state(vm_from_db))
109
        self.assertTrue(response.status_code in [200,203])
110

    
111
    def test_wrong_server(self):
112
        """Test 404 response if server does not exist."""
113
        
114
        response = self.client.get('/api/v1.1/servers/%d' % self.test_wrong_server_id)
115
        self.assertEqual(response.status_code, 404)
116

    
117
    def test_create_server_empty(self):
118
        """Test if the create server call returns a 400 badRequest if
119
           no attributes are specified."""
120
        
121
        response = self.client.post('/api/v1.1/servers', {})
122
        self.assertEqual(response.status_code, 400)
123

    
124
    def test_create_server(self):
125
        """Test if the create server call returns the expected response
126
           if a valid request has been speficied."""
127
        
128
        request = {
129
                    "server": {
130
                        "name": "new-server-test",
131
                        "owner": 1,
132
                        "imageRef": 1,
133
                        "flavorRef": 1,
134
                        "metadata": {
135
                            "My Server Name": "Apache1"
136
                        },
137
                        "personality": []
138
                    }
139
        }
140
        response = self.client.post('/api/v1.1/servers', json.dumps(request),
141
                                    content_type='application/json')
142
        self.assertEqual(response.status_code, 202)
143
        #TODO: check response.content
144
        #TODO: check create server with wrong options (eg non existing flavor)
145

    
146
    def test_server_polling(self):
147
        """Test if the server polling works as expected."""
148
        
149
        response = self.client.get('/api/v1.1/servers/detail')
150
        vms_from_api_initial = json.loads(response.content)['servers']['values']
151
        ts = mktime(parsedate(response['Date']))
152
        since = datetime.datetime.fromtimestamp(ts).isoformat() + 'Z'
153
        response = self.client.get('/api/v1.1/servers/detail?changes-since=%s' % since)
154
        self.assertEqual(len(response.content), 0)
155

    
156
        #now create a machine. Then check if it is on the list
157
        request = {
158
                    "server": {
159
                        "name": "new-server-test",
160
                        "imageRef": 1,
161
                        "flavorRef": 1,
162
                        "metadata": {
163
                            "My Server Name": "Apache1"
164
                        },
165
                        "personality": []
166
                    }
167
        }
168
        
169
        path = '/api/v1.1/servers'
170
        response = self.client.post(path, json.dumps(request), content_type='application/json')
171
        self.assertEqual(response.status_code, 202)
172

    
173
        response = self.client.get('/api/v1.1/servers/detail?changes-since=%s' % since)
174
        self.assertEqual(response.status_code, 200)
175
        vms_from_api_after = json.loads(response.content)['servers']['values']
176
        #make sure the newly created server is included on the updated list
177
        self.assertEqual(len(vms_from_api_after), 1)
178

    
179
    def test_reboot_server(self):
180
        """Test if the specified server is rebooted."""
181
        
182
        request = {'reboot': {'type': 'HARD'}}
183
        path = '/api/v1.1/servers/%d/action' % self.test_server_id
184
        response = self.client.post(path, json.dumps(request), content_type='application/json')
185
        self.assertEqual(response.status_code, 202)
186
        #server id that does not exist
187
        path = '/api/v1.1/servers/%d/action' % self.test_wrong_server_id
188
        response = self.client.post(path, json.dumps(request), content_type='application/json')
189
        self.assertEqual(response.status_code, 404)
190

    
191
    def test_shutdown_server(self):
192
        """Test if the specified server is shutdown."""
193
        
194
        request = {'shutdown': {}}
195
        path = '/api/v1.1/servers/%d/action' % self.test_server_id
196
        response = self.client.post(path, json.dumps(request), content_type='application/json')
197
        self.assertEqual(response.status_code, 202)
198
        #server id that does not exist
199
        path = '/api/v1.1/servers/%d/action' % self.test_wrong_server_id
200
        response = self.client.post(path, json.dumps(request), content_type='application/json')
201
        self.assertEqual(response.status_code, 404)
202

    
203
    def test_start_server(self):
204
        """Test if the specified server is started."""
205
        
206
        request = {'start': {}}
207
        path = '/api/v1.1/servers/%d/action' % self.test_server_id
208
        response = self.client.post(path, json.dumps(request), content_type='application/json')
209
        self.assertEqual(response.status_code, 202)
210
        #server id that does not exist
211
        path = '/api/v1.1/servers/%d/action' % self.test_wrong_server_id
212
        response = self.client.post(path, json.dumps(request), content_type='application/json')
213
        self.assertEqual(response.status_code, 404)
214

    
215
    def test_delete_server(self):
216
        """Test if the specified server is deleted."""
217
        response = self.client.delete('/api/v1.1/servers/%d' % self.test_server_id)
218
        self.assertEqual(response.status_code, 204)
219
        #server id that does not exist
220
        response = self.client.delete('/api/v1.1/servers/%d' % self.test_wrong_server_id)
221
        self.assertEqual(response.status_code, 404)
222

    
223
    def test_flavor_list(self):
224
        """Test if the expected list of flavors is returned by."""
225
        
226
        response = self.client.get('/api/v1.1/flavors')
227
        flavors_from_api = json.loads(response.content)['flavors']['values']
228
        flavors_from_db = Flavor.objects.all()
229
        self.assertEqual(len(flavors_from_api), len(flavors_from_db))
230
        self.assertTrue(response.status_code in [200, 203])
231
        for flavor_from_api in flavors_from_api:
232
            flavor_from_db = Flavor.objects.get(id=flavor_from_api['id'])
233
            self.assertEqual(flavor_from_api['id'], flavor_from_db.id)
234
            self.assertEqual(flavor_from_api['name'], flavor_from_db.name)
235

    
236
    def test_flavors_details(self):
237
        """Test if the flavors details are returned."""
238
        
239
        response = self.client.get('/api/v1.1/flavors/detail')
240
        flavors_from_db = Flavor.objects.all()
241
        flavors_from_api = json.loads(response.content)['flavors']['values']
242

    
243
        # Assert that all flavors in the db appear inthe API call result
244
        for i in range(0, len(flavors_from_db)):
245
            flavor_from_api = flavors_from_api[i]
246
            flavor_from_db = Flavor.objects.get(id=flavors_from_db[i].id)
247
            self.assertEqual(flavor_from_api['cpu'], flavor_from_db.cpu)
248
            self.assertEqual(flavor_from_api['id'], flavor_from_db.id)
249
            self.assertEqual(flavor_from_api['disk'], flavor_from_db.disk)
250
            self.assertEqual(flavor_from_api['name'], flavor_from_db.name)
251
            self.assertEqual(flavor_from_api['ram'], flavor_from_db.ram)
252

    
253
        # Assert that all flavors returned by the API also exist in the db
254
        for flavor_from_api in flavors_from_api:
255
            flavor_from_db = Flavor.objects.get(id=flavor_from_api['id'])
256
            self.assertEqual(flavor_from_api['cpu'], flavor_from_db.cpu)
257
            self.assertEqual(flavor_from_api['id'], flavor_from_db.id)
258
            self.assertEqual(flavor_from_api['disk'], flavor_from_db.disk)
259
            self.assertEqual(flavor_from_api['name'], flavor_from_db.name)
260
            self.assertEqual(flavor_from_api['ram'], flavor_from_db.ram)
261

    
262
        # Check if we have the right status_code
263
        self.assertTrue(response.status_code in [200, 203])
264

    
265
    def test_flavor_details(self):
266
        """Test if the expected flavor is returned."""
267
        
268
        response = self.client.get('/api/v1.1/flavors/%d' % self.test_flavor_id)
269
        flavor_from_api = json.loads(response.content)['flavor']
270
        flavor_from_db = Flavor.objects.get(id=self.test_flavor_id)
271
        self.assertEqual(flavor_from_api['cpu'], flavor_from_db.cpu)
272
        self.assertEqual(flavor_from_api['id'], flavor_from_db.id)
273
        self.assertEqual(flavor_from_api['disk'], flavor_from_db.disk)
274
        self.assertEqual(flavor_from_api['name'], flavor_from_db.name)
275
        self.assertEqual(flavor_from_api['ram'], flavor_from_db.ram)
276
        self.assertTrue(response.status_code in [200, 203])
277

    
278
    def test_wrong_flavor(self):
279
        """Test 404 result when requesting a flavor that does not exist."""
280
        
281
        response = self.client.get('/api/v1.1/flavors/%d' % self.test_wrong_flavor_id)
282
        self.assertTrue(response.status_code in [404, 503])
283

    
284
    def test_image_list(self):
285
        """Test if the expected list of images is returned by the API."""
286
        
287
        response = self.client.get('/api/v1.1/images')
288
        images_from_api = json.loads(response.content)['images']['values']
289
        images_from_db = Image.objects.all()
290
        self.assertEqual(len(images_from_api), len(images_from_db))
291
        self.assertTrue(response.status_code in [200, 203])
292
        for image_from_api in images_from_api:
293
            image_from_db = Image.objects.get(id=image_from_api['id'])
294
            self.assertEqual(image_from_api['id'], image_from_db.id)
295
            self.assertEqual(image_from_api['name'], image_from_db.name)
296

    
297
    def test_wrong_image(self):
298
        """Test 404 result if a non existent image is requested."""
299
        
300
        response = self.client.get('/api/v1.1/images/%d' % self.test_wrong_image_id)
301
        self.assertEqual(response.status_code, 404)
302

    
303
    def test_server_metadata(self):
304
        """Test server's metadata (add, edit)."""
305
        
306
        key = 'name'
307
        request = {'meta': {key: 'a fancy name'}}
308
        
309
        path = '/api/v1.1/servers/%d/meta/%s' % (self.test_server_id, key)
310
        response = self.client.put(path, json.dumps(request), content_type='application/json')
311
        self.assertEqual(response.status_code, 201)
312

    
313

    
314
def create_users(n=1):
315
    for i in range(n):
316
        SynnefoUser.objects.create(
317
            name='User %d' % i,
318
            credit=0)
319

    
320
def create_flavors(n=1):
321
    for i in range(n):
322
        Flavor.objects.create(
323
            cpu=randint(1, 4),
324
            ram=randint(1, 8) * 512,
325
            disk=randint(1, 40))
326

    
327
def create_images(n=1):
328
    users = SynnefoUser.objects.all()
329
    for i in range(n):
330
        Image.objects.create(
331
            name='Image %d' % (i + 1),
332
            state='ACTIVE',
333
            owner=choice(users))
334

    
335
def create_image_metadata(n=1):
336
    images = Image.objects.all()
337
    for i in range(n):
338
        ImageMetadata.objects.create(
339
            meta_key='Key%d' % (i + 1),
340
            meta_value='Value %d' % (i + 1),
341
            image = choice(images))
342

    
343
def create_servers(n=1):
344
    users = SynnefoUser.objects.all()
345
    flavors = Flavor.objects.all()
346
    images = Image.objects.all()
347
    for i in range(n):
348
        VirtualMachine.objects.create(
349
            name='Server %d' % (i + 1),
350
            owner=choice(users),
351
            sourceimage=choice(images),
352
            hostid=str(i),
353
            ipfour='0.0.0.0',
354
            ipsix='::1',
355
            flavor=choice(flavors))
356

    
357
def create_server_metadata(n=1):
358
    servers = VirtualMachine.objects.all()
359
    for i in range(n):
360
        VirtualMachineMetadata.objects.create(
361
            meta_key='Key%d' % (i + 1),
362
            meta_value='Value %d' % (i + 1),
363
            vm = choice(servers))
364

    
365

    
366
class AssertInvariant(object):
367
    def __init__(self, callable, *args, **kwargs):
368
        self.callable = callable
369
        self.args = args
370
        self.kwargs = kwargs
371
    
372
    def __enter__(self):
373
        self.value = self.callable(*self.args, **self.kwargs)
374
        return self.value
375
    
376
    def __exit__(self, type, value, tb):
377
        assert self.value == self.callable(*self.args, **self.kwargs)
378

    
379

    
380
class BaseTestCase(TestCase):
381
    USERS = 0
382
    FLAVORS = 1
383
    IMAGES = 1
384
    SERVERS = 1
385
    SERVER_METADATA = 0
386
    IMAGE_METADATA = 0
387
    
388
    def setUp(self):
389
        self.client = AaiClient()
390
        create_users(self.USERS)
391
        create_flavors(self.FLAVORS)
392
        create_images(self.IMAGES)
393
        create_image_metadata(self.IMAGE_METADATA)
394
        create_servers(self.SERVERS)
395
        create_server_metadata(self.SERVER_METADATA)
396
    
397
    def assertFault(self, response, status_code, name):
398
        self.assertEqual(response.status_code, status_code)
399
        fault = json.loads(response.content)
400
        self.assertEqual(fault.keys(), [name])
401
    
402
    def assertBadRequest(self, response):
403
        self.assertFault(response, 400, 'badRequest')
404

    
405
    def assertItemNotFound(self, response):
406
        self.assertFault(response, 404, 'itemNotFound')
407
    
408
    
409
    def list_images(self, detail=False):
410
        path = '/api/v1.1/images'
411
        if detail:
412
            path += '/detail'
413
        response = self.client.get(path)
414
        self.assertTrue(response.status_code in (200, 203))
415
        reply = json.loads(response.content)
416
        self.assertEqual(reply.keys(), ['images'])
417
        self.assertEqual(reply['images'].keys(), ['values'])
418
        return reply['images']['values']
419
    
420
    def list_metadata(self, path):
421
        response = self.client.get(path)
422
        self.assertTrue(response.status_code in (200, 203))
423
        reply = json.loads(response.content)
424
        self.assertEqual(reply.keys(), ['metadata'])
425
        self.assertEqual(reply['metadata'].keys(), ['values'])
426
        return reply['metadata']['values']
427
    
428
    def list_server_metadata(self, server_id):
429
        path = '/api/v1.1/servers/%d/meta' % server_id
430
        return self.list_metadata(path)
431
    
432
    def list_image_metadata(self, image_id):
433
        path = '/api/v1.1/images/%d/meta' % image_id
434
        return self.list_metadata(path)
435
    
436
    def update_metadata(self, path, metadata):
437
        data = json.dumps({'metadata': metadata})
438
        response = self.client.post(path, data, content_type='application/json')
439
        self.assertEqual(response.status_code, 201)
440
        reply = json.loads(response.content)
441
        self.assertEqual(reply.keys(), ['metadata'])
442
        return reply['metadata']
443
    
444
    def update_server_metadata(self, server_id, metadata):
445
        path = '/api/v1.1/servers/%d/meta' % server_id
446
        return self.update_metadata(path, metadata)
447
    
448
    def update_image_metadata(self, image_id, metadata):
449
        path = '/api/v1.1/images/%d/meta' % image_id
450
        return self.update_metadata(path, metadata)
451
    
452
    def create_server_meta(self, server_id, meta):
453
        key = meta.keys()[0]
454
        path = '/api/v1.1/servers/%d/meta/%s' % (server_id, key)
455
        data = json.dumps({'meta': meta})
456
        response = self.client.put(path, data, content_type='application/json')
457
        self.assertEqual(response.status_code, 201)
458
        reply = json.loads(response.content)
459
        self.assertEqual(reply.keys(), ['meta'])
460
        response_meta = reply['meta']
461
        self.assertEqual(response_meta, meta)
462
    
463
    def get_all_server_metadata(self):
464
        metadata = defaultdict(dict)
465
        for m in VirtualMachineMetadata.objects.all():
466
            metadata[m.vm.id][m.meta_key] = m.meta_value
467
        return metadata
468
    
469
    def get_all_image_metadata(self):
470
        metadata = defaultdict(dict)
471
        for m in ImageMetadata.objects.all():
472
            metadata[m.image.id][m.meta_key] = m.meta_value
473
        return metadata
474

    
475

    
476
class ListImages(BaseTestCase):
477
    IMAGES = 10
478
    
479
    def _pop_image(self, images, image_id):
480
        for i in range(len(images)):
481
            image = images[i]
482
            if image['id'] == image_id:
483
                del images[i]
484
                return image
485
        return None
486
    
487
    def test_list_images(self):
488
        images = self.list_images()
489
        keys = set(['id', 'name'])
490
        for img in Image.objects.all():
491
            image = self._pop_image(images, img.id)
492
            self.assertTrue(image is not None)
493
            self.assertEqual(set(image.keys()), keys)
494
            self.assertEqual(image['id'], img.id)
495
            self.assertEqual(image['name'], img.name)
496
        self.assertEqual(images, [])
497
    
498
    def test_list_images_detail(self):
499
        images = self.list_images(detail=True)
500
        keys = set(['id', 'name', 'updated', 'created', 'status', 'progress'])
501
        for img in Image.objects.all():
502
            image = self._pop_image(images, img.id)
503
            self.assertTrue(image is not None)
504
            self.assertEqual(set(image.keys()), keys)
505
            self.assertEqual(image['id'], img.id)
506
            self.assertEqual(image['name'], img.name)
507
            self.assertEqual(image['status'], img.state)
508
            self.assertEqual(image['progress'], 100 if img.state == 'ACTIVE' else 0)
509
        self.assertEqual(images, [])
510

    
511

    
512
class ListServerMetadata(BaseTestCase):
513
    SERVERS = 5
514
    SERVER_METADATA = 100
515
    
516
    def test_list_metadata(self):
517
        with AssertInvariant(self.get_all_server_metadata) as metadata:
518
            for vm in VirtualMachine.objects.all():
519
                response_metadata = self.list_server_metadata(vm.id)
520
                self.assertEqual(response_metadata, metadata[vm.id])
521
    
522
    def test_invalid_server(self):
523
        with AssertInvariant(self.get_all_server_metadata):
524
            response = self.client.get('/api/v1.1/servers/0/meta')
525
            self.assertItemNotFound(response)
526

    
527

    
528
class UpdateServerMetadata(BaseTestCase):
529
    SERVER_METADATA = 10
530
    
531
    def test_update_metadata(self):
532
        metadata = self.get_all_server_metadata()
533
        server_id = choice(metadata.keys())
534
        new_metadata = {}
535
        for key in sample(metadata[server_id].keys(), 3):
536
            new_metadata[key] = 'New %s value' % key
537
        response_metadata = self.update_server_metadata(server_id, new_metadata)
538
        self.assertEqual(response_metadata, new_metadata)
539
        metadata[server_id].update(new_metadata)
540
        self.assertEqual(metadata, self.get_all_server_metadata())
541
    
542
    def test_does_not_create(self):
543
        with AssertInvariant(self.get_all_server_metadata) as metadata:
544
            server_id = choice(metadata.keys())
545
            new_metadata = {'Foo': 'Bar'}
546
            response_metadata = self.update_server_metadata(server_id, new_metadata)
547
            self.assertEqual(response_metadata, {})
548
    
549
    def test_invalid_data(self):
550
        with AssertInvariant(self.get_all_server_metadata) as metadata:
551
            server_id = choice(metadata.keys())
552
            path = '/api/v1.1/servers/%d/meta' % server_id
553
            response = self.client.post(path, 'metadata', content_type='application/json')
554
            self.assertBadRequest(response)
555
    
556
    def test_invalid_server(self):
557
        with AssertInvariant(self.get_all_server_metadata):
558
            path = '/api/v1.1/servers/0/meta'
559
            data = json.dumps({'metadata': {'Key1': 'A Value'}})
560
            response = self.client.post(path, data, content_type='application/json')
561
            self.assertItemNotFound(response)
562

    
563

    
564
class GetServerMetadataItem(BaseTestCase):
565
    SERVERS = 5
566
    SERVER_METADATA = 100
567
    
568
    def test_get_metadata_item(self):
569
        with AssertInvariant(self.get_all_server_metadata) as metadata:
570
            server_id = choice(metadata.keys())
571
            key = choice(metadata[server_id].keys())
572
            path = '/api/v1.1/servers/%d/meta/%s' % (server_id, key)
573
            response = self.client.get(path)
574
            self.assertTrue(response.status_code in (200, 203))
575
            reply = json.loads(response.content)
576
            self.assertEqual(reply['meta'], {key: metadata[server_id][key]})
577
    
578
    def test_invalid_key(self):
579
        with AssertInvariant(self.get_all_server_metadata) as metadata:
580
            server_id = choice(metadata.keys())
581
            response = self.client.get('/api/v1.1/servers/%d/meta/foo' % server_id)
582
            self.assertItemNotFound(response)
583
    
584
    def test_invalid_server(self):
585
        with AssertInvariant(self.get_all_server_metadata):
586
            response = self.client.get('/api/v1.1/servers/0/meta/foo')
587
            self.assertItemNotFound(response)
588

    
589

    
590
class CreateServerMetadataItem(BaseTestCase):
591
    SERVER_METADATA = 10
592
    
593
    def test_create_metadata(self):
594
        metadata = self.get_all_server_metadata()
595
        server_id = choice(metadata.keys())
596
        meta = {'Foo': 'Bar'}
597
        self.create_server_meta(server_id, meta)
598
        metadata[server_id].update(meta)
599
        self.assertEqual(metadata, self.get_all_server_metadata())
600
    
601
    def test_update_metadata(self):
602
        metadata = self.get_all_server_metadata()
603
        server_id = choice(metadata.keys())
604
        key = choice(metadata[server_id].keys())
605
        meta = {key: 'New Value'}
606
        self.create_server_meta(server_id, meta)
607
        metadata[server_id].update(meta)
608
        self.assertEqual(metadata, self.get_all_server_metadata())
609
    
610
    def test_invalid_server(self):
611
        with AssertInvariant(self.get_all_server_metadata):
612
            path = '/api/v1.1/servers/0/meta/foo'
613
            data = json.dumps({'meta': {'foo': 'bar'}})
614
            response = self.client.put(path, data, content_type='application/json')
615
            self.assertItemNotFound(response)
616
    
617
    def test_invalid_key(self):
618
        with AssertInvariant(self.get_all_server_metadata) as metadata:
619
            server_id = choice(metadata.keys())
620
            path = '/api/v1.1/servers/%d/meta/baz' % server_id
621
            data = json.dumps({'meta': {'foo': 'bar'}})
622
            response = self.client.put(path, data, content_type='application/json')
623
            self.assertBadRequest(response)
624
    
625
    def test_invalid_data(self):
626
        with AssertInvariant(self.get_all_server_metadata) as metadata:
627
            server_id = choice(metadata.keys())
628
            path = '/api/v1.1/servers/%d/meta/foo' % server_id
629
            response = self.client.put(path, 'meta', content_type='application/json')
630
            self.assertBadRequest(response)
631

    
632

    
633
class DeleteServerMetadataItem(BaseTestCase):
634
    SERVER_METADATA = 10
635
    
636
    def test_delete_metadata(self):
637
        metadata = self.get_all_server_metadata()
638
        server_id = choice(metadata.keys())
639
        key = choice(metadata[server_id].keys())
640
        path = '/api/v1.1/servers/%d/meta/%s' % (server_id, key)
641
        response = self.client.delete(path)
642
        self.assertEqual(response.status_code, 204)
643
        metadata[server_id].pop(key)
644
        self.assertEqual(metadata, self.get_all_server_metadata())
645
    
646
    def test_invalid_server(self):
647
        with AssertInvariant(self.get_all_server_metadata):
648
            response = self.client.delete('/api/v1.1/servers/9/meta/Key1')
649
            self.assertItemNotFound(response)
650
    
651
    def test_invalid_key(self):
652
        with AssertInvariant(self.get_all_server_metadata) as metadata:
653
            server_id = choice(metadata.keys())
654
            path = '/api/v1.1/servers/%d/meta/foo' % server_id
655
            response = self.client.delete(path)
656
            self.assertItemNotFound(response)
657

    
658

    
659
class ListImageMetadata(BaseTestCase):
660
    IMAGES = 5
661
    IMAGE_METADATA = 100
662

    
663
    def test_list_metadata(self):
664
        with AssertInvariant(self.get_all_image_metadata) as metadata:
665
            for image in Image.objects.all():
666
                response_metadata = self.list_image_metadata(image.id)
667
                self.assertEqual(response_metadata, metadata[image.id])
668

    
669
    def test_invalid_image(self):
670
        with AssertInvariant(self.get_all_image_metadata):
671
            response = self.client.get('/api/v1.1/images/0/meta')
672
            self.assertItemNotFound(response)
673

    
674
class UpdateImageMetadata(BaseTestCase):
675
    IMAGE_METADATA = 10
676

    
677
    def test_update_metadata(self):
678
        metadata = self.get_all_image_metadata()
679
        image_id = choice(metadata.keys())
680
        new_metadata = {}
681
        for key in sample(metadata[image_id].keys(), 3):
682
            new_metadata[key] = 'New %s value' % key
683
        response_metadata = self.update_image_metadata(image_id, new_metadata)
684
        self.assertEqual(response_metadata, new_metadata)
685
        metadata[image_id].update(new_metadata)
686
        self.assertEqual(metadata, self.get_all_image_metadata())
687

    
688
    def test_does_not_create(self):
689
        with AssertInvariant(self.get_all_image_metadata) as metadata:
690
            image_id = choice(metadata.keys())
691
            new_metadata = {'Foo': 'Bar'}
692
            response_metadata = self.update_image_metadata(image_id, new_metadata)
693
            self.assertEqual(response_metadata, {})
694

    
695
    def test_invalid_data(self):
696
        with AssertInvariant(self.get_all_image_metadata) as metadata:
697
            image_id = choice(metadata.keys())
698
            path = '/api/v1.1/images/%d/meta' % image_id
699
            response = self.client.post(path, 'metadata', content_type='application/json')
700
            self.assertBadRequest(response)
701

    
702
    def test_invalid_server(self):
703
        with AssertInvariant(self.get_all_image_metadata):
704
            path = '/api/v1.1/images/0/meta'
705
            data = json.dumps({'metadata': {'Key1': 'A Value'}})
706
            response = self.client.post(path, data, content_type='application/json')
707
            self.assertItemNotFound(response)
708

    
709

    
710
class ServerVNCConsole(BaseTestCase):
711
    SERVERS = 1
712

    
713
    def test_not_active_server(self):
714
        """Test console req for server not in ACTIVE state returns badRequest"""
715
        server_id = choice(VirtualMachine.objects.all()).id
716
        path = '/api/v1.1/servers/%d/action' % server_id
717
        data = json.dumps({'console': {'type': 'vnc'}})
718
        response = self.client.post(path, data, content_type='application/json')
719
        self.assertBadRequest(response)
720

    
721
    def test_active_server(self):
722
        """Test console req for ACTIVE server"""
723
        server_id = choice(VirtualMachine.objects.all()).id
724
        # FIXME: Start the server properly, instead of tampering with the DB
725
        vm = choice(VirtualMachine.objects.all())
726
        vm.operstate = 'STARTED'
727
        vm.save()
728
        server_id = vm.id
729
        
730
        path = '/api/v1.1/servers/%d/action' % server_id
731
        data = json.dumps({'console': {'type': 'vnc'}})
732
        response = self.client.post(path, data, content_type='application/json')
733
        self.assertEqual(response.status_code, 200)
734
        reply = json.loads(response.content)
735
        self.assertEqual(reply.keys(), ['console'])
736
        console = reply['console']
737
        self.assertEqual(console['type'], 'vnc')
738
        self.assertEqual(set(console.keys()), set(['type', 'host', 'port', 'password']))
739

    
740
class AaiTestCase(TestCase):
741
    fixtures = ['api_test_data', 'auth_test_data']
742
    apibase = '/api/v1.1'
743

    
744
    def setUp(self):
745
        self.client = Client()
746

    
747
    def test_auth_cookie(self):
748
        user = SynnefoUser.objects.get(uniq = "test@synnefo.gr")
749
        self.client.cookies['X-Auth-Token'] = user.auth_token
750
        response = self.client.get('/index.html', {},
751
                                   **{'X-Auth-Token': user.auth_token,
752
                                      'TEST-AAI' : 'true'})
753
        self.assertTrue(response.status_code, 200)
754
        self.assertTrue('Vary' in response)
755
        self.assertTrue('X-Auth-Token' in response['Vary'])
756

    
757
    def test_fail_oapi_auth(self):
758
        """ test authentication from not registered user using OpenAPI
759
        """
760
        response = self.client.get(self.apibase + '/servers', {},
761
                                   **{'X-Auth-User': 'notme',
762
                                      'X-Auth-Key': '0xdeadbabe',
763
                                      'TEST-AAI' : 'true'})
764
        self.assertEquals(response.status_code, 401)
765

    
766
    def test_oapi_auth(self):
767
        """authentication with user registration
768
        """
769
        response = self.client.get(self.apibase + '/index.html', {},
770
                                   **{'X-Auth-User': 'testdbuser',
771
                                      'X-Auth-Key': 'test@synnefo.gr',
772
                                      'TEST-AAI' : 'true'})
773
        self.assertEquals(response.status_code, 204)
774
        self.assertNotEqual(response['X-Auth-Token'], None)
775
        self.assertEquals(response['X-Server-Management-Url'], '')
776
        self.assertEquals(response['X-Storage-Url'], '')
777
        self.assertEquals(response['X-CDN-Management-Url'], '')
778

    
779
    def test_unauthorized_call(self):
780
        request = {'reboot': {'type': 'HARD'}}
781
        path = '/api/v1.1/servers/%d/action' % 1
782
        response = self.client.post(path, json.dumps(request), content_type='application/json')
783
        self.assertEquals(response.status_code, 401)