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)
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)
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)