Ejemplo n.º 1
0
    def validate_static_ip(self, ip):
        """Validates that the requested IP address is acceptable for allocation
        in this `Subnet` (assuming it has not already been allocated).

        Raises `StaticIPAddressUnavailable` if the address is not acceptable.

        Does not consider whether or not the IP address is already allocated,
        only whether or not it is in the proper network and range.

        :raises StaticIPAddressUnavailable: If the IP address specified is not
            available for allocation.
        """
        if ip not in self.get_ipnetwork():
            raise StaticIPAddressOutOfRange(
                "%s is not within subnet CIDR: %s" % (ip, self.cidr))
        for iprange in self.get_reserved_maasipset():
            if ip in iprange:
                raise StaticIPAddressUnavailable(
                    "%s is within the reserved range from %s to %s" %
                    (ip, IPAddress(iprange.first), IPAddress(iprange.last)))
        for iprange in self.get_dynamic_maasipset():
            if ip in iprange:
                raise StaticIPAddressUnavailable(
                    "%s is within the dynamic range from %s to %s" %
                    (ip, IPAddress(iprange.first), IPAddress(iprange.last)))
Ejemplo n.º 2
0
    def allocate_new(
        self,
        subnet=None,
        alloc_type=IPADDRESS_TYPE.AUTO,
        user=None,
        requested_address=None,
        exclude_addresses=None,
    ):
        """Return a new StaticIPAddress.

        :param subnet: The subnet from which to allocate the address.
        :param alloc_type: What sort of IP address to allocate in the
            range of choice in IPADDRESS_TYPE.
        :param user: If providing a user, the alloc_type must be
            IPADDRESS_TYPE.USER_RESERVED. Conversely, if the alloc_type is
            IPADDRESS_TYPE.USER_RESERVED the user must also be provided.
            AssertionError is raised if these conditions are not met.
        :param requested_address: Optional IP address that the caller wishes
            to use instead of being allocated one at random.
        :param exclude_addresses: A list of addresses which MUST NOT be used.

        All IP parameters can be strings or netaddr.IPAddress.
        """
        # This check for `alloc_type` is important for later on. We rely on
        # detecting IntegrityError as a sign than an IP address is already
        # taken, and so we must first eliminate all other possible causes.
        self._verify_alloc_type(alloc_type, user)

        if subnet is None:
            if requested_address:
                subnet = Subnet.objects.get_best_subnet_for_ip(
                    requested_address)
            else:
                raise StaticIPAddressOutOfRange(
                    "Could not find an appropriate subnet.")

        if requested_address is None:
            requested_address = subnet.get_next_ip_for_allocation(
                exclude_addresses=exclude_addresses)
            return self._attempt_allocation_of_free_address(requested_address,
                                                            alloc_type,
                                                            user=user,
                                                            subnet=subnet)
        else:
            requested_address = IPAddress(requested_address)
            # Circular imports.
            from maasserver.models import StaticIPAddress

            if (StaticIPAddress.objects.filter(
                    ip=str(requested_address)).exclude(
                        alloc_type=IPADDRESS_TYPE.DISCOVERED).count() > 0):
                raise StaticIPAddressUnavailable(
                    "The IP address %s is already in use." % requested_address)

            subnet.validate_static_ip(requested_address)
            return self._attempt_allocation(requested_address,
                                            alloc_type,
                                            user=user,
                                            subnet=subnet)
Ejemplo n.º 3
0
    def _attempt_allocation(self,
                            requested_address,
                            alloc_type,
                            user=None,
                            subnet=None):
        """Attempt to allocate `requested_address`.

        All parameters must have been checked first.  This method relies on
        `IntegrityError` to detect addresses that are already in use, so
        nothing else must cause that error.

        Transaction model and isolation level have changed over time, and may
        do so again, so relying on database-level uniqueness validation is the
        most robust way we have of checking for clashes.

        :param requested_address: An `IPAddress` for the address that should
            be allocated.
        :param alloc_type: Allocation type.
        :param user: Optional user.
        :return: `StaticIPAddress` if successful.
        :raise StaticIPAddressUnavailable: if the address was already taken.
        """
        ipaddress = StaticIPAddress(alloc_type=alloc_type, subnet=subnet)
        try:
            # Try to save this address to the database. Do this in a nested
            # transaction so that we can continue using the outer transaction
            # even if this breaks.
            with transaction.atomic():
                ipaddress.set_ip_address(requested_address.format())
                ipaddress.save()
        except IntegrityError:
            # The address is already taken.
            raise StaticIPAddressUnavailable(
                "The IP address %s is already in use." %
                requested_address.format())
        else:
            # We deliberately do *not* save the user until now because it
            # might result in an IntegrityError, and we rely on the latter
            # in the code above to indicate an already allocated IP
            # address and nothing else.
            ipaddress.user = user
            ipaddress.save()
            return ipaddress