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:
Ganesh Nalawade 2017-05-24 19:40:38 +05:30 committed by GitHub
parent daef6f0911
commit 825d9df5ea
6 changed files with 110 additions and 79 deletions

View file

@ -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()':