def test__bad_config(self): omapi_key = factory.make_name("omapi_key") failover_peers = make_failover_peer_config() shared_network = make_shared_network() [shared_network] = fix_shared_networks_failover([shared_network], [failover_peers]) host = make_host() interface = make_interface() global_dhcp_snippets = make_global_dhcp_snippets() dhcpd_error = ( "Internet Systems Consortium DHCP Server 4.3.3\n" "Copyright 2004-2015 Internet Systems Consortium.\n" "All rights reserved.\n" "For info, please visit https://www.isc.org/software/dhcp/\n" "/tmp/maas-dhcpd-z5c7hfzt line 14: semicolon expected.\n" "ignore \n" "^\n" "Configuration file errors encountered -- exiting\n" "\n" "If you think you have received this message due to a bug rather\n" "than a configuration issue please read the section on submitting" "\n" "bugs on either our web page at www.isc.org or in the README file" "\n" "before submitting a bug. These pages explain the proper\n" "process and the information we find helpful for debugging..\n" "\n" "exiting.") self.mock_call_and_check.side_effect = ExternalProcessError( returncode=1, cmd=("dhcpd", ), output=dhcpd_error) self.assertEqual( [{ "error": "semicolon expected.", "line_num": 14, "line": "ignore ", "position": "^", }], self.validate( omapi_key, [failover_peers], [shared_network], [host], [interface], global_dhcp_snippets, ), )
def test__writes_config_and_calls_restart_when_non_host_state_diff(self): write_file = self.patch_sudo_write_file() restart_service = self.patch_restartService() failover_peers = make_failover_peer_config() shared_network = make_shared_network() [shared_network] = fix_shared_networks_failover( [shared_network], [failover_peers]) host = make_host() interface = make_interface() global_dhcp_snippets = make_global_dhcp_snippets() expected_config = factory.make_name('config') self.patch_get_config().return_value = expected_config dhcp_service = dhcp.service_monitor.getServiceByName( self.server.dhcp_service) on = self.patch_autospec(dhcp_service, "on") old_state = dhcp.DHCPState( factory.make_name("omapi_key"), [failover_peers], [shared_network], [host], [interface], global_dhcp_snippets) dhcp._current_server_state[self.server.dhcp_service] = old_state omapi_key = factory.make_name('omapi_key') yield self.configure( omapi_key, [failover_peers], [shared_network], [host], [interface], global_dhcp_snippets) self.assertThat( write_file, MockCallsMatch( call( self.server.config_filename, expected_config.encode("utf-8")), call( self.server.interfaces_filename, interface["name"].encode("utf-8")), )) self.assertThat(on, MockCalledOnceWith()) self.assertThat( restart_service, MockCalledOnceWith(self.server.dhcp_service)) self.assertEquals( dhcp._current_server_state[self.server.dhcp_service], dhcp.DHCPState( omapi_key, [failover_peers], [shared_network], [host], [interface], global_dhcp_snippets))
def test__does_not_log_ServiceActionError_when_restarting(self): self.patch_sudo_write_file() self.patch_restartService().side_effect = ServiceActionError() failover_peers = [make_failover_peer_config()] shared_networks = fix_shared_networks_failover([make_shared_network()], failover_peers) with FakeLogger("maas") as logger: with ExpectedException(exceptions.CannotConfigureDHCP): yield self.configure( factory.make_name("key"), failover_peers, shared_networks, [make_host()], [make_interface()], make_global_dhcp_snippets(), ) self.assertDocTestMatches("", logger.output)
def test__converts_failure_writing_file_to_CannotConfigureDHCP(self): self.patch_sudo_delete_file() self.patch_sudo_write_file().side_effect = ExternalProcessError( 1, "sudo something") self.patch_restartService() failover_peers = [make_failover_peer_config()] shared_networks = fix_shared_networks_failover([make_shared_network()], failover_peers) with ExpectedException(exceptions.CannotConfigureDHCP): yield self.configure( factory.make_name("key"), failover_peers, shared_networks, [make_host()], [make_interface()], make_global_dhcp_snippets(), )
def make_args(self): omapi_key = factory.make_name("omapi_key") failover_peers = [make_failover_peer_config() for _ in range(3)] shared_networks = [make_shared_network() for _ in range(3)] shared_networks = fix_shared_networks_failover( shared_networks, failover_peers ) hosts = [make_host() for _ in range(3)] interfaces = [make_interface() for _ in range(3)] return ( omapi_key, failover_peers, shared_networks, hosts, interfaces, make_global_dhcp_snippets(), )
def test__does_log_other_exceptions_when_restarting(self): self.patch_sudo_write_file() self.patch_restartService().side_effect = ( factory.make_exception("DHCP is on strike today")) failover_peers = [make_failover_peer_config()] shared_networks = fix_shared_networks_failover( [make_shared_network()], failover_peers) with FakeLogger("maas") as logger: with ExpectedException(exceptions.CannotConfigureDHCP): yield self.configure( factory.make_name('key'), failover_peers, shared_networks, [make_host()], [make_interface()], make_global_dhcp_snippets()) self.assertDocTestMatches( "DHCPv... server failed to restart: " "DHCP is on strike today", logger.output)
def test__bad_config(self): omapi_key = factory.make_name('omapi_key') failover_peers = make_failover_peer_config() shared_network = make_shared_network() [shared_network] = fix_shared_networks_failover( [shared_network], [failover_peers]) host = make_host() interface = make_interface() global_dhcp_snippets = make_global_dhcp_snippets() dhcpd_error = ( 'Internet Systems Consortium DHCP Server 4.3.3\n' 'Copyright 2004-2015 Internet Systems Consortium.\n' 'All rights reserved.\n' 'For info, please visit https://www.isc.org/software/dhcp/\n' '/tmp/maas-dhcpd-z5c7hfzt line 14: semicolon expected.\n' 'ignore \n' '^\n' 'Configuration file errors encountered -- exiting\n' '\n' 'If you think you have received this message due to a bug rather\n' 'than a configuration issue please read the section on submitting' '\n' 'bugs on either our web page at www.isc.org or in the README file' '\n' 'before submitting a bug. These pages explain the proper\n' 'process and the information we find helpful for debugging..\n' '\n' 'exiting.' ) self.patch(dhcp, 'call_and_check').side_effect = ExternalProcessError( returncode=1, cmd=("dhcpd",), output=dhcpd_error) self.assertEqual([{ 'error': 'semicolon expected.', 'line_num': 14, 'line': 'ignore ', 'position': '^', }], self.validate( omapi_key, [failover_peers], [shared_network], [host], [interface], global_dhcp_snippets))
def test__good_config(self): omapi_key = factory.make_name("omapi_key") failover_peers = make_failover_peer_config() shared_network = make_shared_network() [shared_network] = fix_shared_networks_failover([shared_network], [failover_peers]) host = make_host() interface = make_interface() global_dhcp_snippets = make_global_dhcp_snippets() self.assertEqual( None, self.validate( omapi_key, [failover_peers], [shared_network], [host], [interface], global_dhcp_snippets, ), )
def make_sample_params_only(ipv6=False): """Return a dict of arbitrary DHCP configuration parameters. :param ipv6: When true, prepare configuration for a DHCPv6 server, otherwise prepare configuration for a DHCPv4 server. :return: A dictionary of sample configuration. """ failover_peers = [make_failover_peer_config() for _ in range(3)] shared_networks = [make_shared_network(ipv6=ipv6) for _ in range(3)] shared_networks = fix_shared_networks_failover(shared_networks, failover_peers) return { 'omapi_key': b64encode(factory.make_bytes()).decode("ascii"), 'failover_peers': failover_peers, 'shared_networks': shared_networks, 'hosts': [make_host(ipv6=ipv6) for _ in range(3)], 'global_dhcp_snippets': make_global_dhcp_snippets(), 'ipv6': ipv6, }
def test__writes_config_and_uses_omapi_to_update_hosts(self): write_file = self.patch_sudo_write_file() get_service_state = self.patch_getServiceState() get_service_state.return_value = ServiceState(SERVICE_STATE.ON, "running") restart_service = self.patch_restartService() ensure_service = self.patch_ensureService() update_hosts = self.patch_update_hosts() failover_peers = make_failover_peer_config() shared_network = make_shared_network() [shared_network] = fix_shared_networks_failover([shared_network], [failover_peers]) old_hosts = [make_host(dhcp_snippets=[]) for _ in range(3)] interface = make_interface() global_dhcp_snippets = make_global_dhcp_snippets() expected_config = factory.make_name("config") self.patch_get_config().return_value = expected_config dhcp_service = dhcp.service_monitor.getServiceByName( self.server.dhcp_service) on = self.patch_autospec(dhcp_service, "on") omapi_key = factory.make_name("omapi_key") old_state = dhcp.DHCPState( omapi_key, [failover_peers], [shared_network], old_hosts, [interface], global_dhcp_snippets, ) dhcp._current_server_state[self.server.dhcp_service] = old_state new_hosts = copy.deepcopy(old_hosts) removed_host = new_hosts.pop() modified_host = new_hosts[0] modified_host["ip"] = factory.make_ip_address() added_host = make_host(dhcp_snippets=[]) new_hosts.append(added_host) yield self.configure( omapi_key, [failover_peers], [shared_network], new_hosts, [interface], global_dhcp_snippets, ) self.assertThat( write_file, MockCallsMatch( call( self.server.config_filename, expected_config.encode("utf-8"), mode=0o640, ), call( self.server.interfaces_filename, interface["name"].encode("utf-8"), mode=0o640, ), ), ) self.assertThat(on, MockCalledOnceWith()) self.assertThat( get_service_state, MockCalledOnceWith(self.server.dhcp_service, now=True), ) self.assertThat(restart_service, MockNotCalled()) self.assertThat(ensure_service, MockCalledOnceWith(self.server.dhcp_service)) self.assertThat( update_hosts, MockCalledOnceWith(ANY, [removed_host], [added_host], [modified_host]), ) self.assertEquals( dhcp._current_server_state[self.server.dhcp_service], dhcp.DHCPState( omapi_key, [failover_peers], [shared_network], new_hosts, [interface], global_dhcp_snippets, ), )
def test__writes_config_and_doesnt_use_omapi_when_was_off(self): write_file = self.patch_sudo_write_file() get_service_state = self.patch_getServiceState() get_service_state.return_value = ServiceState(SERVICE_STATE.OFF, "dead") restart_service = self.patch_restartService() ensure_service = self.patch_ensureService() update_hosts = self.patch_update_hosts() failover_peers = make_failover_peer_config() shared_network = make_shared_network() [shared_network] = fix_shared_networks_failover([shared_network], [failover_peers]) host = make_host(dhcp_snippets=[]) interface = make_interface() global_dhcp_snippets = make_global_dhcp_snippets() expected_config = factory.make_name("config") self.patch_get_config().return_value = expected_config dhcp_service = dhcp.service_monitor.getServiceByName( self.server.dhcp_service) on = self.patch_autospec(dhcp_service, "on") omapi_key = factory.make_name("omapi_key") old_host = make_host(dhcp_snippets=[]) old_state = dhcp.DHCPState( omapi_key, [failover_peers], [shared_network], [old_host], [interface], global_dhcp_snippets, ) dhcp._current_server_state[self.server.dhcp_service] = old_state yield self.configure( omapi_key, [failover_peers], [shared_network], [host], [interface], global_dhcp_snippets, ) self.assertThat( write_file, MockCallsMatch( call( self.server.config_filename, expected_config.encode("utf-8"), mode=0o640, ), call( self.server.interfaces_filename, interface["name"].encode("utf-8"), mode=0o640, ), ), ) self.assertThat(on, MockCalledOnceWith()) self.assertThat( get_service_state, MockCalledOnceWith(self.server.dhcp_service, now=True), ) self.assertThat(restart_service, MockNotCalled()) self.assertThat(ensure_service, MockCalledOnceWith(self.server.dhcp_service)) self.assertThat(update_hosts, MockNotCalled()) self.assertEquals( dhcp._current_server_state[self.server.dhcp_service], dhcp.DHCPState( omapi_key, [failover_peers], [shared_network], [host], [interface], global_dhcp_snippets, ), )
def test_writes_config_and_restarts_when_omapi_fails(self): write_file = self.patch_sudo_write_file() get_service_state = self.patch_getServiceState() get_service_state.return_value = ServiceState(SERVICE_STATE.ON, "running") restart_service = self.patch_restartService() ensure_service = self.patch_ensureService() update_hosts = self.patch_update_hosts() update_hosts.side_effect = factory.make_exception() failover_peers = make_failover_peer_config() shared_network = make_shared_network() [shared_network] = fix_shared_networks_failover([shared_network], [failover_peers]) old_hosts = [make_host(dhcp_snippets=[]) for _ in range(3)] interface = make_interface() global_dhcp_snippets = make_global_dhcp_snippets() expected_config = factory.make_name("config") self.patch_get_config().return_value = expected_config dhcp_service = dhcp.service_monitor.getServiceByName( self.server.dhcp_service) on = self.patch_autospec(dhcp_service, "on") omapi_key = factory.make_name("omapi_key") old_state = dhcp.DHCPState( omapi_key, [failover_peers], [shared_network], old_hosts, [interface], global_dhcp_snippets, ) dhcp._current_server_state[self.server.dhcp_service] = old_state new_hosts = copy.deepcopy(old_hosts) removed_host = new_hosts.pop() modified_host = new_hosts[0] modified_host["ip"] = factory.make_ip_address( ipv6=self.server.dhcp_service == "DHCPv6") added_host = make_host(dhcp_snippets=[]) new_hosts.append(added_host) with FakeLogger("maas") as logger: yield self.configure( omapi_key, [failover_peers], [shared_network], new_hosts, [interface], global_dhcp_snippets, ) self.assertThat( write_file, MockCallsMatch( call( self.server.config_filename, expected_config.encode("utf-8"), mode=0o640, ), call( self.server.interfaces_filename, interface["name"].encode("utf-8"), mode=0o640, ), ), ) self.assertThat(on, MockCalledOnceWith()) self.assertThat( get_service_state, MockCalledOnceWith(self.server.dhcp_service, now=True), ) self.assertThat(restart_service, MockCalledOnceWith(self.server.dhcp_service)) self.assertThat(ensure_service, MockCalledOnceWith(self.server.dhcp_service)) self.assertThat( update_hosts, MockCalledOnceWith(ANY, [removed_host], [added_host], [modified_host]), ) self.assertEqual( dhcp._current_server_state[self.server.dhcp_service], dhcp.DHCPState( omapi_key, [failover_peers], [shared_network], new_hosts, [interface], global_dhcp_snippets, ), ) self.assertDocTestMatches( "Failed to update all host maps. Restarting DHCPv... " "service to ensure host maps are in-sync.", logger.output, )