コード例 #1
0
ファイル: test_shell.py プロジェクト: th3architect/maas
 def test_str__contains_output(self):
     output = b"Mot\xf6rhead"
     unicode_output = "Mot\ufffdrhead"
     error = ExternalProcessError(
         returncode=-1, cmd="foo-bar", output=output
     )
     self.assertIn(unicode_output, error.__str__())
コード例 #2
0
ファイル: test_shell.py プロジェクト: th3architect/maas
 def test_upgrade_does_not_change_other_errors(self):
     error_type = factory.make_exception_type()
     error = error_type()
     self.expectThat(error, Not(IsInstance(ExternalProcessError)))
     ExternalProcessError.upgrade(error)
     self.expectThat(error, Not(IsInstance(ExternalProcessError)))
     self.expectThat(error.__class__, Is(error_type))
コード例 #3
0
ファイル: test_shell.py プロジェクト: th3architect/maas
 def test_upgrade_does_not_change_CalledProcessError_subclasses(self):
     error_type = factory.make_exception_type(bases=(CalledProcessError,))
     error = factory.make_CalledProcessError()
     error.__class__ = error_type  # Change the class.
     self.expectThat(error, Not(IsInstance(ExternalProcessError)))
     ExternalProcessError.upgrade(error)
     self.expectThat(error, Not(IsInstance(ExternalProcessError)))
     self.expectThat(error.__class__, Is(error_type))
コード例 #4
0
ファイル: test_shell.py プロジェクト: zhangrb/maas
 def test_to_ascii_defers_to_bytes(self):
     # Byte strings and non-unicode strings are handed to bytes() to
     # undergo Python's normal coercion strategy. (For byte strings
     # this is actually a no-op, but it's cheaper to do this than
     # special-case byte strings.)
     self.assertEqual(
         str(self).encode("ascii"), ExternalProcessError._to_ascii(self))
コード例 #5
0
 def test_raises_error_when_omshell_crashes(self):
     error_message = factory.make_name("error").encode("ascii")
     omshell = Mock()
     omshell.create.side_effect = ExternalProcessError(returncode=2,
                                                       cmd=("omshell", ),
                                                       output=error_message)
     mac = factory.make_mac_address()
     ip = factory.make_ip_address()
     with FakeLogger("maas.dhcp") as logger:
         error = self.assertRaises(
             exceptions.CannotCreateHostMap,
             dhcp._create_host_map,
             omshell,
             mac,
             ip,
         )
     # The CannotCreateHostMap exception includes a message describing the
     # problematic mapping.
     self.assertDocTestMatches(
         "Could not create host map for %s -> %s: ..." % (mac, ip),
         str(error),
     )
     # A message is also written to the maas.dhcp logger that describes the
     # problematic mapping.
     self.assertDocTestMatches(
         "Could not create host map for %s -> %s: ..." % (mac, ip),
         logger.output,
     )
コード例 #6
0
 def test_raises_error_when_omshell_not_connected(self):
     error = ExternalProcessError(returncode=2,
                                  cmd=("omshell", ),
                                  output="")
     self.patch(ExternalProcessError, "output_as_unicode", "not connected.")
     omshell = Mock()
     omshell.create.side_effect = error
     mac = factory.make_mac_address()
     ip = factory.make_ip_address()
     with FakeLogger("maas.dhcp") as logger:
         error = self.assertRaises(
             exceptions.CannotCreateHostMap,
             dhcp._create_host_map,
             omshell,
             mac,
             ip,
         )
     # The CannotCreateHostMap exception includes a message describing the
     # problematic mapping.
     self.assertDocTestMatches(
         "Could not create host map for %s -> %s: "
         "The DHCP server could not be reached." % (mac, ip),
         str(error),
     )
     # A message is also written to the maas.dhcp logger that describes the
     # problematic mapping.
     self.assertDocTestMatches(
         "Could not create host map for %s -> %s: "
         "The DHCP server could not be reached." % (mac, ip),
         logger.output,
     )
コード例 #7
0
ファイル: test_middleware.py プロジェクト: laoyin/maas
 def test_503_response_includes_retry_after_header(self):
     error = ExternalProcessError(returncode=-1, cmd="foo-bar")
     response = self.process_exception(error)
     self.assertEqual((
         http.client.SERVICE_UNAVAILABLE,
         '%s' % middleware_module.RETRY_AFTER_SERVICE_UNAVAILABLE,
     ), (response.status_code, response['Retry-after']))
コード例 #8
0
ファイル: test_shell.py プロジェクト: th3architect/maas
 def test_to_ascii_encodes_to_bytes(self):
     # Yes, this is how you really spell "smorgasbord."  Look it up.
     unicode_string = "Sm\xf6rg\xe5sbord"
     expected_byte_string = b"Sm?rg?sbord"
     converted_string = ExternalProcessError._to_ascii(unicode_string)
     self.assertIsInstance(converted_string, bytes)
     self.assertEqual(expected_byte_string, converted_string)
コード例 #9
0
ファイル: fs.py プロジェクト: tai271828/maas
def sudo_write_file(filename, contents, mode=0o644):
    """Write (or overwrite) file as root.  USE WITH EXTREME CARE.

    Runs an atomic update using non-interactive `sudo`.  This will fail if
    it needs to prompt for a password.

    When running in a snap or devel mode, this function calls
    `atomic_write` directly.

    :type contents: `bytes`.
    """
    from provisioningserver.config import is_dev_environment

    if not isinstance(contents, bytes):
        raise TypeError("Content must be bytes, got: %r" % (contents, ))
    if snappy.running_in_snap():
        atomic_write(contents, filename, mode=mode)
    else:
        maas_write_file = get_library_script_path("maas-write-file")
        command = _with_dev_python(maas_write_file, filename, "%.4o" % mode)
        if not is_dev_environment():
            command = sudo(command)
        proc = Popen(command, stdin=PIPE)
        stdout, stderr = proc.communicate(contents)
        if proc.returncode != 0:
            raise ExternalProcessError(proc.returncode, command, stderr)
コード例 #10
0
ファイル: test_shell.py プロジェクト: th3architect/maas
 def test_output_as_ascii(self):
     output = b"Joyeux No\xebl"
     ascii_output = b"Joyeux No?l"
     error = ExternalProcessError(
         returncode=-1, cmd="foo-bar", output=output
     )
     self.assertEqual(ascii_output, error.output_as_ascii)
コード例 #11
0
ファイル: omshell.py プロジェクト: ocni-dtu/maas
    def nullify_lease(self, ip_address: str):
        """Reset an existing lease so it's no longer valid.

        You can't delete leases with omshell, so we're setting the expiry
        timestamp to the epoch instead.
        """
        stdin = dedent(
            """\
            server {self.server_address}
            port {self.server_port}
            key omapi_key {self.shared_key}
            connect
            new lease
            set ip-address = {ip_address}
            open
            set ends = 00:00:00:00
            update
            """
        )
        stdin = stdin.format(self=self, ip_address=ip_address)

        returncode, output = self._run(stdin.encode("utf-8"))

        if b"can't open object: not found" in output:
            # Consider nonexistent leases a success.
            return None

        # Catching "invalid" is a bit like catching a bare exception
        # but omshell is so esoteric that this is probably quite safe.
        # If the update succeeded, "ends = 00:00:00:00" will most certainly
        # be in the output.  If it's not, there's been a failure.
        if b"invalid" not in output and b"\nends = 00:00:00:00" in output:
            return None

        raise ExternalProcessError(returncode, self.command, output)
コード例 #12
0
ファイル: test_shell.py プロジェクト: th3architect/maas
 def test_output_as_unicode(self):
     output = b"Mot\xf6rhead"
     unicode_output = "Mot\ufffdrhead"
     error = ExternalProcessError(
         returncode=-1, cmd="foo-bar", output=output
     )
     self.assertEqual(unicode_output, error.output_as_unicode)
コード例 #13
0
 def test__query_outlet_state_crashes_when_wget_exits_nonzero(self):
     driver = dli_module.DLIPowerDriver()
     call_and_check_mock = self.patch(dli_module, 'call_and_check')
     call_and_check_mock.side_effect = (
         ExternalProcessError(1, "dli something"))
     self.assertRaises(
         PowerActionError, driver._query_outlet_state, sentinel.outlet_id,
         sentinel.power_user, sentinel.power_pass, sentinel.power_address)
コード例 #14
0
 def test__issue_fence_cdu_command_handles_power_query_off(self):
     driver = fence_cdu_module.FenceCDUPowerDriver()
     mock = self.patch(fence_cdu_module, 'call_and_check')
     mock.side_effect = ExternalProcessError(2, "Fence CDU error")
     stdout = driver._issue_fence_cdu_command(
         'status', sentinel.power_address,
         sentinel.power_id, sentinel.power_user, sentinel.power_pass)
     self.assertThat(stdout, Equals("Status: OFF\n"))
コード例 #15
0
ファイル: test_fence_cdu.py プロジェクト: adrozdovbering/maas
 def test__issue_fence_cdu_command_errors_on_exception(self):
     driver = fence_cdu_module.FenceCDUPowerDriver()
     mock = self.patch(fence_cdu_module, 'call_and_check')
     mock.side_effect = ExternalProcessError(1, "Fence CDU error")
     self.assertRaises(PowerError, driver._issue_fence_cdu_command,
                       sentinel.command, sentinel.fence_cdu,
                       sentinel.power_address, sentinel.power_id,
                       sentinel.power_user, sentinel.power_pass)
コード例 #16
0
ファイル: test_shell.py プロジェクト: th3architect/maas
 def test_to_ascii_removes_non_printable_chars(self):
     # After conversion to a byte string, all non-printable and
     # non-ASCII characters are replaced with question marks.
     byte_string = b"*How* many roads\x01\x02\xb2\xfe"
     expected_byte_string = b"*How* many roads????"
     converted_string = ExternalProcessError._to_ascii(byte_string)
     self.assertIsInstance(converted_string, bytes)
     self.assertEqual(expected_byte_string, converted_string)
コード例 #17
0
ファイル: test_shell.py プロジェクト: zhangrb/maas
 def test_to_unicode_decodes_to_unicode(self):
     # Byte strings are decoded as ASCII by _to_unicode(), replacing
     # all non-ASCII characters with U+FFFD REPLACEMENT CHARACTERs.
     byte_string = b"This string will be converted. \xe5\xb2\x81\xe5."
     expected_unicode_string = (
         "This string will be converted. \ufffd\ufffd\ufffd\ufffd.")
     converted_string = ExternalProcessError._to_unicode(byte_string)
     self.assertIsInstance(converted_string, str)
     self.assertEqual(expected_unicode_string, converted_string)
コード例 #18
0
def bind_reconfigure():
    """Ask BIND to reload its configuration and *new* zone files.

    From rndc(8):

      Reload the configuration file and load new zones, but do not reload
      existing zone files even if they have changed. This is faster than a
      full reload when there is a large number of zones because it avoids the
      need to examine the modification times of the zones files.

    """
    try:
        execute_rndc_command(("reconfig", ))
    except CalledProcessError as exc:
        maaslog.error("Reloading BIND configuration failed: %s", exc)
        # Log before upgrade so that the output does not go to maaslog.
        ExternalProcessError.upgrade(exc)
        raise
コード例 #19
0
ファイル: test_moonshot.py プロジェクト: ocni-dtu/maas
    def test__issue_ipmitool_raises_power_action_error(self):
        context = make_context()
        moonshot_driver = MoonshotIPMIPowerDriver()
        call_and_check_mock = self.patch(moonshot_module, "call_and_check")
        call_and_check_mock.side_effect = ExternalProcessError(
            1, "ipmitool something")

        self.assertRaises(PowerActionError,
                          moonshot_driver._issue_ipmitool_command, "status",
                          **context)
コード例 #20
0
ファイル: test_middleware.py プロジェクト: anthonydillon/maas
    def test_503_response_includes_retry_after_header(self):
        middleware = APIErrorsMiddleware()
        request = factory.make_fake_request(
            "/api/2.0/" + factory.make_string(), 'POST')
        error = ExternalProcessError(returncode=-1, cmd="foo-bar")
        response = middleware.process_exception(request, error)

        self.assertEqual((
            http.client.SERVICE_UNAVAILABLE,
            '%s' % middleware_module.RETRY_AFTER_SERVICE_UNAVAILABLE,
        ), (response.status_code, response['Retry-after']))
コード例 #21
0
ファイル: test_middleware.py プロジェクト: laoyin/maas
 def test_reports_ExternalProcessError_as_ServiceUnavailable(self):
     error_text = factory.make_string()
     exception = ExternalProcessError(1, ["cmd"], error_text)
     retry_after = random.randint(0, 10)
     self.patch(middleware_module, 'RETRY_AFTER_SERVICE_UNAVAILABLE',
                retry_after)
     response = self.process_exception(exception)
     self.expectThat(response.status_code,
                     Equals(http.client.SERVICE_UNAVAILABLE))
     self.expectThat(response.content.decode(settings.DEFAULT_CHARSET),
                     Equals(str(exception)))
     self.expectThat(response['Retry-After'], Equals("%s" % retry_after))
コード例 #22
0
ファイル: test_seamicro.py プロジェクト: zhangrb/maas
    def test__power_control_seamicro15k_ipmi_raises_PowerFatalError(self):
        ip, username, password, server_id, _ = self.make_context()
        power_change = choice(['on', 'off'])
        seamicro_power_driver = SeaMicroPowerDriver()
        call_and_check_mock = self.patch(seamicro_module, 'call_and_check')
        call_and_check_mock.side_effect = (
            ExternalProcessError(1, "ipmitool something"))

        self.assertRaises(
            PowerActionError,
            seamicro_power_driver._power_control_seamicro15k_ipmi,
            ip, username, password, server_id, power_change)
コード例 #23
0
ファイル: test_dhcp.py プロジェクト: tai271828/maas
    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,
            ),
        )
コード例 #24
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())
コード例 #25
0
ファイル: omshell.py プロジェクト: ocni-dtu/maas
    def create(self, ip_address: str, mac_address: str):
        # The "name" is not a host name; it's an identifier used within
        # the DHCP server. We use the MAC address. Prior to 1.9, MAAS used
        # the IPs as the key but changing to using MAC addresses allows the
        # DHCP service to give all the NICs of a bond the same IP address.
        # The only caveat of this change is that the remove() method in this
        # class has to be able to deal with legacy host mappings (using IP as
        # the key) and new host mappings (using the MAC as the key).
        log.debug(
            "Creating host mapping {mac}->{ip}", mac=mac_address, ip=ip_address
        )
        name = mac_address.replace(":", "-")
        stdin = dedent(
            """\
            server {self.server_address}
            port {self.server_port}
            key omapi_key {self.shared_key}
            connect
            new host
            set ip-address = {ip_address}
            set hardware-address = {mac_address}
            set hardware-type = 1
            set name = "{name}"
            create
            """
        )
        stdin = stdin.format(
            self=self,
            ip_address=ip_address,
            mac_address=mac_address,
            name=name,
        )

        returncode, output = self._run(stdin.encode("utf-8"))
        # If the call to omshell doesn't result in output containing the
        # magic string 'hardware-type' then we can be reasonably sure
        # that the 'create' command failed.  Unfortunately there's no
        # other output like "successful" to check so this is the best we
        # can do.
        if b"hardware-type" in output:
            # Success.
            pass
        elif b"can't open object: I/O error" in output:
            # Host map already existed.  Treat as success.
            pass
        else:
            raise ExternalProcessError(returncode, self.command, output)
コード例 #26
0
ファイル: omshell.py プロジェクト: ocni-dtu/maas
    def remove(self, mac_address: str):
        # The "name" is not a host name; it's an identifier used within
        # the DHCP server. We use the MAC address. Prior to 1.9, MAAS using
        # the IPs as the key but changing to using MAC addresses allows the
        # DHCP service to give all the NICs of a bond the same IP address.
        # The only caveat of this change is that the remove() method needs
        # to be able to deal with legacy host mappings (using IP as
        # the key) and new host mappings (using the MAC as the key).
        # This is achieved by sending both the IP and the MAC: one of them will
        # be the key for the mapping (it will be the IP if the record was
        # created with by an old version of MAAS and the MAC otherwise).
        log.debug("Removing host mapping key={mac}", mac=mac_address)
        mac_address = mac_address.replace(":", "-")
        stdin = dedent(
            """\
            server {self.server_address}
            port {self.server_port}
            key omapi_key {self.shared_key}
            connect
            new host
            set name = "{mac_address}"
            open
            remove
            """
        )
        stdin = stdin.format(self=self, mac_address=mac_address)

        returncode, output = self._run(stdin.encode("utf-8"))

        # If the omshell worked, the last line should reference a null
        # object.  We need to strip blanks, newlines and '>' characters
        # for this to work.
        lines = output.strip(b"\n >").splitlines()
        try:
            last_line = lines[-1]
        except IndexError:
            last_line = ""
        if b"obj: <null" in last_line:
            # Success.
            pass
        elif b"can't open object: not found" in output:
            # It was already removed. Consider success.
            pass
        else:
            raise ExternalProcessError(returncode, self.command, output)
コード例 #27
0
 def test_update_targets_conf_logs_error(self):
     self.patch(boot_resources.service_monitor, "ensureService")
     mock_try_send_rack_event = self.patch(boot_resources,
                                           'try_send_rack_event')
     mock_maaslog = self.patch(boot_resources.maaslog, 'warning')
     self.patch(boot_resources.os.path, 'exists').return_value = True
     self.patch(boot_resources,
                'call_and_check').side_effect = (ExternalProcessError(
                    returncode=2, cmd=('tgt-admin', ), output='error'))
     snapshot = factory.make_name("snapshot")
     boot_resources.update_targets_conf(snapshot)
     self.assertThat(mock_try_send_rack_event, MockCalledOnce())
     self.assertThat(mock_maaslog, MockCalledOnce())
     self.assertThat(
         boot_resources.call_and_check,
         MockCalledOnceWith([
             'sudo', '-n', '/usr/sbin/tgt-admin', '--conf',
             os.path.join(snapshot, 'maas.tgt'), '--update', 'ALL'
         ]))
コード例 #28
0
def sudo_delete_file(filename):
    """Delete file as root.  USE WITH EXTREME CARE.

    Runs an atomic update using non-interactive `sudo`.  This will fail if
    it needs to prompt for a password.

    When running in a snap this function calls `atomic_write` directly.
    """
    from provisioningserver.config import is_dev_environment
    if snappy.running_in_snap():
        atomic_delete(filename)
    else:
        maas_delete_file = get_library_script_path("maas-delete-file")
        command = _with_dev_python(maas_delete_file, filename)
        if not is_dev_environment():
            command = sudo(command)
        proc = Popen(command)
        stdout, stderr = proc.communicate()
        if proc.returncode != 0:
            raise ExternalProcessError(proc.returncode, command, stderr)
コード例 #29
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))
コード例 #30
0
 def test__show_service_start_error(self):
     url = factory.make_simple_http_url()
     secret = factory.make_bytes()
     register_command.run(self.make_args(url=url, secret=to_hex(secret)))
     mock_call_and_check = self.patch(register_command, 'call_and_check')
     mock_call_and_check.side_effect = [
         call(),
         call(),
         ExternalProcessError(1, 'systemctl start', 'mock error'),
     ]
     mock_stderr = self.patch(register_command.stderr, 'write')
     with ExpectedException(SystemExit):
         register_command.run(self.make_args(url=url,
                                             secret=to_hex(secret)))
     self.assertThat(
         mock_stderr,
         MockCallsMatch(
             call('Unable to enable and start the maas-rackd service.'),
             call('\n'),
             call('Failed with error: mock error.'),
             call('\n'),
         ))