def test_pasword_without_data(self): # simulate no data input but Popen using new pty's fails self.mock_popen.return_value = None self.mock_popen.side_effect = [OSError(), self.mock_popen_res] # simulate no data input self.mock_openpty.return_value = (98, 99) self.mock_popen_res.stdout.read.side_effect = [b"some data", b"", b""] self.mock_popen_res.stderr.read.side_effect = [b""] self.mock_selector.select.side_effect = [ [(SelectorKey(self.mock_popen_res.stdout, 1001, [EVENT_READ], None), EVENT_READ)], [(SelectorKey(self.mock_popen_res.stdout, 1001, [EVENT_READ], None), EVENT_READ)], [(SelectorKey(self.mock_popen_res.stderr, 1002, [EVENT_READ], None), EVENT_READ)], [(SelectorKey(self.mock_popen_res.stdout, 1001, [EVENT_READ], None), EVENT_READ)], [] ] self.mock_selector.get_map.side_effect = lambda: True return_code, b_stdout, b_stderr = self.conn._run("ssh", "") assert return_code == 0 assert b_stdout == b'some data' assert b_stderr == b'' assert self.mock_selector.register.called is True assert self.mock_selector.register.call_count == 2 assert self.conn._send_initial_data.called is False
def test_multiple_failures(self, monkeypatch): monkeypatch.setattr(C, 'HOST_KEY_CHECKING', False) monkeypatch.setattr(C, 'ANSIBLE_SSH_RETRIES', 9) monkeypatch.setattr('time.sleep', lambda x: None) self.mock_popen_res.stdout.read.side_effect = [b""] * 10 self.mock_popen_res.stderr.read.side_effect = [b""] * 10 type(self.mock_popen_res).returncode = PropertyMock(side_effect=[255] * 30) self.mock_selector.select.side_effect = [ [(SelectorKey(self.mock_popen_res.stdout, 1001, [EVENT_READ], None), EVENT_READ)], [(SelectorKey(self.mock_popen_res.stderr, 1002, [EVENT_READ], None), EVENT_READ)], [], ] * 10 self.mock_selector.get_map.side_effect = lambda: True self.conn._build_command = MagicMock() self.conn._build_command.return_value = 'ssh' self.conn.get_option = MagicMock() self.conn.get_option.return_value = True pytest.raises(AnsibleConnectionFailure, self.conn.exec_command, 'ssh', 'some data') assert self.mock_popen.call_count == 10
def test_with_password(self): # test with a password set to trigger the sshpass write self.pc.password = '******' self.mock_popen_res.stdout.read.side_effect = [b"some data", b"", b""] self.mock_popen_res.stderr.read.side_effect = [b""] self.mock_selector.select.side_effect = [ [(SelectorKey(self.mock_popen_res.stdout, 1001, [EVENT_READ], None), EVENT_READ)], [(SelectorKey(self.mock_popen_res.stdout, 1001, [EVENT_READ], None), EVENT_READ)], [(SelectorKey(self.mock_popen_res.stderr, 1002, [EVENT_READ], None), EVENT_READ)], [(SelectorKey(self.mock_popen_res.stdout, 1001, [EVENT_READ], None), EVENT_READ)], [] ] self.mock_selector.get_map.side_effect = lambda: True return_code, b_stdout, b_stderr = self.conn._run( ["ssh", "is", "a", "cmd"], "this is more data") assert return_code == 0 assert b_stdout == b'some data' assert b_stderr == b'' assert self.mock_selector.register.called is True assert self.mock_selector.register.call_count == 2 assert self.conn._send_initial_data.called is True assert self.conn._send_initial_data.call_count == 1 assert self.conn._send_initial_data.call_args[0][ 1] == 'this is more data'
def test_password_with_prompt(self): # test with password prompting enabled self.pc.password = None self.conn.become.prompt = b'Password:'******'' assert b_stderr == b'' assert self.mock_selector.register.called is True assert self.mock_selector.register.call_count == 2 assert self.conn._send_initial_data.called is True assert self.conn._send_initial_data.call_count == 1 assert self.conn._send_initial_data.call_args[0][ 1] == 'this is input data'
def test_no_escalation(self): self.mock_popen_res.stdout.read.side_effect = [ b"my_stdout\n", b"second_line" ] self.mock_popen_res.stderr.read.side_effect = [b"my_stderr"] self.mock_selector.select.side_effect = [ [(SelectorKey(self.mock_popen_res.stdout, 1001, [EVENT_READ], None), EVENT_READ)], [(SelectorKey(self.mock_popen_res.stdout, 1001, [EVENT_READ], None), EVENT_READ)], [(SelectorKey(self.mock_popen_res.stderr, 1002, [EVENT_READ], None), EVENT_READ)], [] ] self.mock_selector.get_map.side_effect = lambda: True return_code, b_stdout, b_stderr = self.conn._run( "ssh", "this is input data") assert return_code == 0 assert b_stdout == b'my_stdout\nsecond_line' assert b_stderr == b'my_stderr' assert self.mock_selector.register.called is True assert self.mock_selector.register.call_count == 2 assert self.conn._send_initial_data.called is True assert self.conn._send_initial_data.call_count == 1 assert self.conn._send_initial_data.call_args[0][ 1] == 'this is input data'
def test_retry_then_success(self, monkeypatch): self.conn.set_option('host_key_checking', False) self.conn.set_option('reconnection_retries', 3) monkeypatch.setattr('time.sleep', lambda x: None) self.mock_popen_res.stdout.read.side_effect = [ b"", b"my_stdout\n", b"second_line" ] self.mock_popen_res.stderr.read.side_effect = [b"", b"my_stderr"] type(self.mock_popen_res).returncode = PropertyMock( side_effect=[255] * 3 + [0] * 4) self.mock_selector.select.side_effect = [ [(SelectorKey(self.mock_popen_res.stdout, 1001, [EVENT_READ], None), EVENT_READ)], [(SelectorKey(self.mock_popen_res.stderr, 1002, [EVENT_READ], None), EVENT_READ)], [], [(SelectorKey(self.mock_popen_res.stdout, 1001, [EVENT_READ], None), EVENT_READ)], [(SelectorKey(self.mock_popen_res.stdout, 1001, [EVENT_READ], None), EVENT_READ)], [(SelectorKey(self.mock_popen_res.stderr, 1002, [EVENT_READ], None), EVENT_READ)], [] ] self.mock_selector.get_map.side_effect = lambda: True self.conn._build_command = MagicMock() self.conn._build_command.return_value = 'ssh' return_code, b_stdout, b_stderr = self.conn.exec_command( 'ssh', 'some data') assert return_code == 0 assert b_stdout == b'my_stdout\nsecond_line' assert b_stderr == b'my_stderr'
def test_incorrect_password(self, monkeypatch): self.conn.set_option('host_key_checking', False) self.conn.set_option('reconnection_retries', 5) monkeypatch.setattr('time.sleep', lambda x: None) self.mock_popen_res.stdout.read.side_effect = [b''] self.mock_popen_res.stderr.read.side_effect = [ b'Permission denied, please try again.\r\n' ] type(self.mock_popen_res).returncode = PropertyMock(side_effect=[5] * 4) self.mock_selector.select.side_effect = [ [(SelectorKey(self.mock_popen_res.stdout, 1001, [EVENT_READ], None), EVENT_READ)], [(SelectorKey(self.mock_popen_res.stderr, 1002, [EVENT_READ], None), EVENT_READ)], [], ] self.mock_selector.get_map.side_effect = lambda: True self.conn._build_command = MagicMock() self.conn._build_command.return_value = [ b'sshpass', b'-d41', b'ssh', b'-C' ] exception_info = pytest.raises(AnsibleAuthenticationFailure, self.conn.exec_command, 'sshpass', 'some data') assert exception_info.value.message == ( 'Invalid/incorrect username/password. Skipping remaining 5 retries to prevent account lockout: ' 'Permission denied, please try again.') assert self.mock_popen.call_count == 1
def test_fetch_file_retries(self, monkeypatch): self.conn.set_option('host_key_checking', False) self.conn.set_option('reconnection_retries', 3) monkeypatch.setattr('time.sleep', lambda x: None) monkeypatch.setattr('ansible.plugins.connection.ssh.os.path.exists', lambda x: True) self.mock_popen_res.stdout.read.side_effect = [b"", b"my_stdout\n", b"second_line"] self.mock_popen_res.stderr.read.side_effect = [b"", b"my_stderr"] type(self.mock_popen_res).returncode = PropertyMock(side_effect=[255] * 4 + [0] * 4) self.mock_selector.select.side_effect = [ [(SelectorKey(self.mock_popen_res.stdout, 1001, [EVENT_READ], None), EVENT_READ)], [(SelectorKey(self.mock_popen_res.stderr, 1002, [EVENT_READ], None), EVENT_READ)], [], [(SelectorKey(self.mock_popen_res.stdout, 1001, [EVENT_READ], None), EVENT_READ)], [(SelectorKey(self.mock_popen_res.stdout, 1001, [EVENT_READ], None), EVENT_READ)], [(SelectorKey(self.mock_popen_res.stderr, 1002, [EVENT_READ], None), EVENT_READ)], [] ] self.mock_selector.get_map.side_effect = lambda: True self.conn._build_command = MagicMock() self.conn._build_command.return_value = 'sftp' return_code, b_stdout, b_stderr = self.conn.fetch_file('/path/to/in/file', '/path/to/dest/file') assert return_code == 0 assert b_stdout == b"my_stdout\nsecond_line" assert b_stderr == b"my_stderr" assert self.mock_popen.call_count == 2
def test_password_with_become(self): # test with some become settings self.pc.prompt = b'Password:'******'Password:'******'BECOME-SUCCESS-abcdefg' self.conn.become._id = 'abcdefg' self.conn._examine_output.side_effect = self._password_with_prompt_examine_output self.mock_popen_res.stdout.read.side_effect = [b"Password:"******"BECOME-SUCCESS-abcdefg", b"abc"] self.mock_popen_res.stderr.read.side_effect = [b"123"] self.mock_selector.select.side_effect = [ [(SelectorKey(self.mock_popen_res.stdout, 1001, [EVENT_READ], None), EVENT_READ)], [(SelectorKey(self.mock_popen_res.stdout, 1001, [EVENT_READ], None), EVENT_READ)], [(SelectorKey(self.mock_popen_res.stderr, 1002, [EVENT_READ], None), EVENT_READ)], [(SelectorKey(self.mock_popen_res.stdout, 1001, [EVENT_READ], None), EVENT_READ)], []] self.mock_selector.get_map.side_effect = lambda: True return_code, b_stdout, b_stderr = self.conn._run("ssh", "this is input data") self.mock_popen_res.stdin.flush.assert_called_once_with() assert return_code == 0 assert b_stdout == b'abc' assert b_stderr == b'123' assert self.mock_selector.register.called is True assert self.mock_selector.register.call_count == 2 assert self.conn._send_initial_data.called is True assert self.conn._send_initial_data.call_count == 1 assert self.conn._send_initial_data.call_args[0][1] == 'this is input data'