Пример #1
0
 def test_local_get_interface_ip_addresses_empty(self, job_mock):
     job_mock.side_effect = [
         job.Result(stdout=bytes(MOCK_IP_ADDRESSES, 'utf-8'),
                    encoding='utf-8'),
         job.Result(stdout=bytes(MOCK_IFCONFIG_OUTPUT, 'utf-8'),
                    encoding='utf-8')
     ]
     self.assertEqual(utils.get_interface_ip_addresses(job, 'wlan1'),
                      CORRECT_EMPTY_IP_LIST)
Пример #2
0
 def test_ssh_get_interface_ip_addresses_empty(self, ssh_mock):
     ssh_mock.side_effect = [
         job.Result(stdout=bytes(MOCK_IP_ADDRESSES, 'utf-8'),
                    encoding='utf-8'),
         job.Result(stdout=bytes(MOCK_IFCONFIG_OUTPUT, 'utf-8'),
                    encoding='utf-8')
     ]
     self.assertEqual(
         utils.get_interface_ip_addresses(SshConnection('mock_settings'),
                                          'wlan1'), CORRECT_EMPTY_IP_LIST)
Пример #3
0
    def run(self,
            command,
            timeout=60,
            ignore_status=False,
            env=None,
            io_encoding='utf-8',
            attempts=2):
        """Runs a remote command over ssh.

        Will ssh to a remote host and run a command. This method will
        block until the remote command is finished.

        Args:
            command: The command to execute over ssh. Can be either a string
                     or a list.
            timeout: number seconds to wait for command to finish.
            ignore_status: bool True to ignore the exit code of the remote
                           subprocess.  Note that if you do ignore status codes,
                           you should handle non-zero exit codes explicitly.
            env: dict environment variables to setup on the remote host.
            io_encoding: str unicode encoding of command output.
            attempts: Number of attempts before giving up on command failures.

        Returns:
            A job.Result containing the results of the ssh command.

        Raises:
            job.TimeoutError: When the remote command took to long to execute.
            Error: When the ssh connection failed to be created.
            CommandError: Ssh worked, but the command had an error executing.
        """
        if attempts == 0:
            return None
        if env is None:
            env = {}

        try:
            self.setup_master_ssh(self._settings.connect_timeout)
        except Error:
            self.log.warning('Failed to create master ssh connection, using '
                             'normal ssh connection.')

        extra_options = {'BatchMode': True}
        if self._master_ssh_proc:
            extra_options['ControlPath'] = self.socket_path

        identifier = str(uuid.uuid4())
        full_command = 'echo "CONNECTED: %s"; %s' % (identifier, command)

        terminal_command = self._formatter.format_command(
            full_command, env, self._settings, extra_options=extra_options)

        dns_retry_count = 2
        while True:
            result = job.run(terminal_command,
                             ignore_status=True,
                             timeout=timeout,
                             io_encoding=io_encoding)
            output = result.stdout

            # Check for a connected message to prevent false negatives.
            valid_connection = re.search('^CONNECTED: %s' % identifier,
                                         output,
                                         flags=re.MULTILINE)
            if valid_connection:
                # Remove the first line that contains the connect message.
                line_index = output.find('\n') + 1
                if line_index == 0:
                    line_index = len(output)
                real_output = output[line_index:].encode(io_encoding)

                result = job.Result(command=result.command,
                                    stdout=real_output,
                                    stderr=result._raw_stderr,
                                    exit_status=result.exit_status,
                                    duration=result.duration,
                                    did_timeout=result.did_timeout,
                                    encoding=io_encoding)
                if result.exit_status and not ignore_status:
                    raise job.Error(result)
                return result

            error_string = result.stderr

            had_dns_failure = (result.exit_status == 255 and re.search(
                r'^ssh: .*: Name or service not known',
                error_string,
                flags=re.MULTILINE))
            if had_dns_failure:
                dns_retry_count -= 1
                if not dns_retry_count:
                    raise Error('DNS failed to find host.', result)
                self.log.debug('Failed to connect to host, retrying...')
            else:
                break

        had_timeout = re.search(
            r'^ssh: connect to host .* port .*: '
            r'Connection timed out\r$',
            error_string,
            flags=re.MULTILINE)
        if had_timeout:
            raise Error('Ssh timed out.', result)

        permission_denied = 'Permission denied' in error_string
        if permission_denied:
            raise Error('Permission denied.', result)

        unknown_host = re.search(
            r'ssh: Could not resolve hostname .*: '
            r'Name or service not known',
            error_string,
            flags=re.MULTILINE)
        if unknown_host:
            raise Error('Unknown host.', result)

        self.log.error('An unknown error has occurred. Job result: %s' %
                       result)
        ping_output = job.run('ping %s -c 3 -w 1' % self._settings.hostname,
                              ignore_status=True)
        self.log.error('Ping result: %s' % ping_output)
        if attempts > 1:
            self._cleanup_master_ssh()
            self.run(command, timeout, ignore_status, env, io_encoding,
                     attempts - 1)
        raise Error('The job failed for unknown reasons.', result)