mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-04-24 03:11:24 -07:00
Add eos changes for Python3 (#24600)
* eos python3 changes * changes to convert response from byte to text * Add dellos6 python3 changes Make `execute_command` arguments and its return value complaint to PY3 changes made in PR #24431 * Fix py3 prompt issue for invalid show command * Fix review comments * Add generic fix for error prompt in py3 * Fix CI issue * Fix network_cli unit test failure
This commit is contained in:
parent
daef6f0911
commit
825d9df5ea
6 changed files with 110 additions and 79 deletions
|
@ -28,7 +28,7 @@ from collections import Sequence
|
|||
|
||||
from ansible import constants as C
|
||||
from ansible.errors import AnsibleConnectionFailure
|
||||
from ansible.module_utils.six import BytesIO, binary_type, text_type
|
||||
from ansible.module_utils.six import BytesIO, binary_type
|
||||
from ansible.module_utils._text import to_bytes, to_text
|
||||
from ansible.plugins import terminal_loader
|
||||
from ansible.plugins.connection import ensure_connect
|
||||
|
@ -151,7 +151,7 @@ class Connection(_Connection):
|
|||
window = self._strip(recv.read())
|
||||
|
||||
if obj and (obj.get('prompt') and not handled):
|
||||
handled = self._handle_prompt(window, obj)
|
||||
handled = self._handle_prompt(window, obj['prompt'], obj['answer'])
|
||||
|
||||
if self._find_prompt(window):
|
||||
self._last_response = recv.getvalue()
|
||||
|
@ -177,17 +177,23 @@ class Connection(_Connection):
|
|||
data = regex.sub(b'', data)
|
||||
return data
|
||||
|
||||
def _handle_prompt(self, resp, obj):
|
||||
"""Matches the command prompt and responds"""
|
||||
if isinstance(obj, (binary_type, text_type)) or not isinstance(obj['prompt'], Sequence):
|
||||
obj['prompt'] = [obj['prompt']]
|
||||
prompts = [re.compile(r, re.I) for r in obj['prompt']]
|
||||
answer = obj['answer']
|
||||
def _handle_prompt(self, resp, prompts, answer):
|
||||
"""
|
||||
Matches the command prompt and responds
|
||||
|
||||
:arg resp: Byte string containing the raw response from the remote
|
||||
:arg prompts: Sequence of byte strings that we consider prompts for input
|
||||
:arg answer: Byte string to send back to the remote if we find a prompt.
|
||||
A carriage return is automatically appended to this string.
|
||||
:returns: True if a prompt was found in ``resp``. False otherwise
|
||||
"""
|
||||
prompts = [re.compile(r, re.I) for r in prompts]
|
||||
for regex in prompts:
|
||||
match = regex.search(resp)
|
||||
if match:
|
||||
self._shell.sendall(b'%s\r' % answer)
|
||||
return True
|
||||
return False
|
||||
|
||||
def _sanitize(self, resp, obj=None):
|
||||
"""Removes elements from the response before returning to the caller"""
|
||||
|
@ -197,27 +203,38 @@ class Connection(_Connection):
|
|||
if (command and line.startswith(command.strip())) or self._matched_prompt.strip() in line:
|
||||
continue
|
||||
cleaned.append(line)
|
||||
return b"\n".join(cleaned).strip()
|
||||
return b'\n'.join(cleaned).strip()
|
||||
|
||||
def _find_prompt(self, response):
|
||||
"""Searches the buffered response for a matching command prompt"""
|
||||
errored_response = None
|
||||
is_error_message = False
|
||||
for regex in self._terminal.terminal_stderr_re:
|
||||
if regex.search(response):
|
||||
errored_response = response
|
||||
break
|
||||
is_error_message = True
|
||||
|
||||
for regex in self._terminal.terminal_stdout_re:
|
||||
match = regex.search(response)
|
||||
if match:
|
||||
self._matched_pattern = regex.pattern
|
||||
self._matched_prompt = match.group()
|
||||
if not errored_response:
|
||||
return True
|
||||
# Check if error response ends with command prompt if not
|
||||
# receive it buffered prompt
|
||||
for regex in self._terminal.terminal_stdout_re:
|
||||
match = regex.search(response)
|
||||
if match:
|
||||
errored_response = response
|
||||
break
|
||||
|
||||
if not is_error_message:
|
||||
for regex in self._terminal.terminal_stdout_re:
|
||||
match = regex.search(response)
|
||||
if match:
|
||||
self._matched_pattern = regex.pattern
|
||||
self._matched_prompt = match.group()
|
||||
if not errored_response:
|
||||
return True
|
||||
|
||||
if errored_response:
|
||||
raise AnsibleConnectionFailure(errored_response)
|
||||
|
||||
return False
|
||||
|
||||
def alarm_handler(self, signum, frame):
|
||||
"""Alarm handler raised in case of command timeout """
|
||||
display.display('closing shell due to sigalarm', log_only=True)
|
||||
|
@ -231,11 +248,11 @@ class Connection(_Connection):
|
|||
second form is as a utf8 JSON byte string with additional keywords.
|
||||
|
||||
Keywords supported for cmd:
|
||||
* command - the command string to execute
|
||||
* prompt - the expected prompt generated by executing command
|
||||
* answer - the string to respond to the prompt with
|
||||
* sendonly - bool to disable waiting for response
|
||||
|
||||
:command: the command string to execute
|
||||
:prompt: the expected prompt generated by executing command.
|
||||
This can be a string or a list of strings
|
||||
:answer: the string to respond to the prompt with
|
||||
:sendonly: bool to disable waiting for response
|
||||
:arg cmd: the byte string that represents the command to be executed
|
||||
which can be a single command or a json encoded string.
|
||||
:returns: a tuple of (return code, stdout, stderr). The return
|
||||
|
@ -243,10 +260,21 @@ class Connection(_Connection):
|
|||
"""
|
||||
try:
|
||||
obj = json.loads(to_text(cmd, errors='surrogate_or_strict'))
|
||||
obj = dict((k, to_bytes(v, errors='surrogate_or_strict', nonstring='passthru')) for k, v in obj.items())
|
||||
except (ValueError, TypeError):
|
||||
obj = {'command': to_bytes(cmd.strip(), errors='surrogate_or_strict')}
|
||||
|
||||
obj = dict((k, to_bytes(v, errors='surrogate_or_strict', nonstring='passthru')) for k, v in obj.items())
|
||||
if 'prompt' in obj:
|
||||
if isinstance(obj['prompt'], binary_type):
|
||||
# Prompt was a string
|
||||
obj['prompt'] = [obj['prompt']]
|
||||
elif not isinstance(obj['prompt'], Sequence):
|
||||
# Convert nonstrings into byte strings (to_bytes(5) => b'5')
|
||||
if obj['prompt'] is not None:
|
||||
obj['prompt'] = [to_bytes(obj['prompt'], errors='surrogate_or_strict')]
|
||||
else:
|
||||
# Prompt was a Sequence of strings. Make sure they're byte strings
|
||||
obj['prompt'] = [to_bytes(p, errors='surrogate_or_strict') for p in obj['prompt'] if p is not None]
|
||||
if obj['command'] == b'close_shell()':
|
||||
return self.close_shell()
|
||||
elif obj['command'] == b'open_shell()':
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue