Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / api / test / floating_ips.py @ a95c82a9

History | View | Annotate | Download (9.4 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
import json
35

    
36
from snf_django.utils.testing import BaseAPITest, mocked_quotaholder
37
from synnefo.db.models import FloatingIP
38
from synnefo.db.models_factory import (FloatingIPFactory, NetworkFactory,
39
                                       VirtualMachineFactory,
40
                                       NetworkInterfaceFactory)
41
from mock import patch
42

    
43

    
44
URL = "/api/v1.1/os-floating-ips"
45

    
46

    
47
class FloatingIPAPITest(BaseAPITest):
48
    def test_no_floating_ip(self):
49
        response = self.get(URL)
50
        self.assertSuccess(response)
51
        self.assertEqual(json.loads(response.content)["floating_ips"], [])
52

    
53
    def test_list_ips(self):
54
        ip = FloatingIPFactory(userid="user1")
55
        FloatingIPFactory(userid="user1", deleted=True)
56
        with mocked_quotaholder():
57
            response = self.get(URL, "user1")
58
        self.assertSuccess(response)
59
        api_ip = json.loads(response.content)["floating_ips"][0]
60
        self.assertEqual(api_ip,
61
                         {"instance_id": str(ip.machine.id), "ip": ip.ipv4,
62
                          "fixed_ip": None, "id": str(ip.id),  "pool":
63
                          str(ip.network.id)})
64

    
65
    def test_get_ip(self):
66
        ip = FloatingIPFactory(userid="user1")
67
        with mocked_quotaholder():
68
            response = self.get(URL + "/%s" % ip.id, "user1")
69
        self.assertSuccess(response)
70
        api_ip = json.loads(response.content)["floating_ip"]
71
        self.assertEqual(api_ip,
72
                         {"instance_id": str(ip.machine.id), "ip": ip.ipv4,
73
                          "fixed_ip": None, "id": str(ip.id),  "pool":
74
                          str(ip.network.id)})
75

    
76
    def test_wrong_user(self):
77
        ip = FloatingIPFactory(userid="user1")
78
        with mocked_quotaholder():
79
            response = self.delete(URL + "/%s" % ip.id, "user2")
80
        self.assertItemNotFound(response)
81

    
82
    def test_deleted_ip(self):
83
        ip = FloatingIPFactory(userid="user1", deleted=True)
84
        with mocked_quotaholder():
85
            response = self.delete(URL + "/%s" % ip.id, "user1")
86
        self.assertItemNotFound(response)
87

    
88
    def test_reserve(self):
89
        net = NetworkFactory(userid="test_user", subnet="192.168.2.0/24",
90
                             gateway=None, public=True)
91
        request = {'pool': net.id}
92
        with mocked_quotaholder():
93
            response = self.post(URL, "test_user", json.dumps(request), "json")
94
        self.assertSuccess(response)
95
        ip = FloatingIP.objects.get()
96
        self.assertEqual(ip.ipv4, "192.168.2.1")
97
        self.assertEqual(ip.machine, None)
98
        self.assertEqual(ip.network, net)
99
        self.assertEqual(json.loads(response.content)["floating_ip"],
100
                         {"instance_id": None, "ip": "192.168.2.1",
101
                          "fixed_ip": None, "id": "1", "pool": "1"})
102

    
103
    def test_reserve_full(self):
104
        net = NetworkFactory(userid="test_user", subnet="192.168.2.0/32",
105
                             gateway=None, public=True)
106
        request = {'pool': net.id}
107
        with mocked_quotaholder():
108
            response = self.post(URL, "test_user", json.dumps(request), "json")
109
        self.assertEqual(response.status_code, 413)
110

    
111
    def test_release_in_use(self):
112
        ip = FloatingIPFactory()
113
        vm = ip.machine
114
        vm.operstate = "ACTIVE"
115
        vm.userid = ip.userid
116
        vm.save()
117
        vm.nics.create(index=0, ipv4=ip.ipv4, network=ip.network,
118
                       state="ACTIVE")
119
        with mocked_quotaholder():
120
            response = self.delete(URL + "/%s" % ip.id, ip.userid)
121
        self.assertFault(response, 409, "conflict")
122
        # Also send a notification to remove the NIC and assert that FIP is in
123
        # use until notification from ganeti arrives
124
        request = {"removeFloatingIp": {"address": ip.ipv4}}
125
        url = "/api/v1.1/servers/%s/action" % vm.id
126
        with patch('synnefo.logic.rapi_pool.GanetiRapiClient') as c:
127
            c().ModifyInstance.return_value = 10
128
            response = self.post(url, vm.userid, json.dumps(request),
129
                                 "json")
130
        self.assertEqual(response.status_code, 202)
131
        with mocked_quotaholder():
132
            response = self.delete(URL + "/%s" % ip.id, ip.userid)
133
        self.assertFault(response, 409, "conflict")
134

    
135
    def test_release(self):
136
        ip = FloatingIPFactory(machine=None)
137
        with mocked_quotaholder():
138
            response = self.delete(URL + "/%s" % ip.id, ip.userid)
139
        self.assertSuccess(response)
140
        ips_after = FloatingIP.objects.filter(id=ip.id)
141
        self.assertEqual(len(ips_after), 0)
142

    
143

    
144
POOLS_URL = "/api/v1.1/os-floating-ip-pools"
145

    
146

    
147
class FloatingIPPoolsAPITest(BaseAPITest):
148
    def test_no_pool(self):
149
        response = self.get(POOLS_URL)
150
        self.assertSuccess(response)
151
        self.assertEqual(json.loads(response.content)["floating_ip_pools"], [])
152

    
153
    def test_list_pools(self):
154
        net = NetworkFactory(public=True, deleted=False)
155
        NetworkFactory(public=True, deleted=True)
156
        NetworkFactory(public=False, deleted=False)
157
        response = self.get(POOLS_URL)
158
        self.assertSuccess(response)
159
        self.assertEqual(json.loads(response.content)["floating_ip_pools"],
160
                        [{"name": str(net.id)}])
161

    
162

    
163
class FloatingIPActionsTest(BaseAPITest):
164
    def setUp(self):
165
        vm = VirtualMachineFactory()
166
        vm.operstate = "ACTIVE"
167
        vm.save()
168
        self.vm = vm
169

    
170
    def test_bad_request(self):
171
        url = "/api/v1.1/servers/%s/action" % self.vm.id
172
        response = self.post(url, self.vm.userid, json.dumps({}), "json")
173
        self.assertBadRequest(response)
174
        response = self.post(url, self.vm.userid,
175
                             json.dumps({"addFloatingIp": {}}),
176
                             "json")
177
        self.assertBadRequest(response)
178

    
179
    @patch('synnefo.logic.rapi_pool.GanetiRapiClient')
180
    def test_add_floating_ip(self, mock):
181
        # Not exists
182
        url = "/api/v1.1/servers/%s/action" % self.vm.id
183
        request = {"addFloatingIp": {"address": "10.0.0.1"}}
184
        response = self.post(url, self.vm.userid, json.dumps(request), "json")
185
        self.assertItemNotFound(response)
186
        # In use
187
        vm1 = VirtualMachineFactory()
188
        ip1 = FloatingIPFactory(userid=self.vm.userid, machine=vm1)
189
        request = {"addFloatingIp": {"address": ip1.ipv4}}
190
        response = self.post(url, self.vm.userid, json.dumps(request), "json")
191
        self.assertFault(response, 409, "conflict")
192
        # Success
193
        ip1 = FloatingIPFactory(userid=self.vm.userid, machine=None)
194
        request = {"addFloatingIp": {"address": ip1.ipv4}}
195
        response = self.post(url, self.vm.userid, json.dumps(request), "json")
196
        self.assertEqual(response.status_code, 202)
197
        ip1_after = FloatingIP.objects.get(id=ip1.id)
198
        self.assertEqual(ip1_after.machine, self.vm)
199
        self.assertTrue(ip1_after.in_use())
200

    
201
    @patch('synnefo.logic.rapi_pool.GanetiRapiClient')
202
    def test_remove_floating_ip(self, mock):
203
        # Not exists
204
        url = "/api/v1.1/servers/%s/action" % self.vm.id
205
        request = {"removeFloatingIp": {"address": "10.0.0.1"}}
206
        response = self.post(url, self.vm.userid, json.dumps(request), "json")
207
        self.assertItemNotFound(response)
208
        # Not In Use
209
        ip1 = FloatingIPFactory(userid=self.vm.userid, machine=None)
210
        request = {"removeFloatingIp": {"address": ip1.ipv4}}
211
        response = self.post(url, self.vm.userid, json.dumps(request), "json")
212
        self.assertItemNotFound(response)
213
        # Success
214
        ip1 = FloatingIPFactory(userid=self.vm.userid, machine=self.vm)
215
        NetworkInterfaceFactory(machine=self.vm, ipv4=ip1.ipv4)
216
        request = {"removeFloatingIp": {"address": ip1.ipv4}}
217
        response = self.post(url, self.vm.userid, json.dumps(request), "json")
218
        self.assertEqual(response.status_code, 202)
219
        # Yet used. Wait for the callbacks
220
        ip1_after = FloatingIP.objects.get(id=ip1.id)
221
        self.assertEqual(ip1_after.machine, self.vm)
222
        self.assertTrue(ip1_after.in_use())