def test_send_response(self): conn = terminal_connection.RawConnection() # no responses self.assertEqual(conn._send_response("abcd?", []), -1) # wrong question self.assertEqual( conn._send_response("abcd?", [{ 'question': 'yes?', 'answer': 'no' }]), -1) # correct question conn.conn = MagicMock() conn.logger = MagicMock() conn.conn.send = Mock(return_value=2) conn.conn.closed = False conn.conn.log_file_name = False self.assertEqual( conn._send_response("continue, yes?", [{ 'question': 'yes?', 'answer': 'no' }]), 14) conn.conn.send.assert_called_with("no") # question with new line response conn.conn.send = Mock(return_value=1) self.assertEqual( conn._send_response("continue, yes?", [{ 'question': 'yes?', 'answer': 'n', 'newline': True }]), 14) conn.conn.send.assert_has_calls([call("n"), call('\n')])
def test_run_with_closed_connection_after_third_check(self): conn = terminal_connection.RawConnection() conn.logger = MagicMock() class _fake_conn(object): call_count = 0 def send(self, text): return len(text) def recv(self, size): return "+\n" def close(self): pass @property def closed(self): self.call_count += 1 return (self.call_count >= 4) conn.conn = _fake_conn() self.assertEqual(conn.run("test"), "+")
def test_write_to_log_no_logfile(self): conn = terminal_connection.RawConnection() conn.log_file_name = None conn.logger = MagicMock() conn._write_to_log("Some_text") conn.logger.debug.assert_not_called()
def test_connect_raw(self): conn_mock = MagicMock() conn_mock.recv = MagicMock(return_value=b"some_prompt#") ssh_mock = MagicMock() ssh_mock.connect = MagicMock() ssh_mock.invoke_shell = MagicMock(return_value=conn_mock) conn = terminal_connection.RawConnection(logger=MagicMock(), log_file_name=None) # without responses with patch("paramiko.SSHClient", MagicMock(return_value=ssh_mock)): self.assertEqual( conn.connect("ip", "user", "password", None, port=44, prompt_check=None), "some_prompt") conn_mock.send = MagicMock(return_value=7) conn_mock.recv = MagicMock( return_value=b"Confirm Password:\nsome_prompt#") # with responses with patch("paramiko.SSHClient", MagicMock(return_value=ssh_mock)): self.assertEqual( conn.connect("ip", "user", "password", None, port=44, prompt_check=None, responses=[{ "question": "Confirm Password:"******"answer": "123456" }]), "some_prompt")
def test_connect_with_key(self): ssh_mock = MagicMock() ssh_mock.connect = MagicMock(side_effect=OSError("e")) with patch("paramiko.RSAKey.from_private_key", MagicMock(return_value="key_value")): with patch("paramiko.SSHClient", MagicMock(return_value=ssh_mock)): with self.assertRaises(OSError): conn = terminal_connection.RawConnection( logger="logger", log_file_name="log_file_name") conn.connect( "ip", "user", None, "key", prompt_check=None, ) ssh_mock.connect.assert_called_with('ip', allow_agent=False, pkey='key_value', port=22, timeout=5, username='******') self.assertEqual(conn.logger, "logger") self.assertEqual(conn.log_file_name, "log_file_name")
def test_delete_backspace(self): conn = terminal_connection.RawConnection() # simple case self.assertEqual(conn._delete_backspace("abc\bd\n$a\bbc"), "abd\n$bc") # \b in begging of line self.assertEqual(conn._delete_backspace("\bcd\n$a\bbc"), "cd\n$bc") # \b at the end self.assertEqual(conn._delete_backspace("abc\b\b\b\b\b"), "")
def test_cleanup_response_error(self): conn = terminal_connection.RawConnection() conn.logger = MagicMock() # check with closed connection with self.assertRaises(exceptions.RecoverableError) as error: conn._cleanup_response(text="prompt> text\n some\nerror", prefix="prompt>", warning_examples=[], error_examples=['error'], critical_examples=[]) conn.logger.info.assert_not_called() self.assertEqual( repr(error.exception), 'RecoverableError(\'Looks as we have error in response: ' 'text\\n some\\nerror\',)') # check with alive connection conn.conn = MagicMock() conn.conn.closed = False # save mocks _conn_mock = conn.conn # warnings? with self.assertRaises(exceptions.RecoverableWarning) as error: conn._cleanup_response(text="prompt> text\n some\nerror", prefix="prompt>", warning_examples=['error'], error_examples=[], critical_examples=[]) _conn_mock.close.assert_not_called() # errors? with self.assertRaises(exceptions.RecoverableError) as error: conn._cleanup_response(text="prompt> text\n some\nerror", prefix="prompt>", warning_examples=[], error_examples=['error'], critical_examples=[]) _conn_mock.close.assert_called_with() self.assertFalse(conn.conn) conn.conn = _conn_mock # critical? conn.conn.close = MagicMock() # save mocks _conn_mock = conn.conn # check with close with self.assertRaises(exceptions.NonRecoverableError) as error: conn._cleanup_response(text="prompt> text\n some\nerror", prefix="prompt>", warning_examples=[], error_examples=[], critical_examples=['error']) _conn_mock.close.assert_called_with()
def test_cleanup_response_empty(self): conn = terminal_connection.RawConnection() self.assertEqual( conn._cleanup_response(text=" text ", prefix=":", warning_examples=[], error_examples=[], critical_examples=[]), "text")
def test_recv(self): conn = terminal_connection.RawConnection() conn.conn = MagicMock() conn.logger = MagicMock() conn.conn.recv = MagicMock(return_value="AbCd") conn.conn.log_file_name = False self.assertEqual(conn._conn_recv(4), "AbCd") conn.conn.recv.assert_called_with(4)
def test_send_closed_connection(self): conn = terminal_connection.RawConnection() conn.conn = MagicMock() conn.conn.send = MagicMock(return_value=3) conn.conn.closed = True conn.conn.log_file_name = False conn._conn_send("abcd") conn.conn.send.assert_called_with("abcd")
def test_run_with_closed_connection(self): conn = terminal_connection.RawConnection() conn.logger = MagicMock() conn.conn = MagicMock() conn.conn.closed = True conn.conn.send = MagicMock(return_value=5) self.assertEqual(conn.run("test"), "") conn.conn.send.assert_called_with("test\n")
def test_send_byte_by_byte(self): conn = terminal_connection.RawConnection() conn.conn = MagicMock() conn.logger = MagicMock() conn.conn.send = Mock(return_value=2) conn.conn.closed = False conn.conn.log_file_name = False conn._conn_send("abcd") conn.conn.send.assert_has_calls([call('abcd'), call('cd')])
def test_recv_empty(self): conn = terminal_connection.RawConnection() conn.conn = MagicMock() conn.logger = MagicMock() conn.conn.recv = MagicMock(return_value="") conn.conn.log_file_name = False self.assertEqual(conn._conn_recv(4), "") conn.logger.warn.assert_called_with('We have empty response.') conn.conn.recv.assert_called_with(4)
def test_run_return_without_delay(self): conn = terminal_connection.RawConnection() conn.logger = MagicMock() conn.conn = MagicMock() conn.conn.closed = False conn.conn.send = MagicMock(return_value=5) conn.conn.recv = MagicMock(return_value="\nmessage\n#") self.assertEqual(conn.run("test"), "message") conn.conn.send.assert_called_with("test\n")
def test_send_troubles(self): conn = terminal_connection.RawConnection() conn.conn = MagicMock() conn.logger = MagicMock() conn.conn.send = MagicMock(return_value=-1) conn.conn.closed = True conn.conn.log_file_name = False conn._conn_send("abcd") conn.logger.info.assert_called_with("We have issue with send!") conn.conn.send.assert_called_with("abcd")
def test_is_closed(self): conn = terminal_connection.RawConnection() conn.conn = MagicMock() conn.conn.closed = False self.assertFalse(conn.is_closed()) conn.conn.closed = True self.assertTrue(conn.is_closed()) conn.conn = None self.assertTrue(conn.is_closed())
def test_cleanup_response_without_prompt(self): conn = terminal_connection.RawConnection() conn.logger = MagicMock() self.assertEqual( conn._cleanup_response(text="prmpt> text ", prefix="prompt>", warning_examples=[], error_examples=['error'], critical_examples=[]), "prmpt> text") conn.logger.debug.assert_called_with( "Have not found 'prompt>' in response: ''prmpt> text ''")
def test_cleanup_response_with_prompt(self): conn = terminal_connection.RawConnection() conn.logger = MagicMock() self.assertEqual( conn._cleanup_response(text="prompt> text ", prefix="prompt>", warning_examples=[], error_examples=['error'], critical_examples=[]), "text") conn.logger.info.assert_not_called()
def test_connect(self): conn_mock = MagicMock() conn_mock.recv = MagicMock(return_value=b"some_prompt#") ssh_mock = MagicMock() ssh_mock.connect = MagicMock() ssh_mock.invoke_shell = MagicMock(return_value=conn_mock) conn = terminal_connection.RawConnection( logger=MagicMock(), log_file_name=None) with patch("paramiko.SSHClient", MagicMock(return_value=ssh_mock)): self.assertEqual( conn.connect("ip", "user", "password", None, port=44, prompt_check=None), "some_prompt" )
def test_write_to_log_cantcreate_dir(self): conn = terminal_connection.RawConnection() conn.log_file_name = '/proc/read_only/file' conn.logger = MagicMock() with patch("os.path.isdir", MagicMock(return_value=False)): with patch( "os.makedirs", MagicMock(side_effect=Exception( "[Errno 13] Permission denied: '/proc/read_only'"))): conn._write_to_log("Some_text") conn.logger.info.assert_called_with( 'Can\'t write to log: Exception("[Errno 13] Permission denied:' ' \'/proc/read_only\'",)')
def test_cleanup_response_mess_before_prompt(self): conn = terminal_connection.RawConnection() conn.logger = MagicMock() self.assertEqual( conn._cleanup_response(text="..prompt> text\n some", prefix="prompt>", warning_examples=[], error_examples=['error'], critical_examples=[]), "some") conn.logger.debug.assert_called_with( "Some mess before 'prompt>' in response: ''..prompt> " "text\\n some''")
def test_run_return_without_delay_with_responses(self): conn = terminal_connection.RawConnection() conn.logger = MagicMock() conn.conn = MagicMock() conn.conn.closed = False conn.conn.send = MagicMock(side_effect=[5, 2]) conn.conn.recv = MagicMock(side_effect=["\nmessage, yes?", "ok\n#"]) self.assertEqual( conn.run("test", responses=[{ 'question': 'yes?', 'answer': 'no' }]), "message, yes?ok") conn.conn.send.assert_has_calls([call("test\n"), call('no')])
def test_close(self): conn = terminal_connection.RawConnection() conn_conn = MagicMock() conn_conn.close = MagicMock() conn.conn = conn_conn conn_ssh = MagicMock() conn_ssh.close = MagicMock() conn.ssh = conn_ssh conn.close() conn_conn.close.assert_called_with() conn_ssh.close.assert_called_with()
def test_connect_with_password(self): ssh_mock = MagicMock() ssh_mock.connect = MagicMock(side_effect=OSError("e")) with patch("paramiko.SSHClient", MagicMock(return_value=ssh_mock)): with self.assertRaises(OSError): conn = terminal_connection.RawConnection( logger="logger", log_file_name="log_file_name") conn.connect("ip", "user", "password", None, port=44, prompt_check="prompt_check") ssh_mock.connect.assert_called_with( 'ip', allow_agent=False, look_for_keys=False, password='******', port=44, timeout=5, username='******') self.assertEqual(conn.logger, "logger") self.assertEqual(conn.log_file_name, "log_file_name")
def test_write_to_log_write_file_output(self): conn = terminal_connection.RawConnection() conn.log_file_name = '/proc/read_only_file' conn.logger = MagicMock() with patch("os.path.isdir", MagicMock(return_value=True)): fake_file = mock_open() if PY2: # python 2 with patch('__builtin__.open', fake_file): conn._write_to_log("Some_text") else: # python 3 with patch('builtins.open', fake_file): conn._write_to_log("Some_text") fake_file.assert_called_once_with('/proc/read_only_file', 'a+') fake_file().write.assert_called_with('Some_text')
def test_close(self): conn = terminal_connection.RawConnection() conn.conn = MagicMock() conn.conn.close = MagicMock() conn.ssh = MagicMock() conn.ssh.close = MagicMock() # save mocks _conn_mock = conn.conn _ssh_mock = conn.ssh # run commands conn.close() # check calls _conn_mock.close.assert_called_with() _ssh_mock.close.assert_called_with()
def test_run_with_closed_connection_after_twice_check(self): conn = terminal_connection.RawConnection() conn.logger = MagicMock() conn.conn = MagicMock() conn.conn.closed = False conn.conn.call_count = 0 def _recv(size): if conn.conn.call_count == 1: conn.conn.closed = True conn.conn.call_count += 1 return "+" conn.conn.send = MagicMock(return_value=5) conn.conn.recv = _recv self.assertEqual(conn.run("test"), "++") conn.conn.send.assert_called_with("test\n")
def test_find_any_in(self): conn = terminal_connection.RawConnection() self.assertEqual(conn._find_any_in("abcd\n$abc", ["$", "#"]), 5) self.assertEqual(conn._find_any_in("abcd\n>abc", ["$", "#"]), -1)
def _execute(ctx, properties, runtime_properties, get_resource, host_ip, log_stamp, kwargs): # register logger file logger_file = kwargs.get('logger_file') if logger_file: fh = logging.FileHandler(logger_file) fh.setLevel(logging.DEBUG) fh.setFormatter( logging.Formatter(fmt="%(asctime)s %(levelname)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S")) ctx.logger.addHandler(fh) # get current calls calls = kwargs.get('calls', []) if not calls: ctx.logger.info("No calls") return # credentials terminal_auth = properties.get('terminal_auth', {}) terminal_auth.update(kwargs.get('terminal_auth', {})) ip_list = terminal_auth.get('ip') # if node contained in some other node, try to overwrite ip if not ip_list and host_ip: ip_list = [host_ip] ctx.logger.info("Used host from container: {ip_list}".format( ip_list=repr(ip_list))) if isinstance(ip_list, bytes): ip_list = ip_list.decode('UTF-8') if isinstance(ip_list, text_type): ip_list = [ip_list] user = terminal_auth.get('user') password = terminal_auth.get('password') key_content = terminal_auth.get('key_content') port = terminal_auth.get('port', 22) if not ip_list or not user: raise cfy_exc.NonRecoverableError( "please check your credentials, ip or user not set") # additional settings global_promt_check = terminal_auth.get('promt_check') global_warning_examples = terminal_auth.get('warnings', []) global_error_examples = terminal_auth.get('errors', []) global_critical_examples = terminal_auth.get('criticals', []) global_responses = terminal_auth.get('responses', []) exit_command = terminal_auth.get('exit_command', 'exit') smart_device = terminal_auth.get('smart_device') # save logs to debug file log_file_name = None if terminal_auth.get('store_logs'): log_file_name = "/tmp/terminal-{log_stamp}.log".format( log_stamp=log_stamp) ctx.logger.info("Communication logs will be saved to %s" % log_file_name) if smart_device: ctx.logger.info("Used ssh shell extension.") connection = terminal_connection.SmartConnection( logger=ctx.logger, log_file_name=log_file_name) else: ctx.logger.info("Used raw stream connection.") connection = terminal_connection.RawConnection( logger=ctx.logger, log_file_name=log_file_name) for ip in ip_list: try: prompt = connection.connect(ip, user, password, key_content, port, prompt_check=global_promt_check, responses=global_responses) ctx.logger.info("Will be used: " + ip) break except Exception as ex: ctx.logger.info( "Can't connect to:{} with exception:{} and type:{}".format( repr(ip), str(ex), str(type(ex)))) else: raise cfy_exc.OperationRetry(message="Let's try one more time?") ctx.logger.info( "Device prompt: {prompt}".format(prompt=shorted_text(prompt))) for call in calls: responses = call.get('responses', global_responses) promt_check = call.get('promt_check', global_promt_check) error_examples = call.get('errors', global_error_examples) warning_examples = call.get('warnings', global_warning_examples) critical_examples = call.get('criticals', global_critical_examples) # use action if exist operation = call.get('action', "") # use template if have if not operation and 'template' in call: template_name = call.get('template') template_params = call.get('params') template = get_resource(template_name) if not template: ctx.logger.info("Empty template.") continue if not template_params: template_params = {} # save context for reuse in template template_params['ctx'] = ctx operation = render_template(template.decode('utf-8'), template_params) # incase of template_text if not operation and 'template_text' in call: template_params = call.get('params') template = call.get('template_text') if not template: ctx.logger.info("Empty template_text.") continue if not template_params: template_params = {} # save context for reuse in template template_params['ctx'] = ctx operation = render_template(template, template_params) if not operation: continue if responses: ctx.logger.info("We have predefined responses: {responses}".format( responses=shorted_text(responses))) ctx.logger.debug("Template: \n{operation}".format( operation=shorted_text(obfuscate_passwords(operation)))) result = "" for op_line in operation.split("\n"): # skip empty lines if not op_line.strip(): continue ctx.logger.info("Executing template...") ctx.logger.debug("Execute: {opline}".format( opline=shorted_text(obfuscate_passwords(op_line)))) result_part = rerun(ctx=ctx, func=connection.run, args=[], kwargs={ "command": op_line, "prompt_check": promt_check, "error_examples": error_examples, "warning_examples": warning_examples, "critical_examples": critical_examples, "responses": responses }, retry_count=call.get('retry_count', 10), retry_sleep=call.get('retry_sleep', 15)) if result_part.strip(): ctx.logger.info(shorted_text(result_part)) result += (result_part + "\n") # save results to runtime properties save_to = call.get('save_to') if save_to: ctx.logger.info( "For save: {result}".format(result=shorted_text(result))) runtime_properties[save_to] = result.strip() while not connection.is_closed() and exit_command: ctx.logger.info("Execute close") result = rerun(ctx=ctx, func=connection.run, args=[], kwargs={ "command": exit_command, "prompt_check": promt_check, "warning_examples": global_warning_examples, "error_examples": global_error_examples, "critical_examples": global_error_examples }) ctx.logger.info( "Result of close: {result}".format(result=shorted_text(result))) time.sleep(1) connection.close()
def test_empty_send(self): conn = terminal_connection.RawConnection() conn._conn_send("")