def check_system(fix=False, quit_if_error=False): """ Checks that the system is setup correctly. fix==True, this command will attempt to fix any issues it encounters. If any fixes fail, it will exit(1). Fix will automatically be set to True if the user specifies --fix at the command line. :param fix: if True, try to fix any system dependency issues that are detected. :param quit_if_error: if True, quit with error code 1 if any issues are detected, or if any fixes are unsuccesful. :return: True if all system dependencies are in the proper state, False if they are not. This function will sys.exit(1) instead of returning false if quit_if_error == True """ # modprobe and sysctl require root privileges. enforce_root() system_ok = (_check_kernel_modules(fix) and _check_ip_forwarding(fix) and _check_docker_version()) if quit_if_error and not system_ok: sys.exit(1) return system_ok
def check_system(quit_if_error=False, libnetwork=False, check_docker=True, check_modules=True, check_etcd=True, check_kernel=True): """ Checks that the system is setup correctly. :param check_etcd: Whether to perform etcd checks. :param check_docker: Whether to perform docker checks. :param check_modules: Whether to perform module checks. :param quit_if_error: if True, quit with error code 1 if any issues are detected. :param libnetwork: If True, check for Docker version >= v1.21 to support libnetwork :return: a tuple containing the results of the checks This function will sys.exit(1) instead of returning false if quit_if_error == True """ enforce_root() modules_ok = _check_modules() if check_modules else True docker_ok = _check_docker_version(libnetwork) if check_docker else True etcd_ok = check_etcd_version() if check_etcd else True kernel_ok = _check_kernel_version() if check_kernel else True system_ok = modules_ok and docker_ok and etcd_ok and kernel_ok if quit_if_error and not system_ok: sys.exit(1) return (modules_ok, docker_ok, etcd_ok)
def check_system(quit_if_error=False, libnetwork=False, check_docker=True, check_modules=True, check_etcd=True, check_kernel=True): """ Checks that the system is setup correctly. :param check_etcd: Whether to perform etcd checks. :param check_docker: Whether to perform docker checks. :param check_modules: Whether to perform module checks. :param quit_if_error: if True, quit with error code 1 if any issues are detected. :param libnetwork: If True, check for Docker version >= v1.21 to support libnetwork :return: a tuple containing the results of the checks This function will sys.exit(1) instead of returning false if quit_if_error == True """ enforce_root() modules_ok = _check_modules() if check_modules else True docker_ok = _check_docker_version(libnetwork) if check_docker else True etcd_ok = _check_etcd_version() if check_etcd else True kernel_ok = _check_kernel_version() if check_kernel else True system_ok = modules_ok and docker_ok and etcd_ok and kernel_ok if quit_if_error and not system_ok: sys.exit(1) return (modules_ok, docker_ok, etcd_ok)
def status(arguments): """ Main dispatcher for status commands. Calls the corresponding helper function. :param arguments: A dictionary of arguments already processed through this file's docstring with docopt :return: None """ # Check runtime. runtime = arguments.get("--runtime") if not runtime in ["docker", "rkt"]: print "Invalid runtime specified: '%s'" % runtime sys.exit(1) # Check backend backend = arguments.get("--backend") if not backend in [None, "bird", "gobgp", "none"]: print "Invalid backend specified: '%s'" % backend sys.exit(1) # Start by locating the calico-node container and querying the package # summary file. if runtime == "rkt": enforce_root() check_container_status_rkt() else: check_container_status_docker() # Now query the host BGP details. If the AS number is not specified on the # host then it must be inheriting the default. try: bgp_ipv4, bgp_ipv6 = client.get_host_bgp_ips(hostname) bgp_as = client.get_host_as(hostname) if bgp_as is None: bgp_as = client.get_default_node_as() bgp_as += " (inherited)" except DataStoreError: print "Error connecting to etcd. Ensure ETCD_ENDPOINTS or ETCD_AUTHORITY is set properly." bgp_ipv4 = bgp_ipv6 = "unknown" bgp_as = "unknown" # TODO: Add additional information to the BIRD section: # TODO: - Include AS numbers of peers # TODO: - Include host name of peers when the peer is a calico-node # TODO: - Include details of peers configured multiple times print "\nIPv4 BGP status" if bgp_ipv4: print "IP: %s AS Number: %s" % (bgp_ipv4, bgp_as) pprint_bgp_protocols(4, backend) else: print "No IPv4 address configured.\n" print "IPv6 BGP status" if bgp_ipv6: print "IP: %s AS Number: %s" % (bgp_ipv6, bgp_as) pprint_bgp_protocols(6, backend) else: print "No IPv6 address configured.\n"
def container_ip_remove(container_name, ip, interface): """ Add an IP address to an existing Calico networked container. :param container_name: The name 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) info = get_container_info_or_exit(container_name) container_id = info["Id"] # Check the container is actually running. if not info["State"]["Running"]: print "%s is not currently running." % container_name sys.exit(1) # Check that the container is already networked try: endpoint = client.get_endpoint(hostname=hostname, orchestrator_id=ORCHESTRATOR_ID, workload_id=container_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: container_pid = info["State"]["Pid"] netns.remove_ip_from_ns_veth(container_pid, address, interface) except CalledProcessError: print "Error updating networking in container. Aborting." sys.exit(1) client.unassign_address(pool, ip) print "IP %s removed from %s" % (ip, container_name)
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 diags(arguments): """ Main dispatcher for diags commands. Calls the corresponding helper function. :param arguments: A dictionary of arguments already processed through this file's docstring with docopt :return: None """ enforce_root() print("Collecting diags") save_diags(arguments["--log-dir"], arguments["--upload"]) sys.exit(0)
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 workload_id = escape_etcd(container_id) orchestrator_id = NAMESPACE_ORCHESTRATOR_ID else: workload_id = get_workload_id(container_id) orchestrator_id = DOCKER_ORCHESTRATOR_ID # Find the endpoint ID. We need this to find any ACL rules try: endpoint = client.get_endpoint(hostname=hostname, orchestrator_id=orchestrator_id, workload_id=workload_id) except KeyError: print "Container %s doesn't contain any endpoints" % container_id sys.exit(1) # Remove any IP address assignments that this endpoint has for net in endpoint.ipv4_nets | endpoint.ipv6_nets: assert (net.size == 1) ip = net.ip pools = client.get_ip_pools(ip.version) for pool in pools: if ip in pool: # Ignore failure to unassign address, since we're not # enforcing assignments strictly in datastore.py. client.unassign_address(pool, ip) try: # Remove the interface if it exists netns.remove_veth(endpoint.name) except CalledProcessError: print "Could not remove Calico interface %s" % endpoint.name sys.exit(1) # Remove the container from the datastore. client.remove_workload(hostname, orchestrator_id, workload_id) print "Removed Calico interface from %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 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 workload_id = escape_etcd(container_id) orchestrator_id = NAMESPACE_ORCHESTRATOR_ID else: workload_id = get_workload_id(container_id) orchestrator_id = DOCKER_ORCHESTRATOR_ID # Find the endpoint ID. We need this to find any ACL rules try: endpoint = client.get_endpoint(hostname=hostname, orchestrator_id=orchestrator_id, workload_id=workload_id) except KeyError: print "Container %s doesn't contain any endpoints" % container_id sys.exit(1) # Remove any IP address assignments that this endpoint has for net in endpoint.ipv4_nets | endpoint.ipv6_nets: assert(net.size == 1) ip = net.ip pools = client.get_ip_pools(ip.version) for pool in pools: if ip in pool: # Ignore failure to unassign address, since we're not # enforcing assignments strictly in datastore.py. client.unassign_address(pool, ip) try: # Remove the interface if it exists netns.remove_veth(endpoint.name) except CalledProcessError: print "Could not remove Calico interface %s" % endpoint.name sys.exit(1) # Remove the container from the datastore. client.remove_workload(hostname, orchestrator_id, workload_id) print "Removed Calico interface from %s" % container_id
def diags(arguments): """ Main dispatcher for diags commands. Calls the corresponding helper function. :param arguments: A dictionary of arguments already processed through this file's docstring with docopt :return: None """ # The command has to be run as root for ipset collections (and iptables) enforce_root() print("Collecting diagnostics") save_diags(arguments["--log-dir"]) sys.exit(0)
def container_remove(container_name): """ 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_name: The name or ID of the container. """ # The netns manipulations must be done as root. enforce_root() # Resolve the name to ID. workload_id = get_container_id(container_name) # Find the endpoint ID. We need this to find any ACL rules try: endpoint = client.get_endpoint(hostname=hostname, orchestrator_id=ORCHESTRATOR_ID, workload_id=workload_id) except KeyError: print "Container %s doesn't contain any endpoints" % container_name sys.exit(1) # Remove any IP address assignments that this endpoint has for net in endpoint.ipv4_nets | endpoint.ipv6_nets: assert(net.size == 1) ip = net.ip pools = client.get_ip_pools("v%s" % ip.version) for pool in pools: if ip in pool: # Ignore failure to unassign address, since we're not # enforcing assignments strictly in datastore.py. client.unassign_address(pool, ip) # Remove the endpoint netns.remove_endpoint(endpoint.endpoint_id) # Remove the container from the datastore. client.remove_workload(hostname, ORCHESTRATOR_ID, workload_id) print "Removed Calico interface from %s" % container_name
def container_remove(container_name): """ 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_name: The name or ID of the container. """ # The netns manipulations must be done as root. enforce_root() # Resolve the name to ID. workload_id = get_container_id(container_name) # Find the endpoint ID. We need this to find any ACL rules try: endpoint = client.get_endpoint(hostname=hostname, orchestrator_id=ORCHESTRATOR_ID, workload_id=workload_id) except KeyError: print "Container %s doesn't contain any endpoints" % container_name sys.exit(1) # Remove any IP address assignments that this endpoint has for net in endpoint.ipv4_nets | endpoint.ipv6_nets: assert(net.size == 1) ip = net.ip pools = client.get_ip_pools(ip.version) for pool in pools: if ip in pool: # Ignore failure to unassign address, since we're not # enforcing assignments strictly in datastore.py. client.unassign_address(pool, ip) # Remove the endpoint netns.remove_veth(endpoint.name) # Remove the container from the datastore. client.remove_workload(hostname, ORCHESTRATOR_ID, workload_id) print "Removed Calico interface from %s" % container_name
def node_stop(force): """ Stop the Calico node. This stops the containers (calico/node and calico/node-libnetwork) that are started by calicoctl node. """ # The command has to be run as root to stop the calico-node service enforce_root() 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 try: call(["systemctl", "stop", "calico-node.service"]) except OSError: # systemctl not installed, ignore error. pass print "Node stopped"
def check_system(quit_if_error=False, libnetwork=False): """ Checks that the system is setup correctly. :param quit_if_error: if True, quit with error code 1 if any issues are detected. :param libnetwork: If True, check for Docker version >= v1.21 to support libnetwork :return: True if all system dependencies are in the proper state, False if they are not. This function will sys.exit(1) instead of returning false if quit_if_error == True """ enforce_root() modules_ok = _check_modules() docker_ok = _check_docker_version(libnetwork) system_ok = modules_ok and docker_ok if quit_if_error and not system_ok: sys.exit(1) return system_ok
def pprint_bird_protocols(version): """ Pretty print the output from the BIRD "show protocols". This parses the existing output and lays it out in pretty printed table. :param version: The IP version (4 or 6). :return: None. """ # This needs to be run as root to access the bird data in /var/run/calico enforce_root() # Based on the IP version, run the appropriate BIRD command, and select # the appropriate separator char for an IP address. if getattr(sys, 'frozen', False): # We're running under pyinstaller birdcl = sys._MEIPASS + "/birdcl" else: birdcl = "birdcl" try: if version == 4: results = subprocess32.check_output( "echo show protocols | %s -s /var/run/calico/bird.ctl" % birdcl, shell=True) ip_sep = "." else: results = subprocess32.check_output( "echo show protocols | %s -s /var/run/calico/bird6.ctl" % birdcl, shell=True) ip_sep = ":" except subprocess32.CalledProcessError: print "Couldn't connect to bird." return # Parse the output from BIRD to extract the values in the protocol status # table. We'll further parse the name since that includes details about # the type of peer and the peer IP address. x = PrettyTable(["Peer address", "Peer type", "State", "Since", "Info"]) lines = results.split("\n") found_table = False for line in lines: # When BIRD displays its protocol table, it prints the bird> prompt and # then shifts the cursor to print back over the prompt. However, this # means that we get rogue prompts when parsing the output. For this # processing just remove the prompt if it is present. if line.startswith("bird>"): line = line[5:] # Skip blank lines. line = line.strip() if not line: continue # Split the line into columns based on whitespace separators. We split # a maximum of 5 times because the 6th "info" column may contain a # string that itself includes whitespace that should be maintained. columns = re.split("\s+", line.strip(), 5) # Loop until we find the table heading. if columns == ["name", "proto", "table", "state", "since", "info"]: found_table = True continue elif not found_table: continue # We expect either 5 or 6 columns depending on whether there was a # value in the info column. Anything else is not handled, so revert # to displaying the raw BIRD output. if not (5 <= len(columns) <= 6): found_table = False break # Parse the name, we name our BGP peers as "Mesh", "Node" or "Global" # followed by the IP address. Extract the info so we can pretty # print it. combined = columns[0] if combined.startswith("Mesh_"): name = combined[5:].replace("_", ip_sep) ptype = "node-to-node mesh" elif combined.startswith("Node_"): name = combined[5:].replace("_", ip_sep) ptype = "node specific" elif combined.startswith("Global_"): name = combined[7:].replace("_", ip_sep) ptype = "global" else: # This is not a BGP Peer, so do not include in the output. continue x.add_row([name, ptype, columns[3], columns[4], columns[5] if len(columns) == 6 else ""]) # If we parsed the table then pretty print the table, otherwise just output # the BIRD output directly. The first line of the BIRD output provides an # overall BIRD status. if found_table: print str(x) + "\n" else: print results + "\n"
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_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_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 node_start(node_image, runtime, log_dir, ip, ip6, as_num, detach, libnetwork_image, no_pull): """ Create the calico-node container and establish Calico networking on this host. :param ip: The IPv4 address of the host. :param node_image: The calico-node image to use. :param ip6: The IPv6 address of the host (or None if not configured) :param as_num: The BGP AS Number to use for this node. If not specified the global default value will be used. :param detach: True to run in Docker's "detached" mode, False to run attached. :param libnetwork_image: The name of the Calico libnetwork driver image to use. None, if not using libnetwork. :param no_pull: Boolean, True to prevent function from pulling the Calico node Docker images. :return: None. """ # The command has to be run as root to access iptables and services enforce_root() # Normally, Felix will load the modules it needs, but when running inside a # container it might not be able to do so. Ensure the required modules are # loaded each time the node starts. # We only make a best effort attempt because the command may fail if the # modules are built in. # We'll warn during the check_system() if the modules are unavailable. if not running_in_container(): try: call(["modprobe", "-a"] + REQUIRED_MODULES) except OSError: pass _setup_ip_forwarding() _set_nf_conntrack_max() # Print warnings for any known system issues before continuing if runtime == 'docker' and not running_in_container(): using_docker = True else: using_docker = False (_, docker_ok, etcd_ok) = \ check_system(quit_if_error=False, libnetwork=libnetwork_image, check_docker=using_docker, check_modules=not running_in_container()) if not etcd_ok or (using_docker and not docker_ok): sys.exit(1) # Ensure log directory exists if not os.path.exists(log_dir): os.makedirs(log_dir) # The format of the authority and endpoints strings have already been # validated. etcd_authority = os.getenv(ETCD_AUTHORITY_ENV, ETCD_AUTHORITY_DEFAULT) etcd_endpoints = os.getenv(ETCD_ENDPOINTS_ENV) # Get etcd SSL environment variables if they exist etcd_scheme = os.getenv(ETCD_SCHEME_ENV, ETCD_SCHEME_DEFAULT) etcd_key_file = os.getenv(ETCD_KEY_FILE_ENV) etcd_cert_file = os.getenv(ETCD_CERT_FILE_ENV) etcd_ca_cert_file = os.getenv(ETCD_CA_CERT_FILE_ENV) etcd_volumes = [] etcd_binds = {} etcd_envs = ["ETCD_AUTHORITY=%s" % etcd_authority, "ETCD_SCHEME=%s" % etcd_scheme] if etcd_endpoints: etcd_envs.append("ETCD_ENDPOINTS=%s" % etcd_endpoints) if etcd_ca_cert_file and etcd_key_file and etcd_cert_file: etcd_volumes.append(ETCD_CA_CERT_NODE_FILE) etcd_binds[etcd_ca_cert_file] = {"bind": ETCD_CA_CERT_NODE_FILE, "ro": True} etcd_envs.append("ETCD_CA_CERT_FILE=%s" % ETCD_CA_CERT_NODE_FILE) etcd_volumes.append(ETCD_KEY_NODE_FILE) etcd_binds[etcd_key_file] = {"bind": ETCD_KEY_NODE_FILE, "ro": True} etcd_envs.append("ETCD_KEY_FILE=%s" % ETCD_KEY_NODE_FILE) etcd_volumes.append(ETCD_CERT_NODE_FILE) etcd_binds[etcd_cert_file] = {"bind": ETCD_CERT_NODE_FILE, "ro": True} etcd_envs.append("ETCD_CERT_FILE=%s" % ETCD_CERT_NODE_FILE) if runtime == 'docker': _start_node_container_docker(ip, ip6, as_num, log_dir, node_image, detach, etcd_envs, etcd_volumes, etcd_binds, no_pull) if libnetwork_image: _start_libnetwork_container(libnetwork_image, etcd_envs, etcd_volumes, etcd_binds, no_pull) if runtime == 'rkt': _start_node_container_rkt(ip, ip6, as_num, node_image, etcd_envs, etcd_volumes, etcd_binds)
def check_system(fix=False, quit_if_error=False): """ Checks that the system is setup correctly. fix==True, this command will attempt to fix any issues it encounters. If any fixes fail, it will exit(1). Fix will automatically be set to True if the user specifies --fix at the command line. :param fix: if True, try to fix any system dependency issues that are detected. :param quit_if_error: if True, quit with error code 1 if any issues are detected, or if any fixes are unsuccesful. :return: True if all system dependencies are in the proper state, False if they are not. This function will sys.exit(1) instead of returning false if quit_if_error == True """ # modprobe and sysctl require root privileges. enforce_root() system_ok = True modprobe = sh.Command._create('modprobe') ip6tables = sh.Command._create('ip6tables') try: ip6tables("-L") except: if fix: try: modprobe('ip6_tables') except sh.ErrorReturnCode: print >> sys.stderr, "ERROR: Could not enable ip6_tables." system_ok = False else: print >> sys.stderr, "WARNING: Unable to detect the ip6_tables " \ "module. Load with `modprobe ip6_tables`" system_ok = False if not module_loaded("xt_set"): if fix: try: modprobe('xt_set') except sh.ErrorReturnCode: print >> sys.stderr, "ERROR: Could not enable xt_set." system_ok = False else: print >> sys.stderr, "WARNING: Unable to detect the xt_set " \ "module. Load with `modprobe xt_set`" system_ok = False # Enable IP forwarding since all compute hosts are vRouters. # IPv4 forwarding should be enabled already by docker. if "1" not in sysctl("net.ipv4.ip_forward"): if fix: if "1" not in sysctl("-w", "net.ipv4.ip_forward=1"): print >> sys.stderr, "ERROR: Could not enable ipv4 forwarding." system_ok = False else: print >> sys.stderr, "WARNING: ipv4 forwarding is not enabled." system_ok = False if "1" not in sysctl("net.ipv6.conf.all.forwarding"): if fix: if "1" not in sysctl("-w", "net.ipv6.conf.all.forwarding=1"): print >> sys.stderr, "ERROR: Could not enable ipv6 forwarding." system_ok = False else: print >> sys.stderr, "WARNING: ipv6 forwarding is not enabled." system_ok = False # Check docker version compatability try: info = docker_client.version() except docker.errors.APIError: print >> sys.stderr, "ERROR: Docker server must support Docker " \ "Remote API v%s or greater." % DOCKER_VERSION system_ok = False else: api_version = normalize_version(info['ApiVersion']) # Check that API Version is above the minimum supported version if cmp(api_version, normalize_version(DOCKER_VERSION)) < 0: print >> sys.stderr, "ERROR: Docker server must support Docker " \ "Remote API v%s or greater." % DOCKER_VERSION system_ok = False if quit_if_error and not system_ok: sys.exit(1) return system_ok
def container_add(container_name, ip, interface): """ Add a container (on this host) to Calico networking with the given IP. :param container_name: The name or 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() info = get_container_info_or_exit(container_name) container_id = info["Id"] # Check if the container already exists try: _ = client.get_endpoint(hostname=hostname, orchestrator_id=ORCHESTRATOR_ID, workload_id=container_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 "%s has already been configured with Calico Networking." % \ container_name sys.exit(1) # Check the container is actually running. if not info["State"]["Running"]: print "%s is not currently running." % container_name sys.exit(1) # We can't set up Calico if the container shares the host namespace. if info["HostConfig"]["NetworkMode"] == "host": print "Can't add %s to Calico because it is " \ "running NetworkMode = host." % container_name sys.exit(1) # Check the IP is in the allocation pool. If it isn't, BIRD won't export # it. ip = IPAddress(ip) pool = get_pool_or_exit(ip) # The next hop IPs for this host are stored in etcd. next_hops = client.get_default_next_hops(hostname) try: next_hops[ip.version] except KeyError: print "This node is not configured for IPv%d." % ip.version sys.exit(1) # Assign the IP if not client.assign_address(pool, ip): print "IP address is already assigned in pool %s " % pool 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=ORCHESTRATOR_ID, workload_id=container_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. pid = info["State"]["Pid"] netns.create_veth(ep.name, ep.temp_interface_name) netns.move_veth_into_ns(pid, ep.temp_interface_name, interface) netns.add_ip_to_ns_veth(pid, ip, interface) netns.add_ns_default_route(pid, next_hop, interface) # Grab the MAC assigned to the veth in the namespace. ep.mac = netns.get_ns_veth_mac(pid, interface) # Register the endpoint with Felix. client.set_endpoint(ep) # Let the caller know what endpoint was created. return ep
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 """ 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) 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. if not client.assign_address(pool, address): print "IP address is already assigned in pool %s " % pool sys.exit(1) 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.unassign_address(pool, ip) print "Error updating datastore. 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.unassign_address(pool, ip) sys.exit(1) print "IP %s added to %s" % (ip, container_id)
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.unassign_address(pool, address) print "IP %s removed from %s" % (ip, 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() 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 "%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 "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 "%s has already been configured with Calico Networking." % \ container_id sys.exit(1) # Check the IP is in the allocation pool. If it isn't, BIRD won't export # it. ip = IPAddress(ip) pool = get_pool_or_exit(ip) # The next hop IPs for this host are stored in etcd. next_hops = client.get_default_next_hops(hostname) try: next_hops[ip.version] except KeyError: print "This node is not configured for IPv%d." % ip.version sys.exit(1) # Assign the IP if not client.assign_address(pool, ip): print "IP address is already assigned in pool %s " % pool 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.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. return ep
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) ep = Endpoint(hostname=hostname, orchestrator_id=DOCKER_ORCHESTRATOR_ID, workload_id=workload_id, endpoint_id=uuid.uuid1().hex, state="active", mac=None) ip, _ = get_ip_and_pool(ip) network = IPNetwork(ip) if network.version == 4: ep.ipv4_nets.add(network) else: ep.ipv6_nets.add(network) # 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, ep.name, 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 container_add(container_name, ip, interface): """ Add a container (on this host) to Calico networking with the given IP. :param container_name: The name or ID of the container. :param ip: An IPAddress object with the desired IP to assign. """ # The netns manipulations must be done as root. enforce_root() info = get_container_info_or_exit(container_name) container_id = info["Id"] # Check if the container already exists try: _ = client.get_endpoint(hostname=hostname, orchestrator_id=ORCHESTRATOR_ID, workload_id=container_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 "%s has already been configured with Calico Networking." % \ container_name sys.exit(1) # Check the container is actually running. if not info["State"]["Running"]: print "%s is not currently running." % container_name sys.exit(1) # Check the IP is in the allocation pool. If it isn't, BIRD won't export # it. ip = IPAddress(ip) pool = get_pool_or_exit(ip) # The next hop IPs for this host are stored in etcd. next_hops = client.get_default_next_hops(hostname) try: next_hops[ip.version] except KeyError: print "This node is not configured for IPv%d." % ip.version sys.exit(1) # Assign the IP if not client.assign_address(pool, ip): print "IP address is already assigned in pool %s " % pool sys.exit(1) # Actually configure the netns. Defaults to eth1 since eth0 could # already be in use (e.g. by the Docker bridge) pid = info["State"]["Pid"] endpoint = netns.set_up_endpoint(ip=ip, hostname=hostname, orchestrator_id=ORCHESTRATOR_ID, workload_id=container_id, cpid=pid, next_hop_ips=next_hops, veth_name=interface, proc_alias="/proc") # Register the endpoint client.set_endpoint(endpoint) print "IP %s added to %s" % (ip, container_name)
def container_ip_remove(container_name, ip, version, interface): """ Add an IP address to an existing Calico networked container. :param container_name: The name of the container. :param ip: The IP to add :param version: The IP version ("v4" or "v6") :param interface: The name of the interface in the container. :return: None """ address = check_ip_version(ip, version, IPAddress) # The netns manipulations must be done as root. enforce_root() pool = get_pool_or_exit(address) info = get_container_info_or_exit(container_name) container_id = info["Id"] # Check the container is actually running. if not info["State"]["Running"]: print "%s is not currently running." % container_name sys.exit(1) # Check that the container is already networked try: endpoint = client.get_endpoint(hostname=hostname, orchestrator_id=ORCHESTRATOR_ID, workload_id=container_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: container_pid = info["State"]["Pid"] netns.remove_ip_from_interface(container_pid, address, interface, proc_alias="/proc") except CalledProcessError: print "Error updating networking in container. Aborting." sys.exit(1) client.unassign_address(pool, ip) print "IP %s removed from %s" % (ip, container_name)
def container_ip_add(container_name, ip, version, interface): """ Add an IP address to an existing Calico networked container. :param container_name: The name of the container. :param ip: The IP to add :param version: The IP version ("v4" or "v6") :param interface: The name of the interface in the container. :return: None """ address = check_ip_version(ip, version, IPAddress) # The netns manipulations must be done as root. enforce_root() pool = get_pool_or_exit(address) info = get_container_info_or_exit(container_name) container_id = info["Id"] # Check the container is actually running. if not info["State"]["Running"]: print "%s is not currently running." % container_name sys.exit(1) # Check that the container is already networked try: endpoint = client.get_endpoint(hostname=hostname, orchestrator_id=ORCHESTRATOR_ID, workload_id=container_id) except KeyError: print "Failed to add IP address to container.\n" print_container_not_in_calico_msg(container_name) 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. if not client.assign_address(pool, ip): print "IP address is already assigned in pool %s " % pool sys.exit(1) 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.unassign_address(pool, ip) print "Error updating datastore. Aborting." sys.exit(1) try: container_pid = info["State"]["Pid"] netns.add_ip_to_interface(container_pid, address, interface, proc_alias="/proc") 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.unassign_address(pool, ip) sys.exit(1) print "IP %s added to %s" % (ip, container_id)
def node_start(node_image, runtime, log_dir, ip, ip6, as_num, detach, libnetwork_image, no_pull): """ Create the calico-node container and establish Calico networking on this host. :param ip: The IPv4 address of the host. :param node_image: The calico-node image to use. :param ip6: The IPv6 address of the host (or None if not configured) :param as_num: The BGP AS Number to use for this node. If not specified the global default value will be used. :param detach: True to run in Docker's "detached" mode, False to run attached. :param libnetwork_image: The name of the Calico libnetwork driver image to use. None, if not using libnetwork. :param no_pull: Boolean, True to prevent function from pulling the Calico node Docker images. :return: None. """ # The command has to be run as root to access iptables and services enforce_root() # Normally, Felix will load the modules it needs, but when running inside a # container it might not be able to do so. Ensure the required modules are # loaded each time the node starts. # We only make a best effort attempt because the command may fail if the # modules are built in. # We'll warn during the check_system() if the modules are unavailable. if not running_in_container(): try: call(["modprobe", "-a"] + REQUIRED_MODULES) except OSError: pass _setup_ip_forwarding() _set_nf_conntrack_max() # Print warnings for any known system issues before continuing if runtime == 'docker' and not running_in_container(): using_docker = True else: using_docker = False (_, docker_ok, etcd_ok) = \ check_system(quit_if_error=False, libnetwork=libnetwork_image, check_docker=using_docker, check_modules=not running_in_container()) if not etcd_ok or (using_docker and not docker_ok): sys.exit(1) # Ensure log directory exists if not os.path.exists(log_dir): os.makedirs(log_dir) # The format of the authority and endpoints strings have already been # validated. etcd_authority = os.getenv(ETCD_AUTHORITY_ENV, ETCD_AUTHORITY_DEFAULT) etcd_endpoints = os.getenv(ETCD_ENDPOINTS_ENV) # Get etcd SSL environment variables if they exist etcd_scheme = os.getenv(ETCD_SCHEME_ENV, ETCD_SCHEME_DEFAULT) etcd_key_file = os.getenv(ETCD_KEY_FILE_ENV) etcd_cert_file = os.getenv(ETCD_CERT_FILE_ENV) etcd_ca_cert_file = os.getenv(ETCD_CA_CERT_FILE_ENV) etcd_volumes = [] etcd_binds = {} etcd_envs = [ "ETCD_AUTHORITY=%s" % etcd_authority, "ETCD_SCHEME=%s" % etcd_scheme ] if etcd_endpoints: etcd_envs.append("ETCD_ENDPOINTS=%s" % etcd_endpoints) if etcd_ca_cert_file and etcd_key_file and etcd_cert_file: etcd_volumes.append(ETCD_CA_CERT_NODE_FILE) etcd_binds[etcd_ca_cert_file] = { "bind": ETCD_CA_CERT_NODE_FILE, "ro": True } etcd_envs.append("ETCD_CA_CERT_FILE=%s" % ETCD_CA_CERT_NODE_FILE) etcd_volumes.append(ETCD_KEY_NODE_FILE) etcd_binds[etcd_key_file] = {"bind": ETCD_KEY_NODE_FILE, "ro": True} etcd_envs.append("ETCD_KEY_FILE=%s" % ETCD_KEY_NODE_FILE) etcd_volumes.append(ETCD_CERT_NODE_FILE) etcd_binds[etcd_cert_file] = {"bind": ETCD_CERT_NODE_FILE, "ro": True} etcd_envs.append("ETCD_CERT_FILE=%s" % ETCD_CERT_NODE_FILE) if runtime == 'docker': _start_node_container_docker(ip, ip6, as_num, log_dir, node_image, detach, etcd_envs, etcd_volumes, etcd_binds, no_pull) if libnetwork_image: _start_libnetwork_container(libnetwork_image, etcd_envs, etcd_volumes, etcd_binds, no_pull) if runtime == 'rkt': _start_node_container_rkt(ip, ip6, as_num, node_image, etcd_envs, etcd_volumes, etcd_binds)