def test_max_metric(self, m_namespace): """ Test that route metrics are not incremented beyond the max metric value. """ mock_ns = Mock() m_namespace().__enter__.return_value = mock_ns max_metric = 0xFFFFFFFF ip_route_output = "default via 172.24.114.1 dev eth0\n" \ "default via 172.24.114.2 dev eth0 metric %d\n" \ "default via 172.24.114.3 dev eth0 metric %d" % \ (max_metric - 1, max_metric) mock_ns.check_output.return_value = ip_route_output expected_calls = [ call([ 'ip', 'route', 'add', 'default', 'via', '172.24.114.1', 'dev', 'eth0', 'metric', '1' ]), call([ 'ip', 'route', 'del', 'default', 'via', '172.24.114.1', 'dev', 'eth0', 'metric', '0' ]) ] increment_metrics("test_ns") mock_ns.check_output.assert_has_calls(expected_calls, any_order=False)
def test_metrics_increment(self, m_namespace): """ Test that route metrics are incremented properly. """ mock_ns = Mock() m_namespace().__enter__.return_value = mock_ns mock_ns.check_output.return_value = "default via 172.24.114.1 dev eth0\n" \ "default via 172.24.114.2 dev eth0 metric 1" expected_calls = [ call([ 'ip', 'route', 'add', 'default', 'via', '172.24.114.2', 'dev', 'eth0', 'metric', '2' ]), call([ 'ip', 'route', 'del', 'default', 'via', '172.24.114.2', 'dev', 'eth0', 'metric', '1' ]), call([ 'ip', 'route', 'add', 'default', 'via', '172.24.114.1', 'dev', 'eth0', 'metric', '1' ]), call([ 'ip', 'route', 'del', 'default', 'via', '172.24.114.1', 'dev', 'eth0', 'metric', '0' ]) ] increment_metrics("test_ns") mock_ns.check_output.assert_has_calls(expected_calls, any_order=False)
def test_metrics_increment(self, m_namespace): """ Test that route metrics are incremented properly. """ mock_ns = Mock() m_namespace().__enter__.return_value = mock_ns mock_ns.check_output.return_value = ( "default via 172.24.114.1 dev eth0\n" "default via 172.24.114.2 dev eth0 metric 1" ) expected_calls = [ call(["ip", "route", "add", "default", "via", "172.24.114.2", "dev", "eth0", "metric", "2"]), call(["ip", "route", "del", "default", "via", "172.24.114.2", "dev", "eth0", "metric", "1"]), call(["ip", "route", "add", "default", "via", "172.24.114.1", "dev", "eth0", "metric", "1"]), call(["ip", "route", "del", "default", "via", "172.24.114.1", "dev", "eth0", "metric", "0"]), ] increment_metrics("test_ns") mock_ns.check_output.assert_has_calls(expected_calls, any_order=False)
def test_metrics_increment(self, m_namespace): """ Test that route metrics are incremented properly. """ mock_ns = Mock() m_namespace().__enter__.return_value = mock_ns mock_ns.check_output.return_value = "default via 172.24.114.1 dev eth0\n" \ "default via 172.24.114.2 dev eth0 metric 1" expected_calls = [call(['ip', 'route', 'add', 'default', 'via', '172.24.114.2', 'dev', 'eth0', 'metric', '2']), call(['ip', 'route', 'del', 'default', 'via', '172.24.114.2', 'dev', 'eth0', 'metric', '1']), call(['ip', 'route', 'add', 'default', 'via', '172.24.114.1', 'dev', 'eth0', 'metric', '1']), call(['ip', 'route', 'del', 'default', 'via', '172.24.114.1', 'dev', 'eth0', 'metric', '0'])] increment_metrics("test_ns") mock_ns.check_output.assert_has_calls(expected_calls, any_order=False)
def test_max_metric(self, m_namespace): """ Test that route metrics are not incremented beyond the max metric value. """ mock_ns = Mock() m_namespace().__enter__.return_value = mock_ns max_metric = 0xFFFFFFFF ip_route_output = "default via 172.24.114.1 dev eth0\n" \ "default via 172.24.114.2 dev eth0 metric %d\n" \ "default via 172.24.114.3 dev eth0 metric %d" % \ (max_metric - 1, max_metric) mock_ns.check_output.return_value = ip_route_output expected_calls = [call(['ip', 'route', 'add', 'default', 'via', '172.24.114.1', 'dev', 'eth0', 'metric', '1']), call(['ip', 'route', 'del', 'default', 'via', '172.24.114.1', 'dev', 'eth0', 'metric', '0'])] increment_metrics("test_ns") mock_ns.check_output.assert_has_calls(expected_calls, any_order=False)
def test_max_metric(self, m_namespace): """ Test that route metrics are not incremented beyond the max metric value. """ mock_ns = Mock() m_namespace().__enter__.return_value = mock_ns max_metric = 0xFFFFFFFF ip_route_output = ( "default via 172.24.114.1 dev eth0\n" "default via 172.24.114.2 dev eth0 metric %d\n" "default via 172.24.114.3 dev eth0 metric %d" % (max_metric - 1, max_metric) ) mock_ns.check_output.return_value = ip_route_output expected_calls = [ call(["ip", "route", "add", "default", "via", "172.24.114.1", "dev", "eth0", "metric", "1"]), call(["ip", "route", "del", "default", "via", "172.24.114.1", "dev", "eth0", "metric", "0"]), ] increment_metrics("test_ns") mock_ns.check_output.assert_has_calls(expected_calls, any_order=False)
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 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 "%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) ip, pool = get_ip_and_pool(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 unallocated_ips = client.release_ips({ip}) if unallocated_ips: print ("Error during cleanup. {0} was already unallocated." ).format(unallocated_ips) 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 "IP %s added to %s" % (str(ip), container_id) return ep