def test_check_lease_changes_returns_tuple_if_lease_changed(self): ip = factory.getRandomIPAddress() leases = {ip: factory.getRandomMACAddress()} self.set_lease_state(datetime.utcnow() - timedelta(seconds=10), leases.copy()) leases[ip] = factory.getRandomMACAddress() leases_file = self.fake_leases_file(leases) self.assertEqual((get_write_time(leases_file), leases), check_lease_changes())
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_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_check_lease_changes_returns_tuple_if_lease_changed(self): ip = factory.getRandomIPAddress() leases = {ip: factory.getRandomMACAddress()} self.set_lease_state( datetime.utcnow() - timedelta(seconds=10), leases.copy()) leases[ip] = factory.getRandomMACAddress() leases_file = self.fake_leases_file(leases) self.assertEqual( (get_write_time(leases_file), leases), check_lease_changes())
def test_parse_leases_takes_latest_lease_for_address(self): params = { 'ip': factory.getRandomIPAddress(), 'old_owner': factory.getRandomMACAddress(), 'new_owner': factory.getRandomMACAddress(), } leases = parse_leases(dedent("""\ lease %(ip)s { hardware ethernet %(old_owner)s; } lease %(ip)s { hardware ethernet %(new_owner)s; } """ % params)) self.assertEqual({params['ip']: params['new_owner']}, 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_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_parse_leases_takes_latest_lease_for_address(self): params = { 'ip': factory.getRandomIPAddress(), 'old_owner': factory.getRandomMACAddress(), 'new_owner': factory.getRandomMACAddress(), } leases = parse_leases( dedent("""\ lease %(ip)s { hardware ethernet %(old_owner)s; } lease %(ip)s { hardware ethernet %(new_owner)s; } """ % params)) self.assertEqual({params['ip']: params['new_owner']}, leases)
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_get_config_reader_returns_rendered_params(self): # get_config_reader() takes a dict() of parameters and returns an # `IReader` of a PXE configuration, rendered by `render_pxe_config`. backend = TFTPBackend(self.make_dir(), b"http://example.com/") # Fake configuration parameters, as discovered from the file path. fake_params = {"mac": factory.getRandomMACAddress(b"-")} # Fake kernel configuration parameters, as returned from the API call. fake_kernel_params = make_kernel_parameters() # Stub get_page to return the fake API configuration parameters. fake_get_page_result = json.dumps(fake_kernel_params._asdict()) get_page_patch = self.patch(backend, "get_page") get_page_patch.return_value = succeed(fake_get_page_result) # Stub render_pxe_config to return the render parameters. fake_render_result = factory.make_name("render") render_patch = self.patch(backend, "render_pxe_config") render_patch.return_value = fake_render_result # Get the rendered configuration, which will actually be a JSON dump # of the render-time parameters. reader = yield backend.get_config_reader(fake_params) self.addCleanup(reader.finish) self.assertIsInstance(reader, BytesReader) output = reader.read(10000) # The kernel parameters were fetched using `backend.get_page`. backend.get_page.assert_called_once() # The result has been rendered by `backend.render_pxe_config`. self.assertEqual(fake_render_result.encode("utf-8"), output) backend.render_pxe_config.assert_called_once_with( kernel_params=fake_kernel_params, **fake_params)
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_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_get_config_reader_returns_rendered_params(self): # get_config_reader() takes a dict() of parameters and returns an # `IReader` of a PXE configuration, rendered by `render_pxe_config`. backend = TFTPBackend(self.make_dir(), b"http://example.com/") # Fake configuration parameters, as discovered from the file path. fake_params = {"mac": factory.getRandomMACAddress("-")} # Fake kernel configuration parameters, as returned from the API call. fake_kernel_params = make_kernel_parameters() # Stub get_page to return the fake API configuration parameters. fake_get_page_result = json.dumps(fake_kernel_params._asdict()) get_page_patch = self.patch(backend, "get_page") get_page_patch.return_value = succeed(fake_get_page_result) # Stub render_pxe_config to return the render parameters. fake_render_result = factory.make_name("render") render_patch = self.patch(backend, "render_pxe_config") render_patch.return_value = fake_render_result # Get the rendered configuration, which will actually be a JSON dump # of the render-time parameters. reader = yield backend.get_config_reader(fake_params) self.addCleanup(reader.finish) self.assertIsInstance(reader, BytesReader) output = reader.read(10000) # The kernel parameters were fetched using `backend.get_page`. backend.get_page.assert_called_once() # The result has been rendered by `backend.render_pxe_config`. self.assertEqual(fake_render_result.encode("utf-8"), output) backend.render_pxe_config.assert_called_once_with( kernel_params=fake_kernel_params, **fake_params)
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_init_sets_transaction_ID(self): transaction_id = make_transaction_ID() self.patch(detect_module, 'make_transaction_ID').return_value = (transaction_id) discover = DHCPDiscoverPacket(factory.getRandomMACAddress()) self.assertEqual(transaction_id, discover.transaction_ID)
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_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_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_add_new_dhcp_host_map_failure(self): # Check that task failures are caught. Nothing much happens in # the Task code right now though. mac = factory.getRandomMACAddress() ip = factory.getRandomIPAddress() server_address = factory.getRandomString() key = factory.getRandomString() self.patch(Omshell, '_run', FakeMethod(result=(0, "this_will_fail"))) self.assertRaises(CalledProcessError, add_new_dhcp_host_map.delay, {mac: ip}, server_address, key)
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 get_example_path_and_components(): """Return a plausible path and its components. The path is intended to match `re_config_file`, and the components are the expected groups from a match. """ components = {"mac": factory.getRandomMACAddress(b"-"), "arch": None, "subarch": None} config_path = compose_config_path(components["mac"]) return config_path, components
def test_add_new_dhcp_host_map_failure(self): # Check that task failures are caught. Nothing much happens in # the Task code right now though. mac = factory.getRandomMACAddress() ip = factory.getRandomIPAddress() server_address = factory.getRandomString() key = factory.getRandomString() self.patch(Omshell, '_run', FakeMethod(result=(0, "this_will_fail"))) self.assertRaises( CalledProcessError, add_new_dhcp_host_map.delay, {mac: ip}, server_address, key)
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_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 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_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_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_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_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 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_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_is_lease_and_is_host_recognize_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( (False, True), (is_lease(parsed_host), is_host(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_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_is_lease_and_is_host_recognize_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((False, True), (is_lease(parsed_host), is_host(parsed_host)))
def get_example_path_and_components(): """Return a plausible path and its components. The path is intended to match `re_config_file`, and the components are the expected groups from a match. """ components = { "mac": factory.getRandomMACAddress("-"), "arch": None, "subarch": None } config_path = compose_config_path(components["mac"]) return config_path, components
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_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)
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_ignores_comments(self): params = { 'ip': factory.getRandomIPAddress(), 'mac': factory.getRandomMACAddress(), } leases = parse_leases(dedent("""\ # Top comment (ignored). lease %(ip)s { # End-of-line comment (ignored). # Comment in lease block (ignored). hardware ethernet %(mac)s; # EOL comment in lease (ignored). } # Comment right after closing brace (ignored). # End comment (ignored). """ % params)) self.assertEqual({params['ip']: params['mac']}, leases)
def test_parse_leases_recognizes_host_deleted_statement_as_rubout(self): params = { 'ip': factory.getRandomIPAddress(), 'mac': factory.getRandomMACAddress(), } leases = parse_leases(dedent("""\ host %(ip)s { dynamic; hardware ethernet %(mac)s; fixed-address %(ip)s; deleted; } """ % params)) self.assertEqual({}, 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__build(self): mac = factory.getRandomMACAddress() discover = DHCPDiscoverPacket(mac) discover._build() expected = (b'\x01\x01\x06\x00' + discover.transaction_ID + b'\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + discover.packed_mac + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00' * 67 + b'\x00' * 125 + b'\x63\x82\x53\x63\x35\x01\x01\x3d\x06' + discover.packed_mac + b'\x37\x03\x03\x01\x06\xff') self.assertEqual(expected, discover.packet)
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)
def test_parse_leases_recognizes_host_deleted_statement_as_rubout(self): params = { 'ip': factory.getRandomIPAddress(), 'mac': factory.getRandomMACAddress(), } leases = parse_leases( dedent("""\ host %(ip)s { dynamic; hardware ethernet %(mac)s; fixed-address %(ip)s; deleted; } """ % params)) self.assertEqual({}, leases)
def test_parse_leases_ignores_comments(self): params = { 'ip': factory.getRandomIPAddress(), 'mac': factory.getRandomMACAddress(), } leases = parse_leases( dedent("""\ # Top comment (ignored). lease %(ip)s { # End-of-line comment (ignored). # Comment in lease block (ignored). hardware ethernet %(mac)s; # EOL comment in lease (ignored). } # Comment right after closing brace (ignored). # End comment (ignored). """ % 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_get_generator_url(self): # get_generator_url() merges the parameters obtained from the request # file path (arch, subarch, name) into the configured generator URL. mac = factory.getRandomMACAddress("-") dummy = factory.make_name("dummy").encode("ascii") backend_url = b"http://example.com/?" + urlencode({b"dummy": dummy}) backend = TFTPBackend(self.make_dir(), backend_url) # params is an example of the parameters obtained from a request. params = {"mac": mac} generator_url = urlparse(backend.get_generator_url(params)) self.assertEqual("example.com", generator_url.hostname) query = parse_qsl(generator_url.query) query_expected = [ ("dummy", dummy), ("mac", mac), ] self.assertItemsEqual(query_expected, query)
def test_get_generator_url(self): # get_generator_url() merges the parameters obtained from the request # file path (arch, subarch, name) into the configured generator URL. mac = factory.getRandomMACAddress(b"-") dummy = factory.make_name("dummy").encode("ascii") backend_url = b"http://example.com/?" + urlencode({b"dummy": dummy}) backend = TFTPBackend(self.make_dir(), backend_url) # params is an example of the parameters obtained from a request. params = {"mac": mac} generator_url = urlparse(backend.get_generator_url(params)) self.assertEqual("example.com", generator_url.hostname) query = parse_qsl(generator_url.query) query_expected = [ ("dummy", dummy), ("mac", mac), ] self.assertItemsEqual(query_expected, query)
def test_parse_leases_file_parses_leases(self): params = { 'ip': factory.getRandomIPAddress(), 'mac': factory.getRandomMACAddress(), } leases_file = self.write_leases_file("""\ 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; binding state free; hardware ethernet %(mac)s; } """ % params) self.assertEqual( (get_write_time(leases_file), {params['ip']: params['mac']}), parse_leases_file())
def test_create_raises_when_omshell_fails(self): # If the call to omshell doesn't result in output containing the # magic string 'hardware-type' it means the set of commands # failed. server_address = factory.getRandomString() shared_key = factory.getRandomString() ip_address = factory.getRandomIPAddress() mac_address = factory.getRandomMACAddress() shell = Omshell(server_address, shared_key) # Fake a call that results in a failure with random output. random_output = factory.getRandomString() recorder = FakeMethod(result=(0, random_output)) shell._run = recorder exc = self.assertRaises( CalledProcessError, shell.create, ip_address, mac_address) self.assertEqual(random_output, exc.output)
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(b"-") 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)