Repository: ansible/ansible

Issue:
Ansible Cisco IOS - Telnet
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and devel branch are affected too.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest -->
 - Bug Report

##### COMPONENT NAME
<!---
telnet module
-->

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
ansible 2.4.3.0
  config file = /home/am/test_cswt/ansible.cfg
  configured module search path = ['/home/am/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/am/env2/lib/python3.6/site-packages/ansible
  executable location = /home/am/env2/bin/ansible
  python version = 3.6.3 (default, Feb  8 2018, 12:39:01) [GCC 4.8.5 20150623 (Red Hat 4.8.5-16)]
```

##### 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).
-->
[defaults]
transport = paramiko
hostfile = ./hosts
host_key_checking = false
timeout = 5
log_path = ./ansible.log

##### 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.
-->
Linux  3.10.0-693.11.6.el7.x86_64 #1 SMP Thu Jan 4 01:06:37 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
##### SUMMARY
<!--- Explain the problem briefly -->

##### 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
---
- name: show version
  hosts: "ios_devices"
  gather_facts: false
  connection: local
  serial: 6
  tasks:
   - telnet:
      user: cisco
      password: cisco
      login_prompt: "Username: "
      prompts:
        - "[>|#]"
      command:
        - show version
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
Expect to logon to device and run command: show version
##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes below -->
```
ansible-playbook telnet_test.yml  -vvvv
ansible-playbook 2.4.3.0
  config file = /home/am/test_cswt/ansible.cfg
  configured module search path = ['/home/am/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/am/env2/lib/python3.6/site-packages/ansible
  executable location = /home/am/env2/bin/ansible-playbook
  python version = 3.6.3 (default, Feb  8 2018, 12:39:01) [GCC 4.8.5 20150623 (Red Hat 4.8.5-16)]
Using /home/am/test_cswt/ansible.cfg as config file
[DEPRECATION WARNING]: [defaults]hostfile option, The key is misleading as it can also be a list of hosts, a directory or a list of paths . This feature will be removed in version 2.8. Deprecation warnings can be disabled by setting 
deprecation_warnings=False in ansible.cfg.
setting up inventory plugins
Parsed /home/am/test_cswt/hosts inventory source with ini plugin
Loading callback plugin default of type stdout, v2.0 from /home/am/env2/lib/python3.6/site-packages/ansible/plugins/callback/default.py

PLAYBOOK: telnet_test.yml ******************************************************************************************************************************************************************************************************************
1 plays in telnet_test.yml

PLAY [show version] ************************************************************************************************************************************************************************************************************************
META: ran handlers

TASK [telnet] ******************************************************************************************************************************************************************************************************************************
task path: /home/am/test_cswt/telnet_test.yml:8
The full traceback is:
Traceback (most recent call last):
  File "/home/am/env2/lib/python3.6/site-packages/ansible/executor/task_executor.py", line 130, in run
    res = self._execute()
  File "/home/am/env2/lib/python3.6/site-packages/ansible/executor/task_executor.py", line 528, in _execute
    result = self._handler.run(task_vars=variables)
  File "/home/am/env2/lib/python3.6/site-packages/ansible/plugins/action/telnet.py", line 57, in run
    tn.read_until(login_prompt)
  File "/usr/local/lib/python3.6/telnetlib.py", line 302, in read_until
    i = self.cookedq.find(match)
TypeError: a bytes-like object is required, not 'AnsibleUnicode'
fatal: [x.x.x.x]: FAILED! => {
    "msg": "Unexpected failure during module execution.",
    "stdout": ""
}
        to retry, use: --limit @/home/xxxx/test_cswt/telnet_test.retry

PLAY RECAP *********************************************************************************************************************************************************************************************************************************
x.x.x.x              : ok=0    changed=0    unreachable=0    failed=1   

```



Discussion:
Not sure if you are aware though we have some great `ios_` modules that run over SSH that would be much better, http://docs.ansible.com/ansible/latest/ios_command_module.html

We've got some new Getting Started guide as part of Ansible 2.5 which can be found at http://docs.ansible.com/ansible/devel/network/
I'm trying to use telnet module to do Zero Touch Provisioning for Juniper vRR and also get into this issue:
```
- name: Configure management interface via telnet
  telnet:
    port: 3517
    user: root
    pause: 1
    login_prompt: 'login: '
    prompts:
      - "[>|#]"
    command:
      - cli
      - edit
      - set system services ssh
      - set system root-authentication plain-text-password
      - Juniper
      - Juniper
      - set system services netconf rfc-compliant ssh port 830
      - set intermface em0 unit 0 familt inet address 192.168.0.111/24
      - commit
```

Result
```
The full traceback is:
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/dist-packages/ansible/executor/task_executor.py", line 138, in run
    res = self._execute()
  File "/usr/local/lib/python3.6/dist-packages/ansible/executor/task_executor.py", line 576, in _execute
    result = self._handler.run(task_vars=variables)
  File "/usr/local/lib/python3.6/dist-packages/ansible/plugins/action/telnet.py", line 58, in run
    tn.read_until(login_prompt)
  File "/usr/lib/python3.6/telnetlib.py", line 302, in read_until
    i = self.cookedq.find(match)
TypeError: a bytes-like object is required, not 'AnsibleUnicode'

fatal: [192.168.0.95]: FAILED! => {
    "msg": "Unexpected failure during module execution.",
    "stdout": ""
}
````

Ansible 2.6.0
Python 3.6.5
Ubuntu 18.04

Resolving patch:
diff --git a/lib/ansible/plugins/action/telnet.py b/lib/ansible/plugins/action/telnet.py
--- a/lib/ansible/plugins/action/telnet.py
+++ b/lib/ansible/plugins/action/telnet.py
@@ -8,7 +8,7 @@
 import telnetlib
 from time import sleep
 
-from ansible.module_utils._text import to_native
+from ansible.module_utils._text import to_native, to_bytes
 from ansible.module_utils.six import text_type
 from ansible.plugins.action import ActionBase
 
@@ -64,26 +64,26 @@ def run(self, tmp=None, task_vars=None):
                 output = []
                 try:
                     if send_newline:
-                        tn.write('\n')
+                        tn.write(b'\n')
 
-                    tn.read_until(login_prompt)
-                    tn.write('%s\n' % to_native(user))
+                    tn.read_until(to_bytes(login_prompt))
+                    tn.write(to_bytes(user + "\n"))
 
                     if password:
-                        tn.read_until(password_prompt)
-                        tn.write('%s\n' % to_native(password))
+                        tn.read_until(to_bytes(password_prompt))
+                        tn.write(to_bytes(password + "\n"))
 
-                    tn.expect(prompts)
+                    tn.expect(list(map(to_bytes, prompts)))
 
                     for cmd in commands:
                         display.vvvvv('>>> %s' % cmd)
-                        tn.write('%s\n' % to_native(cmd))
-                        index, match, out = tn.expect(prompts)
+                        tn.write(to_bytes(cmd + "\n"))
+                        index, match, out = tn.expect(list(map(to_bytes, prompts)))
                         display.vvvvv('<<< %s' % cmd)
                         output.append(out)
                         sleep(pause)
 
-                    tn.write("exit\n")
+                    tn.write(b"exit\n")
 
                 except EOFError as e:
                     result['failed'] = True


Test patch: