Exemple #1
0
    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)
Exemple #2
0
    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)
Exemple #4
0
    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)
Exemple #5
0
    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
Exemple #8
0
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