commit 92d2d1ce9ea13c5d2a32e797f6da396f8e02049f
Author: Christos Stavrakakis <cstavr@grnet.gr>
Date:   Thu Oct 10 01:53:23 2013 +0300

    cyclades: Make API server tests pass
    
    Refactor Cyclades code so that it follows the new models for Networks,
    Subnets and IPAddresses. This refactor is not complete. Instead is the
    necessary refactor for making tests for server API calls succeed.

diff --git a/snf-cyclades-app/synnefo/api/floating_ips.py b/snf-cyclades-app/synnefo/api/floating_ips.py
index b01feb7..2f1f947 100644
--- a/snf-cyclades-app/synnefo/api/floating_ips.py
+++ b/snf-cyclades-app/synnefo/api/floating_ips.py
@@ -234,7 +234,7 @@ def release_floating_ip(request, floating_ip_id):
 
 
 def network_to_pool(network):
-    pool = network.get_pool(with_lock=False)
+    pool = network.get_pool(locked=False)
     return {"name": str(network.id),
             "size": pool.pool_size,
             "free": pool.count_available()}
diff --git a/snf-cyclades-app/synnefo/api/servers.py b/snf-cyclades-app/synnefo/api/servers.py
index 4fa8bd3..340a313 100644
--- a/snf-cyclades-app/synnefo/api/servers.py
+++ b/snf-cyclades-app/synnefo/api/servers.py
@@ -43,6 +43,7 @@ from snf_django.lib import api
 from snf_django.lib.api import faults, utils
 
 from synnefo.api import util
+from synnefo.db import query as db_query
 from synnefo.db.models import (VirtualMachine, VirtualMachineMetadata)
 from synnefo.logic import servers, utils as logic_utils
 
@@ -108,9 +109,16 @@ def nic_to_dict(nic):
     d = {'id': nic.id,
          'network_id': str(nic.network.id),
          'mac_address': nic.mac,
-         'ipv4': nic.ipv4,
-         'ipv6': nic.ipv6,
-         'OS-EXT-IPS:type': nic.ip_type.lower()}
+         'ipv4': '',
+         'ipv6': ''}
+    for ip in nic.ips.filter(deleted=False).select_related("subnet"):
+        ip_type = "floating" if ip.floating_ip else "fixed"
+        if ip.subnet.ipversion == 4:
+            d["ipv4"] = ip.address
+            d["OS-EXT-IPS:type"] = ip_type
+        else:
+            d["ipv6"] = ip.address
+            d["OS-EXT-IPS:type"] = ip_type
 
     if nic.firewall_profile:
         d['firewallProfile'] = nic.firewall_profile
@@ -121,9 +129,10 @@ def attachments_to_addresses(attachments):
     addresses = {}
     for nic in attachments:
         net_nics = []
-        net_nics.append({"version": 4,
-                         "addr": nic["ipv4"],
-                         "OS-EXT-IPS:type": nic["OS-EXT-IPS:type"]})
+        if nic["ipv4"]:
+            net_nics.append({"version": 4,
+                             "addr": nic["ipv4"],
+                             "OS-EXT-IPS:type": nic["OS-EXT-IPS:type"]})
         if nic["ipv6"]:
             net_nics.append({"version": 6,
                              "addr": nic["ipv6"],
@@ -180,16 +189,15 @@ def vm_to_dict(vm, detail=False):
 def get_server_fqdn(vm):
     fqdn_setting = settings.CYCLADES_SERVERS_FQDN
     if fqdn_setting is None:
-        public_nics = vm.nics.filter(network__public=True, state="ACTIVE")
         # Return the first public IPv4 address if exists
-        ipv4_nics = public_nics.exclude(ipv4=None)
-        if ipv4_nics:
-            return ipv4_nics[0].ipv4
-        # Else return the first public IPv6 address if exists
-        ipv6_nics = public_nics.exclude(ipv6=None)
-        if ipv6_nics:
-            return ipv6_nics[0].ipv6
-        return ""
+        address = db_query.get_server_public_ip(server=vm, version=4)
+        if address is None:
+            # Else return the first public IPv6 address if exists
+            address = db_query.get_server_public_ip(server=vm, version=6)
+        if address is None:
+            return ""
+        else:
+            return address
     elif isinstance(fqdn_setting, basestring):
         return fqdn_setting % {"id": vm.id}
     else:
@@ -214,13 +222,8 @@ def get_server_port_forwarding(vm, fqdn):
     port_forwarding = {}
     for dport, to_dest in settings.CYCLADES_PORT_FORWARDING.items():
         if hasattr(to_dest, "__call__"):
-            public_nics = vm.nics.filter(network__public=True, state="ACTIVE")\
-                                 .exclude(ipv4=None).order_by('index')
-            if public_nics:
-                vm_ipv4 = public_nics[0].ipv4
-            else:
-                vm_ipv4 = None
-            to_dest = to_dest(vm_ipv4, vm.id, fqdn, vm.userid)
+            address = db_query.get_server_public_ip(server=vm, version=4)
+            to_dest = to_dest(address, vm.id, fqdn, vm.userid)
         msg = ("Invalid setting: CYCLADES_PORT_FOWARDING."
                " Value must be a tuple of two elements (host, port).")
         if to_dest is None:
diff --git a/snf-cyclades-app/synnefo/api/tests/floating_ips.py b/snf-cyclades-app/synnefo/api/tests/floating_ips.py
index 2f43ed7..1e42de2 100644
--- a/snf-cyclades-app/synnefo/api/tests/floating_ips.py
+++ b/snf-cyclades-app/synnefo/api/tests/floating_ips.py
@@ -35,7 +35,7 @@ import json
 
 from snf_django.utils.testing import BaseAPITest, mocked_quotaholder
 from synnefo.db.models import IPAddress
-from synnefo.db.models_factory import (IPAddressFactory, NetworkFactory,
+from synnefo.db.models_factory import (FloatingIPFactory, NetworkFactory,
                                        VirtualMachineFactory,
                                        NetworkInterfaceFactory,
                                        BackendNetworkFactory)
@@ -52,19 +52,21 @@ URL = join_urls(compute_path, "os-floating-ips")
 NETWORKS_URL = join_urls(compute_path, "networks")
 SERVERS_URL = join_urls(compute_path, "servers")
 
-IPAddressPoolFactory = partial(NetworkFactory, public=True, deleted=False,
+
+floating_ips = IPAddress.objects.filter(floating_ip=True)
+FloatingIPPoolFactory = partial(NetworkFactory, public=True, deleted=False,
                                 floating_ip_pool=True)
 
 
-class IPAddressAPITest(BaseAPITest):
+class FloatingIPAPITest(BaseAPITest):
     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 = IPAddressFactory(userid="user1")
-        IPAddressFactory(userid="user1", deleted=True)
+        ip = FloatingIPFactory(userid="user1")
+        FloatingIPFactory(userid="user1", deleted=True)
         with mocked_quotaholder():
             response = self.get(URL, "user1")
         self.assertSuccess(response)
@@ -75,7 +77,7 @@ class IPAddressAPITest(BaseAPITest):
                           str(ip.network.id)})
 
     def test_get_ip(self):
-        ip = IPAddressFactory(userid="user1")
+        ip = FloatingIPFactory(userid="user1")
         with mocked_quotaholder():
             response = self.get(URL + "/%s" % ip.id, "user1")
         self.assertSuccess(response)
@@ -86,26 +88,26 @@ class IPAddressAPITest(BaseAPITest):
                           str(ip.network.id)})
 
     def test_wrong_user(self):
-        ip = IPAddressFactory(userid="user1")
+        ip = FloatingIPFactory(userid="user1")
         with mocked_quotaholder():
             response = self.delete(URL + "/%s" % ip.id, "user2")
         self.assertItemNotFound(response)
 
     def test_deleted_ip(self):
-        ip = IPAddressFactory(userid="user1", deleted=True)
+        ip = FloatingIPFactory(userid="user1", deleted=True)
         with mocked_quotaholder():
             response = self.delete(URL + "/%s" % ip.id, "user1")
         self.assertItemNotFound(response)
 
     def test_reserve(self):
-        net = IPAddressPoolFactory(userid="test_user",
+        net = FloatingIPPoolFactory(userid="test_user",
                                     subnet="192.168.2.0/24",
                                     gateway=None)
         request = {'pool': net.id}
         with mocked_quotaholder():
             response = self.post(URL, "test_user", json.dumps(request), "json")
         self.assertSuccess(response)
-        ip = IPAddress.objects.get()
+        ip = floating_ips.get()
         self.assertEqual(ip.ipv4, "192.168.2.1")
         self.assertEqual(ip.machine, None)
         self.assertEqual(ip.network, net)
@@ -120,27 +122,27 @@ class IPAddressAPITest(BaseAPITest):
             response = self.post(URL, "test_user", json.dumps({}), "json")
         self.assertFault(response, 413, "overLimit")
         # Full network
-        IPAddressPoolFactory(userid="test_user",
+        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 = IPAddressPoolFactory(userid="test_user",
+        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 = IPAddress.objects.get()
+        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)})
 
     def test_reserve_full(self):
-        net = IPAddressPoolFactory(userid="test_user",
+        net = FloatingIPPoolFactory(userid="test_user",
                                     subnet="192.168.2.0/32")
         request = {'pool': net.id}
         with mocked_quotaholder():
@@ -148,20 +150,20 @@ class IPAddressAPITest(BaseAPITest):
         self.assertEqual(response.status_code, 413)
 
     def test_reserve_with_address(self):
-        net = IPAddressPoolFactory(userid="test_user",
+        net = FloatingIPPoolFactory(userid="test_user",
                                     subnet="192.168.2.0/24")
         request = {'pool': net.id, "address": "192.168.2.10"}
         with mocked_quotaholder():
             response = self.post(URL, "test_user", json.dumps(request), "json")
         self.assertSuccess(response)
-        ip = IPAddress.objects.get()
+        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)})
 
         # Already reserved
-        IPAddressFactory(network=net, ipv4="192.168.2.3")
+        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")
@@ -194,7 +196,7 @@ class IPAddressAPITest(BaseAPITest):
         self.assertBadRequest(response)
 
     def test_release_in_use(self):
-        ip = IPAddressFactory()
+        ip = FloatingIPFactory()
         vm = ip.machine
         vm.operstate = "ACTIVE"
         vm.userid = ip.userid
@@ -218,16 +220,16 @@ class IPAddressAPITest(BaseAPITest):
         self.assertFault(response, 409, "conflict")
 
     def test_release(self):
-        ip = IPAddressFactory(machine=None)
+        ip = FloatingIPFactory(machine=None)
         with mocked_quotaholder():
             response = self.delete(URL + "/%s" % ip.id, ip.userid)
         self.assertSuccess(response)
-        ips_after = IPAddress.objects.filter(id=ip.id)
+        ips_after = floating_ips.filter(id=ip.id)
         self.assertEqual(len(ips_after), 0)
 
     @patch("synnefo.logic.backend", Mock())
     def test_delete_network_with_floating_ips(self):
-        ip = IPAddressFactory(machine=None, network__flavor="IP_LESS_ROUTED")
+        ip = FloatingIPFactory(machine=None, network__flavor="IP_LESS_ROUTED")
         net = ip.network
         # Can not remove network with floating IPs
         with mocked_quotaholder():
@@ -246,14 +248,14 @@ class IPAddressAPITest(BaseAPITest):
 POOLS_URL = join_urls(compute_path, "os-floating-ip-pools")
 
 
-class IPAddressPoolsAPITest(BaseAPITest):
+class FloatingIPPoolsAPITest(BaseAPITest):
     def test_no_pool(self):
         response = self.get(POOLS_URL)
         self.assertSuccess(response)
         self.assertEqual(json.loads(response.content)["floating_ip_pools"], [])
 
     def test_list_pools(self):
-        net = IPAddressPoolFactory(subnet="192.168.0.0/30",
+        net = FloatingIPPoolFactory(subnet="192.168.0.0/30",
                                     gateway="192.168.0.1")
         NetworkFactory(public=True, deleted=True)
         NetworkFactory(public=False, deleted=False)
@@ -264,7 +266,7 @@ class IPAddressPoolsAPITest(BaseAPITest):
                          [{"name": str(net.id), "size": 4, "free": 1}])
 
 
-class IPAddressActionsTest(BaseAPITest):
+class FloatingIPActionsTest(BaseAPITest):
     def setUp(self):
         vm = VirtualMachineFactory()
         vm.operstate = "ACTIVE"
@@ -289,21 +291,21 @@ class IPAddressActionsTest(BaseAPITest):
         self.assertItemNotFound(response)
         # In use
         vm1 = VirtualMachineFactory()
-        ip1 = IPAddressFactory(userid=self.vm.userid, machine=vm1)
+        ip1 = FloatingIPFactory(userid=self.vm.userid, machine=vm1)
         BackendNetworkFactory(network=ip1.network, backend=vm1.backend,
                               operstate='ACTIVE')
         request = {"addFloatingIp": {"address": ip1.ipv4}}
         response = self.post(url, self.vm.userid, json.dumps(request), "json")
         self.assertFault(response, 409, "conflict")
         # Success
-        ip1 = IPAddressFactory(userid=self.vm.userid, machine=None)
+        ip1 = FloatingIPFactory(userid=self.vm.userid, machine=None)
         BackendNetworkFactory(network=ip1.network, backend=self.vm.backend,
                               operstate='ACTIVE')
         request = {"addFloatingIp": {"address": ip1.ipv4}}
         mock().ModifyInstance.return_value = 1
         response = self.post(url, self.vm.userid, json.dumps(request), "json")
         self.assertEqual(response.status_code, 202)
-        ip1_after = IPAddress.objects.get(id=ip1.id)
+        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)
@@ -323,18 +325,18 @@ class IPAddressActionsTest(BaseAPITest):
         response = self.post(url, self.vm.userid, json.dumps(request), "json")
         self.assertItemNotFound(response)
         # Not In Use
-        ip1 = IPAddressFactory(userid=self.vm.userid, machine=None)
+        ip1 = FloatingIPFactory(userid=self.vm.userid, machine=None)
         request = {"removeFloatingIp": {"address": ip1.ipv4}}
         response = self.post(url, self.vm.userid, json.dumps(request), "json")
         self.assertItemNotFound(response)
         # Success
-        ip1 = IPAddressFactory(userid=self.vm.userid, machine=self.vm)
+        ip1 = FloatingIPFactory(userid=self.vm.userid, machine=self.vm)
         NetworkInterfaceFactory(machine=self.vm, ipv4=ip1.ipv4)
         request = {"removeFloatingIp": {"address": ip1.ipv4}}
         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 = IPAddress.objects.get(id=ip1.id)
+        ip1_after = floating_ips.get(id=ip1.id)
         self.assertEqual(ip1_after.machine, self.vm)
         self.assertTrue(ip1_after.in_use())
diff --git a/snf-cyclades-app/synnefo/api/tests/servers.py b/snf-cyclades-app/synnefo/api/tests/servers.py
index e0d8e6e..2c99899 100644
--- a/snf-cyclades-app/synnefo/api/tests/servers.py
+++ b/snf-cyclades-app/synnefo/api/tests/servers.py
@@ -122,9 +122,12 @@ class ServerAPITest(ComputeAPITest):
         """Test if a server details are returned."""
         db_vm = self.vm2
         user = self.vm2.userid
-        net = mfactory.NetworkFactory()
-        nic = mfactory.NetworkInterfaceFactory(machine=self.vm2, network=net,
-                                               ipv6="::babe")
+        ip4 = mfactory.IPv4AddressFactory(nic__machine=self.vm2)
+        nic = ip4.nic
+        net = ip4.network
+        ip6 = mfactory.IPv6AddressFactory(nic=nic, network=net)
+        nic.mac = "aa:00:11:22:33:44"
+        nic.save()
 
         db_vm_meta = mfactory.VirtualMachineMetadataFactory(vm=db_vm)
 
@@ -141,14 +144,14 @@ class ServerAPITest(ComputeAPITest):
         self.assertEqual(api_nic['network_id'], str(net.id))
         self.assertEqual(api_nic['mac_address'], nic.mac)
         self.assertEqual(api_nic['firewallProfile'], nic.firewall_profile)
-        self.assertEqual(api_nic['ipv4'], nic.ipv4)
-        self.assertEqual(api_nic['ipv6'], nic.ipv6)
+        self.assertEqual(api_nic['ipv4'], ip4.address)
+        self.assertEqual(api_nic['ipv6'], ip6.address)
         self.assertEqual(api_nic['OS-EXT-IPS:type'], "fixed")
         self.assertEqual(api_nic['id'], nic.id)
         api_address = server["addresses"]
         self.assertEqual(api_address[str(net.id)], [
-            {"version": 4, "addr": nic.ipv4, "OS-EXT-IPS:type": "fixed"},
-            {"version": 6, "addr": nic.ipv6, "OS-EXT-IPS:type": "fixed"}
+            {"version": 4, "addr": ip4.address, "OS-EXT-IPS:type": "fixed"},
+            {"version": 6, "addr": ip6.address, "OS-EXT-IPS:type": "fixed"}
         ])
 
         metadata = server['metadata']
@@ -184,24 +187,22 @@ class ServerAPITest(ComputeAPITest):
             self.assertEqual(server["SNF:fqdn"], "")
 
         # IPv6 NIC
-        nic = mfactory.NetworkInterfaceFactory(machine=vm, ipv4=None,
-                                               ipv6="babe::", state="ACTIVE",
-                                               network__public=True)
+        ipv6_address = mfactory.IPv6AddressFactory(nic__machine=vm,
+                                                   network__public=True)
         with override_settings(settings,
                                CYCLADES_SERVERS_FQDN=None):
             response = self.myget("servers/%d" % vm.id, vm.userid)
             server = json.loads(response.content)['server']
-            self.assertEqual(server["SNF:fqdn"], nic.ipv6)
+            self.assertEqual(server["SNF:fqdn"], ipv6_address.address)
 
         # IPv4 NIC
-        nic = mfactory.NetworkInterfaceFactory(machine=vm,
-                                               network__public=True,
-                                               state="ACTIVE")
+        ipv4_address = mfactory.IPv4AddressFactory(nic__machine=vm,
+                                                   network__public=True)
         with override_settings(settings,
                                CYCLADES_SERVERS_FQDN=None):
             response = self.myget("servers/%d" % vm.id, vm.userid)
             server = json.loads(response.content)['server']
-            self.assertEqual(server["SNF:fqdn"], nic.ipv4)
+            self.assertEqual(server["SNF:fqdn"], ipv4_address.address)
 
     def test_server_port_forwarding(self):
         vm = mfactory.VirtualMachineFactory()
@@ -229,8 +230,9 @@ class ServerAPITest(ComputeAPITest):
             server = json.loads(response.content)['server']
             self.assertEqual(server["SNF:port_forwarding"], {})
 
-        mfactory.NetworkInterfaceFactory(machine=vm, ipv4="192.168.2.2",
-                                         network__public=True)
+        mfactory.IPv4AddressFactory(nic__machine=vm,
+                                    network__public=True,
+                                    address="192.168.2.2")
         with override_settings(settings,
                                CYCLADES_PORT_FORWARDING=ports):
             response = self.myget("servers/%d" % vm.id, vm.userid)
@@ -335,7 +337,8 @@ class ServerCreateAPITest(ComputeAPITest):
     def setUp(self):
         self.flavor = mfactory.FlavorFactory()
         # Create public network and backend
-        self.network = mfactory.NetworkFactory(public=True)
+        subnet = mfactory.IPv4SubnetFactory(network__public=True)
+        self.network = subnet.network
         self.backend = mfactory.BackendFactory()
         mfactory.BackendNetworkFactory(network=self.network,
                                        backend=self.backend,
@@ -386,16 +389,23 @@ class ServerCreateAPITest(ComputeAPITest):
 
     def test_create_network_settings(self, mrapi):
         mrapi().CreateInstance.return_value = 12
-        bnet1 = mfactory.BackendNetworkFactory(operstate="ACTIVE",
-                                               backend=self.backend)
-        bnet2 = mfactory.BackendNetworkFactory(operstate="ACTIVE",
-                                               backend=self.backend)
-        bnet3 = mfactory.BackendNetworkFactory(network__userid="test_user",
-                                               operstate="ACTIVE",
-                                               backend=self.backend)
-        bnet4 = mfactory.BackendNetworkFactory(network__userid="test_user",
-                                               operstate="ACTIVE",
-                                               backend=self.backend)
+        # Create public network and backend
+        subnet1 = mfactory.IPv4SubnetFactory()
+        bnet1 = mfactory.BackendNetworkFactory(network=subnet1.network,
+                                               backend=self.backend,
+                                               operstate="ACTIVE")
+        subnet2 = mfactory.IPv4SubnetFactory()
+        bnet2 = mfactory.BackendNetworkFactory(network=subnet2.network,
+                                               backend=self.backend,
+                                               operstate="ACTIVE")
+        subnet3 = mfactory.IPv4SubnetFactory(network__userid="test_user")
+        bnet3 = mfactory.BackendNetworkFactory(network=subnet3.network,
+                                               backend=self.backend,
+                                               operstate="ACTIVE")
+        subnet4 = mfactory.IPv4SubnetFactory(network__userid="test_user")
+        bnet4 = mfactory.BackendNetworkFactory(network=subnet4.network,
+                                               backend=self.backend,
+                                               operstate="ACTIVE")
         # User requested private networks
         request = deepcopy(self.request)
         request["server"]["networks"] = [bnet3.network.id, bnet4.network.id]
@@ -464,17 +474,13 @@ class ServerCreateAPITest(ComputeAPITest):
         # Test floating IPs
         request = deepcopy(self.request)
         request["server"]["networks"] = [bnet4.network.id]
-        network = mfactory.NetworkFactory(subnet="10.0.0.0/24")
-        mfactory.BackendNetworkFactory(network=network,
-                                       backend=self.backend,
-                                       operstate="ACTIVE")
-        fp1 = mfactory.IPAddressFactory(ipv4="10.0.0.2",
+        fp1 = mfactory.FloatingIPFactory(address="10.0.0.2",
                                          userid="test_user",
-                                         network=network, machine=None)
-        fp2 = mfactory.IPAddressFactory(ipv4="10.0.0.3", network=network,
+                                         nic=None)
+        fp2 = mfactory.FloatingIPFactory(address="10.0.0.3",
                                          userid="test_user",
-                                         machine=None)
-        request["server"]["floating_ips"] = [fp1.ipv4, fp2.ipv4]
+                                         nic=None)
+        request["server"]["floating_ips"] = [fp1.address, fp2.address]
         with override_settings(settings,
                                DEFAULT_INSTANCE_NETWORKS=[bnet3.network.id]):
             with mocked_quotaholder():
@@ -483,18 +489,18 @@ class ServerCreateAPITest(ComputeAPITest):
         self.assertEqual(response.status_code, 202)
         api_server = json.loads(response.content)['server']
         vm = VirtualMachine.objects.get(id=api_server["id"])
-        fp1 = IPAddress.objects.get(id=fp1.id)
-        fp2 = IPAddress.objects.get(id=fp2.id)
-        self.assertEqual(fp1.machine, vm)
-        self.assertEqual(fp2.machine, vm)
+        fp1 = IPAddress.objects.get(floating_ip=True, id=fp1.id)
+        fp2 = IPAddress.objects.get(floating_ip=True, id=fp2.id)
+        self.assertEqual(fp1.nic.machine, vm)
+        self.assertEqual(fp2.nic.machine, vm)
         name, args, kwargs = mrapi().CreateInstance.mock_calls[2]
         self.assertEqual(len(kwargs["nics"]), 4)
         self.assertEqual(kwargs["nics"][0]["network"],
                          bnet3.network.backend_id)
-        self.assertEqual(kwargs["nics"][1]["network"], network.backend_id)
-        self.assertEqual(kwargs["nics"][1]["ip"], fp1.ipv4)
-        self.assertEqual(kwargs["nics"][2]["network"], network.backend_id)
-        self.assertEqual(kwargs["nics"][2]["ip"], fp2.ipv4)
+        self.assertEqual(kwargs["nics"][1]["network"], fp1.network.backend_id)
+        self.assertEqual(kwargs["nics"][1]["ip"], fp1.address)
+        self.assertEqual(kwargs["nics"][2]["network"], fp2.network.backend_id)
+        self.assertEqual(kwargs["nics"][2]["ip"], fp2.address)
         self.assertEqual(kwargs["nics"][3]["network"],
                          bnet4.network.backend_id)
 
@@ -510,10 +516,6 @@ class ServerCreateAPITest(ComputeAPITest):
         """Test if the create server call returns the expected response
            if a valid request has been speficied."""
         mrapi().CreateInstance.side_effect = GanetiApiError("..ganeti is down")
-        # Create public network and backend
-        network = mfactory.NetworkFactory(public=True)
-        backend = mfactory.BackendFactory()
-        mfactory.BackendNetworkFactory(network=network, backend=backend)
 
         request = self.request
         with mocked_quotaholder():
diff --git a/snf-cyclades-app/synnefo/api/util.py b/snf-cyclades-app/synnefo/api/util.py
index 21585ed..d45bbd6 100644
--- a/snf-cyclades-app/synnefo/api/util.py
+++ b/snf-cyclades-app/synnefo/api/util.py
@@ -234,14 +234,14 @@ def get_floating_ip(user_id, ipv4, for_update=False):
         raise faults.ItemNotFound("Floating IP does not exist.")
 
 
-def allocate_public_address(backend):
+def allocate_public_address(backend, userid):
     """Get a public IP for any available network of a backend."""
     # Guarantee exclusive access to backend, because accessing the IP pools of
     # the backend networks may result in a deadlock with backend allocator
     # which also checks that backend networks have a free IP.
     backend = Backend.objects.select_for_update().get(id=backend.id)
     public_networks = backend_public_networks(backend)
-    return get_free_ip(public_networks)
+    return get_free_ip(public_networks, userid)
 
 
 def backend_public_networks(backend):
@@ -255,16 +255,14 @@ def backend_public_networks(backend):
                                           network__public=True,
                                           network__deleted=False,
                                           network__floating_ip_pool=False,
-                                          network__subnet__isnull=False,
                                           network__drained=False)
     return [b.network for b in bnets]
 
 
-def get_free_ip(networks):
+def get_free_ip(networks, userid):
     for network in networks:
         try:
-            address = get_network_free_address(network)
-            return network, address
+            return network.allocate_address(userid=userid)
         except faults.OverLimit:
             pass
     msg = "Can not allocate public IP. Public networks are full."
@@ -272,16 +270,13 @@ def get_free_ip(networks):
     raise faults.OverLimit(msg)
 
 
-def get_network_free_address(network):
+def get_network_free_address(network, userid):
     """Reserve an IP address from the IP Pool of the network."""
 
-    pool = network.get_pool()
     try:
-        address = pool.get()
+        return network.allocate_address(userid=userid)
     except EmptyPool:
         raise faults.OverLimit("Network %s is full." % network.backend_id)
-    pool.save()
-    return address
 
 
 def get_vm_nic(vm, nic_id):
diff --git a/snf-cyclades-app/synnefo/db/models.py b/snf-cyclades-app/synnefo/db/models.py
index 179682f..47d1c45 100644
--- a/snf-cyclades-app/synnefo/db/models.py
+++ b/snf-cyclades-app/synnefo/db/models.py
@@ -531,22 +531,19 @@ class Network(models.Model):
             if not backend_exists:
                 BackendNetwork.objects.create(backend=backend, network=self)
 
-    def get_pool(self, with_lock=True):
+    def get_pool(self, locked=True):
         try:
             subnet = self.subnets.get(ipversion=4, deleted=False)
         except Subnet.DoesNotExist:
             raise pools.EmptyPool
+        return subnet.get_pool(locked=locked)
+
+    def allocate_address(self, userid):
         try:
-            pool = subnet.ip_pools.all()[0]
-        except IndexError:
-            pool = IPPoolTable.objects.create(available_map='',
-                                              reserved_map='',
-                                              size=0,
-                                              subnet=subnet)
-        objects = IPPoolTable.objects
-        if with_lock:
-            objects = objects.select_for_update()
-        return objects.get(id=pool.id)
+            subnet = self.subnets.get(ipversion=4, deleted=False)
+        except Subnet.DoesNotExist:
+            raise pools.EmptyPool
+        return subnet.allocate_address(userid)
 
     def reserve_address(self, address):
         pool = self.get_pool()
@@ -601,6 +598,21 @@ class Subnet(models.Model):
     def __unicode__(self):
         return "<Subnet %s, Network: %s>" % (self.id, self.network_id)
 
+    def get_pool(self, locked=True):
+        if self.ipversion == 6:
+            raise Exception("IPv6 Subnets have no IP Pool.")
+        ip_pools = self.ip_pools
+        if locked:
+            ip_pools = ip_pools.select_for_update()
+        return ip_pools.all()[0].pool
+
+    def allocate_address(self, userid):
+        pool = self.get_pool(locked=True)
+        address = pool.get()
+        pool.save()
+        return IPAddress.objects.create(network=self.network, subnet=self,
+                                        address=address, userid=userid)
+
 
 class BackendNetwork(models.Model):
     OPER_STATES = (
@@ -755,6 +767,13 @@ class NetworkInterface(models.Model):
         """Return the backend id by prepending backend-prefix."""
         return "%snic-%s" % (settings.BACKEND_PREFIX_ID, str(self.id))
 
+    @property
+    def ipv4_address(self):
+        try:
+            return self.ips.get(subnet__ipversion=4).address
+        except IPAddress.DoesNotExist:
+            return None
+
 
 class SecurityGroup(models.Model):
     SECURITY_GROUP_NAME_LENGTH = 128
diff --git a/snf-cyclades-app/synnefo/db/models_factory.py b/snf-cyclades-app/synnefo/db/models_factory.py
index 30588d7..0fcb8c6 100644
--- a/snf-cyclades-app/synnefo/db/models_factory.py
+++ b/snf-cyclades-app/synnefo/db/models_factory.py
@@ -186,10 +186,15 @@ class NetworkInterfaceFactory(factory.DjangoModelFactory):
         factory.Sequence(round_seq_first(FACTORY_FOR.FIREWALL_PROFILES))
 
 
+class IPPoolTableFactory(factory.DjangoModelFactory):
+    FACTORY_FOR = models.IPPoolTable
+    size = 0
+
+
 class IPv4SubnetFactory(factory.DjangoModelFactory):
     FACTORY_FOR = models.Subnet
 
-    network = factory.SubFactory(NetworkFactory)
+    network = factory.SubFactory(NetworkFactory, state="ACTIVE")
     name = factory.LazyAttribute(lambda self: random_string(30))
     ipversion = 4
     cidr = factory.Sequence(lambda n: '192.168.{0}.0/24'.format(n))
@@ -197,6 +202,7 @@ class IPv4SubnetFactory(factory.DjangoModelFactory):
     gateway = factory.Sequence(lambda n: '192.168.{0}.1'.format(n))
     dns_nameservers = []
     host_routes = []
+    pool = factory.RelatedFactory(IPPoolTableFactory, 'subnet')
 
 
 class IPv6SubnetFactory(IPv4SubnetFactory):
@@ -213,6 +219,18 @@ class IPv4AddressFactory(factory.DjangoModelFactory):
     address =\
         factory.LazyAttributeSequence(lambda self, n: self.subnet.cidr[:-4] +
                                       '{0}'.format(int(n) + 2))
+    nic = factory.SubFactory(NetworkInterfaceFactory,
+                             network=factory.SelfAttribute('..network'))
+
+
+class IPv6AddressFactory(IPv4AddressFactory):
+    FACTORY_FOR = models.IPAddress
+
+    subnet = factory.SubFactory(IPv6SubnetFactory)
+    network = factory.SubFactory(NetworkFactory)
+    address = "babe::"
+    nic = factory.SubFactory(NetworkInterfaceFactory,
+                             network=factory.SelfAttribute('..network'))
 
 
 class FloatingIPFactory(IPv4AddressFactory):
diff --git a/snf-cyclades-app/synnefo/db/query.py b/snf-cyclades-app/synnefo/db/query.py
new file mode 100644
index 0000000..ab41282
--- /dev/null
+++ b/snf-cyclades-app/synnefo/db/query.py
@@ -0,0 +1,47 @@
+from synnefo.db.models import IPAddress
+
+
+def get_server_ips(server, for_update=False):
+    ips = IPAddress.objects.select_related("subnet")
+    ips = ips.filter(nic__machine=server, deleted=False)
+    if for_update:
+        ips = ips.select_for_update()
+    return ips
+
+
+def get_server_active_ips(server, for_update=False):
+    ips = get_server_ips(server, for_update=for_update)
+    return ips.filter(nic__state="ACTIVE")
+
+
+def get_server_public_ip(server, version=4):
+    ips = get_server_active_ips(server)
+    try:
+        public_ips = ips.filter(network__public=True,
+                                subnet__ipversion=version)
+        return public_ips[0].address
+    except IndexError:
+        return None
+
+
+def get_floating_ips(for_update=False):
+    ips = IPAddress.objects.select_related("subnet")
+    ips = ips.filter(floating_ip=True, deleted=False)
+    if for_update:
+        ips = ips.select_for_update()
+    return ips
+
+
+def get_server_floating_ips(server, for_update=False):
+    floating_ips = get_floating_ips(for_update=for_update)
+    return floating_ips.filter(nic__machine=server)
+
+
+def get_server_floating_ip(server, address, for_update=False):
+    server_fips = get_server_floating_ips(server, for_update=for_update)
+    return server_fips.get(address=address)
+
+
+def get_user_floating_ip(userid, address, for_update=False):
+    fips = get_floating_ips(for_update=for_update)
+    return fips.get(userid=userid, address=address)
diff --git a/snf-cyclades-app/synnefo/logic/backend.py b/snf-cyclades-app/synnefo/logic/backend.py
index 9a5969b..e7cce94 100644
--- a/snf-cyclades-app/synnefo/logic/backend.py
+++ b/snf-cyclades-app/synnefo/logic/backend.py
@@ -544,12 +544,12 @@ def create_instance(vm, nics, flavor, image):
 
     kw['nics'] = [{"name": nic.backend_uuid,
                    "network": nic.network.backend_id,
-                   "ip": nic.ipv4}
+                   "ip": nic.ipv4_address}
                   for nic in nics]
     backend = vm.backend
     depend_jobs = []
     for nic in nics:
-        network = Network.objects.select_for_update().get(id=nic.network.id)
+        network = Network.objects.select_for_update().get(id=nic.network_id)
         bnet, created = BackendNetwork.objects.get_or_create(backend=backend,
                                                              network=network)
         if bnet.operstate != "ACTIVE":
@@ -714,8 +714,19 @@ def _create_network(network, backend):
     """Create a network."""
 
     tags = network.backend_tag
-    if network.dhcp:
-        tags.append('nfdhcpd')
+    subnet = None
+    subnet6 = None
+    gateway = None
+    gateway6 = None
+    for subnet in network.subnets.all():
+        if subnet.ipversion == 4:
+            if subnet.dhcp:
+                tags.append('nfdhcpd')
+                subnet = subnet.cidr
+                gateway = subnet.gateway
+        elif subnet.ipversion == 6:
+                subnet6 = subnet.cidr
+                gateway6 = subnet.gateway
 
     if network.public:
         conflicts_check = True
@@ -728,7 +739,6 @@ def _create_network(network, backend):
     # not support IPv6 only networks. To bypass this limitation, we create the
     # network with a dummy network subnet, and make Cyclades connect instances
     # to such networks, with address=None.
-    subnet = network.subnet
     if subnet is None:
         subnet = "10.0.0.0/24"
 
@@ -742,9 +752,9 @@ def _create_network(network, backend):
     with pooled_rapi_client(backend) as client:
         return client.CreateNetwork(network_name=network.backend_id,
                                     network=subnet,
-                                    network6=network.subnet6,
-                                    gateway=network.gateway,
-                                    gateway6=network.gateway6,
+                                    network6=subnet6,
+                                    gateway=gateway,
+                                    gateway6=gateway6,
                                     mac_prefix=mac_prefix,
                                     conflicts_check=conflicts_check,
                                     tags=tags)
diff --git a/snf-cyclades-app/synnefo/logic/management/commands/backend-list.py b/snf-cyclades-app/synnefo/logic/management/commands/backend-list.py
index 328f240..a80ce5f 100644
--- a/snf-cyclades-app/synnefo/logic/management/commands/backend-list.py
+++ b/snf-cyclades-app/synnefo/logic/management/commands/backend-list.py
@@ -53,7 +53,7 @@ class Command(ListCommand):
         free_ips = 0
         total_ips = 0
         for network in util.backend_public_networks(backend):
-            pool = network.get_pool(with_lock=False)
+            pool = network.get_pool(locked=False)
             free_ips += pool.count_available()
             total_ips += pool.pool_size
         return "%s/%s" % (free_ips, total_ips)
diff --git a/snf-cyclades-app/synnefo/logic/servers.py b/snf-cyclades-app/synnefo/logic/servers.py
index 28243c3..7d37c63 100644
--- a/snf-cyclades-app/synnefo/logic/servers.py
+++ b/snf-cyclades-app/synnefo/logic/servers.py
@@ -13,7 +13,8 @@ from synnefo.api import util
 from synnefo.logic import backend
 from synnefo.logic.backend_allocator import BackendAllocator
 from synnefo.db.models import (NetworkInterface, VirtualMachine, Network,
-                               VirtualMachineMetadata, IPAddress)
+                               VirtualMachineMetadata, IPAddress, Subnet)
+from synnefo.db import query as db_query
 
 from vncauthproxy.client import request_forwarding as request_vnc_forwarding
 
@@ -242,9 +243,11 @@ def create_instance_nics(vm, userid, private_networks=[], floating_ips=[]):
     """
     attachments = []
     for network_id in settings.DEFAULT_INSTANCE_NETWORKS:
-        network, address = None, None
+        network, ipaddress = None, None
         if network_id == "SNF:ANY_PUBLIC":
-            network, address = util.allocate_public_address(backend=vm.backend)
+            ipaddress = util.allocate_public_address(backend=vm.backend,
+                                                     userid=userid)
+            network = ipaddress.network
         else:
             try:
                 network = Network.objects.get(id=network_id, deleted=False)
@@ -254,31 +257,33 @@ def create_instance_nics(vm, userid, private_networks=[], floating_ips=[]):
                       " network '%s'" % network_id
                 log.error(msg)
                 raise Exception(msg)
-            if network.subnet is not None and network.dhcp:
-                address = util.get_network_free_address(network)
-        attachments.append((network, address))
+            try:
+                subnet = network.subnets.get(ipversion=4, dhcp=True)
+                ipaddress = util.get_network_free_address(subnet, userid)
+            except Subnet.DoesNotExist:
+                ipaddress = None
+        attachments.append((network, ipaddress))
     for address in floating_ips:
-        floating_ip = add_floating_ip_to_vm(vm=vm, address=address)
-        network = floating_ip.network
-        attachments.append((network, address))
+        floating_ip = get_floating_ip(userid=vm.userid, address=address)
+        attachments.append((floating_ip.network, floating_ip))
     for network_id in private_networks:
-        network, address = None, None
         network = util.get_network(network_id, userid, non_deleted=True)
         if network.public:
             raise faults.Forbidden("Can not connect to public network")
-        if network.dhcp:
-            address = util.get_network_free_address(network)
-        attachments.append((network, address))
+        attachments.append((network, ipaddress))
 
     nics = []
-    for index, (network, address) in enumerate(attachments):
+    for index, (network, ipaddress) in enumerate(attachments):
         # Create VM's public NIC. Do not wait notification form ganeti
         # hooks to create this NIC, because if the hooks never run (e.g.
         # building error) the VM's public IP address will never be
         # released!
-        nic = NetworkInterface.objects.create(machine=vm, network=network,
-                                              index=index, ipv4=address,
+        nic = NetworkInterface.objects.create(userid=userid, machine=vm,
+                                              network=network, index=index,
                                               state="BUILDING")
+        if ipaddress is not None:
+            ipaddress.nic = nic
+            ipaddress.save()
         nics.append(nic)
     return nics
 
@@ -427,7 +432,7 @@ def console(vm, console_type):
 
 @server_command("CONNECT")
 def add_floating_ip(vm, address):
-    floating_ip = add_floating_ip_to_vm(vm, address)
+    floating_ip = get_floating_ip(userid=vm.userid, address=address)
     nic = NetworkInterface.objects.create(machine=vm,
                                           network=floating_ip.network,
                                           ipv4=floating_ip.ipv4,
@@ -438,56 +443,41 @@ def add_floating_ip(vm, address):
     return backend.connect_to_network(vm, nic)
 
 
-def add_floating_ip_to_vm(vm, address):
-    """Get a floating IP by it's address and add it to VirtualMachine.
+def get_floating_ip(userid, address):
+    """Get a floating IP by it's address.
 
-    Helper function for looking up a IPAddress by it's address and associating
-    it with a VirtualMachine object (without adding the NIC in the Ganeti
-    backend!). This function also checks if the floating IP is currently used
-    by any instance and if it is available in the Backend that hosts the VM.
+    Helper function for looking up a IPAddress by it's address. This function
+    also checks if the floating IP is currently used by any instance.
 
     """
-    user_id = vm.userid
     try:
         # Get lock in VM, to guarantee that floating IP will only by assigned
         # once
-        floating_ip = IPAddress.objects.select_for_update()\
-                                        .get(userid=user_id, ipv4=address,
-                                             deleted=False)
+        floating_ip = db_query.get_user_floating_ip(userid=userid,
+                                                    address=address,
+                                                    for_update=True)
     except IPAddress.DoesNotExist:
-        raise faults.ItemNotFound("Floating IP '%s' does not exist" % address)
+        raise faults.ItemNotFound("Floating IP with address '%s' does not"
+                                  " exist" % address)
 
-    if floating_ip.in_use():
+    if floating_ip.nic is not None:
         raise faults.Conflict("Floating IP '%s' already in use" %
                               floating_ip.id)
 
-    bnet = floating_ip.network.backend_networks.filter(backend=vm.backend_id)
-    if not bnet.exists():
-        msg = "Network '%s' is a floating IP pool, but it not connected"\
-              " to backend '%s'" % (floating_ip.network, vm.backend)
-        raise faults.ServiceUnavailable(msg)
-
-    floating_ip.machine = vm
-    floating_ip.save()
     return floating_ip
 
 
 @server_command("DISCONNECT")
 def remove_floating_ip(vm, address):
-    user_id = vm.userid
     try:
-        floating_ip = IPAddress.objects.select_for_update()\
-                                        .get(userid=user_id, ipv4=address,
-                                             deleted=False, machine=vm)
+        floating_ip = db_query.get_server_floating_ip(server=vm,
+                                                      address=address,
+                                                      for_update=True)
     except IPAddress.DoesNotExist:
-        raise faults.ItemNotFound("Floating IP '%s' does not exist" % address)
-
-    try:
-        nic = NetworkInterface.objects.get(machine=vm, ipv4=address)
-    except NetworkInterface.DoesNotExist:
-        raise faults.ItemNotFound("Floating IP '%s' is not attached to"
-                                  "VM '%s'" % (floating_ip, vm))
+        raise faults.BadRequest("Server '%s' has no floating ip with"
+                                " address '%s'" % (vm, address))
 
+    nic = floating_ip.nic
     log.info("Removing NIC %s from VM %s. Floating IP '%s'", str(nic.index),
              vm, floating_ip)
 
diff --git a/snf-cyclades-app/synnefo/logic/tests/servers.py b/snf-cyclades-app/synnefo/logic/tests/servers.py
index 5e86543..a6cd2f5 100644
--- a/snf-cyclades-app/synnefo/logic/tests/servers.py
+++ b/snf-cyclades-app/synnefo/logic/tests/servers.py
@@ -122,7 +122,7 @@ class ServerTest(TransactionTestCase):
         mfactory.BackendNetworkFactory(network=net, backend=vm.backend)
         mrapi().ModifyInstance.return_value = 42
         servers.connect(vm, net)
-        pool = net.get_pool(with_lock=False)
+        pool = net.get_pool(locked=False)
         self.assertFalse(pool.is_available("192.168.2.2"))
         args, kwargs = mrapi().ModifyInstance.call_args
         nics = kwargs["nics"][0]
@@ -140,7 +140,7 @@ class ServerTest(TransactionTestCase):
                                       dhcp=False)
         mfactory.BackendNetworkFactory(network=net, backend=vm.backend)
         servers.connect(vm, net)
-        pool = net.get_pool(with_lock=False)
+        pool = net.get_pool(locked=False)
         self.assertTrue(pool.is_available("192.168.2.2"))
         args, kwargs = mrapi().ModifyInstance.call_args
         nics = kwargs["nics"][0]
diff --git a/snf-cyclades-app/synnefo/quotas/__init__.py b/snf-cyclades-app/synnefo/quotas/__init__.py
index 5429b07..ac00bda 100644
--- a/snf-cyclades-app/synnefo/quotas/__init__.py
+++ b/snf-cyclades-app/synnefo/quotas/__init__.py
@@ -298,7 +298,8 @@ def prepare_qh_resources(resource):
     elif isinstance(resource, Network):
         return {"cyclades.network.private": 1}
     elif isinstance(resource, IPAddress):
-        return {"cyclades.floating_ip": 1}
+        if resource.floating_ip:
+            return {"cyclades.floating_ip": 1}
     else:
         raise ValueError("Unknown Resource '%s'" % resource)
 
