def test_configure(self): servers = [ factory.make_ipv4_address(), factory.make_ipv6_address(), factory.make_hostname(), ] peers = [ factory.make_ipv4_address(), factory.make_ipv6_address(), factory.make_hostname(), ] offset = randrange(0, 5) config.configure(servers, peers, offset) ntp_conf_path = get_data_path("etc", config._ntp_conf_name) ntp_maas_conf_path = get_data_path("etc", config._ntp_maas_conf_name) ntp_conf = read_configuration(ntp_conf_path) self.assertThat(extract_servers_and_pools(ntp_conf), Equals([])) self.assertThat(extract_included_files(ntp_conf), Equals([ntp_maas_conf_path])) ntp_maas_conf = read_configuration(ntp_maas_conf_path) self.assertThat(extract_servers_and_pools(ntp_maas_conf), Equals(servers)) self.assertThat(extract_peers(ntp_maas_conf), Equals(peers)) self.assertThat(extract_tos_options(ntp_maas_conf), Equals([str(offset + 8), "orphan"]))
def test_get_a_mapping_returns_ipv4_mapping(self): ttl = random.randint(10, 300) ns_ttl = random.randint(10, 300) ipv4_mapping = { factory.make_name('host'): HostnameIPMapping(None, ttl, [factory.make_ipv4_address()]), factory.make_name('host'): HostnameIPMapping(None, ttl, [factory.make_ipv4_address()]), } ipv6_mapping = { factory.make_name('host'): HostnameIPMapping(None, ttl, [factory.make_ipv6_address()]), factory.make_name('host'): HostnameIPMapping(None, ttl, [factory.make_ipv6_address()]), } combined_mapping = { hostname: value for hostname, value in chain(ipv4_mapping.items(), ipv6_mapping.items()) } expected = [(n, info.ttl, ip) for n, info in ipv4_mapping.items() for ip in info.ips] expect = [(n, t, ip) for n, t, ip in expected] actual = DNSForwardZoneConfig.get_A_mapping(combined_mapping, ns_ttl) self.assertItemsEqual(expect, actual)
def test_get_aaaa_mapping_returns_ipv6_mapping(self): ttl = random.randint(10, 300) ns_ttl = random.randint(10, 300) ipv4_mapping = { factory.make_name("host"): HostnameIPMapping(None, ttl, {factory.make_ipv4_address()}), factory.make_name("host"): HostnameIPMapping(None, ttl, {factory.make_ipv4_address()}), } ipv6_mapping = { factory.make_name("host"): HostnameIPMapping(None, ttl, {factory.make_ipv6_address()}), factory.make_name("host"): HostnameIPMapping(None, ttl, {factory.make_ipv6_address()}), } combined_mapping = { hostname: value for hostname, value in chain(ipv4_mapping.items(), ipv6_mapping.items()) } self.assertItemsEqual( [(n, info.ttl, ip) for n, info in ipv6_mapping.items() for ip in info.ips], DNSForwardZoneConfig.get_AAAA_mapping(combined_mapping, ns_ttl), )
def test_get_render_file_with_ipv6_hosts(self): # Some versions of Twisted have the scope and flow info in the remote # address tuple. See https://twistedmatrix.com/trac/ticket/6826 (the # address is captured by tftp.protocol.TFTP.dataReceived). return self._test_get_render_file( local=(factory.make_ipv6_address(), factory.pick_port(), random.randint(1, 1000), random.randint(1, 1000)), remote=(factory.make_ipv6_address(), factory.pick_port(), random.randint(1, 1000), random.randint(1, 1000)), )
def make_servers_and_peers(self): return ( frozenset({ factory.make_ipv4_address(), factory.make_ipv6_address(), factory.make_hostname(), }), frozenset( {factory.make_ipv4_address(), factory.make_ipv6_address()}), )
def test_get_machine_default_gateway_ip_returns_ipv6(self): gw_address = factory.make_ipv6_address() ipv6_address = factory.make_ipv6_address() iface_name = factory.make_name("eth") self.patch(netifaces, "gateways").return_value = { "default": {netifaces.AF_INET6: (gw_address, iface_name)} } self.patch(netifaces, "ifaddresses").return_value = { netifaces.AF_INET6: [{"addr": ipv6_address}] } self.assertEqual(ipv6_address, get_machine_default_gateway_ip())
def test_requires_brackets_on_ipv6_address(self): name = "[%s]" % factory.make_ipv6_address() url = factory.make_simple_http_url(netloc=name, port=factory.pick_bool()) self.assertEqual(url, self.validator.to_python(url), "url: %s" % url) # rejects bare ipv6 address name = "%s" % factory.make_ipv6_address() url = factory.make_simple_http_url(netloc=name) with ExpectedException(formencode.Invalid, 'That is not a valid URL'): self.assertEqual(url, self.validator.to_python(url), "url: %s" % url)
def test_get_default_gateway_ip_returns_ipv6(self): gw_address = factory.make_ipv6_address() ipv6_address = factory.make_ipv6_address() iface_name = factory.make_name('eth') self.patch(netifaces, 'gateways').return_value = { 'default': { netifaces.AF_INET6: (gw_address, iface_name) } } self.patch(netifaces, 'ifaddresses').return_value = { netifaces.AF_INET6: [{'addr': ipv6_address}] } self.assertEqual(ipv6_address, snappy.get_default_gateway_ip())
def test__parses_URL_with_IPv6_address(self): ip = factory.make_ipv6_address().encode("ascii") path = factory.make_name("path").encode("ascii") uri = get_patched_URI().fromBytes(b"http://[%s]/%s" % (ip, path)) self.expectThat(uri.host, Equals(b"%s" % ip)) self.expectThat(uri.path, Equals(b"/%s" % path)) self.expectThat(uri.port, Equals(80))
def test__preserves_port_with_IPv6(self): ip = factory.make_ipv6_address() port = factory.pick_port() self.assertEqual( "https://[%s]:%s/" % (ip, port), compose_URL("https://:%s/" % port, ip), )
def test_set_maas_url_rejects_bare_ipv6_addresses(self): config = ClusterConfiguration({}) example_url = factory.make_simple_http_url( netloc=factory.make_ipv6_address() ) with ExpectedException(formencode.api.Invalid): config.maas_url = example_url
def test_ip_addresses_are_passed_through(self): address4 = factory.make_ipv4_address() address6 = factory.make_ipv6_address() self.assertThat( config._get_addresses(address4, address6), Equals(([address4], [address6])), )
def test_ignores_generate_directives_for_v6_dynamic_ranges(self): patch_dns_config_path(self) domain = factory.make_string() network = factory.make_ipv4_network() ipv4_hostname = factory.make_name("host") ipv4_ip = factory.pick_ip_in_network(network) ipv6_hostname = factory.make_name("host") ipv6_ip = factory.make_ipv6_address() ipv6_network = factory.make_ipv6_network() dynamic_range = IPRange(ipv6_network.first, ipv6_network.last) ttl = random.randint(10, 300) mapping = { ipv4_hostname: HostnameIPMapping(None, ttl, {ipv4_ip}), ipv6_hostname: HostnameIPMapping(None, ttl, {ipv6_ip}), } dns_zone_config = DNSForwardZoneConfig( domain, serial=random.randint(1, 100), mapping=mapping, default_ttl=ttl, dynamic_ranges=[dynamic_range], ) get_generate_directives = self.patch(dns_zone_config, "get_GENERATE_directives") dns_zone_config.write_config() self.assertThat(get_generate_directives, MockNotCalled())
def test_set_maas_url_accepts_ipv6_addresses_with_brackets(self): config = RegionConfiguration({}) example_url = factory.make_simple_http_url(netloc="[%s]" % factory.make_ipv6_address()) config.maas_url = example_url self.assertEqual(example_url, config.maas_url) self.assertEqual({"maas_url": example_url}, config.store)
def test__escapes_IPv6_zone_index(self): ip = factory.make_ipv6_address() zone = self.make_network_interface() hostname = '%s%%%s' % (ip, zone) path = self.make_path() self.assertEqual('http://[%s%%25%s]/%s' % (ip, zone, path), compose_URL('http:///%s' % path, hostname))
def test__does_not_escape_bracketed_IPv6_zone_index(self): ip = factory.make_ipv6_address() zone = self.make_network_interface() path = self.make_path() hostname = '[%s%%25%s]' % (ip, zone) self.assertEqual('http://%s/%s' % (hostname, path), compose_URL('http:///%s' % path, hostname))
def test_tftp_service_does_not_bind_to_link_local_addresses(self): # Initial set of interfaces to bind to. ipv4_test_net_3 = IPNetwork("203.0.113.0/24") # RFC 5737 normal_addresses = { factory.pick_ip_in_network(ipv4_test_net_3), factory.make_ipv6_address(), } link_local_addresses = { factory.pick_ip_in_network(IPV4_LINK_LOCAL), factory.pick_ip_in_network(IPV6_LINK_LOCAL), } self.patch( tftp_module, "get_all_interface_addresses", lambda: normal_addresses | link_local_addresses, ) tftp_service = TFTPService( resource_root=self.make_dir(), client_service=Mock(), port=factory.pick_port(), ) tftp_service.updateServers() # Only the "normal" addresses have been used. self.assertEqual( normal_addresses, {server.name for server in tftp_service.getServers()}, )
def test_tftp_service(self): # A TFTP service is configured and added to the top-level service. interfaces = [factory.make_ipv4_address(), factory.make_ipv6_address()] self.patch( tftp_module, "get_all_interface_addresses", lambda: interfaces ) example_root = self.make_dir() example_client_service = Mock() example_port = factory.pick_port() tftp_service = TFTPService( resource_root=example_root, client_service=example_client_service, port=example_port, ) tftp_service.updateServers() # The "tftp" service is a multi-service containing UDP servers for # each interface defined by get_all_interface_addresses(). self.assertIsInstance(tftp_service, MultiService) # There's also a TimerService that updates the servers every 45s. self.assertThat( tftp_service.refresher, MatchesStructure.byEquality( step=45, parent=tftp_service, name="refresher", call=(tftp_service.updateServers, (), {}), ), ) expected_backend = MatchesAll( IsInstance(TFTPBackend), AfterPreprocessing( lambda backend: backend.base.path, Equals(example_root) ), AfterPreprocessing( lambda backend: backend.client_service, Equals(example_client_service), ), ) expected_protocol = MatchesAll( IsInstance(TFTP), AfterPreprocessing( lambda protocol: protocol.backend, expected_backend ), ) expected_server = MatchesAll( IsInstance(internet.UDPServer), AfterPreprocessing(lambda service: len(service.args), Equals(2)), AfterPreprocessing( lambda service: service.args[0], Equals(example_port) # port ), AfterPreprocessing( lambda service: service.args[1], expected_protocol # protocol ), ) self.assertThat(tftp_service.getServers(), AllMatch(expected_server)) # Only the interface used for each service differs. self.assertItemsEqual( [svc.kwargs for svc in tftp_service.getServers()], [{"interface": interface} for interface in interfaces], )
def test__composes_bootloader_section_v6(self): ip = factory.make_ipv6_address() output = config.compose_conditional_bootloader(True, ip) for name, method in BootMethodRegistry: if name == "uefi": self.assertThat(output, Contains("else")) self.assertThat(output, Contains(method.bootloader_path)) elif method.arch_octet is not None: if isinstance(method.arch_octet, list): self.assertThat(output, ContainsAll(method.arch_octet)) else: self.assertThat(output, Contains(method.arch_octet)) self.assertThat(output, Contains(method.bootloader_path)) else: # No DHCP configuration is rendered for boot methods that have # no `arch_octet`, with the solitary exception of PXE. pass if method.path_prefix_http or method.http_url: self.assertThat(output, Contains("http://[%s]:5248/" % ip)) if method.path_prefix_force: self.assertThat(output, Contains( "option dhcp6.oro = concat(option dhcp6.oro,00d2);")) if method.http_url: self.assertThat(output, Contains( 'option dhcp6.vendor-class 0 10 "HTTPClient";'))
def test__parses_URL_with_IPv6_address(self): ip = factory.make_ipv6_address().encode('ascii') path = factory.make_name('path').encode('ascii') uri = get_patched_URI().fromBytes(b'http://[%s]/%s' % (ip, path)) self.expectThat(uri.host, Equals(b'%s' % ip)) self.expectThat(uri.path, Equals(b'/%s' % path)) self.expectThat(uri.port, Equals(80))
def test_get_default_gateway_ip_returns_ipv4_over_ipv6(self): gw4_address = factory.make_ipv4_address() gw6_address = factory.make_ipv6_address() ipv4_address = factory.make_ipv4_address() ipv6_address = factory.make_ipv6_address() iface = factory.make_name("eth") self.patch(netifaces, "gateways").return_value = { "default": { netifaces.AF_INET: (gw4_address, iface), netifaces.AF_INET6: (gw6_address, iface), } } self.patch(netifaces, "ifaddresses").return_value = { netifaces.AF_INET: [{"addr": ipv4_address}], netifaces.AF_INET6: [{"addr": ipv6_address}], } self.assertEqual(ipv4_address, snappy.get_default_gateway_ip())
def test_get_default_gateway_ip_returns_ipv6(self): ipv6_address = factory.make_ipv6_address() self.patch(netifaces, 'gateways').return_value = { 'default': { netifaces.AF_INET6: (ipv6_address, factory.make_name('eth')), } } self.assertEqual(ipv6_address, snappy.get_default_gateway_ip())
def test__inserts_bracketed_IPv6_unchanged(self): ip = factory.make_ipv6_address() hostname = "[%s]" % ip path = self.make_path() self.assertEqual( "http://%s/%s" % (hostname, path), compose_URL("http:///%s" % path, hostname), )
def test_ipv6_text(self): ipv4 = factory.make_ipv6_address() self.expectThat(get_ip_based_hostname(ipv4), Equals(ipv4.replace(":", "-"))) self.expectThat( get_ip_based_hostname("2001:67c:1562::15"), Equals("2001-67c-1562--15"), )
def test_get_ip_address_returns_consistent_result_from_address_set(self): addresses = [factory.make_ipv6_address() for _ in range(5)] expected_address = sorted(addresses)[0] for _ in range(5): random.shuffle(addresses) self.patch( address, "get_all_addresses_for_interface").return_value = addresses self.assertEqual(expected_address, address.get_ip_address(b"lo"))
def test_compose_rootfs_over_http_ipv6(self): params = make_kernel_parameters(fs_host=factory.make_ipv6_address()) self.assertThat( compose_kernel_command_line(params), ContainsAll([ "ro", "root=squash:http://[%s]:5248/images/%s/%s/%s/%s/%s/squashfs" % (params.fs_host, params.osystem, params.arch, params.subarch, params.release, params.label) ]))
def make_sources(): hosts = [factory.make_hostname().lower() for _ in range(2)] hosts.append(factory.make_ipv4_address()) hosts.append("[%s]" % factory.make_ipv6_address()) urls = [ "http://%s:%s/images-stream/streams/v1/index.json" % (host, randint(1, 1000)) for host in hosts ] sources = [{"url": url, "selections": []} for url in urls] return sources, hosts
def test_get_ip_address_prefers_v4_addresses_to_v6(self): addresses = [factory.make_ipv6_address() for _ in range(3)] # We add a deliberately low v6 address to show that the v4 # address is always preferred. ipv6_address = "::1" ipv4_address = factory.make_ipv4_address() addresses.append(ipv6_address) addresses.append(ipv4_address) self.patch(address, "get_all_addresses_for_interface").return_value = addresses self.assertEqual(ipv4_address, address.get_ip_address(b"lo"))
def test_enlist_compose_kernel_command_line_inc_purpose_opts6(self): # The result of compose_kernel_command_line includes the purpose # options for a non "commissioning" node. params = self.make_kernel_parameters( purpose="enlist", fs_host=factory.make_ipv6_address()) cmdline = compose_kernel_command_line(params) self.assertThat( cmdline, ContainsAll([ "root=squash:http://", "overlayroot=tmpfs", "ip=off", "ip6=dhcp" ]))
def test_renders_the_given_peers(self): peers = [ factory.make_ipv4_address(), factory.make_ipv6_address(), factory.make_hostname(), ] ntp_maas_conf = config._render_ntp_maas_conf([], peers, 0) self.assertThat(ntp_maas_conf, StartsWith('# MAAS NTP configuration.\n')) observed_peers = extract_peers_full(ntp_maas_conf) self.assertThat(observed_peers, Equals([("peer", peer, "") for peer in peers]))