Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / api / tests.py @ bb54871d

History | View | Annotate | Download (38.4 kB)

1
# Copyright 2011 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
from __future__ import with_statement
35

    
36
from collections import defaultdict
37
from email.utils import parsedate
38
from random import choice, randint, sample
39
from time import mktime
40

    
41
from django.conf import settings
42
from django.utils import simplejson as json
43
from django.test import TestCase
44
from django.test.client import Client
45

    
46
from synnefo.db.models import *
47
from synnefo.logic.utils import get_rsapi_state
48

    
49

    
50
class AaiClient(Client):
51
    def request(self, **request):
52
        request['HTTP_X_AUTH_TOKEN'] = '0000'
53
        return super(AaiClient, self).request(**request)
54

    
55

    
56
class APITestCase(TestCase):
57
    fixtures = ['users', 'api_test_data']
58
    test_server_id = 1001
59
    test_image_id = 1
60
    test_flavor_id = 1
61
    test_group_id = 1
62
    test_wrong_server_id = 99999999
63
    test_wrong_image_id = 99999999
64
    test_wrong_flavor_id = 99999999
65
    test_wrong_group_id = 99999999
66
    #make the testing with these id's
67

    
68
    def setUp(self):
69
        self.client = AaiClient()
70
        settings.MAX_VMS_PER_USER = 5
71

    
72
    def test_api_version(self):
73
        """Check API version."""
74

    
75
        response = self.client.get('/api/v1.1/')
76
        self.assertEqual(response.status_code, 200)
77
        api_version = json.loads(response.content)['version']
78
        self.assertEqual(api_version['id'], 'v1.1')
79
        self.assertEqual(api_version['status'], 'CURRENT')
80

    
81
    def test_server_list(self):
82
        """Test if the expected list of servers is returned."""
83

    
84
        response = self.client.get('/api/v1.1/servers')
85
        vms_from_api = json.loads(response.content)['servers']['values']
86
        vms_from_db = VirtualMachine.objects.filter(deleted=False)
87
        self.assertEqual(len(vms_from_api), len(vms_from_db))
88
        self.assertTrue(response.status_code in [200, 203])
89
        for vm_from_api in vms_from_api:
90
            vm_from_db = VirtualMachine.objects.get(id=vm_from_api['id'])
91
            self.assertEqual(vm_from_api['id'], vm_from_db.id)
92
            self.assertEqual(vm_from_api['name'], vm_from_db.name)
93

    
94
    def test_server_details(self):
95
        """Test if the expected server is returned."""
96

    
97
        response = self.client.get('/api/v1.1/servers/%d' % self.test_server_id)
98
        vm_from_api = json.loads(response.content)['server']
99
        vm_from_db = VirtualMachine.objects.get(id=self.test_server_id)
100
        self.assertEqual(vm_from_api['flavorRef'], vm_from_db.flavor.id)
101
        self.assertEqual(vm_from_api['hostId'], vm_from_db.hostid)
102
        self.assertEqual(vm_from_api['id'], vm_from_db.id)
103
        self.assertEqual(vm_from_api['imageRef'], vm_from_db.imageid)
104
        self.assertEqual(vm_from_api['name'], vm_from_db.name)
105
        self.assertEqual(vm_from_api['status'], get_rsapi_state(vm_from_db))
106
        self.assertTrue(response.status_code in [200, 203])
107

    
108
    def test_servers_details(self):
109
        """Test if the servers details are returned."""
110

    
111
        response = self.client.get('/api/v1.1/servers/detail')
112

    
113
        # Make sure both DB and API responses are sorted by id,
114
        # to allow for 1-1 comparisons
115
        vms_from_db = VirtualMachine.objects.filter(deleted=False).order_by('id')
116
        vms_from_api = json.loads(response.content)['servers']['values']
117
        vms_from_api = sorted(vms_from_api, key=lambda vm: vm['id'])
118
        self.assertEqual(len(vms_from_db), len(vms_from_api))
119

    
120
        id_list = [vm.id for vm in vms_from_db]
121
        number = 0
122
        for vm_id in id_list:
123
            vm_from_api = vms_from_api[number]
124
            vm_from_db = VirtualMachine.objects.get(id=vm_id)
125
            self.assertEqual(vm_from_api['flavorRef'], vm_from_db.flavor.id)
126
            self.assertEqual(vm_from_api['hostId'], vm_from_db.hostid)
127
            self.assertEqual(vm_from_api['id'], vm_from_db.id)
128
            self.assertEqual(vm_from_api['imageRef'], vm_from_db.imageid)
129
            self.assertEqual(vm_from_api['name'], vm_from_db.name)
130
            self.assertEqual(vm_from_api['status'], get_rsapi_state(vm_from_db))
131
            number += 1
132
        for vm_from_api in vms_from_api:
133
            vm_from_db = VirtualMachine.objects.get(id=vm_from_api['id'])
134
            self.assertEqual(vm_from_api['flavorRef'], vm_from_db.flavor.id)
135
            self.assertEqual(vm_from_api['hostId'], vm_from_db.hostid)
136
            self.assertEqual(vm_from_api['id'], vm_from_db.id)
137
            self.assertEqual(vm_from_api['imageRef'], vm_from_db.imageid)
138
            self.assertEqual(vm_from_api['name'], vm_from_db.name)
139
            self.assertEqual(vm_from_api['status'], get_rsapi_state(vm_from_db))
140
        self.assertTrue(response.status_code in [200, 203])
141

    
142
    def test_wrong_server(self):
143
        """Test 404 response if server does not exist."""
144

    
145
        response = self.client.get('/api/v1.1/servers/%d' % self.test_wrong_server_id)
146
        self.assertEqual(response.status_code, 404)
147

    
148
    def test_create_server_empty(self):
149
        """Test if the create server call returns a 400 badRequest if
150
           no attributes are specified."""
151

    
152
        response = self.client.post('/api/v1.1/servers', {})
153
        self.assertEqual(response.status_code, 400)
154

    
155
    def test_create_server(self):
156
        """Test if the create server call returns the expected response
157
           if a valid request has been speficied."""
158

    
159
        request = {
160
                    "server": {
161
                        "name": "new-server-test",
162
                        "userid": "test",
163
                        "imageRef": 1,
164
                        "flavorRef": 1,
165
                        "metadata": {
166
                            "My Server Name": "Apache1"
167
                        },
168
                        "personality": []
169
                    }
170
        }
171
        response = self.client.post('/api/v1.1/servers', json.dumps(request),
172
                                    content_type='application/json')
173
        self.assertEqual(response.status_code, 202)
174
        #TODO: check response.content
175
        #TODO: check create server with wrong options (eg non existing flavor)
176

    
177
    def test_server_polling(self):
178
        """Test if the server polling works as expected."""
179

    
180
        response = self.client.get('/api/v1.1/servers/detail')
181
        vms_from_api_initial = json.loads(response.content)['servers']['values']
182
        ts = mktime(parsedate(response['Date']))
183
        since = datetime.datetime.fromtimestamp(ts).isoformat() + 'Z'
184
        response = self.client.get('/api/v1.1/servers/detail?changes-since=%s' % since)
185
        self.assertEqual(len(response.content), 0)
186

    
187
        #now create a machine. Then check if it is on the list
188
        request = {
189
                    "server": {
190
                        "name": "new-server-test",
191
                        "imageRef": 1,
192
                        "flavorRef": 1,
193
                        "metadata": {
194
                            "My Server Name": "Apache1"
195
                        },
196
                        "personality": []
197
                    }
198
        }
199

    
200
        path = '/api/v1.1/servers'
201
        response = self.client.post(path, json.dumps(request), content_type='application/json')
202
        self.assertEqual(response.status_code, 202)
203

    
204
        response = self.client.get('/api/v1.1/servers/detail?changes-since=%s' % since)
205
        self.assertEqual(response.status_code, 200)
206
        vms_from_api_after = json.loads(response.content)['servers']['values']
207
        #make sure the newly created server is included on the updated list
208
        self.assertEqual(len(vms_from_api_after), 1)
209

    
210
    def test_reboot_server(self):
211
        """Test if the specified server is rebooted."""
212
        request = {'reboot': {'type': 'HARD'}}
213
        path = '/api/v1.1/servers/%d/action' % self.test_server_id
214
        response = self.client.post(path, json.dumps(request), content_type='application/json')
215
        self.assertEqual(response.status_code, 202)
216
        # server id that does not exist
217
        path = '/api/v1.1/servers/%d/action' % self.test_wrong_server_id
218
        response = self.client.post(path, json.dumps(request), content_type='application/json')
219
        self.assertEqual(response.status_code, 404)
220

    
221
    def test_shutdown_server(self):
222
        """Test if the specified server is shutdown."""
223

    
224
        request = {'shutdown': {}}
225
        path = '/api/v1.1/servers/%d/action' % self.test_server_id
226
        response = self.client.post(path, json.dumps(request), content_type='application/json')
227
        self.assertEqual(response.status_code, 202)
228
        # server id that does not exist
229
        path = '/api/v1.1/servers/%d/action' % self.test_wrong_server_id
230
        response = self.client.post(path, json.dumps(request), content_type='application/json')
231
        self.assertEqual(response.status_code, 404)
232

    
233
    def test_start_server(self):
234
        """Test if the specified server is started."""
235

    
236
        request = {'start': {}}
237
        path = '/api/v1.1/servers/%d/action' % self.test_server_id
238
        response = self.client.post(path, json.dumps(request), content_type='application/json')
239
        self.assertEqual(response.status_code, 202)
240
        # server id that does not exist
241
        path = '/api/v1.1/servers/%d/action' % self.test_wrong_server_id
242
        response = self.client.post(path, json.dumps(request), content_type='application/json')
243
        self.assertEqual(response.status_code, 404)
244

    
245
    def test_delete_server(self):
246
        """Test if the specified server is deleted."""
247
        response = self.client.delete('/api/v1.1/servers/%d' % self.test_server_id)
248
        self.assertEqual(response.status_code, 204)
249
        # server id that does not exist
250
        response = self.client.delete('/api/v1.1/servers/%d' % self.test_wrong_server_id)
251
        self.assertEqual(response.status_code, 404)
252

    
253
    def test_flavor_list(self):
254
        """Test if the expected list of flavors is returned by."""
255

    
256
        response = self.client.get('/api/v1.1/flavors')
257
        flavors_from_api = json.loads(response.content)['flavors']['values']
258
        flavors_from_db = Flavor.objects.all()
259
        self.assertEqual(len(flavors_from_api), len(flavors_from_db))
260
        self.assertTrue(response.status_code in [200, 203])
261
        for flavor_from_api in flavors_from_api:
262
            flavor_from_db = Flavor.objects.get(id=flavor_from_api['id'])
263
            self.assertEqual(flavor_from_api['id'], flavor_from_db.id)
264
            self.assertEqual(flavor_from_api['name'], flavor_from_db.name)
265

    
266
    def test_flavors_details(self):
267
        """Test if the flavors details are returned."""
268

    
269
        response = self.client.get('/api/v1.1/flavors/detail')
270
        flavors_from_db = Flavor.objects.all()
271
        flavors_from_api = json.loads(response.content)['flavors']['values']
272

    
273
        # Assert that all flavors in the db appear inthe API call result
274
        for i in range(0, len(flavors_from_db)):
275
            flavor_from_api = flavors_from_api[i]
276
            flavor_from_db = Flavor.objects.get(id=flavors_from_db[i].id)
277
            self.assertEqual(flavor_from_api['cpu'], flavor_from_db.cpu)
278
            self.assertEqual(flavor_from_api['id'], flavor_from_db.id)
279
            self.assertEqual(flavor_from_api['disk'], flavor_from_db.disk)
280
            self.assertEqual(flavor_from_api['name'], flavor_from_db.name)
281
            self.assertEqual(flavor_from_api['ram'], flavor_from_db.ram)
282

    
283
        # Assert that all flavors returned by the API also exist in the db
284
        for flavor_from_api in flavors_from_api:
285
            flavor_from_db = Flavor.objects.get(id=flavor_from_api['id'])
286
            self.assertEqual(flavor_from_api['cpu'], flavor_from_db.cpu)
287
            self.assertEqual(flavor_from_api['id'], flavor_from_db.id)
288
            self.assertEqual(flavor_from_api['disk'], flavor_from_db.disk)
289
            self.assertEqual(flavor_from_api['name'], flavor_from_db.name)
290
            self.assertEqual(flavor_from_api['ram'], flavor_from_db.ram)
291

    
292
        # Check if we have the right status_code
293
        self.assertTrue(response.status_code in [200, 203])
294

    
295
    def test_flavor_details(self):
296
        """Test if the expected flavor is returned."""
297

    
298
        response = self.client.get('/api/v1.1/flavors/%d' % self.test_flavor_id)
299
        flavor_from_api = json.loads(response.content)['flavor']
300
        flavor_from_db = Flavor.objects.get(id=self.test_flavor_id)
301
        self.assertEqual(flavor_from_api['cpu'], flavor_from_db.cpu)
302
        self.assertEqual(flavor_from_api['id'], flavor_from_db.id)
303
        self.assertEqual(flavor_from_api['disk'], flavor_from_db.disk)
304
        self.assertEqual(flavor_from_api['name'], flavor_from_db.name)
305
        self.assertEqual(flavor_from_api['ram'], flavor_from_db.ram)
306
        self.assertTrue(response.status_code in [200, 203])
307

    
308
    def test_wrong_flavor(self):
309
        """Test 404 result when requesting a flavor that does not exist."""
310

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

    
314
    def test_image_list(self):
315
        """Test if the expected list of images is returned by the API."""
316

    
317
        response = self.client.get('/api/v1.1/images')
318
        images_from_api = json.loads(response.content)['images']['values']
319
        images_from_db = Image.objects.all()
320
        self.assertEqual(len(images_from_api), len(images_from_db))
321
        self.assertTrue(response.status_code in [200, 203])
322
        for image_from_api in images_from_api:
323
            image_from_db = Image.objects.get(id=image_from_api['id'])
324
            self.assertEqual(image_from_api['id'], image_from_db.id)
325
            self.assertEqual(image_from_api['name'], image_from_db.name)
326

    
327
    def test_wrong_image(self):
328
        """Test 404 result if a non existent image is requested."""
329

    
330
        response = self.client.get('/api/v1.1/images/%d' % self.test_wrong_image_id)
331
        self.assertEqual(response.status_code, 404)
332

    
333
    def test_server_metadata(self):
334
        """Test server's metadata (add, edit)."""
335

    
336
        key = 'name'
337
        request = {'meta': {key: 'a fancy name'}}
338

    
339
        path = '/api/v1.1/servers/%d/meta/%s' % (self.test_server_id, key)
340
        response = self.client.put(path, json.dumps(request), content_type='application/json')
341
        self.assertEqual(response.status_code, 201)
342

    
343

    
344
def create_flavors(n=1):
345
    for i in range(n):
346
        Flavor.objects.create(
347
            cpu=randint(1, 4),
348
            ram=randint(1, 8) * 512,
349
            disk=randint(1, 40))
350

    
351

    
352
def create_images(n=1):
353
    for i in range(n):
354
        Image.objects.create(
355
            name='Image %d' % (i + 1),
356
            state='ACTIVE',
357
            owner='test')
358

    
359

    
360
def create_image_metadata(n=1):
361
    images = Image.objects.all()
362
    for i in range(n):
363
        ImageMetadata.objects.create(
364
            meta_key='Key%d' % (i + 1),
365
            meta_value='Value %d' % (i + 1),
366
            image=choice(images))
367

    
368

    
369
def create_servers(n=1):
370
    flavors = Flavor.objects.all()
371
    images = Image.objects.all()
372
    for i in range(n):
373
        VirtualMachine.objects.create(
374
            name='Server %d' % (i + 1),
375
            owner='test',
376
            imageid=choice(images).id,
377
            hostid=str(i),
378
            flavor=choice(flavors))
379

    
380

    
381
def create_server_metadata(n=1):
382
    servers = VirtualMachine.objects.all()
383
    for i in range(n):
384
        VirtualMachineMetadata.objects.create(
385
            meta_key='Key%d' % (i + 1),
386
            meta_value='Value %d' % (i + 1),
387
            vm=choice(servers))
388

    
389

    
390
class AssertInvariant(object):
391
    def __init__(self, callable, *args, **kwargs):
392
        self.callable = callable
393
        self.args = args
394
        self.kwargs = kwargs
395

    
396
    def __enter__(self):
397
        self.value = self.callable(*self.args, **self.kwargs)
398
        return self.value
399

    
400
    def __exit__(self, type, value, tb):
401
        assert self.value == self.callable(*self.args, **self.kwargs)
402

    
403

    
404
class BaseTestCase(TestCase):
405
    FLAVORS = 1
406
    IMAGES = 1
407
    SERVERS = 1
408
    SERVER_METADATA = 0
409
    IMAGE_METADATA = 0
410
    fixtures = ['users']
411

    
412
    def setUp(self):
413
        self.client = AaiClient()
414
        create_flavors(self.FLAVORS)
415
        create_images(self.IMAGES)
416
        create_image_metadata(self.IMAGE_METADATA)
417
        create_servers(self.SERVERS)
418
        create_server_metadata(self.SERVER_METADATA)
419

    
420
    def assertFault(self, response, status_code, name):
421
        self.assertEqual(response.status_code, status_code)
422
        fault = json.loads(response.content)
423
        self.assertEqual(fault.keys(), [name])
424

    
425
    def assertBadRequest(self, response):
426
        self.assertFault(response, 400, 'badRequest')
427

    
428
    def assertItemNotFound(self, response):
429
        self.assertFault(response, 404, 'itemNotFound')
430

    
431
    def list_images(self, detail=False):
432
        path = '/api/v1.1/images'
433
        if detail:
434
            path += '/detail'
435
        response = self.client.get(path)
436
        self.assertTrue(response.status_code in (200, 203))
437
        reply = json.loads(response.content)
438
        self.assertEqual(reply.keys(), ['images'])
439
        self.assertEqual(reply['images'].keys(), ['values'])
440
        return reply['images']['values']
441

    
442
    def list_metadata(self, path):
443
        response = self.client.get(path)
444
        self.assertTrue(response.status_code in (200, 203))
445
        reply = json.loads(response.content)
446
        self.assertEqual(reply.keys(), ['metadata'])
447
        self.assertEqual(reply['metadata'].keys(), ['values'])
448
        return reply['metadata']['values']
449

    
450
    def list_server_metadata(self, server_id):
451
        path = '/api/v1.1/servers/%d/meta' % server_id
452
        return self.list_metadata(path)
453

    
454
    def list_image_metadata(self, image_id):
455
        path = '/api/v1.1/images/%d/meta' % image_id
456
        return self.list_metadata(path)
457

    
458
    def update_metadata(self, path, metadata):
459
        data = json.dumps({'metadata': metadata})
460
        response = self.client.post(path, data, content_type='application/json')
461
        self.assertEqual(response.status_code, 201)
462
        reply = json.loads(response.content)
463
        self.assertEqual(reply.keys(), ['metadata'])
464
        return reply['metadata']
465

    
466
    def update_server_metadata(self, server_id, metadata):
467
        path = '/api/v1.1/servers/%d/meta' % server_id
468
        return self.update_metadata(path, metadata)
469

    
470
    def update_image_metadata(self, image_id, metadata):
471
        path = '/api/v1.1/images/%d/meta' % image_id
472
        return self.update_metadata(path, metadata)
473

    
474
    def create_server_meta(self, server_id, meta):
475
        key = meta.keys()[0]
476
        path = '/api/v1.1/servers/%d/meta/%s' % (server_id, key)
477
        data = json.dumps({'meta': meta})
478
        response = self.client.put(path, data, content_type='application/json')
479
        self.assertEqual(response.status_code, 201)
480
        reply = json.loads(response.content)
481
        self.assertEqual(reply.keys(), ['meta'])
482
        response_meta = reply['meta']
483
        self.assertEqual(response_meta, meta)
484

    
485
    def get_all_server_metadata(self):
486
        metadata = defaultdict(dict)
487
        for m in VirtualMachineMetadata.objects.all():
488
            metadata[m.vm.id][m.meta_key] = m.meta_value
489
        return metadata
490

    
491
    def get_all_image_metadata(self):
492
        metadata = defaultdict(dict)
493
        for m in ImageMetadata.objects.all():
494
            metadata[m.image.id][m.meta_key] = m.meta_value
495
        return metadata
496

    
497
    def list_networks(self, detail=False):
498
        path = '/api/v1.1/networks'
499
        if detail:
500
            path += '/detail'
501
        response = self.client.get(path)
502
        self.assertTrue(response.status_code in (200, 203))
503
        reply = json.loads(response.content)
504
        self.assertEqual(reply.keys(), ['networks'])
505
        self.assertEqual(reply['networks'].keys(), ['values'])
506
        return reply['networks']['values']
507

    
508
    def create_network(self, name):
509
        path = '/api/v1.1/networks'
510
        data = json.dumps({'network': {'name': name}})
511
        response = self.client.post(path, data, content_type='application/json')
512
        self.assertEqual(response.status_code, 202)
513
        reply = json.loads(response.content)
514
        self.assertEqual(reply.keys(), ['network'])
515
        return reply
516

    
517
    def get_network_details(self, network_id):
518
        path = '/api/v1.1/networks/%s' % network_id
519
        response = self.client.get(path)
520
        self.assertEqual(response.status_code, 200)
521
        reply = json.loads(response.content)
522
        self.assertEqual(reply.keys(), ['network'])
523
        return reply['network']
524

    
525
    def update_network_name(self, network_id, new_name):
526
        path = '/api/v1.1/networks/%s' % network_id
527
        data = json.dumps({'network': {'name': new_name}})
528
        response = self.client.put(path, data, content_type='application/json')
529
        self.assertEqual(response.status_code, 204)
530

    
531
    def delete_network(self, network_id):
532
        path = '/api/v1.1/networks/%s' % network_id
533
        response = self.client.delete(path)
534
        self.assertEqual(response.status_code, 204)
535

    
536
    def add_to_network(self, network_id, server_id):
537
        path = '/api/v1.1/networks/%s/action' % network_id
538
        data = json.dumps({'add': {'serverRef': server_id}})
539
        response = self.client.post(path, data, content_type='application/json')
540
        self.assertEqual(response.status_code, 202)
541

    
542
    def remove_from_network(self, network_id, server_id):
543
        path = '/api/v1.1/networks/%s/action' % network_id
544
        data = json.dumps({'remove': {'serverRef': server_id}})
545
        response = self.client.post(path, data, content_type='application/json')
546
        self.assertEqual(response.status_code, 202)
547

    
548

    
549
def popdict(l, **kwargs):
550
    """Pops a dict from list `l` based on the predicates given as `kwargs`."""
551

    
552
    for i in range(len(l)):
553
        item = l[i]
554
        match = True
555
        for key, val in kwargs.items():
556
            if item[key] != val:
557
                match = False
558
                break
559
        if match:
560
            del l[i]
561
            return item
562
    return None
563

    
564

    
565
class ListImages(BaseTestCase):
566
    IMAGES = 10
567

    
568
    def test_list_images(self):
569
        images = self.list_images()
570
        keys = set(['id', 'name'])
571
        for img in Image.objects.all():
572
            image = popdict(images, id=img.id)
573
            self.assertTrue(image is not None)
574
            self.assertEqual(set(image.keys()), keys)
575
            self.assertEqual(image['id'], img.id)
576
            self.assertEqual(image['name'], img.name)
577
        self.assertEqual(images, [])
578

    
579
    def test_list_images_detail(self):
580
        images = self.list_images(detail=True)
581
        keys = set(['id', 'name', 'updated', 'created', 'status', 'progress'])
582
        for img in Image.objects.all():
583
            image = popdict(images, id=img.id)
584
            self.assertTrue(image is not None)
585
            self.assertEqual(set(image.keys()), keys)
586
            self.assertEqual(image['id'], img.id)
587
            self.assertEqual(image['name'], img.name)
588
            self.assertEqual(image['status'], img.state)
589
            self.assertEqual(image['progress'], 100 if img.state == 'ACTIVE' else 0)
590
        self.assertEqual(images, [])
591

    
592

    
593
class ListServerMetadata(BaseTestCase):
594
    SERVERS = 5
595
    SERVER_METADATA = 100
596

    
597
    def test_list_metadata(self):
598
        with AssertInvariant(self.get_all_server_metadata) as metadata:
599
            for vm in VirtualMachine.objects.all():
600
                response_metadata = self.list_server_metadata(vm.id)
601
                self.assertEqual(response_metadata, metadata[vm.id])
602

    
603
    def test_invalid_server(self):
604
        with AssertInvariant(self.get_all_server_metadata):
605
            response = self.client.get('/api/v1.1/servers/0/meta')
606
            self.assertItemNotFound(response)
607

    
608

    
609
class UpdateServerMetadata(BaseTestCase):
610
    SERVER_METADATA = 10
611

    
612
    def test_update_metadata(self):
613
        metadata = self.get_all_server_metadata()
614
        server_id = choice(metadata.keys())
615
        new_metadata = {}
616
        for key in sample(metadata[server_id].keys(), 3):
617
            new_metadata[key] = 'New %s value' % key
618
        response_metadata = self.update_server_metadata(server_id,
619
                                                        new_metadata)
620
        metadata[server_id].update(new_metadata)
621
        self.assertEqual(response_metadata, metadata[server_id])
622
        self.assertEqual(metadata, self.get_all_server_metadata())
623

    
624
    def test_invalid_data(self):
625
        with AssertInvariant(self.get_all_server_metadata) as metadata:
626
            server_id = choice(metadata.keys())
627
            path = '/api/v1.1/servers/%d/meta' % server_id
628
            response = self.client.post(path, 'metadata', content_type='application/json')
629
            self.assertBadRequest(response)
630

    
631
    def test_invalid_server(self):
632
        with AssertInvariant(self.get_all_server_metadata):
633
            path = '/api/v1.1/servers/0/meta'
634
            data = json.dumps({'metadata': {'Key1': 'A Value'}})
635
            response = self.client.post(path, data, content_type='application/json')
636
            self.assertItemNotFound(response)
637

    
638

    
639
class GetServerMetadataItem(BaseTestCase):
640
    SERVERS = 5
641
    SERVER_METADATA = 100
642

    
643
    def test_get_metadata_item(self):
644
        with AssertInvariant(self.get_all_server_metadata) as metadata:
645
            server_id = choice(metadata.keys())
646
            key = choice(metadata[server_id].keys())
647
            path = '/api/v1.1/servers/%d/meta/%s' % (server_id, key)
648
            response = self.client.get(path)
649
            self.assertTrue(response.status_code in (200, 203))
650
            reply = json.loads(response.content)
651
            self.assertEqual(reply['meta'], {key: metadata[server_id][key]})
652

    
653
    def test_invalid_key(self):
654
        with AssertInvariant(self.get_all_server_metadata) as metadata:
655
            server_id = choice(metadata.keys())
656
            response = self.client.get('/api/v1.1/servers/%d/meta/foo' % server_id)
657
            self.assertItemNotFound(response)
658

    
659
    def test_invalid_server(self):
660
        with AssertInvariant(self.get_all_server_metadata):
661
            response = self.client.get('/api/v1.1/servers/0/meta/foo')
662
            self.assertItemNotFound(response)
663

    
664

    
665
class CreateServerMetadataItem(BaseTestCase):
666
    SERVER_METADATA = 10
667

    
668
    def test_create_metadata(self):
669
        metadata = self.get_all_server_metadata()
670
        server_id = choice(metadata.keys())
671
        meta = {'Foo': 'Bar'}
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_update_metadata(self):
677
        metadata = self.get_all_server_metadata()
678
        server_id = choice(metadata.keys())
679
        key = choice(metadata[server_id].keys())
680
        meta = {key: 'New Value'}
681
        self.create_server_meta(server_id, meta)
682
        metadata[server_id].update(meta)
683
        self.assertEqual(metadata, self.get_all_server_metadata())
684

    
685
    def test_invalid_server(self):
686
        with AssertInvariant(self.get_all_server_metadata):
687
            path = '/api/v1.1/servers/0/meta/foo'
688
            data = json.dumps({'meta': {'foo': 'bar'}})
689
            response = self.client.put(path, data, content_type='application/json')
690
            self.assertItemNotFound(response)
691

    
692
    def test_invalid_key(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/baz' % server_id
696
            data = json.dumps({'meta': {'foo': 'bar'}})
697
            response = self.client.put(path, data, content_type='application/json')
698
            self.assertBadRequest(response)
699

    
700
    def test_invalid_data(self):
701
        with AssertInvariant(self.get_all_server_metadata) as metadata:
702
            server_id = choice(metadata.keys())
703
            path = '/api/v1.1/servers/%d/meta/foo' % server_id
704
            response = self.client.put(path, 'meta', content_type='application/json')
705
            self.assertBadRequest(response)
706

    
707

    
708
class DeleteServerMetadataItem(BaseTestCase):
709
    SERVER_METADATA = 10
710

    
711
    def test_delete_metadata(self):
712
        metadata = self.get_all_server_metadata()
713
        server_id = choice(metadata.keys())
714
        key = choice(metadata[server_id].keys())
715
        path = '/api/v1.1/servers/%d/meta/%s' % (server_id, key)
716
        response = self.client.delete(path)
717
        self.assertEqual(response.status_code, 204)
718
        metadata[server_id].pop(key)
719
        self.assertEqual(metadata, self.get_all_server_metadata())
720

    
721
    def test_invalid_server(self):
722
        with AssertInvariant(self.get_all_server_metadata):
723
            response = self.client.delete('/api/v1.1/servers/9/meta/Key1')
724
            self.assertItemNotFound(response)
725

    
726
    def test_invalid_key(self):
727
        with AssertInvariant(self.get_all_server_metadata) as metadata:
728
            server_id = choice(metadata.keys())
729
            path = '/api/v1.1/servers/%d/meta/foo' % server_id
730
            response = self.client.delete(path)
731
            self.assertItemNotFound(response)
732

    
733

    
734
class ListImageMetadata(BaseTestCase):
735
    IMAGES = 5
736
    IMAGE_METADATA = 100
737

    
738
    def test_list_metadata(self):
739
        with AssertInvariant(self.get_all_image_metadata) as metadata:
740
            for image in Image.objects.all():
741
                response_metadata = self.list_image_metadata(image.id)
742
                self.assertEqual(response_metadata, metadata[image.id])
743

    
744
    def test_invalid_image(self):
745
        with AssertInvariant(self.get_all_image_metadata):
746
            response = self.client.get('/api/v1.1/images/0/meta')
747
            self.assertItemNotFound(response)
748

    
749

    
750
class UpdateImageMetadata(BaseTestCase):
751
    IMAGE_METADATA = 10
752

    
753
    def test_update_metadata(self):
754
        metadata = self.get_all_image_metadata()
755
        image_id = choice(metadata.keys())
756
        new_metadata = {}
757
        for key in sample(metadata[image_id].keys(), 3):
758
            new_metadata[key] = 'New %s value' % key
759
        response_metadata = self.update_image_metadata(image_id, new_metadata)
760
        metadata[image_id].update(new_metadata)
761
        self.assertEqual(response_metadata, metadata[image_id])
762
        self.assertEqual(metadata, self.get_all_image_metadata())
763

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

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

    
778

    
779
class ServerVNCConsole(BaseTestCase):
780
    SERVERS = 1
781

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

    
790
    def test_active_server(self):
791
        """Test console req for ACTIVE server"""
792
        server_id = choice(VirtualMachine.objects.all()).id
793
        # FIXME: Start the server properly, instead of tampering with the DB
794
        vm = choice(VirtualMachine.objects.all())
795
        vm.operstate = 'STARTED'
796
        vm.save()
797
        server_id = vm.id
798

    
799
        path = '/api/v1.1/servers/%d/action' % server_id
800
        data = json.dumps({'console': {'type': 'vnc'}})
801
        response = self.client.post(path, data, content_type='application/json')
802
        self.assertEqual(response.status_code, 200)
803
        reply = json.loads(response.content)
804
        self.assertEqual(reply.keys(), ['console'])
805
        console = reply['console']
806
        self.assertEqual(console['type'], 'vnc')
807
        self.assertEqual(set(console.keys()), set(['type', 'host', 'port', 'password']))
808

    
809

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

    
814
    def setUp(self):
815
        self.client = Client()
816

    
817
    def test_fail_oapi_auth(self):
818
        """ test authentication from not registered user using OpenAPI
819
        """
820
        response = self.client.get(self.apibase + '/servers', {},
821
                                   **{'X-Auth-User': 'notme',
822
                                      'X-Auth-Key': '0xdeadbabe',
823
                                      'TEST-AAI': 'true'})
824
        self.assertEquals(response.status_code, 401)
825

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

    
839
    def test_unauthorized_call(self):
840
        request = {'reboot': {'type': 'HARD'}}
841
        path = '/api/v1.1/servers/%d/action' % 1
842
        response = self.client.post(path, json.dumps(request),
843
                                    content_type='application/json')
844
        self.assertEquals(response.status_code, 401)
845

    
846

    
847
class ListNetworks(BaseTestCase):
848
    SERVERS = 5
849

    
850
    def setUp(self):
851
        BaseTestCase.setUp(self)
852

    
853
        for i in range(5):
854
            self.create_network('net%d' % i)
855

    
856
        machines = VirtualMachine.objects.all()
857
        for network in Network.objects.all():
858
            n = randint(0, self.SERVERS - 1)
859
            for machine in sample(machines, n):
860
                machine.nics.create(network=network)
861

    
862
    def test_list_networks(self):
863
        networks = self.list_networks()
864
        for net in Network.objects.all():
865
            net_id = str(net.id) if not net.public else 'public'
866
            network = popdict(networks, id=net_id)
867
            self.assertEqual(network['name'], net.name)
868
        self.assertEqual(networks, [])
869

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

    
880

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

    
894

    
895
class GetNetworkDetails(BaseTestCase):
896
    SERVERS = 5
897

    
898
    def test_get_network_details(self):
899
        name = 'net'
900
        self.create_network(name)
901

    
902
        servers = VirtualMachine.objects.all()
903
        network = Network.objects.all()[1]
904

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

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

    
914

    
915
class UpdateNetworkName(BaseTestCase):
916
    def test_update_network_name(self):
917
        name = 'net'
918
        self.create_network(name)
919
        networks = self.list_networks(detail=True)
920
        priv = [net for net in networks if net['id'] != 'public']
921
        network = choice(priv)
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
        priv = [net for net in networks if net['id'] != 'public']
940
        network = choice(priv)
941
        network_id = network['id']
942
        self.delete_network(network_id)
943

    
944
        net = self.get_network_details(network_id)
945
        self.assertEqual(net['status'], 'DELETED')
946

    
947
        priv.remove(network)
948
        networks = self.list_networks()
949
        new_priv = [net for net in networks if net['id'] != 'public']
950
        self.assertEqual(priv, new_priv)
951

    
952

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

    
956
    def test_add_remove_server(self):
957
        self.create_network('net')
958

    
959
        server_ids = [vm.id for vm in VirtualMachine.objects.all()]
960
        network = self.list_networks(detail=True)[1]
961
        network_id = network['id']
962

    
963
        to_add = set(sample(server_ids, 10))
964
        for server_id in to_add:
965
            self.add_to_network(network_id, server_id)
966

    
967
        to_remove = set(sample(to_add, 5))
968
        for server_id in to_remove:
969
            self.remove_from_network(network_id, server_id)