Revision 92d2d1ce

b/snf-cyclades-app/synnefo/api/floating_ips.py
234 234

  
235 235

  
236 236
def network_to_pool(network):
237
    pool = network.get_pool(with_lock=False)
237
    pool = network.get_pool(locked=False)
238 238
    return {"name": str(network.id),
239 239
            "size": pool.pool_size,
240 240
            "free": pool.count_available()}
b/snf-cyclades-app/synnefo/api/servers.py
43 43
from snf_django.lib.api import faults, utils
44 44

  
45 45
from synnefo.api import util
46
from synnefo.db import query as db_query
46 47
from synnefo.db.models import (VirtualMachine, VirtualMachineMetadata)
47 48
from synnefo.logic import servers, utils as logic_utils
48 49

  
......
108 109
    d = {'id': nic.id,
109 110
         'network_id': str(nic.network.id),
110 111
         'mac_address': nic.mac,
111
         'ipv4': nic.ipv4,
112
         'ipv6': nic.ipv6,
113
         'OS-EXT-IPS:type': nic.ip_type.lower()}
112
         'ipv4': '',
113
         'ipv6': ''}
114
    for ip in nic.ips.filter(deleted=False).select_related("subnet"):
115
        ip_type = "floating" if ip.floating_ip else "fixed"
116
        if ip.subnet.ipversion == 4:
117
            d["ipv4"] = ip.address
118
            d["OS-EXT-IPS:type"] = ip_type
119
        else:
120
            d["ipv6"] = ip.address
121
            d["OS-EXT-IPS:type"] = ip_type
114 122

  
115 123
    if nic.firewall_profile:
116 124
        d['firewallProfile'] = nic.firewall_profile
......
121 129
    addresses = {}
122 130
    for nic in attachments:
123 131
        net_nics = []
124
        net_nics.append({"version": 4,
125
                         "addr": nic["ipv4"],
126
                         "OS-EXT-IPS:type": nic["OS-EXT-IPS:type"]})
132
        if nic["ipv4"]:
133
            net_nics.append({"version": 4,
134
                             "addr": nic["ipv4"],
135
                             "OS-EXT-IPS:type": nic["OS-EXT-IPS:type"]})
127 136
        if nic["ipv6"]:
128 137
            net_nics.append({"version": 6,
129 138
                             "addr": nic["ipv6"],
......
180 189
def get_server_fqdn(vm):
181 190
    fqdn_setting = settings.CYCLADES_SERVERS_FQDN
182 191
    if fqdn_setting is None:
183
        public_nics = vm.nics.filter(network__public=True, state="ACTIVE")
184 192
        # Return the first public IPv4 address if exists
185
        ipv4_nics = public_nics.exclude(ipv4=None)
186
        if ipv4_nics:
187
            return ipv4_nics[0].ipv4
188
        # Else return the first public IPv6 address if exists
189
        ipv6_nics = public_nics.exclude(ipv6=None)
190
        if ipv6_nics:
191
            return ipv6_nics[0].ipv6
192
        return ""
193
        address = db_query.get_server_public_ip(server=vm, version=4)
194
        if address is None:
195
            # Else return the first public IPv6 address if exists
196
            address = db_query.get_server_public_ip(server=vm, version=6)
197
        if address is None:
198
            return ""
199
        else:
200
            return address
193 201
    elif isinstance(fqdn_setting, basestring):
194 202
        return fqdn_setting % {"id": vm.id}
195 203
    else:
......
214 222
    port_forwarding = {}
215 223
    for dport, to_dest in settings.CYCLADES_PORT_FORWARDING.items():
216 224
        if hasattr(to_dest, "__call__"):
217
            public_nics = vm.nics.filter(network__public=True, state="ACTIVE")\
218
                                 .exclude(ipv4=None).order_by('index')
219
            if public_nics:
220
                vm_ipv4 = public_nics[0].ipv4
221
            else:
222
                vm_ipv4 = None
223
            to_dest = to_dest(vm_ipv4, vm.id, fqdn, vm.userid)
225
            address = db_query.get_server_public_ip(server=vm, version=4)
226
            to_dest = to_dest(address, vm.id, fqdn, vm.userid)
224 227
        msg = ("Invalid setting: CYCLADES_PORT_FOWARDING."
225 228
               " Value must be a tuple of two elements (host, port).")
226 229
        if to_dest is None:
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 (IPAddressFactory, NetworkFactory,
38
from synnefo.db.models_factory import (FloatingIPFactory, NetworkFactory,
39 39
                                       VirtualMachineFactory,
40 40
                                       NetworkInterfaceFactory,
41 41
                                       BackendNetworkFactory)
......
52 52
NETWORKS_URL = join_urls(compute_path, "networks")
53 53
SERVERS_URL = join_urls(compute_path, "servers")
54 54

  
55
IPAddressPoolFactory = partial(NetworkFactory, public=True, deleted=False,
55

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

  
58 60

  
59
class IPAddressAPITest(BaseAPITest):
61
class FloatingIPAPITest(BaseAPITest):
60 62
    def test_no_floating_ip(self):
61 63
        response = self.get(URL)
62 64
        self.assertSuccess(response)
63 65
        self.assertEqual(json.loads(response.content)["floating_ips"], [])
64 66

  
65 67
    def test_list_ips(self):
66
        ip = IPAddressFactory(userid="user1")
67
        IPAddressFactory(userid="user1", deleted=True)
68
        ip = FloatingIPFactory(userid="user1")
69
        FloatingIPFactory(userid="user1", deleted=True)
68 70
        with mocked_quotaholder():
69 71
            response = self.get(URL, "user1")
70 72
        self.assertSuccess(response)
......
75 77
                          str(ip.network.id)})
76 78

  
77 79
    def test_get_ip(self):
78
        ip = IPAddressFactory(userid="user1")
80
        ip = FloatingIPFactory(userid="user1")
79 81
        with mocked_quotaholder():
80 82
            response = self.get(URL + "/%s" % ip.id, "user1")
81 83
        self.assertSuccess(response)
......
86 88
                          str(ip.network.id)})
87 89

  
88 90
    def test_wrong_user(self):
89
        ip = IPAddressFactory(userid="user1")
91
        ip = FloatingIPFactory(userid="user1")
90 92
        with mocked_quotaholder():
91 93
            response = self.delete(URL + "/%s" % ip.id, "user2")
92 94
        self.assertItemNotFound(response)
93 95

  
94 96
    def test_deleted_ip(self):
95
        ip = IPAddressFactory(userid="user1", deleted=True)
97
        ip = FloatingIPFactory(userid="user1", deleted=True)
96 98
        with mocked_quotaholder():
97 99
            response = self.delete(URL + "/%s" % ip.id, "user1")
98 100
        self.assertItemNotFound(response)
99 101

  
100 102
    def test_reserve(self):
101
        net = IPAddressPoolFactory(userid="test_user",
103
        net = FloatingIPPoolFactory(userid="test_user",
102 104
                                    subnet="192.168.2.0/24",
103 105
                                    gateway=None)
104 106
        request = {'pool': net.id}
105 107
        with mocked_quotaholder():
106 108
            response = self.post(URL, "test_user", json.dumps(request), "json")
107 109
        self.assertSuccess(response)
108
        ip = IPAddress.objects.get()
110
        ip = floating_ips.get()
109 111
        self.assertEqual(ip.ipv4, "192.168.2.1")
110 112
        self.assertEqual(ip.machine, None)
111 113
        self.assertEqual(ip.network, net)
......
120 122
            response = self.post(URL, "test_user", json.dumps({}), "json")
121 123
        self.assertFault(response, 413, "overLimit")
122 124
        # Full network
123
        IPAddressPoolFactory(userid="test_user",
125
        FloatingIPPoolFactory(userid="test_user",
124 126
                              subnet="192.168.2.0/32",
125 127
                              gateway=None)
126 128
        with mocked_quotaholder():
127 129
            response = self.post(URL, "test_user", json.dumps({}), "json")
128 130
        self.assertFault(response, 413, "overLimit")
129 131
        # Success
130
        net2 = IPAddressPoolFactory(userid="test_user",
132
        net2 = FloatingIPPoolFactory(userid="test_user",
131 133
                                     subnet="192.168.2.0/24",
132 134
                                     gateway=None)
133 135
        with mocked_quotaholder():
134 136
            response = self.post(URL, "test_user", json.dumps({}), "json")
135 137
        self.assertSuccess(response)
136
        ip = IPAddress.objects.get()
138
        ip = floating_ips.get()
137 139
        self.assertEqual(json.loads(response.content)["floating_ip"],
138 140
                         {"instance_id": None, "ip": "192.168.2.1",
139 141
                          "fixed_ip": None, "id": str(ip.id),
140 142
                          "pool": str(net2.id)})
141 143

  
142 144
    def test_reserve_full(self):
143
        net = IPAddressPoolFactory(userid="test_user",
145
        net = FloatingIPPoolFactory(userid="test_user",
144 146
                                    subnet="192.168.2.0/32")
145 147
        request = {'pool': net.id}
146 148
        with mocked_quotaholder():
......
148 150
        self.assertEqual(response.status_code, 413)
149 151

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

  
163 165
        # Already reserved
164
        IPAddressFactory(network=net, ipv4="192.168.2.3")
166
        FloatingIPFactory(network=net, ipv4="192.168.2.3")
165 167
        request = {'pool': net.id, "address": "192.168.2.3"}
166 168
        with mocked_quotaholder():
167 169
            response = self.post(URL, "test_user", json.dumps(request), "json")
......
194 196
        self.assertBadRequest(response)
195 197

  
196 198
    def test_release_in_use(self):
197
        ip = IPAddressFactory()
199
        ip = FloatingIPFactory()
198 200
        vm = ip.machine
199 201
        vm.operstate = "ACTIVE"
200 202
        vm.userid = ip.userid
......
218 220
        self.assertFault(response, 409, "conflict")
219 221

  
220 222
    def test_release(self):
221
        ip = IPAddressFactory(machine=None)
223
        ip = FloatingIPFactory(machine=None)
222 224
        with mocked_quotaholder():
223 225
            response = self.delete(URL + "/%s" % ip.id, ip.userid)
224 226
        self.assertSuccess(response)
225
        ips_after = IPAddress.objects.filter(id=ip.id)
227
        ips_after = floating_ips.filter(id=ip.id)
226 228
        self.assertEqual(len(ips_after), 0)
227 229

  
228 230
    @patch("synnefo.logic.backend", Mock())
229 231
    def test_delete_network_with_floating_ips(self):
230
        ip = IPAddressFactory(machine=None, network__flavor="IP_LESS_ROUTED")
232
        ip = FloatingIPFactory(machine=None, network__flavor="IP_LESS_ROUTED")
231 233
        net = ip.network
232 234
        # Can not remove network with floating IPs
233 235
        with mocked_quotaholder():
......
246 248
POOLS_URL = join_urls(compute_path, "os-floating-ip-pools")
247 249

  
248 250

  
249
class IPAddressPoolsAPITest(BaseAPITest):
251
class FloatingIPPoolsAPITest(BaseAPITest):
250 252
    def test_no_pool(self):
251 253
        response = self.get(POOLS_URL)
252 254
        self.assertSuccess(response)
253 255
        self.assertEqual(json.loads(response.content)["floating_ip_pools"], [])
254 256

  
255 257
    def test_list_pools(self):
256
        net = IPAddressPoolFactory(subnet="192.168.0.0/30",
258
        net = FloatingIPPoolFactory(subnet="192.168.0.0/30",
257 259
                                    gateway="192.168.0.1")
258 260
        NetworkFactory(public=True, deleted=True)
259 261
        NetworkFactory(public=False, deleted=False)
......
264 266
                         [{"name": str(net.id), "size": 4, "free": 1}])
265 267

  
266 268

  
267
class IPAddressActionsTest(BaseAPITest):
269
class FloatingIPActionsTest(BaseAPITest):
268 270
    def setUp(self):
269 271
        vm = VirtualMachineFactory()
270 272
        vm.operstate = "ACTIVE"
......
289 291
        self.assertItemNotFound(response)
290 292
        # In use
291 293
        vm1 = VirtualMachineFactory()
292
        ip1 = IPAddressFactory(userid=self.vm.userid, machine=vm1)
294
        ip1 = FloatingIPFactory(userid=self.vm.userid, machine=vm1)
293 295
        BackendNetworkFactory(network=ip1.network, backend=vm1.backend,
294 296
                              operstate='ACTIVE')
295 297
        request = {"addFloatingIp": {"address": ip1.ipv4}}
296 298
        response = self.post(url, self.vm.userid, json.dumps(request), "json")
297 299
        self.assertFault(response, 409, "conflict")
298 300
        # Success
299
        ip1 = IPAddressFactory(userid=self.vm.userid, machine=None)
301
        ip1 = FloatingIPFactory(userid=self.vm.userid, machine=None)
300 302
        BackendNetworkFactory(network=ip1.network, backend=self.vm.backend,
301 303
                              operstate='ACTIVE')
302 304
        request = {"addFloatingIp": {"address": ip1.ipv4}}
303 305
        mock().ModifyInstance.return_value = 1
304 306
        response = self.post(url, self.vm.userid, json.dumps(request), "json")
305 307
        self.assertEqual(response.status_code, 202)
306
        ip1_after = IPAddress.objects.get(id=ip1.id)
308
        ip1_after = floating_ips.get(id=ip1.id)
307 309
        self.assertEqual(ip1_after.machine, self.vm)
308 310
        self.assertTrue(ip1_after.in_use())
309 311
        nic = self.vm.nics.get(ipv4=ip1_after.ipv4)
......
323 325
        response = self.post(url, self.vm.userid, json.dumps(request), "json")
324 326
        self.assertItemNotFound(response)
325 327
        # Not In Use
326
        ip1 = IPAddressFactory(userid=self.vm.userid, machine=None)
328
        ip1 = FloatingIPFactory(userid=self.vm.userid, machine=None)
327 329
        request = {"removeFloatingIp": {"address": ip1.ipv4}}
328 330
        response = self.post(url, self.vm.userid, json.dumps(request), "json")
329 331
        self.assertItemNotFound(response)
330 332
        # Success
331
        ip1 = IPAddressFactory(userid=self.vm.userid, machine=self.vm)
333
        ip1 = FloatingIPFactory(userid=self.vm.userid, machine=self.vm)
332 334
        NetworkInterfaceFactory(machine=self.vm, ipv4=ip1.ipv4)
333 335
        request = {"removeFloatingIp": {"address": ip1.ipv4}}
334 336
        mock().ModifyInstance.return_value = 2
335 337
        response = self.post(url, self.vm.userid, json.dumps(request), "json")
336 338
        self.assertEqual(response.status_code, 202)
337 339
        # Yet used. Wait for the callbacks
338
        ip1_after = IPAddress.objects.get(id=ip1.id)
340
        ip1_after = floating_ips.get(id=ip1.id)
339 341
        self.assertEqual(ip1_after.machine, self.vm)
340 342
        self.assertTrue(ip1_after.in_use())
b/snf-cyclades-app/synnefo/api/tests/servers.py
122 122
        """Test if a server details are returned."""
123 123
        db_vm = self.vm2
124 124
        user = self.vm2.userid
125
        net = mfactory.NetworkFactory()
126
        nic = mfactory.NetworkInterfaceFactory(machine=self.vm2, network=net,
127
                                               ipv6="::babe")
125
        ip4 = mfactory.IPv4AddressFactory(nic__machine=self.vm2)
126
        nic = ip4.nic
127
        net = ip4.network
128
        ip6 = mfactory.IPv6AddressFactory(nic=nic, network=net)
129
        nic.mac = "aa:00:11:22:33:44"
130
        nic.save()
128 131

  
129 132
        db_vm_meta = mfactory.VirtualMachineMetadataFactory(vm=db_vm)
130 133

  
......
141 144
        self.assertEqual(api_nic['network_id'], str(net.id))
142 145
        self.assertEqual(api_nic['mac_address'], nic.mac)
143 146
        self.assertEqual(api_nic['firewallProfile'], nic.firewall_profile)
144
        self.assertEqual(api_nic['ipv4'], nic.ipv4)
145
        self.assertEqual(api_nic['ipv6'], nic.ipv6)
147
        self.assertEqual(api_nic['ipv4'], ip4.address)
148
        self.assertEqual(api_nic['ipv6'], ip6.address)
146 149
        self.assertEqual(api_nic['OS-EXT-IPS:type'], "fixed")
147 150
        self.assertEqual(api_nic['id'], nic.id)
148 151
        api_address = server["addresses"]
149 152
        self.assertEqual(api_address[str(net.id)], [
150
            {"version": 4, "addr": nic.ipv4, "OS-EXT-IPS:type": "fixed"},
151
            {"version": 6, "addr": nic.ipv6, "OS-EXT-IPS:type": "fixed"}
153
            {"version": 4, "addr": ip4.address, "OS-EXT-IPS:type": "fixed"},
154
            {"version": 6, "addr": ip6.address, "OS-EXT-IPS:type": "fixed"}
152 155
        ])
153 156

  
154 157
        metadata = server['metadata']
......
184 187
            self.assertEqual(server["SNF:fqdn"], "")
185 188

  
186 189
        # IPv6 NIC
187
        nic = mfactory.NetworkInterfaceFactory(machine=vm, ipv4=None,
188
                                               ipv6="babe::", state="ACTIVE",
189
                                               network__public=True)
190
        ipv6_address = mfactory.IPv6AddressFactory(nic__machine=vm,
191
                                                   network__public=True)
190 192
        with override_settings(settings,
191 193
                               CYCLADES_SERVERS_FQDN=None):
192 194
            response = self.myget("servers/%d" % vm.id, vm.userid)
193 195
            server = json.loads(response.content)['server']
194
            self.assertEqual(server["SNF:fqdn"], nic.ipv6)
196
            self.assertEqual(server["SNF:fqdn"], ipv6_address.address)
195 197

  
196 198
        # IPv4 NIC
197
        nic = mfactory.NetworkInterfaceFactory(machine=vm,
198
                                               network__public=True,
199
                                               state="ACTIVE")
199
        ipv4_address = mfactory.IPv4AddressFactory(nic__machine=vm,
200
                                                   network__public=True)
200 201
        with override_settings(settings,
201 202
                               CYCLADES_SERVERS_FQDN=None):
202 203
            response = self.myget("servers/%d" % vm.id, vm.userid)
203 204
            server = json.loads(response.content)['server']
204
            self.assertEqual(server["SNF:fqdn"], nic.ipv4)
205
            self.assertEqual(server["SNF:fqdn"], ipv4_address.address)
205 206

  
206 207
    def test_server_port_forwarding(self):
207 208
        vm = mfactory.VirtualMachineFactory()
......
229 230
            server = json.loads(response.content)['server']
230 231
            self.assertEqual(server["SNF:port_forwarding"], {})
231 232

  
232
        mfactory.NetworkInterfaceFactory(machine=vm, ipv4="192.168.2.2",
233
                                         network__public=True)
233
        mfactory.IPv4AddressFactory(nic__machine=vm,
234
                                    network__public=True,
235
                                    address="192.168.2.2")
234 236
        with override_settings(settings,
235 237
                               CYCLADES_PORT_FORWARDING=ports):
236 238
            response = self.myget("servers/%d" % vm.id, vm.userid)
......
335 337
    def setUp(self):
336 338
        self.flavor = mfactory.FlavorFactory()
337 339
        # Create public network and backend
338
        self.network = mfactory.NetworkFactory(public=True)
340
        subnet = mfactory.IPv4SubnetFactory(network__public=True)
341
        self.network = subnet.network
339 342
        self.backend = mfactory.BackendFactory()
340 343
        mfactory.BackendNetworkFactory(network=self.network,
341 344
                                       backend=self.backend,
......
386 389

  
387 390
    def test_create_network_settings(self, mrapi):
388 391
        mrapi().CreateInstance.return_value = 12
389
        bnet1 = mfactory.BackendNetworkFactory(operstate="ACTIVE",
390
                                               backend=self.backend)
391
        bnet2 = mfactory.BackendNetworkFactory(operstate="ACTIVE",
392
                                               backend=self.backend)
393
        bnet3 = mfactory.BackendNetworkFactory(network__userid="test_user",
394
                                               operstate="ACTIVE",
395
                                               backend=self.backend)
396
        bnet4 = mfactory.BackendNetworkFactory(network__userid="test_user",
397
                                               operstate="ACTIVE",
398
                                               backend=self.backend)
392
        # Create public network and backend
393
        subnet1 = mfactory.IPv4SubnetFactory()
394
        bnet1 = mfactory.BackendNetworkFactory(network=subnet1.network,
395
                                               backend=self.backend,
396
                                               operstate="ACTIVE")
397
        subnet2 = mfactory.IPv4SubnetFactory()
398
        bnet2 = mfactory.BackendNetworkFactory(network=subnet2.network,
399
                                               backend=self.backend,
400
                                               operstate="ACTIVE")
401
        subnet3 = mfactory.IPv4SubnetFactory(network__userid="test_user")
402
        bnet3 = mfactory.BackendNetworkFactory(network=subnet3.network,
403
                                               backend=self.backend,
404
                                               operstate="ACTIVE")
405
        subnet4 = mfactory.IPv4SubnetFactory(network__userid="test_user")
406
        bnet4 = mfactory.BackendNetworkFactory(network=subnet4.network,
407
                                               backend=self.backend,
408
                                               operstate="ACTIVE")
399 409
        # User requested private networks
400 410
        request = deepcopy(self.request)
401 411
        request["server"]["networks"] = [bnet3.network.id, bnet4.network.id]
......
464 474
        # Test floating IPs
465 475
        request = deepcopy(self.request)
466 476
        request["server"]["networks"] = [bnet4.network.id]
467
        network = mfactory.NetworkFactory(subnet="10.0.0.0/24")
468
        mfactory.BackendNetworkFactory(network=network,
469
                                       backend=self.backend,
470
                                       operstate="ACTIVE")
471
        fp1 = mfactory.IPAddressFactory(ipv4="10.0.0.2",
477
        fp1 = mfactory.FloatingIPFactory(address="10.0.0.2",
472 478
                                         userid="test_user",
473
                                         network=network, machine=None)
474
        fp2 = mfactory.IPAddressFactory(ipv4="10.0.0.3", network=network,
479
                                         nic=None)
480
        fp2 = mfactory.FloatingIPFactory(address="10.0.0.3",
475 481
                                         userid="test_user",
476
                                         machine=None)
477
        request["server"]["floating_ips"] = [fp1.ipv4, fp2.ipv4]
482
                                         nic=None)
483
        request["server"]["floating_ips"] = [fp1.address, fp2.address]
478 484
        with override_settings(settings,
479 485
                               DEFAULT_INSTANCE_NETWORKS=[bnet3.network.id]):
480 486
            with mocked_quotaholder():
......
483 489
        self.assertEqual(response.status_code, 202)
484 490
        api_server = json.loads(response.content)['server']
485 491
        vm = VirtualMachine.objects.get(id=api_server["id"])
486
        fp1 = IPAddress.objects.get(id=fp1.id)
487
        fp2 = IPAddress.objects.get(id=fp2.id)
488
        self.assertEqual(fp1.machine, vm)
489
        self.assertEqual(fp2.machine, vm)
492
        fp1 = IPAddress.objects.get(floating_ip=True, id=fp1.id)
493
        fp2 = IPAddress.objects.get(floating_ip=True, id=fp2.id)
494
        self.assertEqual(fp1.nic.machine, vm)
495
        self.assertEqual(fp2.nic.machine, vm)
490 496
        name, args, kwargs = mrapi().CreateInstance.mock_calls[2]
491 497
        self.assertEqual(len(kwargs["nics"]), 4)
492 498
        self.assertEqual(kwargs["nics"][0]["network"],
493 499
                         bnet3.network.backend_id)
494
        self.assertEqual(kwargs["nics"][1]["network"], network.backend_id)
495
        self.assertEqual(kwargs["nics"][1]["ip"], fp1.ipv4)
496
        self.assertEqual(kwargs["nics"][2]["network"], network.backend_id)
497
        self.assertEqual(kwargs["nics"][2]["ip"], fp2.ipv4)
500
        self.assertEqual(kwargs["nics"][1]["network"], fp1.network.backend_id)
501
        self.assertEqual(kwargs["nics"][1]["ip"], fp1.address)
502
        self.assertEqual(kwargs["nics"][2]["network"], fp2.network.backend_id)
503
        self.assertEqual(kwargs["nics"][2]["ip"], fp2.address)
498 504
        self.assertEqual(kwargs["nics"][3]["network"],
499 505
                         bnet4.network.backend_id)
500 506

  
......
510 516
        """Test if the create server call returns the expected response
511 517
           if a valid request has been speficied."""
512 518
        mrapi().CreateInstance.side_effect = GanetiApiError("..ganeti is down")
513
        # Create public network and backend
514
        network = mfactory.NetworkFactory(public=True)
515
        backend = mfactory.BackendFactory()
516
        mfactory.BackendNetworkFactory(network=network, backend=backend)
517 519

  
518 520
        request = self.request
519 521
        with mocked_quotaholder():
b/snf-cyclades-app/synnefo/api/util.py
234 234
        raise faults.ItemNotFound("Floating IP does not exist.")
235 235

  
236 236

  
237
def allocate_public_address(backend):
237
def allocate_public_address(backend, userid):
238 238
    """Get a public IP for any available network of a backend."""
239 239
    # Guarantee exclusive access to backend, because accessing the IP pools of
240 240
    # the backend networks may result in a deadlock with backend allocator
241 241
    # which also checks that backend networks have a free IP.
242 242
    backend = Backend.objects.select_for_update().get(id=backend.id)
243 243
    public_networks = backend_public_networks(backend)
244
    return get_free_ip(public_networks)
244
    return get_free_ip(public_networks, userid)
245 245

  
246 246

  
247 247
def backend_public_networks(backend):
......
255 255
                                          network__public=True,
256 256
                                          network__deleted=False,
257 257
                                          network__floating_ip_pool=False,
258
                                          network__subnet__isnull=False,
259 258
                                          network__drained=False)
260 259
    return [b.network for b in bnets]
261 260

  
262 261

  
263
def get_free_ip(networks):
262
def get_free_ip(networks, userid):
264 263
    for network in networks:
265 264
        try:
266
            address = get_network_free_address(network)
267
            return network, address
265
            return network.allocate_address(userid=userid)
268 266
        except faults.OverLimit:
269 267
            pass
270 268
    msg = "Can not allocate public IP. Public networks are full."
......
272 270
    raise faults.OverLimit(msg)
273 271

  
274 272

  
275
def get_network_free_address(network):
273
def get_network_free_address(network, userid):
276 274
    """Reserve an IP address from the IP Pool of the network."""
277 275

  
278
    pool = network.get_pool()
279 276
    try:
280
        address = pool.get()
277
        return network.allocate_address(userid=userid)
281 278
    except EmptyPool:
282 279
        raise faults.OverLimit("Network %s is full." % network.backend_id)
283
    pool.save()
284
    return address
285 280

  
286 281

  
287 282
def get_vm_nic(vm, nic_id):
b/snf-cyclades-app/synnefo/db/models.py
531 531
            if not backend_exists:
532 532
                BackendNetwork.objects.create(backend=backend, network=self)
533 533

  
534
    def get_pool(self, with_lock=True):
534
    def get_pool(self, locked=True):
535 535
        try:
536 536
            subnet = self.subnets.get(ipversion=4, deleted=False)
537 537
        except Subnet.DoesNotExist:
538 538
            raise pools.EmptyPool
539
        return subnet.get_pool(locked=locked)
540

  
541
    def allocate_address(self, userid):
539 542
        try:
540
            pool = subnet.ip_pools.all()[0]
541
        except IndexError:
542
            pool = IPPoolTable.objects.create(available_map='',
543
                                              reserved_map='',
544
                                              size=0,
545
                                              subnet=subnet)
546
        objects = IPPoolTable.objects
547
        if with_lock:
548
            objects = objects.select_for_update()
549
        return objects.get(id=pool.id)
543
            subnet = self.subnets.get(ipversion=4, deleted=False)
544
        except Subnet.DoesNotExist:
545
            raise pools.EmptyPool
546
        return subnet.allocate_address(userid)
550 547

  
551 548
    def reserve_address(self, address):
552 549
        pool = self.get_pool()
......
601 598
    def __unicode__(self):
602 599
        return "<Subnet %s, Network: %s>" % (self.id, self.network_id)
603 600

  
601
    def get_pool(self, locked=True):
602
        if self.ipversion == 6:
603
            raise Exception("IPv6 Subnets have no IP Pool.")
604
        ip_pools = self.ip_pools
605
        if locked:
606
            ip_pools = ip_pools.select_for_update()
607
        return ip_pools.all()[0].pool
608

  
609
    def allocate_address(self, userid):
610
        pool = self.get_pool(locked=True)
611
        address = pool.get()
612
        pool.save()
613
        return IPAddress.objects.create(network=self.network, subnet=self,
614
                                        address=address, userid=userid)
615

  
604 616

  
605 617
class BackendNetwork(models.Model):
606 618
    OPER_STATES = (
......
755 767
        """Return the backend id by prepending backend-prefix."""
756 768
        return "%snic-%s" % (settings.BACKEND_PREFIX_ID, str(self.id))
757 769

  
770
    @property
771
    def ipv4_address(self):
772
        try:
773
            return self.ips.get(subnet__ipversion=4).address
774
        except IPAddress.DoesNotExist:
775
            return None
776

  
758 777

  
759 778
class SecurityGroup(models.Model):
760 779
    SECURITY_GROUP_NAME_LENGTH = 128
b/snf-cyclades-app/synnefo/db/models_factory.py
186 186
        factory.Sequence(round_seq_first(FACTORY_FOR.FIREWALL_PROFILES))
187 187

  
188 188

  
189
class IPPoolTableFactory(factory.DjangoModelFactory):
190
    FACTORY_FOR = models.IPPoolTable
191
    size = 0
192

  
193

  
189 194
class IPv4SubnetFactory(factory.DjangoModelFactory):
190 195
    FACTORY_FOR = models.Subnet
191 196

  
192
    network = factory.SubFactory(NetworkFactory)
197
    network = factory.SubFactory(NetworkFactory, state="ACTIVE")
193 198
    name = factory.LazyAttribute(lambda self: random_string(30))
194 199
    ipversion = 4
195 200
    cidr = factory.Sequence(lambda n: '192.168.{0}.0/24'.format(n))
......
197 202
    gateway = factory.Sequence(lambda n: '192.168.{0}.1'.format(n))
198 203
    dns_nameservers = []
199 204
    host_routes = []
205
    pool = factory.RelatedFactory(IPPoolTableFactory, 'subnet')
200 206

  
201 207

  
202 208
class IPv6SubnetFactory(IPv4SubnetFactory):
......
213 219
    address =\
214 220
        factory.LazyAttributeSequence(lambda self, n: self.subnet.cidr[:-4] +
215 221
                                      '{0}'.format(int(n) + 2))
222
    nic = factory.SubFactory(NetworkInterfaceFactory,
223
                             network=factory.SelfAttribute('..network'))
224

  
225

  
226
class IPv6AddressFactory(IPv4AddressFactory):
227
    FACTORY_FOR = models.IPAddress
228

  
229
    subnet = factory.SubFactory(IPv6SubnetFactory)
230
    network = factory.SubFactory(NetworkFactory)
231
    address = "babe::"
232
    nic = factory.SubFactory(NetworkInterfaceFactory,
233
                             network=factory.SelfAttribute('..network'))
216 234

  
217 235

  
218 236
class FloatingIPFactory(IPv4AddressFactory):
b/snf-cyclades-app/synnefo/db/query.py
1
from synnefo.db.models import IPAddress
2

  
3

  
4
def get_server_ips(server, for_update=False):
5
    ips = IPAddress.objects.select_related("subnet")
6
    ips = ips.filter(nic__machine=server, deleted=False)
7
    if for_update:
8
        ips = ips.select_for_update()
9
    return ips
10

  
11

  
12
def get_server_active_ips(server, for_update=False):
13
    ips = get_server_ips(server, for_update=for_update)
14
    return ips.filter(nic__state="ACTIVE")
15

  
16

  
17
def get_server_public_ip(server, version=4):
18
    ips = get_server_active_ips(server)
19
    try:
20
        public_ips = ips.filter(network__public=True,
21
                                subnet__ipversion=version)
22
        return public_ips[0].address
23
    except IndexError:
24
        return None
25

  
26

  
27
def get_floating_ips(for_update=False):
28
    ips = IPAddress.objects.select_related("subnet")
29
    ips = ips.filter(floating_ip=True, deleted=False)
30
    if for_update:
31
        ips = ips.select_for_update()
32
    return ips
33

  
34

  
35
def get_server_floating_ips(server, for_update=False):
36
    floating_ips = get_floating_ips(for_update=for_update)
37
    return floating_ips.filter(nic__machine=server)
38

  
39

  
40
def get_server_floating_ip(server, address, for_update=False):
41
    server_fips = get_server_floating_ips(server, for_update=for_update)
42
    return server_fips.get(address=address)
43

  
44

  
45
def get_user_floating_ip(userid, address, for_update=False):
46
    fips = get_floating_ips(for_update=for_update)
47
    return fips.get(userid=userid, address=address)
b/snf-cyclades-app/synnefo/logic/backend.py
544 544

  
545 545
    kw['nics'] = [{"name": nic.backend_uuid,
546 546
                   "network": nic.network.backend_id,
547
                   "ip": nic.ipv4}
547
                   "ip": nic.ipv4_address}
548 548
                  for nic in nics]
549 549
    backend = vm.backend
550 550
    depend_jobs = []
551 551
    for nic in nics:
552
        network = Network.objects.select_for_update().get(id=nic.network.id)
552
        network = Network.objects.select_for_update().get(id=nic.network_id)
553 553
        bnet, created = BackendNetwork.objects.get_or_create(backend=backend,
554 554
                                                             network=network)
555 555
        if bnet.operstate != "ACTIVE":
......
714 714
    """Create a network."""
715 715

  
716 716
    tags = network.backend_tag
717
    if network.dhcp:
718
        tags.append('nfdhcpd')
717
    subnet = None
718
    subnet6 = None
719
    gateway = None
720
    gateway6 = None
721
    for subnet in network.subnets.all():
722
        if subnet.ipversion == 4:
723
            if subnet.dhcp:
724
                tags.append('nfdhcpd')
725
                subnet = subnet.cidr
726
                gateway = subnet.gateway
727
        elif subnet.ipversion == 6:
728
                subnet6 = subnet.cidr
729
                gateway6 = subnet.gateway
719 730

  
720 731
    if network.public:
721 732
        conflicts_check = True
......
728 739
    # not support IPv6 only networks. To bypass this limitation, we create the
729 740
    # network with a dummy network subnet, and make Cyclades connect instances
730 741
    # to such networks, with address=None.
731
    subnet = network.subnet
732 742
    if subnet is None:
733 743
        subnet = "10.0.0.0/24"
734 744

  
......
742 752
    with pooled_rapi_client(backend) as client:
743 753
        return client.CreateNetwork(network_name=network.backend_id,
744 754
                                    network=subnet,
745
                                    network6=network.subnet6,
746
                                    gateway=network.gateway,
747
                                    gateway6=network.gateway6,
755
                                    network6=subnet6,
756
                                    gateway=gateway,
757
                                    gateway6=gateway6,
748 758
                                    mac_prefix=mac_prefix,
749 759
                                    conflicts_check=conflicts_check,
750 760
                                    tags=tags)
b/snf-cyclades-app/synnefo/logic/management/commands/backend-list.py
53 53
        free_ips = 0
54 54
        total_ips = 0
55 55
        for network in util.backend_public_networks(backend):
56
            pool = network.get_pool(with_lock=False)
56
            pool = network.get_pool(locked=False)
57 57
            free_ips += pool.count_available()
58 58
            total_ips += pool.pool_size
59 59
        return "%s/%s" % (free_ips, total_ips)
b/snf-cyclades-app/synnefo/logic/servers.py
13 13
from synnefo.logic import backend
14 14
from synnefo.logic.backend_allocator import BackendAllocator
15 15
from synnefo.db.models import (NetworkInterface, VirtualMachine, Network,
16
                               VirtualMachineMetadata, IPAddress)
16
                               VirtualMachineMetadata, IPAddress, Subnet)
17
from synnefo.db import query as db_query
17 18

  
18 19
from vncauthproxy.client import request_forwarding as request_vnc_forwarding
19 20

  
......
242 243
    """
243 244
    attachments = []
244 245
    for network_id in settings.DEFAULT_INSTANCE_NETWORKS:
245
        network, address = None, None
246
        network, ipaddress = None, None
246 247
        if network_id == "SNF:ANY_PUBLIC":
247
            network, address = util.allocate_public_address(backend=vm.backend)
248
            ipaddress = util.allocate_public_address(backend=vm.backend,
249
                                                     userid=userid)
250
            network = ipaddress.network
248 251
        else:
249 252
            try:
250 253
                network = Network.objects.get(id=network_id, deleted=False)
......
254 257
                      " network '%s'" % network_id
255 258
                log.error(msg)
256 259
                raise Exception(msg)
257
            if network.subnet is not None and network.dhcp:
258
                address = util.get_network_free_address(network)
259
        attachments.append((network, address))
260
            try:
261
                subnet = network.subnets.get(ipversion=4, dhcp=True)
262
                ipaddress = util.get_network_free_address(subnet, userid)
263
            except Subnet.DoesNotExist:
264
                ipaddress = None
265
        attachments.append((network, ipaddress))
260 266
    for address in floating_ips:
261
        floating_ip = add_floating_ip_to_vm(vm=vm, address=address)
262
        network = floating_ip.network
263
        attachments.append((network, address))
267
        floating_ip = get_floating_ip(userid=vm.userid, address=address)
268
        attachments.append((floating_ip.network, floating_ip))
264 269
    for network_id in private_networks:
265
        network, address = None, None
266 270
        network = util.get_network(network_id, userid, non_deleted=True)
267 271
        if network.public:
268 272
            raise faults.Forbidden("Can not connect to public network")
269
        if network.dhcp:
270
            address = util.get_network_free_address(network)
271
        attachments.append((network, address))
273
        attachments.append((network, ipaddress))
272 274

  
273 275
    nics = []
274
    for index, (network, address) in enumerate(attachments):
276
    for index, (network, ipaddress) in enumerate(attachments):
275 277
        # Create VM's public NIC. Do not wait notification form ganeti
276 278
        # hooks to create this NIC, because if the hooks never run (e.g.
277 279
        # building error) the VM's public IP address will never be
278 280
        # released!
279
        nic = NetworkInterface.objects.create(machine=vm, network=network,
280
                                              index=index, ipv4=address,
281
        nic = NetworkInterface.objects.create(userid=userid, machine=vm,
282
                                              network=network, index=index,
281 283
                                              state="BUILDING")
284
        if ipaddress is not None:
285
            ipaddress.nic = nic
286
            ipaddress.save()
282 287
        nics.append(nic)
283 288
    return nics
284 289

  
......
427 432

  
428 433
@server_command("CONNECT")
429 434
def add_floating_ip(vm, address):
430
    floating_ip = add_floating_ip_to_vm(vm, address)
435
    floating_ip = get_floating_ip(userid=vm.userid, address=address)
431 436
    nic = NetworkInterface.objects.create(machine=vm,
432 437
                                          network=floating_ip.network,
433 438
                                          ipv4=floating_ip.ipv4,
......
438 443
    return backend.connect_to_network(vm, nic)
439 444

  
440 445

  
441
def add_floating_ip_to_vm(vm, address):
442
    """Get a floating IP by it's address and add it to VirtualMachine.
446
def get_floating_ip(userid, address):
447
    """Get a floating IP by it's address.
443 448

  
444
    Helper function for looking up a IPAddress by it's address and associating
445
    it with a VirtualMachine object (without adding the NIC in the Ganeti
446
    backend!). This function also checks if the floating IP is currently used
447
    by any instance and if it is available in the Backend that hosts the VM.
449
    Helper function for looking up a IPAddress by it's address. This function
450
    also checks if the floating IP is currently used by any instance.
448 451

  
449 452
    """
450
    user_id = vm.userid
451 453
    try:
452 454
        # Get lock in VM, to guarantee that floating IP will only by assigned
453 455
        # once
454
        floating_ip = IPAddress.objects.select_for_update()\
455
                                        .get(userid=user_id, ipv4=address,
456
                                             deleted=False)
456
        floating_ip = db_query.get_user_floating_ip(userid=userid,
457
                                                    address=address,
458
                                                    for_update=True)
457 459
    except IPAddress.DoesNotExist:
458
        raise faults.ItemNotFound("Floating IP '%s' does not exist" % address)
460
        raise faults.ItemNotFound("Floating IP with address '%s' does not"
461
                                  " exist" % address)
459 462

  
460
    if floating_ip.in_use():
463
    if floating_ip.nic is not None:
461 464
        raise faults.Conflict("Floating IP '%s' already in use" %
462 465
                              floating_ip.id)
463 466

  
464
    bnet = floating_ip.network.backend_networks.filter(backend=vm.backend_id)
465
    if not bnet.exists():
466
        msg = "Network '%s' is a floating IP pool, but it not connected"\
467
              " to backend '%s'" % (floating_ip.network, vm.backend)
468
        raise faults.ServiceUnavailable(msg)
469

  
470
    floating_ip.machine = vm
471
    floating_ip.save()
472 467
    return floating_ip
473 468

  
474 469

  
475 470
@server_command("DISCONNECT")
476 471
def remove_floating_ip(vm, address):
477
    user_id = vm.userid
478 472
    try:
479
        floating_ip = IPAddress.objects.select_for_update()\
480
                                        .get(userid=user_id, ipv4=address,
481
                                             deleted=False, machine=vm)
473
        floating_ip = db_query.get_server_floating_ip(server=vm,
474
                                                      address=address,
475
                                                      for_update=True)
482 476
    except IPAddress.DoesNotExist:
483
        raise faults.ItemNotFound("Floating IP '%s' does not exist" % address)
484

  
485
    try:
486
        nic = NetworkInterface.objects.get(machine=vm, ipv4=address)
487
    except NetworkInterface.DoesNotExist:
488
        raise faults.ItemNotFound("Floating IP '%s' is not attached to"
489
                                  "VM '%s'" % (floating_ip, vm))
477
        raise faults.BadRequest("Server '%s' has no floating ip with"
478
                                " address '%s'" % (vm, address))
490 479

  
480
    nic = floating_ip.nic
491 481
    log.info("Removing NIC %s from VM %s. Floating IP '%s'", str(nic.index),
492 482
             vm, floating_ip)
493 483

  
b/snf-cyclades-app/synnefo/logic/tests/servers.py
122 122
        mfactory.BackendNetworkFactory(network=net, backend=vm.backend)
123 123
        mrapi().ModifyInstance.return_value = 42
124 124
        servers.connect(vm, net)
125
        pool = net.get_pool(with_lock=False)
125
        pool = net.get_pool(locked=False)
126 126
        self.assertFalse(pool.is_available("192.168.2.2"))
127 127
        args, kwargs = mrapi().ModifyInstance.call_args
128 128
        nics = kwargs["nics"][0]
......
140 140
                                      dhcp=False)
141 141
        mfactory.BackendNetworkFactory(network=net, backend=vm.backend)
142 142
        servers.connect(vm, net)
143
        pool = net.get_pool(with_lock=False)
143
        pool = net.get_pool(locked=False)
144 144
        self.assertTrue(pool.is_available("192.168.2.2"))
145 145
        args, kwargs = mrapi().ModifyInstance.call_args
146 146
        nics = kwargs["nics"][0]
b/snf-cyclades-app/synnefo/quotas/__init__.py
298 298
    elif isinstance(resource, Network):
299 299
        return {"cyclades.network.private": 1}
300 300
    elif isinstance(resource, IPAddress):
301
        return {"cyclades.floating_ip": 1}
301
        if resource.floating_ip:
302
            return {"cyclades.floating_ip": 1}
302 303
    else:
303 304
        raise ValueError("Unknown Resource '%s'" % resource)
304 305

  

Also available in: Unified diff