Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / api / tests / floating_ips.py @ 9a73f94d

History | View | Annotate | Download (18.3 kB)

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
from django.utils import simplejson as json
35
from snf_django.utils.testing import BaseAPITest, mocked_quotaholder
36
from synnefo.db.models import IPAddress
37
from synnefo.db import models_factory as mf
38
from synnefo.db.models_factory import (NetworkFactory,
39
                                       VirtualMachineFactory)
40

    
41
from mock import patch, Mock
42
from functools import partial
43

    
44
from synnefo.cyclades_settings import cyclades_services
45
from synnefo.lib.services import get_service_path
46
from synnefo.lib import join_urls
47

    
48

    
49
compute_path = get_service_path(cyclades_services, "compute", version="v2.0")
50
URL = join_urls(compute_path, "floatingips")
51
NETWORKS_URL = join_urls(compute_path, "networks")
52
SERVERS_URL = join_urls(compute_path, "servers")
53

    
54

    
55
floating_ips = IPAddress.objects.filter(floating_ip=True)
56
FloatingIPPoolFactory = partial(NetworkFactory, public=True, deleted=False,
57
                                floating_ip_pool=True)
58

    
59

    
60
class FloatingIPAPITest(BaseAPITest):
61
    def setUp(self):
62
        self.pool = mf.NetworkWithSubnetFactory(floating_ip_pool=True,
63
                                                public=True,
64
                                                subnet__cidr="192.168.2.0/24",
65
                                                subnet__gateway="192.168.2.1")
66

    
67
    def test_no_floating_ip(self):
68
        response = self.get(URL)
69
        self.assertSuccess(response)
70
        self.assertEqual(json.loads(response.content)["floatingips"], [])
71

    
72
    def test_list_ips(self):
73
        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True)
74
        with mocked_quotaholder():
75
            response = self.get(URL, "user1")
76
        self.assertSuccess(response)
77
        api_ip = json.loads(response.content)["floatingips"][0]
78
        self.assertEqual(api_ip,
79
                         {"instance_id": str(ip.nic.machine_id),
80
                          "floating_ip_address": ip.address,
81
                          "fixed_ip_address": None,
82
                          "id": str(ip.id),
83
                          "port_id": str(ip.nic.id),
84
                          "floating_network_id": str(ip.network_id)})
85

    
86
    def test_get_ip(self):
87
        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True)
88
        with mocked_quotaholder():
89
            response = self.get(URL + "/%s" % ip.id, "user1")
90
        self.assertSuccess(response)
91
        api_ip = json.loads(response.content)["floatingip"]
92
        self.assertEqual(api_ip,
93
                         {"instance_id": str(ip.nic.machine_id),
94
                          "floating_ip_address": ip.address,
95
                          "fixed_ip_address": None,
96
                          "id": str(ip.id),
97
                          "port_id": str(ip.nic.id),
98
                          "floating_network_id": str(ip.network_id)})
99

    
100
    def test_wrong_user(self):
101
        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True)
102
        response = self.delete(URL + "/%s" % ip.id, "user2")
103
        self.assertItemNotFound(response)
104

    
105
    def test_deleted_ip(self):
106
        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True,
107
                                   deleted=True)
108
        response = self.delete(URL + "/%s" % ip.id, "user1")
109
        self.assertItemNotFound(response)
110

    
111
    def test_reserve(self):
112
        request = {"floatingip": {
113
            "floating_network_id": self.pool.id}
114
            }
115
        with mocked_quotaholder():
116
            response = self.post(URL, "test_user", json.dumps(request), "json")
117
        self.assertSuccess(response)
118
        api_ip = json.loads(response.content, encoding="utf-8")["floatingip"]
119
        ip = floating_ips.get()
120
        self.assertEqual(ip.address, "192.168.2.2")
121
        self.assertEqual(ip.nic, None)
122
        self.assertEqual(ip.network, self.pool)
123
        self.assertEqual(api_ip,
124
                         {"instance_id": None,
125
                          "floating_ip_address": "192.168.2.2",
126
                          "fixed_ip_address": None,
127
                          "id": str(ip.id),
128
                          "port_id": None,
129
                          "floating_network_id": str(self.pool.id)})
130

    
131
    def test_reserve_no_pool(self):
132
        # Network is not a floating IP pool
133
        pool2 = mf.NetworkWithSubnetFactory(floating_ip_pool=False,
134
                                            public=True,
135
                                            subnet__cidr="192.168.2.0/24",
136
                                            subnet__gateway="192.168.2.1")
137
        request = {"floatingip": {
138
            'floating_network_id': pool2.id}
139
            }
140
        response = self.post(URL, "test_user", json.dumps(request), "json")
141
        self.assertEqual(response.status_code, 404)
142

    
143
        # Full network
144
        net = mf.NetworkWithSubnetFactory(floating_ip_pool=True,
145
                                          public=True,
146
                                          subnet__cidr="192.168.2.0/31",
147
                                          subnet__gateway="192.168.2.1",
148
                                          subnet__pool__size=0)
149
        request = {"floatingip": {
150
            'floating_network_id': net.id}
151
            }
152
        response = self.post(URL, "test_user", json.dumps(request), "json")
153
        self.assertConflict(response)
154

    
155
    def test_reserve_with_address(self):
156
        request = {"floatingip": {
157
            "floating_network_id": self.pool.id,
158
            "floating_ip_address": "192.168.2.10"}
159
            }
160
        with mocked_quotaholder():
161
            response = self.post(URL, "test_user", json.dumps(request), "json")
162
        self.assertSuccess(response)
163
        ip = floating_ips.get()
164
        self.assertEqual(json.loads(response.content)["floatingip"],
165
                         {"instance_id": None,
166
                          "floating_ip_address": "192.168.2.10",
167
                          "fixed_ip_address": None,
168
                          "id": str(ip.id),
169
                          "port_id": None,
170
                          "floating_network_id": str(self.pool.id)})
171

    
172
        # Already reserved
173
        with mocked_quotaholder():
174
            response = self.post(URL, "test_user", json.dumps(request), "json")
175
        self.assertFault(response, 409, "conflict")
176

    
177
        # Used by instance
178
        self.pool.reserve_address("192.168.2.20")
179
        request = {"floatingip": {
180
            "floating_network_id": self.pool.id,
181
            "floating_ip_address": "192.168.2.20"}
182
            }
183
        with mocked_quotaholder():
184
            response = self.post(URL, "test_user", json.dumps(request), "json")
185
        self.assertFault(response, 409, "conflict")
186

    
187
        # Address out of pool
188
        request = {"floatingip": {
189
            "floating_network_id": self.pool.id,
190
            "floating_ip_address": "192.168.3.5"}
191
            }
192
        with mocked_quotaholder():
193
            response = self.post(URL, "test_user", json.dumps(request), "json")
194
        self.assertBadRequest(response)
195

    
196
    '''
197
    @patch("synnefo.db.models.get_rapi_client")
198
    def test_reserve_and_connect(self, mrapi):
199
        vm = mf.VirtualMachineFactory(userid="test_user")
200
        request = {"floatingip": {
201
            "floating_network_id": self.pool.id,
202
            "floating_ip_address": "192.168.2.12",
203
            "device_id": vm.id}
204
            }
205
        response = self.post(URL, "test_user", json.dumps(request), "json")
206
        ip = floating_ips.get()
207
        api_ip = json.loads(response.content, "utf-8")["floatingip"]
208
        self.assertEqual(api_ip,
209
                         {"instance_id": str(vm.id),
210
                          "floating_ip_address": "192.168.2.12",
211
                          "fixed_ip_address": None,
212
                          "id": str(ip.id),
213
                          "port_id": str(vm.nics.all()[0].id),
214
                          "floating_network_id": str(self.pool.id)})
215

216
    @patch("synnefo.db.models.get_rapi_client")
217
    def test_update_attach(self, mrapi):
218
        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True, nic=None)
219
        vm = mf.VirtualMachineFactory(userid="user1")
220
        request = {"floatingip": {
221
            "device_id": vm.id}
222
            }
223
        with mocked_quotaholder():
224
            response = self.put(URL + "/%s" % ip.id, "user1",
225
                                json.dumps(request), "json")
226
        self.assertEqual(response.status_code, 202)
227

228
    def test_update_attach_conflict(self):
229
        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True)
230
        vm = mf.VirtualMachineFactory(userid="user1")
231
        request = {"floatingip": {
232
            "device_id": vm.id}
233
            }
234
        with mocked_quotaholder():
235
            response = self.put(URL + "/%s" % ip.id, "user1",
236
                                json.dumps(request), "json")
237
        self.assertEqual(response.status_code, 409)
238

239
    @patch("synnefo.db.models.get_rapi_client")
240
    def test_update_dettach(self, mrapi):
241
        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True)
242
        request = {"floatingip": {
243
            "device_id": None}
244
            }
245
        mrapi().ModifyInstance.return_value = 42
246
        with mocked_quotaholder():
247
            response = self.put(URL + "/%s" % ip.id, "user1",
248
                                json.dumps(request), "json")
249
        self.assertEqual(response.status_code, 202)
250

251
    def test_update_dettach_unassociated(self):
252
        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True, nic=None)
253
        request = {"floatingip": {}}
254
        with mocked_quotaholder():
255
            response = self.put(URL + "/%s" % ip.id, "user1",
256
                                json.dumps(request), "json")
257
        self.assertEqual(response.status_code, 400)
258

259
    def test_release_in_use(self):
260
        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True)
261
        vm = mf.VirtualMachineFactory(userid="user1")
262
        request = {"floatingip": {
263
            "device_id": vm.id}
264
            }
265
        with mocked_quotaholder():
266
            response = self.put(URL + "/%s" % ip.id, "user1",
267
                                json.dumps(request), "json")
268
        self.assertEqual(response.status_code, 409)
269

270
    @patch("synnefo.db.models.get_rapi_client")
271
    def test_update_dettach(self, mrapi):
272
        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True)
273
        request = {"floatingip": {
274
            "device_id": None}
275
            }
276
        mrapi().ModifyInstance.return_value = 42
277
        with mocked_quotaholder():
278
            response = self.put(URL + "/%s" % ip.id, "user1",
279
                                json.dumps(request), "json")
280
        self.assertEqual(response.status_code, 202)
281

282
    def test_update_dettach_unassociated(self):
283
        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True, nic=None)
284
        request = {"floatingip": {}}
285
        with mocked_quotaholder():
286
            response = self.put(URL + "/%s" % ip.id, "user1",
287
                                json.dumps(request), "json")
288
        self.assertEqual(response.status_code, 400)
289

290
    def test_release_in_use(self):
291
        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True)
292
        vm = ip.nic.machine
293
        with mocked_quotaholder():
294
            response = self.delete(URL + "/%s" % ip.id, ip.userid)
295
        self.assertFault(response, 409, "conflict")
296
    '''
297

    
298
    def test_update(self):
299
        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True, nic=None)
300
        with mocked_quotaholder():
301
            response = self.put(URL + "/%s" % ip.id, ip.userid)
302
        self.assertEqual(response.status_code, 501)
303

    
304
    def test_release(self):
305
        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True, nic=None)
306
        with mocked_quotaholder():
307
            response = self.delete(URL + "/%s" % ip.id, ip.userid)
308
        self.assertSuccess(response)
309
        ips_after = floating_ips.filter(id=ip.id)
310
        self.assertEqual(len(ips_after), 0)
311

    
312
    @patch("synnefo.logic.backend", Mock())
313
    def test_delete_network_with_floating_ips(self):
314
        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True,
315
                                   network=self.pool, nic=None)
316
        # Mark the network as non-pubic to not get 403
317
        network = ip.network
318
        network.public = False
319
        network.save()
320
        # Can not remove network with floating IPs
321
        with mocked_quotaholder():
322
            response = self.delete(NETWORKS_URL + "/%s" % self.pool.id,
323
                                   self.pool.userid)
324
        self.assertConflict(response)
325
        # But we can with only deleted Floating Ips
326
        ip.deleted = True
327
        ip.save()
328
        with mocked_quotaholder():
329
            response = self.delete(NETWORKS_URL + "/%s" % self.pool.id,
330
                                   self.pool.userid)
331
        self.assertSuccess(response)
332

    
333
'''
334
POOLS_URL = join_urls(compute_path, "os-floating-ip-pools")
335

336

337
class FloatingIPPoolsAPITest(BaseAPITest):
338
    def test_no_pool(self):
339
        response = self.get(POOLS_URL)
340
        self.assertSuccess(response)
341
        self.assertEqual(json.loads(response.content)["floating_ip_pools"], [])
342

343
    def test_list_pools(self):
344
        net = mf.NetworkWithSubnetFactory(floating_ip_pool=True,
345
                                          public=True,
346
                                          subnet__cidr="192.168.2.0/30",
347
                                          subnet__gateway="192.168.2.1",
348
                                          subnet__pool__size=1,
349
                                          subnet__pool__offset=1)
350
        mf.NetworkWithSubnetFactory(public=True, deleted=True)
351
        mf.NetworkWithSubnetFactory(public=False, deleted=False)
352
        mf.NetworkWithSubnetFactory(public=True, floating_ip_pool=False)
353
        response = self.get(POOLS_URL)
354
        self.assertSuccess(response)
355
        self.assertEqual(json.loads(response.content)["floating_ip_pools"],
356
                         [{"name": str(net.id), "size": 1, "free": 1}])
357

358

359
class FloatingIPActionsTest(BaseAPITest):
360
    def setUp(self):
361
        self.vm = VirtualMachineFactory()
362
        self.vm.operstate = "ACTIVE"
363
        self.vm.save()
364

365
    def test_bad_request(self):
366
        url = SERVERS_URL + "/%s/action" % self.vm.id
367
        response = self.post(url, self.vm.userid, json.dumps({}), "json")
368
        self.assertBadRequest(response)
369
        response = self.post(url, self.vm.userid,
370
                             json.dumps({"addFloatingIp": {}}),
371
                             "json")
372
        self.assertBadRequest(response)
373

374
    @patch('synnefo.logic.rapi_pool.GanetiRapiClient')
375
    def test_add_floating_ip(self, mock):
376
        # Not exists
377
        url = SERVERS_URL + "/%s/action" % self.vm.id
378
        request = {"addFloatingIp": {"address": "10.0.0.1"}}
379
        response = self.post(url, self.vm.userid, json.dumps(request), "json")
380
        self.assertItemNotFound(response)
381
        # In use
382
        ip = mf.IPv4AddressFactory(floating_ip=True, userid=self.vm.userid)
383
        request = {"addFloatingIp": {"address": ip.address}}
384
        response = self.post(url, self.vm.userid, json.dumps(request), "json")
385
        self.assertConflict(response)
386
        # Success
387
        ip = mf.IPv4AddressFactory(floating_ip=True, nic=None,
388
                                   userid=self.vm.userid)
389
        request = {"addFloatingIp": {"address": ip.address}}
390
        mock().ModifyInstance.return_value = 1
391
        response = self.post(url, self.vm.userid, json.dumps(request), "json")
392
        self.assertEqual(response.status_code, 202)
393
        ip_after = floating_ips.get(id=ip.id)
394
        self.assertEqual(ip_after.nic.machine, self.vm)
395
        nic = self.vm.nics.get()
396
        nic.state = "ACTIVE"
397
        nic.save()
398
        response = self.get(SERVERS_URL + "/%s" % self.vm.id,
399
                            self.vm.userid)
400
        self.assertSuccess(response)
401
        nic = json.loads(response.content)["server"]["attachments"][0]
402
        self.assertEqual(nic["OS-EXT-IPS:type"], "floating")
403

404
    @patch('synnefo.logic.rapi_pool.GanetiRapiClient')
405
    def test_remove_floating_ip(self, mock):
406
        # Not exists
407
        url = SERVERS_URL + "/%s/action" % self.vm.id
408
        request = {"removeFloatingIp": {"address": "10.0.0.1"}}
409
        response = self.post(url, self.vm.userid, json.dumps(request), "json")
410
        self.assertBadRequest(response)
411
        # Not In Use
412
        ip = mf.IPv4AddressFactory(floating_ip=True, nic=None,
413
                                   userid=self.vm.userid)
414
        request = {"removeFloatingIp": {"address": ip.address}}
415
        response = self.post(url, self.vm.userid, json.dumps(request), "json")
416
        self.assertBadRequest(response)
417
        # Success
418
        ip = mf.IPv4AddressFactory(floating_ip=True,
419
                                   userid=self.vm.userid, nic__machine=self.vm)
420
        request = {"removeFloatingIp": {"address": ip.address}}
421
        mock().ModifyInstance.return_value = 2
422
        response = self.post(url, self.vm.userid, json.dumps(request), "json")
423
        self.assertEqual(response.status_code, 202)
424
        # Yet used. Wait for the callbacks
425
        ip_after = floating_ips.get(id=ip.id)
426
        self.assertEqual(ip_after.nic.machine, self.vm)
427
'''