Revision fde2c1f7

b/snf-cyclades-app/synnefo/api/floating_ips.py
40 40
from snf_django.lib.api import faults, utils
41 41
from synnefo.api import util
42 42
from synnefo import quotas
43
from synnefo.db.models import Network, IPAddress, NetworkInterface
43
from synnefo.db.models import Network, IPAddress
44
from synnefo.db import pools
44 45

  
45 46

  
46 47
from logging import getLogger
......
77 78

  
78 79

  
79 80
def ip_to_dict(floating_ip):
80
    machine_id = floating_ip.machine_id
81
    machine_id = None
82
    if floating_ip.nic is not None:
83
        machine_id = floating_ip.nic.machine_id
81 84
    return {"fixed_ip": None,
82 85
            "id": str(floating_ip.id),
83 86
            "instance_id": str(machine_id) if machine_id else None,
84
            "ip": floating_ip.ipv4,
87
            "ip": floating_ip.address,
85 88
            "pool": str(floating_ip.network_id)}
86 89

  
87 90

  
......
92 95
    log.debug("list_floating_ips")
93 96

  
94 97
    userid = request.user_uniq
95
    floating_ips = IPAddress.objects.filter(userid=userid).order_by("id")
98
    floating_ips = IPAddress.objects.filter(userid=userid, deleted=False,
99
                                            floating_ip=True).order_by("id")\
100
                                    .select_related("nic")
96 101
    floating_ips = utils.filter_modified_since(request, objects=floating_ips)
97 102

  
98 103
    floating_ips = map(ip_to_dict, floating_ips)
......
108 113
def get_floating_ip(request, floating_ip_id):
109 114
    """Return information for a floating IP."""
110 115
    userid = request.user_uniq
111
    try:
112
        floating_ip = IPAddress.objects.get(id=floating_ip_id,
113
                                             deleted=False,
114
                                             userid=userid)
115
    except IPAddress.DoesNotExist:
116
        raise faults.ItemNotFound("Floating IP '%s' does not exist" %
117
                                  floating_ip_id)
116
    floating_ip = util.get_floating_ip_by_id(userid, floating_ip_id)
118 117
    request.serialization = "json"
119 118
    data = json.dumps({"floating_ip": ip_to_dict(floating_ip)})
120 119
    return HttpResponse(data, status=200)
......
122 121

  
123 122
@api.api_method(http_method='POST', user_required=True, logger=log,
124 123
                serializations=["json"])
125
@transaction.commit_manually
124
@transaction.commit_on_success
126 125
def allocate_floating_ip(request):
127 126
    """Allocate a floating IP."""
128 127
    req = utils.get_request_dict(request)
......
131 130
    userid = request.user_uniq
132 131
    pool = req.get("pool", None)
133 132
    address = req.get("address", None)
134
    machine = None
135
    net_objects = Network.objects.select_for_update()\
136
                                 .filter(public=True, floating_ip_pool=True,
137
                                         deleted=False)
138
    try:
139
        if pool is None:
140
            # User did not specified a pool. Choose a random public IP
141
            network, address = util.get_free_ip(net_objects)
142
        else:
143
            try:
144
                network_id = int(pool)
145
            except ValueError:
146
                raise faults.BadRequest("Invalid pool ID.")
147
            network = next((n for n in net_objects if n.id == network_id),
148
                           None)
149
            if network is None:
150
                raise faults.ItemNotFound("Pool '%s' does not exist." % pool)
151
            if address is None:
152
                # User did not specified an IP address. Choose a random one
153
                # Gets X-Lock on IP pool
154
                address = util.get_network_free_address(network)
155
            else:
156
                # User specified an IP address. Check that it is not a used
157
                # floating IP
158
                if IPAddress.objects.filter(network=network,
159
                                             deleted=False,
160
                                             ipv4=address).exists():
161
                    msg = "Floating IP '%s' is reserved" % address
162
                    raise faults.Conflict(msg)
163
                pool = network.get_pool()  # Gets X-Lock
164
                # Check address belongs to pool
165
                if not pool.contains(address):
166
                    raise faults.BadRequest("Invalid address")
167
                if pool.is_available(address):
168
                    pool.reserve(address)
169
                    pool.save()
170
                # If address is not available, check that it belongs to the
171
                # same user
172
                else:
173
                    try:
174
                        nic = network.nics.get(ipv4=address,
175
                                               machine__userid=userid)
176
                        nic.ip_type = "FLOATING"
177
                        nic.save()
178
                    except NetworkInterface.DoesNotExist:
179
                        msg = "Address '%s' is already in use" % address
180
                        raise faults.Conflict(msg)
181
        floating_ip = IPAddress.objects.create(ipv4=address, network=network,
182
                                                userid=userid, machine=machine)
183
        quotas.issue_and_accept_commission(floating_ip)
184
    except:
185
        transaction.rollback()
186
        raise
187
    else:
188
        transaction.commit()
189 133

  
190
    log.info("User '%s' allocated floating IP '%s", userid, floating_ip)
134
    if pool is None:
135
        # User did not specified a pool. Choose a random public IP
136
        try:
137
            floating_ip = util.allocate_public_ip(userid=userid,
138
                                                  floating_ip=True)
139
        except pools.EmptyPool:
140
            raise faults.Conflict("No more IP addresses available.")
141
    else:
142
        try:
143
            network_id = int(pool)
144
        except ValueError:
145
            raise faults.BadRequest("Invalid pool ID.")
146
        network = util.get_network(network_id, userid, for_update=True,
147
                                   non_deleted=True)
148
        if not network.floating_ip_pool:
149
            # Check that it is a floating IP pool
150
            raise faults.ItemNotFound("Floating IP pool %s does not exist." %
151
                                      network_id)
152
        floating_ip = util.allocate_ip(network, userid, address=address,
153
                                       floating_ip=True)
154

  
155
    quotas.issue_and_accept_commission(floating_ip)
156
    transaction.commit()
157

  
158
    log.info("User '%s' allocated floating IP '%s'", userid, floating_ip)
191 159

  
192 160
    request.serialization = "json"
193 161
    data = json.dumps({"floating_ip": ip_to_dict(floating_ip)})
......
201 169
    """Release a floating IP."""
202 170
    userid = request.user_uniq
203 171
    log.info("release_floating_ip '%s'. User '%s'.", floating_ip_id, userid)
204
    try:
205
        floating_ip = IPAddress.objects.select_for_update()\
206
                                        .get(id=floating_ip_id,
207
                                             deleted=False,
208
                                             userid=userid)
209
    except IPAddress.DoesNotExist:
210
        raise faults.ItemNotFound("Floating IP '%s' does not exist" %
211
                                  floating_ip_id)
212

  
213
    # Since we have got an exlusively lock in floating IP, and since
214
    # to remove a floating IP you need the same lock, the in_use() query
215
    # is safe
216
    if floating_ip.in_use():
217
        msg = "Floating IP '%s' is used" % floating_ip.id
218
        raise faults.Conflict(message=msg)
219

  
220
    try:
221
        floating_ip.network.release_address(floating_ip.ipv4)
222
        floating_ip.deleted = True
223
        quotas.issue_and_accept_commission(floating_ip, delete=True)
224
    except:
225
        transaction.rollback()
226
        raise
227
    else:
228
        floating_ip.delete()
229
        transaction.commit()
172

  
173
    floating_ip = util.get_floating_ip_by_id(userid, floating_ip_id,
174
                                             for_update=True)
175
    if floating_ip.nic:
176
        # This is safe, you also need for_update to attach floating IP to
177
        # instance.
178
        msg = "Floating IP '%s' is attached to instance." % floating_ip.id
179
        raise faults.Conflict(msg)
180

  
181
    # Return the address of the floating IP back to pool
182
    floating_ip.release_address()
183
    # And mark the floating IP as deleted
184
    floating_ip.deleted = True
185
    floating_ip.save()
186
    # Release quota for floating IP
187
    quotas.issue_and_accept_commission(floating_ip, delete=True)
188
    transaction.commit()
189
    # Delete the floating IP from DB
190
    floating_ip.delete()
230 191

  
231 192
    log.info("User '%s' released IP '%s", userid, floating_ip)
232 193

  
233 194
    return HttpResponse(status=204)
234 195

  
235 196

  
236
def network_to_pool(network):
237
    pool = network.get_pool(locked=False)
238
    return {"name": str(network.id),
239
            "size": pool.pool_size,
240
            "free": pool.count_available()}
241

  
242

  
197
# Floating IP pools
243 198
@api.api_method(http_method='GET', user_required=True, logger=log,
244 199
                serializations=["json"])
245 200
def list_floating_ip_pools(request):
246
    networks = Network.objects.filter(public=True, floating_ip_pool=True)
201
    networks = Network.objects.filter(public=True, floating_ip_pool=True,
202
                                      deleted=False)
247 203
    networks = utils.filter_modified_since(request, objects=networks)
248
    pools = map(network_to_pool, networks)
204
    pools = map(network_to_floating_ip_pool, networks)
249 205
    request.serialization = "json"
250 206
    data = json.dumps({"floating_ip_pools": pools})
251 207
    request.serialization = "json"
252 208
    return HttpResponse(data, status=200)
209

  
210

  
211
def network_to_floating_ip_pool(network):
212
    """Convert a 'Network' object to a floating IP pool dict."""
213
    total, free = network.ip_count()
214
    return {"name": str(network.id),
215
            "size": total,
216
            "free": free}
b/snf-cyclades-app/synnefo/api/tests/__init__.py
40 40
from .versions import *
41 41
from .extensions import *
42 42
from .subnets import *
43
#from .floating_ips import *
43
from .floating_ips import *
b/snf-cyclades-app/synnefo/api/tests/floating_ips.py
35 35

  
36 36
from snf_django.utils.testing import BaseAPITest, mocked_quotaholder
37 37
from synnefo.db.models import IPAddress
38
from synnefo.db.models_factory import (FloatingIPFactory, NetworkFactory,
39
                                       VirtualMachineFactory,
40
                                       NetworkInterfaceFactory,
41
                                       BackendNetworkFactory)
38
from synnefo.db import models_factory as mf
39
from synnefo.db.models_factory import (NetworkFactory,
40
                                       VirtualMachineFactory)
42 41
from mock import patch, Mock
43 42
from functools import partial
44 43

  
......
59 58

  
60 59

  
61 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

  
62 67
    def test_no_floating_ip(self):
63 68
        response = self.get(URL)
64 69
        self.assertSuccess(response)
65 70
        self.assertEqual(json.loads(response.content)["floating_ips"], [])
66 71

  
67 72
    def test_list_ips(self):
68
        ip = FloatingIPFactory(userid="user1")
69
        FloatingIPFactory(userid="user1", deleted=True)
73
        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True)
70 74
        with mocked_quotaholder():
71 75
            response = self.get(URL, "user1")
72 76
        self.assertSuccess(response)
73 77
        api_ip = json.loads(response.content)["floating_ips"][0]
74 78
        self.assertEqual(api_ip,
75
                         {"instance_id": str(ip.machine.id), "ip": ip.ipv4,
76
                          "fixed_ip": None, "id": str(ip.id),  "pool":
77
                          str(ip.network.id)})
79
                         {"instance_id": str(ip.nic.machine_id),
80
                          "ip": ip.address,
81
                          "fixed_ip": None,
82
                          "id": str(ip.id),
83
                          "pool": str(ip.network_id)})
78 84

  
79 85
    def test_get_ip(self):
80
        ip = FloatingIPFactory(userid="user1")
86
        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True)
81 87
        with mocked_quotaholder():
82 88
            response = self.get(URL + "/%s" % ip.id, "user1")
83 89
        self.assertSuccess(response)
84 90
        api_ip = json.loads(response.content)["floating_ip"]
85 91
        self.assertEqual(api_ip,
86
                         {"instance_id": str(ip.machine.id), "ip": ip.ipv4,
87
                          "fixed_ip": None, "id": str(ip.id),  "pool":
88
                          str(ip.network.id)})
92
                         {"instance_id": str(ip.nic.machine_id),
93
                          "ip": ip.address,
94
                          "fixed_ip": None,
95
                          "id": str(ip.id),
96
                          "pool": str(ip.network_id)})
89 97

  
90 98
    def test_wrong_user(self):
91
        ip = FloatingIPFactory(userid="user1")
92
        with mocked_quotaholder():
93
            response = self.delete(URL + "/%s" % ip.id, "user2")
99
        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True)
100
        response = self.delete(URL + "/%s" % ip.id, "user2")
94 101
        self.assertItemNotFound(response)
95 102

  
96 103
    def test_deleted_ip(self):
97
        ip = FloatingIPFactory(userid="user1", deleted=True)
98
        with mocked_quotaholder():
99
            response = self.delete(URL + "/%s" % ip.id, "user1")
104
        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True,
105
                                   deleted=True)
106
        response = self.delete(URL + "/%s" % ip.id, "user1")
100 107
        self.assertItemNotFound(response)
101 108

  
102 109
    def test_reserve(self):
103
        net = FloatingIPPoolFactory(userid="test_user",
104
                                    subnet="192.168.2.0/24",
105
                                    gateway=None)
106
        request = {'pool': net.id}
110
        request = {'pool': self.pool.id}
107 111
        with mocked_quotaholder():
108 112
            response = self.post(URL, "test_user", json.dumps(request), "json")
109 113
        self.assertSuccess(response)
110 114
        ip = floating_ips.get()
111
        self.assertEqual(ip.ipv4, "192.168.2.1")
112
        self.assertEqual(ip.machine, None)
113
        self.assertEqual(ip.network, net)
115
        self.assertEqual(ip.address, "192.168.2.2")
116
        self.assertEqual(ip.nic, None)
117
        self.assertEqual(ip.network, self.pool)
114 118
        self.assertEqual(json.loads(response.content)["floating_ip"],
115
                         {"instance_id": None, "ip": "192.168.2.1",
119
                         {"instance_id": None, "ip": "192.168.2.2",
116 120
                          "fixed_ip": None, "id": str(ip.id),
117
                          "pool": str(net.id)})
121
                          "pool": str(self.pool.id)})
118 122

  
119 123
    def test_reserve_no_pool(self):
120
        # No networks
121
        with mocked_quotaholder():
122
            response = self.post(URL, "test_user", json.dumps({}), "json")
123
        self.assertFault(response, 413, "overLimit")
124
        # No floating IP pools
125
        self.pool.delete()
126
        response = self.post(URL, "test_user", json.dumps({}), "json")
127
        self.assertFault(response, 503, 'serviceUnavailable')
128

  
124 129
        # Full network
125
        FloatingIPPoolFactory(userid="test_user",
126
                              subnet="192.168.2.0/32",
127
                              gateway=None)
128
        with mocked_quotaholder():
129
            response = self.post(URL, "test_user", json.dumps({}), "json")
130
        self.assertFault(response, 413, "overLimit")
131
        # Success
132
        net2 = FloatingIPPoolFactory(userid="test_user",
133
                                     subnet="192.168.2.0/24",
134
                                     gateway=None)
135
        with mocked_quotaholder():
136
            response = self.post(URL, "test_user", json.dumps({}), "json")
137
        self.assertSuccess(response)
138
        ip = floating_ips.get()
139
        self.assertEqual(json.loads(response.content)["floating_ip"],
140
                         {"instance_id": None, "ip": "192.168.2.1",
141
                          "fixed_ip": None, "id": str(ip.id),
142
                          "pool": str(net2.id)})
130
        pool = mf.NetworkWithSubnetFactory(floating_ip_pool=True,
131
                                           public=True,
132
                                           subnet__cidr="192.168.2.0/31",
133
                                           subnet__gateway="192.168.2.1")
134
        response = self.post(URL, "test_user", json.dumps({}), "json")
135
        self.assertFault(response, 503, 'serviceUnavailable')
143 136

  
144
    def test_reserve_full(self):
145
        net = FloatingIPPoolFactory(userid="test_user",
146
                                    subnet="192.168.2.0/32")
147
        request = {'pool': net.id}
148
        with mocked_quotaholder():
149
            response = self.post(URL, "test_user", json.dumps(request), "json")
150
        self.assertEqual(response.status_code, 413)
137
        request = {'pool': pool.id}
138
        response = self.post(URL, "test_user", json.dumps(request), "json")
139
        self.assertConflict(response)
151 140

  
152 141
    def test_reserve_with_address(self):
153
        net = FloatingIPPoolFactory(userid="test_user",
154
                                    subnet="192.168.2.0/24")
155
        request = {'pool': net.id, "address": "192.168.2.10"}
142
        request = {'pool': self.pool.id, "address": "192.168.2.10"}
156 143
        with mocked_quotaholder():
157 144
            response = self.post(URL, "test_user", json.dumps(request), "json")
158 145
        self.assertSuccess(response)
159 146
        ip = floating_ips.get()
160 147
        self.assertEqual(json.loads(response.content)["floating_ip"],
161 148
                         {"instance_id": None, "ip": "192.168.2.10",
162
                          "fixed_ip": None, "id": str(ip.id), "pool":
163
                          str(net.id)})
149
                          "fixed_ip": None, "id": str(ip.id),
150
                          "pool": str(self.pool.id)})
164 151

  
165 152
        # Already reserved
166
        FloatingIPFactory(network=net, ipv4="192.168.2.3")
167
        request = {'pool': net.id, "address": "192.168.2.3"}
168 153
        with mocked_quotaholder():
169 154
            response = self.post(URL, "test_user", json.dumps(request), "json")
170 155
        self.assertFault(response, 409, "conflict")
171 156

  
172
        # Already used
173
        pool = net.get_pool()
174
        pool.reserve("192.168.2.5")
175
        pool.save()
176
        # ..by another_user
177
        nic = NetworkInterfaceFactory(network=net, ipv4="192.168.2.5",
178
                                      machine__userid="test2")
179
        request = {'pool': net.id, "address": "192.168.2.5"}
157
        # Used by instance
158
        self.pool.reserve_address("192.168.2.20")
159
        request = {'pool': self.pool.id, "address": "192.168.2.20"}
180 160
        with mocked_quotaholder():
181 161
            response = self.post(URL, "test_user", json.dumps(request), "json")
182 162
        self.assertFault(response, 409, "conflict")
183
        # ..and by him
184
        nic.delete()
185
        NetworkInterfaceFactory(network=net, ipv4="192.168.2.5",
186
                                machine__userid="test_user")
187
        request = {'pool': net.id, "address": "192.168.2.5"}
188
        with mocked_quotaholder():
189
            response = self.post(URL, "test_user", json.dumps(request), "json")
190
        self.assertSuccess(response)
191 163

  
192 164
        # Address out of pool
193
        request = {'pool': net.id, "address": "192.168.3.5"}
165
        request = {'pool': self.pool.id, "address": "192.168.3.5"}
194 166
        with mocked_quotaholder():
195 167
            response = self.post(URL, "test_user", json.dumps(request), "json")
196 168
        self.assertBadRequest(response)
197 169

  
198 170
    def test_release_in_use(self):
199
        ip = FloatingIPFactory()
200
        vm = ip.machine
201
        vm.operstate = "ACTIVE"
202
        vm.userid = ip.userid
203
        vm.save()
204
        vm.nics.create(index=0, ipv4=ip.ipv4, network=ip.network,
205
                       state="ACTIVE")
171
        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True)
172
        vm = ip.nic.machine
206 173
        with mocked_quotaholder():
207 174
            response = self.delete(URL + "/%s" % ip.id, ip.userid)
208 175
        self.assertFault(response, 409, "conflict")
209 176
        # Also send a notification to remove the NIC and assert that FIP is in
210 177
        # use until notification from ganeti arrives
211
        request = {"removeFloatingIp": {"address": ip.ipv4}}
178
        request = {"removeFloatingIp": {"address": ip.address}}
212 179
        url = SERVERS_URL + "/%s/action" % vm.id
213 180
        with patch('synnefo.logic.rapi_pool.GanetiRapiClient') as c:
214 181
            c().ModifyInstance.return_value = 10
......
220 187
        self.assertFault(response, 409, "conflict")
221 188

  
222 189
    def test_release(self):
223
        ip = FloatingIPFactory(machine=None)
190
        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True, nic=None)
224 191
        with mocked_quotaholder():
225 192
            response = self.delete(URL + "/%s" % ip.id, ip.userid)
226 193
        self.assertSuccess(response)
......
229 196

  
230 197
    @patch("synnefo.logic.backend", Mock())
231 198
    def test_delete_network_with_floating_ips(self):
232
        ip = FloatingIPFactory(machine=None, network__flavor="IP_LESS_ROUTED")
233
        net = ip.network
199
        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True,
200
                                   network=self.pool, nic=None)
201
        # Mark the network as non-pubic to not get 403
202
        network = ip.network
203
        network.public = False
204
        network.save()
234 205
        # Can not remove network with floating IPs
235 206
        with mocked_quotaholder():
236
            response = self.delete(NETWORKS_URL + "/%s" % net.id,
237
                                   net.userid)
238
        self.assertFault(response, 421, "networkInUse")
207
            response = self.delete(NETWORKS_URL + "/%s" % self.pool.id,
208
                                   self.pool.userid)
209
        self.assertConflict(response)
239 210
        # But we can with only deleted Floating Ips
240 211
        ip.deleted = True
241 212
        ip.save()
242 213
        with mocked_quotaholder():
243
            response = self.delete(NETWORKS_URL + "/%s" % net.id,
244
                                   net.userid)
214
            response = self.delete(NETWORKS_URL + "/%s" % self.pool.id,
215
                                   self.pool.userid)
245 216
        self.assertSuccess(response)
246 217

  
247 218

  
......
255 226
        self.assertEqual(json.loads(response.content)["floating_ip_pools"], [])
256 227

  
257 228
    def test_list_pools(self):
258
        net = FloatingIPPoolFactory(subnet="192.168.0.0/30",
259
                                    gateway="192.168.0.1")
260
        NetworkFactory(public=True, deleted=True)
261
        NetworkFactory(public=False, deleted=False)
262
        NetworkFactory(public=True, deleted=False)
229
        net = mf.NetworkWithSubnetFactory(floating_ip_pool=True,
230
                                          public=True,
231
                                          subnet__cidr="192.168.2.0/30",
232
                                          subnet__gateway="192.168.2.1")
233
        mf.NetworkWithSubnetFactory(public=True, deleted=True)
234
        mf.NetworkWithSubnetFactory(public=False, deleted=False)
235
        mf.NetworkWithSubnetFactory(public=True, floating_ip_pool=False)
263 236
        response = self.get(POOLS_URL)
264 237
        self.assertSuccess(response)
265 238
        self.assertEqual(json.loads(response.content)["floating_ip_pools"],
......
268 241

  
269 242
class FloatingIPActionsTest(BaseAPITest):
270 243
    def setUp(self):
271
        vm = VirtualMachineFactory()
272
        vm.operstate = "ACTIVE"
273
        vm.save()
274
        self.vm = vm
244
        self.vm = VirtualMachineFactory()
245
        self.vm.operstate = "ACTIVE"
246
        self.vm.save()
275 247

  
276 248
    def test_bad_request(self):
277 249
        url = SERVERS_URL + "/%s/action" % self.vm.id
......
290 262
        response = self.post(url, self.vm.userid, json.dumps(request), "json")
291 263
        self.assertItemNotFound(response)
292 264
        # In use
293
        vm1 = VirtualMachineFactory()
294
        ip1 = FloatingIPFactory(userid=self.vm.userid, machine=vm1)
295
        BackendNetworkFactory(network=ip1.network, backend=vm1.backend,
296
                              operstate='ACTIVE')
297
        request = {"addFloatingIp": {"address": ip1.ipv4}}
265
        ip = mf.IPv4AddressFactory(floating_ip=True, userid=self.vm.userid)
266
        request = {"addFloatingIp": {"address": ip.address}}
298 267
        response = self.post(url, self.vm.userid, json.dumps(request), "json")
299
        self.assertFault(response, 409, "conflict")
268
        self.assertConflict(response)
300 269
        # Success
301
        ip1 = FloatingIPFactory(userid=self.vm.userid, machine=None)
302
        BackendNetworkFactory(network=ip1.network, backend=self.vm.backend,
303
                              operstate='ACTIVE')
304
        request = {"addFloatingIp": {"address": ip1.ipv4}}
270
        ip = mf.IPv4AddressFactory(floating_ip=True, nic=None,
271
                                   userid=self.vm.userid)
272
        request = {"addFloatingIp": {"address": ip.address}}
305 273
        mock().ModifyInstance.return_value = 1
306 274
        response = self.post(url, self.vm.userid, json.dumps(request), "json")
307 275
        self.assertEqual(response.status_code, 202)
308
        ip1_after = floating_ips.get(id=ip1.id)
309
        self.assertEqual(ip1_after.machine, self.vm)
310
        self.assertTrue(ip1_after.in_use())
311
        nic = self.vm.nics.get(ipv4=ip1_after.ipv4)
276
        ip_after = floating_ips.get(id=ip.id)
277
        self.assertEqual(ip_after.nic.machine, self.vm)
278
        nic = self.vm.nics.get()
312 279
        nic.state = "ACTIVE"
313 280
        nic.save()
314 281
        response = self.get(SERVERS_URL + "/%s" % self.vm.id,
......
323 290
        url = SERVERS_URL + "/%s/action" % self.vm.id
324 291
        request = {"removeFloatingIp": {"address": "10.0.0.1"}}
325 292
        response = self.post(url, self.vm.userid, json.dumps(request), "json")
326
        self.assertItemNotFound(response)
293
        self.assertBadRequest(response)
327 294
        # Not In Use
328
        ip1 = FloatingIPFactory(userid=self.vm.userid, machine=None)
329
        request = {"removeFloatingIp": {"address": ip1.ipv4}}
295
        ip = mf.IPv4AddressFactory(floating_ip=True, nic=None,
296
                                   userid=self.vm.userid)
297
        request = {"removeFloatingIp": {"address": ip.address}}
330 298
        response = self.post(url, self.vm.userid, json.dumps(request), "json")
331
        self.assertItemNotFound(response)
299
        self.assertBadRequest(response)
332 300
        # Success
333
        ip1 = FloatingIPFactory(userid=self.vm.userid, machine=self.vm)
334
        NetworkInterfaceFactory(machine=self.vm, ipv4=ip1.ipv4)
335
        request = {"removeFloatingIp": {"address": ip1.ipv4}}
301
        ip = mf.IPv4AddressFactory(floating_ip=True,
302
                                   userid=self.vm.userid, nic__machine=self.vm)
303
        request = {"removeFloatingIp": {"address": ip.address}}
336 304
        mock().ModifyInstance.return_value = 2
337 305
        response = self.post(url, self.vm.userid, json.dumps(request), "json")
338 306
        self.assertEqual(response.status_code, 202)
339 307
        # Yet used. Wait for the callbacks
340
        ip1_after = floating_ips.get(id=ip1.id)
341
        self.assertEqual(ip1_after.machine, self.vm)
342
        self.assertTrue(ip1_after.in_use())
308
        ip_after = floating_ips.get(id=ip.id)
309
        self.assertEqual(ip_after.nic.machine, self.vm)
b/snf-django-lib/snf_django/utils/testing.py
237 237
    def assertBadRequest(self, response):
238 238
        self.assertFault(response, 400, 'badRequest')
239 239

  
240
    def assertConflict(self, response):
241
        self.assertFault(response, 409, 'conflict')
242

  
240 243
    def assertItemNotFound(self, response):
241 244
        self.assertFault(response, 404, 'itemNotFound')
242 245

  

Also available in: Unified diff