Revision 0e0a6ef9

b/snf-cyclades-app/synnefo/db/models.py
532 532
            if not backend_exists:
533 533
                BackendNetwork.objects.create(backend=backend, network=self)
534 534

  
535
    def get_pool(self, locked=True):
536
        try:
537
            subnet = self.subnets.get(ipversion=4, deleted=False)
538
        except Subnet.DoesNotExist:
539
            raise pools.EmptyPool
540
        return subnet.get_pool(locked=locked)
541

  
542
    def allocate_address(self, userid):
543
        try:
544
            subnet = self.subnets.get(ipversion=4, deleted=False)
545
        except Subnet.DoesNotExist:
546
            raise pools.EmptyPool
547
        return subnet.allocate_address(userid)
535
    def get_ip_pools(self, locked=True):
536
        subnets = self.subnets.filter(ipversion=4, deleted=False)\
537
                              .prefetch_related("ip_pools")
538
        return [ip_pool for subnet in subnets
539
                for ip_pool in subnet.get_ip_pools(locked=locked)]
548 540

  
549 541
    def reserve_address(self, address, external=False):
550
        pool = self.get_pool()
551
        pool.reserve(address, external=external)
552
        pool.save()
542
        for ip_pool in self.get_ip_pools():
543
            if ip_pool.contains(address):
544
                ip_pool.reserve(address, external=external)
545
                ip_pool.save()
546
                return
547
        raise pools.InvalidValue("Network %s does not have an IP pool that"
548
                                 " contains address %s" % (self, address))
553 549

  
554 550
    def release_address(self, address, external=True):
555
        pool = self.get_pool()
556
        pool.put(address, external=external)
557
        pool.save()
551
        for ip_pool in self.get_ip_pools():
552
            if ip_pool.contains(address):
553
                ip_pool.release(address, external=external)
554
                ip_pool.save()
555
                return
556
        raise pools.InvalidValue("Network %s does not have an IP pool that"
557
                                 " contains address %s" % (self, address))
558 558

  
559 559
    @property
560 560
    def subnet4(self):
......
571 571

  
572 572
    def ip_count(self):
573 573
        """Return the total and free IPv4 addresses of the network."""
574
        subnets = self.subnets.filter(ipversion=4).prefetch_related("ip_pools")
575 574
        total, free = 0, 0
576
        for subnet in subnets:
577
            for ip_pool in subnet.ip_pools.all():
578
                pool = ip_pool.pool
579
                total += pool.pool_size
580
                free += pool.count_available()
575
        ip_pools = self.get_ip_pools(locked=False)
576
        for ip_pool in ip_pools:
577
            total += ip_pool.pool_size
578
            free += ip_pool.count_available()
581 579
        return total, free
582 580

  
583 581
    class InvalidBackendIdError(Exception):
......
624 622
        msg = u"<Subnet %s, Network: %s, CIDR: %s>"
625 623
        return msg % (self.id, self.network_id, self.cidr)
626 624

  
627
    def get_pool(self, locked=True):
628
        if self.ipversion == 6:
629
            raise Exception("IPv6 Subnets have no IP Pool.")
625
    def get_ip_pools(self, locked=True):
630 626
        ip_pools = self.ip_pools
631 627
        if locked:
632 628
            ip_pools = ip_pools.select_for_update()
633
        return ip_pools.all()[0].pool
634

  
635
    def allocate_address(self, userid):
636
        pool = self.get_pool(locked=True)
637
        address = pool.get()
638
        pool.save()
639
        return IPAddress.objects.create(network=self.network, subnet=self,
640
                                        address=address, userid=userid)
629
        return map(lambda ip_pool: ip_pool.pool, ip_pools.all())
641 630

  
642 631

  
643 632
class BackendNetwork(models.Model):
b/snf-cyclades-app/synnefo/logic/reconciliation.py
508 508
        corresponding Ganeti networks in all Ganeti backends.
509 509

  
510 510
        """
511
        network_ip_pool = network.get_pool()  # X-Lock on IP Pool
511
        ip_pools = network.get_ip_pools()  # X-Lock on IP pools
512 512
        for bend in self.backends:
513 513
            bnet = get_backend_network(network, bend)
514 514
            gnet = self.ganeti_networks[bend].get(network.id)
......
563 563
            if externally_reserved:
564 564
                for ip in externally_reserved.split(","):
565 565
                    ip = ip.strip()
566
                    if not network_ip_pool.is_reserved(ip):
567
                        msg = ("D: IP '%s' is reserved for network '%s' in"
568
                               " backend '%s' but not in DB.")
569
                        self.log.info(msg, ip, network, bend)
570
                        if self.fix:
571
                            network_ip_pool.reserve(ip, external=True)
572
                            network_ip_pool.save()
573
                            self.log.info("F: Reserved IP '%s'", ip)
566
                    for ip_pool in ip_pools:
567
                        if ip_pool.contains(ip):
568
                            if not ip_pool.is_reserved(ip):
569
                                msg = ("D: IP '%s' is reserved for network"
570
                                       " '%s' in backend '%s' but not in DB.")
571
                                self.log.info(msg, ip, network, bend)
572
                                if self.fix:
573
                                    ip_pool.reserve(ip, external=True)
574
                                    ip_pool.save()
575
                                    self.log.info("F: Reserved IP '%s'", ip)
574 576

  
575 577
    def reconcile_parted_network(self, network, backend):
576 578
        self.log.info("D: Missing DB entry for network %s in backend %s",
......
699 701
    @transaction.commit_on_success
700 702
    def reconcile_ip_pool(self, network):
701 703
        # Check that all NICs have unique IPv4 address
702
        nics = network.nics.filter(ipv4__isnull=False)
703
        check_unique_values(objects=nics, field='ipv4', logger=self.log)
704

  
705
        # Check that all Floating IPs have unique IPv4 address
706
        floating_ips = network.floating_ips.filter(deleted=False)
707
        check_unique_values(objects=floating_ips, field='ipv4',
708
                            logger=self.log)
709

  
710
        # First get(lock) the IP pool of the network to prevent new NICs
711
        # from being created.
712
        network_ip_pool = network.get_pool()
713
        used_ips = set(list(nics.values_list("ipv4", flat=True)) +
714
                       list(floating_ips.values_list("ipv4", flat=True)))
715

  
716
        check_pool_consistent(pool=network_ip_pool,
717
                              pool_class=pools.IPPool,
718
                              used_values=used_ips,
719
                              fix=self.fix, logger=self.log)
704
        nics = network.ips.all()
705
        check_unique_values(objects=nics, field="address", logger=self.log)
706

  
707
        for ip_pool in network.get_ip_pools():
708
            used_ips = ip_pool.pool_table.subnet.ips.values_list("address",
709
                                                                 flat=True)
710
            used_ips = filter(lambda x: ip_pool.contains(x), used_ips)
711
            check_pool_consistent(pool=ip_pool,
712
                                  pool_class=pools.IPPool,
713
                                  used_values=used_ips,
714
                                  fix=self.fix, logger=self.log)
720 715

  
721 716

  
722 717
def check_unique_values(objects, field, logger):

Also available in: Unified diff