def container_remove(container_id): """ Remove a container (on this host) from Calico networking. The container may be left in a state without any working networking. If there is a network adaptor in the host namespace used by the container then it is removed. :param container_id: The namespace path or the ID of the container. """ # The netns manipulations must be done as root. enforce_root() # Resolve the name to ID. if container_id.startswith("/") and os.path.exists(container_id): # The ID is a path. Don't do any docker lookups orchestrator_id = NAMESPACE_ORCHESTRATOR_ID endpoints = client.get_endpoints(hostname=hostname, orchestrator_id=orchestrator_id, workload_id=escape_etcd(container_id)) else: # We know we're using "docker" as the orchestrator. If we have a direct # hit on the container id then we can proceed. Otherwise, ask docker to # try converting the name/id fragment into a full ID. orchestrator_id = DOCKER_ORCHESTRATOR_ID endpoints = client.get_endpoints(hostname=hostname, orchestrator_id=orchestrator_id, workload_id=container_id) if not endpoints: container_id = get_workload_id(container_id) endpoints = client.get_endpoints(hostname=hostname, orchestrator_id=orchestrator_id, workload_id=container_id) for endpoint in endpoints: # Remove any IP address assignments that this endpoint has client.release_ips( set(map(IPAddress, endpoint.ipv4_nets | endpoint.ipv6_nets))) try: # Remove the interface if it exists netns.remove_veth(endpoint.name) except CalledProcessError: print "Could not remove Calico interface %s" % endpoint.name # Always try to remove the workload, even if we didn't find any # endpoints. try: client.remove_workload(hostname, orchestrator_id, container_id) print "Removed Calico from %s" % container_id except KeyError: print "Failed find Calico data for %s" % container_id
def container_remove(container_id): """ Remove a container (on this host) from Calico networking. The container may be left in a state without any working networking. If there is a network adaptor in the host namespace used by the container then it is removed. :param container_id: The namespace path or the ID of the container. """ # The netns manipulations must be done as root. enforce_root() # Resolve the name to ID. if container_id.startswith("/") and os.path.exists(container_id): # The ID is a path. Don't do any docker lookups orchestrator_id = NAMESPACE_ORCHESTRATOR_ID endpoints = client.get_endpoints(hostname=hostname, orchestrator_id=orchestrator_id, workload_id=escape_etcd(container_id)) else: # We know we're using "docker" as the orchestrator. If we have a direct # hit on the container id then we can proceed. Otherwise, ask docker to # try converting the name/id fragment into a full ID. orchestrator_id = DOCKER_ORCHESTRATOR_ID endpoints = client.get_endpoints(hostname=hostname, orchestrator_id=orchestrator_id, workload_id=container_id) if not endpoints: container_id = get_workload_id(container_id) endpoints = client.get_endpoints(hostname=hostname, orchestrator_id=orchestrator_id, workload_id=container_id) for endpoint in endpoints: # Remove any IP address assignments that this endpoint has client.release_ips(set(map(IPAddress, endpoint.ipv4_nets | endpoint.ipv6_nets))) try: # Remove the interface if it exists netns.remove_veth(endpoint.name) except CalledProcessError: print "Could not remove Calico interface %s" % endpoint.name # Always try to remove the workload, even if we didn't find any # endpoints. try: client.remove_workload(hostname, orchestrator_id, container_id) print "Removed Calico from %s" % container_id except KeyError: print "Failed find Calico data for %s" % container_id
def node_stop(force): """ Stop the Calico node. This stops the containers (calico/node and calico/node-libnetwork) that are started by calicoctl node. """ endpoints = len(client.get_endpoints(hostname=hostname)) if endpoints: if not force: print_paragraph("Current host has active endpoints so can't be " "stopped. Force with --force") print_paragraph("Note that stopping the node while there are " "active endpoints may make it difficult to clean " "up the endpoints: for example, Docker containers " "networked using libnetwork with Calico will not " "invoke network cleanup during the normal " "container lifecycle.") sys.exit(1) else: print_paragraph("Stopping node while host has active endpoints. " "If this in error, restart the node using the " "'calicoctl node' command.") try: docker_client.stop("calico-node") except docker.errors.APIError as err: if err.response.status_code != 404: raise try: docker_client.stop("calico-libnetwork") except docker.errors.APIError as err: if err.response.status_code != 404: raise print "Node stopped"
def node_remove(remove_endpoints): """ Remove a node from the Calico network. :param remove_endpoints: Whether the endpoint data should be forcibly removed. """ if _container_running("calico-node") or \ _container_running("calico-libnetwork"): print_paragraph("The node cannot be removed while it is running. " "Please run 'calicoctl node stop' to stop the node " "before removing it.") sys.exit(1) endpoints = client.get_endpoints(hostname=hostname) if endpoints and not remove_endpoints: print_paragraph("The node has active Calico endpoints so can't be " "deleted. Force with --remove-endpoints") print_paragraph("Note that forcible removing the node may leave some " "workloads in an indeterminate networked state. If " "this is in error, you may restart the node using the " "'calicoctl node' command and clean up the workloads " "in the normal way.") sys.exit(1) for endpoint in endpoints: remove_veth(endpoint.name) client.remove_host(hostname) print "Node configuration removed"
def node_remove(remove_endpoints, host): """ Remove a node from the Calico network. :param remove_endpoints: Whether the endpoint data should be forcibly removed. :param host: The hostname of the host whose node will be removed, or None if removing this host's node. :return: None. """ host_to_remove = host or hostname if host_to_remove == hostname and ( _container_running("calico-node") or _container_running("calico-libnetwork")): print_paragraph("The node cannot be removed while it is running. " "Please run 'calicoctl node stop' to stop the node " "before removing it.") sys.exit(1) endpoints = client.get_endpoints(hostname=host_to_remove) if endpoints and not remove_endpoints: print_paragraph("The node has active Calico endpoints so can't be " "deleted. Force with --remove-endpoints") print_paragraph("Note that forcible removing the node may leave some " "workloads in an indeterminate networked state. If " "this is in error, you may restart the node using the " "'calicoctl node' command and clean up the workloads " "in the normal way.") sys.exit(1) # Remove the veths, and release all IPs associated with the endpoints. To # release the IPs, we construct a set of all IP addresses across all # endpoints (this assumes the endpoint nets are all single IPs). ips = set() for endpoint in endpoints: remove_veth(endpoint.name) ips |= {net.ip for net in endpoint.ipv4_nets} ips |= {net.ip for net in endpoint.ipv6_nets} client.release_ips(ips) # Remove the IPAM host data. client.remove_ipam_host(host_to_remove) # If the host had an IPIP tunnel address, release it back to the IPAM pool # so that we don't leak it when we delete the config. raw_addr = client.get_per_host_config(host_to_remove, "IpInIpTunnelAddr") try: ip_addr = IPAddress(raw_addr) client.release_ips({ip_addr}) except (AddrFormatError, ValueError, TypeError): pass client.remove_per_host_config(host_to_remove, "IpInIpTunnelAddr") client.remove_host(host_to_remove) print "Node configuration removed"
def node_remove(remove_endpoints, host): """ Remove a node from the Calico network. :param remove_endpoints: Whether the endpoint data should be forcibly removed. :param host: The hostname of the host whose node will be removed, or None if removing this host's node. :return: None. """ host_to_remove = host or hostname if host_to_remove == hostname and (_container_running("calico-node") or _container_running("calico-libnetwork")): print_paragraph("The node cannot be removed while it is running. " "Please run 'calicoctl node stop' to stop the node " "before removing it.") sys.exit(1) endpoints = client.get_endpoints(hostname=host_to_remove) if endpoints and not remove_endpoints: print_paragraph("The node has active Calico endpoints so can't be " "deleted. Force with --remove-endpoints") print_paragraph("Note that forcible removing the node may leave some " "workloads in an indeterminate networked state. If " "this is in error, you may restart the node using the " "'calicoctl node' command and clean up the workloads " "in the normal way.") sys.exit(1) # Remove the veths, and release all IPs associated with the endpoints. To # release the IPs, we construct a set of all IP addresses across all # endpoints (this assumes the endpoint nets are all single IPs). ips = set() for endpoint in endpoints: remove_veth(endpoint.name) ips |= {net.ip for net in endpoint.ipv4_nets} ips |= {net.ip for net in endpoint.ipv6_nets} client.release_ips(ips) # Remove the IPAM host data. client.remove_ipam_host(host_to_remove) # If the host had an IPIP tunnel address, release it back to the IPAM pool # so that we don't leak it when we delete the config. raw_addr = client.get_per_host_config(host_to_remove, "IpInIpTunnelAddr") try: ip_addr = IPAddress(raw_addr) client.release_ips({ip_addr}) except (AddrFormatError, ValueError, TypeError): pass client.remove_per_host_config(host_to_remove, "IpInIpTunnelAddr") client.remove_host(host_to_remove) print "Node configuration removed"
def node_stop(force): if force or len(client.get_endpoints(hostname=hostname, orchestrator_id=DOCKER_ORCHESTRATOR_ID)) == 0: client.remove_host(hostname) try: docker_client.stop("calico-node") except docker.errors.APIError as err: if err.response.status_code != 404: raise print "Node stopped and all configuration removed" else: print "Current host has active endpoints so can't be stopped." + \ " Force with --force"
def endpoint_show(hostname, orchestrator_id, workload_id, endpoint_id, detailed): """ List the profiles for a given endpoint. All parameters will be used to filter down which endpoints should be shown. :param endpoint_id: The endpoint ID. :param workload_id: The workload ID. :param orchestrator_id: The orchestrator ID. :param hostname: The hostname. :param detailed: Optional flag, when set to True, will provide more information in the shown table :return: Nothing """ endpoints = client.get_endpoints(hostname=hostname, orchestrator_id=orchestrator_id, workload_id=workload_id, endpoint_id=endpoint_id) if detailed: headings = [ "Hostname", "Orchestrator ID", "Workload ID", "Endpoint ID", "Addresses", "MAC", "Profiles", "State" ] x = PrettyTable(headings, sortby="Hostname") for endpoint in endpoints: addresses = "\n".join( [str(net) for net in endpoint.ipv4_nets | endpoint.ipv6_nets]) x.add_row([ endpoint.hostname, endpoint.orchestrator_id, endpoint.workload_id, endpoint.endpoint_id, addresses, endpoint.mac, ','.join(endpoint.profile_ids), endpoint.state ]) else: headings = [ "Hostname", "Orchestrator ID", "Number of Workloads", "Number of Endpoints" ] x = PrettyTable(headings, sortby="Hostname") # The summary table has one entry for each host/orchestrator # combination. We create a dictionary to maintain the summary # information, using the (hostname, orchestrator_id) as the unique # key, with a value of an EndpointSummary object to store unique # workload IDs and a count of endpoint IDs. # # We use a default dict to automatically create an "empty" (zero count) # EndpointSummary for each new table entry. host_orch_summary = defaultdict(EndpointSummary) for endpoint in endpoints: key = (endpoint.hostname, endpoint.orchestrator_id) summary = host_orch_summary[key] summary.add_endpoint(endpoint) # This table has one entry for each host/orchestrator combination. for key, summary in host_orch_summary.iteritems(): hostname, orchestrator_id = key x.add_row([ hostname, orchestrator_id, len(summary.workload_ids), summary.num_endpoints ]) print str(x) + "\n"
def endpoint_show(hostname, orchestrator_id, workload_id, endpoint_id, detailed): """ List the profiles for a given endpoint. All parameters will be used to filter down which endpoints should be shown. :param endpoint_id: The endpoint ID. :param workload_id: The workload ID. :param orchestrator_id: The orchestrator ID. :param hostname: The hostname. :param detailed: Optional flag, when set to True, will provide more information in the shown table :return: Nothing """ endpoints = client.get_endpoints(hostname=hostname, orchestrator_id=orchestrator_id, workload_id=workload_id, endpoint_id=endpoint_id) if detailed: headings = ["Hostname", "Orchestrator ID", "Workload ID", "Endpoint ID", "Addresses", "MAC", "Profiles", "State"] x = PrettyTable(headings, sortby="Hostname") for endpoint in endpoints: addresses = "\n".join([str(net) for net in endpoint.ipv4_nets | endpoint.ipv6_nets]) x.add_row([endpoint.hostname, endpoint.orchestrator_id, endpoint.workload_id, endpoint.endpoint_id, addresses, endpoint.mac, ','.join(endpoint.profile_ids), endpoint.state]) else: headings = ["Hostname", "Orchestrator ID", "Number of Workloads", "Number of Endpoints"] x = PrettyTable(headings, sortby="Hostname") # The summary table has one entry for each host/orchestrator # combination. We create a dictionary to maintain the summary # information, using the (hostname, orchestrator_id) as the unique # key, with a value of an EndpointSummary object to store unique # workload IDs and a count of endpoint IDs. # # We use a default dict to automatically create an "empty" (zero count) # EndpointSummary for each new table entry. host_orch_summary = defaultdict(EndpointSummary) for endpoint in endpoints: key = (endpoint.hostname, endpoint.orchestrator_id) summary = host_orch_summary[key] summary.add_endpoint(endpoint) # This table has one entry for each host/orchestrator combination. for key, summary in host_orch_summary.iteritems(): hostname, orchestrator_id = key x.add_row([hostname, orchestrator_id, len(summary.workload_ids), summary.num_endpoints]) print str(x) + "\n"