Skip to content

A first answer doesn't use in a second prompt when check_all is false #205

Open
@sky-joker

Description

@sky-joker
SUMMARY

Maybe I don’t understand correctly, so can you please let me know?

In the document is written the following like.

In the following example, the second answer would be ignored and y would be the answer given to both prompts.

https://docs.ansible.com/ansible/latest/network/user_guide/network_working_with_command_output.html#handling-prompts-in-network-modules

In my perception, the first answer should be used even in the second prompt when check_all is false.
I tried that but the first answer wasn't used and a timeout occurs.

ISSUE TYPE
  • Bug Report
COMPONENT NAME

plugins/connection/network_cli.py

ANSIBLE VERSION
$ ansible --version
ansible 2.10.1
CONFIGURATION
$ ansible-config dump --only-changed
HOST_KEY_CHECKING(/path/ansible.cfg) = False
OS / ENVIRONMENT

Cisco csr1000

STEPS TO REPRODUCE

pre condition in csr1000

I set that the following was displayed to prompt when executing reload command.

  • System configuration has been modified. Save? [yes/no]:
  • Proceed with reload? [confirm]

After completing the above work, I created the following playbook and execute it.

---
- name: Test Playbook
  hosts: cisco1
  gather_facts: false
  become: true
  tasks:
    - name: Execute command
      cli_command:
        command: reload
        prompt:
           - 'System configuration has been modified'
           - 'confirm'
        answer:
           - 'yes'
           - 'yes'
EXPECTED RESULTS

The first answer also used in the second prompt and reboot OS.

ACTUAL RESULTS

The first answer doesn't use in the second prompt and the time-out error occurs.

fatal: [ping]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "msg": "timeout value 30 seconds reached while trying to send command: b'reload'"}

As far as I'm looking at the following codes, I seem that the first answer is ignored in the second prompt if check_all is false.

def _handle_prompt(
self,
resp,
prompts,
answer,
newline,
prompt_retry_check=False,
check_all=False,
):
"""
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: Sequence of Byte string to send back to the remote if we find a prompt.
A carriage return is automatically appended to this string.
:param prompt_retry_check: Bool value for trying to detect more prompts
:param check_all: Bool value to indicate if all the values in prompt sequence should be matched or any one of
given prompt.
:returns: True if a prompt was found in ``resp``. If check_all is True
will True only after all the prompt in the prompts list are matched. False otherwise.
"""
single_prompt = False
if not isinstance(prompts, list):
prompts = [prompts]
single_prompt = True
if not isinstance(answer, list):
answer = [answer]
prompts_regex = [re.compile(to_bytes(r), re.I) for r in prompts]
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:
prompt_answer = (
answer[index] if len(answer) > index else answer[0]
)
self._ssh_shell.sendall(b"%s" % prompt_answer)
if newline:
self._ssh_shell.sendall(b"\r")
prompt_answer += b"\r"
self._log_messages(
"matched command prompt answer: %s" % prompt_answer
)
if check_all and prompts and not single_prompt:
prompts.pop(0)
answer.pop(0)
return False
return True
return False

while True:
if command_prompt_matched:
try:
signal.signal(
signal.SIGALRM, self._handle_buffer_read_timeout
)
signal.setitimer(
signal.ITIMER_REAL, self._buffer_read_timeout
)
data = self._ssh_shell.recv(256)
signal.alarm(0)
self._log_messages(
"response-%s: %s" % (self._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.
command_prompt_matched = False
# restart command_timeout timer
signal.signal(signal.SIGALRM, self._handle_command_timeout)
signal.alarm(self._command_timeout)
except AnsibleCmdRespRecv:
# reset socket timeout to global timeout
self._ssh_shell.settimeout(cache_socket_timeout)
return self._command_response
else:
data = self._ssh_shell.recv(256)
self._log_messages(
"response-%s: %s" % (self._window_count + 1, data)
)
# when a channel stream is closed, received data will be empty
if not data:
break
recv.write(data)
offset = recv.tell() - 256 if recv.tell() > 256 else 0
recv.seek(offset)
window = self._strip(recv.read())
self._last_recv_window = window
self._window_count += 1
if prompts and not handled:
handled = self._handle_prompt(
window, prompts, answer, newline, False, check_all
)
self._matched_prompt_window = self._window_count
elif (
prompts
and handled
and prompt_retry_check
and self._matched_prompt_window + 1 == self._window_count
):
# check again even when handled, if same prompt repeats in next window
# (like in the case of a wrong enable password, etc) indicates
# value of answer is wrong, report this as error.
if self._handle_prompt(
window,
prompts,
answer,
newline,
prompt_retry_check,
check_all,
):
raise AnsibleConnectionFailure(
"For matched prompt '%s', answer is not valid"
% self._matched_cmd_prompt
)

Is this correct behavior?

Metadata

Metadata

Assignees

No one assigned

    Labels

    docsThis issue/PR relates to the documentation.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions