def test_get_boot_method_render_substitutes_armhf_in_params(self): # get_config_reader() should substitute "arm" for "armhf" in the # arch field of the parameters (mapping from pxe to maas # namespace). config_path = b"pxelinux.cfg/default-arm" backend = TFTPBackend(self.make_dir(), "http://example.com/") # python-tx-tftp sets up call context so that backends can discover # more about the environment in which they're running. call_context = { "local": (factory.make_ipv4_address(), factory.pick_port()), "remote": (factory.make_ipv4_address(), factory.pick_port()), } @partial(self.patch, backend, "get_boot_method_reader") def get_boot_method_reader(boot_method, params): params_json = json.dumps(params).encode("ascii") params_json_reader = BytesReader(params_json) return succeed(params_json_reader) reader = yield context.call( call_context, backend.get_reader, config_path ) output = reader.read(10000).decode("ascii") observed_params = json.loads(output) # XXX: GavinPanella 2015-11-25 bug=1519804: get_by_pxealias() on # ArchitectureRegistry is not stable, so we permit either here. self.assertIn(observed_params["arch"], ["armhf", "arm64"])
def test_make_parsed_url_asserts_with_conflicting_port_numbers(self): with ExpectedException(AssertionError): netloc = "%s:%d" % (factory.make_hostname(), factory.pick_port()) factory.make_parsed_url(netloc=netloc, port=factory.pick_port()) with ExpectedException(AssertionError): netloc = "%s:%d" % (factory.make_hostname(), factory.pick_port()) factory.make_parsed_url(netloc=netloc, port=True)
def test_get_render_file_with_ipv4_hosts(self): return self._test_get_render_file( local=( factory.make_ipv4_address(), factory.pick_port()), remote=( factory.make_ipv4_address(), factory.pick_port()), )
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 test_get_remote_mac(self): remote_host = factory.make_ipv4_address() call_context = { "local": (factory.make_ipv4_address(), factory.pick_port()), "remote": (remote_host, factory.pick_port()), } mock_find = self.patch(boot, "find_mac_via_arp") yield context.call(call_context, get_remote_mac) self.assertThat(mock_find, MockCalledOnceWith(remote_host))
def test_set_and_get_tftp_port(self): config = ClusterConfiguration({}) example_port = factory.pick_port() config.tftp_port = example_port self.assertEqual(example_port, config.tftp_port) # It's also stored in the configuration database. self.assertEqual({"tftp_port": example_port}, config.store)
def test_getConfiguration_returns_configuration_object(self): port = factory.pick_port() is_region, is_rack = factory.pick_bool(), factory.pick_bool() rpc_service, protocol = yield prepareRegion( self, is_region=is_region, is_rack=is_rack, syslog_port=port ) forwarders = self.extract_regions(rpc_service) service, syslog = self.make_RackSyslog_ExternalService( rpc_service, reactor ) yield service.startService() self.addCleanup((yield service.stopService)) config = yield service._getConfiguration() observed = syslog._getConfiguration( config.controller_type, config.syslog_configuration, config.connections, ) self.assertThat(observed, IsInstance(external._SyslogConfiguration)) self.assertThat( observed, MatchesStructure.byEquality( port=port, forwarders=forwarders, is_region=is_region, is_rack=is_rack, ), )
def test_render_GET_500_server_error(self): path = factory.make_name("path") ip = factory.make_ip_address() request = DummyRequest([path.encode("utf-8")]) request.requestHeaders = Headers( { "X-Server-Addr": ["192.168.1.1"], "X-Server-Port": ["5248"], "X-Forwarded-For": [ip], "X-Forwarded-Port": ["%s" % factory.pick_port()], } ) self.patch(http.log, "info") mock_deferLater = self.patch(http, "deferLater") mock_deferLater.side_effect = always_succeed_with(None) exc = factory.make_exception("internal error") self.tftp.backend.get_reader.return_value = fail(exc) resource = http.HTTPBootResource() yield self.render_GET(resource, request) self.assertEquals(500, request.responseCode) self.assertEquals(str(exc).encode("utf-8"), b"".join(request.written))
def test__tryUpdate_updates_syslog_server(self): self.useFixture(MAASRootFixture()) port = factory.pick_port() rpc_service, _ = yield prepareRegion(self, syslog_port=port) forwarders = self.extract_regions(rpc_service) service, _ = self.make_RackSyslog_ExternalService(rpc_service, reactor) write_config = self.patch_autospec(external.syslog_config, "write_config") service_monitor = self.patch_autospec(external, "service_monitor") yield service.startService() self.addCleanup((yield service.stopService)) yield service._orig_tryUpdate() expected_forwards = [{ "name": name, "ip": ip } for name, ip in forwarders] self.assertThat( write_config, MockCalledOnceWith(False, forwarders=expected_forwards, port=port), ) self.assertThat(service_monitor.restartService, MockCalledOnceWith("syslog_rack")) # If the configuration has not changed then a second call to # `_tryUpdate` does not result in another call to `_configure`. yield service._orig_tryUpdate() self.assertThat( write_config, MockCalledOnceWith(False, forwarders=expected_forwards, port=port), ) self.assertThat(service_monitor.restartService, MockCalledOnceWith("syslog_rack"))
def test__preserves_port_with_hostname(self): hostname = factory.make_name("host") port = factory.pick_port() self.assertEqual( "https://%s:%s/" % (hostname, port), compose_URL("https://:%s/" % port, hostname), )
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_render_GET_logs_node_event_with_original_path_ip(self): path = factory.make_name('path') ip = factory.make_ip_address() request = DummyRequest([path.encode('utf-8')]) request.requestHeaders = Headers({ 'X-Server-Addr': ['192.168.1.1'], 'X-Server-Port': ['5248'], 'X-Forwarded-For': [ip], 'X-Forwarded-Port': ['%s' % factory.pick_port()], }) log_info = self.patch(http.log, 'info') mock_deferLater = self.patch(http, 'deferLater') mock_deferLater.side_effect = always_succeed_with(None) self.tftp.backend.get_reader.return_value = fail(AccessViolation()) resource = http.HTTPBootResource() yield self.render_GET(resource, request) self.assertThat( log_info, MockCalledOnceWith("{path} requested by {remoteHost}", path=path, remoteHost=ip)) self.assertThat( mock_deferLater, MockCalledOnceWith(ANY, 0, http.send_node_event_ip_address, event_type=EVENT_TYPES.NODE_HTTP_REQUEST, ip_address=ip, description=path))
def test_render_GET_produces_from_reader(self): path = factory.make_name('path') ip = factory.make_ip_address() request = DummyRequest([path.encode('utf-8')]) request.requestHeaders = Headers({ 'X-Server-Addr': ['192.168.1.1'], 'X-Server-Port': ['5248'], 'X-Forwarded-For': [ip], 'X-Forwarded-Port': ['%s' % factory.pick_port()], }) self.patch(http.log, 'info') mock_deferLater = self.patch(http, 'deferLater') mock_deferLater.side_effect = always_succeed_with(None) content = factory.make_string(size=100).encode('utf-8') reader = BytesReader(content) self.tftp.backend.get_reader.return_value = succeed(reader) resource = http.HTTPBootResource() yield self.render_GET(resource, request) self.assertEquals( [100], request.responseHeaders.getRawHeaders(b'Content-Length')) self.assertEquals(content, b''.join(request.written))
def test_render_GET_503_when_no_tftp_service(self): # Remove the fake 'tftp' service. self.tftp.disownServiceParent() self.tftp = None path = factory.make_name('path') ip = factory.make_ip_address() request = DummyRequest([path.encode('utf-8')]) request.requestHeaders = Headers({ 'X-Server-Addr': ['192.168.1.1'], 'X-Server-Port': ['5248'], 'X-Forwarded-For': [ip], 'X-Forwarded-Port': ['%s' % factory.pick_port()], }) self.patch(http.log, 'info') mock_deferLater = self.patch(http, 'deferLater') mock_deferLater.side_effect = always_succeed_with(None) resource = http.HTTPBootResource() yield self.render_GET(resource, request) self.assertEquals(503, request.responseCode) self.assertEquals(b'HTTP boot service not ready.', b''.join(request.written))
def test__ignores_protocol_starting_on_events(self): event = make_event( "%s starting on %d" % (factory.make_name("protocol"), factory.pick_port())) with TwistedLoggerFixture() as logger: observe_twisted_internet_unix(event) self.assertThat(logger.events, HasLength(0))
def test_make_parsed_url_accepts_explicit_port(self): port = factory.pick_port() url = factory.make_parsed_url(port=port) self.assertThat( url.netloc, EndsWith(':%d' % port), 'The generated URL does not contain' 'a port specification for port %d' % port)
def test_render_GET_503_when_no_tftp_service(self): # Remove the fake 'tftp' service. self.tftp.disownServiceParent() self.tftp = None path = factory.make_name("path") ip = factory.make_ip_address() request = DummyRequest([path.encode("utf-8")]) request.requestHeaders = Headers({ "X-Server-Addr": ["192.168.1.1"], "X-Server-Port": ["5248"], "X-Forwarded-For": [ip], "X-Forwarded-Port": ["%s" % factory.pick_port()], }) self.patch(http.log, "info") mock_deferLater = self.patch(http, "deferLater") mock_deferLater.side_effect = always_succeed_with(None) resource = http.HTTPBootResource() yield self.render_GET(resource, request) self.assertEqual(503, request.responseCode) self.assertEqual(b"HTTP boot service not ready.", b"".join(request.written))
def test__ignores_port_closed_events(self): event = make_event( "(%s Port %d Closed)" % (factory.make_name("port-name"), factory.pick_port())) with TwistedLoggerFixture() as logger: observe_twisted_internet_udp(event) self.assertThat(logger.events, HasLength(0))
def test_render_GET_produces_from_reader(self): path = factory.make_name("path") ip = factory.make_ip_address() request = DummyRequest([path.encode("utf-8")]) request.requestHeaders = Headers({ "X-Server-Addr": ["192.168.1.1"], "X-Server-Port": ["5248"], "X-Forwarded-For": [ip], "X-Forwarded-Port": ["%s" % factory.pick_port()], }) self.patch(http.log, "info") mock_deferLater = self.patch(http, "deferLater") mock_deferLater.side_effect = always_succeed_with(None) content = factory.make_string(size=100).encode("utf-8") reader = BytesReader(content) self.tftp.backend.get_reader.return_value = succeed(reader) resource = http.HTTPBootResource() yield self.render_GET(resource, request) self.assertEqual( [100], request.responseHeaders.getRawHeaders(b"Content-Length")) self.assertEqual(content, b"".join(request.written))
def test_tftp_service_rebinds_on_HUP(self): # Initial set of interfaces to bind to. interfaces = {"1.1.1.1", "2.2.2.2"} self.patch( tftp_module, "get_all_interface_addresses", lambda: interfaces ) tftp_service = TFTPService( resource_root=self.make_dir(), client_service=Mock(), port=factory.pick_port(), ) tftp_service.updateServers() # The child services of tftp_services are named after the # interface they bind to. self.assertEqual( interfaces, {server.name for server in tftp_service.getServers()} ) # Update the set of interfaces to bind to. interfaces.add("3.3.3.3") interfaces.remove("1.1.1.1") # Ask the TFTP service to update its set of servers. tftp_service.updateServers() # We're in the reactor thread but we want to move the reactor # forwards, hence we need to get all explicit about it. reactor.runUntilCurrent() # The interfaces now bound match the updated interfaces set. self.assertEqual( interfaces, {server.name for server in tftp_service.getServers()} )
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__parses_URL_with_hostname_and_port(self): hostname = factory.make_name("host").encode("ascii") port = factory.pick_port() path = factory.make_name("path").encode("ascii") uri = get_patched_URI().fromBytes(b"http://%s:%d/%s" % (hostname, port, path)) self.expectThat(uri.host, Equals(hostname)) self.expectThat(uri.path, Equals(b"/%s" % path)) self.expectThat(uri.port, Equals(port))
def test__parses_URL_with_hostname_and_port(self): hostname = factory.make_name('host').encode('ascii') port = factory.pick_port() path = factory.make_name('path').encode('ascii') uri = get_patched_URI().fromBytes(b'http://%s:%d/%s' % (hostname, port, path)) self.expectThat(uri.host, Equals(hostname)) self.expectThat(uri.path, Equals(b'/%s' % path)) self.expectThat(uri.port, Equals(port))
def test__parses_URL_with_IPv6_address_and_port(self): ip = factory.make_ipv6_address().encode('ascii') port = factory.pick_port() path = factory.make_name('path').encode('ascii') uri = get_patched_URI().fromBytes(b'http://[%s]:%d/%s' % (ip, port, path)) self.expectThat(uri.host, Equals(b'%s' % ip)) self.expectThat(uri.path, Equals(b'/%s' % path)) self.expectThat(uri.port, Equals(port))
def test_udp_and_tcp_both_use_different_port(self): port = factory.pick_port() config.write_config(False, port=port) matcher_one = Contains('input(type="imtcp" port="%d")' % port) matcher_two = Contains('input(type="imudp" port="%d")' % port) self.assertThat( "%s/%s" % (self.tmpdir, config.MAAS_SYSLOG_CONF_NAME), FileContains(matcher=MatchesAll(matcher_one, matcher_two)), )
def test__parses_URL_with_IPv4_address_and_port(self): ip = factory.make_ipv4_address().encode("ascii") port = factory.pick_port() path = factory.make_name("path").encode("ascii") uri = get_patched_URI().fromBytes(b"http://%s:%d/%s" % (ip, port, path)) self.expectThat(uri.host, Equals(ip)) self.expectThat(uri.path, Equals(b"/%s" % path)) self.expectThat(uri.port, Equals(port))
def make_parameters_context(self): return { "power_address": "".join( [ factory.make_name("power_address"), ":%s" % factory.pick_port(), ] ), "instance_name": factory.make_name("instance_name"), "password": factory.make_name("password"), }
def make_protocol(self, patch_authenticate=True, transport_uri=""): listener = FakePostgresListenerService() factory = WebSocketFactory(listener) self.patch(factory, "registerRPCEvents") self.patch(factory, "unregisterRPCEvents") factory.startFactory() self.addCleanup(factory.stopFactory) protocol = factory.buildProtocol(None) protocol.transport = MagicMock() protocol.transport.cookies = b"" protocol.transport.uri = transport_uri protocol.transport.host = random.choice([ maastesting_factory.make_ipv4_address() + ":%d" % maastesting_factory.pick_port(), "[" + maastesting_factory.make_ipv6_address() + "]:%d" % maastesting_factory.pick_port(), ]) protocol.request = HttpRequest() if patch_authenticate: self.patch(protocol, "authenticate") return protocol, factory
def test__set_and_get(self): config = RegionConfiguration({}) if isinstance(getattr(config, self.option), str): example_value = factory.make_name(self.option) else: example_value = factory.pick_port() # Argument values will most often be passed in from the command-line, # so convert to a string before use to reflect that usage. setattr(config, self.option, str(example_value)) self.assertEqual(example_value, getattr(config, self.option)) # It's also stored in the configuration database. self.assertEqual({self.option: example_value}, config.store)