mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-05-06 13:22:48 +00:00
Log device interaction and push labeled logs back to controller (#50028)
* Add session tracing support for network_cli, netconf and httapi connection * Add `persistent_log_messages` configuration option to log device inteaction in log file for network_cli, netconf and httapi connection type * Log jsonrpc request and response in log file is configuration option is enabled * Update docs to talk about warning shown when persistent_log_messages is on
This commit is contained in:
@@ -357,6 +357,12 @@ class NetworkConnectionBase(ConnectionBase):
|
||||
|
||||
def set_options(self, task_keys=None, var_options=None, direct=None):
|
||||
super(NetworkConnectionBase, self).set_options(task_keys=task_keys, var_options=var_options, direct=direct)
|
||||
if self.get_option('persistent_log_messages'):
|
||||
warning = "Persistent connection logging is enabled for %s. This will log ALL interactions" % self._play_context.remote_addr
|
||||
logpath = getattr(C, 'DEFAULT_LOG_PATH')
|
||||
if logpath is not None:
|
||||
warning += " to %s" % logpath
|
||||
self.queue_message('warning', "%s and WILL NOT redact sensitive configuration like passwords. USE WITH CAUTION!" % warning)
|
||||
|
||||
if self._sub_plugin.get('obj') and self._sub_plugin.get('type') != 'external':
|
||||
try:
|
||||
@@ -386,3 +392,7 @@ class NetworkConnectionBase(ConnectionBase):
|
||||
if os.path.exists(socket_path):
|
||||
self._connected = True
|
||||
self._socket_path = socket_path
|
||||
|
||||
def _log_messages(self, message):
|
||||
if self.get_option('persistent_log_messages'):
|
||||
self.queue_message('log', message)
|
||||
|
||||
@@ -143,6 +143,22 @@ options:
|
||||
- name: ANSIBLE_PERSISTENT_COMMAND_TIMEOUT
|
||||
vars:
|
||||
- name: ansible_command_timeout
|
||||
persistent_log_messages:
|
||||
type: boolean
|
||||
description:
|
||||
- This flag will enable logging the command executed and response received from
|
||||
target device in the ansible log file. For this option to work 'log_path' ansible
|
||||
configuration option is required to be set to a file path with write access.
|
||||
- Be sure to fully understand the security implications of enabling this
|
||||
option as it could create a security vulnerability by logging sensitive information in log file.
|
||||
default: False
|
||||
ini:
|
||||
- section: persistent_connection
|
||||
key: log_messages
|
||||
env:
|
||||
- name: ANSIBLE_PERSISTENT_LOG_MESSAGES
|
||||
vars:
|
||||
- name: ansible_persistent_log_messages
|
||||
"""
|
||||
|
||||
from io import BytesIO
|
||||
@@ -248,7 +264,9 @@ class Connection(NetworkConnectionBase):
|
||||
url_kwargs['url_password'] = self.get_option('password')
|
||||
|
||||
try:
|
||||
response = open_url(self._url + path, data=data, **url_kwargs)
|
||||
url = self._url + path
|
||||
self._log_messages("send url '%s' with data '%s' and kwargs '%s'" % (url, data, url_kwargs))
|
||||
response = open_url(url, data=data, **url_kwargs)
|
||||
except HTTPError as exc:
|
||||
is_handled = self.handle_httperror(exc)
|
||||
if is_handled is True:
|
||||
@@ -261,7 +279,9 @@ class Connection(NetworkConnectionBase):
|
||||
raise AnsibleConnectionFailure('Could not connect to {0}: {1}'.format(self._url + path, exc.reason))
|
||||
|
||||
response_buffer = BytesIO()
|
||||
response_buffer.write(response.read())
|
||||
resp_data = response.read()
|
||||
self._log_messages("received response: '%s'" % resp_data)
|
||||
response_buffer.write(resp_data)
|
||||
|
||||
# Try to assign a new auth token if one is given
|
||||
self._auth = self.update_auth(response, response_buffer) or self._auth
|
||||
|
||||
@@ -170,6 +170,22 @@ options:
|
||||
vars:
|
||||
- name: ansible_netconf_ssh_config
|
||||
version_added: '2.7'
|
||||
persistent_log_messages:
|
||||
type: boolean
|
||||
description:
|
||||
- This flag will enable logging the command executed and response received from
|
||||
target device in the ansible log file. For this option to work 'log_path' ansible
|
||||
configuration option is required to be set to a file path with write access.
|
||||
- Be sure to fully understand the security implications of enabling this
|
||||
option as it could create a security vulnerability by logging sensitive information in log file.
|
||||
default: False
|
||||
ini:
|
||||
- section: persistent_connection
|
||||
key: log_messages
|
||||
env:
|
||||
- name: ANSIBLE_PERSISTENT_LOG_MESSAGES
|
||||
vars:
|
||||
- name: ansible_persistent_log_messages
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
@@ -172,6 +172,22 @@ options:
|
||||
- name: ANSIBLE_PERSISTENT_BUFFER_READ_TIMEOUT
|
||||
vars:
|
||||
- name: ansible_buffer_read_timeout
|
||||
persistent_log_messages:
|
||||
type: boolean
|
||||
description:
|
||||
- This flag will enable logging the command executed and response received from
|
||||
target device in the ansible log file. For this option to work 'log_path' ansible
|
||||
configuration option is required to be set to a file path with write access.
|
||||
- Be sure to fully understand the security implications of enabling this
|
||||
option as it could create a security vulnerability by logging sensitive information in log file.
|
||||
default: False
|
||||
ini:
|
||||
- section: persistent_connection
|
||||
key: log_messages
|
||||
env:
|
||||
- name: ANSIBLE_PERSISTENT_LOG_MESSAGES
|
||||
vars:
|
||||
- name: ansible_persistent_log_messages
|
||||
"""
|
||||
|
||||
import getpass
|
||||
@@ -374,6 +390,7 @@ class Connection(NetworkConnectionBase):
|
||||
buffer_read_timeout = self.get_option('persistent_buffer_read_timeout')
|
||||
self._validate_timeout_value(buffer_read_timeout, "persistent_buffer_read_timeout")
|
||||
|
||||
self._log_messages("command: %s" % command)
|
||||
while True:
|
||||
if command_prompt_matched:
|
||||
try:
|
||||
@@ -381,6 +398,7 @@ class Connection(NetworkConnectionBase):
|
||||
signal.setitimer(signal.ITIMER_REAL, buffer_read_timeout)
|
||||
data = self._ssh_shell.recv(256)
|
||||
signal.alarm(0)
|
||||
self._log_messages("response-%s: %s" % (window_count + 1, data))
|
||||
# if data is still received on channel it indicates the prompt string
|
||||
# is wrongly matched in between response chunks, continue to read
|
||||
# remaining response.
|
||||
@@ -396,7 +414,7 @@ class Connection(NetworkConnectionBase):
|
||||
return self._command_response
|
||||
else:
|
||||
data = self._ssh_shell.recv(256)
|
||||
|
||||
self._log_messages("response-%s: %s" % (window_count + 1, data))
|
||||
# when a channel stream is closed, received data will be empty
|
||||
if not data:
|
||||
break
|
||||
@@ -493,6 +511,9 @@ class Connection(NetworkConnectionBase):
|
||||
for index, regex in enumerate(prompts_regex):
|
||||
match = regex.search(resp)
|
||||
if match:
|
||||
self._matched_cmd_prompt = match.group()
|
||||
self._log_messages("matched command prompt: %s" % self._matched_cmd_prompt)
|
||||
|
||||
# if prompt_retry_check is enabled to check if same prompt is
|
||||
# repeated don't send answer again.
|
||||
if not prompt_retry_check:
|
||||
@@ -500,7 +521,8 @@ class Connection(NetworkConnectionBase):
|
||||
self._ssh_shell.sendall(b'%s' % prompt_answer)
|
||||
if newline:
|
||||
self._ssh_shell.sendall(b'\r')
|
||||
self._matched_cmd_prompt = match.group()
|
||||
prompt_answer += '\r'
|
||||
self._log_messages("matched command prompt answer: %s" % self.prompt_answer)
|
||||
if check_all and prompts and not single_prompt:
|
||||
prompts.pop(0)
|
||||
answer.pop(0)
|
||||
@@ -536,6 +558,7 @@ class Connection(NetworkConnectionBase):
|
||||
errored_response = response
|
||||
self._matched_pattern = regex.pattern
|
||||
self._matched_prompt = match.group()
|
||||
self._log_messages("matched error regex '%s' from response '%s'" % (self._matched_pattern, errored_response))
|
||||
break
|
||||
|
||||
if not is_error_message:
|
||||
@@ -544,6 +567,7 @@ class Connection(NetworkConnectionBase):
|
||||
if match:
|
||||
self._matched_pattern = regex.pattern
|
||||
self._matched_prompt = match.group()
|
||||
self._log_messages("matched cli prompt '%s' with regex '%s' from response '%s'" % (self._matched_prompt, self._matched_pattern, response))
|
||||
if not errored_response:
|
||||
return True
|
||||
|
||||
|
||||
Reference in New Issue
Block a user