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