Exemple #1
0
    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")),
                call(
                    self.server.interfaces_filename,
                    interface["name"].encode("utf-8")),
                ))
        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))
Exemple #2
0
 def test_performs_operations(self):
     remove_host = make_host()
     add_host = make_host()
     modify_host = make_host()
     omapi_cli = Mock()
     self.patch(dhcp, "OmapiClient").return_value = omapi_cli
     dhcp._update_hosts(Mock(), [remove_host], [add_host], [modify_host])
     self.assertEqual(
         omapi_cli.mock_calls,
         [
             call.del_host(remove_host["mac"]),
             call.add_host(add_host["mac"], add_host["ip"]),
             call.update_host(modify_host["mac"], modify_host["ip"]),
         ],
     )
Exemple #3
0
def make_sample_params_only(ipv6=False, with_interface=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, with_interface=with_interface)
        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,
    }
Exemple #4
0
 def test_requires_restart_returns_False_when_hosts_are_ipv6_and_server_is_ipv6(
     self, ):
     (
         omapi_key,
         failover_peers,
         shared_networks,
         _,
         interfaces,
         global_dhcp_snippets,
     ) = self.make_args()
     state = dhcp.DHCPState(
         omapi_key,
         failover_peers,
         shared_networks,
         [],
         interfaces,
         global_dhcp_snippets,
     )
     new_hosts = [make_host(dhcp_snippets=[], ipv6=True) for _ in range(3)]
     new_state = dhcp.DHCPState(
         omapi_key,
         copy.deepcopy(failover_peers),
         copy.deepcopy(shared_networks),
         new_hosts,
         copy.deepcopy(interfaces),
         copy.deepcopy(global_dhcp_snippets),
     )
     self.assertFalse(
         new_state.requires_restart(state, is_dhcpv6_server=True))
Exemple #5
0
 def test_requires_restart_returns_False_when_hosts_different(self):
     (
         omapi_key,
         failover_peers,
         shared_networks,
         hosts,
         interfaces,
         global_dhcp_snippets,
     ) = self.make_args()
     state = dhcp.DHCPState(
         omapi_key,
         failover_peers,
         shared_networks,
         hosts,
         interfaces,
         global_dhcp_snippets,
     )
     changed_hosts = copy.deepcopy(hosts)
     changed_hosts.append(make_host(dhcp_snippets=[]))
     new_state = dhcp.DHCPState(
         omapi_key,
         copy.deepcopy(failover_peers),
         copy.deepcopy(shared_networks),
         changed_hosts,
         copy.deepcopy(interfaces),
         copy.deepcopy(global_dhcp_snippets),
     )
     self.assertFalse(new_state.requires_restart(state))
Exemple #6
0
 def test_host_diff_returns_removal_added_and_modify(self):
     (
         omapi_key,
         failover_peers,
         shared_networks,
         hosts,
         interfaces,
         global_dhcp_snippets,
     ) = self.make_args()
     state = dhcp.DHCPState(
         omapi_key,
         failover_peers,
         shared_networks,
         hosts,
         interfaces,
         global_dhcp_snippets,
     )
     changed_hosts = copy.deepcopy(hosts)
     removed_host = changed_hosts.pop()
     modified_host = changed_hosts[0]
     modified_host["ip"] = factory.make_ip_address()
     added_host = make_host()
     changed_hosts.append(added_host)
     new_state = dhcp.DHCPState(
         omapi_key,
         copy.deepcopy(failover_peers),
         copy.deepcopy(shared_networks),
         changed_hosts,
         copy.deepcopy(interfaces),
         copy.deepcopy(global_dhcp_snippets),
     )
     self.assertEqual(
         ([removed_host], [added_host], [modified_host]),
         new_state.host_diff(state),
     )
Exemple #7
0
    def test__writes_config_and_calls_restart_when_no_current_state(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")

        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"),
                    mode=0o640,
                ),
                call(
                    self.server.interfaces_filename,
                    interface["name"].encode("utf-8"),
                    mode=0o640,
                ),
            ),
        )
        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,
            ),
        )
Exemple #8
0
def make_sample_params_only(ipv6=False,
                            with_interface=False,
                            disabled_boot_architectures=None):
    """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,
            with_interface=with_interface,
            disabled_boot_architectures=disabled_boot_architectures,
        ) 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,
    }
Exemple #9
0
 def test__performs_operations(self):
     omshell = Mock()
     self.patch(dhcp, "Omshell").return_value = omshell
     remove_host = make_host()
     add_host = make_host()
     modify_host = make_host()
     server = Mock()
     server.ipv6 = factory.pick_bool()
     dhcp._update_hosts(server, [remove_host], [add_host], [modify_host])
     self.assertThat(omshell.remove,
                     MockCallsMatch(call(remove_host["mac"]), ))
     self.assertThat(
         omshell.create,
         MockCallsMatch(call(add_host["ip"], add_host["mac"]), ))
     self.assertThat(
         omshell.modify,
         MockCallsMatch(call(modify_host["ip"], modify_host["mac"]), ))
Exemple #10
0
 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())
Exemple #11
0
 def test__converts_dhcp_restart_failure_to_CannotConfigureDHCP(self):
     self.patch_sudo_write_file()
     self.patch_sudo_delete_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 ExpectedException(exceptions.CannotConfigureDHCP):
         yield self.configure(
             factory.make_name('key'),
             failover_peers, shared_networks,
             [make_host()], [make_interface()], make_global_dhcp_snippets())
Exemple #12
0
    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,
            ),
        )
Exemple #13
0
 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)
Exemple #14
0
 def test_fail_modify(self):
     host = make_host()
     omapi_cli = Mock()
     omapi_cli.update_host.side_effect = OmapiError("Fail")
     self.patch(dhcp, "OmapiClient").return_value = omapi_cli
     err = self.assertRaises(
         exceptions.CannotModifyHostMap,
         dhcp._update_hosts,
         Mock(),
         [],
         [],
         [host],
     )
     self.assertEqual(str(err), "Fail")
Exemple #15
0
    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))
Exemple #16
0
 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(),
         )
Exemple #17
0
    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))
Exemple #18
0
 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,
     )
Exemple #19
0
    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,
            ),
        )
Exemple #20
0
    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,
        )