Revision 9e8be4fb

b/snf-cyclades-app/synnefo/api/floating_ips.py
126 126
    log.info('allocate_floating_ip %s', req)
127 127

  
128 128
    userid = request.user_uniq
129
    try:
130
        pool = req['pool']
131
    except KeyError:
132
        raise faults.BadRequest("Malformed request. Missing"
133
                                " 'pool' attribute")
134

  
135
    try:
136
        objects = Network.objects.select_for_update()
137
        network = objects.get(id=pool, public=True, deleted=False)
138
    except Network.DoesNotExist:
139
        raise faults.ItemNotFound("Pool '%s' does not exist." % pool)
140

  
129
    pool = req.get("pool", None)
141 130
    address = req.get("address", None)
142 131
    machine = None
132
    net_objects = Network.objects.select_for_update().filter(public=True,
133
                                                             deleted=False)
143 134
    try:
144
        if address is None:
145
            address = util.get_network_free_address(network)  # Get X-Lock
135
        if pool is None:
136
            # User did not specified a pool. Choose a random public IP
137
            network, address = util.allocate_public_ip(net_objects)
146 138
        else:
147
            if FloatingIP.objects.filter(network=network,
148
                                         deleted=False,
149
                                         ipv4=address).exists():
150
                msg = "Floating IP '%s' is reserved" % address
151
                raise faults.Conflict(msg)
152
            pool = network.get_pool()  # Gets X-Lock
153
            if not pool.contains(address):
154
                raise faults.BadRequest("Invalid address")
155
            if not pool.is_available(address):
156
                try:
157
                    network.nics.get(ipv4=address,
158
                                     machine__userid=userid)
159
                except NetworkInterface.DoesNotExist:
160
                    msg = "Address '%s' is already in use" % address
139
            try:
140
                network = net_objects.get(id=pool)
141
            except Network.DoesNotExist:
142
                raise faults.ItemNotFound("Pool '%s' does not exist." % pool)
143
            if address is None:
144
                # User did not specified an IP address. Choose a random one
145
                # Gets X-Lock on IP pool
146
                address = util.get_network_free_address(network)
147
            else:
148
                # User specified an IP address. Check that it is not a used
149
                # floating IP
150
                if FloatingIP.objects.filter(network=network,
151
                                             deleted=False,
152
                                             ipv4=address).exists():
153
                    msg = "Floating IP '%s' is reserved" % address
161 154
                    raise faults.Conflict(msg)
162
            pool.reserve(address)
163
            pool.save()
155
                pool = network.get_pool()  # Gets X-Lock
156
                # Check address belongs to pool
157
                if not pool.contains(address):
158
                    raise faults.BadRequest("Invalid address")
159
                if pool.is_available(address):
160
                    pool.reserve(address)
161
                    pool.save()
162
                # If address is not available, check that it belongs to the
163
                # same user
164
                elif not network.nics.filter(ipv4=address,
165
                                            machine__userid=userid).exists():
166
                        msg = "Address '%s' is already in use" % address
167
                        raise faults.Conflict(msg)
164 168
        floating_ip = FloatingIP.objects.create(ipv4=address, network=network,
165 169
                                                userid=userid, machine=machine)
166 170
        quotas.issue_and_accept_commission(floating_ip)
b/snf-cyclades-app/synnefo/api/test/floating_ips.py
37 37
from synnefo.db.models import FloatingIP
38 38
from synnefo.db.models_factory import (FloatingIPFactory, NetworkFactory,
39 39
                                       VirtualMachineFactory,
40
                                       NetworkInterfaceFactory)
40
                                       NetworkInterfaceFactory,
41
                                       BackendNetworkFactory)
41 42
from mock import patch, Mock
42 43

  
43 44

  
......
100 101
                         {"instance_id": None, "ip": "192.168.2.1",
101 102
                          "fixed_ip": None, "id": "1", "pool": "1"})
102 103

  
104
    def test_reserve_no_pool(self):
105
        # No networks
106
        with mocked_quotaholder():
107
            response = self.post(URL, "test_user", json.dumps({}), "json")
108
        self.assertFault(response, 413, "overLimit")
109
        # Full network
110
        net = NetworkFactory(userid="test_user", subnet="192.168.2.0/32",
111
                             gateway=None, public=True)
112
        with mocked_quotaholder():
113
            response = self.post(URL, "test_user", json.dumps({}), "json")
114
        self.assertFault(response, 413, "overLimit")
115
        # Success
116
        net2 = NetworkFactory(userid="test_user", subnet="192.168.2.0/24",
117
                              gateway=None, public=True)
118
        with mocked_quotaholder():
119
            response = self.post(URL, "test_user", json.dumps({}), "json")
120
        self.assertEqual(json.loads(response.content)["floating_ip"],
121
                         {"instance_id": None, "ip": "192.168.2.1",
122
                          "fixed_ip": None, "id": "1", "pool": str(net2.id)})
123

  
103 124
    def test_reserve_full(self):
104 125
        net = NetworkFactory(userid="test_user", subnet="192.168.2.0/32",
105 126
                             gateway=None, public=True)
......
186 207

  
187 208
    @patch("synnefo.logic.backend", Mock())
188 209
    def test_delete_network_with_floating_ips(self):
189
        ip = FloatingIPFactory(machine=None)
210
        ip = FloatingIPFactory(machine=None, network__flavor="IP_LESS_ROUTED")
190 211
        net = ip.network
191 212
        # Can not remove network with floating IPs
192 213
        with mocked_quotaholder():
......
247 268
        # In use
248 269
        vm1 = VirtualMachineFactory()
249 270
        ip1 = FloatingIPFactory(userid=self.vm.userid, machine=vm1)
271
        BackendNetworkFactory(network=ip1.network, backend=vm1.backend,
272
                              operstate='ACTIVE')
250 273
        request = {"addFloatingIp": {"address": ip1.ipv4}}
251 274
        response = self.post(url, self.vm.userid, json.dumps(request), "json")
252 275
        self.assertFault(response, 409, "conflict")
253 276
        # Success
254 277
        ip1 = FloatingIPFactory(userid=self.vm.userid, machine=None)
278
        BackendNetworkFactory(network=ip1.network, backend=self.vm.backend,
279
                              operstate='ACTIVE')
255 280
        request = {"addFloatingIp": {"address": ip1.ipv4}}
256 281
        response = self.post(url, self.vm.userid, json.dumps(request), "json")
257 282
        self.assertEqual(response.status_code, 202)
b/snf-cyclades-app/synnefo/api/util.py
213 213
    return disk_template, provider
214 214

  
215 215

  
216
def get_network(network_id, user_id, for_update=False):
216
def get_network(network_id, user_id, for_update=False, non_deleted=False):
217 217
    """Return a Network instance or raise ItemNotFound."""
218 218

  
219 219
    try:
......
221 221
        objects = Network.objects
222 222
        if for_update:
223 223
            objects = objects.select_for_update()
224
        return objects.get(Q(userid=user_id) | Q(public=True), id=network_id)
224
        network = objects.get(Q(userid=user_id) | Q(public=True),
225
                              id=network_id)
226
        if non_deleted and network.deleted:
227
            raise faults.BadRequest("Networkhas been deleted.")
228
        return network
225 229
    except (ValueError, Network.DoesNotExist):
226 230
        raise faults.ItemNotFound('Network not found.')
227 231

  
......
336 340
    return address
337 341

  
338 342

  
343
def allocate_public_ip(networks=None):
344
    """Allocate an IP address from public networks."""
345
    if networks is None:
346
        networks = Network.objects.select_for_update().filter(public=True,
347
                                                              deleted=False)
348
    for network in networks:
349
        try:
350
            address = get_network_free_address(network)
351
        except:
352
            pass
353
        else:
354
            return network, address
355
    msg = "Can not allocate public IP. Public networks are full."
356
    log.error(msg)
357
    raise faults.OverLimit(msg)
358

  
359

  
339 360
def get_nic(machine, network):
340 361
    try:
341 362
        return NetworkInterface.objects.get(machine=machine, network=network)

Also available in: Unified diff