def test_determine_cluster_interfaces_returns_interface_names(self): eth0_addr = factory.getRandomIPAddress() wlan0_addr = factory.getRandomIPAddress() self.patch_fake_interfaces_list([("eth0", eth0_addr), ("wlan0", wlan0_addr)]) self.assertEqual([("eth0", eth0_addr), ("wlan0", wlan0_addr)], determine_cluster_interfaces(self.knowledge))
def test_get_reader_config_file(self): # For paths matching re_config_file, TFTPBackend.get_reader() returns # a Deferred that will yield a BytesReader. cluster_uuid = factory.getRandomUUID() self.patch(tftp_module, 'get_cluster_uuid').return_value = (cluster_uuid) mac = factory.getRandomMACAddress("-") config_path = compose_config_path(mac) backend = TFTPBackend(self.make_dir(), b"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.getRandomIPAddress(), factory.getRandomPort()), "remote": (factory.getRandomIPAddress(), factory.getRandomPort()), } @partial(self.patch, backend, "get_config_reader") def get_config_reader(params): params_json = json.dumps(params) 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) # The addresses provided by python-tx-tftp in the call context are # passed over the wire as address:port strings. expected_params = { "mac": mac, "local": call_context["local"][0], # address only. "remote": call_context["remote"][0], # address only. "cluster_uuid": cluster_uuid, } observed_params = json.loads(output) self.assertEqual(expected_params, observed_params)
def test_tftp_service(self): # A TFTP service is configured and added to the top-level service. interfaces = [ factory.getRandomIPAddress(), factory.getRandomIPAddress(), ] self.patch( plugin, "get_all_interface_addresses", lambda: interfaces) config = { "tftp": { "generator": "http://candlemass/solitude", "root": self.tempdir, "port": factory.getRandomPort(), }, } options = Options() options["config-file"] = self.write_config(config) service_maker = ProvisioningServiceMaker("Harry", "Hill") service = service_maker.makeService(options) tftp_services = service.getServiceNamed("tftp") # The "tftp" service is a multi-service containing UDP servers for # each interface defined by get_all_interface_addresses(). self.assertIsInstance(tftp_services, MultiService) services = [ tftp_services.getServiceNamed(interface) for interface in interfaces ] expected_backend = MatchesAll( IsInstance(TFTPBackend), AfterPreprocessing( lambda backend: backend.base.path, Equals(config["tftp"]["root"])), AfterPreprocessing( lambda backend: backend.generator_url.geturl(), Equals(config["tftp"]["generator"]))) expected_protocol = MatchesAll( IsInstance(TFTP), AfterPreprocessing( lambda protocol: protocol.backend, expected_backend)) expected_service = MatchesAll( IsInstance(UDPServer), AfterPreprocessing( lambda service: len(service.args), Equals(2)), AfterPreprocessing( lambda service: service.args[0], # port Equals(config["tftp"]["port"])), AfterPreprocessing( lambda service: service.args[1], # protocol expected_protocol)) self.assertThat(services, AllMatch(expected_service)) # Only the interface used for each service differs. self.assertEqual( [svc.kwargs for svc in services], [{"interface": interface} for interface in interfaces])
def test_periodic_probe_task_updates_region_with_no_detected_server(self): eth0_addr = factory.getRandomIPAddress() wlan0_addr = factory.getRandomIPAddress() self.patch_fake_interfaces_list([("eth0", eth0_addr), ("wlan0", wlan0_addr)]) self.patch(detect_module, 'probe_dhcp').return_value = set() mocked_update = self.patch(detect_module, 'update_region_controller') periodic_probe_task() calls = [ mock.call(self.knowledge, 'eth0', None), mock.call(self.knowledge, 'wlan0', None), ] mocked_update.assert_has_calls(calls, any_order=True)
def test_tftp_service(self): # A TFTP service is configured and added to the top-level service. interfaces = [ factory.getRandomIPAddress(), factory.getRandomIPAddress(), ] self.patch(plugin, "get_all_interface_addresses", lambda: interfaces) config = { "tftp": { "generator": "http://candlemass/solitude", "root": self.tempdir, "port": factory.getRandomPort(), }, } options = Options() options["config-file"] = self.write_config(config) service_maker = ProvisioningServiceMaker("Harry", "Hill") service = service_maker.makeService(options) tftp_services = service.getServiceNamed("tftp") # The "tftp" service is a multi-service containing UDP servers for # each interface defined by get_all_interface_addresses(). self.assertIsInstance(tftp_services, MultiService) services = [ tftp_services.getServiceNamed(interface) for interface in interfaces ] expected_backend = MatchesAll( IsInstance(TFTPBackend), AfterPreprocessing(lambda backend: backend.base.path, Equals(config["tftp"]["root"])), AfterPreprocessing(lambda backend: backend.generator_url.geturl(), Equals(config["tftp"]["generator"]))) expected_protocol = MatchesAll( IsInstance(TFTP), AfterPreprocessing(lambda protocol: protocol.backend, expected_backend)) expected_service = MatchesAll( IsInstance(UDPServer), AfterPreprocessing(lambda service: len(service.args), Equals(2)), AfterPreprocessing( lambda service: service.args[0], # port Equals(config["tftp"]["port"])), AfterPreprocessing( lambda service: service.args[1], # protocol expected_protocol)) self.assertThat(services, AllMatch(expected_service)) # Only the interface used for each service differs. self.assertEqual([svc.kwargs for svc in services], [{ "interface": interface } for interface in interfaces])
def test_parse_leases_ignores_incomplete_lease_at_end(self): params = { 'ip': factory.getRandomIPAddress(), 'mac': factory.getRandomMACAddress(), 'incomplete_ip': factory.getRandomIPAddress(), } leases = parse_leases(dedent("""\ lease %(ip)s { hardware ethernet %(mac)s; } lease %(incomplete_ip)s { starts 5 2010/01/01 00:00:05; """ % params)) self.assertEqual({params['ip']: params['mac']}, leases)
def test_parse_leases_ignores_incomplete_lease_at_end(self): params = { 'ip': factory.getRandomIPAddress(), 'mac': factory.getRandomMACAddress(), 'incomplete_ip': factory.getRandomIPAddress(), } leases = parse_leases( dedent("""\ lease %(ip)s { hardware ethernet %(mac)s; } lease %(incomplete_ip)s { starts 5 2010/01/01 00:00:05; """ % params)) self.assertEqual({params['ip']: params['mac']}, leases)
def test_remove_calls_omshell_correctly(self): server_address = factory.getRandomString() shared_key = factory.getRandomString() ip_address = factory.getRandomIPAddress() shell = Omshell(server_address, shared_key) # Instead of calling a real omshell, we'll just record the # parameters passed to Popen. recorder = FakeMethod(result=(0, "thing1\nthing2\nobj: <null>")) shell._run = recorder shell.remove(ip_address) expected_script = dedent("""\ server {server} key omapi_key {key} connect new host set name = "{ip}" open remove """) expected_script = expected_script.format(server=server_address, key=shared_key, ip=ip_address) # Check that the 'stdin' arg contains the correct set of # commands. self.assertEqual([(expected_script, )], recorder.extract_args())
def test_create_succeeds_when_host_map_already_exists(self): # To omshell, creating the same host map twice is an error. But # Omshell.create swallows the error and makes it look like # success. params = { 'ip': factory.getRandomIPAddress(), 'mac': factory.getRandomMACAddress(), 'hostname': factory.make_name('hostname') } shell = Omshell(factory.make_name('server'), factory.make_name('key')) # This is the kind of error output we get if a host map has # already been created. error_output = dedent("""\ obj: host ip-address = %(ip)s hardware-address = %(mac)s name = "%(hostname)s" > can't open object: I/O error obj: host ip-address = %(ip)s hardware-address = %(mac)s name = "%(hostname)s" """) % params shell._run = Mock(return_value=(0, error_output)) shell.create(params['ip'], params['mac']) # The test is that we get here without error. pass
def test_parse_leases_parses_lease(self): params = { 'ip': factory.getRandomIPAddress(), 'mac': factory.getRandomMACAddress(), } leases = parse_leases( dedent("""\ lease %(ip)s { starts 5 2010/01/01 00:00:01; ends never; tstp 6 2010/01/02 05:00:00; tsfp 6 2010/01/02 05:00:00; atsfp 6 2010/01/02 05:00:00; cltt 1 2010/01/02 05:00:00; binding state free; next binding state free; rewind binding state free; hardware ethernet %(mac)s; uid "\001\000\234\002\242\2020"; set vendorclass = "PXEClient:Arch:00000:UNDI:002001"; client-hostname foo; abandoned; option agent.circuit-id thing; option agent.remote-id thing; ddns-text foo; ddns-fwd-name foo; ddns-client-fqdn foo; ddns-rev-name foo; vendor-class-identifier foo; bootp; reserved; } """ % params)) self.assertEqual({params['ip']: params['mac']}, leases)
def test_gather_hosts_ignores_rubbed_out_entries(self): ip = factory.getRandomIPAddress() hosts = [ self.fake_parsed_host(ip=ip), self.fake_parsed_rubout(ip=ip), ] self.assertEqual({}, gather_hosts(hosts))
def test_create_calls_omshell_correctly(self): server_address = factory.getRandomString() shared_key = factory.getRandomString() ip_address = factory.getRandomIPAddress() mac_address = factory.getRandomMACAddress() shell = Omshell(server_address, shared_key) # Instead of calling a real omshell, we'll just record the # parameters passed to Popen. recorder = FakeMethod(result=(0, "hardware-type")) shell._run = recorder shell.create(ip_address, mac_address) expected_script = dedent("""\ server {server} key omapi_key {key} connect new host set ip-address = {ip} set hardware-address = {mac} set hardware-type = 1 set name = "{ip}" create """) expected_script = expected_script.format(server=server_address, key=shared_key, ip=ip_address, mac=mac_address) # Check that the 'stdin' arg contains the correct set of # commands. self.assertEqual([1, (expected_script, )], [recorder.call_count, recorder.extract_args()[0]])
def test_probe_interface_returns_empty_set_when_nothing_detected(self): eth0_addr = factory.getRandomIPAddress() self.patch_fake_interfaces_list([("eth0", eth0_addr)]) self.patch(detect_module, 'probe_dhcp').return_value = set() interfaces = determine_cluster_interfaces(self.knowledge) results = probe_interface(*interfaces[0]) self.assertEqual(set(), results)
def test_parse_leases_parses_host_rubout(self): leases = parse_leases(dedent("""\ host %s { deleted; } """ % factory.getRandomIPAddress())) self.assertEqual({}, leases)
def test_parse_leases_parses_lease(self): params = { 'ip': factory.getRandomIPAddress(), 'mac': factory.getRandomMACAddress(), } leases = parse_leases(dedent("""\ lease %(ip)s { starts 5 2010/01/01 00:00:01; ends never; tstp 6 2010/01/02 05:00:00; tsfp 6 2010/01/02 05:00:00; atsfp 6 2010/01/02 05:00:00; cltt 1 2010/01/02 05:00:00; binding state free; next binding state free; rewind binding state free; hardware ethernet %(mac)s; uid "\001\000\234\002\242\2020"; set vendorclass = "PXEClient:Arch:00000:UNDI:002001"; client-hostname foo; abandoned; option agent.circuit-id thing; option agent.remote-id thing; ddns-text foo; ddns-fwd-name foo; ddns-client-fqdn foo; ddns-rev-name foo; vendor-class-identifier foo; bootp; reserved; } """ % params)) self.assertEqual({params['ip']: params['mac']}, leases)
def test_getRandomIPAddress(self): ip_address = factory.getRandomIPAddress() self.assertIsInstance(ip_address, unicode) octets = ip_address.split('.') self.assertEqual(4, len(octets)) for octet in octets: self.assertTrue(0 <= int(octet) <= 255)
def test_remove_calls_omshell_correctly(self): server_address = factory.getRandomString() shared_key = factory.getRandomString() ip_address = factory.getRandomIPAddress() shell = Omshell(server_address, shared_key) # Instead of calling a real omshell, we'll just record the # parameters passed to Popen. recorder = FakeMethod(result=(0, "thing1\nthing2\nobj: <null>")) shell._run = recorder shell.remove(ip_address) expected_args = (dedent("""\ server {server} key omapi_key {key} connect new host set name = "{ip}" open remove """).format( server=server_address, key=shared_key, ip=ip_address),) # Check that the 'stdin' arg contains the correct set of # commands. self.assertEqual([expected_args], recorder.extract_args())
def test_create_calls_omshell_correctly(self): server_address = factory.getRandomString() shared_key = factory.getRandomString() ip_address = factory.getRandomIPAddress() mac_address = factory.getRandomMACAddress() shell = Omshell(server_address, shared_key) # Instead of calling a real omshell, we'll just record the # parameters passed to Popen. recorder = FakeMethod(result=(0, "hardware-type")) shell._run = recorder shell.create(ip_address, mac_address) expected_args = (dedent("""\ server {server} key omapi_key {key} connect new host set ip-address = {ip} set hardware-address = {mac} set hardware-type = 1 set name = "{ip}" create """).format( server=server_address, key=shared_key, ip=ip_address, mac=mac_address),) # Check that the 'stdin' arg contains the correct set of # commands. self.assertEqual( [1, expected_args], [recorder.call_count, recorder.extract_args()[0]])
def test_check_lease_changes_returns_tuple_if_lease_added(self): leases = factory.make_random_leases() self.set_lease_state(datetime.utcnow() - timedelta(seconds=10), leases.copy()) leases[factory.getRandomIPAddress()] = factory.getRandomMACAddress() leases_file = self.fake_leases_file(leases) self.assertEqual((get_write_time(leases_file), leases), check_lease_changes())
def test_probe_interface_returns_populated_set(self): # Test that the detected DHCP server is returned. eth0_addr = factory.getRandomIPAddress() self.patch_fake_interfaces_list([("eth0", eth0_addr)]) self.patch(detect_module, 'probe_dhcp').return_value = {'10.2.2.2'} interfaces = determine_cluster_interfaces(self.knowledge) results = probe_interface(*interfaces[0]) self.assertEqual({'10.2.2.2'}, results)
def test_parse_leases_parses_host_rubout(self): leases = parse_leases( dedent("""\ host %s { deleted; } """ % factory.getRandomIPAddress())) self.assertEqual({}, leases)
def patch_offer_packet(self): """Patch a mock `DHCPOfferPacket`.""" transaction_id = factory.getRandomBytes(4) packet = mock.MagicMock() packet.transaction_ID = transaction_id packet.dhcp_server_ID = factory.getRandomIPAddress() self.patch(detect_module, 'DHCPOfferPacket').return_value = packet return packet
def test_get_host_mac_returns_None_for_rubout(self): ip = factory.getRandomIPAddress() [parsed_host] = lease_parser.searchString(dedent("""\ host %s { deleted; } """ % ip)) self.assertIsNone(get_host_mac(parsed_host))
def test_remove_dhcp_host_map_failure(self): # Check that task failures are caught. Nothing much happens in # the Task code right now though. ip = factory.getRandomIPAddress() server_address = factory.getRandomString() key = factory.getRandomString() self.patch(Omshell, '_run', FakeMethod(result=(0, "this_will_fail"))) self.assertRaises(CalledProcessError, remove_dhcp_host_map.delay, ip, server_address, key)
def test_reverse_config_file_is_world_readable(self): self.patch(DNSReverseZoneConfig, 'target_dir', self.make_dir()) dns_zone_config = DNSReverseZoneConfig( factory.getRandomString(), serial=random.randint(1, 100), dns_ip=factory.getRandomIPAddress(), network=factory.getRandomNetwork()) dns_zone_config.write_config() filepath = FilePath(dns_zone_config.target_path) self.assertTrue(filepath.getPermissions().other.read)
def test_gather_hosts_follows_reassigned_host(self): ip = factory.getRandomIPAddress() new_owner = factory.getRandomMACAddress() hosts = [ self.fake_parsed_host(ip=ip), self.fake_parsed_rubout(ip=ip), self.fake_parsed_host(ip=ip, mac=new_owner), ] self.assertEqual({ip: new_owner}, gather_hosts(hosts))
def test_get_host_mac_returns_None_for_rubout(self): ip = factory.getRandomIPAddress() [parsed_host] = lease_parser.searchString( dedent("""\ host %s { deleted; } """ % ip)) self.assertIsNone(get_host_mac(parsed_host))
def test_update_region_controller_sets_detected_dhcp(self): mocked_put = self.patch(MAASClient, 'put') mocked_put.return_value = MockResponse() detected_server = factory.getRandomIPAddress() update_region_controller(self.knowledge, "eth0", detected_server) mocked_put.assert_called_once_with( 'api/1.0/nodegroups/%s/interfaces/eth0/' % self.knowledge['nodegroup_uuid'], foreign_dhcp_ip=detected_server)
def test_combine_entries_accepts_expired_lease_followed_by_host(self): ip = factory.getRandomIPAddress() mac = factory.getRandomMACAddress() earlier = '1 2001/01/01 00:00:00' entries = [ self.fake_parsed_lease(ip=ip, ends=earlier), self.fake_parsed_host(ip=ip, mac=mac), ] self.assertEqual({ip: mac}, combine_entries(entries))
def test_combine_entries_accepts_reassigned_host(self): ip = factory.getRandomIPAddress() mac = factory.getRandomMACAddress() entries = [ self.fake_parsed_host(ip=ip), self.fake_parsed_rubout(ip=ip), self.fake_parsed_host(ip=ip, mac=mac), ] self.assertEqual({ip: mac}, combine_entries(entries))
def test_check_lease_changes_returns_tuple_if_lease_added(self): leases = factory.make_random_leases() self.set_lease_state( datetime.utcnow() - timedelta(seconds=10), leases.copy()) leases[factory.getRandomIPAddress()] = factory.getRandomMACAddress() leases_file = self.fake_leases_file(leases) self.assertEqual( (get_write_time(leases_file), leases), check_lease_changes())
def test_gather_leases_ignores_ordering(self): earlier = '1 2001/01/01 00:00:00' ip = factory.getRandomIPAddress() old_owner = factory.getRandomMACAddress() new_owner = factory.getRandomMACAddress() leases = [ self.fake_parsed_lease(ip=ip, mac=new_owner), self.fake_parsed_lease(ip=ip, mac=old_owner, ends=earlier), ] self.assertEqual({ip: new_owner}, gather_leases(leases))
def test_set_up_options_conf_writes_configuration(self): dns_conf_dir = self.make_dir() self.patch(conf, 'DNS_CONFIG_DIR', dns_conf_dir) fake_dns = factory.getRandomIPAddress() set_up_options_conf(upstream_dns=fake_dns) target_file = os.path.join( dns_conf_dir, MAAS_NAMED_CONF_OPTIONS_INSIDE_NAME) self.assertThat( target_file, FileContains(matcher=Contains(fake_dns)))
def test_combine_entries_ignores_rubout_followed_by_expired_lease(self): ip = factory.getRandomIPAddress() mac = factory.getRandomMACAddress() earlier = '1 2001/01/01 00:00:00' entries = [ self.fake_parsed_host(ip=ip), self.fake_parsed_rubout(ip=ip), self.fake_parsed_lease(ip=ip, mac=mac, ends=earlier), ] self.assertEqual({}, combine_entries(entries))
def test_probe_interface_filters_interface_own_ip(self): # Test that the interface shows the detected DHCP server except # if it is the same IP as the interface's. eth0_addr = factory.getRandomIPAddress() self.patch_fake_interfaces_list([("eth0", eth0_addr)]) detected_dhcp = eth0_addr self.patch(detect_module, 'probe_dhcp').return_value = {detected_dhcp} interfaces = determine_cluster_interfaces(self.knowledge) results = probe_interface(*interfaces[0]) self.assertEqual(set(), results)
def test_gather_leases_combines_expired_and_current_leases(self): earlier = '1 2001/01/01 00:00:00' ip = factory.getRandomIPAddress() old_owner = factory.getRandomMACAddress() new_owner = factory.getRandomMACAddress() leases = [ self.fake_parsed_lease(ip=ip, mac=old_owner, ends=earlier), self.fake_parsed_lease(ip=ip, mac=new_owner), ] self.assertEqual({ip: new_owner}, gather_leases(leases))
def test_remove_dhcp_host_map_failure(self): # Check that task failures are caught. Nothing much happens in # the Task code right now though. ip = factory.getRandomIPAddress() server_address = factory.getRandomString() key = factory.getRandomString() self.patch(Omshell, '_run', FakeMethod(result=(0, "this_will_fail"))) self.assertRaises( CalledProcessError, remove_dhcp_host_map.delay, ip, server_address, key)
def test_get_host_mac_returns_None_for_host(self): params = { 'ip': factory.getRandomIPAddress(), 'mac': factory.getRandomMACAddress(), } [parsed_host] = lease_parser.searchString(dedent("""\ host %(ip)s { hardware ethernet %(mac)s; } """ % params)) self.assertEqual(params['mac'], get_host_mac(parsed_host))
def fake_parsed_lease(self, ip=None, mac=None, ends=None, entry_type='lease'): """Fake a lease as produced by the parser.""" if ip is None: ip = factory.getRandomIPAddress() if mac is None: mac = factory.getRandomMACAddress() Hardware = namedtuple('Hardware', ['mac']) Lease = namedtuple( 'Lease', ['lease_or_host', 'ip', 'hardware', 'ends']) return Lease(entry_type, ip, Hardware(mac), ends)
def test_parse_leases_treats_missing_end_date_as_eternity(self): params = { 'ip': factory.getRandomIPAddress(), 'mac': factory.getRandomMACAddress(), } leases = parse_leases(dedent("""\ lease %(ip)s { hardware ethernet %(mac)s; } """ % params)) self.assertEqual({params['ip']: params['mac']}, leases)
def test_remove_dhcp_host_map(self): # We don't want to actually run omshell in the task, so we stub # out the wrapper class's _run method and record what it would # do. ip = factory.getRandomIPAddress() server_address = factory.getRandomString() key = factory.getRandomString() recorder = FakeMethod(result=(0, "obj: <null>")) self.patch(Omshell, '_run', recorder) remove_dhcp_host_map.delay(ip, server_address, key) self.assertRecordedStdin(recorder, ip, server_address, key)
def test_forward_zone_get_cname_mapping_returns_iterator(self): name = factory.getRandomString() network = IPNetwork('192.12.0.1/30') dns_ip = factory.getRandomIPInNetwork(network) dns_zone_config = DNSForwardZoneConfig( name, networks=[network], dns_ip=dns_ip, mapping={ factory.make_name('hostname'): factory.getRandomIPAddress()}) self.assertThat( dns_zone_config.get_cname_mapping(), MatchesAll( IsInstance(Iterable), Not(IsInstance(Sequence))))
def test_get_host_mac_returns_None_for_rubout_even_with_mac(self): params = { 'ip': factory.getRandomIPAddress(), 'mac': factory.getRandomMACAddress(), } [parsed_host] = lease_parser.searchString(dedent("""\ host %(ip)s { deleted; hardware ethernet %(mac)s; } """ % params)) self.assertIsNone(get_host_mac(parsed_host))
def test_host_declaration_is_like_an_unexpired_lease(self): params = { 'ip': factory.getRandomIPAddress(), 'mac': factory.getRandomMACAddress(), } leases = parse_leases(dedent("""\ host %(ip)s { hardware ethernet %(mac)s; fixed-address %(ip)s; } """ % params)) self.assertEqual({params['ip']: params['mac']}, leases)
def test_parse_leases_ignores_expired_leases(self): params = { 'ip': factory.getRandomIPAddress(), 'mac': factory.getRandomMACAddress(), } leases = parse_leases(dedent("""\ lease %(ip)s { hardware ethernet %(mac)s; ends 1 2001/01/01 00:00:00; } """ % params)) self.assertEqual({}, leases)
def test_add_new_dhcp_host_map(self): # We don't want to actually run omshell in the task, so we stub # out the wrapper class's _run method and record what it would # do. mac = factory.getRandomMACAddress() ip = factory.getRandomIPAddress() server_address = factory.getRandomString() key = factory.getRandomString() recorder = FakeMethod(result=(0, "hardware-type")) self.patch(Omshell, '_run', recorder) add_new_dhcp_host_map.delay({ip: mac}, server_address, key) self.assertRecordedStdin(recorder, ip, mac, server_address, key)
def test_parse_leases_parses_host(self): params = { 'ip': factory.getRandomIPAddress(), 'mac': factory.getRandomMACAddress(), } leases = parse_leases(dedent("""\ host %(ip)s { dynamic; hardware ethernet %(mac)s; fixed-address %(ip)s; } """ % params)) self.assertEqual({params['ip']: params['mac']}, leases)