Exemple #1
0
    def assign_ip(self, address, handle_id, attributes, hostname=None):
        """
        Assign the given address.  Throws AlreadyAssignedError if the address
        is taken.

        :param address: IPAddress to assign.
        :param handle_id: allocation handle ID for this request.  You can
        query this key using get_assignments_by_handle() or release all
        addresses with this handle_id using release_by_handle().
        :param attributes: Contents of this dict will be stored with the
        assignment and can be queried using get_assignment_attributes().  Must
        be JSON serializable.
        :param hostname: (optional) the hostname to use for affinity if the
        block containing the IP address has no host affinity.  Defaults to the
        hostname returned by get_hostname().
        :return: None.
        """
        assert isinstance(handle_id, str) or handle_id is None
        assert isinstance(address, IPAddress)
        if not hostname:
            hostname = get_hostname()
        block_cidr = get_block_cidr_for_address(address)

        for _ in xrange(RETRIES):
            try:
                block = self._read_block(block_cidr)
            except KeyError:
                _log.debug("Block %s doesn't exist.", block_cidr)
                pools = self.get_ip_pools(address.version, ipam=True)
                if any([address in pool for pool in pools]):
                    _log.debug("Create and claim block %s.", block_cidr)
                    try:
                        self._claim_block_affinity(hostname, block_cidr)
                    except HostAffinityClaimedError:
                        _log.debug("Someone else claimed block %s before us.",
                                   block_cidr)
                        continue
                    # Block exists now, retry writing to it.
                    _log.debug("Claimed block %s", block_cidr)
                    continue
                else:
                    raise ValueError("%s is not in any configured pool" %
                                     address)

            # Try to assign.  Throws exception if already assigned -- let it.
            block.assign(address, handle_id, attributes)

            # If using a handle, increment by one IP
            if handle_id is not None:
                self._increment_handle(handle_id, block_cidr, 1)

            # Try to commit.
            try:
                self._compare_and_swap_block(block)
                return  # Success!
            except CASError:
                _log.debug("CAS failed on block %s", block_cidr)
                if handle_id is not None:
                    self._decrement_handle(handle_id, block_cidr, 1)
        raise RuntimeError("Hit max retries.")
Exemple #2
0
    def auto_assign(self, num, handle_id, attributes, affinity_check=True):
        """
        Automatically pick and assign the given number of IP addresses.

        :param num: Number of addresses to request
        :param handle_id: allocation handle ID for this request.  You can
        query this key using get_assignments_by_handle() or release all
        addresses with this key using release_by_handle().
        :param attributes: Contents of this dict will be stored with the
        assignment and can be queried using get_assignment_attributes().  Must
        be JSON serializable.
        :param affinity_check: If true, verify that this block's affinity is
        this host and throw a NoHostAffinityWarning if it isn't.  Set to false
        to disable this check.
        :return: List of assigned addresses.  When the block is at or near
        full, this method may return fewer than requested IPs.
        """
        assert num >= 0

        if affinity_check and get_hostname() != self.host_affinity:
            raise NoHostAffinityWarning("Host affinity is %s" %
                                        self.host_affinity)

        ordinals = []
        # Walk the allocations until we find enough.
        for o in xrange(BLOCK_SIZE):
            if len(ordinals) == num:
                break
            if self.allocations[o] is None:
                ordinals.append(o)

        ips = []
        if ordinals:
            # We found some addresses, now we need to set up attributes.
            attr_index = self._find_or_add_attrs(handle_id, attributes)

            # Perform the allocation.
            for o in ordinals:
                assert self.allocations[o] is None
                self.allocations[o] = attr_index

                # Convert ordinal to IP.
                ip = IPAddress(self.cidr.first + o, version=self.cidr.version)
                ips.append(ip)
        return ips
Exemple #3
0
    def auto_assign(self, num, handle_id, attributes, affinity_check=True):
        """
        Automatically pick and assign the given number of IP addresses.

        :param num: Number of addresses to request
        :param handle_id: allocation handle ID for this request.  You can
        query this key using get_assignments_by_handle() or release all
        addresses with this key using release_by_handle().
        :param attributes: Contents of this dict will be stored with the
        assignment and can be queried using get_assignment_attributes().  Must
        be JSON serializable.
        :param affinity_check: If true, verify that this block's affinity is
        this host and throw a NoHostAffinityWarning if it isn't.  Set to false
        to disable this check.
        :return: List of assigned addresses.  When the block is at or near
        full, this method may return fewer than requested IPs.
        """
        assert num >= 0

        if affinity_check and get_hostname() != self.host_affinity:
            raise NoHostAffinityWarning("Host affinity is %s" %
                                        self.host_affinity)

        ordinals = []
        # Walk the allocations until we find enough.
        for o in xrange(BLOCK_SIZE):
            if len(ordinals) == num:
                break
            if self.allocations[o] is None:
                ordinals.append(o)

        ips = []
        if ordinals:
            # We found some addresses, now we need to set up attributes.
            attr_index = self._find_or_add_attrs(handle_id, attributes)

            # Perform the allocation.
            for o in ordinals:
                assert self.allocations[o] is None
                self.allocations[o] = attr_index

                # Convert ordinal to IP.
                ip = IPAddress(self.cidr.first + o, version=self.cidr.version)
                ips.append(ip)
        return ips
Exemple #4
0
    def auto_assign_ips(self,
                        num_v4,
                        num_v6,
                        handle_id,
                        attributes,
                        pool=(None, None),
                        host=None):
        """
        Automatically pick and assign the given number of IPv4 and IPv6
        addresses.

        :param num_v4: Number of IPv4 addresses to request
        :param num_v6: Number of IPv6 addresses to request
        :param handle_id: allocation handle ID for this request.  You can query
        this key using get_assignments_by_handle() or release all addresses
        with this key using release_by_handle().
        :param attributes: Contents of this dict will be stored with the
        assignment and can be queried using get_assignment_attributes().  Must
        be JSON serializable.
        :param pool: (optional) Tuple of (v4 pool, v6 pool); if supplied, the
        pool(s) to assign from,  If None, automatically choose a pool.
        :param host: (optional) The host ID to use for affinity in assigning IP
        addresses.  Defaults to the hostname returned by get_hostname().
        :return: A tuple of (v4_address_list, v6_address_list).  When IPs in
        configured pools are at or near exhaustion, this method may return
        fewer than requested addresses.
        """
        assert isinstance(handle_id, str) or handle_id is None

        if not host:
            host = get_hostname()

        _log.info("Auto-assign %d IPv4, %d IPv6 addrs", num_v4, num_v6)
        v4_address_list = self._auto_assign(4, num_v4, handle_id, attributes,
                                            pool[0], host)
        _log.info("Auto-assigned IPv4s %s",
                  [str(addr) for addr in v4_address_list])
        v6_address_list = self._auto_assign(6, num_v6, handle_id, attributes,
                                            pool[1], host)
        _log.info("Auto-assigned IPv6s %s",
                  [str(addr) for addr in v6_address_list])
        return v4_address_list, v6_address_list
Exemple #5
0
    def ensure_global_config(self):
        """
        Ensure the global config settings for Calico exist, creating them with
        defaults if they don't.
        :return: None.
        """
        # Configure Felix config.
        self._write_global_config(CONFIG_IF_PREF_PATH, IF_PREFIX)

        # Configure IPAM directory structures (to ensure confd is able to
        # watch appropriate directory trees).
        host = get_hostname()
        for version in (4, 6):
            path = IPAM_HOST_AFFINITY_PATH % {"hostname": host,
                                              "version": version}
            try:
                self.etcd_client.write(path, None, dir=True)
            except EtcdNotFile:
                # Directory already exists.
                pass

        # Configure BGP global (default) config if it doesn't exist.
        self._write_global_config(BGP_NODE_DEF_AS_PATH, str(DEFAULT_AS_NUM))
        self._write_global_config(BGP_NODE_MESH_PATH,
                                  json.dumps(DEFAULT_NODE_MESH))

        # Configure logging levels.
        self._write_global_config(LOG_SEVERITY_FILE_PATH,
                                  DEFAULT_LOG_SEVERITY_FILE)
        self._write_global_config(LOG_SEVERITY_SCREEN_PATH,
                                  DEFAULT_LOG_SEVERITY_SCREEN)
        self._write_global_config(LOG_FILE_PATH_PATH,
                                  DEFAULT_LOG_FILE_PATH)

        # IP in IP is enabled globally.
        self._write_global_config(IP_IN_IP_PATH, IP_IN_IP_DISABLED)

        # We are always ready.
        self.etcd_client.write(CALICO_V_PATH + "/Ready", "true")
Exemple #6
0
    def ensure_global_config(self):
        """
        Ensure the global config settings for Calico exist, creating them with
        defaults if they don't.
        :return: None.
        """
        # Configure Felix config.
        self._write_global_config(CONFIG_IF_PREF_PATH, IF_PREFIX)

        # Configure IPAM directory structures (to ensure confd is able to
        # watch appropriate directory trees).
        host = get_hostname()
        for version in (4, 6):
            path = IPAM_HOST_AFFINITY_PATH % {"hostname": host,
                                              "version": version}
            try:
                self.etcd_client.write(path, None, dir=True)
            except EtcdNotFile:
                # Directory already exists.
                pass

        # Configure BGP global (default) config if it doesn't exist.
        self._write_global_config(BGP_NODE_DEF_AS_PATH, str(DEFAULT_AS_NUM))
        self._write_global_config(BGP_NODE_MESH_PATH,
                                  json.dumps(DEFAULT_NODE_MESH))

        # Configure logging levels.
        self._write_global_config(LOG_SEVERITY_FILE_PATH,
                                  DEFAULT_LOG_SEVERITY_FILE)
        self._write_global_config(LOG_SEVERITY_SCREEN_PATH,
                                  DEFAULT_LOG_SEVERITY_SCREEN)
        self._write_global_config(LOG_FILE_PATH_PATH,
                                  DEFAULT_LOG_FILE_PATH)

        # IP in IP is enabled globally.
        self._write_global_config(IP_IN_IP_PATH, IP_IN_IP_DISABLED)

        # We are always ready.
        self.etcd_client.write(CALICO_V_PATH + "/Ready", "true")
Exemple #7
0
    def auto_assign_ips(self, num_v4, num_v6, handle_id, attributes,
                        pool=(None, None), host=None):
        """
        Automatically pick and assign the given number of IPv4 and IPv6
        addresses.

        :param num_v4: Number of IPv4 addresses to request
        :param num_v6: Number of IPv6 addresses to request
        :param handle_id: allocation handle ID for this request.  You can query
        this key using get_assignments_by_handle() or release all addresses
        with this key using release_by_handle().
        :param attributes: Contents of this dict will be stored with the
        assignment and can be queried using get_assignment_attributes().  Must
        be JSON serializable.
        :param pool: (optional) Tuple of (v4 pool, v6 pool); if supplied, the
        pool(s) to assign from,  If None, automatically choose a pool.
        :param host: (optional) The host ID to use for affinity in assigning IP
        addresses.  Defaults to the hostname returned by get_hostname().
        :return: A tuple of (v4_address_list, v6_address_list).  When IPs in
        configured pools are at or near exhaustion, this method may return
        fewer than requested addresses.
        """
        assert isinstance(handle_id, str) or handle_id is None

        if not host:
            host = get_hostname()

        _log.info("Auto-assign %d IPv4, %d IPv6 addrs",
                  num_v4, num_v6)
        v4_address_list = self._auto_assign(4, num_v4, handle_id, attributes,
                                            pool[0], host)
        _log.info("Auto-assigned IPv4s %s",
                  [str(addr) for addr in v4_address_list])
        v6_address_list = self._auto_assign(6, num_v6, handle_id, attributes,
                                            pool[1], host)
        _log.info("Auto-assigned IPv6s %s",
                  [str(addr) for addr in v6_address_list])
        return v4_address_list, v6_address_list
Exemple #8
0
import sys
import textwrap
import urllib

import netaddr
from netaddr.core import AddrFormatError
from pycalico.util import get_hostname

DOCKER_VERSION = "1.16"
DOCKER_LIBNETWORK_VERSION = "1.21"
# There is an issue with 2.0.9 and CAS (when using py-etcd) - https://github.com/projectcalico/calico-containers/issues/479
ETCD_VERSION = "2.0.10"
DOCKER_ORCHESTRATOR_ID = "docker"
NAMESPACE_ORCHESTRATOR_ID = "namespace"
REQUIRED_MODULES = ["xt_set", "ip6_tables"]
hostname = get_hostname()

# Extracts UUID, version and container status from rkt list output.
RKT_CONTAINER_RE = re.compile(
    "([a-z0-9]+)\s+.*calico\/node:([a-z0-9\.\_\-]+)\s+([a-z]+)\s+")


def enforce_root():
    """
    Check if the current process is running as the root user.
    :return: Nothing. sys.exit if not running as root.
    """
    if os.geteuid() != 0:
        print("This command must be run as root.", file=sys.stderr)
        sys.exit(2)
Exemple #9
0
import textwrap
import netaddr
import re
import urllib

from pycalico.util import get_hostname
from netaddr.core import AddrFormatError

DOCKER_VERSION = "1.16"
DOCKER_LIBNETWORK_VERSION = "1.21"
# There is an issue with 2.0.9 and CAS (when using py-etcd) - https://github.com/projectcalico/calico-docker/issues/479
ETCD_VERSION = "2.0.10"
DOCKER_ORCHESTRATOR_ID = "docker"
NAMESPACE_ORCHESTRATOR_ID = "namespace"
REQUIRED_MODULES = ["xt_set", "ip6_tables"]
hostname = get_hostname()


def enforce_root():
    """
    Check if the current process is running as the root user.
    :return: Nothing. sys.exit if not running as root.
    """
    if os.geteuid() != 0:
        print >> sys.stderr, "This command must be run as root."
        sys.exit(2)


def print_paragraph(msg):
    """
    Print a fixed width (80 chars) paragraph of text.
Exemple #10
0
    def assign_ip(self, address, handle_id, attributes, host=None):
        """
        Assign the given address.  Throws AlreadyAssignedError if the address
        is taken.

        :param address: IPAddress to assign.
        :param handle_id: allocation handle ID for this request.  You can
        query this key using get_assignments_by_handle() or release all
        addresses with this handle_id using release_by_handle().
        :param attributes: Contents of this dict will be stored with the
        assignment and can be queried using get_assignment_attributes().  Must
        be JSON serializable.
        :param host: (optional) The host ID to use for affinity in assigning IP
        addresses.  Defaults to the hostname returned by get_hostname().
        :return: None.
        """
        assert isinstance(handle_id, str) or handle_id is None
        assert isinstance(address, IPAddress)
        if not host:
            host = get_hostname()
        block_cidr = get_block_cidr_for_address(address)

        for _ in xrange(RETRIES):
            try:
                block = self._read_block(block_cidr)
            except KeyError:
                _log.debug("Block %s doesn't exist.", block_cidr)
                pools = self.get_ip_pools(address.version, ipam=True)
                if any([address in pool for pool in pools]):
                    _log.debug("Create and claim block %s.",
                               block_cidr)
                    try:
                        self._claim_block_affinity(host, block_cidr)
                    except HostAffinityClaimedError:
                        _log.debug("Someone else claimed block %s before us.",
                                   block_cidr)
                        continue
                    # Block exists now, retry writing to it.
                    _log.debug("Claimed block %s", block_cidr)
                    continue
                else:
                    raise PoolNotFound("%s is not in any configured pool" %
                                       address)

            # Try to assign.  Throws exception if already assigned -- let it.
            block.assign(address, handle_id, attributes)

            # If using a handle, increment by one IP
            if handle_id is not None:
                self._increment_handle(handle_id, block_cidr, 1)

            # Try to commit.
            try:
                self._compare_and_swap_block(block)
                return  # Success!
            except CASError:
                _log.debug("CAS failed on block %s", block_cidr)
                if handle_id is not None:
                    self._decrement_handle(handle_id,
                                           block_cidr,
                                           1)
        raise RuntimeError("Hit max retries.")