I’m learning Ansible by reading the official document and some books like Ansible入门. Most of the instructions show you how to deploy or maintain the services on Linux, or file operations. But what I do is trying to explore its capability on network device management automation.
The most important thing to practice routers/switches/routing protocol is accessing the device and configure it via CLI when I started to learn network, so I start my ansible tour by accessing the network device from CLI too.
I build a simple lab on EVE for testing: a Cios IOS switch for telnet access, a Cisco IOS router for SSH access.
then install Python, ansible on macOS High Sierra (it should be same on Linux, windows), here is the version info:
ansible 2.6.3
config file = None
configured module search path = ['/Users/kz/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /Users/kz/Documents/C/ansibleprj/venv/lib/python3.7/site-packages/ansible
executable location = /Users/kz/Documents/C/ansibleprj/venv/bin/ansible
python version = 3.7.0 (v3.7.0:1bf9cc5093, Jun 26 2018, 23:26:24) [Clang 6.0 (clang-600.0.57)]
Ansible is designed to running over SSH, but there is a module: telnet extends its capbility to access network device via Telnet.
Part 1
1. Enable Telnet on device
start device: SW on eve, configure IP, enable telnet from line vty, set username/password: test/test123
2. new inventory_telnet.yml
---
telnet:
hosts:
sw1:
ansible_host: "192.168.1.100"
Note: I use YAML format (ini format should also work) because Misleading documentation, it shows that you can store vaulted passwords in INI files but it does not work #43897
3. new playbook: pb_telnet.yml
---
- name: show version
hosts: telnet
gather_facts: false
connection: local
tasks:
- name: telnet sw
telnet:
user: test
password: test123
login_prompt: "Username: "
prompts:
- '[>|#]'
command:
- terminal length 0
- show version
The playbook will connect to the device: sw, and run command show version
4.run playbook
Run the task by the command:
ansible-playbook -i inventory_telnet.yml pb_telnet.yml
5. Debugging
Unfortunately, the task failed to run:
(venv) KZs-MacBook-Pro:pys kz$ ansible-playbook -i inventory_telnet.yml pb_telnet.yml
PLAY [show version] ***********************************************************************************************************************************
TASK [telnet sw] **************************************************************************************************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: TypeError: argument should be integer or bytes-like object, not 'AnsibleUnicode'
fatal: [sw1]: FAILED! => {"msg": "Unexpected failure during module execution.", "stdout": ""}
to retry, use: --limit @/Users/kz/Documents/C/ansibleprj/pys/pb_telnet.retry
PLAY RECAP ********************************************************************************************************************************************
sw1 : ok=0 changed=0 unreachable=0 failed=1
Debugging it by the command:
ansible-playbook -i inventory_telnet.yml pb_telnet.yml -vvv
get the full stack:
task path: /Users/kz/Documents/C/ansibleprj/pys/pb_telnet.yml:8
The full traceback is:
Traceback (most recent call last):
File "/Users/kz/Documents/C/ansibleprj/venv/lib/python3.7/site-packages/ansible/executor/task_executor.py", line 139, in run
res = self._execute()
File "/Users/kz/Documents/C/ansibleprj/venv/lib/python3.7/site-packages/ansible/executor/task_executor.py", line 577, in _execute
result = self._handler.run(task_vars=variables)
File "/Users/kz/Documents/C/ansibleprj/venv/lib/python3.7/site-packages/ansible/plugins/action/telnet.py", line 69, in run
tn.read_until(login_prompt)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/telnetlib.py", line 302, in read_until
i = self.cookedq.find(match)
TypeError: argument should be integer or bytes-like object, not 'AnsibleUnicode'
fatal: [sw1]: FAILED! => {
"msg": "Unexpected failure during module execution.",
"stdout": ""
}
to retry, use: --limit @/Users/kz/Documents/C/ansibleprj/pys/pb_telnet.retryPLAY RECAP ********************************************************************************************************************************************
sw1 : ok=0 changed=0 unreachable=0 failed=1
Searched on Google and find it’s a known issue in current version:Ansible [Cisco IOS – Telnet](Ansible Cisco IOS – Telnet), what I did is copy the latest telnet.py from telnet action plugin type error #43609 to my ansible (Share my path for reference: /Users/kz/Documents/C/ansibleprj/venv/lib/python3.7/site-packages/ansible/plugins/action/telnet.py).
Run the playbook again:
ansible-playbook -i inventory_telnet.yml pb_telnet.yml
everything works as expected:
PLAY [show version] ***********************************************************************************************************************************
TASK [telnet sw] **************************************************************************************************************************************
changed: [sw1]
PLAY RECAP ********************************************************************************************************************************************
sw1 : ok=1 changed=1 unreachable=0 failed=0
Part 2
A minor Improvement
I still don’t want to save the password in cleartext as what I did in Part 1 (the password was defined in the playbook).
But I find it will only retrieve the password form playbook and won’t try the credential which was defined in inventory file after I went through telnet.py:
so, refer to other modules, I made some change: ansible-telnet so that that telnet.py can retrieve the info from the inventory file. update the inventory with the telnet.py:
---
telnet:
hosts:
sw1:
ansible_host: "192.168.1.100"
connection: local
ansible_ssh_user: test
ansible_ssh_pass: test123
ansible_ssh_timeout: 2
ansible_ssh_port: 23
then playbook file:
---
- name: show version
hosts: telnet
gather_facts: false
# connection: local
tasks:
- name: CM via telnet
telnet:
login_prompt: "Username: "
prompts:
- '[>|#]'
command:
- terminal length 0
- show version
Verify the change by the same cmd:
ansible-playbook -i inventory_telnet.yml pb_telnet.yml
encrypt the password
password can be encrypted by “ansible-vault” command:
ansible-vault encrypt_string --vault-id test@prompt 'test123' --name 'ansible_ssh_pass'
I use the password: test123 to encrypt the string.
Update the hashed string to inventory file:
---
telnet:
hosts:
sw1:
ansible_host: "192.168.1.100"
connection: local
ansible_ssh_user: test
ansible_ssh_pass: !vault |
$ANSIBLE_VAULT;1.2;AES256;test
63386332356237643731346539336262336231343432313963376438653933323737636535383365
3562383633646261653739343536386566323462323063320a613638373439363032353137343330
34306264613932323832373532636230323730626239393564326564303563356666343734633135
6664373266663238660a363666336661353364393437356433616462346331313537623430393861
3536
ansible_ssh_timeout: 2
ansible_ssh_port: 23
Verify it by the command:
ansible-playbook -i inventory_telnet.yml --vault-id test@prompt pb_telnet.yml
Part 3
Update1 (20180916)
Thanks for Stefan P’s comment about the command: “show version | include uptime”. Tried it and find that the strings after ‘|’ were cutted:
Read the code
- line 80 of telnet.py:
tn.expect(list(map(to_bytes, prompts)))
- line 652 and 654 of telnet.py:
return self._expect_with_poll(list, timeout)
returnself._expect_with_select(list, timeout)
- line 667 and line 731 of telnet.py:
list[i] = re.compile(list[i])
prompts of playbook actually work as the regular expression match, this is why “|” in ansible official sample because ‘|’ works for either ‘>’ or ‘ #’. Since [] in Python RE works as a single character match, remove “|” from playbook can work:
---
- name: show version
hosts: telnet
gather_facts: false
# connection: local
tasks:
- name: CM via telnet
telnet:
login_prompt: "Username: "
prompts:
- '[>#]'
command:
- terminal length 0
- show version | include uptime
- show version | include Processor board ID
- exit
Output:
Scan the QR code using WeChat