def test_get_host_ips_exclude(self, m_check_output): # Exclude "docker0" m_check_output.return_value = MOCK_IP_ADDR addrs = get_host_ips(version=4, exclude=["docker0"]) m_check_output.assert_called_once_with(["ip", "-4", "addr"]) m_check_output.reset_mock() self.assertEquals(addrs, ['172.24.114.18']) addrs = get_host_ips(version=6, exclude=["docker0"]) m_check_output.assert_called_once_with(["ip", "-6", "addr"]) m_check_output.reset_mock() self.assertEquals(addrs, [ '2620:104:4008:69:8d7c:499f:2f04:9e55', '2620:104:4008:69:a00:27ff:fe73:c8d0', 'fe80::a00:27ff:fe73:c8d0' ]) # Exclude empty list addrs = get_host_ips(version=4, exclude=[""]) m_check_output.assert_called_once_with(["ip", "-4", "addr"]) m_check_output.reset_mock() self.assertEquals(addrs, ['172.24.114.18', '172.17.42.1']) addrs = get_host_ips(version=6, exclude=[""]) m_check_output.assert_called_once_with(["ip", "-6", "addr"]) m_check_output.reset_mock() self.assertEquals(addrs, [ '2620:104:4008:69:8d7c:499f:2f04:9e55', '2620:104:4008:69:a00:27ff:fe73:c8d0', 'fe80::a00:27ff:fe73:c8d0', 'fe80::188f:d6ff:fe1f:1482' ])
def test_get_host_ips_exclude(self, m_check_output): # Exclude "docker0" m_check_output.return_value = MOCK_IP_ADDR addrs = get_host_ips(version=4, exclude=["docker0"]) m_check_output.assert_called_once_with(["ip", "-4", "addr"]) m_check_output.reset_mock() self.assertEquals(addrs, ['172.24.114.18']) addrs = get_host_ips(version=6, exclude=["docker0"]) m_check_output.assert_called_once_with(["ip", "-6", "addr"]) m_check_output.reset_mock() self.assertEquals(addrs, ['2620:104:4008:69:8d7c:499f:2f04:9e55', '2620:104:4008:69:a00:27ff:fe73:c8d0', 'fe80::a00:27ff:fe73:c8d0']) # Exclude empty list addrs = get_host_ips(version=4, exclude=[""]) m_check_output.assert_called_once_with(["ip", "-4", "addr"]) m_check_output.reset_mock() self.assertEquals(addrs, ['172.24.114.18', '172.17.42.1']) addrs = get_host_ips(version=6, exclude=[""]) m_check_output.assert_called_once_with(["ip", "-6", "addr"]) m_check_output.reset_mock() self.assertEquals(addrs, ['2620:104:4008:69:8d7c:499f:2f04:9e55', '2620:104:4008:69:a00:27ff:fe73:c8d0', 'fe80::a00:27ff:fe73:c8d0', 'fe80::188f:d6ff:fe1f:1482'])
def test_get_host_ips_loopback_only(self, m_check_output): # Test Loopback m_check_output.return_value = MOCK_IP_ADDR_LOOPBACK addrs = get_host_ips(version=4) m_check_output.assert_called_once_with(["ip", "-4", "addr"]) m_check_output.reset_mock() self.assertEquals(addrs, []) addrs = get_host_ips(version=6) m_check_output.assert_called_once_with(["ip", "-6", "addr"]) m_check_output.reset_mock() self.assertEquals(addrs, [])
def warn_if_unknown_ip(ip, ip6): """ Prints a warning message if the IP addresses are not assigned to interfaces on the current host. :param ip: IPv4 address which should be present on the host. :param ip6: IPv6 address which should be present on the host. :return: None """ if ip and IPAddress(ip) not in get_host_ips(version=4, exclude=["docker0"]): print "WARNING: Could not confirm that the provided IPv4 address is" " assigned to this host." if ip6 and IPAddress(ip6) not in get_host_ips(version=6, exclude=["docker0"]): print "WARNING: Could not confirm that the provided IPv6 address is" " assigned to this host."
def test_get_host_ips_loopback_only(self, m_check_output): '''Test get_host_ips with loopback''' # Test IPv4 m_check_output.return_value = MOCK_IP_ADDR_LOOPBACK addrs = util.get_host_ips(version=4) m_check_output.assert_called_once_with(["ip", "-4", "addr"]) m_check_output.reset_mock() self.assertEqual(addrs, []) # Test IPv6 addrs = util.get_host_ips(version=6) m_check_output.assert_called_once_with(["ip", "-6", "addr"]) m_check_output.reset_mock() self.assertEqual(addrs, [])
def _get_node_ip(self): """ Determine the IP for the host node. """ # Compile list of addresses on network, return the first entry. # Try IPv4 and IPv6. addrs = get_host_ips(version=4) or get_host_ips(version=6) try: addr = addrs[0] print('Using IP Address %s' % (addr)) return addr except IndexError: # If both get_host_ips return empty lists, print message and exit. print('No Valid IP Address Found for Host - cannot configure networking for pod %s' % (self.pod_name)) sys.exit(1)
def warn_if_unknown_ip(ip, ip6): """ Prints a warning message if the IP addresses are not assigned to interfaces on the current host. :param ip: IPv4 address which should be present on the host. :param ip6: IPv6 address which should be present on the host. :return: None """ if ip and ip not in get_host_ips(version=4, exclude=["docker0"]): print "WARNING: Could not confirm that the provided IPv4 address is assigned" \ " to this host." if ip6 and ip6 not in get_host_ips(version=6, exclude=["docker0"]): print "WARNING: Could not confirm that the provided IPv6 address is assigned" \ " to this host."
def test_get_host_ips_standard(self, m_check_output): # Test IPv4 m_check_output.return_value = MOCK_IP_ADDR addrs = get_host_ips(version=4) m_check_output.assert_called_once_with(["ip", "-4", "addr"]) m_check_output.reset_mock() self.assertEquals(addrs, ['172.24.114.18', '172.17.42.1']) # Test IPv6 addrs = get_host_ips(version=6) m_check_output.assert_called_once_with(["ip", "-6", "addr"]) m_check_output.reset_mock() self.assertEquals(addrs, ['2620:104:4008:69:8d7c:499f:2f04:9e55', '2620:104:4008:69:a00:27ff:fe73:c8d0', 'fe80::a00:27ff:fe73:c8d0', 'fe80::188f:d6ff:fe1f:1482'])
def get_ip(v6=False): """ Return a string of the IP of the hosts interface. Try to get the local IP from the environment variables. This allows testers to specify the IP address in cases where there is more than one configured IP address for the test system. """ env = LOCAL_IPv6_ENV if v6 else LOCAL_IP_ENV ip = os.environ.get(env) if not ip: try: # No env variable set; try to auto detect. socket_type = socket.AF_INET6 if v6 else socket.AF_INET s = socket.socket(socket_type, socket.SOCK_DGRAM) remote_ip = "2001:4860:4860::8888" if v6 else "8.8.8.8" s.connect((remote_ip, 0)) ip = s.getsockname()[0] s.close() except BaseException: # Failed to connect, just try to get the address from the interfaces version = 6 if v6 else 4 ips = get_host_ips(version) if ips: ip = str(ips[0]) return ip
def test_get_host_ips_exclude_docker_prefix(self, m_check_output): '''Test get_host_ips exclude "docker0.*''' # Test IPv4 m_check_output.return_value = MOCK_IP_ADDR_DOCKER_NONE addrs = get_host_ips(version=4, exclude=["docker0.*"]) m_check_output.assert_called_once_with(["ip", "-4", "addr"]) m_check_output.reset_mock() self.assertEquals(addrs, ['172.24.114.18']) # Test IPv6 addrs = get_host_ips(version=6, exclude=["docker0.*"]) m_check_output.assert_called_once_with(["ip", "-6", "addr"]) m_check_output.reset_mock() self.assertEquals(addrs, ['2620:104:4008:69:8d7c:499f:2f04:9e55', '2620:104:4008:69:a00:27ff:fe73:c8d0', 'fe80::a00:27ff:fe73:c8d0'])
def get_ip(v6=False): """ Return a string of the IP of the hosts interface. Try to get the local IP from the environment variables. This allows testers to specify the IP address in cases where there is more than one configured IP address for the test system. """ env = LOCAL_IPv6_ENV if v6 else LOCAL_IP_ENV ip = os.environ.get(env) if not ip: try: # No env variable set; try to auto detect. socket_type = socket.AF_INET6 if v6 else socket.AF_INET s = socket.socket(socket_type, socket.SOCK_DGRAM) remote_ip = "2001:4860:4860::8888" if v6 else "8.8.8.8" s.connect((remote_ip, 0)) ip = s.getsockname()[0] s.close() except BaseException: # Failed to connect, just try to get the address from the interfaces version = 6 if v6 else 4 ips = get_host_ips(version) if ips: ip = ips[0] return ip
def test_get_host_ips_exclude_docker(self, m_check_output): '''Test get_host_ips exclude "docker0"''' # Test IPv4 m_check_output.return_value = MOCK_IP_ADDR addrs = get_host_ips(version=4, exclude=["docker0"]) m_check_output.assert_called_once_with(["ip", "-4", "addr"]) m_check_output.reset_mock() self.assertEquals(addrs, ["172.24.114.18"]) # Test IPv6 addrs = get_host_ips(version=6, exclude=["docker0"]) m_check_output.assert_called_once_with(["ip", "-6", "addr"]) m_check_output.reset_mock() self.assertEquals( addrs, ["2620:104:4008:69:8d7c:499f:2f04:9e55", "2620:104:4008:69:a00:27ff:fe73:c8d0", "fe80::a00:27ff:fe73:c8d0"], )
def test_get_host_ips_standard(self, m_check_output): # Test IPv4 m_check_output.return_value = MOCK_IP_ADDR addrs = get_host_ips(version=4) m_check_output.assert_called_once_with(["ip", "-4", "addr"]) m_check_output.reset_mock() self.assertEquals(addrs, ['172.24.114.18', '172.17.42.1']) # Test IPv6 addrs = get_host_ips(version=6) m_check_output.assert_called_once_with(["ip", "-6", "addr"]) m_check_output.reset_mock() self.assertEquals(addrs, [ '2620:104:4008:69:8d7c:499f:2f04:9e55', '2620:104:4008:69:a00:27ff:fe73:c8d0', 'fe80::a00:27ff:fe73:c8d0', 'fe80::188f:d6ff:fe1f:1482' ])
def test_get_host_ips_exclude_docker_prefix(self, m_check_output): '''Test get_host_ips exclude "docker0.*''' # Test IPv4 m_check_output.return_value = MOCK_IP_ADDR_DOCKER_NONE addrs = get_host_ips(version=4, exclude=["docker0.*"]) m_check_output.assert_called_once_with(["ip", "-4", "addr"]) m_check_output.reset_mock() self.assertEquals(addrs, ['172.24.114.18']) # Test IPv6 addrs = get_host_ips(version=6, exclude=["docker0.*"]) m_check_output.assert_called_once_with(["ip", "-6", "addr"]) m_check_output.reset_mock() self.assertEquals(addrs, [ '2620:104:4008:69:8d7c:499f:2f04:9e55', '2620:104:4008:69:a00:27ff:fe73:c8d0', 'fe80::a00:27ff:fe73:c8d0' ])
def _get_node_ip(self): """ Determine the IP for the host node. """ # Compile list of addresses on network, return the first entry. # Try IPv4 and IPv6. addrs = get_host_ips(version=4) or get_host_ips(version=6) try: addr = addrs[0] logger.debug("Node's IP address: %s", addr) return addr except IndexError: # If both get_host_ips return empty lists, print message and exit. logger.exception('No Valid IP Address Found for Host - cannot ' 'configure networking for pod %s. ' 'Exiting', self.pod_name) sys.exit(1)
def _get_node_ip(self): """ Determine the IP for the host node. """ # Compile list of addresses on network, return the first entry. # Try IPv4 and IPv6. addrs = get_host_ips(version=4) or get_host_ips(version=6) try: addr = addrs[0] print('Using IP Address %s' % (addr)) return addr except IndexError: # If both get_host_ips return empty lists, print message and exit. print( 'No Valid IP Address Found for Host - cannot configure networking for pod %s' % (self.pod_name)) sys.exit(1)
def test_get_host_ips_exclude_empty(self, m_check_output): '''Test get_host_ips exclude empty list''' # Test IPv4 m_check_output.return_value = MOCK_IP_ADDR addrs = get_host_ips(version=4, exclude=["^$"]) m_check_output.assert_called_once_with(["ip", "-4", "addr"]) m_check_output.reset_mock() self.assertEquals(addrs, [IPAddress('172.24.114.18'), IPAddress('172.17.42.1')]) # Test IPv6 addrs = get_host_ips(version=6, exclude=["^$"]) m_check_output.assert_called_once_with(["ip", "-6", "addr"]) m_check_output.reset_mock() self.assertEquals(addrs, [IPAddress('2620:104:4008:69:8d7c:499f:2f04:9e55'), IPAddress('2620:104:4008:69:a00:27ff:fe73:c8d0'), IPAddress('fe80::a00:27ff:fe73:c8d0'), IPAddress('fe80::188f:d6ff:fe1f:1482')])
def test_get_host_ips_standard(self, m_check_output): '''Test general case for get_host_ips''' # Test IPv4 m_check_output.return_value = MOCK_IP_ADDR addrs = util.get_host_ips(version=4) m_check_output.assert_called_once_with(["ip", "-4", "addr"]) m_check_output.reset_mock() self.assertEqual(addrs, [IPAddress('172.24.114.18'), IPAddress('172.17.42.1')]) # Test IPv6 addrs = util.get_host_ips(version=6) m_check_output.assert_called_once_with(["ip", "-6", "addr"]) m_check_output.reset_mock() self.assertEqual(addrs, [IPAddress('2620:104:4008:69:8d7c:499f:2f04:9e55'), IPAddress('2620:104:4008:69:a00:27ff:fe73:c8d0'), IPAddress('fe80::a00:27ff:fe73:c8d0'), IPAddress('fe80::188f:d6ff:fe1f:1482')])
def test_get_host_ips_exclude_empty(self, m_check_output): '''Test get_host_ips exclude empty list''' # Test IPv4 m_check_output.return_value = MOCK_IP_ADDR addrs = get_host_ips(version=4, exclude=["^$"]) m_check_output.assert_called_once_with(["ip", "-4", "addr"]) m_check_output.reset_mock() self.assertEquals( addrs, [IPAddress('172.24.114.18'), IPAddress('172.17.42.1')]) # Test IPv6 addrs = get_host_ips(version=6, exclude=["^$"]) m_check_output.assert_called_once_with(["ip", "-6", "addr"]) m_check_output.reset_mock() self.assertEquals(addrs, [ IPAddress('2620:104:4008:69:8d7c:499f:2f04:9e55'), IPAddress('2620:104:4008:69:a00:27ff:fe73:c8d0'), IPAddress('fe80::a00:27ff:fe73:c8d0'), IPAddress('fe80::188f:d6ff:fe1f:1482') ])
def test_get_host_ips_standard(self, m_check_output): """Test general case for get_host_ips""" # Test IPv4 m_check_output.return_value = MOCK_IP_ADDR addrs = get_host_ips(version=4) m_check_output.assert_called_once_with(["ip", "-4", "addr"]) m_check_output.reset_mock() self.assertEquals(addrs, ["172.24.114.18", "172.17.42.1"]) # Test IPv6 addrs = get_host_ips(version=6) m_check_output.assert_called_once_with(["ip", "-6", "addr"]) m_check_output.reset_mock() self.assertEquals( addrs, [ "2620:104:4008:69:8d7c:499f:2f04:9e55", "2620:104:4008:69:a00:27ff:fe73:c8d0", "fe80::a00:27ff:fe73:c8d0", "fe80::188f:d6ff:fe1f:1482", ], )
def test_isolate(self): # Test isolate hostname = socket.gethostname() indata = { "command": "isolate", "args": { "hostname": hostname, "container-id": self.container_id, "ipv4_addrs": ["192.168.23.4"], "ipv6_addrs": [], "netgroups": ["prod", "frontend"], "labels": { "rack": "3A", "pop": "houston" }, "pid": 3789 } } # Prepare network environment calicoctl("pool", "add", "192.168.0.0/16") # Set up bgp host configuration calicoctl("node") output = self.binary_exec(indata) self.assertEqual(self.stderr, '') self.assertEqual(output, error_msg()) # Check if the endpoint was correctly written to etcd host = "127.0.0.1" port = 4001 etcd_client = etcd.Client(host=host, port=port) leaves = etcd_client.read('/calico/v1/host/%s/workload/%s/%s/endpoint' % \ (hostname, "mesos", self.container_id), recursive=True).leaves values = [leaf for leaf in leaves] self.assertEqual( len(values), 1, "Only 1 endpoint should exist: %d were found" % len(values)) endpoint = values.pop() endpoint_dict = json.loads(endpoint.value) self.assertEqual(endpoint_dict["ipv4_gateway"], get_host_ips(exclude="docker0").pop()) self.assertEqual(endpoint_dict["ipv4_nets"], ["192.168.23.4"]) self.assertEqual(endpoint_dict["profile_ids"], ["prod", "frontend"])
def create_profile_with_default_mesos_rules(profile): _log.info("Autocreating profile %s", profile) datastore.create_profile(profile) prof = datastore.get_profile(profile) # Set up the profile rules to allow incoming connections from the host # since the slave process will be running there. # Also allow connections from others in the profile. # Deny other connections (default, so not explicitly needed). # TODO: confirm that we're not getting more interfaces than we bargained for ipv4 = get_host_ips(4, exclude=["docker0"]).pop() host_net = ipv4 + "/32" _log.info("adding accept rule for %s" % host_net) allow_slave = Rule(action="allow", src_net=host_net) allow_self = Rule(action="allow", src_tag=profile) allow_all = Rule(action="allow") prof.rules = Rules(id=profile, inbound_rules=[allow_slave, allow_self], outbound_rules=[allow_all]) datastore.profile_update_rules(prof)
def test_isolate(self): # Test isolate hostname = socket.gethostname() indata = { "command": "isolate", "args": { "hostname": hostname, "container-id": self.container_id, "ipv4_addrs": ["192.168.23.4"], "ipv6_addrs": [], "netgroups": ["prod", "frontend"], "labels": { "rack": "3A", "pop": "houston" }, "pid": 3789 } } # Prepare network environment calicoctl("pool", "add", "192.168.0.0/16") # Set up bgp host configuration calicoctl("node") output = self.binary_exec(indata) self.assertEqual(self.stderr, '') self.assertEqual(output, error_msg()) # Check if the endpoint was correctly written to etcd host = "127.0.0.1" port = 4001 etcd_client = etcd.Client(host=host, port=port) leaves = etcd_client.read('/calico/v1/host/%s/workload/%s/%s/endpoint' % \ (hostname, "mesos", self.container_id), recursive=True).leaves values = [leaf for leaf in leaves] self.assertEqual(len(values), 1, "Only 1 endpoint should exist: %d were found" % len(values)) endpoint = values.pop() endpoint_dict = json.loads(endpoint.value) self.assertEqual(endpoint_dict["ipv4_gateway"], get_host_ips(exclude="docker0").pop()) self.assertEqual(endpoint_dict["ipv4_nets"], ["192.168.23.4"]) self.assertEqual(endpoint_dict["profile_ids"], ["prod", "frontend"])
def _create_profile_with_default_mesos_rules(profile): _log.info("Autocreating profile %s", profile) datastore.create_profile(profile) prof = datastore.get_profile(profile) # Set up the profile rules to allow incoming connections from the host # since the slave process will be running there. # Also allow connections from others in the profile. # Deny other connections (default, so not explicitly needed). # TODO: confirm that we're not getting more interfaces than we bargained for ipv4 = get_host_ips(4, exclude=["docker0"]).pop() host_net = str(_get_host_ip_net()) _log.info("adding accept rule for %s" % host_net) allow_slave = Rule(action="allow", src_net=host_net) allow_self = Rule(action="allow", src_tag=profile) allow_all = Rule(action="allow") prof.rules = Rules(id=profile, inbound_rules=[allow_slave, allow_self], outbound_rules=[allow_all]) datastore.profile_update_rules(prof)
def main(): # If we're running with the k8s backend, don't do any of this, # since it currently doesn't support BGP, Calico IPAM, and doesn't # require any of the etcd interactions below. if os.getenv("DATASTORE_TYPE", "") == "kubernetes": print "Using k8s backend" with open('startup.env', 'w') as f: f.write("DATASTORE_TYPE=kubernetes\n") f.write("HOSTNAME=%s\n" % hostname) return # Check to see if etcd is available. If not, wait until it is before # continuing. This is to avoid etcd / node startup race conditions. print "Waiting for etcd connection..." while os.getenv("WAIT_FOR_DATASTORE", "false") == "true": try: # Just try accessing etcd to see if we can reach it or not. client.get_host_as(hostname) except DataStoreError: # Not connected to etcd yet, wait a bit. time.sleep(1) continue else: # Connected to etcd - break out of loop. print "Connected to etcd" break # Start node. ip = os.getenv("IP") ip = ip or None if ip and not netaddr.valid_ipv4(ip): print "IP environment (%s) is not a valid IPv4 address." % ip sys.exit(1) ip6 = os.getenv("IP6") ip6 = ip6 or None if ip6 and not netaddr.valid_ipv6(ip6): print "IP6 environment (%s) is not a valid IPv6 address." % ip6 sys.exit(1) as_num = os.getenv("AS") as_num = as_num or None if as_num and not validate_asn(as_num): print "AS environment (%s) is not a AS number." % as_num sys.exit(1) # Get IP address of host, if none was specified if not ip: ips = get_host_ips(exclude=[ "^docker.*", "^cbr.*", "virbr.*", "lxcbr.*", "veth.*", "cali.*", "tunl.*", "flannel.*" ]) try: ip = str(ips.pop()) except IndexError: print "Couldn't autodetect a management IP address. Please " \ "provide an IP address by rerunning the container with the" \ " IP environment variable set." sys.exit(1) else: print "No IP provided. Using detected IP: %s" % ip # Write a startup environment file containing the IP address that may have # just been detected. # This is required because the confd templates expect to be able to fill in # some templates by fetching them from the environment. with open('startup.env', 'w') as f: f.write("IP=%s\n" % ip) f.write("HOSTNAME=%s\n" % hostname) warn_if_hostname_conflict(ip) # Verify that IPs are not already in use by another host. error_if_bgp_ip_conflict(ip, ip6) # Verify that the chosen IP exists on the current host warn_if_unknown_ip(ip, ip6) if os.getenv("NO_DEFAULT_POOLS", "").lower() != "true": # Set up etcd ipv4_pools = client.get_ip_pools(4) ipv6_pools = client.get_ip_pools(6) # Create default pools if required if not ipv4_pools: client.add_ip_pool(4, DEFAULT_IPV4_POOL) # If the OS has not been built with IPv6 then the /proc config for IPv6 # will not be present. if not ipv6_pools and os.path.exists('/proc/sys/net/ipv6'): client.add_ip_pool(6, DEFAULT_IPV6_POOL) client.ensure_global_config() client.create_host(hostname, ip, ip6, as_num) # If IPIP is enabled, the host requires an IP address for its tunnel # device, which is in an IPIP pool. Without this, a host can't originate # traffic to a pool address because the response traffic would not be # routed via the tunnel (likely being dropped by RPF checks in the fabric). ipv4_pools = client.get_ip_pools(4) ipip_pools = [p for p in ipv4_pools if p.ipip] if ipip_pools: # IPIP is enabled, make sure the host has an address for its tunnel. _ensure_host_tunnel_addr(ipv4_pools, ipip_pools) else: # No IPIP pools, clean up any old address. _remove_host_tunnel_addr()
def main(): ip = os.getenv("IP") ip = ip or None if ip and not netaddr.valid_ipv4(ip): print "IP environment (%s) is not a valid IPv4 address." % ip sys.exit(1) ip6 = os.getenv("IP6") ip6 = ip6 or None if ip6 and not netaddr.valid_ipv6(ip6): print "IP6 environment (%s) is not a valid IPv6 address." % ip6 sys.exit(1) as_num = os.getenv("AS") as_num = as_num or None if as_num and not validate_asn(as_num): print "AS environment (%s) is not a AS number." % as_num sys.exit(1) # Check etcd connectivity first. if not check_etcd_version(): sys.exit(1) # Get IP address of host, if none was specified if not ip: ips = get_host_ips(exclude=["^docker.*", "^cbr.*", "virbr.*", "lxcbr.*", "veth.*", "cali.*", "tunl.*"]) try: ip = ips.pop() except IndexError: print "Couldn't autodetect a management IP address. Please " \ "provide an IP address by rerunning the container with the" \ " IP environment variable set." sys.exit(1) else: print "No IP provided. Using detected IP: %s" % ip warn_if_hostname_conflict(ip) # Verify that IPs are not already in use by another host. error_if_bgp_ip_conflict(ip, ip6) # Verify that the chosen IP exists on the current host warn_if_unknown_ip(ip, ip6) # Set up etcd ipv4_pools = client.get_ip_pools(4) ipv6_pools = client.get_ip_pools(6) # Create default pools if required if not ipv4_pools: client.add_ip_pool(4, DEFAULT_IPV4_POOL) if not ipv6_pools and ipv6_enabled(): client.add_ip_pool(6, DEFAULT_IPV6_POOL) client.ensure_global_config() client.create_host(hostname, ip, ip6, as_num) # If IPIP is enabled, the host requires an IP address for its tunnel # device, which is in an IPIP pool. Without this, a host can't originate # traffic to a pool address because the response traffic would not be # routed via the tunnel (likely being dropped by RPF checks in the fabric). ipv4_pools = client.get_ip_pools(4) ipip_pools = [p for p in ipv4_pools if p.ipip] if ipip_pools: # IPIP is enabled, make sure the host has an address for its tunnel. _ensure_host_tunnel_addr(ipv4_pools, ipip_pools) else: # No IPIP pools, clean up any old address. _remove_host_tunnel_addr()
def main(): ip = os.getenv("IP") ip = ip or None if ip and not netaddr.valid_ipv4(ip): print "IP environment (%s) is not a valid IPv4 address." % ip sys.exit(1) ip6 = os.getenv("IP6") ip6 = ip6 or None if ip6 and not netaddr.valid_ipv6(ip6): print "IP6 environment (%s) is not a valid IPv6 address." % ip6 sys.exit(1) as_num = os.getenv("AS") as_num = as_num or None if as_num and not validate_asn(as_num): print "AS environment (%s) is not a AS number." % as_num sys.exit(1) # Get IP address of host, if none was specified if not ip: ips = get_host_ips(exclude=[ "^docker.*", "^cbr.*", "virbr.*", "lxcbr.*", "veth.*", "cali.*", "tunl.*", "flannel.*" ]) try: ip = str(ips.pop()) except IndexError: print "Couldn't autodetect a management IP address. Please " \ "provide an IP address by rerunning the container with the" \ " IP environment variable set." sys.exit(1) else: print "No IP provided. Using detected IP: %s" % ip # Write a startup environment file containing the IP address that may have # just been detected. # This is required because the confd templates expect to be able to fill in # some templates by fetching them from the environment. with open('startup.env', 'w') as f: f.write("IP=%s\n" % ip) f.write("HOSTNAME=%s\n" % hostname) warn_if_hostname_conflict(ip) # Verify that IPs are not already in use by another host. error_if_bgp_ip_conflict(ip, ip6) # Verify that the chosen IP exists on the current host warn_if_unknown_ip(ip, ip6) if os.getenv("NO_DEFAULT_POOLS", "").lower() != "true": # Set up etcd ipv4_pools = client.get_ip_pools(4) ipv6_pools = client.get_ip_pools(6) # Create default pools if required if not ipv4_pools: client.add_ip_pool(4, DEFAULT_IPV4_POOL) # If the OS has not been built with IPv6 then the /proc config for IPv6 # will not be present. if not ipv6_pools and os.path.exists('/proc/sys/net/ipv6'): client.add_ip_pool(6, DEFAULT_IPV6_POOL) client.ensure_global_config() client.create_host(hostname, ip, ip6, as_num) # If IPIP is enabled, the host requires an IP address for its tunnel # device, which is in an IPIP pool. Without this, a host can't originate # traffic to a pool address because the response traffic would not be # routed via the tunnel (likely being dropped by RPF checks in the fabric). ipv4_pools = client.get_ip_pools(4) ipip_pools = [p for p in ipv4_pools if p.ipip] if ipip_pools: # IPIP is enabled, make sure the host has an address for its tunnel. _ensure_host_tunnel_addr(ipv4_pools, ipip_pools) else: # No IPIP pools, clean up any old address. _remove_host_tunnel_addr()
def node_start(node_image, runtime, log_dir, ip, ip6, as_num, detach, libnetwork_image): """ 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. :return: None. """ # 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 # We will always want to setup IP forwarding _setup_ip_forwarding() # Print warnings for any known system issues before continuing if runtime == 'docker' and not running_in_container(): using_docker = True else: using_docker = False (_, _, 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: sys.exit(1) # Ensure log directory exists if not os.path.exists(log_dir): os.makedirs(log_dir) # Get IP address of host, if none was specified if not ip: ips = get_host_ips(exclude=["^docker.*", "^cbr.*", "virbr.*", "lxcbr.*", "veth.*", "cali.*", "tunl.*"]) try: ip = ips.pop() except IndexError: print "Couldn't autodetect a management IP address. Please provide" \ " an IP by rerunning the command with the --ip=<IP_ADDRESS> flag." sys.exit(1) else: print "No IP provided. Using detected IP: %s" % ip # Verify that IPs are not already in use by another host. error_if_bgp_ip_conflict(ip, ip6) # Verify that the chosen IP exists on the current host warn_if_unknown_ip(ip, ip6) # Warn if this hostname conflicts with an existing host warn_if_hostname_conflict(ip) # Set up etcd ipv4_pools = client.get_ip_pools(4) ipv6_pools = client.get_ip_pools(6) # Create default pools if required if not ipv4_pools: client.add_ip_pool(4, DEFAULT_IPV4_POOL) if not ipv6_pools: client.add_ip_pool(6, DEFAULT_IPV6_POOL) client.ensure_global_config() client.create_host(hostname, ip, ip6, as_num) # If IPIP is enabled, the host requires an IP address for its tunnel # device, which is in an IPIP pool. Without this, a host can't originate # traffic to a pool address because the response traffic would not be # routed via the tunnel (likely being dropped by RPF checks in the fabric). ipv4_pools = client.get_ip_pools(4) ipip_pools = [p for p in ipv4_pools if p.ipip] if ipip_pools: # IPIP is enabled, make sure the host has an address for its tunnel. _ensure_host_tunnel_addr(ipv4_pools, ipip_pools) else: # No IPIP pools, clean up any old address. _remove_host_tunnel_addr() # The format of the authority string has already been validated. etcd_authority = os.getenv(ETCD_AUTHORITY_ENV, ETCD_AUTHORITY_DEFAULT) # 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, None) etcd_cert_file = os.getenv(ETCD_CERT_FILE_ENV, None) etcd_ca_cert_file = os.getenv(ETCD_CA_CERT_FILE_ENV, None) etcd_volumes = [] etcd_binds = {} etcd_envs = ["ETCD_AUTHORITY=%s" % etcd_authority, "ETCD_SCHEME=%s" % etcd_scheme] felix_envs = ["FELIX_ETCDADDR=%s" % etcd_authority, "FELIX_ETCDSCHEME=%s" % etcd_scheme] 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) felix_envs.append("FELIX_ETCDCAFILE=%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) felix_envs.append("FELIX_ETCDKEYFILE=%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) felix_envs.append("FELIX_ETCDCERTFILE=%s" % ETCD_CERT_NODE_FILE) if runtime == 'docker': _start_node_container_docker(ip, ip6, log_dir, node_image, detach, etcd_envs, felix_envs, etcd_volumes, etcd_binds) if libnetwork_image: _start_libnetwork_container(libnetwork_image, etcd_envs, etcd_volumes, etcd_binds) if runtime == 'rkt': _start_node_container_rkt(ip, ip6, node_image, etcd_envs, felix_envs, etcd_volumes, etcd_binds)
def test_get_host_ips_fail_check_output(self, m_check_output): '''Test get_host_ip failing to check output of ip addr''' m_check_output.side_effect = CalledProcessError( returncode=1, cmd=check_output(["ip", "-4", "addr"])) with self.assertRaises(SystemExit): addrs = get_host_ips(version=4)
def main(): # Check to see if etcd is available. If not, wait until it is before # continuing. This is to avoid etcd / node startup race conditions. print "Waiting for etcd connection..." while os.getenv("WAIT_FOR_DATASTORE", "false") == "true": try: # Just try accessing etcd to see if we can reach it or not. client.get_host_as(hostname) except DataStoreError: # Not connected to etcd yet, wait a bit. time.sleep(1) continue else: # Connected to etcd - break out of loop. print "Connected to etcd" break # Start node. ip = os.getenv("IP") ip = ip or None if ip and not netaddr.valid_ipv4(ip): print "IP environment (%s) is not a valid IPv4 address." % ip sys.exit(1) ip6 = os.getenv("IP6") ip6 = ip6 or None if ip6 and not netaddr.valid_ipv6(ip6): print "IP6 environment (%s) is not a valid IPv6 address." % ip6 sys.exit(1) as_num = os.getenv("AS") as_num = as_num or None if as_num and not validate_asn(as_num): print "AS environment (%s) is not a AS number." % as_num sys.exit(1) # Get IP address of host, if none was specified if not ip: ips = get_host_ips(exclude=["^docker.*", "^cbr.*", "virbr.*", "lxcbr.*", "veth.*", "cali.*", "tunl.*", "flannel.*"]) try: ip = str(ips.pop()) except IndexError: print "Couldn't autodetect a management IP address. Please " \ "provide an IP address by rerunning the container with the" \ " IP environment variable set." sys.exit(1) else: print "No IP provided. Using detected IP: %s" % ip # Write a startup environment file containing the IP address that may have # just been detected. # This is required because the confd templates expect to be able to fill in # some templates by fetching them from the environment. with open('startup.env', 'w') as f: f.write("IP=%s\n" % ip) f.write("HOSTNAME=%s\n" % hostname) warn_if_hostname_conflict(ip) # Verify that IPs are not already in use by another host. error_if_bgp_ip_conflict(ip, ip6) # Verify that the chosen IP exists on the current host warn_if_unknown_ip(ip, ip6) if os.getenv("NO_DEFAULT_POOLS", "").lower() != "true": # Set up etcd ipv4_pools = client.get_ip_pools(4) ipv6_pools = client.get_ip_pools(6) # Create default pools if required if not ipv4_pools: client.add_ip_pool(4, DEFAULT_IPV4_POOL) # If the OS has not been built with IPv6 then the /proc config for IPv6 # will not be present. if not ipv6_pools and os.path.exists('/proc/sys/net/ipv6'): client.add_ip_pool(6, DEFAULT_IPV6_POOL) client.ensure_global_config() client.create_host(hostname, ip, ip6, as_num) # If IPIP is enabled, the host requires an IP address for its tunnel # device, which is in an IPIP pool. Without this, a host can't originate # traffic to a pool address because the response traffic would not be # routed via the tunnel (likely being dropped by RPF checks in the fabric). ipv4_pools = client.get_ip_pools(4) ipip_pools = [p for p in ipv4_pools if p.ipip] if ipip_pools: # IPIP is enabled, make sure the host has an address for its tunnel. _ensure_host_tunnel_addr(ipv4_pools, ipip_pools) else: # No IPIP pools, clean up any old address. _remove_host_tunnel_addr()
def main(): # If we're running with the k8s backend, don't do any of this, # since it currently doesn't support BGP, Calico IPAM, and doesn't # require any of the etcd interactions below. if os.getenv("DATASTORE_TYPE", "") == "kubernetes": print "Using k8s backend" with open("startup.env", "w") as f: f.write("export DATASTORE_TYPE=kubernetes\n") f.write("export HOSTNAME=%s\n" % hostname) return # Check to see if etcd is available. If not, wait until it is before # continuing. This is to avoid etcd / node startup race conditions. print "Waiting for etcd connection..." while os.getenv("WAIT_FOR_DATASTORE", "false") == "true": try: # Just try accessing etcd to see if we can reach it or not. client.get_host_as(hostname) except DataStoreError: # Not connected to etcd yet, wait a bit. time.sleep(1) continue else: # Connected to etcd - break out of loop. print "Connected to etcd" break # Query the currently configured host IPs try: current_ip, current_ip6 = client.get_host_bgp_ips(hostname) except KeyError: current_ip, current_ip6 = None, None # Determine the IP addresses and AS Number to use ip = os.getenv("IP") or None if ip == "autodetect": # If explicitly requesting auto-detection, set the ip to None to force # auto-detection. We print below if we are auto-detecting the IP. ip = None elif ip: if not netaddr.valid_ipv4(ip): print "IP environment (%s) is not a valid IPv4 address." % ip sys.exit(1) print "Using IPv4 address from IP environment: %s" % ip elif current_ip: print "Using configured IPv4 address: %s" % current_ip ip = current_ip # Get IP address of host, if none was specified or previously configured. if not ip: ips = get_host_ips( exclude=["^docker.*", "^cbr.*", "dummy.*", "virbr.*", "lxcbr.*", "veth.*", "cali.*", "tunl.*", "flannel.*"] ) try: ip = str(ips.pop()) except IndexError: print "Couldn't autodetect a management IPv4 address. Please " "provide an IP address either by configuring one in the " "node resource, or by re-running the container with the " "IP environment variable set." sys.exit(1) else: print "Using auto-detected IPv4 address: %s" % ip ip6 = os.getenv("IP6") or None if ip6: if not netaddr.valid_ipv6(ip6): print "IP6 environment (%s) is not a valid IPv6 address." % ip6 sys.exit(1) print "Using IPv6 address from IP6 environment: %s" % ip6 elif current_ip6: print "Using configured IPv6 address: %s" % current_ip6 ip6 = current_ip6 else: print "No IPv6 address configured" as_num = os.getenv("AS") if as_num: if not validate_asn(as_num): print "AS environment (%s) is not a valid AS number." % as_num sys.exit(1) print "Using AS number from AS environment: %s" % as_num else: as_num = client.get_host_as(hostname) if as_num: print "Using configured AS number: %s" % as_num else: print "Using global AS number" # Write a startup environment file containing the IP address that may have # just been detected. # This is required because the confd templates expect to be able to fill in # some templates by fetching them from the environment. with open("startup.env", "w") as f: f.write("export IP=%s\n" % ip) f.write("export HOSTNAME=%s\n" % hostname) warn_if_hostname_conflict(ip) # Verify that IPs are not already in use by another host. error_if_bgp_ip_conflict(ip, ip6) # Verify that the chosen IP exists on the current host warn_if_unknown_ip(ip, ip6) if os.getenv("NO_DEFAULT_POOLS", "").lower() != "true": # Set up etcd ipv4_pools = client.get_ip_pools(4) ipv6_pools = client.get_ip_pools(6) # Create default pools if required if not ipv4_pools: client.add_ip_pool(4, DEFAULT_IPV4_POOL) # If the OS has not been built with IPv6 then the /proc config for IPv6 # will not be present. if not ipv6_pools and os.path.exists("/proc/sys/net/ipv6"): client.add_ip_pool(6, DEFAULT_IPV6_POOL) client.ensure_global_config() client.create_host(hostname, ip, ip6, as_num) # If IPIP is enabled, the host requires an IP address for its tunnel # device, which is in an IPIP pool. Without this, a host can't originate # traffic to a pool address because the response traffic would not be # routed via the tunnel (likely being dropped by RPF checks in the fabric). ipv4_pools = client.get_ip_pools(4) ipip_pools = [p for p in ipv4_pools if p.ipip] if ipip_pools: # IPIP is enabled, make sure the host has an address for its tunnel. _ensure_host_tunnel_addr(ipv4_pools, ipip_pools) else: # No IPIP pools, clean up any old address. _remove_host_tunnel_addr() # Tell the user what the name of the node is. print "Calico node name: ", hostname
def node_start(node_image, runtime, log_dir, ip, ip6, as_num, detach, kubernetes_version, rkt, libnetwork_image): """ 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 kubernetes_version: The version of the calico-kubernetes plugin to install, or None if the plugin should not be installed. :param rkt: True to install the rkt plugin, False otherwise. :param libnetwork_image: The name of the Calico libnetwork driver image to use. None, if not using libnetwork. :return: None. """ # Normally, Felix will load the modules it needs, but when running inside a # container it might not be able to so ensure the required modules are # loaded each time the node starts. # This is just a best error attempt, as the modules might be builtins. # We'll warn during the check_system() if the modules are unavailable. try: call(["modprobe", "-a"] + REQUIRED_MODULES) except OSError: pass # Print warnings for any known system issues before continuing using_docker = True if runtime == 'docker' else False (_, _, etcd_ok) = \ check_system(quit_if_error=False, libnetwork=libnetwork_image, check_docker=using_docker) if not etcd_ok: sys.exit(1) # We will always want to setup IP forwarding _setup_ip_forwarding() # Ensure log directory exists if not os.path.exists(log_dir): os.makedirs(log_dir) # Get IP address of host, if none was specified if not ip: ips = get_host_ips(exclude=["^docker.*", "^cbr.*"]) try: ip = ips.pop() except IndexError: print "Couldn't autodetect a management IP address. Please provide" \ " an IP by rerunning the command with the --ip=<IP_ADDRESS> flag." sys.exit(1) else: print "No IP provided. Using detected IP: %s" % ip # Verify that the chosen IP exists on the current host warn_if_unknown_ip(ip, ip6) # Warn if this hostname conflicts with an existing host warn_if_hostname_conflict(ip) # Install Kubernetes plugin if kubernetes_version: # Build a URL based on the provided Kubernetes_version. url = KUBERNETES_BINARY_URL % kubernetes_version try: # Attempt to install to the default Kubernetes directory install_plugin(KUBERNETES_PLUGIN_DIR, url) except OSError: # Use the backup directory install_plugin(KUBERNETES_PLUGIN_DIR_BACKUP, url) # Install rkt plugin if rkt: try: # Attempt to install to the default rkt directory install_plugin(RKT_PLUGIN_DIR, RKT_BINARY_URL) except OSError: # Use the backup directory install_plugin(RKT_PLUGIN_DIR_BACKUP, RKT_BINARY_URL) # Set up etcd ipv4_pools = client.get_ip_pools(4) ipv6_pools = client.get_ip_pools(6) # Create default pools if required if not ipv4_pools: client.add_ip_pool(4, DEFAULT_IPV4_POOL) if not ipv6_pools: client.add_ip_pool(6, DEFAULT_IPV6_POOL) client.ensure_global_config() client.create_host(hostname, ip, ip6, as_num) # Always try to convert the address(hostname) to an IP. This is a noop if # the address is already an IP address. Note that the format of the authority # string has already been validated. etcd_authority = os.getenv(ETCD_AUTHORITY_ENV, ETCD_AUTHORITY_DEFAULT) etcd_authority_address, etcd_authority_port = etcd_authority.split(':') etcd_authority = '%s:%s' % (socket.gethostbyname(etcd_authority_address), etcd_authority_port) if runtime == 'docker': _start_node_container(ip, ip6, etcd_authority, log_dir, node_image, detach) if libnetwork_image: _start_libnetwork_container(etcd_authority, libnetwork_image)
def node_start(node_image, log_dir, ip, ip6, as_num, detach, kubernetes): """ 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. :return: None. """ # Ensure log directory exists if not os.path.exists(log_dir): os.makedirs(log_dir) # Print warnings for any known system issues before continuing check_system(fix=False, quit_if_error=False) # Get IP address of host, if none was specified if not ip: ips = get_host_ips(exclude=["docker0"]) try: ip = ips.pop() except IndexError: print "Couldn't autodetect a management IP address. Please provide" \ " an IP by rerunning the command with the --ip=<IP_ADDRESS> flag." sys.exit(1) else: print "No IP provided. Using detected IP: %s" % ip # Verify that the chosen IP exists on the current host warn_if_unknown_ip(ip, ip6) # Warn if this hostname conflicts with an existing host warn_if_hostname_conflict(ip) # Install kubernetes plugin if kubernetes: try: # Attempt to install to the default kubernetes directory install_kubernetes(KUBERNETES_PLUGIN_DIR) except OSError: # Use the backup directory install_kubernetes(KUBERNETES_PLUGIN_DIR_BACKUP) # Set up etcd ipv4_pools = client.get_ip_pools(4) ipv6_pools = client.get_ip_pools(6) # Create default pools if required if not ipv4_pools: client.add_ip_pool(4, DEFAULT_IPV4_POOL) if not ipv6_pools: client.add_ip_pool(6, DEFAULT_IPV6_POOL) client.ensure_global_config() client.create_host(hostname, ip, ip6, as_num) try: docker_client.remove_container("calico-node", force=True) except docker.errors.APIError as err: if err.response.status_code != 404: raise etcd_authority = os.getenv(ETCD_AUTHORITY_ENV, ETCD_AUTHORITY_DEFAULT) etcd_authority_split = etcd_authority.split(':') if len(etcd_authority_split) != 2: print_paragraph("Invalid %s. Must take the form <address>:<port>. Value " \ "provided is '%s'" % (ETCD_AUTHORITY_ENV, etcd_authority)) sys.exit(1) etcd_authority_address = etcd_authority_split[0] etcd_authority_port = etcd_authority_split[1] # Always try to convert the address(hostname) to an IP. This is a noop if # the address is already an IP address. etcd_authority = '%s:%s' % (socket.gethostbyname(etcd_authority_address), etcd_authority_port) environment = [ "HOSTNAME=%s" % hostname, "IP=%s" % ip, "IP6=%s" % (ip6 or ""), "ETCD_AUTHORITY=%s" % etcd_authority, # etcd host:port "FELIX_ETCDADDR=%s" % etcd_authority, # etcd host:port ] binds = { "/proc": { "bind": "/proc_host", "ro": False }, log_dir: { "bind": "/var/log/calico", "ro": False }, "/run/docker/plugins": { "bind": "/usr/share/docker/plugins", "ro": False } } host_config = docker.utils.create_host_config( privileged=True, restart_policy={"Name": "Always"}, network_mode="host", binds=binds) _find_or_pull_node_image(node_image, docker_client) container = docker_client.create_container( node_image, name="calico-node", detach=True, environment=environment, host_config=host_config, volumes=["/proc_host", "/var/log/calico", "/usr/share/docker/plugins"]) cid = container["Id"] docker_client.start(container) print "Calico node is running with id: %s" % cid if not detach: _attach_and_stream(container)
def node_start(node_image, log_dir, ip, ip6, as_num, detach, kubernetes): """ 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. :return: None. """ # Print warnings for any known system issues before continuing check_system(fix=False, quit_if_error=False) # Ensure log directory exists if not os.path.exists(log_dir): os.makedirs(log_dir) # Get IP address of host, if none was specified if not ip: ips = get_host_ips(exclude=["docker0"]) try: ip = ips.pop() except IndexError: print "Couldn't autodetect a management IP address. Please provide" \ " an IP by rerunning the command with the --ip=<IP_ADDRESS> flag." sys.exit(1) else: print "No IP provided. Using detected IP: %s" % ip # Verify that the chosen IP exists on the current host warn_if_unknown_ip(ip, ip6) # Warn if this hostname conflicts with an existing host warn_if_hostname_conflict(ip) # Install kubernetes plugin if kubernetes: try: # Attempt to install to the default kubernetes directory install_kubernetes(KUBERNETES_PLUGIN_DIR) except OSError: # Use the backup directory install_kubernetes(KUBERNETES_PLUGIN_DIR_BACKUP) # Set up etcd ipv4_pools = client.get_ip_pools(4) ipv6_pools = client.get_ip_pools(6) # Create default pools if required if not ipv4_pools: client.add_ip_pool(4, DEFAULT_IPV4_POOL) if not ipv6_pools: client.add_ip_pool(6, DEFAULT_IPV6_POOL) client.ensure_global_config() client.create_host(hostname, ip, ip6, as_num) try: docker_client.remove_container("calico-node", force=True) except docker.errors.APIError as err: if err.response.status_code != 404: raise # Always try to convert the address(hostname) to an IP. This is a noop if # the address is already an IP address. Note that the format of the authority # string has already been validated. etcd_authority = os.getenv(ETCD_AUTHORITY_ENV, ETCD_AUTHORITY_DEFAULT) etcd_authority_address, etcd_authority_port = etcd_authority.split(':') etcd_authority = '%s:%s' % (socket.gethostbyname(etcd_authority_address), etcd_authority_port) environment = [ "HOSTNAME=%s" % hostname, "IP=%s" % ip, "IP6=%s" % (ip6 or ""), "ETCD_AUTHORITY=%s" % etcd_authority, # etcd host:port "FELIX_ETCDADDR=%s" % etcd_authority, # etcd host:port "POLICY_ONLY_CALICO=%s" % os.getenv(POLICY_ONLY_ENV, ""), ] binds = { "/proc": { "bind": "/proc_host", "ro": False }, log_dir: { "bind": "/var/log/calico", "ro": False }, "/run/docker/plugins": { "bind": "/usr/share/docker/plugins", "ro": False } } host_config = docker.utils.create_host_config( privileged=True, restart_policy={"Name": "Always"}, network_mode="host", binds=binds) _find_or_pull_node_image(node_image) container = docker_client.create_container( node_image, name="calico-node", detach=True, environment=environment, host_config=host_config, volumes=["/proc_host", "/var/log/calico", "/usr/share/docker/plugins"]) cid = container["Id"] docker_client.start(container) print "Calico node is running with id: %s" % cid if not detach: _attach_and_stream(container)
def node_start(node_image, runtime, log_dir, ip, ip6, as_num, detach, libnetwork_image): """ 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. :return: None. """ # 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 # We will always want to setup IP forwarding _setup_ip_forwarding() # Print warnings for any known system issues before continuing if runtime == 'docker' and not running_in_container(): using_docker = True else: using_docker = False (_, _, 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: sys.exit(1) # Ensure log directory exists if not os.path.exists(log_dir): os.makedirs(log_dir) # Get IP address of host, if none was specified if not ip: ips = get_host_ips(exclude=[ "^docker.*", "^cbr.*", "virbr.*", "lxcbr.*", "veth.*", "cali.*", "tunl.*" ]) try: ip = ips.pop() except IndexError: print "Couldn't autodetect a management IP address. Please provide" \ " an IP by rerunning the command with the --ip=<IP_ADDRESS> flag." sys.exit(1) else: print "No IP provided. Using detected IP: %s" % ip # Verify that IPs are not already in use by another host. error_if_bgp_ip_conflict(ip, ip6) # Verify that the chosen IP exists on the current host warn_if_unknown_ip(ip, ip6) # Warn if this hostname conflicts with an existing host warn_if_hostname_conflict(ip) # Set up etcd ipv4_pools = client.get_ip_pools(4) ipv6_pools = client.get_ip_pools(6) # Create default pools if required if not ipv4_pools: client.add_ip_pool(4, DEFAULT_IPV4_POOL) if not ipv6_pools: client.add_ip_pool(6, DEFAULT_IPV6_POOL) client.ensure_global_config() client.create_host(hostname, ip, ip6, as_num) # If IPIP is enabled, the host requires an IP address for its tunnel # device, which is in an IPIP pool. Without this, a host can't originate # traffic to a pool address because the response traffic would not be # routed via the tunnel (likely being dropped by RPF checks in the fabric). ipv4_pools = client.get_ip_pools(4) ipip_pools = [p for p in ipv4_pools if p.ipip] if ipip_pools: # IPIP is enabled, make sure the host has an address for its tunnel. _ensure_host_tunnel_addr(ipv4_pools, ipip_pools) else: # No IPIP pools, clean up any old address. _remove_host_tunnel_addr() # The format of the authority string has already been validated. etcd_authority = os.getenv(ETCD_AUTHORITY_ENV, ETCD_AUTHORITY_DEFAULT) # 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, None) etcd_cert_file = os.getenv(ETCD_CERT_FILE_ENV, None) etcd_ca_cert_file = os.getenv(ETCD_CA_CERT_FILE_ENV, None) etcd_volumes = [] etcd_binds = {} etcd_envs = [ "ETCD_AUTHORITY=%s" % etcd_authority, "ETCD_SCHEME=%s" % etcd_scheme ] felix_envs = [ "FELIX_ETCDADDR=%s" % etcd_authority, "FELIX_ETCDSCHEME=%s" % etcd_scheme ] 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) felix_envs.append("FELIX_ETCDCAFILE=%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) felix_envs.append("FELIX_ETCDKEYFILE=%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) felix_envs.append("FELIX_ETCDCERTFILE=%s" % ETCD_CERT_NODE_FILE) if runtime == 'docker': _start_node_container_docker(ip, ip6, log_dir, node_image, detach, etcd_envs, felix_envs, etcd_volumes, etcd_binds) if libnetwork_image: _start_libnetwork_container(libnetwork_image, etcd_envs, etcd_volumes, etcd_binds) if runtime == 'rkt': _start_node_container_rkt(ip, ip6, node_image, etcd_envs, felix_envs, etcd_volumes, etcd_binds)
def test_get_host_ips_fail_check_output(self, m_check_output): '''Test get_host_ip failing to check output of ip addr''' m_check_output.side_effect = CalledProcessError(returncode=1, cmd=check_output(["ip", "-4", "addr"])) with self.assertRaises(SystemExit): addrs = get_host_ips(version=4)
def node_start(node_image, log_dir, ip, ip6, as_num, detach, kubernetes, rkt, libnetwork_image): """ 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 kubernetes: True to install the kubernetes plugin, False otherwise. :param rkt: True to install the rkt plugin, False otherwise. :param libnetwork_image: The name of the Calico libnetwork driver image to use. None, if not using libnetwork. :return: None. """ # Print warnings for any known system issues before continuing check_system(fix=False, quit_if_error=False) # Ensure log directory exists if not os.path.exists(log_dir): os.makedirs(log_dir) # Get IP address of host, if none was specified if not ip: ips = get_host_ips(exclude=["^docker.*", "^cbr.*"]) try: ip = ips.pop() except IndexError: print "Couldn't autodetect a management IP address. Please provide" \ " an IP by rerunning the command with the --ip=<IP_ADDRESS> flag." sys.exit(1) else: print "No IP provided. Using detected IP: %s" % ip # Verify that the chosen IP exists on the current host warn_if_unknown_ip(ip, ip6) # Warn if this hostname conflicts with an existing host warn_if_hostname_conflict(ip) # Install kubernetes plugin if kubernetes: try: # Attempt to install to the default kubernetes directory install_plugin(KUBERNETES_PLUGIN_DIR, KUBERNETES_BINARY_URL) except OSError: # Use the backup directory install_plugin(KUBERNETES_PLUGIN_DIR_BACKUP, KUBERNETES_BINARY_URL) # Install rkt plugin if rkt: try: # Attempt to install to the default rkt directory install_plugin(RKT_PLUGIN_DIR, RKT_BINARY_URL) except OSError: # Use the backup directory install_plugin(RKT_PLUGIN_DIR_BACKUP, RKT_BINARY_URL) # Set up etcd ipv4_pools = client.get_ip_pools(4) ipv6_pools = client.get_ip_pools(6) # Create default pools if required if not ipv4_pools: client.add_ip_pool(4, DEFAULT_IPV4_POOL) if not ipv6_pools: client.add_ip_pool(6, DEFAULT_IPV6_POOL) client.ensure_global_config() client.create_host(hostname, ip, ip6, as_num) # Always try to convert the address(hostname) to an IP. This is a noop if # the address is already an IP address. Note that the format of the authority # string has already been validated. etcd_authority = os.getenv(ETCD_AUTHORITY_ENV, ETCD_AUTHORITY_DEFAULT) etcd_authority_address, etcd_authority_port = etcd_authority.split(':') etcd_authority = '%s:%s' % (socket.gethostbyname(etcd_authority_address), etcd_authority_port) _start_node_container(ip, ip6, etcd_authority, log_dir, node_image, detach) if libnetwork_image: _start_libnetwork_container(etcd_authority, libnetwork_image)