Repository: ansible/ansible

Issue:
Use of pause fails with python3 when stdin is closed
<!---
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: -->
 - Bug Report

##### COMPONENT NAME
<!--- Name of the module/plugin/task/feature -->
./lib/ansible/plugins/action/pause.py

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
ansible-playbook 2.4.0.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.6/site-packages/ansible
  executable location = /usr/bin/ansible-playbook
  python version = 3.6.2 (default, Oct  2 2017, 16:51:32) [GCC 7.2.1 20170915 (Red Hat 7.2.1-2)]
```

##### CONFIGURATION
<!---
If using Ansible 2.4 or above, paste the results of "ansible-config dump --only-changed"

Otherwise, mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).

-->
Output of `python3 /usr/bin/ansible-config dump --only-changed` is empty.

##### OS / ENVIRONMENT
<!---
Mention the OS you are running Ansible from, and the OS you are
managing, or say "N/A" for anything that is not platform-specific.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.
-->
Running on Fedora 26 under python3 but don't assume it to be OS-specific.

##### SUMMARY
<!--- Explain the problem briefly -->
When running playbook which uses `pause`, for example https://github.com/openshift/openshift-ansible/blob/master/roles/os_firewall/tasks/iptables.yml#L13, in environment where stdin is closed, for example in CI and test automation, the playbook fails with error like

```
TASK [os_firewall : Wait 10 seconds after disabling firewalld] *****************
Pausing for 10 seconds
(ctrl+C then 'C' = continue early, ctrl+C then 'A' = abort)
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: AttributeError: 'str' object has no attribute 'buffer'
fatal: [host.example.com]: FAILED! => {"failed": true, "msg": "Unexpected failure during module execution.", "stdout": ""}
```

##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used.
-->

<!--- Paste example playbooks or commands between quotes below -->
```yaml
- hosts: localhost
  connection: local
  gather_facts: no
  tasks:
    - pause:
        seconds: 5
```

Run as `python3 /usr/bin/ansible-playbook -i localhost, /tmp/pause.yaml < /dev/null`

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->

```

PLAY [localhost] *******************************************************************************************************************************************************

TASK [pause] ***********************************************************************************************************************************************************
Pausing for 5 seconds
(ctrl+C then 'C' = continue early, ctrl+C then 'A' = abort)
ok: [localhost]

PLAY RECAP *************************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0   

```

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes below -->
```

PLAY [localhost] ***************************************************************

TASK [pause] *******************************************************************
Pausing for 5 seconds
(ctrl+C then 'C' = continue early, ctrl+C then 'A' = abort)
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: AttributeError: 'str' object has no attribute 'buffer'
fatal: [localhost]: FAILED! => {"failed": true, "msg": "Unexpected failure during module execution.", "stdout": ""}

NO MORE HOSTS LEFT *************************************************************
	to retry, use: --limit @/tmp/pause.retry

PLAY RECAP *********************************************************************
localhost                  : ok=0    changed=0    unreachable=0    failed=1   

```

Use of pause fails with python3 when stdin is closed
<!---
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: -->
 - Bug Report

##### COMPONENT NAME
<!--- Name of the module/plugin/task/feature -->
./lib/ansible/plugins/action/pause.py

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
ansible-playbook 2.4.0.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.6/site-packages/ansible
  executable location = /usr/bin/ansible-playbook
  python version = 3.6.2 (default, Oct  2 2017, 16:51:32) [GCC 7.2.1 20170915 (Red Hat 7.2.1-2)]
```

##### CONFIGURATION
<!---
If using Ansible 2.4 or above, paste the results of "ansible-config dump --only-changed"

Otherwise, mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).

-->
Output of `python3 /usr/bin/ansible-config dump --only-changed` is empty.

##### OS / ENVIRONMENT
<!---
Mention the OS you are running Ansible from, and the OS you are
managing, or say "N/A" for anything that is not platform-specific.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.
-->
Running on Fedora 26 under python3 but don't assume it to be OS-specific.

##### SUMMARY
<!--- Explain the problem briefly -->
When running playbook which uses `pause`, for example https://github.com/openshift/openshift-ansible/blob/master/roles/os_firewall/tasks/iptables.yml#L13, in environment where stdin is closed, for example in CI and test automation, the playbook fails with error like

```
TASK [os_firewall : Wait 10 seconds after disabling firewalld] *****************
Pausing for 10 seconds
(ctrl+C then 'C' = continue early, ctrl+C then 'A' = abort)
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: AttributeError: 'str' object has no attribute 'buffer'
fatal: [host.example.com]: FAILED! => {"failed": true, "msg": "Unexpected failure during module execution.", "stdout": ""}
```

##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used.
-->

<!--- Paste example playbooks or commands between quotes below -->
```yaml
- hosts: localhost
  connection: local
  gather_facts: no
  tasks:
    - pause:
        seconds: 5
```

Run as `python3 /usr/bin/ansible-playbook -i localhost, /tmp/pause.yaml < /dev/null`

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->

```

PLAY [localhost] *******************************************************************************************************************************************************

TASK [pause] ***********************************************************************************************************************************************************
Pausing for 5 seconds
(ctrl+C then 'C' = continue early, ctrl+C then 'A' = abort)
ok: [localhost]

PLAY RECAP *************************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0   

```

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes below -->
```

PLAY [localhost] ***************************************************************

TASK [pause] *******************************************************************
Pausing for 5 seconds
(ctrl+C then 'C' = continue early, ctrl+C then 'A' = abort)
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: AttributeError: 'str' object has no attribute 'buffer'
fatal: [localhost]: FAILED! => {"failed": true, "msg": "Unexpected failure during module execution.", "stdout": ""}

NO MORE HOSTS LEFT *************************************************************
	to retry, use: --limit @/tmp/pause.retry

PLAY RECAP *********************************************************************
localhost                  : ok=0    changed=0    unreachable=0    failed=1   

```



Discussion:
cc @tbielawa
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
cc @tbielawa
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/lib/ansible/plugins/action/pause.py b/lib/ansible/plugins/action/pause.py
--- a/lib/ansible/plugins/action/pause.py
+++ b/lib/ansible/plugins/action/pause.py
@@ -144,17 +144,17 @@ def run(self, tmp=None, task_vars=None):
 
             # save the attributes on the existing (duped) stdin so
             # that we can restore them later after we set raw mode
-            if PY3:
-                stdin = self._connection._new_stdin.buffer
-            else:
-                stdin = self._connection._new_stdin
             fd = None
             try:
+                if PY3:
+                    stdin = self._connection._new_stdin.buffer
+                else:
+                    stdin = self._connection._new_stdin
                 fd = stdin.fileno()
             except (ValueError, AttributeError):
                 # ValueError: someone is using a closed file descriptor as stdin
                 # AttributeError: someone is using a null file descriptor as stdin on windoez
-                pass
+                stdin = None
             if fd is not None:
                 if isatty(fd):
                     old_settings = termios.tcgetattr(fd)


Test patch: