Revision 3278725f
b/snf-cyclades-app/synnefo/db/models.py | ||
---|---|---|
545 | 545 |
raise pools.EmptyPool |
546 | 546 |
return subnet.allocate_address(userid) |
547 | 547 |
|
548 |
def reserve_address(self, address): |
|
548 |
def reserve_address(self, address, external=False):
|
|
549 | 549 |
pool = self.get_pool() |
550 |
pool.reserve(address) |
|
550 |
pool.reserve(address, external=external)
|
|
551 | 551 |
pool.save() |
552 | 552 |
|
553 | 553 |
def release_address(self, address): |
b/snf-cyclades-app/synnefo/logic/backend.py | ||
---|---|---|
37 | 37 |
from synnefo.db.models import (Backend, VirtualMachine, Network, |
38 | 38 |
BackendNetwork, BACKEND_STATUSES, |
39 | 39 |
pooled_rapi_client, VirtualMachineDiagnostic, |
40 |
Flavor, IPAddress)
|
|
40 |
Flavor) |
|
41 | 41 |
from synnefo.logic import utils |
42 | 42 |
from synnefo import quotas |
43 |
from synnefo.api.util import release_resource |
|
43 |
from synnefo.api.util import release_resource, allocate_ip
|
|
44 | 44 |
from synnefo.util.mac2eui64 import mac2eui64 |
45 | 45 |
from synnefo.logic.rapi import GanetiApiError |
46 | 46 |
|
... | ... | |
59 | 59 |
# stale and removed from DB. |
60 | 60 |
BUILDING_NIC_TIMEOUT = timedelta(seconds=180) |
61 | 61 |
|
62 |
NIC_FIELDS = ["state", "mac", "ipv4_address", "ipv6_address", "network", |
|
63 |
"firewall_profile", "index"] |
|
62 |
SIMPLE_NIC_FIELDS = ["state", "mac", "network", "firewall_profile", "index"] |
|
63 |
COMPLEX_NIC_FIELDS = ["ipv4_address", "ipv6_address"] |
|
64 |
NIC_FIELDS = SIMPLE_NIC_FIELDS + COMPLEX_NIC_FIELDS |
|
64 | 65 |
UNKNOWN_NIC_PREFIX = "unknown-" |
65 | 66 |
|
66 | 67 |
|
... | ... | |
178 | 179 |
# VM has been deleted |
179 | 180 |
for nic in vm.nics.all(): |
180 | 181 |
# Release the IP |
181 |
release_nic_address(nic)
|
|
182 |
remove_nic_ips(nic)
|
|
182 | 183 |
# And delete the NIC. |
183 | 184 |
nic.delete() |
184 | 185 |
vm.deleted = True |
... | ... | |
231 | 232 |
|
232 | 233 |
""" |
233 | 234 |
ganeti_nics = process_ganeti_nics(nics) |
234 |
db_nics = dict([(nic.id, nic) for nic in vm.nics.all()]) |
|
235 |
db_nics = dict([(nic.id, nic) |
|
236 |
for nic in vm.nics.prefetch_related("ips__subnet")]) |
|
235 | 237 |
|
236 | 238 |
# Get X-Lock on backend before getting X-Lock on network IP pools, to |
237 | 239 |
# guarantee that no deadlock will occur with Backend allocator. |
... | ... | |
248 | 250 |
if db_nic.state != "BUILDING" or\ |
249 | 251 |
(db_nic.state == "BUILDING" and |
250 | 252 |
etime > db_nic.created + BUILDING_NIC_TIMEOUT): |
251 |
release_nic_address(db_nic)
|
|
253 |
remove_nic_ips(db_nic)
|
|
252 | 254 |
db_nic.delete() |
253 | 255 |
else: |
254 | 256 |
log.warning("Ignoring recent building NIC: %s", db_nic) |
255 | 257 |
elif db_nic is None: |
256 |
# NIC exists in Ganeti but not in DB |
|
257 |
if str(nic_name).startswith(UNKNOWN_NIC_PREFIX): |
|
258 |
msg = "Can not process NIC! NIC '%s' does not have a"\ |
|
259 |
" valid name." % ganeti_nic |
|
260 |
log.error(msg) |
|
261 |
continue |
|
262 |
ipaddress = None |
|
263 |
network = ganeti_nic["network"] |
|
264 |
ipv4_address = ganeti_nic["ipv4_address"] |
|
265 |
if ipv4_address: |
|
266 |
network.reserve_address(ipv4_address) |
|
267 |
subnet = network.subnets.get(ipversion=4) |
|
268 |
ipaddress = IPAddress.objects.create(address=ipv4_address, |
|
269 |
network=network, |
|
270 |
subnet=subnet, |
|
271 |
userid=vm.userid) |
|
272 |
# TODO |
|
273 |
ganeti_nic.pop("ipv4_address") |
|
274 |
ganeti_nic.pop("ip", None) |
|
275 |
ganeti_nic.pop("ipv6_address") |
|
276 |
nic = vm.nics.create(id=nic_name, **ganeti_nic) |
|
277 |
if ipaddress is not None: |
|
278 |
ipaddress.nic = nic |
|
279 |
ipaddress.save() |
|
258 |
msg = ("NIC/%s of VM %s does not exist in DB! Cannot automatically" |
|
259 |
" fix this issue!" % (nic_name, vm)) |
|
260 |
log.error(msg) |
|
261 |
continue |
|
280 | 262 |
elif not nics_are_equal(db_nic, ganeti_nic): |
263 |
for f in SIMPLE_NIC_FIELDS: |
|
264 |
# Update the NIC in DB with the values from Ganeti NIC |
|
265 |
setattr(db_nic, f, ganeti_nic[f]) |
|
266 |
db_nic.save() |
|
281 | 267 |
# Special case where the IPv4 address has changed, because you |
282 | 268 |
# need to release the old IPv4 address and reserve the new one |
283 | 269 |
ipv4_address = ganeti_nic["ipv4_address"] |
284 | 270 |
if db_nic.ipv4_address != ipv4_address: |
285 |
release_nic_address(db_nic)
|
|
271 |
remove_nic_ips(db_nic)
|
|
286 | 272 |
if ipv4_address: |
287 | 273 |
network = ganeti_nic["network"] |
288 |
network.reserve_address(ipv4_address) |
|
289 |
subnet = network.subnets.get(ipversion=4) |
|
290 |
ipaddress, _ =\ |
|
291 |
IPAddress.objects.get_or_create(network=network, |
|
292 |
subnet=subnet, |
|
293 |
userid=vm.userid, |
|
294 |
address=ipv4_address) |
|
274 |
ipaddress = allocate_ip(network, vm.userid, |
|
275 |
address=ipv4_address) |
|
295 | 276 |
ipaddress.nic = nic |
296 | 277 |
ipaddress.save() |
297 | 278 |
|
298 |
for f in ["state", "mac", "network", "firewall_profile", "index"]: |
|
299 |
# Update the NIC in DB with the values from Ganeti NIC |
|
300 |
setattr(db_nic, f, ganeti_nic[f]) |
|
301 |
db_nic.save() |
|
302 |
|
|
303 |
# Dummy update the network, to work with 'changed-since' |
|
304 |
db_nic.network.save() |
|
305 |
|
|
306 | 279 |
vm.backendtime = etime |
307 | 280 |
vm.save() |
308 | 281 |
|
... | ... | |
353 | 326 |
return dict(new_nics) |
354 | 327 |
|
355 | 328 |
|
356 |
def release_nic_address(nic):
|
|
357 |
"""Release the IPv4 address of a NIC.
|
|
329 |
def remove_nic_ips(nic):
|
|
330 |
"""Remove IP addresses associated with a NetworkInterface.
|
|
358 | 331 |
|
359 |
Check if an instance's NIC has an IPv4 address and release it if it is not
|
|
360 |
a Floating IP. If it is as Floating IP, then disassociate the FloatingIP
|
|
361 |
from the machine.
|
|
332 |
Remove all IP addresses that are associated with the NetworkInterface
|
|
333 |
object, by returning them to the pool and deleting the IPAddress object. If
|
|
334 |
the IP is a floating IP, then it is just disassociated from the NIC.
|
|
362 | 335 |
|
363 | 336 |
""" |
364 | 337 |
|
365 | 338 |
for ip in nic.ips.all(): |
366 |
if ip.subnet.ipversion == 4:
|
|
339 |
if ip.ipversion == 4: |
|
367 | 340 |
if ip.floating_ip: |
368 | 341 |
ip.nic = None |
369 | 342 |
ip.save() |
370 | 343 |
else: |
371 |
ip.network.release_address(ip.address) |
|
372 |
ip.delete() |
|
373 |
else: |
|
344 |
ip.release_address() |
|
345 |
if not ip.floating_ip: |
|
374 | 346 |
ip.delete() |
375 | 347 |
|
376 | 348 |
|
... | ... | |
488 | 460 |
|
489 | 461 |
add_reserved_ips = job_fields.get("add_reserved_ips") |
490 | 462 |
if add_reserved_ips: |
491 |
net = back_network.network |
|
492 |
pool = net.get_pool() |
|
493 |
if add_reserved_ips: |
|
494 |
for ip in add_reserved_ips: |
|
495 |
pool.reserve(ip, external=True) |
|
496 |
pool.save() |
|
463 |
network = back_network.network |
|
464 |
for ip in add_reserved_ips: |
|
465 |
network.reserve_address(ip, external=True) |
|
497 | 466 |
|
498 | 467 |
if status == 'success': |
499 | 468 |
back_network.backendtime = etime |
... | ... | |
751 | 720 |
subnet6 = None |
752 | 721 |
gateway = None |
753 | 722 |
gateway6 = None |
754 |
for dbsubnet in network.subnets.all():
|
|
755 |
if dbsubnet.ipversion == 4:
|
|
756 |
if dbsubnet.dhcp:
|
|
723 |
for _subnet in network.subnets.all():
|
|
724 |
if _subnet.ipversion == 4:
|
|
725 |
if _subnet.dhcp:
|
|
757 | 726 |
tags.append('nfdhcpd') |
758 |
subnet = dbsubnet.cidr
|
|
759 |
gateway = dbsubnet.gateway
|
|
760 |
elif dbsubnet.ipversion == 6:
|
|
761 |
subnet6 = dbsubnet.cidr
|
|
762 |
gateway6 = dbsubnet.gateway
|
|
727 |
subnet = _subnet.cidr
|
|
728 |
gateway = _subnet.gateway
|
|
729 |
elif _subnet.ipversion == 6:
|
|
730 |
subnet6 = _subnet.cidr
|
|
731 |
gateway6 = _subnet.gateway
|
|
763 | 732 |
|
764 | 733 |
if network.public: |
765 | 734 |
conflicts_check = True |
b/snf-cyclades-app/synnefo/logic/reconciliation.py | ||
---|---|---|
396 | 396 |
|
397 | 397 |
|
398 | 398 |
def get_database_servers(backend): |
399 |
servers = backend.virtual_machines.select_related("nics", "flavor")\ |
|
399 |
servers = backend.virtual_machines.select_related("flavor")\ |
|
400 |
.prefetch_related("nics__ips__subnet")\ |
|
400 | 401 |
.filter(deleted=False) |
401 | 402 |
return dict([(s.id, s) for s in servers]) |
402 | 403 |
|
b/snf-cyclades-app/synnefo/logic/tests/callbacks.py | ||
---|---|---|
44 | 44 |
from synnefo.logic.callbacks import (update_db, update_network, |
45 | 45 |
update_build_progress) |
46 | 46 |
from snf_django.utils.testing import mocked_quotaholder |
47 |
from django.conf import settings |
|
48 | 47 |
from synnefo.logic.rapi import GanetiApiError |
49 | 48 |
|
50 | 49 |
now = datetime.now |
... | ... | |
362 | 361 |
db_vm = VirtualMachine.objects.get(id=vm.id) |
363 | 362 |
self.assertEqual(len(db_vm.nics.all()), 0) |
364 | 363 |
|
365 |
def test_empty_nic(self, client): |
|
366 |
vm = mfactory.VirtualMachineFactory(operstate='ERROR') |
|
367 |
for public in [True, False]: |
|
368 |
net = mfactory.NetworkFactory(public=public) |
|
369 |
msg = self.create_msg(instance_nics=[{'network': net.backend_id, |
|
370 |
'name': 'snf-nic-100'}], |
|
371 |
instance=vm.backend_vm_id) |
|
372 |
update_db(client, msg) |
|
373 |
self.assertTrue(client.basic_ack.called) |
|
374 |
db_vm = VirtualMachine.objects.get(id=vm.id) |
|
375 |
nics = db_vm.nics.all() |
|
376 |
self.assertEqual(len(nics), 1) |
|
377 |
self.assertEqual(nics[0].index, 0) |
|
378 |
self.assertEqual(nics[0].ipv4_address, None) |
|
379 |
self.assertEqual(nics[0].ipv6_address, None) |
|
380 |
self.assertEqual(nics[0].mac, None) |
|
381 |
if public: |
|
382 |
self.assertEqual(nics[0].firewall_profile, |
|
383 |
settings.DEFAULT_FIREWALL_PROFILE) |
|
384 |
else: |
|
385 |
self.assertEqual(nics[0].firewall_profile, None) |
|
386 |
|
|
387 |
def test_full_nic(self, client): |
|
388 |
vm = mfactory.VirtualMachineFactory(operstate='ERROR') |
|
389 |
net = mfactory.NetworkWithSubnetFactory(subnet__cidr='10.0.0.0/24', |
|
390 |
subnet__gateway="10.0.0.1", |
|
391 |
subnet6=None) |
|
392 |
pool = net.get_pool() |
|
393 |
self.assertTrue(pool.is_available('10.0.0.22')) |
|
364 |
def test_changed_nic(self, client): |
|
365 |
ip = mfactory.IPv4AddressFactory(subnet__cidr="10.0.0.0/24", |
|
366 |
address="10.0.0.2") |
|
367 |
network = ip.network |
|
368 |
subnet = ip.subnet |
|
369 |
vm = ip.nic.machine |
|
370 |
pool = subnet.get_pool() |
|
371 |
pool.reserve("10.0.0.2") |
|
394 | 372 |
pool.save() |
395 |
msg = self.create_msg(instance_nics=[{'network': net.backend_id, |
|
396 |
'ip': '10.0.0.22', |
|
373 |
|
|
374 |
msg = self.create_msg(instance_nics=[{'network': network.backend_id, |
|
375 |
'ip': '10.0.0.3', |
|
397 | 376 |
'mac': 'aa:bb:cc:00:11:22', |
398 |
'name': 'snf-nic-200'}],
|
|
377 |
'name': ip.nic.backend_uuid}],
|
|
399 | 378 |
instance=vm.backend_vm_id) |
400 | 379 |
update_db(client, msg) |
401 | 380 |
self.assertTrue(client.basic_ack.called) |
... | ... | |
403 | 382 |
nics = db_vm.nics.all() |
404 | 383 |
self.assertEqual(len(nics), 1) |
405 | 384 |
self.assertEqual(nics[0].index, 0) |
406 |
self.assertEqual(nics[0].ipv4_address, '10.0.0.22') |
|
407 |
self.assertEqual(nics[0].ipv6_address, None) |
|
385 |
self.assertEqual(nics[0].ipv4_address, '10.0.0.3') |
|
408 | 386 |
self.assertEqual(nics[0].mac, 'aa:bb:cc:00:11:22') |
409 |
pool = net.get_pool() |
|
410 |
self.assertFalse(pool.is_available('10.0.0.22')) |
|
387 |
pool = subnet.get_pool() |
|
388 |
self.assertTrue(pool.is_available('10.0.0.2')) |
|
389 |
self.assertFalse(pool.is_available('10.0.0.3')) |
|
411 | 390 |
pool.save() |
412 | 391 |
|
413 | 392 |
|
Also available in: Unified diff