Revision 72dea98f

b/snf-cyclades-app/synnefo/api/floating_ips.py
144 144
            address = util.get_network_free_address(network)  # Get X-Lock
145 145
        else:
146 146
            if FloatingIP.objects.filter(network=network,
147
                                         deleted=False,
147 148
                                         ipv4=address).exists():
148 149
                msg = "Floating IP '%s' is reserved" % address
149 150
                raise faults.Conflict(msg)
......
190 191
        raise faults.ItemNotFound("Floating IP '%s' does not exist" %
191 192
                                  floating_ip_id)
192 193

  
194
    # Since we have got an exlusively lock in floating IP, and since
195
    # to remove a floating IP you need the same lock, the in_use() query
196
    # is safe
193 197
    if floating_ip.in_use():
194 198
        msg = "Floating IP '%s' is used" % floating_ip.id
195 199
        raise faults.Conflict(message=msg)
b/snf-cyclades-app/synnefo/logic/backend.py
35 35
from datetime import datetime
36 36

  
37 37
from synnefo.db.models import (Backend, VirtualMachine, Network,
38
                               FloatingIP,
38 39
                               BackendNetwork, BACKEND_STATUSES,
39 40
                               pooled_rapi_client, VirtualMachineDiagnostic,
40 41
                               Flavor)
......
160 161
        # See ticket #799 for all the details.
161 162
        if status == 'success' or (status == 'error' and
162 163
                                   vm.operstate == 'ERROR'):
163
            _process_net_status(vm, etime, nics=[])
164
            # VM has been deleted. Release the instance IPs
165
            release_instance_ips(vm, [])
166
            # And delete the releated NICs (must be performed after release!)
167
            vm.nics.all().delete()
164 168
            vm.deleted = True
165 169
            vm.operstate = state_for_success
166 170
            vm.backendtime = etime
......
220 224
    # guarantee that no deadlock will occur with Backend allocator.
221 225
    Backend.objects.select_for_update().get(id=vm.backend_id)
222 226

  
223
    release_instance_nics(vm)
227
    # NICs have changed. Release the instance IPs
228
    release_instance_ips(vm, ganeti_nics)
229
    # And delete the releated NICs (must be performed after release!)
230
    vm.nics.all().delete()
224 231

  
225 232
    for nic in ganeti_nics:
226 233
        ipv4 = nic.get('ipv4', '')
......
286 293
    return False
287 294

  
288 295

  
289
def release_instance_nics(vm):
290
    for nic in vm.nics.all():
291
        net = nic.network
292
        if nic.ipv4:
293
            net.release_address(nic.ipv4)
294
        nic.delete()
295
        net.save()
296
def release_instance_ips(vm, ganeti_nics):
297
    old_addresses = set(vm.nics.values_list("network", "ipv4"))
298
    new_addresses = set(map(lambda nic: (nic["network"], nic["ipv4"]),
299
                            ganeti_nics))
300
    to_release = old_addresses - new_addresses
301
    for (network_id, ipv4) in to_release:
302
        if ipv4:
303
            net = Network.objects.get(id=network_id)
304
            # Important: Take exclusive lock in pool before checking if there
305
            # is a floating IP with this ipv4 address, otherwise there is a
306
            # race condition, where you may release a floating IP that has been
307
            # created after search floating IPs and before you get exclusively
308
            # the pool
309
            pool = net.get_pool()
310
            try:
311
                floating_ip = net.floating_ips.select_for_update()\
312
                                              .get(ipv4=ipv4, machine=vm,
313
                                                   deleted=False)
314
                floating_ip.machine = None
315
                floating_ip.save()
316
            except FloatingIP.DoesNotExist:
317
                net.release_address(ipv4)
318
                pool.save()
296 319

  
297 320

  
298 321
@transaction.commit_on_success
b/snf-cyclades-app/synnefo/logic/tests.py
254 254
    def test_remove(self, client):
255 255
        vm = mfactory.VirtualMachineFactory()
256 256
        # Also create a NIC
257
        mfactory.NetworkInterfaceFactory(machine=vm)
257
        nic = mfactory.NetworkInterfaceFactory(machine=vm)
258
        nic.network.get_pool().reserve(nic.ipv4)
258 259
        msg = self.create_msg(operation='OP_INSTANCE_REMOVE',
259 260
                              instance=vm.backend_vm_id)
260 261
        with mocked_quotaholder():
......
265 266
        self.assertTrue(db_vm.deleted)
266 267
        # Check that nics are deleted
267 268
        self.assertFalse(db_vm.nics.all())
269
        self.assertTrue(nic.network.get_pool().is_available(nic.ipv4))
270
        vm2 = mfactory.VirtualMachineFactory()
271
        network = mfactory.NetworkFactory()
272
        fp1 = mfactory.FloatingIPFactory(machine=vm2, network=network)
273
        fp2 = mfactory.FloatingIPFactory(machine=vm2, network=network)
274
        mfactory.NetworkInterfaceFactory(machine=vm2, network=network,
275
                ipv4=fp1.ipv4)
276
        mfactory.NetworkInterfaceFactory(machine=vm2, network=network,
277
                ipv4=fp2.ipv4)
278
        pool = network.get_pool()
279
        pool.reserve(fp1.ipv4)
280
        pool.reserve(fp2.ipv4)
281
        pool.save()
282
        msg = self.create_msg(operation='OP_INSTANCE_REMOVE',
283
                              instance=vm2.backend_vm_id)
284
        with mocked_quotaholder():
285
            update_db(client, msg)
286
        client.basic_ack.assert_called_once()
287
        db_vm = VirtualMachine.objects.get(id=vm.id)
288
        self.assertEqual(db_vm.operstate, 'DESTROYED')
289
        self.assertTrue(db_vm.deleted)
290
        self.assertEqual(FloatingIP.objects.get(id=fp1.id).machine, None)
291
        self.assertEqual(FloatingIP.objects.get(id=fp2.id).machine, None)
292
        pool = network.get_pool()
293
        # Test that floating ips are not released
294
        self.assertFalse(pool.is_available(fp1.ipv4))
295
        self.assertFalse(pool.is_available(fp2.ipv4))
268 296

  
269 297
    def test_create(self, client):
270 298
        vm = mfactory.VirtualMachineFactory()

Also available in: Unified diff