Statistics
| Branch: | Tag: | Revision:

root / api / tests.py @ 64938cb0

History | View | Annotate | Download (38.1 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
            flavor=choice(flavors))
353

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

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

    
370

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

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

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

    
384

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

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

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

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

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

    
415

    
416
    def list_images(self, detail=False):
417
        path = '/api/v1.1/images'
418
        if detail:
419
            path += '/detail'
420
        response = self.client.get(path)
421
        self.assertTrue(response.status_code in (200, 203))
422
        reply = json.loads(response.content)
423
        self.assertEqual(reply.keys(), ['images'])
424
        self.assertEqual(reply['images'].keys(), ['values'])
425
        return reply['images']['values']
426

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
533

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

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

    
549

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

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

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

    
577

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

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

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

    
593

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

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

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

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

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

    
629

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

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

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

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

    
655

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

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

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

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

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

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

    
698

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

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

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

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

    
724

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

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

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

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

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

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

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

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

    
775

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

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

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

    
806

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

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

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

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

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

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

    
852

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

    
857
    def setUp(self):
858
        BaseTestCase.setUp(self)
859
        machines = VirtualMachine.objects.all()
860
        for network in Network.objects.all():
861
            n = randint(0, self.SERVERS)
862
            for machine in sample(machines, n):
863
                machine.nics.create(network=network)
864
    
865
    def test_list_networks(self):
866
        networks = self.list_networks()
867
        for net in Network.objects.all():
868
            network = popdict(networks, id=net.id)
869
            self.assertEqual(network['name'], net.name)
870
        self.assertEqual(networks, [])
871

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

    
881

    
882
class CreateNetwork(BaseTestCase):
883
    def test_create_network(self):
884
        before = self.list_networks()
885
        self.create_network('net')
886
        after = self.list_networks()
887
        self.assertEqual(len(after) - len(before), 1)
888
        found = False
889
        for network in after:
890
            if network['name'] == 'net':
891
                found = True
892
                break
893
        self.assertTrue(found)
894

    
895

    
896
class GetNetworkDetails(BaseTestCase):
897
    SERVERS = 5
898
    
899
    def test_get_network_details(self):
900
        name = 'net'
901
        self.create_network(name)
902
        
903
        servers = VirtualMachine.objects.all()
904
        network = Network.objects.all()[1]
905

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

    
910
        server_id = choice(servers).id
911
        self.add_to_network(network.id, server_id)
912
        net = self.get_network_details(network.id)
913
        self.assertEqual(net['name'], network.name)
914

    
915

    
916
class UpdateNetworkName(BaseTestCase):
917
    NETWORKS = 5
918

    
919
    def test_update_network_name(self):
920
        networks = self.list_networks(detail=True)
921
        network = choice(networks)
922
        network_id = network['id']
923
        new_name = network['name'] + '_2'
924
        self.update_network_name(network_id, new_name)
925

    
926
        network['name'] = new_name
927
        del network['updated']
928
        net = self.get_network_details(network_id)
929
        del net['updated']
930
        self.assertEqual(net, network)
931

    
932

    
933
class DeleteNetwork(BaseTestCase):
934
    def test_delete_network(self):
935
        for i in range(5):
936
            self.create_network('net%d' % i)
937
        
938
        networks = self.list_networks()
939
        network = choice(networks)
940
        while network['id'] == 1:
941
            network = choice(networks)
942
        network_id = network['id']
943
        self.delete_network(network_id)
944
        
945
        net = self.get_network_details(network_id)
946
        self.assertEqual(net['status'], 'DELETED')
947

    
948
        networks.remove(network)
949
        self.assertEqual(self.list_networks(), networks)
950

    
951

    
952
class NetworkActions(BaseTestCase):
953
    SERVERS = 20
954

    
955
    def test_add_remove_server(self):
956
        self.create_network('net')
957
        
958
        server_ids = [vm.id for vm in VirtualMachine.objects.all()]
959
        network = self.list_networks(detail=True)[1]
960
        network_id = network['id']
961

    
962
        to_add = set(sample(server_ids, 10))
963
        for server_id in to_add:
964
            self.add_to_network(network_id, server_id)
965
        
966
        to_remove = set(sample(to_add, 5))
967
        for server_id in to_remove:
968
            self.remove_from_network(network_id, server_id)