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