Statistics
| Branch: | Tag: | Revision:

root / api / tests.py @ aa197ee4

History | View | Annotate | Download (38.3 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
        request = {'reboot': {'type': 'HARD'}}
182
        path = '/api/v1.1/servers/%d/action' % self.test_server_id
183
        response = self.client.post(path, json.dumps(request), content_type='application/json')
184
        self.assertEqual(response.status_code, 202)
185
        #server id that does not exist
186
        path = '/api/v1.1/servers/%d/action' % self.test_wrong_server_id
187
        response = self.client.post(path, json.dumps(request), content_type='application/json')
188
        self.assertEqual(response.status_code, 404)
189

    
190
    def test_shutdown_server(self):
191
        """Test if the specified server is shutdown."""
192

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

    
202
    def test_start_server(self):
203
        """Test if the specified server is started."""
204

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

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

    
222
    def test_flavor_list(self):
223
        """Test if the expected list of flavors is returned by."""
224

    
225
        response = self.client.get('/api/v1.1/flavors')
226
        flavors_from_api = json.loads(response.content)['flavors']['values']
227
        flavors_from_db = Flavor.objects.all()
228
        self.assertEqual(len(flavors_from_api), len(flavors_from_db))
229
        self.assertTrue(response.status_code in [200, 203])
230
        for flavor_from_api in flavors_from_api:
231
            flavor_from_db = Flavor.objects.get(id=flavor_from_api['id'])
232
            self.assertEqual(flavor_from_api['id'], flavor_from_db.id)
233
            self.assertEqual(flavor_from_api['name'], flavor_from_db.name)
234

    
235
    def test_flavors_details(self):
236
        """Test if the flavors details are returned."""
237

    
238
        response = self.client.get('/api/v1.1/flavors/detail')
239
        flavors_from_db = Flavor.objects.all()
240
        flavors_from_api = json.loads(response.content)['flavors']['values']
241

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

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

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

    
264
    def test_flavor_details(self):
265
        """Test if the expected flavor is returned."""
266

    
267
        response = self.client.get('/api/v1.1/flavors/%d' % self.test_flavor_id)
268
        flavor_from_api = json.loads(response.content)['flavor']
269
        flavor_from_db = Flavor.objects.get(id=self.test_flavor_id)
270
        self.assertEqual(flavor_from_api['cpu'], flavor_from_db.cpu)
271
        self.assertEqual(flavor_from_api['id'], flavor_from_db.id)
272
        self.assertEqual(flavor_from_api['disk'], flavor_from_db.disk)
273
        self.assertEqual(flavor_from_api['name'], flavor_from_db.name)
274
        self.assertEqual(flavor_from_api['ram'], flavor_from_db.ram)
275
        self.assertTrue(response.status_code in [200, 203])
276

    
277
    def test_wrong_flavor(self):
278
        """Test 404 result when requesting a flavor that does not exist."""
279

    
280
        response = self.client.get('/api/v1.1/flavors/%d' % self.test_wrong_flavor_id)
281
        self.assertTrue(response.status_code in [404, 503])
282

    
283
    def test_image_list(self):
284
        """Test if the expected list of images is returned by the API."""
285

    
286
        response = self.client.get('/api/v1.1/images')
287
        images_from_api = json.loads(response.content)['images']['values']
288
        images_from_db = Image.objects.all()
289
        self.assertEqual(len(images_from_api), len(images_from_db))
290
        self.assertTrue(response.status_code in [200, 203])
291
        for image_from_api in images_from_api:
292
            image_from_db = Image.objects.get(id=image_from_api['id'])
293
            self.assertEqual(image_from_api['id'], image_from_db.id)
294
            self.assertEqual(image_from_api['name'], image_from_db.name)
295

    
296
    def test_wrong_image(self):
297
        """Test 404 result if a non existent image is requested."""
298

    
299
        response = self.client.get('/api/v1.1/images/%d' % self.test_wrong_image_id)
300
        self.assertEqual(response.status_code, 404)
301

    
302
    def test_server_metadata(self):
303
        """Test server's metadata (add, edit)."""
304

    
305
        key = 'name'
306
        request = {'meta': {key: 'a fancy name'}}
307

    
308
        path = '/api/v1.1/servers/%d/meta/%s' % (self.test_server_id, key)
309
        response = self.client.put(path, json.dumps(request), content_type='application/json')
310
        self.assertEqual(response.status_code, 201)
311

    
312

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

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

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

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

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

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

    
364
def create_networks(n):
365
    users = SynnefoUser.objects.all()
366
    for i in range(n):
367
        Network.objects.create(
368
            name='Network%d' % (i + 1),
369
            owner=choice(users))
370

    
371

    
372
class AssertInvariant(object):
373
    def __init__(self, callable, *args, **kwargs):
374
        self.callable = callable
375
        self.args = args
376
        self.kwargs = kwargs
377

    
378
    def __enter__(self):
379
        self.value = self.callable(*self.args, **self.kwargs)
380
        return self.value
381

    
382
    def __exit__(self, type, value, tb):
383
        assert self.value == self.callable(*self.args, **self.kwargs)
384

    
385

    
386
class BaseTestCase(TestCase):
387
    USERS = 0
388
    FLAVORS = 1
389
    IMAGES = 1
390
    SERVERS = 1
391
    SERVER_METADATA = 0
392
    IMAGE_METADATA = 0
393
    NETWORKS = 0
394

    
395
    def setUp(self):
396
        self.client = AaiClient()
397
        create_users(self.USERS)
398
        create_flavors(self.FLAVORS)
399
        create_images(self.IMAGES)
400
        create_image_metadata(self.IMAGE_METADATA)
401
        create_servers(self.SERVERS)
402
        create_server_metadata(self.SERVER_METADATA)
403
        create_networks(self.NETWORKS)
404

    
405
    def assertFault(self, response, status_code, name):
406
        self.assertEqual(response.status_code, status_code)
407
        fault = json.loads(response.content)
408
        self.assertEqual(fault.keys(), [name])
409

    
410
    def assertBadRequest(self, response):
411
        self.assertFault(response, 400, 'badRequest')
412

    
413
    def assertItemNotFound(self, response):
414
        self.assertFault(response, 404, 'itemNotFound')
415

    
416

    
417
    def list_images(self, detail=False):
418
        path = '/api/v1.1/images'
419
        if detail:
420
            path += '/detail'
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(), ['images'])
425
        self.assertEqual(reply['images'].keys(), ['values'])
426
        return reply['images']['values']
427

    
428
    def list_metadata(self, path):
429
        response = self.client.get(path)
430
        self.assertTrue(response.status_code in (200, 203))
431
        reply = json.loads(response.content)
432
        self.assertEqual(reply.keys(), ['metadata'])
433
        self.assertEqual(reply['metadata'].keys(), ['values'])
434
        return reply['metadata']['values']
435

    
436
    def list_server_metadata(self, server_id):
437
        path = '/api/v1.1/servers/%d/meta' % server_id
438
        return self.list_metadata(path)
439

    
440
    def list_image_metadata(self, image_id):
441
        path = '/api/v1.1/images/%d/meta' % image_id
442
        return self.list_metadata(path)
443

    
444
    def update_metadata(self, path, metadata):
445
        data = json.dumps({'metadata': metadata})
446
        response = self.client.post(path, data, content_type='application/json')
447
        self.assertEqual(response.status_code, 201)
448
        reply = json.loads(response.content)
449
        self.assertEqual(reply.keys(), ['metadata'])
450
        return reply['metadata']
451

    
452
    def update_server_metadata(self, server_id, metadata):
453
        path = '/api/v1.1/servers/%d/meta' % server_id
454
        return self.update_metadata(path, metadata)
455

    
456
    def update_image_metadata(self, image_id, metadata):
457
        path = '/api/v1.1/images/%d/meta' % image_id
458
        return self.update_metadata(path, metadata)
459

    
460
    def create_server_meta(self, server_id, meta):
461
        key = meta.keys()[0]
462
        path = '/api/v1.1/servers/%d/meta/%s' % (server_id, key)
463
        data = json.dumps({'meta': meta})
464
        response = self.client.put(path, data, content_type='application/json')
465
        self.assertEqual(response.status_code, 201)
466
        reply = json.loads(response.content)
467
        self.assertEqual(reply.keys(), ['meta'])
468
        response_meta = reply['meta']
469
        self.assertEqual(response_meta, meta)
470

    
471
    def get_all_server_metadata(self):
472
        metadata = defaultdict(dict)
473
        for m in VirtualMachineMetadata.objects.all():
474
            metadata[m.vm.id][m.meta_key] = m.meta_value
475
        return metadata
476

    
477
    def get_all_image_metadata(self):
478
        metadata = defaultdict(dict)
479
        for m in ImageMetadata.objects.all():
480
            metadata[m.image.id][m.meta_key] = m.meta_value
481
        return metadata
482

    
483
    def list_networks(self, detail=False):
484
        path = '/api/v1.1/networks'
485
        if detail:
486
            path += '/detail'
487
        response = self.client.get(path)
488
        self.assertTrue(response.status_code in (200, 203))
489
        reply = json.loads(response.content)
490
        self.assertEqual(reply.keys(), ['networks'])
491
        self.assertEqual(reply['networks'].keys(), ['values'])
492
        return reply['networks']['values']
493

    
494
    def create_network(self, name):
495
        path = '/api/v1.1/networks'
496
        data = json.dumps({'network': {'name': name}})
497
        response = self.client.post(path, data, content_type='application/json')
498
        self.assertEqual(response.status_code, 202)
499
        reply = json.loads(response.content)
500
        self.assertEqual(reply.keys(), ['network'])
501
        return reply
502

    
503
    def get_network_details(self, network_id):
504
        path = '/api/v1.1/networks/%d' % network_id
505
        response = self.client.get(path)
506
        self.assertEqual(response.status_code, 200)
507
        reply = json.loads(response.content)
508
        self.assertEqual(reply.keys(), ['network'])
509
        return reply['network']
510

    
511
    def update_network_name(self, network_id, new_name):
512
        path = '/api/v1.1/networks/%d' % network_id
513
        data = json.dumps({'network': {'name': new_name}})
514
        response = self.client.put(path, data, content_type='application/json')
515
        self.assertEqual(response.status_code, 204)
516

    
517
    def delete_network(self, network_id):
518
        path = '/api/v1.1/networks/%d' % network_id
519
        response = self.client.delete(path)
520
        self.assertEqual(response.status_code, 204)
521

    
522
    def add_to_network(self, network_id, server_id):
523
        path = '/api/v1.1/networks/%d/action' % network_id
524
        data = json.dumps({'add': {'serverRef': server_id}})
525
        response = self.client.post(path, data, content_type='application/json')
526
        self.assertEqual(response.status_code, 202)
527

    
528
    def remove_from_network(self, network_id, server_id):
529
        path = '/api/v1.1/networks/%d/action' % network_id
530
        data = json.dumps({'remove': {'serverRef': server_id}})
531
        response = self.client.post(path, data, content_type='application/json')
532
        self.assertEqual(response.status_code, 202)
533

    
534

    
535
def popdict(l, **kwargs):
536
    """Pops a dict from list `l` based on the predicates given as `kwargs`."""
537

    
538
    for i in range(len(l)):
539
        item = l[i]
540
        match = True
541
        for key, val in kwargs.items():
542
            if item[key] != val:
543
                match = False
544
                break
545
        if match:
546
            del l[i]
547
            return item
548
    return None
549

    
550

    
551
class ListImages(BaseTestCase):
552
    IMAGES = 10
553

    
554
    def test_list_images(self):
555
        images = self.list_images()
556
        keys = set(['id', 'name'])
557
        for img in Image.objects.all():
558
            image = popdict(images, id=img.id)
559
            self.assertTrue(image is not None)
560
            self.assertEqual(set(image.keys()), keys)
561
            self.assertEqual(image['id'], img.id)
562
            self.assertEqual(image['name'], img.name)
563
        self.assertEqual(images, [])
564

    
565
    def test_list_images_detail(self):
566
        images = self.list_images(detail=True)
567
        keys = set(['id', 'name', 'updated', 'created', 'status', 'progress'])
568
        for img in Image.objects.all():
569
            image = popdict(images, id=img.id)
570
            self.assertTrue(image is not None)
571
            self.assertEqual(set(image.keys()), keys)
572
            self.assertEqual(image['id'], img.id)
573
            self.assertEqual(image['name'], img.name)
574
            self.assertEqual(image['status'], img.state)
575
            self.assertEqual(image['progress'], 100 if img.state == 'ACTIVE' else 0)
576
        self.assertEqual(images, [])
577

    
578

    
579
class ListServerMetadata(BaseTestCase):
580
    SERVERS = 5
581
    SERVER_METADATA = 100
582

    
583
    def test_list_metadata(self):
584
        with AssertInvariant(self.get_all_server_metadata) as metadata:
585
            for vm in VirtualMachine.objects.all():
586
                response_metadata = self.list_server_metadata(vm.id)
587
                self.assertEqual(response_metadata, metadata[vm.id])
588

    
589
    def test_invalid_server(self):
590
        with AssertInvariant(self.get_all_server_metadata):
591
            response = self.client.get('/api/v1.1/servers/0/meta')
592
            self.assertItemNotFound(response)
593

    
594

    
595
class UpdateServerMetadata(BaseTestCase):
596
    SERVER_METADATA = 10
597

    
598
    def test_update_metadata(self):
599
        metadata = self.get_all_server_metadata()
600
        server_id = choice(metadata.keys())
601
        new_metadata = {}
602
        for key in sample(metadata[server_id].keys(), 3):
603
            new_metadata[key] = 'New %s value' % key
604
        response_metadata = self.update_server_metadata(server_id, new_metadata)
605
        self.assertEqual(response_metadata, new_metadata)
606
        metadata[server_id].update(new_metadata)
607
        self.assertEqual(metadata, self.get_all_server_metadata())
608

    
609
    def test_does_not_create(self):
610
        with AssertInvariant(self.get_all_server_metadata) as metadata:
611
            server_id = choice(metadata.keys())
612
            new_metadata = {'Foo': 'Bar'}
613
            response_metadata = self.update_server_metadata(server_id, new_metadata)
614
            self.assertEqual(response_metadata, {})
615

    
616
    def test_invalid_data(self):
617
        with AssertInvariant(self.get_all_server_metadata) as metadata:
618
            server_id = choice(metadata.keys())
619
            path = '/api/v1.1/servers/%d/meta' % server_id
620
            response = self.client.post(path, 'metadata', content_type='application/json')
621
            self.assertBadRequest(response)
622

    
623
    def test_invalid_server(self):
624
        with AssertInvariant(self.get_all_server_metadata):
625
            path = '/api/v1.1/servers/0/meta'
626
            data = json.dumps({'metadata': {'Key1': 'A Value'}})
627
            response = self.client.post(path, data, content_type='application/json')
628
            self.assertItemNotFound(response)
629

    
630

    
631
class GetServerMetadataItem(BaseTestCase):
632
    SERVERS = 5
633
    SERVER_METADATA = 100
634

    
635
    def test_get_metadata_item(self):
636
        with AssertInvariant(self.get_all_server_metadata) as metadata:
637
            server_id = choice(metadata.keys())
638
            key = choice(metadata[server_id].keys())
639
            path = '/api/v1.1/servers/%d/meta/%s' % (server_id, key)
640
            response = self.client.get(path)
641
            self.assertTrue(response.status_code in (200, 203))
642
            reply = json.loads(response.content)
643
            self.assertEqual(reply['meta'], {key: metadata[server_id][key]})
644

    
645
    def test_invalid_key(self):
646
        with AssertInvariant(self.get_all_server_metadata) as metadata:
647
            server_id = choice(metadata.keys())
648
            response = self.client.get('/api/v1.1/servers/%d/meta/foo' % server_id)
649
            self.assertItemNotFound(response)
650

    
651
    def test_invalid_server(self):
652
        with AssertInvariant(self.get_all_server_metadata):
653
            response = self.client.get('/api/v1.1/servers/0/meta/foo')
654
            self.assertItemNotFound(response)
655

    
656

    
657
class CreateServerMetadataItem(BaseTestCase):
658
    SERVER_METADATA = 10
659

    
660
    def test_create_metadata(self):
661
        metadata = self.get_all_server_metadata()
662
        server_id = choice(metadata.keys())
663
        meta = {'Foo': 'Bar'}
664
        self.create_server_meta(server_id, meta)
665
        metadata[server_id].update(meta)
666
        self.assertEqual(metadata, self.get_all_server_metadata())
667

    
668
    def test_update_metadata(self):
669
        metadata = self.get_all_server_metadata()
670
        server_id = choice(metadata.keys())
671
        key = choice(metadata[server_id].keys())
672
        meta = {key: 'New Value'}
673
        self.create_server_meta(server_id, meta)
674
        metadata[server_id].update(meta)
675
        self.assertEqual(metadata, self.get_all_server_metadata())
676

    
677
    def test_invalid_server(self):
678
        with AssertInvariant(self.get_all_server_metadata):
679
            path = '/api/v1.1/servers/0/meta/foo'
680
            data = json.dumps({'meta': {'foo': 'bar'}})
681
            response = self.client.put(path, data, content_type='application/json')
682
            self.assertItemNotFound(response)
683

    
684
    def test_invalid_key(self):
685
        with AssertInvariant(self.get_all_server_metadata) as metadata:
686
            server_id = choice(metadata.keys())
687
            path = '/api/v1.1/servers/%d/meta/baz' % server_id
688
            data = json.dumps({'meta': {'foo': 'bar'}})
689
            response = self.client.put(path, data, content_type='application/json')
690
            self.assertBadRequest(response)
691

    
692
    def test_invalid_data(self):
693
        with AssertInvariant(self.get_all_server_metadata) as metadata:
694
            server_id = choice(metadata.keys())
695
            path = '/api/v1.1/servers/%d/meta/foo' % server_id
696
            response = self.client.put(path, 'meta', content_type='application/json')
697
            self.assertBadRequest(response)
698

    
699

    
700
class DeleteServerMetadataItem(BaseTestCase):
701
    SERVER_METADATA = 10
702

    
703
    def test_delete_metadata(self):
704
        metadata = self.get_all_server_metadata()
705
        server_id = choice(metadata.keys())
706
        key = choice(metadata[server_id].keys())
707
        path = '/api/v1.1/servers/%d/meta/%s' % (server_id, key)
708
        response = self.client.delete(path)
709
        self.assertEqual(response.status_code, 204)
710
        metadata[server_id].pop(key)
711
        self.assertEqual(metadata, self.get_all_server_metadata())
712

    
713
    def test_invalid_server(self):
714
        with AssertInvariant(self.get_all_server_metadata):
715
            response = self.client.delete('/api/v1.1/servers/9/meta/Key1')
716
            self.assertItemNotFound(response)
717

    
718
    def test_invalid_key(self):
719
        with AssertInvariant(self.get_all_server_metadata) as metadata:
720
            server_id = choice(metadata.keys())
721
            path = '/api/v1.1/servers/%d/meta/foo' % server_id
722
            response = self.client.delete(path)
723
            self.assertItemNotFound(response)
724

    
725

    
726
class ListImageMetadata(BaseTestCase):
727
    IMAGES = 5
728
    IMAGE_METADATA = 100
729

    
730
    def test_list_metadata(self):
731
        with AssertInvariant(self.get_all_image_metadata) as metadata:
732
            for image in Image.objects.all():
733
                response_metadata = self.list_image_metadata(image.id)
734
                self.assertEqual(response_metadata, metadata[image.id])
735

    
736
    def test_invalid_image(self):
737
        with AssertInvariant(self.get_all_image_metadata):
738
            response = self.client.get('/api/v1.1/images/0/meta')
739
            self.assertItemNotFound(response)
740

    
741
class UpdateImageMetadata(BaseTestCase):
742
    IMAGE_METADATA = 10
743

    
744
    def test_update_metadata(self):
745
        metadata = self.get_all_image_metadata()
746
        image_id = choice(metadata.keys())
747
        new_metadata = {}
748
        for key in sample(metadata[image_id].keys(), 3):
749
            new_metadata[key] = 'New %s value' % key
750
        response_metadata = self.update_image_metadata(image_id, new_metadata)
751
        self.assertEqual(response_metadata, new_metadata)
752
        metadata[image_id].update(new_metadata)
753
        self.assertEqual(metadata, self.get_all_image_metadata())
754

    
755
    def test_does_not_create(self):
756
        with AssertInvariant(self.get_all_image_metadata) as metadata:
757
            image_id = choice(metadata.keys())
758
            new_metadata = {'Foo': 'Bar'}
759
            response_metadata = self.update_image_metadata(image_id, new_metadata)
760
            self.assertEqual(response_metadata, {})
761

    
762
    def test_invalid_data(self):
763
        with AssertInvariant(self.get_all_image_metadata) as metadata:
764
            image_id = choice(metadata.keys())
765
            path = '/api/v1.1/images/%d/meta' % image_id
766
            response = self.client.post(path, 'metadata', content_type='application/json')
767
            self.assertBadRequest(response)
768

    
769
    def test_invalid_server(self):
770
        with AssertInvariant(self.get_all_image_metadata):
771
            path = '/api/v1.1/images/0/meta'
772
            data = json.dumps({'metadata': {'Key1': 'A Value'}})
773
            response = self.client.post(path, data, content_type='application/json')
774
            self.assertItemNotFound(response)
775

    
776

    
777
class ServerVNCConsole(BaseTestCase):
778
    SERVERS = 1
779

    
780
    def test_not_active_server(self):
781
        """Test console req for server not in ACTIVE state returns badRequest"""
782
        server_id = choice(VirtualMachine.objects.all()).id
783
        path = '/api/v1.1/servers/%d/action' % server_id
784
        data = json.dumps({'console': {'type': 'vnc'}})
785
        response = self.client.post(path, data, content_type='application/json')
786
        self.assertBadRequest(response)
787

    
788
    def test_active_server(self):
789
        """Test console req for ACTIVE server"""
790
        server_id = choice(VirtualMachine.objects.all()).id
791
        # FIXME: Start the server properly, instead of tampering with the DB
792
        vm = choice(VirtualMachine.objects.all())
793
        vm.operstate = 'STARTED'
794
        vm.save()
795
        server_id = vm.id
796
        
797
        path = '/api/v1.1/servers/%d/action' % server_id
798
        data = json.dumps({'console': {'type': 'vnc'}})
799
        response = self.client.post(path, data, content_type='application/json')
800
        self.assertEqual(response.status_code, 200)
801
        reply = json.loads(response.content)
802
        self.assertEqual(reply.keys(), ['console'])
803
        console = reply['console']
804
        self.assertEqual(console['type'], 'vnc')
805
        self.assertEqual(set(console.keys()), set(['type', 'host', 'port', 'password']))
806

    
807

    
808
class AaiTestCase(TestCase):
809
    fixtures = ['api_test_data', 'auth_test_data']
810
    apibase = '/api/v1.1'
811

    
812
    def setUp(self):
813
        self.client = Client()
814

    
815
    def test_auth_cookie(self):
816
        user = SynnefoUser.objects.get(uniq = "test@synnefo.gr")
817
        self.client.cookies['X-Auth-Token'] = user.auth_token
818
        response = self.client.get('/index.html', {},
819
                                   **{'X-Auth-Token': user.auth_token,
820
                                      'TEST-AAI' : 'true'})
821
        self.assertTrue(response.status_code, 200)
822
        self.assertTrue('Vary' in response)
823
        self.assertTrue('X-Auth-Token' in response['Vary'])
824

    
825
    def test_fail_oapi_auth(self):
826
        """ test authentication from not registered user using OpenAPI
827
        """
828
        response = self.client.get(self.apibase + '/servers', {},
829
                                   **{'X-Auth-User': 'notme',
830
                                      'X-Auth-Key': '0xdeadbabe',
831
                                      'TEST-AAI' : 'true'})
832
        self.assertEquals(response.status_code, 401)
833

    
834
    def test_oapi_auth(self):
835
        """authentication with user registration
836
        """
837
        response = self.client.get(self.apibase + '/index.html', {},
838
                                   **{'X-Auth-User': 'testdbuser',
839
                                      'X-Auth-Key': 'test@synnefo.gr',
840
                                      'TEST-AAI' : 'true'})
841
        self.assertEquals(response.status_code, 204)
842
        self.assertNotEqual(response['X-Auth-Token'], None)
843
        self.assertEquals(response['X-Server-Management-Url'], '')
844
        self.assertEquals(response['X-Storage-Url'], '')
845
        self.assertEquals(response['X-CDN-Management-Url'], '')
846

    
847
    def test_unauthorized_call(self):
848
        request = {'reboot': {'type': 'HARD'}}
849
        path = '/api/v1.1/servers/%d/action' % 1
850
        response = self.client.post(path, json.dumps(request), content_type='application/json')
851
        self.assertEquals(response.status_code, 401)
852

    
853

    
854
class ListNetworks(BaseTestCase):
855
    SERVERS = 5
856
    NETWORKS = 5
857

    
858
    def setUp(self):
859
        BaseTestCase.setUp(self)
860
        machines = VirtualMachine.objects.all()
861
        for network in Network.objects.all():
862
            n = randint(0, self.SERVERS)
863
            network.machines.add(*sample(machines, n))
864
            network.save()
865

    
866
    def test_list_networks(self):
867
        networks = self.list_networks()
868
        for net in Network.objects.all():
869
            network = popdict(networks, id=net.id)
870
            self.assertEqual(network['name'], net.name)
871
        self.assertEqual(networks, [])
872

    
873
    def test_list_networks_detail(self):
874
        networks = self.list_networks(detail=True)
875
        for net in Network.objects.all():
876
            network = popdict(networks, id=net.id)
877
            self.assertEqual(network['name'], net.name)
878
            machines = set(vm.id for vm in net.machines.all())
879
            self.assertEqual(set(network['servers']['values']), machines)
880
        self.assertEqual(networks, [])
881

    
882

    
883
class CreateNetwork(BaseTestCase):
884
    def test_create_network(self):
885
        self.assertEqual(self.list_networks(), [])
886
        self.create_network('net')
887
        networks = self.list_networks()
888
        self.assertEqual(len(networks), 1)
889
        network = networks[0]
890
        self.assertEqual(network['name'], 'net')
891

    
892

    
893
class GetNetworkDetails(BaseTestCase):
894
    SERVERS = 5
895
    NETWORKS = 1
896

    
897
    def test_get_network_details(self):
898
        servers = VirtualMachine.objects.all()
899
        network = Network.objects.all()[0]
900

    
901
        net = self.get_network_details(network.id)
902
        self.assertEqual(net['name'], network.name)
903
        self.assertEqual(net['servers']['values'], [])
904

    
905
        server_id = choice(servers).id
906
        self.add_to_network(network.id, server_id)
907
        net = self.get_network_details(network.id)
908
        self.assertEqual(net['name'], network.name)
909
        self.assertEqual(net['servers']['values'], [server_id])
910

    
911

    
912
class UpdateNetworkName(BaseTestCase):
913
    NETWORKS = 5
914

    
915
    def test_update_network_name(self):
916
        networks = self.list_networks(detail=True)
917
        network = choice(networks)
918
        network_id = network['id']
919
        new_name = network['name'] + '_2'
920
        self.update_network_name(network_id, new_name)
921

    
922
        network['name'] = new_name
923
        self.assertEqual(self.get_network_details(network_id), network)
924

    
925

    
926
class DeleteNetwork(BaseTestCase):
927
    NETWORKS = 5
928

    
929
    def test_delete_network(self):
930
        networks = self.list_networks()
931
        network = choice(networks)
932
        network_id = network['id']
933
        self.delete_network(network_id)
934

    
935
        response = self.client.get('/api/v1.1/networks/%d' % network_id)
936
        self.assertItemNotFound(response)
937

    
938
        networks.remove(network)
939
        self.assertEqual(self.list_networks(), networks)
940

    
941

    
942
class NetworkActions(BaseTestCase):
943
    SERVERS = 20
944
    NETWORKS = 1
945

    
946
    def test_add_remove_server(self):
947
        server_ids = [vm.id for vm in VirtualMachine.objects.all()]
948
        network = self.list_networks(detail=True)[0]
949
        network_id = network['id']
950

    
951
        to_add = set(sample(server_ids, 10))
952
        for server_id in to_add:
953
            self.add_to_network(network_id, server_id)
954
            net = self.get_network_details(network_id)
955
            self.assertTrue(server_id in net['servers']['values'])
956

    
957
        net = self.get_network_details(network_id)
958
        self.assertEqual(set(net['servers']['values']), to_add)
959

    
960
        to_remove = set(sample(to_add, 5))
961
        for server_id in to_remove:
962
            self.remove_from_network(network_id, server_id)
963
            net = self.get_network_details(network_id)
964
            self.assertTrue(server_id not in net['servers']['values'])
965

    
966
        net = self.get_network_details(network_id)
967
        self.assertEqual(set(net['servers']['values']), to_add - to_remove)
968