Revision 039e3e61

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

  
34
import json
35

  
36
from synnefo.api.tests import BaseAPITest
37
from synnefo.db.models import Flavor
38
from synnefo.db.models_factory import FlavorFactory
39

  
40

  
41
class FlavorAPITest(BaseAPITest):
42

  
43
    def setUp(self):
44
        self.flavor1 = FlavorFactory()
45
        self.flavor2 = FlavorFactory(deleted=True)
46
        self.flavor3 = FlavorFactory()
47

  
48
    def test_flavor_list(self):
49
        """Test if the expected list of flavors is returned."""
50
        response = self.get('/api/v1.1/flavors')
51
        self.assertSuccess(response)
52

  
53
        api_flavors = json.loads(response.content)['flavors']['values']
54
        db_flavors = Flavor.objects.filter(deleted=False)
55
        self.assertEqual(len(api_flavors), len(db_flavors))
56
        for api_flavor in api_flavors:
57
            db_flavor = Flavor.objects.get(id=api_flavor['id'])
58
            self.assertEqual(api_flavor['id'], db_flavor.id)
59
            self.assertEqual(api_flavor['name'], db_flavor.name)
60

  
61
    def test_flavors_details(self):
62
        """Test if the flavors details are returned."""
63
        response = self.get('/api/v1.1/flavors/detail')
64
        self.assertSuccess(response)
65

  
66
        db_flavors = Flavor.objects.filter(deleted=False)
67
        api_flavors = json.loads(response.content)['flavors']['values']
68

  
69
        self.assertEqual(len(db_flavors), len(api_flavors))
70

  
71
        for i in range(0, len(db_flavors)):
72
            api_flavor = api_flavors[i]
73
            db_flavor = Flavor.objects.get(id=db_flavors[i].id)
74
            self.assertEqual(api_flavor['cpu'], db_flavor.cpu)
75
            self.assertEqual(api_flavor['id'], db_flavor.id)
76
            self.assertEqual(api_flavor['disk'], db_flavor.disk)
77
            self.assertEqual(api_flavor['name'], db_flavor.name)
78
            self.assertEqual(api_flavor['ram'], db_flavor.ram)
79
            self.assertEqual(api_flavor['SNF:disk_template'],
80
                                        db_flavor.disk_template)
81

  
82
    def test_flavor_details(self):
83
        """Test if the expected flavor is returned."""
84
        flavor = self.flavor3
85

  
86
        response = self.get('/api/v1.1/flavors/%d' % flavor.id)
87
        self.assertSuccess(response)
88

  
89
        api_flavor = json.loads(response.content)['flavor']
90
        db_flavor = Flavor.objects.get(id=flavor.id)
91
        self.assertEqual(api_flavor['cpu'], db_flavor.cpu)
92
        self.assertEqual(api_flavor['id'], db_flavor.id)
93
        self.assertEqual(api_flavor['disk'], db_flavor.disk)
94
        self.assertEqual(api_flavor['name'], db_flavor.name)
95
        self.assertEqual(api_flavor['ram'], db_flavor.ram)
96
        self.assertEqual(api_flavor['SNF:disk_template'],
97
                         db_flavor.disk_template)
98

  
99
    def test_deleted_flavor_details(self):
100
        """Test that API returns details for deleted flavors"""
101
        flavor = self.flavor2
102
        response = self.get('/api/v1.1/flavors/%d' % flavor.id)
103
        self.assertSuccess(response)
104
        api_flavor = json.loads(response.content)['flavor']
105
        self.assertEquals(api_flavor['name'], flavor.name)
106

  
107
    def test_deleted_flavors_list(self):
108
        """Test that deleted flavors do not appear to flavors list"""
109
        response = self.get('/api/v1.1/flavors')
110
        self.assertSuccess(response)
111
        api_flavors = json.loads(response.content)['flavors']['values']
112
        self.assertEqual(len(api_flavors), 2)
113

  
114
    def test_deleted_flavors_details(self):
115
        """Test that deleted flavors do not appear to flavors detail list"""
116
        FlavorFactory(deleted=True)
117
        response = self.get('/api/v1.1/flavors/detail')
118
        self.assertSuccess(response)
119
        api_flavors = json.loads(response.content)['flavors']['values']
120
        self.assertEqual(len(api_flavors), 2)
121

  
122
    def test_wrong_flavor(self):
123
        """Test 404 result when requesting a flavor that does not exist."""
124

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

  
34
import json
35

  
36
from synnefo.api.tests import BaseAPITest
37
from synnefo.api.faults import ItemNotFound
38

  
39
from mock import patch
40
from functools import wraps
41

  
42

  
43
def assert_backend_closed(func):
44
    """Decorator for ensuring that ImageBackend is returned to pool."""
45
    @wraps(func)
46
    def wrapper(self, backend):
47
        result = func(self, backend)
48
        if backend.called is True:
49
            backend.return_value.close.assert_called_once_with()
50
        return result
51
    return wrapper
52

  
53

  
54
@patch('synnefo.api.images.ImageBackend')
55
class ImageAPITest(BaseAPITest):
56
    @assert_backend_closed
57
    def test_create_image(self, mimage):
58
        """Test that create image is not implemented"""
59
        response = self.post('/api/v1.1/images/', 'user', json.dumps(''),
60
                             'json')
61
        self.assertEqual(response.status_code, 503)
62

  
63
    @assert_backend_closed
64
    def test_list_images(self, mimage):
65
        """Test that expected list of images is returned"""
66
        images = [{'id': 1, 'name': 'image-1'},
67
                  {'id': 2, 'name': 'image-2'},
68
                  {'id': 3, 'name': 'image-3'}]
69
        mimage().list.return_value = images
70
        response = self.get('/api/v1.1/images/', 'user')
71
        self.assertSuccess(response)
72
        api_images = json.loads(response.content)['images']['values']
73
        self.assertEqual(images, api_images)
74

  
75
    @assert_backend_closed
76
    def test_list_images_detail(self, mimage):
77
        images = [{'id': 1,
78
                   'name': 'image-1',
79
                   'status':'available',
80
                   'created_at': '2012-11-26 11:52:54',
81
                   'updated_at': '2012-12-26 11:52:54',
82
                   'deleted_at': '',
83
                   'properties': {'foo':'bar'}},
84
                  {'id': 2,
85
                   'name': 'image-2',
86
                   'status': 'deleted',
87
                   'created_at': '2012-11-26 11:52:54',
88
                   'updated_at': '2012-12-26 11:52:54',
89
                   'deleted_at': '2012-12-27 11:52:54',
90
                   'properties': ''},
91
                  {'id': 3,
92
                   'name': 'image-3',
93
                   'status': 'available',
94
                   'created_at': '2012-11-26 11:52:54',
95
                   'deleted_at': '',
96
                   'updated_at': '2012-12-26 11:52:54',
97
                   'properties': ''}]
98
        result_images = [
99
                  {'id': 1,
100
                   'name': 'image-1',
101
                   'status':'ACTIVE',
102
                   'progress': 100,
103
                   'created': '2012-11-26T11:52:54+00:00',
104
                   'updated': '2012-12-26T11:52:54+00:00',
105
                   'metadata': {'values': {'foo':'bar'}}},
106
                  {'id': 2,
107
                   'name': 'image-2',
108
                   'status': 'DELETED',
109
                   'progress': 0,
110
                   'created': '2012-11-26T11:52:54+00:00',
111
                   'updated': '2012-12-26T11:52:54+00:00'},
112
                  {'id': 3,
113
                   'name': 'image-3',
114
                   'status': 'ACTIVE',
115
                   'progress': 100,
116
                   'created': '2012-11-26T11:52:54+00:00',
117
                   'updated': '2012-12-26T11:52:54+00:00'}]
118
        mimage().list.return_value = images
119
        response = self.get('/api/v1.1/images/detail', 'user')
120
        self.assertSuccess(response)
121
        api_images = json.loads(response.content)['images']['values']
122
        self.assertEqual(len(result_images), len(api_images))
123
        self.assertEqual(result_images, api_images)
124

  
125
    @assert_backend_closed
126
    def test_list_images_detail_since(self, mimage):
127
        from datetime import datetime, timedelta
128
        from time import sleep
129
        old_time = datetime.now()
130
        new_time = old_time + timedelta(seconds=0.1)
131
        sleep(0.1)
132
        images = [
133
                  {'id': 1,
134
                   'name': 'image-1',
135
                   'status':'available',
136
                   'progress': 100,
137
                   'created_at': old_time.isoformat(),
138
                   'deleted_at': '',
139
                   'updated_at': old_time.isoformat(),
140
                   'properties': ''},
141
                  {'id': 2,
142
                   'name': 'image-2',
143
                   'status': 'deleted',
144
                   'progress': 0,
145
                   'created_at': new_time.isoformat(),
146
                   'updated_at': new_time.isoformat(),
147
                   'deleted_at': new_time.isoformat(),
148
                   'properties': ''}]
149
        mimage().iter.return_value = images
150
        response =\
151
            self.get('/api/v1.1/images/detail?changes-since=%sUTC' % new_time)
152
        self.assertSuccess(response)
153
        api_images = json.loads(response.content)['images']['values']
154
        self.assertEqual(1, len(api_images))
155

  
156
    @assert_backend_closed
157
    def test_get_image_details(self, mimage):
158
        image = {'id': 42,
159
                 'name': 'image-1',
160
                 'status': 'available',
161
                 'created_at': '2012-11-26 11:52:54',
162
                 'updated_at': '2012-12-26 11:52:54',
163
                 'deleted_at': '',
164
                 'properties': {'foo': 'bar'}}
165
        result_image = \
166
                  {'id': 42,
167
                   'name': 'image-1',
168
                   'status': 'ACTIVE',
169
                   'progress': 100,
170
                   'created': '2012-11-26T11:52:54+00:00',
171
                   'updated': '2012-12-26T11:52:54+00:00',
172
                   'metadata': {'values': {'foo': 'bar'}}}
173
        with patch('synnefo.api.util.get_image') as m:
174
            m.return_value = image
175
            response = self.get('/api/v1.1/images/42', 'user')
176
        self.assertSuccess(response)
177
        api_image = json.loads(response.content)['image']
178
        self.assertEqual(api_image, result_image)
179

  
180
    @assert_backend_closed
181
    def test_invalid_image(self, mimage):
182
        with patch('synnefo.api.util.get_image') as m:
183
            m.side_effect = ItemNotFound('Image not found')
184
            response = self.get('/api/v1.1/images/42', 'user')
185
        self.assertItemNotFound(response)
186

  
187
    def test_delete_image(self, mimage):
188
        response = self.delete("/api/v1.1/images/42", "user")
189
        self.assertEqual(response.status_code, 204)
190
        mimage.return_value.delete.assert_called_once_with('42')
191

  
192

  
193
@patch('synnefo.api.util.ImageBackend')
194
class ImageMetadataAPITest(BaseAPITest):
195
    def setUp(self):
196
        self.image = {'id': 42,
197
                 'name': 'image-1',
198
                 'status': 'available',
199
                 'created_at': '2012-11-26 11:52:54',
200
                 'updated_at': '2012-12-26 11:52:54',
201
                 'deleted_at': '',
202
                 'properties': {'foo': 'bar', 'foo2': 'bar2'}}
203
        self.result_image = \
204
                  {'id': 42,
205
                   'name': 'image-1',
206
                   'status': 'ACTIVE',
207
                   'progress': 100,
208
                   'created': '2012-11-26T11:52:54+00:00',
209
                   'updated': '2012-12-26T11:52:54+00:00',
210
                   'metadata': {'values': {'foo': 'bar'}}}
211

  
212
    @assert_backend_closed
213
    def test_list_metadata(self, backend):
214
        backend.return_value.get_image.return_value = self.image
215
        response = self.get('/api/v1.1/images/42/meta', 'user')
216
        self.assertSuccess(response)
217
        meta = json.loads(response.content)['metadata']['values']
218
        self.assertEqual(meta, self.image['properties'])
219

  
220
    @assert_backend_closed
221
    def test_get_metadata(self, backend):
222
        backend.return_value.get_image.return_value = self.image
223
        response = self.get('/api/v1.1/images/42/meta/foo', 'user')
224
        self.assertSuccess(response)
225
        meta = json.loads(response.content)['meta']
226
        self.assertEqual(meta['foo'], 'bar')
227

  
228
    @assert_backend_closed
229
    def test_get_invalid_metadata(self, backend):
230
        backend.return_value.get_image.return_value = self.image
231
        response = self.get('/api/v1.1/images/42/meta/not_found', 'user')
232
        self.assertItemNotFound(response)
233

  
234
    @assert_backend_closed
235
    def test_delete_metadata_item(self, backend):
236
        backend.return_value.get_image.return_value = self.image
237
        with patch("synnefo.api.images.ImageBackend") as m:
238
            response = self.delete('/api/v1.1/images/42/meta/foo', 'user')
239
            self.assertEqual(response.status_code, 204)
240
            m.return_value.update.assert_called_once_with('42',
241
                                        {'properties': {'foo2': 'bar2'}})
242

  
243
    @assert_backend_closed
244
    def test_create_metadata_item(self, backend):
245
        backend.return_value.get_image.return_value = self.image
246
        with patch("synnefo.api.images.ImageBackend") as m:
247
                request = {'meta': {'foo3': 'bar3'}}
248
                response = self.put('/api/v1.1/images/42/meta/foo3', 'user',
249
                                    json.dumps(request), 'json')
250
                self.assertEqual(response.status_code, 201)
251
                m.return_value.update.assert_called_once_with('42',
252
                        {'properties':
253
                            {'foo': 'bar', 'foo2': 'bar2', 'foo3': 'bar3'}})
254

  
255
    @assert_backend_closed
256
    def test_create_metadata_malformed_1(self, backend):
257
        backend.return_value.get_image.return_value = self.image
258
        with patch("synnefo.api.images.ImageBackend"):
259
                request = {'met': {'foo3': 'bar3'}}
260
                response = self.put('/api/v1.1/images/42/meta/foo3', 'user',
261
                                    json.dumps(request), 'json')
262
                self.assertBadRequest(response)
263

  
264
    @assert_backend_closed
265
    def test_create_metadata_malformed_2(self, backend):
266
        backend.return_value.get_image.return_value = self.image
267
        with patch("synnefo.api.images.ImageBackend"):
268
                request = {'meta': [('foo3', 'bar3')]}
269
                response = self.put('/api/v1.1/images/42/meta/foo3', 'user',
270
                                    json.dumps(request), 'json')
271
                self.assertBadRequest(response)
272

  
273
    @assert_backend_closed
274
    def test_create_metadata_malformed_3(self, backend):
275
        backend.return_value.get_image.return_value = self.image
276
        with patch("synnefo.api.images.ImageBackend"):
277
            request = {'met': {'foo3': 'bar3', 'foo4': 'bar4'}}
278
            response = self.put('/api/v1.1/images/42/meta/foo3', 'user',
279
                                    json.dumps(request), 'json')
280
            self.assertBadRequest(response)
281

  
282
    @assert_backend_closed
283
    def test_create_metadata_malformed_4(self, backend):
284
        backend.return_value.get_image.return_value = self.image
285
        with patch("synnefo.api.images.ImageBackend"):
286
            request = {'met': {'foo3': 'bar3'}}
287
            response = self.put('/api/v1.1/images/42/meta/foo4', 'user',
288
                                    json.dumps(request), 'json')
289
            self.assertBadRequest(response)
290

  
291
    @assert_backend_closed
292
    def test_update_metadata_item(self, backend):
293
        backend.return_value.get_image.return_value = self.image
294
        with patch("synnefo.api.images.ImageBackend") as m:
295
                request = {'metadata': {'foo': 'bar_new', 'foo4': 'bar4'}}
296
                response = self.post('/api/v1.1/images/42/meta', 'user',
297
                                    json.dumps(request), 'json')
298
                self.assertEqual(response.status_code, 201)
299
                m.return_value.update.assert_called_once_with('42',
300
                        {'properties':
301
                            {'foo': 'bar_new', 'foo2': 'bar2', 'foo4': 'bar4'}
302
                        })
303

  
304
    @assert_backend_closed
305
    def test_update_metadata_malformed(self, backend):
306
        backend.return_value.get_image.return_value = self.image
307
        with patch("synnefo.api.images.ImageBackend"):
308
                request = {'meta': {'foo': 'bar_new', 'foo4': 'bar4'}}
309
                response = self.post('/api/v1.1/images/42/meta', 'user',
310
                                    json.dumps(request), 'json')
311
                self.assertBadRequest(response)
b/snf-cyclades-app/synnefo/api/test/networks.py
1
# Copyright 2012 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

  
34
import json
35
from mock import patch
36

  
37
from synnefo.api.tests import BaseAPITest
38
from synnefo.db.models import Network, NetworkInterface
39
from synnefo.db import models_factory as mfactory
40

  
41

  
42
@patch('synnefo.logic.rapi_pool.GanetiRapiClient')
43
class NetworkAPITest(BaseAPITest):
44
    def setUp(self):
45
        self.mac_prefixes = mfactory.MacPrefixPoolTableFactory()
46
        self.bridges = mfactory.BridgePoolTableFactory()
47
        self.user = 'dummy-user'
48
        self.net1 = mfactory.NetworkFactory(userid=self.user)
49
        self.vm1 = mfactory.VirtualMachineFactory(userid=self.user)
50
        self.nic1 = mfactory.NetworkInterfaceFactory(network=self.net1,
51
                                                     machine=self.vm1)
52
        self.nic2 = mfactory.NetworkInterfaceFactory(network=self.net1,
53
                                                     machine=self.vm1)
54
        self.net2 = mfactory.NetworkFactory(userid=self.user)
55
        self.nic3 = mfactory.NetworkInterfaceFactory(network=self.net2)
56

  
57
    def assertNetworksEqual(self, db_net, api_net, detail=False):
58
        self.assertEqual(str(db_net.id), api_net["id"])
59
        self.assertEqual(db_net.name, api_net['name'])
60
        if detail:
61
            self.assertEqual(db_net.state, api_net['status'])
62
            self.assertEqual(db_net.flavor, api_net['type'])
63
            self.assertEqual(db_net.subnet, api_net['cidr'])
64
            self.assertEqual(db_net.subnet6, api_net['cidr6'])
65
            self.assertEqual(db_net.gateway, api_net['gateway'])
66
            self.assertEqual(db_net.gateway6, api_net['gateway6'])
67
            self.assertEqual(db_net.dhcp, api_net['dhcp'])
68
            self.assertEqual(db_net.public, api_net['public'])
69
            db_nics = ["nic-%d-%d" % (nic.machine.id, nic.index) for nic in
70
                       db_net.nics.filter(machine__userid=db_net.userid)]
71
            self.assertEqual(db_nics, api_net['attachments']['values'])
72

  
73
    def test_create_network_1(self, mrapi):
74
        request = {
75
            'network': {'name': 'foo'}
76
            }
77
        response = self.post('/api/v1.1/networks/', 'user1',
78
                             json.dumps(request), 'json')
79
        self.assertEqual(response.status_code, 202)
80
        db_networks = Network.objects.filter(userid='user1')
81
        self.assertEqual(len(db_networks), 1)
82
        db_net = db_networks[0]
83
        api_net = json.loads(response.content)['network']
84
        self.assertNetworksEqual(db_net, api_net)
85
        mrapi.CreateNetwork.assert_called()
86
        mrapi.ConnectNetwork.assert_called()
87

  
88
    def test_invalid_data_1(self, mrapi):
89
        """Test invalid flavor"""
90
        request = {
91
            'network': {'name': 'foo', 'type': 'LoLo'}
92
            }
93
        response = self.post('/api/v1.1/networks/', 'user1',
94
                             json.dumps(request), 'json')
95
        self.assertBadRequest(response)
96
        self.assertEqual(len(Network.objects.filter(userid='user1')), 0)
97

  
98
    def test_invalid_data_2(self, mrapi):
99
        """Test invalid subnet"""
100
        request = {
101
            'network': {'name': 'foo', 'cidr': '10.0.0.0/8'}
102
            }
103
        response = self.post('/api/v1.1/networks/', 'user1',
104
                             json.dumps(request), 'json')
105
        self.assertFault(response, 413, "overLimit")
106

  
107
    def test_invalid_data_3(self, mrapi):
108
        """Test unauthorized to create public network"""
109
        request = {
110
                'network': {'name': 'foo', 'public': True}
111
            }
112
        response = self.post('/api/v1.1/networks/', 'user1',
113
                             json.dumps(request), 'json')
114
        self.assertFault(response, 403, "forbidden")
115

  
116
    def test_invalid_data_4(self, mrapi):
117
        """Test unauthorized to create network not in settings"""
118
        request = {
119
                'network': {'name': 'foo', 'type': 'CUSTOM'}
120
            }
121
        response = self.post('/api/v1.1/networks/', 'user1',
122
                             json.dumps(request), 'json')
123
        self.assertFault(response, 403, "forbidden")
124

  
125
    def test_list_networks(self, mrapi):
126
        """Test that expected list of networks is returned."""
127
        # Create a deleted network
128
        mfactory.NetworkFactory(userid=self.user, deleted=True)
129

  
130
        response = self.get('/api/v1.1/networks/', self.user)
131
        self.assertSuccess(response)
132

  
133
        db_nets = Network.objects.filter(userid=self.user, deleted=False)
134
        api_nets = json.loads(response.content)["networks"]["values"]
135

  
136
        self.assertEqual(len(db_nets), len(api_nets))
137
        for api_net in api_nets:
138
            net_id = api_net['id']
139
            self.assertNetworksEqual(Network.objects.get(id=net_id), api_net)
140

  
141
    def test_list_networks_detail(self, mrapi):
142
        """Test that expected networks details are returned."""
143
        # Create a deleted network
144
        mfactory.NetworkFactory(userid=self.user, deleted=True)
145

  
146
        response = self.get('/api/v1.1/networks/detail', self.user)
147
        self.assertSuccess(response)
148

  
149
        db_nets = Network.objects.filter(userid=self.user, deleted=False)
150
        api_nets = json.loads(response.content)["networks"]["values"]
151

  
152
        self.assertEqual(len(db_nets), len(api_nets))
153
        for api_net in api_nets:
154
            net_id = api_net['id']
155
            self.assertNetworksEqual(Network.objects.get(id=net_id), api_net,
156
                                     detail=True)
157

  
158
    def test_network_details_1(self, mrapi):
159
        """Test that expected details for a network are returned"""
160
        response = self.get('/api/v1.1/networks/%d' % self.net1.id,
161
                            self.net1.userid)
162
        self.assertSuccess(response)
163
        api_net = json.loads(response.content)["network"]
164
        self.assertNetworksEqual(self.net1, api_net, detail=True)
165

  
166
    def test_invalid_network(self, mrapi):
167
        """Test details for non-existing network."""
168
        response = self.get('/api/v1.1/networks/%d' % 42,
169
                            self.net1.userid)
170
        self.assertItemNotFound(response)
171

  
172
    def test_rename_network(self, mrapi):
173
        request = {'network': {'name': "new_name"}}
174
        response = self.put('/api/v1.1/networks/%d' % self.net2.id,
175
                            self.net2.userid, json.dumps(request), 'json')
176
        self.assertEqual(response.status_code, 204)
177
        self.assertEqual(Network.objects.get(id=self.net2.id).name, "new_name")
178
        # Check invalid
179
        request = {'name': "new_name"}
180
        response = self.put('/api/v1.1/networks/%d' % self.net2.id,
181
                            self.net2.userid, json.dumps(request), 'json')
182
        self.assertBadRequest(response)
183

  
184
    def test_rename_deleted_network(self, mrapi):
185
        net = mfactory.NetworkFactory(deleted=True)
186
        request = {'network': {'name': "new_name"}}
187
        response = self.put('/api/v1.1/networks/%d' % net.id,
188
                            net.userid, json.dumps(request), 'json')
189
        self.assertBadRequest(response)
190

  
191
    def test_rename_public_network(self, mrapi):
192
        net = mfactory.NetworkFactory(public=True)
193
        request = {'network': {'name': "new_name"}}
194
        response = self.put('/api/v1.1/networks/%d' % net.id,
195
                            self.net2.userid, json.dumps(request), 'json')
196
        self.assertFault(response, 403, 'forbidden')
197

  
198
    def test_delete_network(self, mrapi):
199
        net = mfactory.NetworkFactory()
200
        response = self.delete('/api/v1.1/networks/%d' % net.id,
201
                                net.userid)
202
        self.assertEqual(response.status_code, 204)
203
        net = Network.objects.get(id=net.id, userid=net.userid)
204
        self.assertEqual(net.action, 'DESTROY')
205
        mrapi.DeleteNetwork.assert_called()
206

  
207
    def test_delete_public_network(self, mrapi):
208
        net = mfactory.NetworkFactory(public=True)
209
        response = self.delete('/api/v1.1/networks/%d' % net.id,
210
                                self.net2.userid)
211
        self.assertFault(response, 403, 'forbidden')
212
        self.assertFalse(mrapi.called)
213

  
214
    def test_delete_deleted_network(self, mrapi):
215
        net = mfactory.NetworkFactory(deleted=True)
216
        response = self.delete('/api/v1.1/networks/%d' % net.id,
217
                                net.userid)
218
        self.assertBadRequest(response)
219

  
220
    def test_delete_network_in_use(self, mrapi):
221
        net = self.net1
222
        response = self.delete('/api/v1.1/networks/%d' % net.id,
223
                                net.userid)
224
        self.assertFault(response, 421, 'networkInUse')
225
        self.assertFalse(mrapi.called)
226

  
227
    def test_add_nic(self, mrapi):
228
        user = 'userr'
229
        vm = mfactory.VirtualMachineFactory(name='yo', userid=user)
230
        net = mfactory.NetworkFactory(state='ACTIVE', userid=user)
231
        request = {'add': {'serverRef': vm.id}}
232
        response = self.post('/api/v1.1/networks/%d/action' % net.id,
233
                             net.userid, json.dumps(request), 'json')
234
        self.assertEqual(response.status_code, 202)
235

  
236
    def test_add_nic_to_deleted_network(self, mrapi):
237
        user = 'userr'
238
        vm = mfactory.VirtualMachineFactory(name='yo', userid=user)
239
        net = mfactory.NetworkFactory(state='ACTIVE', userid=user,
240
                                      deleted=True)
241
        request = {'add': {'serverRef': vm.id}}
242
        response = self.post('/api/v1.1/networks/%d/action' % net.id,
243
                             net.userid, json.dumps(request), 'json')
244
        self.assertBadRequest(response)
245
        self.assertFalse(mrapi.called)
246

  
247
    def test_add_nic_to_public_network(self, mrapi):
248
        user = 'userr'
249
        vm = mfactory.VirtualMachineFactory(name='yo', userid=user)
250
        net = mfactory.NetworkFactory(state='ACTIVE', userid=user, public=True)
251
        request = {'add': {'serverRef': vm.id}}
252
        response = self.post('/api/v1.1/networks/%d/action' % net.id,
253
                             net.userid, json.dumps(request), 'json')
254
        self.assertFault(response, 403, 'forbidden')
255
        self.assertFalse(mrapi.called)
256

  
257
    def test_add_nic_malformed_1(self, mrapi):
258
        user = 'userr'
259
        vm = mfactory.VirtualMachineFactory(name='yo', userid=user)
260
        net = mfactory.NetworkFactory(state='ACTIVE', userid=user)
261
        request = {'add': {'serveRef': vm.id}}
262
        response = self.post('/api/v1.1/networks/%d/action' % net.id,
263
                             net.userid, json.dumps(request), 'json')
264
        self.assertBadRequest(response)
265
        self.assertFalse(mrapi.called)
266

  
267
    def test_add_nic_malformed_2(self, mrapi):
268
        user = 'userr'
269
        vm = mfactory.VirtualMachineFactory(name='yo', userid=user)
270
        net = mfactory.NetworkFactory(state='ACTIVE', userid=user)
271
        request = {'add': {'serveRef': [vm.id, 22]}}
272
        response = self.post('/api/v1.1/networks/%d/action' % net.id,
273
                             net.userid, json.dumps(request), 'json')
274
        self.assertBadRequest(response)
275
        self.assertFalse(mrapi.called)
276

  
277
    def test_add_nic_not_active(self, mrapi):
278
        """Test connecting VM to non-active network"""
279
        user = 'dummy'
280
        vm = mfactory.VirtualMachineFactory(name='yo', userid=user)
281
        net = mfactory.NetworkFactory(state='PENDING', subnet='10.0.0.0/31',
282
                                      userid=user)
283
        request = {'add': {'serveRef': vm.id}}
284
        response = self.post('/api/v1.1/networks/%d/action' % net.id,
285
                             net.userid, json.dumps(request), 'json')
286
        # Test that returns BuildInProgress
287
        self.assertEqual(response.status_code, 409)
288
        self.assertFalse(mrapi.called)
289

  
290
    def test_add_nic_full_network(self, mrapi):
291
        """Test connecting VM to a full network"""
292
        user = 'userr'
293
        vm = mfactory.VirtualMachineFactory(name='yo', userid=user)
294
        net = mfactory.NetworkFactory(state='ACTIVE', subnet='10.0.0.0/30',
295
                                      userid=user, dhcp=True)
296
        pool = net.get_pool()
297
        while not pool.empty():
298
            pool.get()
299
        pool.save()
300
        pool = net.get_pool()
301
        self.assertTrue(pool.empty())
302
        request = {'add': {'serverRef': vm.id}}
303
        response = self.post('/api/v1.1/networks/%d/action' % net.id,
304
                             net.userid, json.dumps(request), 'json')
305
        # Test that returns OverLimit
306
        self.assertEqual(response.status_code, 413)
307
        self.assertFalse(mrapi.called)
308

  
309
    def test_remove_nic(self, mrapi):
310
        user = 'userr'
311
        vm = mfactory.VirtualMachineFactory(name='yo', userid=user)
312
        net = mfactory.NetworkFactory(state='ACTIVE', userid=user)
313
        nic = mfactory.NetworkInterfaceFactory(machine=vm, network=net)
314
        request = {'remove': {'attachment': 'nic-%s-%s' % (vm.id, nic.index)}}
315
        response = self.post('/api/v1.1/networks/%d/action' % net.id,
316
                             net.userid, json.dumps(request), 'json')
317
        self.assertEqual(response.status_code, 202)
318
        self.assertTrue(NetworkInterface.objects.get(id=nic.id).dirty)
319
        # Remove dirty nic
320
        response = self.post('/api/v1.1/networks/%d/action' % net.id,
321
                             net.userid, json.dumps(request), 'json')
322
        self.assertFault(response, 409, 'buildInProgress')
323

  
324
    def test_remove_nic_malformed(self, mrapi):
325
        user = 'userr'
326
        vm = mfactory.VirtualMachineFactory(name='yo', userid=user)
327
        net = mfactory.NetworkFactory(state='ACTIVE', userid=user)
328
        nic = mfactory.NetworkInterfaceFactory(machine=vm, network=net)
329
        request = {'remove':
330
                    {'att234achment': 'nic-%s-%s' % (vm.id, nic.index)}
331
                  }
332
        response = self.post('/api/v1.1/networks/%d/action' % net.id,
333
                             net.userid, json.dumps(request), 'json')
334
        self.assertBadRequest(response)
335

  
336
    def test_remove_nic_malformed_2(self, mrapi):
337
        user = 'userr'
338
        vm = mfactory.VirtualMachineFactory(name='yo', userid=user)
339
        net = mfactory.NetworkFactory(state='ACTIVE', userid=user)
340
        request = {'remove':
341
                    {'attachment': 'nic-%s' % vm.id}
342
                  }
343
        response = self.post('/api/v1.1/networks/%d/action' % net.id,
344
                             net.userid, json.dumps(request), 'json')
345
        self.assertBadRequest(response)
b/snf-cyclades-app/synnefo/api/test/servers.py
1
# Copyright 2012 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

  
34
import json
35

  
36
from synnefo.api.tests import  BaseAPITest
37
from synnefo.db.models import VirtualMachine, VirtualMachineMetadata
38
from synnefo.db import models_factory as mfactory
39
from synnefo.logic.utils import get_rsapi_state
40

  
41
from mock import patch
42

  
43

  
44
class ServerAPITest(BaseAPITest):
45
    def setUp(self):
46
        self.user1 = 'user1'
47
        self.user2 = 'user2'
48
        self.vm1 = mfactory.VirtualMachineFactory(userid=self.user1)
49
        self.vm2 = mfactory.VirtualMachineFactory(userid=self.user2)
50
        self.vm3 = mfactory.VirtualMachineFactory(deleted=True,
51
                                                  userid=self.user1)
52
        self.vm4 = mfactory.VirtualMachineFactory(userid=self.user2)
53

  
54
    def test_server_list_1(self):
55
        """Test if the expected list of servers is returned."""
56
        response = self.get('/api/v1.1/servers')
57
        self.assertSuccess(response)
58
        servers = json.loads(response.content)['servers']['values']
59
        self.assertEqual(servers, [])
60

  
61
    def test_server_list_2(self):
62
        """Test if the expected list of servers is returned."""
63
        response = self.get('/api/v1.1/servers', self.user1)
64
        self.assertSuccess(response)
65
        servers = json.loads(response.content)['servers']['values']
66
        db_server = self.vm1
67
        self.assertEqual(servers, [{'name': db_server.name,
68
                                    'id': db_server.id}])
69

  
70
    def test_server_list_detail(self):
71
        """Test if the servers list details are returned."""
72
        user = self.user2
73
        user_vms = {self.vm2.id: self.vm2,
74
                    self.vm4.id: self.vm4}
75

  
76
        response = self.get('/api/v1.1/servers/detail', user)
77
        servers = json.loads(response.content)['servers']['values']
78
        self.assertEqual(len(servers), len(user_vms))
79
        for api_vm in servers:
80
            db_vm = user_vms[api_vm['id']]
81
            self.assertEqual(api_vm['flavorRef'], db_vm.flavor.id)
82
            self.assertEqual(api_vm['hostId'], db_vm.hostid)
83
            self.assertEqual(api_vm['id'], db_vm.id)
84
            self.assertEqual(api_vm['imageRef'], db_vm.imageid)
85
            self.assertEqual(api_vm['name'], db_vm.name)
86
            self.assertEqual(api_vm['status'], get_rsapi_state(db_vm))
87
            self.assertSuccess(response)
88

  
89
    def test_server_detail(self):
90
        """Test if a server details are returned."""
91
        db_vm = self.vm2
92
        user = self.vm2.userid
93
        net = mfactory.NetworkFactory()
94
        nic = mfactory.NetworkInterfaceFactory(machine=self.vm2, network=net)
95

  
96
        db_vm_meta = mfactory.VirtualMachineMetadataFactory(vm=db_vm)
97

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

  
101
        self.assertEqual(server['flavorRef'], db_vm.flavor.id)
102
        self.assertEqual(server['hostId'], db_vm.hostid)
103
        self.assertEqual(server['id'], db_vm.id)
104
        self.assertEqual(server['imageRef'], db_vm.imageid)
105
        self.assertEqual(server['name'], db_vm.name)
106
        self.assertEqual(server['status'], get_rsapi_state(db_vm))
107
        api_nic = server['attachments']['values'][0]
108
        self.assertEqual(api_nic['network_id'], str(net.id))
109
        self.assertEqual(api_nic['mac_address'], nic.mac)
110
        self.assertEqual(api_nic['firewallProfile'], nic.firewall_profile)
111
        self.assertEqual(api_nic['ipv4'], nic.ipv4)
112
        self.assertEqual(api_nic['ipv6'], nic.ipv6)
113
        self.assertEqual(api_nic['id'], 'nic-%s-%s' % (db_vm.id, nic.index))
114

  
115
        metadata = server['metadata']['values']
116
        self.assertEqual(len(metadata), 1)
117
        self.assertEqual(metadata[db_vm_meta.meta_key], db_vm_meta.meta_value)
118
        self.assertSuccess(response)
119

  
120
    def test_noauthorized(self):
121
        """Test 404 for detail of other user vm"""
122
        db_vm = self.vm2
123

  
124
        response = self.get('/api/v1.1/servers/%d' % db_vm.id, 'wrong_user')
125
        self.assertItemNotFound(response)
126

  
127
    def test_wrong_server(self):
128
        """Test 404 response if server does not exist."""
129
        response = self.get('/api/v1.1/servers/%d' % 5000)
130
        self.assertItemNotFound(response)
131

  
132
    def test_create_server_empty(self):
133
        """Test if the create server call returns a 400 badRequest if
134
           no attributes are specified."""
135

  
136
        response = self.post('/api/v1.1/servers', params={})
137
        self.assertBadRequest(response)
138

  
139
    def test_rename_server(self):
140
        vm = self.vm2
141
        request = {'server': {'name': 'new_name'}}
142
        response = self.put('/api/v1.1/servers/%d' % vm.id, vm.userid,
143
                            json.dumps(request), 'json')
144
        self.assertSuccess(response)
145
        self.assertEqual(VirtualMachine.objects.get(id=vm.id).name, "new_name")
146

  
147

  
148
@patch('synnefo.api.util.get_image')
149
@patch('synnefo.logic.rapi_pool.GanetiRapiClient')
150
class ServerCreateAPITest(BaseAPITest):
151
    def test_create_server(self, mrapi, mimage):
152
        """Test if the create server call returns the expected response
153
           if a valid request has been speficied."""
154
        mimage.return_value = {'location': 'pithos://foo',
155
                               'disk_format': 'diskdump'}
156
        mrapi().CreateInstance.return_value = 12
157
        flavor = mfactory.FlavorFactory()
158
        # Create public network and backend
159
        network = mfactory.NetworkFactory(public=True)
160
        backend = mfactory.BackendFactory()
161
        mfactory.BackendNetworkFactory(network=network, backend=backend)
162

  
163
        request = {
164
                    "server": {
165
                        "name": "new-server-test",
166
                        "userid": "test_user",
167
                        "imageRef": 1,
168
                        "flavorRef": flavor.id,
169
                        "metadata": {
170
                            "My Server Name": "Apache1"
171
                        },
172
                        "personality": []
173
                    }
174
        }
175
        response = self.post('/api/v1.1/servers', 'test_user',
176
                                 json.dumps(request), 'json')
177
        self.assertEqual(response.status_code, 202)
178
        mrapi().CreateInstance.assert_called_once()
179

  
180
        api_server = json.loads(response.content)['server']
181
        self.assertEqual(api_server['status'], "BUILD")
182
        self.assertEqual(api_server['progress'], 0)
183
        self.assertEqual(api_server['metadata']['values'],
184
                        {"My Server Name":  "Apache1"})
185
        self.assertTrue('adminPass' in api_server)
186

  
187
        db_vm = VirtualMachine.objects.get(userid='test_user')
188
        self.assertEqual(api_server['name'], db_vm.name)
189
        self.assertEqual(api_server['status'], db_vm.operstate)
190

  
191
    def test_create_server_no_flavor(self, mrapi, mimage):
192
        request = {
193
                    "server": {
194
                        "name": "new-server-test",
195
                        "userid": "test_user",
196
                        "imageRef": 1,
197
                        "flavorRef": 42,
198
                        "metadata": {
199
                            "My Server Name": "Apache1"
200
                        },
201
                        "personality": []
202
                    }
203
        }
204
        response = self.post('/api/v1.1/servers', 'test_user',
205
                                 json.dumps(request), 'json')
206
        self.assertItemNotFound(response)
207

  
208

  
209
@patch('synnefo.logic.rapi_pool.GanetiRapiClient')
210
class ServerDestroyAPITest(BaseAPITest):
211
    def test_delete_server(self, mrapi):
212
        vm = mfactory.VirtualMachineFactory()
213
        response = self.delete('/api/v1.1/servers/%d' % vm.id, vm.userid)
214
        self.assertEqual(response.status_code, 204)
215
        mrapi().DeleteInstance.assert_called_once()
216

  
217
    def test_non_existing_delete_server(self, mrapi):
218
        vm = mfactory.VirtualMachineFactory()
219
        response = self.delete('/api/v1.1/servers/%d' % 42, vm.userid)
220
        self.assertItemNotFound(response)
221
        self.assertFalse(mrapi.mock_calls)
222

  
223

  
224
class ServerMetadataAPITest(BaseAPITest):
225
    def setUp(self):
226
        self.vm = mfactory.VirtualMachineFactory()
227
        self.metadata = mfactory.VirtualMachineMetadataFactory(vm=self.vm)
228

  
229
    def test_get_metadata(self):
230
        vm = self.vm
231
        create_meta = lambda: mfactory.VirtualMachineMetadataFactory(vm=vm)
232
        metadata = [create_meta(), create_meta(), create_meta()]
233
        response = self.get('/api/v1.1/servers/%d/meta' % vm.id, vm.userid)
234
        self.assertTrue(response.status_code in [200, 203])
235
        api_metadata = json.loads(response.content)['metadata']['values']
236
        self.assertEqual(len(api_metadata), len(metadata) + 1)
237
        for db_m in metadata:
238
            self.assertEqual(api_metadata[db_m.meta_key], db_m.meta_value)
239

  
240
        request = {'metadata':
241
                        {'foo': 'bar'},
242
                        metadata[0].meta_key: 'bar2'
243
                  }
244
        response = self.post('/api/v1.1/servers/%d/meta' % vm.id, vm.userid,
245
                             json.dumps(request), 'json')
246
        metadata2 = VirtualMachineMetadata.objects.filter(vm=vm)
247
        response = self.get('/api/v1.1/servers/%d/meta' % vm.id, vm.userid)
248
        self.assertTrue(response.status_code in [200, 203])
249
        api_metadata2 = json.loads(response.content)['metadata']['values']
250
        self.assertTrue('foo' in api_metadata2.keys())
251
        self.assertTrue(api_metadata2[metadata[0].meta_key], 'bar2')
252
        self.assertEqual(len(api_metadata2), len(metadata2))
253
        for db_m in metadata2:
254
            self.assertEqual(api_metadata2[db_m.meta_key], db_m.meta_value)
255

  
256
        # Create new meta
257
        request = {'meta': {'foo2': 'bar2'}}
258
        response = self.put('/api/v1.1/servers/%d/meta/foo2' % vm.id,
259
                            vm.userid, json.dumps(request), 'json')
260

  
261
        # Get the new meta
262
        response = self.get('/api/v1.1/servers/%d/meta/foo2' % vm.id,
263
                            vm.userid)
264
        meta = json.loads(response.content)['meta']
265
        self.assertEqual(meta['foo2'], 'bar2')
266

  
267
        # Delete the new meta
268
        response = self.delete('/api/v1.1/servers/%d/meta/foo2' % vm.id,
269
                               vm.userid)
270
        self.assertEqual(response.status_code, 204)
271

  
272
        # Try to get the deleted meta: should raise 404
273
        response = self.get('/api/v1.1/servers/%d/meta/foo2' % vm.id,
274
                            vm.userid)
275
        self.assertEqual(response.status_code, 404)
276

  
277
    def test_invalid_metadata(self):
278
        vm = self.vm
279
        response = self.post('/api/v1.1/servers/%d/meta' % vm.id, vm.userid)
280
        self.assertBadRequest(response)
281
        self.assertEqual(len(vm.metadata.all()), 1)
282

  
283
    def test_invalid_metadata_server(self):
284
        response = self.post('/api/v1.1/servers/42/meta', 'user')
285
        self.assertItemNotFound(response)
286

  
287
    def test_get_meta_invalid_key(self):
288
        vm = self.vm
289
        response = self.get('/api/v1.1/servers/%d/meta/foo2' % vm.id,
290
                            vm.userid)
291
        self.assertItemNotFound(response)
292

  
293

  
294
@patch('synnefo.api.util.get_image')
295
@patch('synnefo.logic.rapi_pool.GanetiRapiClient')
296
class ServerActionAPITest(BaseAPITest):
297
    def test_actions(self, mrapi, mimage):
298
        actions = ['start', 'shutdown', 'reboot']
299
        vm = mfactory.VirtualMachineFactory()
300
        vm.operstate = "STOPPED"
301
        vm.save()
302
        for action in actions:
303
            val = {'type': 'HARD'} if action == 'reboot' else {}
304
            request = {action: val}
305
            response = self.post('/api/v1.1/servers/%d/action' % vm.id,
306
                                vm.userid, json.dumps(request), 'json')
307
            self.assertEqual(response.status_code, 202)
308
            if action == 'shutdown':
309
                self.assertEqual(VirtualMachine.objects.get(id=vm.id).action,
310
                                 "STOP")
311
            else:
312
                self.assertEqual(VirtualMachine.objects.get(id=vm.id).action,
313
                                 action.upper())
314

  
315
    def test_action_in_building_vm(self, mrapi, mimage):
316
        """Test building in progress"""
317
        vm = mfactory.VirtualMachineFactory()
318
        request = {'start': '{}'}
319
        response = self.post('/api/v1.1/servers/%d/action' % vm.id,
320
                             vm.userid, json.dumps(request), 'json')
321
        self.assertEqual(response.status_code, 409)
322
        self.assertFalse(mrapi.mock_calls)
323

  
324
    def test_destroy_build_vm(self, mrapi, mimage):
325
        """Test building in progress"""
326
        vm = mfactory.VirtualMachineFactory()
327
        response = self.delete('/api/v1.1/servers/%d' % vm.id,
328
                             vm.userid)
329
        self.assertSuccess(response)
330
        mrapi().RemoveInstance.assert_called_once()
331

  
332
    def test_firewall(self, mrapi, mimage):
333
        vm = mfactory.VirtualMachineFactory()
334
        vm.operstate = "STOPPED"
335
        vm.save()
336
        request = {'firewallProfile': {'profile': 'PROTECTED'}}
337
        response = self.post('/api/v1.1/servers/%d/action' % vm.id,
338
                             vm.userid, json.dumps(request), 'json')
339
        self.assertEqual(response.status_code, 202)
340
        mrapi().ModifyInstance.assert_called_once()
341

  
342
    def test_unsupported_firewall(self, mrapi, mimage):
343
        vm = mfactory.VirtualMachineFactory()
344
        vm.operstate = "STOPPED"
345
        vm.save()
346
        request = {'firewallProfile': {'profile': 'FOO'}}
347
        response = self.post('/api/v1.1/servers/%d/action' % vm.id,
348
                             vm.userid, json.dumps(request), 'json')
349
        self.assertBadRequest(response)
350
        self.assertFalse(mrapi.mock_calls)
351

  
352

  
353
class ServerVNCConsole(BaseAPITest):
354
    def test_not_active_server(self):
355
        """Test console req for not ACTIVE server returns badRequest"""
356
        vm = mfactory.VirtualMachineFactory()
357
        data = json.dumps({'console': {'type': 'vnc'}})
358
        response = self.post('/api/v1.1/servers/%d/action' % vm.id,
359
                             vm.userid, data, 'json')
360
        self.assertBadRequest(response)
361

  
362
    def test_active_server(self):
363
        """Test console req for ACTIVE server"""
364
        vm = mfactory.VirtualMachineFactory()
365
        vm.operstate = 'STARTED'
366
        vm.save()
367

  
368
        data = json.dumps({'console': {'type': 'vnc'}})
369
        response = self.post('/api/v1.1/servers/%d/action' % vm.id,
370
                             vm.userid, data, 'json')
371
        self.assertEqual(response.status_code, 200)
372
        reply = json.loads(response.content)
373
        self.assertEqual(reply.keys(), ['console'])
374
        console = reply['console']
375
        self.assertEqual(console['type'], 'vnc')
376
        self.assertEqual(set(console.keys()),
377
                         set(['type', 'host', 'port', 'password']))
378

  
379
    def test_wrong_console_type(self):
380
        """Test console req for ACTIVE server"""
381
        vm = mfactory.VirtualMachineFactory()
382
        vm.operstate = 'STARTED'
383
        vm.save()
384

  
385
        data = json.dumps({'console': {'type': 'foo'}})
386
        response = self.post('/api/v1.1/servers/%d/action' % vm.id,
387
                             vm.userid, data, 'json')
388
        self.assertBadRequest(response)
b/snf-cyclades-app/synnefo/api/tests.py
36 36
from django.utils import simplejson as json
37 37
from django.test import TestCase
38 38

  
39
from mock import patch, Mock
39
from mock import patch
40 40
from contextlib import contextmanager
41 41
from functools import wraps
42 42

  
43 43
from synnefo.db.models import *
44 44
from synnefo.db import models_factory as mfactory
45
from synnefo.logic.utils import get_rsapi_state
46 45

  
47 46
from synnefo.api import faults
48 47

  
49 48

  
49

  
50 50
@contextmanager
51 51
def astakos_user(user):
52 52
    """
......
108 108
        self.assertFault(response, 404, 'itemNotFound')
109 109

  
110 110

  
111
class FlavorAPITest(BaseAPITest):
112

  
113
    def setUp(self):
114
        self.flavor1 = mfactory.FlavorFactory()
115
        self.flavor2 = mfactory.FlavorFactory(deleted=True)
116
        self.flavor3 = mfactory.FlavorFactory()
117

  
118
    def test_flavor_list(self):
119
        """Test if the expected list of flavors is returned by."""
120
        response = self.get('/api/v1.1/flavors')
121
        self.assertSuccess(response)
122

  
123
        flavors_from_api = json.loads(response.content)['flavors']['values']
124
        flavors_from_db = Flavor.objects.filter(deleted=False)
125
        self.assertEqual(len(flavors_from_api), len(flavors_from_db))
126
        for flavor_from_api in flavors_from_api:
127
            flavor_from_db = Flavor.objects.get(id=flavor_from_api['id'])
128
            self.assertEqual(flavor_from_api['id'], flavor_from_db.id)
129
            self.assertEqual(flavor_from_api['name'], flavor_from_db.name)
130

  
131
    def test_flavors_details(self):
132
        """Test if the flavors details are returned."""
133
        response = self.get('/api/v1.1/flavors/detail')
134
        self.assertSuccess(response)
135

  
136
        flavors_from_db = Flavor.objects.filter(deleted=False)
137
        flavors_from_api = json.loads(response.content)['flavors']['values']
138

  
139
        # Assert that all flavors in the db appear inthe API call result
140
        for i in range(0, len(flavors_from_db)):
141
            flavor_from_api = flavors_from_api[i]
142
            flavor_from_db = Flavor.objects.get(id=flavors_from_db[i].id)
143
            self.assertEqual(flavor_from_api['cpu'], flavor_from_db.cpu)
144
            self.assertEqual(flavor_from_api['id'], flavor_from_db.id)
145
            self.assertEqual(flavor_from_api['disk'], flavor_from_db.disk)
146
            self.assertEqual(flavor_from_api['name'], flavor_from_db.name)
147
            self.assertEqual(flavor_from_api['ram'], flavor_from_db.ram)
148

  
149
        # Assert that all flavors returned by the API also exist in the db
150
        for flavor_from_api in flavors_from_api:
151
            flavor_from_db = Flavor.objects.get(id=flavor_from_api['id'])
152
            self.assertEqual(flavor_from_api['cpu'], flavor_from_db.cpu)
153
            self.assertEqual(flavor_from_api['id'], flavor_from_db.id)
154
            self.assertEqual(flavor_from_api['disk'], flavor_from_db.disk)
155
            self.assertEqual(flavor_from_api['name'], flavor_from_db.name)
156
            self.assertEqual(flavor_from_api['ram'], flavor_from_db.ram)
157

  
158
    def test_flavor_details(self):
159
        """Test if the expected flavor is returned."""
160
        flavor = self.flavor3
161

  
162
        response = self.get('/api/v1.1/flavors/%d' % flavor.id)
163
        self.assertSuccess(response)
164

  
165
        flavor_from_api = json.loads(response.content)['flavor']
166
        flavor_from_db = Flavor.objects.get(id=flavor.id)
167
        self.assertEqual(flavor_from_api['cpu'], flavor_from_db.cpu)
168
        self.assertEqual(flavor_from_api['id'], flavor_from_db.id)
169
        self.assertEqual(flavor_from_api['disk'], flavor_from_db.disk)
170
        self.assertEqual(flavor_from_api['name'], flavor_from_db.name)
171
        self.assertEqual(flavor_from_api['ram'], flavor_from_db.ram)
172

  
173
    def test_deleted_flavor_details(self):
174
        """Test that API returns details for deleted flavors"""
175
        flavor = self.flavor2
176
        response = self.get('/api/v1.1/flavors/%d' % flavor.id)
177
        self.assertSuccess(response)
178
        flavor_from_api = json.loads(response.content)['flavor']
179
        self.assertEquals(flavor_from_api['name'], flavor.name)
180

  
181
    def test_deleted_flavors_list(self):
182
        """Test that deleted flavors do not appear to flavors list"""
183
        response = self.get('/api/v1.1/flavors')
184
        self.assertSuccess(response)
185
        flavors_from_api = json.loads(response.content)['flavors']['values']
186
        self.assertEqual(len(flavors_from_api), 2)
187

  
188
    def test_deleted_flavors_details(self):
189
        """Test that deleted flavors do not appear to flavors detail list"""
190
        mfactory.FlavorFactory(deleted=True)
191
        response = self.get('/api/v1.1/flavors/detail')
192
        self.assertSuccess(response)
193
        flavors_from_api = json.loads(response.content)['flavors']['values']
194
        self.assertEqual(len(flavors_from_api), 2)
195

  
196
    def test_wrong_flavor(self):
197
        """Test 404 result when requesting a flavor that does not exist."""
198

  
199
        response = self.get('/api/v1.1/flavors/%d' % 22)
200
        self.assertItemNotFound(response)
201

  
202

  
203
class ServerAPITest(BaseAPITest):
204
    def setUp(self):
205
        self.user1 = 'user1'
206
        self.user2 = 'user2'
207
        self.vm1 = mfactory.VirtualMachineFactory(userid=self.user1)
208
        self.vm2 = mfactory.VirtualMachineFactory(userid=self.user2)
209
        self.vm3 = mfactory.VirtualMachineFactory(deleted=True,
210
                                                  userid=self.user1)
211
        self.vm4 = mfactory.VirtualMachineFactory(userid=self.user2)
212

  
213
    def test_server_list_1(self):
214
        """Test if the expected list of servers is returned."""
215
        response = self.get('/api/v1.1/servers')
216
        self.assertSuccess(response)
217
        servers = json.loads(response.content)['servers']['values']
218
        self.assertEqual(servers, [])
219

  
220
    def test_server_list_2(self):
221
        """Test if the expected list of servers is returned."""
222
        response = self.get('/api/v1.1/servers', self.user1)
223
        self.assertSuccess(response)
224
        servers = json.loads(response.content)['servers']['values']
225
        db_server = self.vm1
226
        self.assertEqual(servers, [{'name': db_server.name,
227
                                    'id': db_server.id}])
228

  
229
    def test_server_list_detail(self):
230
        """Test if the servers list details are returned."""
231
        user = self.user2
232
        user_vms = {self.vm2.id: self.vm2,
233
                    self.vm4.id: self.vm4}
234

  
235
        response = self.get('/api/v1.1/servers/detail', user)
236
        servers = json.loads(response.content)['servers']['values']
237
        self.assertEqual(len(servers), len(user_vms))
238
        for api_vm in servers:
239
            db_vm = user_vms[api_vm['id']]
240
            self.assertEqual(api_vm['flavorRef'], db_vm.flavor.id)
241
            self.assertEqual(api_vm['hostId'], db_vm.hostid)
242
            self.assertEqual(api_vm['id'], db_vm.id)
243
            self.assertEqual(api_vm['imageRef'], db_vm.imageid)
244
            self.assertEqual(api_vm['name'], db_vm.name)
245
            self.assertEqual(api_vm['status'], get_rsapi_state(db_vm))
246
            self.assertSuccess(response)
247

  
248
    def test_server_detail(self):
249
        """Test if a server details are returned."""
250
        db_vm = self.vm2
251
        user = self.vm2.userid
252
        net = mfactory.NetworkFactory()
253
        nic = mfactory.NetworkInterfaceFactory(machine=self.vm2, network=net)
254

  
255
        db_vm_meta = mfactory.VirtualMachineMetadataFactory(vm=db_vm)
256

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

  
260
        self.assertEqual(server['flavorRef'], db_vm.flavor.id)
261
        self.assertEqual(server['hostId'], db_vm.hostid)
262
        self.assertEqual(server['id'], db_vm.id)
263
        self.assertEqual(server['imageRef'], db_vm.imageid)
264
        self.assertEqual(server['name'], db_vm.name)
265
        self.assertEqual(server['status'], get_rsapi_state(db_vm))
266
        api_nic = server['attachments']['values'][0]
267
        self.assertEqual(api_nic['network_id'], str(net.id))
268
        self.assertEqual(api_nic['mac_address'], nic.mac)
269
        self.assertEqual(api_nic['firewallProfile'], nic.firewall_profile)
270
        self.assertEqual(api_nic['ipv4'], nic.ipv4)
271
        self.assertEqual(api_nic['ipv6'], nic.ipv6)
272
        self.assertEqual(api_nic['id'], 'nic-%s-%s' % (db_vm.id, nic.index))
273

  
274
        metadata = server['metadata']['values']
275
        self.assertEqual(len(metadata), 1)
276
        self.assertEqual(metadata[db_vm_meta.meta_key], db_vm_meta.meta_value)
277
        self.assertSuccess(response)
278

  
279
    def test_noauthorized(self):
280
        """Test 404 for detail of other user vm"""
281
        db_vm = self.vm2
282

  
283
        response = self.get('/api/v1.1/servers/%d' % db_vm.id, 'wrong_user')
284
        self.assertItemNotFound(response)
285

  
286
    def test_wrong_server(self):
287
        """Test 404 response if server does not exist."""
288
        response = self.get('/api/v1.1/servers/%d' % 5000)
289
        self.assertItemNotFound(response)
290

  
291
    def test_create_server_empty(self):
292
        """Test if the create server call returns a 400 badRequest if
293
           no attributes are specified."""
294

  
295
        response = self.post('/api/v1.1/servers', params={})
296
        self.assertBadRequest(response)
297

  
298
    def test_rename_server(self):
299
        vm = self.vm2
300
        request = {'server': {'name': 'new_name'}}
301
        response = self.put('/api/v1.1/servers/%d' % vm.id, vm.userid,
302
                            json.dumps(request), 'json')
303
        self.assertSuccess(response)
304
        self.assertEqual(VirtualMachine.objects.get(id=vm.id).name, "new_name")
305

  
306

  
307
@patch('synnefo.api.util.get_image')
308
@patch('synnefo.logic.rapi_pool.GanetiRapiClient')
309
class ServerCreateAPITest(BaseAPITest):
310
    def test_create_server(self, mrapi, mimage):
311
        """Test if the create server call returns the expected response
312
           if a valid request has been speficied."""
313
        mimage.return_value = {'location': 'pithos://foo',
314
                               'disk_format': 'diskdump'}
315
        mrapi().CreateInstance.return_value = 12
316
        flavor = mfactory.FlavorFactory()
317
        # Create public network and backend
318
        network = mfactory.NetworkFactory(public=True)
319
        backend = mfactory.BackendFactory()
320
        mfactory.BackendNetworkFactory(network=network, backend=backend)
321

  
322
        request = {
323
                    "server": {
324
                        "name": "new-server-test",
325
                        "userid": "test_user",
326
                        "imageRef": 1,
327
                        "flavorRef": flavor.id,
328
                        "metadata": {
329
                            "My Server Name": "Apache1"
330
                        },
331
                        "personality": []
332
                    }
333
        }
334
        response = self.post('/api/v1.1/servers', 'test_user',
335
                                 json.dumps(request), 'json')
336
        self.assertEqual(response.status_code, 202)
337
        mrapi().CreateInstance.assert_called_once()
338

  
339
        api_server = json.loads(response.content)['server']
340
        self.assertEqual(api_server['status'], "BUILD")
341
        self.assertEqual(api_server['progress'], 0)
342
        self.assertEqual(api_server['metadata']['values'],
343
                        {"My Server Name":  "Apache1"})
344
        self.assertTrue('adminPass' in api_server)
345

  
346
        db_vm = VirtualMachine.objects.get(userid='test_user')
347
        self.assertEqual(api_server['name'], db_vm.name)
348
        self.assertEqual(api_server['status'], db_vm.operstate)
349

  
350
    def test_create_server_no_flavor(self, mrapi, mimage):
351
        request = {
352
                    "server": {
353
                        "name": "new-server-test",
354
                        "userid": "test_user",
355
                        "imageRef": 1,
356
                        "flavorRef": 42,
357
                        "metadata": {
358
                            "My Server Name": "Apache1"
359
                        },
360
                        "personality": []
361
                    }
362
        }
363
        response = self.post('/api/v1.1/servers', 'test_user',
364
                                 json.dumps(request), 'json')
365
        self.assertItemNotFound(response)
366

  
367

  
368
@patch('synnefo.logic.rapi_pool.GanetiRapiClient')
369
class ServerDestroyAPITest(BaseAPITest):
370
    def test_delete_server(self, mrapi):
371
        vm = mfactory.VirtualMachineFactory()
372
        response = self.delete('/api/v1.1/servers/%d' % vm.id, vm.userid)
373
        self.assertEqual(response.status_code, 204)
374
        mrapi().DeleteInstance.assert_called_once()
375

  
376
    def test_non_existing_delete_server(self, mrapi):
377
        vm = mfactory.VirtualMachineFactory()
378
        response = self.delete('/api/v1.1/servers/%d' % 42, vm.userid)
379
        self.assertItemNotFound(response)
380
        self.assertFalse(mrapi.mock_calls)
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff