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:
Nathaniel Case
2018-12-21 10:31:43 -05:00
committed by GitHub
parent c093ea5a28
commit b2423e7602
6 changed files with 134 additions and 5 deletions

View File

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

View File

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

View File

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

View File

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