def _makeTFTPService(self, tftp_root, tftp_port, rpc_service): """Create the dynamic TFTP service.""" from provisioningserver.rackdservices.tftp import TFTPService tftp_service = TFTPService(resource_root=tftp_root, port=tftp_port, client_service=rpc_service) tftp_service.setName("tftp") # *** EXPERIMENTAL *** # https://code.launchpad.net/~allenap/maas/tftp-offload/+merge/312146 # If the TFTP port has been set to zero, use the experimental offload # service. Otherwise stick to the normal in-process TFTP service. if tftp_port == 0: from provisioningserver.path import get_data_path from provisioningserver.rackdservices import tftp_offload from twisted.internet.endpoints import UNIXServerEndpoint tftp_offload_socket = get_data_path( "/var/lib/maas/tftp-offload.sock") tftp_offload_endpoint = UNIXServerEndpoint(reactor, tftp_offload_socket, wantPID=False) tftp_offload_service = tftp_offload.TFTPOffloadService( reactor, tftp_offload_endpoint, tftp_service.backend) tftp_offload_service.setName("tftp-offload") return tftp_offload_service # *** /EXPERIMENTAL *** return tftp_service
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_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(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], )