Revision 47eeffa9

b/snf-cyclades-app/synnefo/api/tests.py
1
# Copyright 2011 GRNET S.A. All rights reserved.
1
# Copyright 2012 GRNET S.A. All rights reserved.
2 2
#
3 3
# Redistribution and use in source and binary forms, with or
4 4
# without modification, are permitted provided that the following
......
33 33

  
34 34
from __future__ import with_statement
35 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 36
from django.utils import simplejson as json
43 37
from django.test import TestCase
44 38
from django.test.client import Client
......
46 40
from synnefo.db.models import *
47 41
from synnefo.logic.utils import get_rsapi_state
48 42

  
49
import mock
50
from synnefo.lib import astakos
43
from mock import patch, Mock
51 44

  
52 45
from contextlib import contextmanager
46
from synnefo.db import models_factory as mfactory
47

  
48
from synnefo.api import util
49
from synnefo.api import faults
50

  
51

  
52
def get_image_mock(request, *Args, **kwargs):
53
    return {'backend_id': '1234',
54
            'location': 'pithos://dummyimage',
55
            'disk_format': 'drbd'}
56
util.get_image = get_image_mock
53 57

  
54
# This is a hack to override astakos
55
# The global variable ASTAKOS_PATCHED_USER keeps the name of
56
# a dummy user that we provide through replacement of the
57
# corresponding astakos function with a dummy function.
58
ASTAKOS_PATCHED_USER = "user"
59 58

  
60 59
@contextmanager
61 60
def astakos_user(user):
......
64 63

  
65 64
    usage:
66 65
    with astakos_user("user@user.com"):
67
        .... make api calls .... get_user will act as user@user.com requested the api.
66
        .... make api calls ....
68 67

  
69 68
    """
70 69
    from synnefo.lib import astakos
70
    from synnefo.api import util
71 71
    orig_method = astakos.get_user
72
    
73
    global ASTAKOS_PATCHED_USER
74
    ASTAKOS_PATCHED_USER = user
72

  
75 73
    def dummy_get_user(request, *args, **kwargs):
76
        global ASTAKOS_PATCHED_USER
77
        request.user = {'username': ASTAKOS_PATCHED_USER, 'groups': []}
78
        request.user_uniq = ASTAKOS_PATCHED_USER
74
        request.user = {'username': user, 'groups': []}
75
        request.user_uniq = user
79 76
    astakos.get_user = dummy_get_user
77
    util.get_user = dummy_get_user
80 78
    yield
81 79
    astakos.get_user = orig_method
82 80

  
81

  
83 82
class AaiClient(Client):
84 83
    def request(self, **request):
85 84
        request['HTTP_X_AUTH_TOKEN'] = '0000'
86 85
        return super(AaiClient, self).request(**request)
87 86

  
88
class NetworksTest(TestCase):
89
    
90
    fixtures = ['network_test_data']
91

  
92
    def test_attachments_list(self):
93
        with astakos_user("admin@adminland.com"):
94
            r = self.client.get("/api/v1.1/networks")
95
            data = json.loads(r.content)
96
            self.assertEqual(data["networks"]["values"][1]["name"], "network4admin")
97
            #import pdb; pdb.set_trace()
98
            self.assertEqual(len(data["networks"]["values"]), 2)
99
        with astakos_user("user1@userland.com"):
100
            r = self.client.get("/api/v1.1/networks")
101
            data = json.loads(r.content)
102
            self.assertEqual(data["networks"]["values"][1]["name"], "network4user1")
103
            #import pdb; pdb.set_trace()
104
            self.assertEqual(len(data["networks"]["values"]), 2)
105

  
106
    def test_create_network(self):
107
        with astakos_user("admin@adminland.com"):
108
            r = self.client.post("/api/v1.1/networks")
109

  
110
class ServersTest(TestCase):
111

  
112
    fixtures = ['network_test_data']
113

  
114
    def test_attachments_list(self):
115
        with astakos_user("admin@adminland.com"):
116
            r = self.client.get("/api/v1.1/servers")
117
            data = json.loads(r.content)
118
            self.assertEqual(data["servers"]["values"][0]["id"], 1001)
119
            self.assertEqual(len(data["servers"]["values"]), 1)
120
            r = self.client.get("/api/v1.1/servers/1001")
121

  
122

  
123
class TestQuota(TestCase):
124 87

  
125
    fixtures = ['users', 'flavors']
126
    astakos_response_user = 'test'
127

  
128
    def setUp(self):
129

  
130
        self.astakos_response_user = 'test'
131
        def get_user_mock(request, *Args, **kwargs):
132
            if request.META.get('HTTP_X_AUTH_TOKEN', None) == '0000':
133
                request.user_uniq = self.astakos_response_user
134
                request.user = {'uniq': self.astakos_response_user}
135

  
136
        def get_image_mock(request, *Args, **kwargs):
137
            return {'backend_id':'1234', 'location':'pithos://dummyimage',
138
                    'disk_format': 'plain'}
139

  
140
        self.rapi_patch = mock.patch('synnefo.logic.backend.rapi')
141
        self.rapi_mock = self.rapi_patch.start()
142

  
143
        self.pithos_patch = mock.patch('synnefo.api.util.get_image')
144
        self.pithos_mock = self.rapi_patch.start()
145

  
146
        # mock the astakos authentication function
147
        from synnefo.api import util
148
        util.get_user = get_user_mock
149
        util.get_image = get_image_mock
150

  
151
        settings.SKIP_SSH_VALIDATION = True
152
        self.client = AaiClient()
153
        self.user = 'test'
154

  
155
    def test_vms_quota(self):
156
        request = {
157
                    "server": {
158
                        "name": "new-server-test",
159
                        "userid": "test",
160
                        "imageRef": 1,
161
                        "flavorRef": 1,
162
                        "metadata": {
163
                            "My Server Name": "Apache1"
164
                        },
165
                        "personality": []
166
                    }
167
        }
88
def create_flavors(num):
89
    return [mfactory.FlavorFactory() for x in range(num)]
168 90

  
169
        def create_server(for_user='test'):
170
            self.astakos_response_user = for_user
171
            return self.client.post('/api/v1.1/servers', json.dumps(request),
172
                content_type="application/json")
173

  
174
        def user_vms_count(user='test'):
175
            return VirtualMachine.objects.filter(userid=user).count()
176

  
177
        # admin sets max vms per user to 2
178
        settings.MAX_VMS_PER_USER = 2
179
        create_server()
180
        create_server()
181
        self.assertEqual(user_vms_count(), 2)
182

  
183
        # third creation fails
184
        resp = create_server()
185
        self.assertEqual(resp.status_code, 413)
186
        self.assertEqual(user_vms_count(), 2)
187

  
188
        # setting changed, no additional servers can get created
189
        settings.MAX_VMS_PER_USER = 1
190
        resp = create_server()
191
        self.assertEqual(resp.status_code, 413)
192
        self.assertEqual(user_vms_count(), 2)
193

  
194
        # admin wants test user to create 4 vms, now test user can create
195
        # one additional vm, but no more
196
        settings.VMS_USER_QUOTA = {'test':3}
197
        create_server()
198
        self.assertEqual(user_vms_count(), 3)
199
        resp = create_server()
200
        self.assertEqual(resp.status_code, 413)
201
        self.assertEqual(user_vms_count(), 3)
202
        # other users still apply to the global quota
203
        create_server("testuser2")
204
        self.assertEqual(user_vms_count("testuser2"), 1)
205
        resp = create_server("testuser2")
206
        self.assertEqual(resp.status_code, 413)
207
        self.assertEqual(user_vms_count("testuser2"), 1)
208

  
209

  
210
    def test_networks_quota(self):
211

  
212
        def create_network(for_user='test'):
213
            request = json.dumps({'network': {'name': 'user %s network' %
214
                for_user}})
215
            self.astakos_response_user = for_user
216
            return self.client.post('/api/v1.1/networks', request,
217
                content_type="application/json")
218

  
219
        def user_networks_count(user='test'):
220
            return Network.objects.filter(userid=user).count()
221

  
222
        settings.MAX_NETWORKS_PER_USER = 1
223
        create_network()
224
        self.assertEqual(user_networks_count(),1)
225
        resp = create_network()
226
        self.assertEqual(resp.status_code, 413)
227
        self.assertEqual(user_networks_count(), 1)
228

  
229
        settings.NETWORKS_USER_QUOTA = {'test':2}
230
        create_network()
231
        self.assertEqual(user_networks_count(),2)
232
        resp = create_network()
233
        self.assertEqual(resp.status_code, 413)
234
        self.assertEqual(user_networks_count(), 2)
235

  
236
        create_network("testuser2")
237
        self.assertEqual(user_networks_count("testuser2"),1)
238
        resp = create_network("testuser2")
239
        self.assertEqual(resp.status_code, 413)
240
        self.assertEqual(user_networks_count("testuser2"), 1)
241

  
242
        settings.GANETI_MAX_LINK_NUMBER = 3
243
        settings.NETWORKS_USER_QUOTA = {'test':10}
244
        resp = create_network()
245
        self.assertEqual(Network.objects.count(), 4)
246
        self.assertEqual('No networks available.' in resp.content, True)
247
        self.assertEqual(user_networks_count(), 2)
248

  
249

  
250

  
251

  
252
class APITestCase(TestCase):
253
    fixtures = ['users', 'api_test_data']
254
    test_server_id = 1001
255
    test_image_id = 1
256
    test_flavor_id = 1
257
    test_group_id = 1
258
    test_wrong_server_id = 99999999
259
    test_wrong_image_id = 99999999
260
    test_wrong_flavor_id = 99999999
261
    test_wrong_group_id = 99999999
262
    #make the testing with these id's
263 91

  
264
    def setUp(self):
265
        self.client = AaiClient()
266
        settings.MAX_VMS_PER_USER = 5
92
class APITest(TestCase):
267 93

  
268 94
    def test_api_version(self):
269 95
        """Check API version."""
270

  
271
        response = self.client.get('/api/v1.1/')
96
        with astakos_user('user'):
97
            response = self.client.get('/api/v1.1/')
272 98
        self.assertEqual(response.status_code, 200)
273 99
        api_version = json.loads(response.content)['version']
274 100
        self.assertEqual(api_version['id'], 'v1.1')
275 101
        self.assertEqual(api_version['status'], 'CURRENT')
276 102

  
277
    def test_server_list(self):
278
        """Test if the expected list of servers is returned."""
279

  
280
        response = self.client.get('/api/v1.1/servers')
281
        vms_from_api = json.loads(response.content)['servers']['values']
282
        vms_from_db = VirtualMachine.objects.filter(deleted=False)
283
        self.assertEqual(len(vms_from_api), len(vms_from_db))
284
        self.assertTrue(response.status_code in [200, 203])
285
        for vm_from_api in vms_from_api:
286
            vm_from_db = VirtualMachine.objects.get(id=vm_from_api['id'])
287
            self.assertEqual(vm_from_api['id'], vm_from_db.id)
288
            self.assertEqual(vm_from_api['name'], vm_from_db.name)
289

  
290
    def test_server_details(self):
291
        """Test if the expected server is returned."""
292

  
293
        response = self.client.get('/api/v1.1/servers/%d' % self.test_server_id)
294
        vm_from_api = json.loads(response.content)['server']
295
        vm_from_db = VirtualMachine.objects.get(id=self.test_server_id)
296
        self.assertEqual(vm_from_api['flavorRef'], vm_from_db.flavor.id)
297
        self.assertEqual(vm_from_api['hostId'], vm_from_db.hostid)
298
        self.assertEqual(vm_from_api['id'], vm_from_db.id)
299
        self.assertEqual(vm_from_api['imageRef'], vm_from_db.imageid)
300
        self.assertEqual(vm_from_api['name'], vm_from_db.name)
301
        self.assertEqual(vm_from_api['status'], get_rsapi_state(vm_from_db))
302
        self.assertTrue(response.status_code in [200, 203])
303 103

  
304
    def test_servers_details(self):
305
        """Test if the servers details are returned."""
306

  
307
        response = self.client.get('/api/v1.1/servers/detail')
308

  
309
        # Make sure both DB and API responses are sorted by id,
310
        # to allow for 1-1 comparisons
311
        vms_from_db = VirtualMachine.objects.filter(deleted=False).order_by('id')
312
        vms_from_api = json.loads(response.content)['servers']['values']
313
        vms_from_api = sorted(vms_from_api, key=lambda vm: vm['id'])
314
        self.assertEqual(len(vms_from_db), len(vms_from_api))
315

  
316
        id_list = [vm.id for vm in vms_from_db]
317
        number = 0
318
        for vm_id in id_list:
319
            vm_from_api = vms_from_api[number]
320
            vm_from_db = VirtualMachine.objects.get(id=vm_id)
321
            self.assertEqual(vm_from_api['flavorRef'], vm_from_db.flavor.id)
322
            self.assertEqual(vm_from_api['hostId'], vm_from_db.hostid)
323
            self.assertEqual(vm_from_api['id'], vm_from_db.id)
324
            self.assertEqual(vm_from_api['imageRef'], vm_from_db.imageid)
325
            self.assertEqual(vm_from_api['name'], vm_from_db.name)
326
            self.assertEqual(vm_from_api['status'], get_rsapi_state(vm_from_db))
327
            number += 1
328
        for vm_from_api in vms_from_api:
329
            vm_from_db = VirtualMachine.objects.get(id=vm_from_api['id'])
330
            self.assertEqual(vm_from_api['flavorRef'], vm_from_db.flavor.id)
331
            self.assertEqual(vm_from_api['hostId'], vm_from_db.hostid)
332
            self.assertEqual(vm_from_api['id'], vm_from_db.id)
333
            self.assertEqual(vm_from_api['imageRef'], vm_from_db.imageid)
334
            self.assertEqual(vm_from_api['name'], vm_from_db.name)
335
            self.assertEqual(vm_from_api['status'], get_rsapi_state(vm_from_db))
104
class BaseTestCase(TestCase):
105
    def get(self, url, user='user'):
106
        with astakos_user(user):
107
            response = self.client.get(url)
108
        return response
109

  
110
    def delete(self, url, user='user'):
111
        with astakos_user(user):
112
            response = self.client.delete(url)
113
        return response
114

  
115
    def post(self, url, user='user', params={}, ctype='json'):
116
        if ctype == 'json':
117
            content_type = 'application/json'
118
        with astakos_user(user):
119
            response = self.client.post(url, params, content_type=content_type)
120
        return response
121

  
122
    def put(self, url, user='user', params={}, ctype='json'):
123
        if ctype == 'json':
124
            content_type = 'application/json'
125
        with astakos_user(user):
126
            response = self.client.put(url, params, content_type=content_type)
127
        return response
128

  
129
    def assertSuccess(self, response):
336 130
        self.assertTrue(response.status_code in [200, 203])
337 131

  
338
    def test_wrong_server(self):
339
        """Test 404 response if server does not exist."""
340

  
341
        response = self.client.get('/api/v1.1/servers/%d' % self.test_wrong_server_id)
342
        self.assertEqual(response.status_code, 404)
343

  
344
    def test_create_server_empty(self):
345
        """Test if the create server call returns a 400 badRequest if
346
           no attributes are specified."""
347

  
348
        response = self.client.post('/api/v1.1/servers', {})
349
        self.assertEqual(response.status_code, 400)
350

  
351
    def test_create_server(self):
352
        """Test if the create server call returns the expected response
353
           if a valid request has been speficied."""
354

  
355
        request = {
356
                    "server": {
357
                        "name": "new-server-test",
358
                        "userid": "test",
359
                        "imageRef": 1,
360
                        "flavorRef": 1,
361
                        "metadata": {
362
                            "My Server Name": "Apache1"
363
                        },
364
                        "personality": []
365
                    }
366
        }
367
        response = self.client.post('/api/v1.1/servers', json.dumps(request),
368
                                    content_type='application/json')
369
        self.assertEqual(response.status_code, 202)
370
        #TODO: check response.content
371
        #TODO: check create server with wrong options (eg non existing flavor)
372

  
373
    def test_server_polling(self):
374
        """Test if the server polling works as expected."""
375

  
376
        response = self.client.get('/api/v1.1/servers/detail')
377
        vms_from_api_initial = json.loads(response.content)['servers']['values']
378
        ts = mktime(parsedate(response['Date']))
379
        since = datetime.datetime.fromtimestamp(ts).isoformat() + 'Z'
380
        response = self.client.get('/api/v1.1/servers/detail?changes-since=%s' % since)
381
        self.assertEqual(len(response.content), 0)
382

  
383
        #now create a machine. Then check if it is on the list
384
        request = {
385
                    "server": {
386
                        "name": "new-server-test",
387
                        "imageRef": 1,
388
                        "flavorRef": 1,
389
                        "metadata": {
390
                            "My Server Name": "Apache1"
391
                        },
392
                        "personality": []
393
                    }
394
        }
395

  
396
        path = '/api/v1.1/servers'
397
        response = self.client.post(path, json.dumps(request), content_type='application/json')
398
        self.assertEqual(response.status_code, 202)
399

  
400
        response = self.client.get('/api/v1.1/servers/detail?changes-since=%s' % since)
401
        self.assertEqual(response.status_code, 200)
402
        vms_from_api_after = json.loads(response.content)['servers']['values']
403
        #make sure the newly created server is included on the updated list
404
        self.assertEqual(len(vms_from_api_after), 1)
405

  
406
    def test_reboot_server(self):
407
        """Test if the specified server is rebooted."""
408
        request = {'reboot': {'type': 'HARD'}}
409
        path = '/api/v1.1/servers/%d/action' % self.test_server_id
410
        response = self.client.post(path, json.dumps(request), content_type='application/json')
411
        self.assertEqual(response.status_code, 202)
412
        # server id that does not exist
413
        path = '/api/v1.1/servers/%d/action' % self.test_wrong_server_id
414
        response = self.client.post(path, json.dumps(request), content_type='application/json')
415
        self.assertEqual(response.status_code, 404)
132
    def assertFault(self, response, status_code, name):
133
        self.assertEqual(response.status_code, status_code)
134
        fault = json.loads(response.content)
135
        self.assertEqual(fault.keys(), [name])
416 136

  
417
    def test_shutdown_server(self):
418
        """Test if the specified server is shutdown."""
137
    def assertBadRequest(self, response):
138
        self.assertFault(response, 400, 'badRequest')
419 139

  
420
        request = {'shutdown': {}}
421
        path = '/api/v1.1/servers/%d/action' % self.test_server_id
422
        response = self.client.post(path, json.dumps(request), content_type='application/json')
423
        self.assertEqual(response.status_code, 202)
424
        # server id that does not exist
425
        path = '/api/v1.1/servers/%d/action' % self.test_wrong_server_id
426
        response = self.client.post(path, json.dumps(request), content_type='application/json')
427
        self.assertEqual(response.status_code, 404)
140
    def assertItemNotFound(self, response):
141
        self.assertFault(response, 404, 'itemNotFound')
428 142

  
429
    def test_start_server(self):
430
        """Test if the specified server is started."""
431 143

  
432
        request = {'start': {}}
433
        path = '/api/v1.1/servers/%d/action' % self.test_server_id
434
        response = self.client.post(path, json.dumps(request), content_type='application/json')
435
        self.assertEqual(response.status_code, 202)
436
        # server id that does not exist
437
        path = '/api/v1.1/servers/%d/action' % self.test_wrong_server_id
438
        response = self.client.post(path, json.dumps(request), content_type='application/json')
439
        self.assertEqual(response.status_code, 404)
144
class FlavorAPITest(BaseTestCase):
440 145

  
441
    def test_delete_server(self):
442
        """Test if the specified server is deleted."""
443
        response = self.client.delete('/api/v1.1/servers/%d' % self.test_server_id)
444
        self.assertEqual(response.status_code, 204)
445
        # server id that does not exist
446
        response = self.client.delete('/api/v1.1/servers/%d' % self.test_wrong_server_id)
447
        self.assertEqual(response.status_code, 404)
146
    def setUp(self):
147
        self.flavor1 = mfactory.FlavorFactory()
148
        self.flavor2 = mfactory.FlavorFactory(deleted=True)
149
        self.flavor3 = mfactory.FlavorFactory()
448 150

  
449 151
    def test_flavor_list(self):
450 152
        """Test if the expected list of flavors is returned by."""
451 153

  
452
        response = self.client.get('/api/v1.1/flavors')
154
        # with astakos_user('user'):
155
        #     response = self.client.get('/api/v1.1/flavors')
156
        response = self.get('/api/v1.1/flavors')
157

  
453 158
        flavors_from_api = json.loads(response.content)['flavors']['values']
454
        flavors_from_db = Flavor.objects.all()
159
        flavors_from_db = Flavor.objects.filter(deleted=False)
455 160
        self.assertEqual(len(flavors_from_api), len(flavors_from_db))
456 161
        self.assertTrue(response.status_code in [200, 203])
457 162
        for flavor_from_api in flavors_from_api:
......
461 166

  
462 167
    def test_flavors_details(self):
463 168
        """Test if the flavors details are returned."""
169
        response = self.get('/api/v1.1/flavors/detail')
464 170

  
465
        response = self.client.get('/api/v1.1/flavors/detail')
466
        flavors_from_db = Flavor.objects.all()
171
        flavors_from_db = Flavor.objects.filter(deleted=False)
467 172
        flavors_from_api = json.loads(response.content)['flavors']['values']
468 173

  
469 174
        # Assert that all flavors in the db appear inthe API call result
......
490 195

  
491 196
    def test_flavor_details(self):
492 197
        """Test if the expected flavor is returned."""
198
        flavor = self.flavor3
199

  
200
        response = self.get('/api/v1.1/flavors/%d' % flavor.id)
493 201

  
494
        response = self.client.get('/api/v1.1/flavors/%d' % self.test_flavor_id)
495 202
        flavor_from_api = json.loads(response.content)['flavor']
496
        flavor_from_db = Flavor.objects.get(id=self.test_flavor_id)
203
        flavor_from_db = Flavor.objects.get(id=flavor.id)
497 204
        self.assertEqual(flavor_from_api['cpu'], flavor_from_db.cpu)
498 205
        self.assertEqual(flavor_from_api['id'], flavor_from_db.id)
499 206
        self.assertEqual(flavor_from_api['disk'], flavor_from_db.disk)
......
501 208
        self.assertEqual(flavor_from_api['ram'], flavor_from_db.ram)
502 209
        self.assertTrue(response.status_code in [200, 203])
503 210

  
211
    def test_deleted_flavor_details(self):
212
        """Test that API returns details for deleted flavors"""
213
        flavor = self.flavor2
214
        response = self.get('/api/v1.1/flavors/%d' % flavor.id)
215
        flavor_from_api = json.loads(response.content)['flavor']
216
        self.assertEqual(response.status_code, 200)
217
        self.assertEquals(flavor_from_api['name'], flavor.name)
218

  
219
    def test_deleted_flavors_list(self):
220
        """Test that deleted flavors do not appear to flavors list"""
221
        response = self.get('/api/v1.1/flavors')
222
        flavors_from_api = json.loads(response.content)['flavors']['values']
223
        self.assertEqual(len(flavors_from_api), 2)
224

  
225
    def test_deleted_flavors_details(self):
226
        """Test that deleted flavors do not appear to flavors detail list"""
227
        mfactory.FlavorFactory(deleted=True)
228
        response = self.get('/api/v1.1/flavors/detail')
229
        flavors_from_api = json.loads(response.content)['flavors']['values']
230
        self.assertEqual(len(flavors_from_api), 2)
231

  
504 232
    def test_wrong_flavor(self):
505 233
        """Test 404 result when requesting a flavor that does not exist."""
506 234

  
507
        response = self.client.get('/api/v1.1/flavors/%d' % self.test_wrong_flavor_id)
508
        self.assertTrue(response.status_code in [404, 503])
235
        response = self.get('/api/v1.1/flavors/%d' % 22)
236
        self.assertTrue(response.status_code in [404])
509 237

  
510
    def test_image_list(self):
511
        """Test if the expected list of images is returned by the API."""
512 238

  
513
        response = self.client.get('/api/v1.1/images')
514
        images_from_api = json.loads(response.content)['images']['values']
515
        images_from_db = Image.objects.all()
516
        self.assertEqual(len(images_from_api), len(images_from_db))
239
class ServerAPITest(BaseTestCase):
240
    def setUp(self):
241
        self.user1 = 'user1'
242
        self.user2 = 'user2'
243
        self.vm1 = mfactory.VirtualMachineFactory(userid=self.user1)
244
        self.vm2 = mfactory.VirtualMachineFactory(userid=self.user2)
245
        self.vm3 = mfactory.VirtualMachineFactory(deleted=True,
246
                                                  userid=self.user1)
247
        self.vm4 = mfactory.VirtualMachineFactory(userid=self.user2)
248

  
249
    def test_server_list_1(self):
250
        """Test if the expected list of servers is returned."""
251
        response = self.get('/api/v1.1/servers')
517 252
        self.assertTrue(response.status_code in [200, 203])
518
        for image_from_api in images_from_api:
519
            image_from_db = Image.objects.get(id=image_from_api['id'])
520
            self.assertEqual(image_from_api['id'], image_from_db.id)
521
            self.assertEqual(image_from_api['name'], image_from_db.name)
253
        servers = json.loads(response.content)['servers']['values']
254
        self.assertEqual(servers, [])
522 255

  
523
    def test_wrong_image(self):
524
        """Test 404 result if a non existent image is requested."""
256
    def test_server_list_2(self):
257
        """Test if the expected list of servers is returned."""
258
        response = self.get('/api/v1.1/servers', self.user1)
259
        self.assertTrue(response.status_code in [200, 203])
260
        servers = json.loads(response.content)['servers']['values']
261
        db_server = self.vm1
262
        self.assertEqual(servers, [{'name': db_server.name,
263
                                    'id': db_server.id}])
264

  
265
    def test_server_list_detail(self):
266
        """Test if the servers list details are returned."""
267
        user = self.user2
268
        user_vms = {self.vm2.id: self.vm2,
269
                    self.vm4.id: self.vm4}
270

  
271
        response = self.get('/api/v1.1/servers/detail', user)
272
        servers = json.loads(response.content)['servers']['values']
273
        self.assertEqual(len(servers), len(user_vms))
274
        for api_vm in servers:
275
            db_vm = user_vms[api_vm['id']]
276
            self.assertEqual(api_vm['flavorRef'], db_vm.flavor.id)
277
            self.assertEqual(api_vm['hostId'], db_vm.hostid)
278
            self.assertEqual(api_vm['id'], db_vm.id)
279
            self.assertEqual(api_vm['imageRef'], db_vm.imageid)
280
            self.assertEqual(api_vm['name'], db_vm.name)
281
            self.assertEqual(api_vm['status'], get_rsapi_state(db_vm))
282
            self.assertTrue(response.status_code in [200, 203])
283

  
284
    def test_server_detail(self):
285
        """Test if a server details are returned."""
286
        db_vm = self.vm2
287
        user = self.vm2.userid
288
        db_vm_meta = mfactory.VirtualMachineMetadataFactory(vm=db_vm)
289

  
290
        response = self.get('/api/v1.1/servers/%d' % db_vm.id, user)
291
        server = json.loads(response.content)['server']
292

  
293
        self.assertEqual(server['flavorRef'], db_vm.flavor.id)
294
        self.assertEqual(server['hostId'], db_vm.hostid)
295
        self.assertEqual(server['id'], db_vm.id)
296
        self.assertEqual(server['imageRef'], db_vm.imageid)
297
        self.assertEqual(server['name'], db_vm.name)
298
        self.assertEqual(server['status'], get_rsapi_state(db_vm))
299

  
300
        metadata = server['metadata']['values']
301
        self.assertEqual(len(metadata), 1)
302
        self.assertEqual(metadata[db_vm_meta.meta_key], db_vm_meta.meta_value)
303
        self.assertTrue(response.status_code in [200, 203])
304

  
305
    def test_noauthorized(self):
306
        """Test 404 for detail of other user vm"""
307
        db_vm = self.vm2
525 308

  
526
        response = self.client.get('/api/v1.1/images/%d' % self.test_wrong_image_id)
309
        response = self.get('/api/v1.1/servers/%d' % db_vm.id, 'wrong_user')
527 310
        self.assertEqual(response.status_code, 404)
528 311

  
529
    def test_server_metadata(self):
530
        """Test server's metadata (add, edit)."""
312
    def test_wrong_server(self):
313
        """Test 404 response if server does not exist."""
314
        response = self.get('/api/v1.1/servers/%d' % 5000)
315
        self.assertEqual(response.status_code, 404)
531 316

  
532
        key = 'name'
533
        request = {'meta': {key: 'a fancy name'}}
317
    def test_create_server_empty(self):
318
        """Test if the create server call returns a 400 badRequest if
319
           no attributes are specified."""
534 320

  
535
        path = '/api/v1.1/servers/%d/meta/%s' % (self.test_server_id, key)
536
        response = self.client.put(path, json.dumps(request), content_type='application/json')
537
        self.assertEqual(response.status_code, 201)
321
        response = self.post('/api/v1.1/servers', params={})
322
        self.assertEqual(response.status_code, 400)
538 323

  
539 324

  
540
def create_flavors(n=1):
541
    for i in range(n):
542
        Flavor.objects.create(
543
            cpu=randint(1, 4),
544
            ram=randint(1, 8) * 512,
545
            disk=randint(1, 40))
325
@patch('synnefo.api.util.get_image')
326
@patch('synnefo.logic.rapi_pool.GanetiRapiClient')
327
class ServerCreateAPITest(BaseTestCase):
328
    def test_create_server(self, mrapi, mimage):
329
        """Test if the create server call returns the expected response
330
           if a valid request has been speficied."""
331
        mimage.return_value = {'location': 'pithos://foo',
332
                               'disk_format': 'diskdump'}
333
        mrapi().CreateInstance.return_value = 12
334
        flavor = mfactory.FlavorFactory()
335
        # Create public network and backend
336
        network = mfactory.NetworkFactory(public=True)
337
        backend = mfactory.BackendFactory()
338
        mfactory.BackendNetworkFactory(network=network, backend=backend)
546 339

  
340
        request = {
341
                    "server": {
342
                        "name": "new-server-test",
343
                        "userid": "test_user",
344
                        "imageRef": 1,
345
                        "flavorRef": flavor.id,
346
                        "metadata": {
347
                            "My Server Name": "Apache1"
348
                        },
349
                        "personality": []
350
                    }
351
        }
352
        response = self.post('/api/v1.1/servers', 'test_user',
353
                                 json.dumps(request), 'json')
354
        self.assertEqual(response.status_code, 202)
355
        mrapi().CreateInstance.assert_called_once()
547 356

  
548
def create_images(n=1):
549
    for i in range(n):
550
        Image.objects.create(
551
            name='Image %d' % (i + 1),
552
            state='ACTIVE',
553
            owner='test')
357
        api_server = json.loads(response.content)['server']
358
        self.assertEqual(api_server['status'], "BUILD")
359
        self.assertEqual(api_server['progress'], 0)
360
        self.assertEqual(api_server['metadata']['values'],
361
                        {"My Server Name":  "Apache1"})
362
        self.assertTrue('adminPass' in api_server)
554 363

  
364
        db_vm = VirtualMachine.objects.get(userid='test_user')
365
        self.assertEqual(api_server['name'], db_vm.name)
366
        self.assertEqual(api_server['status'], db_vm.operstate)
555 367

  
556
def create_image_metadata(n=1):
557
    images = Image.objects.all()
558
    for i in range(n):
559
        ImageMetadata.objects.create(
560
            meta_key='Key%d' % (i + 1),
561
            meta_value='Value %d' % (i + 1),
562
            image=choice(images))
368
    def test_create_server_no_flavor(self, mrapi, mimage):
369
        request = {
370
                    "server": {
371
                        "name": "new-server-test",
372
                        "userid": "test_user",
373
                        "imageRef": 1,
374
                        "flavorRef": 42,
375
                        "metadata": {
376
                            "My Server Name": "Apache1"
377
                        },
378
                        "personality": []
379
                    }
380
        }
381
        response = self.post('/api/v1.1/servers', 'test_user',
382
                                 json.dumps(request), 'json')
383
        self.assertItemNotFound(response)
563 384

  
564 385

  
565
def create_servers(n=1):
566
    flavors = Flavor.objects.all()
567
    images = Image.objects.all()
568
    for i in range(n):
569
        VirtualMachine.objects.create(
570
            name='Server %d' % (i + 1),
571
            owner='test',
572
            imageid=choice(images).id,
573
            hostid=str(i),
574
            flavor=choice(flavors))
386
@patch('synnefo.logic.rapi_pool.GanetiRapiClient')
387
class ServerDestroyAPITest(BaseTestCase):
388
    def test_delete_server(self, mrapi):
389
        vm = mfactory.VirtualMachineFactory()
390
        response = self.delete('/api/v1.1/servers/%d' % vm.id, vm.userid)
391
        self.assertEqual(response.status_code, 204)
392
        mrapi().DeleteInstance.assert_called_once()
393

  
394
    def test_non_existing_delete_server(self, mrapi):
395
        vm = mfactory.VirtualMachineFactory()
396
        response = self.delete('/api/v1.1/servers/%d' % 42, vm.userid)
397
        self.assertItemNotFound(response)
398
        mrapi().DeleteInstance.assert_not_called()
399

  
400

  
401
@patch('synnefo.api.util.get_image')
402
@patch('synnefo.logic.rapi_pool.GanetiRapiClient')
403
class ServerActionAPITest(BaseTestCase):
404
    def test_actions(self, mrapi, mimage):
405
        actions = ['start', 'shutdown', 'reboot']
406
        vm = mfactory.VirtualMachineFactory()
407
        vm.operstate = "STOPPED"
408
        vm.save()
409
        for action in actions:
410
            val = {'type': 'HARD'} if action == 'reboot' else {}
411
            request = {action: val}
412
            response = self.post('/api/v1.1/servers/%d/action' % vm.id,
413
                                vm.userid, json.dumps(request), 'json')
414
            self.assertEqual(response.status_code, 202)
415
            if action == 'shutdown':
416
                self.assertEqual(VirtualMachine.objects.get(id=vm.id).action,
417
                                 "STOP")
418
            else:
419
                self.assertEqual(VirtualMachine.objects.get(id=vm.id).action,
420
                                 action.upper())
421

  
422
    def test_firewall(self, mrapi, mimage):
423
        vm = mfactory.VirtualMachineFactory()
424
        vm.operstate = "STOPPED"
425
        vm.save()
426
        request = {'firewallProfile': {'profile': 'PROTECTED'}}
427
        response = self.post('/api/v1.1/servers/%d/action' % vm.id,
428
                             vm.userid, json.dumps(request), 'json')
429
        self.assertEqual(response.status_code, 202)
430
        mrapi().ModifyInstance.assert_called_once()
575 431

  
576 432

  
577
def create_server_metadata(n=1):
578
    servers = VirtualMachine.objects.all()
579
    for i in range(n):
580
        VirtualMachineMetadata.objects.create(
581
            meta_key='Key%d' % (i + 1),
582
            meta_value='Value %d' % (i + 1),
583
            vm=choice(servers))
433
class ServerMetadataAPITest(BaseTestCase):
434
    def setUp(self):
435
        self.vm = mfactory.VirtualMachineFactory()
436
        self.metadata = mfactory.VirtualMachineMetadataFactory(vm=self.vm)
437

  
438
    def test_get_metadata(self):
439
        vm = self.vm
440
        create_meta = lambda: mfactory.VirtualMachineMetadataFactory(vm=vm)
441
        metadata = [create_meta(), create_meta(), create_meta()]
442
        response = self.get('/api/v1.1/servers/%d/meta' % vm.id, vm.userid)
443
        self.assertTrue(response.status_code in [200, 203])
444
        api_metadata = json.loads(response.content)['metadata']['values']
445
        self.assertEqual(len(api_metadata), len(metadata) + 1)
446
        for db_m in metadata:
447
            self.assertEqual(api_metadata[db_m.meta_key], db_m.meta_value)
448

  
449
        request = {'metadata':
450
                        {'foo': 'bar'},
451
                        metadata[0].meta_key: 'bar2'
452
                  }
453
        response = self.post('/api/v1.1/servers/%d/meta' % vm.id, vm.userid,
454
                             json.dumps(request), 'json')
455
        metadata2 = VirtualMachineMetadata.objects.filter(vm=vm)
456
        response = self.get('/api/v1.1/servers/%d/meta' % vm.id, vm.userid)
457
        self.assertTrue(response.status_code in [200, 203])
458
        api_metadata2 = json.loads(response.content)['metadata']['values']
459
        self.assertTrue('foo' in api_metadata2.keys())
460
        self.assertTrue(api_metadata2[metadata[0].meta_key], 'bar2')
461
        self.assertEqual(len(api_metadata2), len(metadata2))
462
        for db_m in metadata2:
463
            self.assertEqual(api_metadata2[db_m.meta_key], db_m.meta_value)
464

  
465
        # Create new meta
466
        request = {'meta': {'foo2': 'bar2'}}
467
        response = self.put('/api/v1.1/servers/%d/meta/foo2' % vm.id,
468
                            vm.userid, json.dumps(request), 'json')
469

  
470
        # Get the new meta
471
        response = self.get('/api/v1.1/servers/%d/meta/foo2' % vm.id,
472
                            vm.userid)
473
        meta = json.loads(response.content)['meta']
474
        self.assertEqual(meta['foo2'], 'bar2')
475

  
476
        # Delete the new meta
477
        response = self.delete('/api/v1.1/servers/%d/meta/foo2' % vm.id,
478
                               vm.userid)
479
        self.assertEqual(response.status_code, 204)
584 480

  
481
        # Try to get the deleted meta: should raise 404
482
        response = self.get('/api/v1.1/servers/%d/meta/foo2' % vm.id,
483
                            vm.userid)
484
        self.assertEqual(response.status_code, 404)
585 485

  
586
class AssertInvariant(object):
587
    def __init__(self, callable, *args, **kwargs):
588
        self.callable = callable
589
        self.args = args
590
        self.kwargs = kwargs
486
    def test_invalid_metadata(self):
487
        vm = self.vm
488
        response = self.post('/api/v1.1/servers/%d/meta' % vm.id, vm.userid)
489
        self.assertBadRequest(response)
490
        self.assertEqual(len(vm.metadata.all()), 1)
591 491

  
592
    def __enter__(self):
593
        self.value = self.callable(*self.args, **self.kwargs)
594
        return self.value
492
    def test_invalid_metadata_server(self):
493
        response = self.post('/api/v1.1/servers/42/meta', 'user')
494
        self.assertItemNotFound(response)
595 495

  
596
    def __exit__(self, type, value, tb):
597
        assert self.value == self.callable(*self.args, **self.kwargs)
496
    def test_get_meta_invalid_key(self):
497
        vm = self.vm
498
        response = self.get('/api/v1.1/servers/%d/meta/foo2' % vm.id,
499
                            vm.userid)
500
        self.assertItemNotFound(response)
598 501

  
599 502

  
600
class BaseTestCase(TestCase):
601
    FLAVORS = 1
602
    IMAGES = 1
603
    SERVERS = 1
604
    SERVER_METADATA = 0
605
    IMAGE_METADATA = 0
606
    fixtures = ['users']
607

  
503
@patch('synnefo.logic.rapi_pool.GanetiRapiClient')
504
class NetworkAPITest(BaseTestCase):
608 505
    def setUp(self):
609
        self.client = AaiClient()
610
        create_flavors(self.FLAVORS)
611
        create_images(self.IMAGES)
612
        create_image_metadata(self.IMAGE_METADATA)
613
        create_servers(self.SERVERS)
614
        create_server_metadata(self.SERVER_METADATA)
615

  
616
    def assertFault(self, response, status_code, name):
617
        self.assertEqual(response.status_code, status_code)
618
        fault = json.loads(response.content)
619
        self.assertEqual(fault.keys(), [name])
620

  
621
    def assertBadRequest(self, response):
622
        self.assertFault(response, 400, 'badRequest')
623

  
624
    def assertItemNotFound(self, response):
625
        self.assertFault(response, 404, 'itemNotFound')
626

  
627
    def list_images(self, detail=False):
628
        path = '/api/v1.1/images'
506
        self.mac_prefixes = mfactory.MacPrefixPoolTableFactory()
507
        self.bridges = mfactory.BridgePoolTableFactory()
508
        self.user = 'dummy-user'
509
        self.net1 = mfactory.NetworkFactory(userid=self.user)
510
        self.net2 = mfactory.NetworkFactory(userid=self.user)
511

  
512
    def assertNetworksEqual(self, db_net, api_net, detail=False):
513
        self.assertEqual(str(db_net.id), api_net["id"])
514
        self.assertEqual(db_net.name, api_net['name'])
629 515
        if detail:
630
            path += '/detail'
631
        response = self.client.get(path)
632
        self.assertTrue(response.status_code in (200, 203))
633
        reply = json.loads(response.content)
634
        self.assertEqual(reply.keys(), ['images'])
635
        self.assertEqual(reply['images'].keys(), ['values'])
636
        return reply['images']['values']
637

  
638
    def list_metadata(self, path):
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.keys(), ['metadata'])
643
        self.assertEqual(reply['metadata'].keys(), ['values'])
644
        return reply['metadata']['values']
645

  
646
    def list_server_metadata(self, server_id):
647
        path = '/api/v1.1/servers/%d/meta' % server_id
648
        return self.list_metadata(path)
649

  
650
    def list_image_metadata(self, image_id):
651
        path = '/api/v1.1/images/%d/meta' % image_id
652
        return self.list_metadata(path)
653

  
654
    def update_metadata(self, path, metadata):
655
        data = json.dumps({'metadata': metadata})
656
        response = self.client.post(path, data, content_type='application/json')
657
        self.assertEqual(response.status_code, 201)
658
        reply = json.loads(response.content)
659
        self.assertEqual(reply.keys(), ['metadata'])
660
        return reply['metadata']
661

  
662
    def update_server_metadata(self, server_id, metadata):
663
        path = '/api/v1.1/servers/%d/meta' % server_id
664
        return self.update_metadata(path, metadata)
665

  
666
    def update_image_metadata(self, image_id, metadata):
667
        path = '/api/v1.1/images/%d/meta' % image_id
668
        return self.update_metadata(path, metadata)
669

  
670
    def create_server_meta(self, server_id, meta):
671
        key = meta.keys()[0]
672
        path = '/api/v1.1/servers/%d/meta/%s' % (server_id, key)
673
        data = json.dumps({'meta': meta})
674
        response = self.client.put(path, data, content_type='application/json')
675
        self.assertEqual(response.status_code, 201)
676
        reply = json.loads(response.content)
677
        self.assertEqual(reply.keys(), ['meta'])
678
        response_meta = reply['meta']
679
        self.assertEqual(response_meta, meta)
680

  
681
    def get_all_server_metadata(self):
682
        metadata = defaultdict(dict)
683
        for m in VirtualMachineMetadata.objects.all():
684
            metadata[m.vm.id][m.meta_key] = m.meta_value
685
        return metadata
686

  
687
    def get_all_image_metadata(self):
688
        metadata = defaultdict(dict)
689
        for m in ImageMetadata.objects.all():
690
            metadata[m.image.id][m.meta_key] = m.meta_value
691
        return metadata
692

  
693
    def list_networks(self, detail=False):
694
        path = '/api/v1.1/networks'
695
        if detail:
696
            path += '/detail'
697
        response = self.client.get(path)
698
        self.assertTrue(response.status_code in (200, 203))
699
        reply = json.loads(response.content)
700
        self.assertEqual(reply.keys(), ['networks'])
701
        self.assertEqual(reply['networks'].keys(), ['values'])
702
        return reply['networks']['values']
703

  
704
    def create_network(self, name):
705
        with astakos_user("admin@adminland.com"):
706
            path = '/api/v1.1/networks'
707
            data = json.dumps({'network': {'name': name}})
708
            response = self.client.post(path, data, content_type='application/json')
709
            self.assertEqual(response.status_code, 202)
710
            reply = json.loads(response.content)
711
            self.assertEqual(reply.keys(), ['network'])
712
            return reply
713

  
714
    def get_network_details(self, network_id):
715
        path = '/api/v1.1/networks/%s' % network_id
716
        response = self.client.get(path)
717
        self.assertEqual(response.status_code, 200)
718
        reply = json.loads(response.content)
719
        self.assertEqual(reply.keys(), ['network'])
720
        return reply['network']
516
            self.assertEqual(db_net.state, api_net['status'])
517
            self.assertEqual(db_net.flavor, api_net['type'])
518
            self.assertEqual(db_net.subnet, api_net['cidr'])
519
            self.assertEqual(db_net.subnet6, api_net['cidr6'])
520
            self.assertEqual(db_net.gateway, api_net['gateway'])
521
            self.assertEqual(db_net.gateway6, api_net['gateway6'])
522
            self.assertEqual(db_net.dhcp, api_net['dhcp'])
523

  
524
    def test_create_network_1(self, mrapi):
525
        request = {
526
            'network': {'name': 'foo'}
527
            }
528
        response = self.post('/api/v1.1/networks/', 'user1',
529
                             json.dumps(request), 'json')
530
        self.assertEqual(response.status_code, 202)
531
        db_networks = Network.objects.filter(userid='user1')
532
        self.assertEqual(len(db_networks), 1)
533
        db_net = db_networks[0]
534
        api_net = json.loads(response.content)['network']
535
        self.assertNetworksEqual(db_net, api_net)
536
        mrapi.CreateNetwork.assert_called()
537
        mrapi.ConnectNetwork.assert_called()
538

  
539
    def test_invalid_data_1(self, mrapi):
540
        """Test invalid flavor"""
541
        request = {
542
            'network': {'name': 'foo', 'type': 'LoLo'}
543
            }
544
        response = self.post('/api/v1.1/networks/', 'user1',
545
                             json.dumps(request), 'json')
546
        self.assertBadRequest(response)
547
        self.assertEqual(len(Network.objects.filter(userid='user1')), 0)
721 548

  
722
    def update_network_name(self, network_id, new_name):
723
        path = '/api/v1.1/networks/%s' % network_id
724
        data = json.dumps({'network': {'name': new_name}})
725
        response = self.client.put(path, data, content_type='application/json')
549
    def test_invalid_data_2(self, mrapi):
550
        """Test invalid subnet"""
551
        request = {
552
            'network': {'name': 'foo', 'cidr': '10.0.0.0/8'}
553
            }
554
        response = self.post('/api/v1.1/networks/', 'user1',
555
                             json.dumps(request), 'json')
556
        self.assertFault(response, 413, "overLimit")
557

  
558
    def test_invalid_data_3(self, mrapi):
559
        """Test unauthorized to create public network"""
560
        request = {
561
                'network': {'name': 'foo', 'public': True}
562
            }
563
        response = self.post('/api/v1.1/networks/', 'user1',
564
                             json.dumps(request), 'json')
565
        self.assertFault(response, 403, "forbidden")
566

  
567
    def test_list_networks(self, mrapi):
568
        mfactory.NetworkFactory(userid=self.user, deleted=True)
569
        response = self.get('/api/v1.1/networks/', self.user)
570
        self.assertSuccess(response)
571
        db_nets = Network.objects.filter(userid=self.user, deleted=False)
572
        api_nets = json.loads(response.content)["networks"]["values"]
573
        self.assertEqual(len(db_nets), len(api_nets))
574
        for api_net in api_nets:
575
            net_id = api_net['id']
576
            self.assertNetworksEqual(Network.objects.get(id=net_id), api_net)
577

  
578
    def test_list_networks_detail(self, mrapi):
579
        mfactory.NetworkFactory(userid=self.user, deleted=True)
580
        response = self.get('/api/v1.1/networks/detail', self.user)
581
        self.assertSuccess(response)
582
        db_nets = Network.objects.filter(userid=self.user, deleted=False)
583
        api_nets = json.loads(response.content)["networks"]["values"]
584
        self.assertEqual(len(db_nets), len(api_nets))
585
        for api_net in api_nets:
586
            net_id = api_net['id']
587
            self.assertNetworksEqual(Network.objects.get(id=net_id), api_net,
588
                                     detail=True)
589

  
590
    def test_network_details_1(self, mrapi):
591
        response = self.get('/api/v1.1/networks/%d' % self.net1.id,
592
                            self.net1.userid)
593
        self.assertSuccess(response)
594
        api_net = json.loads(response.content)["network"]
595
        self.assertNetworksEqual(self.net1, api_net, detail=True)
596

  
597
    def test_invalid_network(self, mrapi):
598
        response = self.get('/api/v1.1/networks/%d' % 42,
599
                            self.net1.userid)
600
        self.assertItemNotFound(response)
601

  
602
    def test_rename_network(self, mrapi):
603
        request = {'network': {'name': "new_name"}}
604
        response = self.put('/api/v1.1/networks/%d' % self.net2.id,
605
                            self.net2.userid, json.dumps(request), 'json')
726 606
        self.assertEqual(response.status_code, 204)
727

  
728
    def delete_network(self, network_id):
729
        path = '/api/v1.1/networks/%s' % network_id
730
        response = self.client.delete(path)
607
        self.assertEqual(Network.objects.get(id=self.net2.id).name, "new_name")
608

  
609
    def test_rename_public_network(self, mrapi):
610
        net = mfactory.NetworkFactory(public=True)
611
        request = {'network': {'name': "new_name"}}
612
        response = self.put('/api/v1.1/networks/%d' % net.id,
613
                            self.net2.userid, json.dumps(request), 'json')
614
        self.assertFault(response, 403, 'forbidden')
615

  
616
    def test_delete_network(self, mrapi):
617
        response = self.delete('/api/v1.1/networks/%d' % self.net2.id,
618
                                self.net2.userid)
731 619
        self.assertEqual(response.status_code, 204)
732

  
733
    def add_to_network(self, network_id, server_id):
734
        path = '/api/v1.1/networks/%s/action' % network_id
735
        data = json.dumps({'add': {'serverRef': server_id}})
736
        response = self.client.post(path, data, content_type='application/json')
620
        net = Network.objects.get(id=self.net2.id, userid=self.net2.userid)
621
        self.assertEqual(net.action, 'DESTROY')
622
        mrapi.DeleteNetwork.assert_called()
623

  
624
    def test_delete_public_network(self, mrapi):
625
        net = mfactory.NetworkFactory(public=True)
626
        response = self.delete('/api/v1.1/networks/%d' % net.id,
627
                                self.net2.userid)
628
        self.assertFault(response, 403, 'forbidden')
629
        mrapi.DeleteNetwork.assert_not_called()
630

  
631
    def test_add_nic(self, mrapi):
632
        user = 'userr'
633
        vm = mfactory.VirtualMachineFactory(name='yo', userid=user)
634
        net = mfactory.NetworkFactory(state='ACTIVE', userid=user)
635
        request = {'add': {'serverRef': vm.id}}
636
        response = self.post('/api/v1.1/networks/%d/action' % net.id,
637
                             net.userid, json.dumps(request), 'json')
737 638
        self.assertEqual(response.status_code, 202)
738 639

  
739
    def remove_from_network(self, network_id, server_id):
740
        path = '/api/v1.1/networks/%s/action' % network_id
741
        data = json.dumps({'remove': {'serverRef': server_id}})
742
        response = self.client.post(path, data, content_type='application/json')
743
        self.assertEqual(response.status_code, 202)
744

  
745

  
746
def popdict(l, **kwargs):
747
    """Pops a dict from list `l` based on the predicates given as `kwargs`."""
748

  
749
    for i in range(len(l)):
750
        item = l[i]
751
        match = True
752
        for key, val in kwargs.items():
753
            if item[key] != val:
754
                match = False
755
                break
756
        if match:
757
            del l[i]
758
            return item
759
    return None
760

  
761

  
762
class ListImages(BaseTestCase):
763
    IMAGES = 10
764

  
765
    def test_list_images(self):
766
        images = self.list_images()
767
        keys = set(['id', 'name'])
768
        for img in Image.objects.all():
769
            image = popdict(images, id=img.id)
770
            self.assertTrue(image is not None)
771
            self.assertEqual(set(image.keys()), keys)
772
            self.assertEqual(image['id'], img.id)
773
            self.assertEqual(image['name'], img.name)
774
        self.assertEqual(images, [])
775

  
776
    def test_list_images_detail(self):
777
        images = self.list_images(detail=True)
778
        keys = set(['id', 'name', 'updated', 'created', 'status', 'progress'])
779
        for img in Image.objects.all():
780
            image = popdict(images, id=img.id)
781
            self.assertTrue(image is not None)
782
            self.assertEqual(set(image.keys()), keys)
783
            self.assertEqual(image['id'], img.id)
784
            self.assertEqual(image['name'], img.name)
785
            self.assertEqual(image['status'], img.state)
786
            self.assertEqual(image['progress'], 100 if img.state == 'ACTIVE' else 0)
787
        self.assertEqual(images, [])
788

  
789

  
790
class ListServerMetadata(BaseTestCase):
791
    SERVERS = 5
792
    SERVER_METADATA = 100
793

  
794
    def test_list_metadata(self):
795
        with AssertInvariant(self.get_all_server_metadata) as metadata:
796
            for vm in VirtualMachine.objects.all():
797
                response_metadata = self.list_server_metadata(vm.id)
798
                self.assertEqual(response_metadata, metadata[vm.id])
799

  
800
    def test_invalid_server(self):
801
        with AssertInvariant(self.get_all_server_metadata):
802
            response = self.client.get('/api/v1.1/servers/0/meta')
803
            self.assertItemNotFound(response)
804

  
805

  
806
class UpdateServerMetadata(BaseTestCase):
807
    SERVER_METADATA = 10
808

  
809
    def test_update_metadata(self):
810
        metadata = self.get_all_server_metadata()
811
        server_id = choice(metadata.keys())
812
        new_metadata = {}
813
        for key in sample(metadata[server_id].keys(), 3):
814
            new_metadata[key] = 'New %s value' % key
815
        response_metadata = self.update_server_metadata(server_id,
816
                                                        new_metadata)
817
        metadata[server_id].update(new_metadata)
818
        self.assertEqual(response_metadata, metadata[server_id])
819
        self.assertEqual(metadata, self.get_all_server_metadata())
820

  
821
    def test_invalid_data(self):
822
        with AssertInvariant(self.get_all_server_metadata) as metadata:
823
            server_id = choice(metadata.keys())
824
            path = '/api/v1.1/servers/%d/meta' % server_id
825
            response = self.client.post(path, 'metadata', content_type='application/json')
826
            self.assertBadRequest(response)
827

  
828
    def test_invalid_server(self):
829
        with AssertInvariant(self.get_all_server_metadata):
830
            path = '/api/v1.1/servers/0/meta'
831
            data = json.dumps({'metadata': {'Key1': 'A Value'}})
832
            response = self.client.post(path, data, content_type='application/json')
833
            self.assertItemNotFound(response)
834

  
835

  
836
class GetServerMetadataItem(BaseTestCase):
837
    SERVERS = 5
838
    SERVER_METADATA = 100
839

  
840
    def test_get_metadata_item(self):
841
        with AssertInvariant(self.get_all_server_metadata) as metadata:
842
            server_id = choice(metadata.keys())
843
            key = choice(metadata[server_id].keys())
844
            path = '/api/v1.1/servers/%d/meta/%s' % (server_id, key)
845
            response = self.client.get(path)
846
            self.assertTrue(response.status_code in (200, 203))
847
            reply = json.loads(response.content)
848
            self.assertEqual(reply['meta'], {key: metadata[server_id][key]})
849

  
850
    def test_invalid_key(self):
851
        with AssertInvariant(self.get_all_server_metadata) as metadata:
852
            server_id = choice(metadata.keys())
853
            response = self.client.get('/api/v1.1/servers/%d/meta/foo' % server_id)
854
            self.assertItemNotFound(response)
855

  
856
    def test_invalid_server(self):
857
        with AssertInvariant(self.get_all_server_metadata):
858
            response = self.client.get('/api/v1.1/servers/0/meta/foo')
859
            self.assertItemNotFound(response)
860

  
861

  
862
class CreateServerMetadataItem(BaseTestCase):
863
    SERVER_METADATA = 10
864

  
865
    def test_create_metadata(self):
866
        metadata = self.get_all_server_metadata()
867
        server_id = choice(metadata.keys())
868
        meta = {'Foo': 'Bar'}
869
        self.create_server_meta(server_id, meta)
870
        metadata[server_id].update(meta)
871
        self.assertEqual(metadata, self.get_all_server_metadata())
872

  
873
    def test_update_metadata(self):
874
        metadata = self.get_all_server_metadata()
875
        server_id = choice(metadata.keys())
876
        key = choice(metadata[server_id].keys())
877
        meta = {key: 'New Value'}
878
        self.create_server_meta(server_id, meta)
879
        metadata[server_id].update(meta)
880
        self.assertEqual(metadata, self.get_all_server_metadata())
881

  
882
    def test_invalid_server(self):
883
        with AssertInvariant(self.get_all_server_metadata):
884
            path = '/api/v1.1/servers/0/meta/foo'
885
            data = json.dumps({'meta': {'foo': 'bar'}})
886
            response = self.client.put(path, data, content_type='application/json')
887
            self.assertItemNotFound(response)
888

  
889
    def test_invalid_key(self):
890
        with AssertInvariant(self.get_all_server_metadata) as metadata:
891
            server_id = choice(metadata.keys())
892
            path = '/api/v1.1/servers/%d/meta/baz' % server_id
893
            data = json.dumps({'meta': {'foo': 'bar'}})
894
            response = self.client.put(path, data, content_type='application/json')
895
            self.assertBadRequest(response)
896

  
897
    def test_invalid_data(self):
898
        with AssertInvariant(self.get_all_server_metadata) as metadata:
899
            server_id = choice(metadata.keys())
900
            path = '/api/v1.1/servers/%d/meta/foo' % server_id
901
            response = self.client.put(path, 'meta', content_type='application/json')
902
            self.assertBadRequest(response)
903

  
904

  
905
class DeleteServerMetadataItem(BaseTestCase):
906
    SERVER_METADATA = 10
907

  
908
    def test_delete_metadata(self):
909
        metadata = self.get_all_server_metadata()
910
        server_id = choice(metadata.keys())
911
        key = choice(metadata[server_id].keys())
912
        path = '/api/v1.1/servers/%d/meta/%s' % (server_id, key)
913
        response = self.client.delete(path)
914
        self.assertEqual(response.status_code, 204)
915
        metadata[server_id].pop(key)
916
        self.assertEqual(metadata, self.get_all_server_metadata())
917

  
918
    def test_invalid_server(self):
919
        with AssertInvariant(self.get_all_server_metadata):
920
            response = self.client.delete('/api/v1.1/servers/9/meta/Key1')
921
            self.assertItemNotFound(response)
922

  
923
    def test_invalid_key(self):
924
        with AssertInvariant(self.get_all_server_metadata) as metadata:
925
            server_id = choice(metadata.keys())
926
            path = '/api/v1.1/servers/%d/meta/foo' % server_id
927
            response = self.client.delete(path)
928
            self.assertItemNotFound(response)
929

  
930

  
931
class ListImageMetadata(BaseTestCase):
932
    IMAGES = 5
933
    IMAGE_METADATA = 100
640
    def test_add_nic_malformed(self, mrapi):
641
        user = 'userr'
642
        vm = mfactory.VirtualMachineFactory(name='yo', userid=user)
643
        net = mfactory.NetworkFactory(state='ACTIVE', userid=user)
644
        request = {'add': {'serveRef': vm.id}}
645
        response = self.post('/api/v1.1/networks/%d/action' % net.id,
646
                             net.userid, json.dumps(request), 'json')
647
        self.assertBadRequest(response)
934 648

  
935
    def test_list_metadata(self):
936
        with AssertInvariant(self.get_all_image_metadata) as metadata:
937
            for image in Image.objects.all():
938
                response_metadata = self.list_image_metadata(image.id)
939
                self.assertEqual(response_metadata, metadata[image.id])
940

  
941
    def test_invalid_image(self):
942
        with AssertInvariant(self.get_all_image_metadata):
943
            response = self.client.get('/api/v1.1/images/0/meta')
944
            self.assertItemNotFound(response)
945

  
946

  
947
class UpdateImageMetadata(BaseTestCase):
948
    IMAGE_METADATA = 10
949

  
950
    def test_update_metadata(self):
951
        metadata = self.get_all_image_metadata()
952
        image_id = choice(metadata.keys())
953
        new_metadata = {}
954
        for key in sample(metadata[image_id].keys(), 3):
955
            new_metadata[key] = 'New %s value' % key
956
        response_metadata = self.update_image_metadata(image_id, new_metadata)
957
        metadata[image_id].update(new_metadata)
958
        self.assertEqual(response_metadata, metadata[image_id])
959
        self.assertEqual(metadata, self.get_all_image_metadata())
960

  
961
    def test_invalid_data(self):
962
        with AssertInvariant(self.get_all_image_metadata) as metadata:
963
            image_id = choice(metadata.keys())
964
            path = '/api/v1.1/images/%d/meta' % image_id
965
            response = self.client.post(path, 'metadata', content_type='application/json')
966
            self.assertBadRequest(response)
967

  
968
    def test_invalid_server(self):
969
        with AssertInvariant(self.get_all_image_metadata):
970
            path = '/api/v1.1/images/0/meta'
971
            data = json.dumps({'metadata': {'Key1': 'A Value'}})
972
            response = self.client.post(path, data, content_type='application/json')
973
            self.assertItemNotFound(response)
649
    def test_remove_nic(self, mrapi):
650
        user = 'userr'
651
        vm = mfactory.VirtualMachineFactory(name='yo', userid=user)
652
        net = mfactory.NetworkFactory(state='ACTIVE', userid=user)
653
        nic = mfactory.NetworkInterfaceFactory(machine=vm, network=net)
654
        request = {'remove': {'attachment': 'nic-%s-%s' % (vm.id, nic.index)}}
655
        response = self.post('/api/v1.1/networks/%d/action' % net.id,
656
                             net.userid, json.dumps(request), 'json')
657
        self.assertEqual(response.status_code, 202)
658
        self.assertTrue(NetworkInterface.objects.get(id=nic.id).dirty)
659
        # Remove dirty nic
660
        response = self.post('/api/v1.1/networks/%d/action' % net.id,
661
                             net.userid, json.dumps(request), 'json')
662
        self.assertFault(response, 409, 'buildInProgress')
663

  
664
    def test_remove_nic_malformed(self, mrapi):
665
        user = 'userr'
666
        vm = mfactory.VirtualMachineFactory(name='yo', userid=user)
667
        net = mfactory.NetworkFactory(state='ACTIVE', userid=user)
668
        nic = mfactory.NetworkInterfaceFactory(machine=vm, network=net)
669
        request = {'remove':
670
                    {'att234achment': 'nic-%s-%s' % (vm.id, nic.index)}
671
                  }
672
        response = self.post('/api/v1.1/networks/%d/action' % net.id,
673
                             net.userid, json.dumps(request), 'json')
674
        self.assertBadRequest(response)
974 675

  
975 676

  
976 677
class ServerVNCConsole(BaseTestCase):
977
    SERVERS = 1
978 678

  
979 679
    def test_not_active_server(self):
980
        """Test console req for server not in ACTIVE state returns badRequest"""
981
        server_id = choice(VirtualMachine.objects.all()).id
982
        path = '/api/v1.1/servers/%d/action' % server_id
680
        """Test console req for not ACTIVE server returns badRequest"""
681
        vm = mfactory.VirtualMachineFactory()
983 682
        data = json.dumps({'console': {'type': 'vnc'}})
984
        response = self.client.post(path, data, content_type='application/json')
683
        response = self.post('/api/v1.1/servers/%d/action' % vm.id,
684
                             vm.userid, data, 'json')
985 685
        self.assertBadRequest(response)
986 686

  
987 687
    def test_active_server(self):
988 688
        """Test console req for ACTIVE server"""
989
        server_id = choice(VirtualMachine.objects.all()).id
990
        # FIXME: Start the server properly, instead of tampering with the DB
991
        vm = choice(VirtualMachine.objects.all())
689
        vm = mfactory.VirtualMachineFactory()
992 690
        vm.operstate = 'STARTED'
993 691
        vm.save()
994
        server_id = vm.id
995 692

  
996
        path = '/api/v1.1/servers/%d/action' % server_id
997 693
        data = json.dumps({'console': {'type': 'vnc'}})
998
        response = self.client.post(path, data, content_type='application/json')
694
        response = self.post('/api/v1.1/servers/%d/action' % vm.id,
695
                             vm.userid, data, 'json')
999 696
        self.assertEqual(response.status_code, 200)
1000 697
        reply = json.loads(response.content)
1001 698
        self.assertEqual(reply.keys(), ['console'])
1002 699
        console = reply['console']
1003 700
        self.assertEqual(console['type'], 'vnc')
1004
        self.assertEqual(set(console.keys()), set(['type', 'host', 'port', 'password']))
1005

  
1006

  
1007
class AaiTestCase(TestCase):
1008
    fixtures = ['users', 'api_test_data', 'auth_test_data']
1009
    apibase = '/api/v1.1'
1010

  
701
        self.assertEqual(set(console.keys()),
702
                         set(['type', 'host', 'port', 'password']))
703

  
704

  
705
@patch('synnefo.api.images.ImageBackend')
706
class ImageAPITest(BaseTestCase):
707
    def test_create_image(self, mimage):
708
        """Test that create image is not implemented"""
709
        response = self.post('/api/v1.1/images/', 'user', json.dumps(''),
710
                             'json')
711
        self.assertEqual(response.status_code, 503)
712

  
713
    def test_list_images(self, mimage):
714
        """Test that expected list of images is returned"""
715
        images = [{'id': 1, 'name': 'image-1'},
716
                  {'id': 2, 'name': 'image-2'},
717
                  {'id': 3, 'name': 'image-3'}]
718
        mimage().list.return_value = images
719
        response = self.get('/api/v1.1/images/', 'user')
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff