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.")
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
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
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")
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)
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.
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.")