commit fde2c1f79a414145ab74806318489f7c5f9e4318
Author: Christos Stavrakakis <cstavr@grnet.gr>
Date:   Mon Oct 14 13:57:53 2013 +0300

    cyclades: Fix the os-floating-ips API
    
    Rewrite the of-floating-ips API to match with the new DB schema.

diff --git a/snf-cyclades-app/synnefo/api/floating_ips.py b/snf-cyclades-app/synnefo/api/floating_ips.py
index 2f1f947..063e708 100644
--- a/snf-cyclades-app/synnefo/api/floating_ips.py
+++ b/snf-cyclades-app/synnefo/api/floating_ips.py
@@ -40,7 +40,8 @@ from snf_django.lib import api
 from snf_django.lib.api import faults, utils
 from synnefo.api import util
 from synnefo import quotas
-from synnefo.db.models import Network, IPAddress, NetworkInterface
+from synnefo.db.models import Network, IPAddress
+from synnefo.db import pools
 
 
 from logging import getLogger
@@ -77,11 +78,13 @@ def floating_ip_demux(request, floating_ip_id):
 
 
 def ip_to_dict(floating_ip):
-    machine_id = floating_ip.machine_id
+    machine_id = None
+    if floating_ip.nic is not None:
+        machine_id = floating_ip.nic.machine_id
     return {"fixed_ip": None,
             "id": str(floating_ip.id),
             "instance_id": str(machine_id) if machine_id else None,
-            "ip": floating_ip.ipv4,
+            "ip": floating_ip.address,
             "pool": str(floating_ip.network_id)}
 
 
@@ -92,7 +95,9 @@ def list_floating_ips(request):
     log.debug("list_floating_ips")
 
     userid = request.user_uniq
-    floating_ips = IPAddress.objects.filter(userid=userid).order_by("id")
+    floating_ips = IPAddress.objects.filter(userid=userid, deleted=False,
+                                            floating_ip=True).order_by("id")\
+                                    .select_related("nic")
     floating_ips = utils.filter_modified_since(request, objects=floating_ips)
 
     floating_ips = map(ip_to_dict, floating_ips)
@@ -108,13 +113,7 @@ def list_floating_ips(request):
 def get_floating_ip(request, floating_ip_id):
     """Return information for a floating IP."""
     userid = request.user_uniq
-    try:
-        floating_ip = IPAddress.objects.get(id=floating_ip_id,
-                                             deleted=False,
-                                             userid=userid)
-    except IPAddress.DoesNotExist:
-        raise faults.ItemNotFound("Floating IP '%s' does not exist" %
-                                  floating_ip_id)
+    floating_ip = util.get_floating_ip_by_id(userid, floating_ip_id)
     request.serialization = "json"
     data = json.dumps({"floating_ip": ip_to_dict(floating_ip)})
     return HttpResponse(data, status=200)
@@ -122,7 +121,7 @@ def get_floating_ip(request, floating_ip_id):
 
 @api.api_method(http_method='POST', user_required=True, logger=log,
                 serializations=["json"])
-@transaction.commit_manually
+@transaction.commit_on_success
 def allocate_floating_ip(request):
     """Allocate a floating IP."""
     req = utils.get_request_dict(request)
@@ -131,63 +130,32 @@ def allocate_floating_ip(request):
     userid = request.user_uniq
     pool = req.get("pool", None)
     address = req.get("address", None)
-    machine = None
-    net_objects = Network.objects.select_for_update()\
-                                 .filter(public=True, floating_ip_pool=True,
-                                         deleted=False)
-    try:
-        if pool is None:
-            # User did not specified a pool. Choose a random public IP
-            network, address = util.get_free_ip(net_objects)
-        else:
-            try:
-                network_id = int(pool)
-            except ValueError:
-                raise faults.BadRequest("Invalid pool ID.")
-            network = next((n for n in net_objects if n.id == network_id),
-                           None)
-            if network is None:
-                raise faults.ItemNotFound("Pool '%s' does not exist." % pool)
-            if address is None:
-                # User did not specified an IP address. Choose a random one
-                # Gets X-Lock on IP pool
-                address = util.get_network_free_address(network)
-            else:
-                # User specified an IP address. Check that it is not a used
-                # floating IP
-                if IPAddress.objects.filter(network=network,
-                                             deleted=False,
-                                             ipv4=address).exists():
-                    msg = "Floating IP '%s' is reserved" % address
-                    raise faults.Conflict(msg)
-                pool = network.get_pool()  # Gets X-Lock
-                # Check address belongs to pool
-                if not pool.contains(address):
-                    raise faults.BadRequest("Invalid address")
-                if pool.is_available(address):
-                    pool.reserve(address)
-                    pool.save()
-                # If address is not available, check that it belongs to the
-                # same user
-                else:
-                    try:
-                        nic = network.nics.get(ipv4=address,
-                                               machine__userid=userid)
-                        nic.ip_type = "FLOATING"
-                        nic.save()
-                    except NetworkInterface.DoesNotExist:
-                        msg = "Address '%s' is already in use" % address
-                        raise faults.Conflict(msg)
-        floating_ip = IPAddress.objects.create(ipv4=address, network=network,
-                                                userid=userid, machine=machine)
-        quotas.issue_and_accept_commission(floating_ip)
-    except:
-        transaction.rollback()
-        raise
-    else:
-        transaction.commit()
 
-    log.info("User '%s' allocated floating IP '%s", userid, floating_ip)
+    if pool is None:
+        # User did not specified a pool. Choose a random public IP
+        try:
+            floating_ip = util.allocate_public_ip(userid=userid,
+                                                  floating_ip=True)
+        except pools.EmptyPool:
+            raise faults.Conflict("No more IP addresses available.")
+    else:
+        try:
+            network_id = int(pool)
+        except ValueError:
+            raise faults.BadRequest("Invalid pool ID.")
+        network = util.get_network(network_id, userid, for_update=True,
+                                   non_deleted=True)
+        if not network.floating_ip_pool:
+            # Check that it is a floating IP pool
+            raise faults.ItemNotFound("Floating IP pool %s does not exist." %
+                                      network_id)
+        floating_ip = util.allocate_ip(network, userid, address=address,
+                                       floating_ip=True)
+
+    quotas.issue_and_accept_commission(floating_ip)
+    transaction.commit()
+
+    log.info("User '%s' allocated floating IP '%s'", userid, floating_ip)
 
     request.serialization = "json"
     data = json.dumps({"floating_ip": ip_to_dict(floating_ip)})
@@ -201,52 +169,48 @@ def release_floating_ip(request, floating_ip_id):
     """Release a floating IP."""
     userid = request.user_uniq
     log.info("release_floating_ip '%s'. User '%s'.", floating_ip_id, userid)
-    try:
-        floating_ip = IPAddress.objects.select_for_update()\
-                                        .get(id=floating_ip_id,
-                                             deleted=False,
-                                             userid=userid)
-    except IPAddress.DoesNotExist:
-        raise faults.ItemNotFound("Floating IP '%s' does not exist" %
-                                  floating_ip_id)
-
-    # Since we have got an exlusively lock in floating IP, and since
-    # to remove a floating IP you need the same lock, the in_use() query
-    # is safe
-    if floating_ip.in_use():
-        msg = "Floating IP '%s' is used" % floating_ip.id
-        raise faults.Conflict(message=msg)
-
-    try:
-        floating_ip.network.release_address(floating_ip.ipv4)
-        floating_ip.deleted = True
-        quotas.issue_and_accept_commission(floating_ip, delete=True)
-    except:
-        transaction.rollback()
-        raise
-    else:
-        floating_ip.delete()
-        transaction.commit()
+
+    floating_ip = util.get_floating_ip_by_id(userid, floating_ip_id,
+                                             for_update=True)
+    if floating_ip.nic:
+        # This is safe, you also need for_update to attach floating IP to
+        # instance.
+        msg = "Floating IP '%s' is attached to instance." % floating_ip.id
+        raise faults.Conflict(msg)
+
+    # Return the address of the floating IP back to pool
+    floating_ip.release_address()
+    # And mark the floating IP as deleted
+    floating_ip.deleted = True
+    floating_ip.save()
+    # Release quota for floating IP
+    quotas.issue_and_accept_commission(floating_ip, delete=True)
+    transaction.commit()
+    # Delete the floating IP from DB
+    floating_ip.delete()
 
     log.info("User '%s' released IP '%s", userid, floating_ip)
 
     return HttpResponse(status=204)
 
 
-def network_to_pool(network):
-    pool = network.get_pool(locked=False)
-    return {"name": str(network.id),
-            "size": pool.pool_size,
-            "free": pool.count_available()}
-
-
+# Floating IP pools
 @api.api_method(http_method='GET', user_required=True, logger=log,
                 serializations=["json"])
 def list_floating_ip_pools(request):
-    networks = Network.objects.filter(public=True, floating_ip_pool=True)
+    networks = Network.objects.filter(public=True, floating_ip_pool=True,
+                                      deleted=False)
     networks = utils.filter_modified_since(request, objects=networks)
-    pools = map(network_to_pool, networks)
+    pools = map(network_to_floating_ip_pool, networks)
     request.serialization = "json"
     data = json.dumps({"floating_ip_pools": pools})
     request.serialization = "json"
     return HttpResponse(data, status=200)
+
+
+def network_to_floating_ip_pool(network):
+    """Convert a 'Network' object to a floating IP pool dict."""
+    total, free = network.ip_count()
+    return {"name": str(network.id),
+            "size": total,
+            "free": free}
diff --git a/snf-cyclades-app/synnefo/api/tests/__init__.py b/snf-cyclades-app/synnefo/api/tests/__init__.py
index b1e7bed..3f00e5b 100644
--- a/snf-cyclades-app/synnefo/api/tests/__init__.py
+++ b/snf-cyclades-app/synnefo/api/tests/__init__.py
@@ -40,4 +40,4 @@ from .images import *
 from .versions import *
 from .extensions import *
 from .subnets import *
-#from .floating_ips import *
+from .floating_ips import *
diff --git a/snf-cyclades-app/synnefo/api/tests/floating_ips.py b/snf-cyclades-app/synnefo/api/tests/floating_ips.py
index 1e42de2..7b50598 100644
--- a/snf-cyclades-app/synnefo/api/tests/floating_ips.py
+++ b/snf-cyclades-app/synnefo/api/tests/floating_ips.py
@@ -35,10 +35,9 @@ import json
 
 from snf_django.utils.testing import BaseAPITest, mocked_quotaholder
 from synnefo.db.models import IPAddress
-from synnefo.db.models_factory import (FloatingIPFactory, NetworkFactory,
-                                       VirtualMachineFactory,
-                                       NetworkInterfaceFactory,
-                                       BackendNetworkFactory)
+from synnefo.db import models_factory as mf
+from synnefo.db.models_factory import (NetworkFactory,
+                                       VirtualMachineFactory)
 from mock import patch, Mock
 from functools import partial
 
@@ -59,156 +58,124 @@ FloatingIPPoolFactory = partial(NetworkFactory, public=True, deleted=False,
 
 
 class FloatingIPAPITest(BaseAPITest):
+    def setUp(self):
+        self.pool = mf.NetworkWithSubnetFactory(floating_ip_pool=True,
+                                                public=True,
+                                                subnet__cidr="192.168.2.0/24",
+                                                subnet__gateway="192.168.2.1")
+
     def test_no_floating_ip(self):
         response = self.get(URL)
         self.assertSuccess(response)
         self.assertEqual(json.loads(response.content)["floating_ips"], [])
 
     def test_list_ips(self):
-        ip = FloatingIPFactory(userid="user1")
-        FloatingIPFactory(userid="user1", deleted=True)
+        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True)
         with mocked_quotaholder():
             response = self.get(URL, "user1")
         self.assertSuccess(response)
         api_ip = json.loads(response.content)["floating_ips"][0]
         self.assertEqual(api_ip,
-                         {"instance_id": str(ip.machine.id), "ip": ip.ipv4,
-                          "fixed_ip": None, "id": str(ip.id),  "pool":
-                          str(ip.network.id)})
+                         {"instance_id": str(ip.nic.machine_id),
+                          "ip": ip.address,
+                          "fixed_ip": None,
+                          "id": str(ip.id),
+                          "pool": str(ip.network_id)})
 
     def test_get_ip(self):
-        ip = FloatingIPFactory(userid="user1")
+        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True)
         with mocked_quotaholder():
             response = self.get(URL + "/%s" % ip.id, "user1")
         self.assertSuccess(response)
         api_ip = json.loads(response.content)["floating_ip"]
         self.assertEqual(api_ip,
-                         {"instance_id": str(ip.machine.id), "ip": ip.ipv4,
-                          "fixed_ip": None, "id": str(ip.id),  "pool":
-                          str(ip.network.id)})
+                         {"instance_id": str(ip.nic.machine_id),
+                          "ip": ip.address,
+                          "fixed_ip": None,
+                          "id": str(ip.id),
+                          "pool": str(ip.network_id)})
 
     def test_wrong_user(self):
-        ip = FloatingIPFactory(userid="user1")
-        with mocked_quotaholder():
-            response = self.delete(URL + "/%s" % ip.id, "user2")
+        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True)
+        response = self.delete(URL + "/%s" % ip.id, "user2")
         self.assertItemNotFound(response)
 
     def test_deleted_ip(self):
-        ip = FloatingIPFactory(userid="user1", deleted=True)
-        with mocked_quotaholder():
-            response = self.delete(URL + "/%s" % ip.id, "user1")
+        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True,
+                                   deleted=True)
+        response = self.delete(URL + "/%s" % ip.id, "user1")
         self.assertItemNotFound(response)
 
     def test_reserve(self):
-        net = FloatingIPPoolFactory(userid="test_user",
-                                    subnet="192.168.2.0/24",
-                                    gateway=None)
-        request = {'pool': net.id}
+        request = {'pool': self.pool.id}
         with mocked_quotaholder():
             response = self.post(URL, "test_user", json.dumps(request), "json")
         self.assertSuccess(response)
         ip = floating_ips.get()
-        self.assertEqual(ip.ipv4, "192.168.2.1")
-        self.assertEqual(ip.machine, None)
-        self.assertEqual(ip.network, net)
+        self.assertEqual(ip.address, "192.168.2.2")
+        self.assertEqual(ip.nic, None)
+        self.assertEqual(ip.network, self.pool)
         self.assertEqual(json.loads(response.content)["floating_ip"],
-                         {"instance_id": None, "ip": "192.168.2.1",
+                         {"instance_id": None, "ip": "192.168.2.2",
                           "fixed_ip": None, "id": str(ip.id),
-                          "pool": str(net.id)})
+                          "pool": str(self.pool.id)})
 
     def test_reserve_no_pool(self):
-        # No networks
-        with mocked_quotaholder():
-            response = self.post(URL, "test_user", json.dumps({}), "json")
-        self.assertFault(response, 413, "overLimit")
+        # No floating IP pools
+        self.pool.delete()
+        response = self.post(URL, "test_user", json.dumps({}), "json")
+        self.assertFault(response, 503, 'serviceUnavailable')
+
         # Full network
-        FloatingIPPoolFactory(userid="test_user",
-                              subnet="192.168.2.0/32",
-                              gateway=None)
-        with mocked_quotaholder():
-            response = self.post(URL, "test_user", json.dumps({}), "json")
-        self.assertFault(response, 413, "overLimit")
-        # Success
-        net2 = FloatingIPPoolFactory(userid="test_user",
-                                     subnet="192.168.2.0/24",
-                                     gateway=None)
-        with mocked_quotaholder():
-            response = self.post(URL, "test_user", json.dumps({}), "json")
-        self.assertSuccess(response)
-        ip = floating_ips.get()
-        self.assertEqual(json.loads(response.content)["floating_ip"],
-                         {"instance_id": None, "ip": "192.168.2.1",
-                          "fixed_ip": None, "id": str(ip.id),
-                          "pool": str(net2.id)})
+        pool = mf.NetworkWithSubnetFactory(floating_ip_pool=True,
+                                           public=True,
+                                           subnet__cidr="192.168.2.0/31",
+                                           subnet__gateway="192.168.2.1")
+        response = self.post(URL, "test_user", json.dumps({}), "json")
+        self.assertFault(response, 503, 'serviceUnavailable')
 
-    def test_reserve_full(self):
-        net = FloatingIPPoolFactory(userid="test_user",
-                                    subnet="192.168.2.0/32")
-        request = {'pool': net.id}
-        with mocked_quotaholder():
-            response = self.post(URL, "test_user", json.dumps(request), "json")
-        self.assertEqual(response.status_code, 413)
+        request = {'pool': pool.id}
+        response = self.post(URL, "test_user", json.dumps(request), "json")
+        self.assertConflict(response)
 
     def test_reserve_with_address(self):
-        net = FloatingIPPoolFactory(userid="test_user",
-                                    subnet="192.168.2.0/24")
-        request = {'pool': net.id, "address": "192.168.2.10"}
+        request = {'pool': self.pool.id, "address": "192.168.2.10"}
         with mocked_quotaholder():
             response = self.post(URL, "test_user", json.dumps(request), "json")
         self.assertSuccess(response)
         ip = floating_ips.get()
         self.assertEqual(json.loads(response.content)["floating_ip"],
                          {"instance_id": None, "ip": "192.168.2.10",
-                          "fixed_ip": None, "id": str(ip.id), "pool":
-                          str(net.id)})
+                          "fixed_ip": None, "id": str(ip.id),
+                          "pool": str(self.pool.id)})
 
         # Already reserved
-        FloatingIPFactory(network=net, ipv4="192.168.2.3")
-        request = {'pool': net.id, "address": "192.168.2.3"}
         with mocked_quotaholder():
             response = self.post(URL, "test_user", json.dumps(request), "json")
         self.assertFault(response, 409, "conflict")
 
-        # Already used
-        pool = net.get_pool()
-        pool.reserve("192.168.2.5")
-        pool.save()
-        # ..by another_user
-        nic = NetworkInterfaceFactory(network=net, ipv4="192.168.2.5",
-                                      machine__userid="test2")
-        request = {'pool': net.id, "address": "192.168.2.5"}
+        # Used by instance
+        self.pool.reserve_address("192.168.2.20")
+        request = {'pool': self.pool.id, "address": "192.168.2.20"}
         with mocked_quotaholder():
             response = self.post(URL, "test_user", json.dumps(request), "json")
         self.assertFault(response, 409, "conflict")
-        # ..and by him
-        nic.delete()
-        NetworkInterfaceFactory(network=net, ipv4="192.168.2.5",
-                                machine__userid="test_user")
-        request = {'pool': net.id, "address": "192.168.2.5"}
-        with mocked_quotaholder():
-            response = self.post(URL, "test_user", json.dumps(request), "json")
-        self.assertSuccess(response)
 
         # Address out of pool
-        request = {'pool': net.id, "address": "192.168.3.5"}
+        request = {'pool': self.pool.id, "address": "192.168.3.5"}
         with mocked_quotaholder():
             response = self.post(URL, "test_user", json.dumps(request), "json")
         self.assertBadRequest(response)
 
     def test_release_in_use(self):
-        ip = FloatingIPFactory()
-        vm = ip.machine
-        vm.operstate = "ACTIVE"
-        vm.userid = ip.userid
-        vm.save()
-        vm.nics.create(index=0, ipv4=ip.ipv4, network=ip.network,
-                       state="ACTIVE")
+        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True)
+        vm = ip.nic.machine
         with mocked_quotaholder():
             response = self.delete(URL + "/%s" % ip.id, ip.userid)
         self.assertFault(response, 409, "conflict")
         # Also send a notification to remove the NIC and assert that FIP is in
         # use until notification from ganeti arrives
-        request = {"removeFloatingIp": {"address": ip.ipv4}}
+        request = {"removeFloatingIp": {"address": ip.address}}
         url = SERVERS_URL + "/%s/action" % vm.id
         with patch('synnefo.logic.rapi_pool.GanetiRapiClient') as c:
             c().ModifyInstance.return_value = 10
@@ -220,7 +187,7 @@ class FloatingIPAPITest(BaseAPITest):
         self.assertFault(response, 409, "conflict")
 
     def test_release(self):
-        ip = FloatingIPFactory(machine=None)
+        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True, nic=None)
         with mocked_quotaholder():
             response = self.delete(URL + "/%s" % ip.id, ip.userid)
         self.assertSuccess(response)
@@ -229,19 +196,23 @@ class FloatingIPAPITest(BaseAPITest):
 
     @patch("synnefo.logic.backend", Mock())
     def test_delete_network_with_floating_ips(self):
-        ip = FloatingIPFactory(machine=None, network__flavor="IP_LESS_ROUTED")
-        net = ip.network
+        ip = mf.IPv4AddressFactory(userid="user1", floating_ip=True,
+                                   network=self.pool, nic=None)
+        # Mark the network as non-pubic to not get 403
+        network = ip.network
+        network.public = False
+        network.save()
         # Can not remove network with floating IPs
         with mocked_quotaholder():
-            response = self.delete(NETWORKS_URL + "/%s" % net.id,
-                                   net.userid)
-        self.assertFault(response, 421, "networkInUse")
+            response = self.delete(NETWORKS_URL + "/%s" % self.pool.id,
+                                   self.pool.userid)
+        self.assertConflict(response)
         # But we can with only deleted Floating Ips
         ip.deleted = True
         ip.save()
         with mocked_quotaholder():
-            response = self.delete(NETWORKS_URL + "/%s" % net.id,
-                                   net.userid)
+            response = self.delete(NETWORKS_URL + "/%s" % self.pool.id,
+                                   self.pool.userid)
         self.assertSuccess(response)
 
 
@@ -255,11 +226,13 @@ class FloatingIPPoolsAPITest(BaseAPITest):
         self.assertEqual(json.loads(response.content)["floating_ip_pools"], [])
 
     def test_list_pools(self):
-        net = FloatingIPPoolFactory(subnet="192.168.0.0/30",
-                                    gateway="192.168.0.1")
-        NetworkFactory(public=True, deleted=True)
-        NetworkFactory(public=False, deleted=False)
-        NetworkFactory(public=True, deleted=False)
+        net = mf.NetworkWithSubnetFactory(floating_ip_pool=True,
+                                          public=True,
+                                          subnet__cidr="192.168.2.0/30",
+                                          subnet__gateway="192.168.2.1")
+        mf.NetworkWithSubnetFactory(public=True, deleted=True)
+        mf.NetworkWithSubnetFactory(public=False, deleted=False)
+        mf.NetworkWithSubnetFactory(public=True, floating_ip_pool=False)
         response = self.get(POOLS_URL)
         self.assertSuccess(response)
         self.assertEqual(json.loads(response.content)["floating_ip_pools"],
@@ -268,10 +241,9 @@ class FloatingIPPoolsAPITest(BaseAPITest):
 
 class FloatingIPActionsTest(BaseAPITest):
     def setUp(self):
-        vm = VirtualMachineFactory()
-        vm.operstate = "ACTIVE"
-        vm.save()
-        self.vm = vm
+        self.vm = VirtualMachineFactory()
+        self.vm.operstate = "ACTIVE"
+        self.vm.save()
 
     def test_bad_request(self):
         url = SERVERS_URL + "/%s/action" % self.vm.id
@@ -290,25 +262,20 @@ class FloatingIPActionsTest(BaseAPITest):
         response = self.post(url, self.vm.userid, json.dumps(request), "json")
         self.assertItemNotFound(response)
         # In use
-        vm1 = VirtualMachineFactory()
-        ip1 = FloatingIPFactory(userid=self.vm.userid, machine=vm1)
-        BackendNetworkFactory(network=ip1.network, backend=vm1.backend,
-                              operstate='ACTIVE')
-        request = {"addFloatingIp": {"address": ip1.ipv4}}
+        ip = mf.IPv4AddressFactory(floating_ip=True, userid=self.vm.userid)
+        request = {"addFloatingIp": {"address": ip.address}}
         response = self.post(url, self.vm.userid, json.dumps(request), "json")
-        self.assertFault(response, 409, "conflict")
+        self.assertConflict(response)
         # Success
-        ip1 = FloatingIPFactory(userid=self.vm.userid, machine=None)
-        BackendNetworkFactory(network=ip1.network, backend=self.vm.backend,
-                              operstate='ACTIVE')
-        request = {"addFloatingIp": {"address": ip1.ipv4}}
+        ip = mf.IPv4AddressFactory(floating_ip=True, nic=None,
+                                   userid=self.vm.userid)
+        request = {"addFloatingIp": {"address": ip.address}}
         mock().ModifyInstance.return_value = 1
         response = self.post(url, self.vm.userid, json.dumps(request), "json")
         self.assertEqual(response.status_code, 202)
-        ip1_after = floating_ips.get(id=ip1.id)
-        self.assertEqual(ip1_after.machine, self.vm)
-        self.assertTrue(ip1_after.in_use())
-        nic = self.vm.nics.get(ipv4=ip1_after.ipv4)
+        ip_after = floating_ips.get(id=ip.id)
+        self.assertEqual(ip_after.nic.machine, self.vm)
+        nic = self.vm.nics.get()
         nic.state = "ACTIVE"
         nic.save()
         response = self.get(SERVERS_URL + "/%s" % self.vm.id,
@@ -323,20 +290,20 @@ class FloatingIPActionsTest(BaseAPITest):
         url = SERVERS_URL + "/%s/action" % self.vm.id
         request = {"removeFloatingIp": {"address": "10.0.0.1"}}
         response = self.post(url, self.vm.userid, json.dumps(request), "json")
-        self.assertItemNotFound(response)
+        self.assertBadRequest(response)
         # Not In Use
-        ip1 = FloatingIPFactory(userid=self.vm.userid, machine=None)
-        request = {"removeFloatingIp": {"address": ip1.ipv4}}
+        ip = mf.IPv4AddressFactory(floating_ip=True, nic=None,
+                                   userid=self.vm.userid)
+        request = {"removeFloatingIp": {"address": ip.address}}
         response = self.post(url, self.vm.userid, json.dumps(request), "json")
-        self.assertItemNotFound(response)
+        self.assertBadRequest(response)
         # Success
-        ip1 = FloatingIPFactory(userid=self.vm.userid, machine=self.vm)
-        NetworkInterfaceFactory(machine=self.vm, ipv4=ip1.ipv4)
-        request = {"removeFloatingIp": {"address": ip1.ipv4}}
+        ip = mf.IPv4AddressFactory(floating_ip=True,
+                                   userid=self.vm.userid, nic__machine=self.vm)
+        request = {"removeFloatingIp": {"address": ip.address}}
         mock().ModifyInstance.return_value = 2
         response = self.post(url, self.vm.userid, json.dumps(request), "json")
         self.assertEqual(response.status_code, 202)
         # Yet used. Wait for the callbacks
-        ip1_after = floating_ips.get(id=ip1.id)
-        self.assertEqual(ip1_after.machine, self.vm)
-        self.assertTrue(ip1_after.in_use())
+        ip_after = floating_ips.get(id=ip.id)
+        self.assertEqual(ip_after.nic.machine, self.vm)
diff --git a/snf-django-lib/snf_django/utils/testing.py b/snf-django-lib/snf_django/utils/testing.py
index cc36a6b..f1941f0 100644
--- a/snf-django-lib/snf_django/utils/testing.py
+++ b/snf-django-lib/snf_django/utils/testing.py
@@ -237,6 +237,9 @@ class BaseAPITest(TestCase):
     def assertBadRequest(self, response):
         self.assertFault(response, 400, 'badRequest')
 
+    def assertConflict(self, response):
+        self.assertFault(response, 409, 'conflict')
+
     def assertItemNotFound(self, response):
         self.assertFault(response, 404, 'itemNotFound')
 
