Statistics
| Branch: | Tag: | Revision:

root / api / tests.py @ 13b954b0

History | View | Annotate | Download (36.5 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
import datetime
13

    
14
from django.utils import simplejson as json
15
from django.test import TestCase
16
from django.test.client import Client
17

    
18
from synnefo.db.models import *
19
from synnefo.logic.utils import get_rsapi_state
20

    
21

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

    
27

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

    
40
    def setUp(self):
41
        self.client = AaiClient()
42

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
315

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

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

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

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

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

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

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

    
374

    
375
class AssertInvariant(object):
376
    def __init__(self, callable, *args, **kwargs):
377
        self.callable = callable
378
        self.args = args
379
        self.kwargs = kwargs
380
    
381
    def __enter__(self):
382
        self.value = self.callable(*self.args, **self.kwargs)
383
        return self.value
384
    
385
    def __exit__(self, type, value, tb):
386
        assert self.value == self.callable(*self.args, **self.kwargs)
387

    
388

    
389
class BaseTestCase(TestCase):
390
    USERS = 0
391
    FLAVORS = 1
392
    IMAGES = 1
393
    SERVERS = 1
394
    SERVER_METADATA = 0
395
    IMAGE_METADATA = 0
396
    NETWORKS = 0
397
    
398
    def setUp(self):
399
        self.client = AaiClient()
400
        create_users(self.USERS)
401
        create_flavors(self.FLAVORS)
402
        create_images(self.IMAGES)
403
        create_image_metadata(self.IMAGE_METADATA)
404
        create_servers(self.SERVERS)
405
        create_server_metadata(self.SERVER_METADATA)
406
        create_networks(self.NETWORKS)
407
    
408
    def assertFault(self, response, status_code, name):
409
        self.assertEqual(response.status_code, status_code)
410
        fault = json.loads(response.content)
411
        self.assertEqual(fault.keys(), [name])
412
    
413
    def assertBadRequest(self, response):
414
        self.assertFault(response, 400, 'badRequest')
415

    
416
    def assertItemNotFound(self, response):
417
        self.assertFault(response, 404, 'itemNotFound')
418
    
419
    
420
    def list_images(self, detail=False):
421
        path = '/api/v1.1/images'
422
        if detail:
423
            path += '/detail'
424
        response = self.client.get(path)
425
        self.assertTrue(response.status_code in (200, 203))
426
        reply = json.loads(response.content)
427
        self.assertEqual(reply.keys(), ['images'])
428
        self.assertEqual(reply['images'].keys(), ['values'])
429
        return reply['images']['values']
430
    
431
    def list_metadata(self, path):
432
        response = self.client.get(path)
433
        self.assertTrue(response.status_code in (200, 203))
434
        reply = json.loads(response.content)
435
        self.assertEqual(reply.keys(), ['metadata'])
436
        self.assertEqual(reply['metadata'].keys(), ['values'])
437
        return reply['metadata']['values']
438
    
439
    def list_server_metadata(self, server_id):
440
        path = '/api/v1.1/servers/%d/meta' % server_id
441
        return self.list_metadata(path)
442
    
443
    def list_image_metadata(self, image_id):
444
        path = '/api/v1.1/images/%d/meta' % image_id
445
        return self.list_metadata(path)
446
    
447
    def update_metadata(self, path, metadata):
448
        data = json.dumps({'metadata': metadata})
449
        response = self.client.post(path, data, content_type='application/json')
450
        self.assertEqual(response.status_code, 201)
451
        reply = json.loads(response.content)
452
        self.assertEqual(reply.keys(), ['metadata'])
453
        return reply['metadata']
454
    
455
    def update_server_metadata(self, server_id, metadata):
456
        path = '/api/v1.1/servers/%d/meta' % server_id
457
        return self.update_metadata(path, metadata)
458
    
459
    def update_image_metadata(self, image_id, metadata):
460
        path = '/api/v1.1/images/%d/meta' % image_id
461
        return self.update_metadata(path, metadata)
462
    
463
    def create_server_meta(self, server_id, meta):
464
        key = meta.keys()[0]
465
        path = '/api/v1.1/servers/%d/meta/%s' % (server_id, key)
466
        data = json.dumps({'meta': meta})
467
        response = self.client.put(path, data, content_type='application/json')
468
        self.assertEqual(response.status_code, 201)
469
        reply = json.loads(response.content)
470
        self.assertEqual(reply.keys(), ['meta'])
471
        response_meta = reply['meta']
472
        self.assertEqual(response_meta, meta)
473
    
474
    def get_all_server_metadata(self):
475
        metadata = defaultdict(dict)
476
        for m in VirtualMachineMetadata.objects.all():
477
            metadata[m.vm.id][m.meta_key] = m.meta_value
478
        return metadata
479
    
480
    def get_all_image_metadata(self):
481
        metadata = defaultdict(dict)
482
        for m in ImageMetadata.objects.all():
483
            metadata[m.image.id][m.meta_key] = m.meta_value
484
        return metadata
485
    
486
    def list_networks(self, detail=False):
487
        path = '/api/v1.1/networks'
488
        if detail:
489
            path += '/detail'
490
        response = self.client.get(path)
491
        self.assertTrue(response.status_code in (200, 203))
492
        reply = json.loads(response.content)
493
        self.assertEqual(reply.keys(), ['networks'])
494
        self.assertEqual(reply['networks'].keys(), ['values'])
495
        return reply['networks']['values']
496
    
497
    def create_network(self, name):
498
        path = '/api/v1.1/networks'
499
        data = json.dumps({'network': {'name': name}})
500
        response = self.client.post(path, data, content_type='application/json')
501
        self.assertEqual(response.status_code, 202)
502
        reply = json.loads(response.content)
503
        self.assertEqual(reply.keys(), ['network'])
504
        return reply
505
    
506
    def get_network_details(self, name):
507
        path = '/api/v1.1/networks/%s' % name
508
        response = self.client.get(path)
509
        self.assertEqual(response.status_code, 200)
510
        reply = json.loads(response.content)
511
        self.assertEqual(reply.keys(), ['network'])
512
        return reply['network']
513
    
514
    def update_network_name(self, name, new_name):
515
        path = '/api/v1.1/networks/%s' % name
516
        data = json.dumps({'network': {'name': new_name}})
517
        response = self.client.put(path, data, content_type='application/json')
518
        self.assertEqual(response.status_code, 204)
519
    
520
    def delete_network(self, name):
521
        path = '/api/v1.1/networks/%s' % name
522
        response = self.client.delete(path)
523
        self.assertEqual(response.status_code, 204)
524
    
525
    def add_to_network(self, network_name, server_id):
526
        path = '/api/v1.1/networks/%s/action' % network_name
527
        data = json.dumps({'add': {'serverRef': server_id}})
528
        response = self.client.post(path, data, content_type='application/json')
529
        self.assertEqual(response.status_code, 202)
530
    
531
    def remove_from_network(self, network_name, server_id):
532
        path = '/api/v1.1/networks/%s/action' % network_name
533
        data = json.dumps({'remove': {'serverRef': server_id}})
534
        response = self.client.post(path, data, content_type='application/json')
535
        self.assertEqual(response.status_code, 202)
536

    
537

    
538
def popdict(l, **kwargs):
539
    """Pops a dict from list `l` based on the predicates given as `kwargs`."""
540
    
541
    for i in range(len(l)):
542
        item = l[i]
543
        match = True
544
        for key, val in kwargs.items():
545
            if item[key] != val:
546
                match = False
547
                break
548
        if match:
549
            del l[i]
550
            return item
551
    return None
552

    
553

    
554
class ListImages(BaseTestCase):
555
    IMAGES = 10
556
    
557
    def test_list_images(self):
558
        images = self.list_images()
559
        keys = set(['id', 'name'])
560
        for img in Image.objects.all():
561
            image = popdict(images, id=img.id)
562
            self.assertTrue(image is not None)
563
            self.assertEqual(set(image.keys()), keys)
564
            self.assertEqual(image['id'], img.id)
565
            self.assertEqual(image['name'], img.name)
566
        self.assertEqual(images, [])
567
    
568
    def test_list_images_detail(self):
569
        images = self.list_images(detail=True)
570
        keys = set(['id', 'name', 'updated', 'created', 'status', 'progress'])
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(image['status'], img.state)
578
            self.assertEqual(image['progress'], 100 if img.state == 'ACTIVE' else 0)
579
        self.assertEqual(images, [])
580

    
581

    
582
class ListServerMetadata(BaseTestCase):
583
    SERVERS = 5
584
    SERVER_METADATA = 100
585
    
586
    def test_list_metadata(self):
587
        with AssertInvariant(self.get_all_server_metadata) as metadata:
588
            for vm in VirtualMachine.objects.all():
589
                response_metadata = self.list_server_metadata(vm.id)
590
                self.assertEqual(response_metadata, metadata[vm.id])
591
    
592
    def test_invalid_server(self):
593
        with AssertInvariant(self.get_all_server_metadata):
594
            response = self.client.get('/api/v1.1/servers/0/meta')
595
            self.assertItemNotFound(response)
596

    
597

    
598
class UpdateServerMetadata(BaseTestCase):
599
    SERVER_METADATA = 10
600
    
601
    def test_update_metadata(self):
602
        metadata = self.get_all_server_metadata()
603
        server_id = choice(metadata.keys())
604
        new_metadata = {}
605
        for key in sample(metadata[server_id].keys(), 3):
606
            new_metadata[key] = 'New %s value' % key
607
        response_metadata = self.update_server_metadata(server_id, new_metadata)
608
        self.assertEqual(response_metadata, new_metadata)
609
        metadata[server_id].update(new_metadata)
610
        self.assertEqual(metadata, self.get_all_server_metadata())
611
    
612
    def test_does_not_create(self):
613
        with AssertInvariant(self.get_all_server_metadata) as metadata:
614
            server_id = choice(metadata.keys())
615
            new_metadata = {'Foo': 'Bar'}
616
            response_metadata = self.update_server_metadata(server_id, new_metadata)
617
            self.assertEqual(response_metadata, {})
618
    
619
    def test_invalid_data(self):
620
        with AssertInvariant(self.get_all_server_metadata) as metadata:
621
            server_id = choice(metadata.keys())
622
            path = '/api/v1.1/servers/%d/meta' % server_id
623
            response = self.client.post(path, 'metadata', content_type='application/json')
624
            self.assertBadRequest(response)
625
    
626
    def test_invalid_server(self):
627
        with AssertInvariant(self.get_all_server_metadata):
628
            path = '/api/v1.1/servers/0/meta'
629
            data = json.dumps({'metadata': {'Key1': 'A Value'}})
630
            response = self.client.post(path, data, content_type='application/json')
631
            self.assertItemNotFound(response)
632

    
633

    
634
class GetServerMetadataItem(BaseTestCase):
635
    SERVERS = 5
636
    SERVER_METADATA = 100
637
    
638
    def test_get_metadata_item(self):
639
        with AssertInvariant(self.get_all_server_metadata) as metadata:
640
            server_id = choice(metadata.keys())
641
            key = choice(metadata[server_id].keys())
642
            path = '/api/v1.1/servers/%d/meta/%s' % (server_id, key)
643
            response = self.client.get(path)
644
            self.assertTrue(response.status_code in (200, 203))
645
            reply = json.loads(response.content)
646
            self.assertEqual(reply['meta'], {key: metadata[server_id][key]})
647
    
648
    def test_invalid_key(self):
649
        with AssertInvariant(self.get_all_server_metadata) as metadata:
650
            server_id = choice(metadata.keys())
651
            response = self.client.get('/api/v1.1/servers/%d/meta/foo' % server_id)
652
            self.assertItemNotFound(response)
653
    
654
    def test_invalid_server(self):
655
        with AssertInvariant(self.get_all_server_metadata):
656
            response = self.client.get('/api/v1.1/servers/0/meta/foo')
657
            self.assertItemNotFound(response)
658

    
659

    
660
class CreateServerMetadataItem(BaseTestCase):
661
    SERVER_METADATA = 10
662
    
663
    def test_create_metadata(self):
664
        metadata = self.get_all_server_metadata()
665
        server_id = choice(metadata.keys())
666
        meta = {'Foo': 'Bar'}
667
        self.create_server_meta(server_id, meta)
668
        metadata[server_id].update(meta)
669
        self.assertEqual(metadata, self.get_all_server_metadata())
670
    
671
    def test_update_metadata(self):
672
        metadata = self.get_all_server_metadata()
673
        server_id = choice(metadata.keys())
674
        key = choice(metadata[server_id].keys())
675
        meta = {key: 'New Value'}
676
        self.create_server_meta(server_id, meta)
677
        metadata[server_id].update(meta)
678
        self.assertEqual(metadata, self.get_all_server_metadata())
679
    
680
    def test_invalid_server(self):
681
        with AssertInvariant(self.get_all_server_metadata):
682
            path = '/api/v1.1/servers/0/meta/foo'
683
            data = json.dumps({'meta': {'foo': 'bar'}})
684
            response = self.client.put(path, data, content_type='application/json')
685
            self.assertItemNotFound(response)
686
    
687
    def test_invalid_key(self):
688
        with AssertInvariant(self.get_all_server_metadata) as metadata:
689
            server_id = choice(metadata.keys())
690
            path = '/api/v1.1/servers/%d/meta/baz' % server_id
691
            data = json.dumps({'meta': {'foo': 'bar'}})
692
            response = self.client.put(path, data, content_type='application/json')
693
            self.assertBadRequest(response)
694
    
695
    def test_invalid_data(self):
696
        with AssertInvariant(self.get_all_server_metadata) as metadata:
697
            server_id = choice(metadata.keys())
698
            path = '/api/v1.1/servers/%d/meta/foo' % server_id
699
            response = self.client.put(path, 'meta', content_type='application/json')
700
            self.assertBadRequest(response)
701

    
702

    
703
class DeleteServerMetadataItem(BaseTestCase):
704
    SERVER_METADATA = 10
705
    
706
    def test_delete_metadata(self):
707
        metadata = self.get_all_server_metadata()
708
        server_id = choice(metadata.keys())
709
        key = choice(metadata[server_id].keys())
710
        path = '/api/v1.1/servers/%d/meta/%s' % (server_id, key)
711
        response = self.client.delete(path)
712
        self.assertEqual(response.status_code, 204)
713
        metadata[server_id].pop(key)
714
        self.assertEqual(metadata, self.get_all_server_metadata())
715
    
716
    def test_invalid_server(self):
717
        with AssertInvariant(self.get_all_server_metadata):
718
            response = self.client.delete('/api/v1.1/servers/9/meta/Key1')
719
            self.assertItemNotFound(response)
720
    
721
    def test_invalid_key(self):
722
        with AssertInvariant(self.get_all_server_metadata) as metadata:
723
            server_id = choice(metadata.keys())
724
            path = '/api/v1.1/servers/%d/meta/foo' % server_id
725
            response = self.client.delete(path)
726
            self.assertItemNotFound(response)
727

    
728

    
729
class ListImageMetadata(BaseTestCase):
730
    IMAGES = 5
731
    IMAGE_METADATA = 100
732

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

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

    
744
class UpdateImageMetadata(BaseTestCase):
745
    IMAGE_METADATA = 10
746

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

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

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

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

    
779

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

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

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

    
810

    
811
class ListNetworks(BaseTestCase):
812
    SERVERS = 5
813
    NETWORKS = 5
814
    
815
    def setUp(self):
816
        BaseTestCase.setUp(self)
817
        machines = VirtualMachine.objects.all()
818
        for network in Network.objects.all():
819
            n = randint(0, self.SERVERS)
820
            network.machines.add(*sample(machines, n))
821
            network.save()
822
    
823
    def test_list_networks(self):
824
        networks = self.list_networks()
825
        for net in Network.objects.all():
826
            popdict(networks, name=net.name)
827
        self.assertEqual(networks, [])
828
    
829
    def test_list_networks_detail(self):
830
        networks = self.list_networks(detail=True)
831
        for net in Network.objects.all():
832
            network = popdict(networks, name=net.name)
833
            machines = set(vm.id for vm in net.machines.all())
834
            self.assertEqual(set(network['servers']['values']), machines)
835
        self.assertEqual(networks, [])
836

    
837

    
838
class CreateNetwork(BaseTestCase):
839
    def test_create_network(self):
840
        self.assertEqual(self.list_networks(), [])
841
        self.create_network('net')
842
        networks = self.list_networks()
843
        self.assertEqual(len(networks), 1)
844
        network = networks[0]
845
        self.assertEqual(network['name'], 'net')
846

    
847

    
848
class GetNetworkDetails(BaseTestCase):
849
    SERVERS = 5
850
    NETWORKS = 1
851
    
852
    def test_get_network_details(self):
853
        servers = VirtualMachine.objects.all()
854
        network = Network.objects.all()[0]
855
        name = network.name
856
        
857
        net = self.get_network_details(name)
858
        self.assertEqual(net['servers']['values'], [])
859
        
860
        server_id = choice(servers).id
861
        self.add_to_network(name, server_id)
862
        net = self.get_network_details(name)
863
        self.assertEqual(net['servers']['values'], [server_id])
864

    
865

    
866
class UpdateNetworkName(BaseTestCase):
867
    NETWORKS = 5
868
    
869
    def test_update_network_name(self):
870
        networks = self.list_networks(detail=True)
871
        network = choice(networks)
872
        name = network['name']
873
        new_name = name + '_2'
874
        self.update_network_name(name, new_name)
875
        
876
        network['name'] = new_name
877
        self.assertEqual(self.get_network_details(new_name), network)
878
        
879
        response = self.client.get('/api/v1.1/networks/' + name)
880
        self.assertItemNotFound(response)
881

    
882

    
883
class DeleteNetwork(BaseTestCase):
884
    NETWORKS = 5
885
    
886
    def test_delete_network(self):
887
        networks = self.list_networks()
888
        network = choice(networks)
889
        name = network['name']
890
        self.delete_network(name)
891
        
892
        response = self.client.get('/api/v1.1/networks/' + name)
893
        self.assertItemNotFound(response)
894
        
895
        networks.remove(network)
896
        self.assertEqual(self.list_networks(), networks)
897

    
898

    
899
class NetworkActions(BaseTestCase):
900
    SERVERS = 20
901
    NETWORKS = 1
902
    
903
    def test_add_remove_server(self):
904
        server_ids = [vm.id for vm in VirtualMachine.objects.all()]
905
        network = self.list_networks(detail=True)[0]
906
        name = network['name']
907
        
908
        to_add = set(sample(server_ids, 10))
909
        for server_id in to_add:
910
            self.add_to_network(name, server_id)
911
            net = self.get_network_details(name)
912
            self.assertTrue(server_id in net['servers']['values'])
913
        
914
        net = self.get_network_details(name)
915
        self.assertEqual(set(net['servers']['values']), to_add)
916
        
917
        to_remove = set(sample(to_add, 5))
918
        for server_id in to_remove:
919
            self.remove_from_network(name, server_id)
920
            net = self.get_network_details(name)
921
            self.assertTrue(server_id not in net['servers']['values'])
922
        
923
        net = self.get_network_details(name)
924
        self.assertEqual(set(net['servers']['values']), to_add - to_remove)