Ejemplo n.º 1
0
    def __init__(self, network_dict, datastore):
        '''Python representation of a _network block

        Raises:
            ValueError - if a _network block has invalid information'''

        self.datastore = datastore
        self._network_block_utilization = {}

        self._network_id = network_dict['id']
        self.network_name = network_dict['name']
        self.family = network_dict['family']
        self.location = network_dict['location']
        self._network = ipaddress.ip_network(network_dict['network'], strict=True)
        self.allocation_size = network_dict['allocation_size']
        self.reserved_blocks = network_dict['reserved_blocks']

        # Sanity check ourselves
        check.is_valid_ip_family(self.family)

        # Work out some values based on family size, and make sure our allocation is sane
        total_length_of_an_ip = None
        if self.family == AF_INET:
            total_length_of_an_ip = 32
            if self.allocation_size > 32 or self.allocation_size < self._network.prefixlen:
                raise ValueError('Allocation prefix size is too large!')

        if self.family == AF_INET6:
            total_length_of_an_ip = 128
            if self.allocation_size > 128 or self.allocation_size < self._network.prefixlen:
                raise ValueError('Allocation prefix size is too large!')

        # We can calculate the number of hosts by doing powers of 2 math
        self._total_number_of_allocations = 2**(self.allocation_size-self._network.prefixlen)

        # Now the tricky bit. We need to know (in binary), how much we need to add to get
        # the next IP range we're allocation.

        # Clarity note, if we're allocating single IPs, the following equation will be 0. Anything
        # raised to 0 becomes 1.
        self._block_seperator = 2**(total_length_of_an_ip-self.allocation_size)

        # Depending on the size of the block, and our family, there are some allocations
        # that aren't valid. If we're handing out /32 addresses, we need to take in account
        # that the _network address and broadcast address of a block are unusable. Allocation
        # handles this case for >/32 blocks, but we need to handle it here otherwise.

        # FIXME: Confirm this logic is sane ...

        # IP ranges are 0-255. Powers of two math gets us 256, so drop it by one so everything
        # else ends up in the right ranges
        self._total_number_of_allocations -= 1

        # In all cases, we need to handle the _network address
        self._mark_network_address()

        # If we're IPv4, we need handle the broadcast address
        if self.family == AF_INET:
            self._mark_broadcast_address()
Ejemplo n.º 2
0
    def add_ip(self, ip_dict):
        '''Adds an IP to an interface. Lower-level function to add an IP address

        Args:
            ip_dict - takes an IP dictionary (see get_ips) and adds it to an interface
                      directorly

        Raises:
            ValueError
                IP address invalid. See message for more info
            DuplicateIPError
                This IP is already configured
            InterfaceConfigurationError
                The ip_dict was valid, but the IP failed add
        '''
        check.validate_and_normalize_ip_dict(ip_dict)

        # Throw an error if we try to add an existing address.
        existing_ip_check = None
        try:
            existing_ip_check = self.get_full_ip_info(ip_dict['ip_address'])
        except IPNotFound:
            pass

        if existing_ip_check:
            raise DuplicateIPError("This IP has already been assigned!")

        # We call add slightly differently based on socket family
        if ip_dict['family'] == AF_INET:
            self.iproute_api.addr('add',
                                  index=self.interface_index,
                                  family=AF_INET,
                                  address=ip_dict['ip_address'],
                                  broadcast=ip_dict['broadcast'],
                                  prefixlen=ip_dict['prefix_length'])

        if ip_dict['family'] == AF_INET6:
            self.iproute_api.addr('add',
                                  index=self.interface_index,
                                  family=AF_INET6,
                                  address=ip_dict['ip_address'],
                                  prefixlen=ip_dict['prefix_length'])

        # Do a sanity check and make sure the IP actually got added
        ip_check = self.get_full_ip_info(ip_dict['ip_address'])

        if not (ip_check['ip_address'] == ip_dict['ip_address'] and
                ip_check['prefix_length'] == ip_dict['prefix_length']):
            raise InterfaceConfigurationError("IP failed to add!")
Ejemplo n.º 3
0
    def __init__(self, ip_range):
        '''Create a new _allocation based on this range'''
        self.allocation_id = None
        self._allocation_utilization = {}

        # _allocation_status refers to the status of a block as a whole. It is equal to the
        # highest status of any IP within a block
        self._allocation_status = 'UNALLOCATED'

        # Do the usual validation and sanity check
        self._allocation = check.confirm_valid_network(ipaddress.ip_network(ip_range, strict=True))
        self._allocation_start = self._allocation.network_address

        address_size = None
        if self._allocation.version == 4:
            address_size = 32
            self.family = AF_INET

        if self._allocation.version == 6:
            address_size = 128
            self.family = AF_INET6

        # Power of 2 math to the rescue; work out how many IPs we represent
        self._total_number_of_ip = 2**(address_size-self._allocation.prefixlen)
        self._available_ips = self._total_number_of_ip

        # The network address is unusable in IPv4, and for our sanity, mark it
        # unusable in IPv6 (dealing with b1oc:: as a valid IP is bleh)
        #
        # If we're IPv4, the  broadcast addresses is unusable within a block.
        if self._total_number_of_ip != 1:
            self._mark_network_address()

            if self.family == AF_INET:
                self._mark_broadcast_address()
Ejemplo n.º 4
0
    def create_network(self, name, location, family, network, allocation_size, reserved_blocks):
        # pylint: disable=too-many-arguments
        """Creates a network in the database"""

        # Argument validation
        check.is_valid_ip_family(family)
        network = check.validate_and_normalize_ip_network(network)
        check.is_valid_prefix_size(allocation_size, family)

        # FIXME: make sure we're not trying to add ourselves twice
        query = """INSERT INTO network_topology (name, location, family, network, allocation_size,
                                                 reserved_blocks) VALUES (%s, %s, %s,%s, %s, %s)"""

        self._do_insert(query, (name, location, int(family), network, allocation_size, reserved_blocks))

        # Update our state information to see the new network
        self.refresh_network_topogoly()
Ejemplo n.º 5
0
    def _get_allocation_offset(self, cidr_block):
        '''Gets the offset within the dict for a given allocation'''

        # Validate our input
        ip_network = check.validate_ip_network(cidr_block)
        if not check.do_cidr_blocks_overlap(self._network, cidr_block):
            raise ValueError('Allocation block not within NetworkBlock')
        if ip_network.prefixlen != self.allocation_size:
            raise ValueError('Allocation block has wrong allocation size')

        # Offset is calculated by the difference in network addresses
        offset = int(ip_network.network_address)-int(self._network.network_address)

        import pprint
        pprint.pprint(offset)
        pprint.pprint(self._network_block_utilization)
        # Confirm it exists, or throw a ValueError
        if offset in self._network_block_utilization:
            return offset

        raise AllocationNotFound("Allocation doesn't exist within NetworkBlock")
Ejemplo n.º 6
0
    def remove_ip(self, ip_address):
        '''Removes an IP from an interface. Full details are looked up via
            get_full_ip_info for removal

            Args:
                ip_address - IP address to remove

            Raises:
                ValueError
                    The IP address provided was invalid
                IPNotFound
                    The IP is not configured on this interface
                InterfaceConfigurationError
                    The IP address was valid, but the IP was not successfully removed
        '''

        # San check
        ip_address = check.validate_and_normalize_ip(ip_address)

        # Get the full set of IP information
        ip_info = self.get_full_ip_info(ip_address)

        # Attempt to delete
        if ip_info['family'] == AF_INET:
            self.iproute_api.addr('delete',
                                  index=self.interface_index,
                                  address=ip_info['ip_address'],
                                  broadcast=ip_info['broadcast'],
                                  prefixlen=ip_info['prefix_length'])

        if ip_info['family'] == AF_INET6:
            self.iproute_api.addr('delete',
                                  index=self.interface_index,
                                  address=ip_info['ip_address'],
                                  prefixlen=ip_info['prefix_length'])

        # Confirm the delete. get_full_ip_info will throw an exception if it can't find it
        try:
            self.get_full_ip_info(ip_address)
        except IPNotFound:
            # We got it!
            return

        # Didn't get it. Throw an exception and bail
        raise InterfaceConfigurationError("IP deletion failure")
Ejemplo n.º 7
0
    def set_ip_status(self, ip_address, status, allocation, machine):
        """Sets an IP status to reserved in the database"""

        # Sanity check our input
        ip_address = check.validate_and_normalize_ip(ip_address)
        if not isinstance(allocation, AllocationServerSide):
            raise ValueError("Invalid Allocation object")
        if not isinstance(machine, Machine):
            raise ValueError("Invalid Machine object")
        if not (status == "UNMANAGED" or status == "RESERVED" or status == "STANDBY" or status == "ACTIVE_UTILIZATION"):
            raise ValueError("Invalid status for IP")

        # We use REPLACE to make sure statuses are always accurate to the server. In case
        # of conflict, the server is always the correct source of information
        query = """REPLACE INTO ip_allocations (from_allocation, allocated_to, ip_address,
                   status, reservation_expires) VALUES (%s, %s, %s, %s, %s)"""

        # reservation status is null unless we're going to/from RESERVED
        reservation_status = "NULL"
        self._do_insert(query, (allocation.get_id(), machine.get_id(), ip_address, status, reservation_status))
Ejemplo n.º 8
0
    def mark_ip_as_reserved(self, ip_to_reserve):
        '''Moves an IP from unused to reserved'''
        # Check that this is a valid IP for this allocation
        ip_address = ipaddress.ip_address(ip_to_reserve)
        if not check.is_ip_within_block(ip_address, self._allocation):
            raise ValueError('ip_address not within allocation')

        if not self._confirm_ip_is_unused(ip_address):
            raise ValueError(('%s is not UNALLOCATED' % (str(ip_address),) ))

        # We're good, create the allocation
        offset = self._calculate_offset(ip_address)

        ip_status = {}
        ip_status['status'] = 'RESERVED'
        ip_status['reserved_until'] = None
        self._allocation_utilization.update({offset: ip_status})

        # We return the validated ip_address for postprocessing by the parent class
        return ip_address
Ejemplo n.º 9
0
    def get_full_ip_info(self, wanted_address):
        '''Returns an ip_dict for an individual IP address. Format identical to get_ips()

        Args:
            wanted_address - a valid v4 or v6 address to search for. Value is normalized
                             by ipaddress.ip_address when returning

        Raises:
            ValueError - wanted_address is not a valid IP address
            IPNotFound - this interface doesn't have this IP address
        '''
        wanted_address = check.validate_and_normalize_ip(wanted_address)

        ips = self.get_ips()

        # Walk the IP table and find the specific IP we want
        for ip_address in ips:
            if ip_address['ip_address'] == wanted_address:
                return ip_address

        # If we get here, the IP wasn't found
        raise IPNotFound("IP not found on interface")