Repository: ansible/ansible

Issue:
Current expect module is limited in use
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and master branch are affected too.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest: -->
 - Feature Idea

##### COMPONENT NAME
<!--- Name of the module/plugin/task/feature -->
expect

##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
v2.3

##### SUMMARY
<!--- Explain the problem briefly -->
I need something similar to the expect module, but the use-case doesn't seem to be supported.

I need to log on to an out-of-band management controller to follow up on the hardware provisioning progress. And put Ansible back in control as soon as we have determined that the system PXE booted fine. (Or fail if that did not happen in the specified interval).

This is close to what the expect module seems to do, however I cannot specify a success/failure scenario in the module. I would have hoped for something more flexible in this.

Another problem is that one of the things I am waiting for is in fact variable, and the use of a dictionary (with the key being the string to expect) makes this impossible. I think we would have been better off with a pseudo-language like expect.


Discussion:
cc @sivel
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
My solution to this was doing something like:
```yaml
- name: Wait for successful PXE boot via CIMC OOB
  shell: |
      set timeout 300
      spawn ssh admin@{{ cimc_host }}

      expect "password:"
      send "{{ cimc_password }}\n"

      expect "\n{{ cimc_name }}"
      send "connect host\n"

      expect "pxeboot.n12"
      send "\n"

      exit 0
  args:
    executable: /usr/bin/expect
  delegate_to: 127.0.0.1
```
> I think we would have been better off with a pseudo-language like expect.

I can't see myself implementing something like this in the expect module.  The expect module was designed for a very simple scenario, where you may want to run a single command that requires input.

Should you want more expect functionality, I think you are on the correct track. Use actual expect for more advanced needs.  Implementing a DSL, within a DSL, to build out expect doesn't seem worthwhile.
That is fine. Are you ok with improving the module doc with this information and the win_shell example ?
iirc this is already in the shell docs.  Maybe a note of something to the effect of:

> This module is designed for the simple scenario of running a single command that requires input. For more complex needs, consider the use of expect code with the shell or script modules.

Resolving patch:
diff --git a/lib/ansible/modules/commands/expect.py b/lib/ansible/modules/commands/expect.py
--- a/lib/ansible/modules/commands/expect.py
+++ b/lib/ansible/modules/commands/expect.py
@@ -23,33 +23,30 @@
                     'supported_by': 'community'}
 
 
-DOCUMENTATION = '''
+DOCUMENTATION = r'''
 ---
 module: expect
-version_added: 2.0
-short_description: Executes a command and responds to prompts
+version_added: '2.0'
+short_description: Executes a command and responds to prompts.
 description:
-     - The C(expect) module executes a command and responds to prompts
+     - The C(expect) module executes a command and responds to prompts.
      - The given command will be executed on all selected nodes. It will not be
        processed through the shell, so variables like C($HOME) and operations
-       like C("<"), C(">"), C("|"), and C("&") will not work
+       like C("<"), C(">"), C("|"), and C("&") will not work.
 options:
   command:
     description:
-      - the command module takes command to run.
+      - The command module takes command to run.
     required: true
   creates:
     description:
-      - a filename, when it already exists, this step will B(not) be run.
-    required: false
+      - A filename, when it already exists, this step will B(not) be run.
   removes:
     description:
-      - a filename, when it does not exist, this step will B(not) be run.
-    required: false
+      - A filename, when it does not exist, this step will B(not) be run.
   chdir:
     description:
-      - cd into this directory before running the command
-    required: false
+      - Change into this directory before running the command.
   responses:
     description:
       - Mapping of expected string/regex and string to respond with. If the
@@ -58,11 +55,11 @@
     required: true
   timeout:
     description:
-      - Amount of time in seconds to wait for the expected strings
+      - Amount of time in seconds to wait for the expected strings.
     default: 30
   echo:
     description:
-      - Whether or not to echo out your response strings
+      - Whether or not to echo out your response strings.
     default: false
 requirements:
   - python >= 2.6
@@ -70,25 +67,28 @@
 notes:
   - If you want to run a command through the shell (say you are using C(<),
     C(>), C(|), etc), you must specify a shell in the command such as
-    C(/bin/bash -c "/path/to/something | grep else")
+    C(/bin/bash -c "/path/to/something | grep else").
   - The question, or key, under I(responses) is a python regex match. Case
-    insensitive searches are indicated with a prefix of C(?i)
-  - By default, if a question is encountered multiple times, it's string
+    insensitive searches are indicated with a prefix of C(?i).
+  - By default, if a question is encountered multiple times, its string
     response will be repeated. If you need different responses for successive
     question matches, instead of a string response, use a list of strings as
-    the response. The list functionality is new in 2.1
+    the response. The list functionality is new in 2.1.
+  - The M(expect) module is designed for simple scenarios. For more complex
+    needs, consider the use of expect code with the M(shell) or M(script)
+    modules. (An example is part of the M(shell) module documentation)
 author: "Matt Martz (@sivel)"
 '''
 
-EXAMPLES = '''
-# Case insensitve password string match
-- expect:
+EXAMPLES = r'''
+- name: Case insensitve password string match
+  expect:
     command: passwd username
     responses:
       (?i)password: "MySekretPa$$word"
 
-# Generic question with multiple different responses
-- expect:
+- name: Generic question with multiple different responses
+  expect:
     command: /path/to/custom/command
     responses:
       Question:


Test patch: