def _isolate(hostname, ns_pid, container_id, ipv4_addrs, ipv6_addrs, profiles, labels): """ Configure networking for a container. This function performs the following steps: 1.) Create endpoint in memory 2.) Fill endpoint with data 3.) Configure network to match the filled endpoint's specifications 4.) Write endpoint to etcd :param hostname: Hostname of the slave which the container is running on :param container_id: The container's ID :param ipv4_addrs: List of desired IPv4 addresses to be assigned to the endpoint :param ipv6_addrs: List of desired IPv6 addresses to be assigned to the endpoint :param profiles: List of desired profiles to be assigned to the endpoint :param labels: TODO :return: None """ _log.info("Preparing network for Container with ID %s", container_id) _log.info("IP: %s, Profile %s", ipv4_addrs, profiles) # Exit if the endpoint has already been configured if len( datastore.get_endpoints(hostname=hostname, orchestrator_id=ORCHESTRATOR_ID, workload_id=container_id)) == 1: raise IsolatorException( "This container has already been configured with Calico Networking." ) # Create the endpoint ep = datastore.create_endpoint(hostname=hostname, orchestrator_id=ORCHESTRATOR_ID, workload_id=container_id, ip_list=ipv4_addrs) # Create any profiles in etcd that do not already exist if profiles == []: profiles = ["default"] _log.info("Assigning Profiles: %s" % profiles) for profile in profiles: # Create profile with default rules, if it does not exist if not datastore.profile_exists(profile): _create_profile_with_default_mesos_rules(profile) # Set profiles on the endpoint _log.info("Adding container %s to profile %s", container_id, profile) ep.profile_ids = profiles # Call through to complete the network setup matching this endpoint try: ep.mac = ep.provision_veth(netns.PidNamespace(ns_pid), "eth0") except netns.NamespaceError as e: raise IsolatorException(e.message) datastore.set_endpoint(ep) _log.info("Finished networking for container %s", container_id)
def _container_add(self, pid, interface): """ Add a container (on this host) to Calico networking with the given IP. """ # Check if the container already exists. If it does, exit. try: _ = self._datastore_client.get_endpoint( hostname=HOSTNAME, orchestrator_id=ORCHESTRATOR_ID, workload_id=self.docker_id ) except KeyError: # Calico doesn't know about this container. Continue. pass else: logger.error("This container has already been configured " "with Calico Networking.") sys.exit(1) # Obtain information from Docker Client and validate container state self._validate_container_state(self.docker_id) ip_list = [self._assign_container_ip()] # Create Endpoint object try: logger.info("Creating endpoint with IPs %s", ip_list) ep = self._datastore_client.create_endpoint(HOSTNAME, ORCHESTRATOR_ID, self.docker_id, ip_list) except (AddrFormatError, KeyError): logger.exception("Failed to create endpoint with IPs %s. " "Unassigning IP address, then exiting.", ip_list) self._datastore_client.release_ips(set(ip_list)) sys.exit(1) # Create the veth, move into the container namespace, add the IP and # set up the default routes. logger.debug("Creating the veth with namespace pid %s on interface " "name %s", pid, interface) ep.mac = ep.provision_veth(netns.PidNamespace(pid), interface) logger.debug("Setting mac address %s to endpoint %s", ep.mac, ep.name) self._datastore_client.set_endpoint(ep) # Let the caller know what endpoint was created. return ep
def _create_endpoint(self, pid): """ Creates a Calico endpoint for this pod. - Assigns an IP address for this pod. - Creates the Calico endpoint object in the datastore. - Provisions the Calico veth pair for this pod. Returns the created libcalico Endpoint object. """ # Check if the container already exists. If it does, exit. if self._get_endpoint(): logger.error("This container has already been configured " "with Calico Networking.") sys.exit(1) ip_list = [self._assign_container_ip()] # Create Endpoint object try: logger.info("Creating Calico endpoint with IPs %s", ip_list) ep = self._datastore_client.create_endpoint( HOSTNAME, ORCHESTRATOR_ID, self.docker_id, ip_list) except (AddrFormatError, KeyError): # We failed to create the endpoint - we must release the IPs # that we assigned for this endpoint or else they will leak. logger.exception( "Failed to create endpoint with IPs %s. " "Unassigning IP address, then exiting.", ip_list) self._datastore_client.release_ips(set(ip_list)) sys.exit(1) # Create the veth, move into the container namespace, add the IP and # set up the default routes. logger.debug("Creating eth0 in network namespace with pid=%s", pid) ep.mac = ep.provision_veth(netns.PidNamespace(pid), "eth0") logger.debug("Setting mac address %s on endpoint %s", ep.mac, ep.name) self._datastore_client.set_endpoint(ep) # Let the caller know what endpoint was created. return ep
def container_ip_remove(container_id, ip, interface): """ Add an IP address to an existing Calico networked container. :param container_id: The namespace path or container_id of the container. :param ip: The IP to add :param interface: The name of the interface in the container. :return: None """ address = IPAddress(ip) # The netns manipulations must be done as root. enforce_root() pool = get_pool_or_exit(address) if container_id.startswith("/") and os.path.exists(container_id): # The ID is a path. Don't do any docker lookups workload_id = escape_etcd(container_id) namespace = netns.Namespace(container_id) orchestrator_id = NAMESPACE_ORCHESTRATOR_ID else: info = get_container_info_or_exit(container_id) workload_id = info["Id"] namespace = netns.PidNamespace(info["State"]["Pid"]) orchestrator_id = DOCKER_ORCHESTRATOR_ID # Check the container is actually running. if not info["State"]["Running"]: print "%s is not currently running." % container_id sys.exit(1) # Check that the container is already networked try: endpoint = client.get_endpoint(hostname=hostname, orchestrator_id=orchestrator_id, workload_id=workload_id) if address.version == 4: nets = endpoint.ipv4_nets else: nets = endpoint.ipv6_nets if not IPNetwork(address) in nets: print "IP address is not assigned to container. Aborting." sys.exit(1) except KeyError: print "Container is unknown to Calico." sys.exit(1) try: nets.remove(IPNetwork(address)) client.update_endpoint(endpoint) except (KeyError, ValueError): print "Error updating datastore. Aborting." sys.exit(1) try: netns.remove_ip_from_ns_veth(namespace, address, interface) except CalledProcessError: print "Error updating networking in container. Aborting." sys.exit(1) client.release_ips({address}) print "IP %s removed from %s" % (ip, container_id)
def container_ip_add(container_id, ip, interface): """ Add an IP address to an existing Calico networked container. :param container_id: The namespace path or container_id of the container. :param ip: The IP to add :param interface: The name of the interface in the container. :return: None """ # The netns manipulations must be done as root. enforce_root() if container_id.startswith("/") and os.path.exists(container_id): # The ID is a path. Don't do any docker lookups workload_id = escape_etcd(container_id) namespace = netns.Namespace(container_id) orchestrator_id = NAMESPACE_ORCHESTRATOR_ID else: info = get_container_info_or_exit(container_id) workload_id = info["Id"] namespace = netns.PidNamespace(info["State"]["Pid"]) orchestrator_id = DOCKER_ORCHESTRATOR_ID # Check the container is actually running. if not info["State"]["Running"]: print "%s is not currently running." % container_id sys.exit(1) # Check that the container is already networked try: endpoint = client.get_endpoint(hostname=hostname, orchestrator_id=orchestrator_id, workload_id=workload_id) except KeyError: print "Failed to add IP address to container.\n" print_container_not_in_calico_msg(container_id) sys.exit(1) # From here, this method starts having side effects. If something # fails then at least try to leave the system in a clean state. address, pool = get_ip_and_pool(ip) try: if address.version == 4: endpoint.ipv4_nets.add(IPNetwork(address)) else: endpoint.ipv6_nets.add(IPNetwork(address)) client.update_endpoint(endpoint) except (KeyError, ValueError): client.release_ips({address}) print "Error updating datastore. Aborting." sys.exit(1) if not netns.ns_veth_exists(namespace, interface): print "Interface provided does not exist in container. Aborting." sys.exit(1) try: netns.add_ip_to_ns_veth(namespace, address, interface) except CalledProcessError: print "Error updating networking in container. Aborting." if address.version == 4: endpoint.ipv4_nets.remove(IPNetwork(address)) else: endpoint.ipv6_nets.remove(IPNetwork(address)) client.update_endpoint(endpoint) client.release_ips({address}) sys.exit(1) print "IP %s added to %s" % (str(address), container_id)
def container_add(container_id, ip, interface): """ Add a container (on this host) to Calico networking with the given IP. :param container_id: The namespace path or the docker name/ID of the container. :param ip: An IPAddress object with the desired IP to assign. :param interface: The name of the interface in the container. """ # The netns manipulations must be done as root. enforce_root() # TODO: This section is redundant in container_add_ip and elsewhere if container_id.startswith("/") and os.path.exists(container_id): # The ID is a path. Don't do any docker lookups workload_id = escape_etcd(container_id) orchestrator_id = NAMESPACE_ORCHESTRATOR_ID namespace = netns.Namespace(container_id) else: info = get_container_info_or_exit(container_id) workload_id = info["Id"] orchestrator_id = DOCKER_ORCHESTRATOR_ID namespace = netns.PidNamespace(info["State"]["Pid"]) # Check the container is actually running. if not info["State"]["Running"]: print_paragraph("%s is not currently running." % container_id) sys.exit(1) # We can't set up Calico if the container shares the host namespace. if info["HostConfig"]["NetworkMode"] == "host": print_paragraph("Can't add %s to Calico because it is " "running NetworkMode = host." % container_id) sys.exit(1) # Check if the container already exists try: _ = client.get_endpoint(hostname=hostname, orchestrator_id=orchestrator_id, workload_id=workload_id) except KeyError: # Calico doesn't know about this container. Continue. pass else: # Calico already set up networking for this container. Since we got # called with an IP address, we shouldn't just silently exit, since # that would confuse the user: the container would not be reachable on # that IP address. print_paragraph("%s has already been configured with Calico " "Networking." % container_id) sys.exit(1) ip, pool = get_ip_and_pool(ip) try: # The next hop IPs for this host are stored in etcd. next_hops = client.get_default_next_hops(hostname) next_hops[ip.version] except KeyError: print_paragraph("This node is not configured for IPv%d. " "Is calico-node running?" % ip.version) unallocated_ips = client.release_ips({ip}) if unallocated_ips: print_paragraph("Error during cleanup. %s was already" "unallocated." % ip) sys.exit(1) # Get the next hop for the IP address. next_hop = next_hops[ip.version] network = IPNetwork(IPAddress(ip)) ep = Endpoint(hostname=hostname, orchestrator_id=DOCKER_ORCHESTRATOR_ID, workload_id=workload_id, endpoint_id=uuid.uuid1().hex, state="active", mac=None) if network.version == 4: ep.ipv4_nets.add(network) ep.ipv4_gateway = next_hop else: ep.ipv6_nets.add(network) ep.ipv6_gateway = next_hop # Create the veth, move into the container namespace, add the IP and # set up the default routes. netns.increment_metrics(namespace) netns.create_veth(ep.name, ep.temp_interface_name) netns.move_veth_into_ns(namespace, ep.temp_interface_name, interface) netns.add_ip_to_ns_veth(namespace, ip, interface) netns.add_ns_default_route(namespace, next_hop, interface) # Grab the MAC assigned to the veth in the namespace. ep.mac = netns.get_ns_veth_mac(namespace, interface) # Register the endpoint with Felix. client.set_endpoint(ep) # Let the caller know what endpoint was created. print_paragraph("IP %s added to %s" % (str(ip), container_id)) return ep
def _isolate(hostname, ns_pid, container_id, ipv4_addrs, ipv6_addrs, profiles, labels): """ Configure networking for a container. This function performs the following steps: 1.) Create endpoint in memory 2.) Fill endpoint with data 3.) Configure network to match the filled endpoint's specifications 4.) Write endpoint to etcd :param hostname: Hostname of the slave which the container is running on :param container_id: The container's ID :param ipv4_addrs: List of desired IPv4 addresses to be assigned to the endpoint :param ipv6_addrs: List of desired IPv6 addresses to be assigned to the endpoint :param profiles: List of desired profiles to be assigned to the endpoint :param labels: TODO :return: None """ _log.info("Preparing network for Container with ID %s", container_id) _log.info("IP: %s, Profile %s", ipv4_addrs, profiles) # Exit if the endpoint has already been configured if len( datastore.get_endpoints(hostname=HOSTNAME, orchestrator_id=ORCHESTRATOR_ID, workload_id=container_id)) == 1: raise IsolatorException("This container has already been configured " "with Calico Networking.") # Create the endpoint ep = datastore.create_endpoint(hostname=HOSTNAME, orchestrator_id=ORCHESTRATOR_ID, workload_id=container_id, ip_list=ipv4_addrs) # Create any profiles in etcd that do not already exist assigned_profiles = [] _log.info("Assigning Profiles: %s" % profiles) # First remove any keyword profile names try: profiles.remove("public") except ValueError: pass else: _log.info("Assigning Public Profile") if not datastore.profile_exists("public"): _create_profile_for_public_communication("public") assigned_profiles.append("public") # Assign remaining netgroup profiles for profile in profiles: profile = "ng_%s" % profile if not datastore.profile_exists(profile): _log.info("Assigning Netgroup Profile: %s" % profile) _create_profile_for_netgroup(profile) assigned_profiles.append(profile) # Insert the host-communication profile default_profile_name = "default_%s" % hostname _log.info("Assigning Default Host Profile: %s" % default_profile_name) if not datastore.profile_exists(default_profile_name): _create_profile_for_host_communication(default_profile_name) assigned_profiles.insert(0, default_profile_name) # Call through to complete the network setup matching this endpoint ep.profile_ids = assigned_profiles try: ep.mac = ep.provision_veth(netns.PidNamespace(ns_pid), "eth0") except netns.NamespaceError as e: raise IsolatorException(e.message) datastore.set_endpoint(ep) _log.info("Finished networking for container %s", container_id)