Ansible expect responses were feed twice - ansible

My Ansible code:
- expect:
command: virsh console myguest
responses:
'Escape character is': ''
'login:': 'root'
'root#:~ #': 'cli'
'root>': 'show version'
register: myreg
- debug: msg="{{ myreg.stdout_lines }}"
Execution of the code above showed each of my responses were feed twice. See sample outputs below:
"login: root",
"Last login: Mon May 10 03:57:15 on ttyu0",
"",
"root#:~ # root",
"",
"root: Command not found.",
"root#:~ # cli",
"",
"cli",
"root> cli",
" ^",
"unknown command.",
"",
"root> show version ",
......
Although the wrong feedings were not recognized so the results were still usable, but what might be wrong that caused the double feeds?

You have an ambiguous regex:
"Last login: Mon May 10 03:57:15 on ttyu0",
for sure matches login: just with a Last prefix
You likely want:
...
responses:
'^login:': 'root'
'^root#:~ #': 'cli'
since in your examples those strings occur at the leading edge of the output.
The rest of the malformity you're encourtering may straighten itself out once you and expect are on the same page

Related

Ansible cli_command prompt not working as expected

I'm trying to do a ping command from a Cisco device. I have to use the extended ping command, I cannot specify the arguments in only one line.
This is what the router is expecting:
ping
Protocol [ip]:
Target IP address: <IP HERE>
Repeat count [5]:
Datagram size [100]:
Timeout in seconds [2]:
Extended commands [n]: y
Ingress ping [n]:
Source address or interface: <INT HERE>
DSCP Value [0]:
Type of service [0]:
Set DF bit in IP header? [no]:
Validate reply data? [no]:
Data pattern [0x0000ABCD]:
Loose, Strict, Record, Timestamp, Verbose[none]:
Sweep range of sizes [n]:
As you see, I'm only defining the target IP, the extended command flag and the source interface.
This is how my task looks like now:
- name: Run Cisco command (ping)
cli_command:
command: ping
prompt:
- "Protocol [ip]:"
- "Target IP address:"
- "Repeat count [5]:"
- "Datagram size [100]:"
- "Timeout in seconds [2]:"
- "Extended commands [n]:"
- "Ingress ping [n]:"
- "Source address or interface:"
- "DSCP Value [0]:"
- "Type of service [0]:"
- "Set DF bit in IP header? [no]:"
- "Validate reply data? [no]:"
- "Data pattern [0x0000ABCD]:"
- "Loose, Strict, Record, Timestamp, Verbose[none]:"
- "Sweep range of sizes [n]:"
answer:
- ''
- "{{ item }}"
- "{{ count }}" (I'm specifying the number of packets here but its optional, same for the size)
- "{{ size }}"
- ''
- y
- ''
- loopback1
- ''
- ''
- ''
- ''
- ''
- ''
- ''
with_lines: cat "{{ dest_file }}"
ignore_errors: yes
when: ansible_network_os == 'ios'
And this is what ansible returns:
"results":[
{
"ansible_loop_var":"item",
"item": first target here,
"changed":false,
"failed":true,
"invocation":{
"module_args":{
"sendonly":false,
"prompt":[
"Protocol [ip]",
"Target IP address",
"Repeat count [5]",
"Datagram size [100]:",
"Timeout in seconds [2]:",
"Extended commands [n]",
"Ingress ping [n]",
"Source address or interface",
"DSCP Value [0]",
"Type of service [0]",
"Set DF bit in IP header? [no]",
"Validate reply data? [no]",
"Data pattern [0x0000ABCD]",
"Loose, Strict, Record, Timestamp, Verbose[none]",
"Sweep range of sizes [n]"
],
"check_all":true,
"newline":true,
"command":"ping",
"answer":[
"",
"192.168.1.1", (first target here)
"5",
"72",
"",
"y",
"",
"loopback 1",
"",
"",
"",
"",
"",
"",
""
]
}
},
"ansible_facts":{
"discovered_interpreter_python":"/usr/bin/python"
},
**"msg":"timeout value 30 seconds reached while trying to send command: ping"**
},
{
"ansible_loop_var":"item",
**"stdout":"% Unknown protocol - \"ping\", type \"ping ?\" for help",**
"failed":false,
"changed":false,
"item":"second target here",
"invocation":{
"module_args":{
"sendonly":false,
"prompt":[
"Protocol [ip]",
"Target IP address",
"Repeat count [5]",
"Datagram size [100]:",
"Timeout in seconds [2]:",
"Extended commands [n]",
"Ingress ping [n]",
"Source address or interface",
"DSCP Value [0]",
"Type of service [0]",
"Set DF bit in IP header? [no]",
"Validate reply data? [no]",
"Data pattern [0x0000ABCD]",
"Loose, Strict, Record, Timestamp, Verbose[none]",
"Sweep range of sizes [n]"
],
"check_all":true,
"newline":true,
"command":"ping",
"answer":[
"",
"192.168.1.2", (second target here)
"5",
"72",
"",
"y",
"",
"loopback 1",
"",
"",
"",
"",
"",
"",
""
]
}
},
"stdout_lines":[
**"% Unknown protocol - \"ping\", type \"ping ?\" for help"**
]
}
First, the first of each pair of IPs always timeouts, even if its reachable. It doesn't matter if its a 30 second or 120 second timeout.
Second, why is the first prompt (protocol) taking the value "ping" instead of the value ""? "% Unknown protocol - "ping", type "ping ?" for help"
I changed the empty answer to : "", '', \n, "ip" but none worked, the result is always the same.
I changed also the prompt to: "Protocol [ip]:", "Protocol [ip]", "Protocol [ip]: ", Protocol [ip], Protocol [ip] but none worked.
The idea is that the 15 answers answer to the 15 prompts in order.
Any idea on what I'm doing wrong?

how to execute multiline command in ansible 2.9.10 in fedora 32

I want to execute a command using ansible 2.9.10 in remote machine, first I tried like this:
ansible kubernetes-root -m command -a "cat > /etc/docker/daemon.json <<EOF
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2",
"registry-mirrors":[
"https://kfwkfulq.mirror.aliyuncs.com",
"https://2lqq34jg.mirror.aliyuncs.com",
"https://pee6w651.mirror.aliyuncs.com",
"http://hub-mirror.c.163.com",
"https://docker.mirrors.ustc.edu.cn",
"https://registry.docker-cn.com"
]
}"
obviously it is not working.so I read this guide and tried like this:
- hosts: kubernetes-root
remote_user: root
tasks:
- name: add docker config
shell: >
cat > /etc/docker/daemon.json <<EOF
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2",
"registry-mirrors":[
"https://kfwkfulq.mirror.aliyuncs.com",
"https://2lqq34jg.mirror.aliyuncs.com",
"https://pee6w651.mirror.aliyuncs.com",
"http://hub-mirror.c.163.com",
"https://docker.mirrors.ustc.edu.cn",
"https://registry.docker-cn.com"
]
}
and execute it like this:
[dolphin#MiWiFi-R4CM-srv playboook]$ ansible-playbook add-docker-config.yaml
[WARNING]: Invalid characters were found in group names but not replaced, use
-vvvv to see details
ERROR! We were unable to read either as JSON nor YAML, these are the errors we got from each:
JSON: Expecting value: line 1 column 1 (char 0)
Syntax Error while loading YAML.
could not find expected ':'
The error appears to be in '/home/dolphin/source-share/source/dolphin/dolphin-scripts/ansible/playboook/add-docker-config.yaml': line 7, column 7, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
cat > /etc/docker/daemon.json <<EOF
{
^ here
is there anyway to achive this?how to fix it?
your playbook should work fine, you just have to add some indentation after the shell clause line, and change the > to |:
here is the updated PB:
---
- name: play name
hosts: dell420
gather_facts: false
vars:
tasks:
- name: run shell task
shell: |
cat > /tmp/temp.file << EOF
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2",
"registry-mirrors":[
"https://kfwkfulq.mirror.aliyuncs.com",
"https://2lqq34jg.mirror.aliyuncs.com",
"https://pee6w651.mirror.aliyuncs.com",
"http://hub-mirror.c.163.com",
"https://docker.mirrors.ustc.edu.cn",
"https://registry.docker-cn.com"
]
}
EOF
Not sure what is wrong with the ad-hoc command, i tried a few things but didnt manage to make it work.
hope these help
EDIT:
as pointed out by Zeitounator, the ad-hoc command will work if you use shell module instead of command. example:
ansible -i hosts dell420 -m shell -a 'cat > /tmp/temp.file <<EOF
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2",
"registry-mirrors":[
"https://kfwkfulq.mirror.aliyuncs.com",
"https://2lqq34jg.mirror.aliyuncs.com",
"https://pee6w651.mirror.aliyuncs.com",
"http://hub-mirror.c.163.com",
"https://docker.mirrors.ustc.edu.cn",
"https://registry.docker-cn.com"
]
}
EOF
'

Ansible - User input

If a user is prompted for some input and in this case they don't input anything and just press enter a " '' " value is put into the variable:
Example of the question:
- name: "vs_rule"
prompt: "enter the associated rule [Press enter to skip or enter in the following format: rule1 rule2]"
private: no
I'd like to use something similar to the below code but I don't want to pass the quotes onto the LTM (''), since the user will see the following error: "std exception: (basic_string::substr), exiting..."
Is there a way to filter the '' after the else and just send nothing to the LTM?
- "{{ 'tmsh list ltm rule ' + vs_rule + '' if vs_rule else '' |default([]) }}"
According to the vars_prompt documentation (here), you can add a default value for your variable, in the case the user does not enter any content.
On your case you could pass an empty string:
- name: "vs_rule"
prompt: "enter the associated rule [Press enter to skip or enter in the following format: rule1 rule2]"
private: no
default: ""
and the remaining "code" will work

Empty string in Ansible responses

I am developing RouterOS network module for Ansible 2.5.
RouterOS shell can print a few messages that should be detected in on_open_shell() event and either skipped or dismissed automatically. These are Do you want to see the software license? [Y/n]: and a few others, all of which are well documented here in the MikroTik Wiki.
Here is how I'm doing this:
def on_open_shell(self):
try:
if not prompt.strip().endswith(b'>'):
self._exec_cli_command(b' ')
except AnsibleConnectionFailure:
raise AnsibleConnectionFailure('unable to bypass license prompt')
It indeed does bypass the license prompt. However it seems that the \n response from the RouterOS device counts as a reply for the actual commands that follow. So, if I have two tasks in my playbook like this:
---
- hosts: routeros
gather_facts: no
connection: network_cli
tasks:
- routeros_command:
commands:
- /system resource print
- /system routerboard print
register: result
- name: Print result
debug: var=result.stdout_lines
This is the output I get:
ok: [example] => {
"result.stdout_lines": [
[
""
],
[
"uptime: 12h33m29s",
" version: 6.42.1 (stable)",
" build-time: Apr/23/2018 10:46:55",
" free-memory: 231.0MiB",
" total-memory: 249.5MiB",
" cpu: Intel(R)",
" cpu-count: 1",
" cpu-frequency: 2700MHz",
" cpu-load: 2%",
" free-hdd-space: 943.8MiB",
" total-hdd-space: 984.3MiB",
" write-sect-since-reboot: 7048",
" write-sect-total: 7048",
" architecture-name: x86",
" board-name: x86",
" platform: MikroTik"
]
]
}
As you can see, the output seems to be offset by 1. What should I do to correct this?
It turns out that the problem was in the regular expression that defined the shell prompt. I had it defined like this:
terminal_stdout_re = [
re.compile(br"\[\w+\#[\w\-\.]+\] ?>"),
# other cases
]
It did not match the end of prompt which caused Ansible to think that there was a newline before the actual command output. Here is the correct regexp:
terminal_stdout_re = [
re.compile(br"\[\w+\#[\w\-\.]+\] ?> ?$"),
# other cases
]

ansible expect module throwing error Module failure Parse: failed

my playbook is like
- name: configuring pdf harmony
expect:
command: ./install.sh
responses:
'This will install pdfharmony on your system. Do you want to continue[y/N]?': y
'Do you agree with this copyright? [y/N]': y
'Now you must enter a valid registration number:': XXXX-XXXX-XXXX-XXXX-XXXX
echo: yes
args:
chdir: '{{ dest_dir }}/pdfHarmony_20_L26_64'
become: yes
Error:
fatal: [10.135.232.213]: FAILED! => {"changed": true, "cmd": "./install.sh",
"delta": "0:00:30.186764", "end": "2016-08-12 14:02:23.384237", "failed":
true, "invocation": {"module_args": {"chdir":
"/usr/local/pdfHarmony_20_L26_64", "command": "./
mounted or mapped drive. You may not copy the software to use for ",
"development, testing or staging purposes.", "", "1. Restrictions: You may
not resell, transfer, rent or lease the Software ", "without the specific
agreement of Appligent. You may not reverse engineer, ", "\u001b[7m--More--
\u001b[27m"]}
I could not understand the cause of this error
when installing manually,
Here when you pass y to the first prompt, you get a copyright agreement which is more than a page and you will find --More-- at the end of the page and when keep on pressing enter last line of the agreement the second prompt(question appears) like
when you press y for 1st prompt you see this
then you press more
after pressing more at last you see this
then it asks for reg. keys
Update:
it is like not listening to my next response after more.. i am not able to understand how this is happening
it is not taking my response for
'Do you agree with this copyright? [y/N]': y
i have added more and now it looks like
- name: configuring pdf harmony
expect:
command: ./install.sh
responses:
'This will install pdfharmony on your system. Do you want to continue[y/N]?': y
'--More--': \r
'(?i)Do you agree with this copyright? [y/N]': y
'Now you must enter a valid registration number': XXXX-XXXX-XXXX-XXXX-XXXX
echo: yes
args:
chdir: '{{ dest_dir }}/pdfHarmony_20_L26_64'
become: yes
But now it fails at next response it doesn't take the next response and i don't know why it fails at:
"\u001b[7m--More--\u001b[27m\u0007\u0007", "Updates may be licensed to you
by Appligent with additional or different terms.", "", "Do you agree with
this copyright? [y/N] : "]}
There is a new error with the module and i can make nothing out of it, ichanghed the play to :
- name: configuring stamp pdf batch
expect:
command: /bin/bash -c "stty rows 2000,2000; ./install.sh"
responses:
'This will install stamppdf on your system. Do you want to continue[y/N]?': y
# '--More--': \r
'(?i)Do you agree with this copyright\? \[y/N\]': y
'AP_FONT_DIR [/usr/local/fonts]': y
'Now you must enter a valid registration number': '{{ stamp_pdf_key }}'
echo: yes
args:
chdir: '{{ dest_dir }}/StampPDFBatch_60_L26_64'
become: yes
the error is:
fatal: [10.135.232.213]: FAILED! => {"changed": false, "failed": true,
"invocation": {"module_name": "expect"}, "module_stderr": "",
"module_stdout": "Traceback (most recent call last):\r\n File
\"/tmp/ansible_V_modk/ansible_module_expect.py\", line 230, in <module>\r\n
main()\r\n File \"/tmp/ansible_V_modk/ansible_module_expect.py\", line 199,
in main\r\n events=events, cwd=chdir, echo=echo)\r\n File
\"/usr/lib/python2.6/site-packages/pexpect/__init__.py\", line 225, in
runu\r\n env=env, _spawn=spawnu, **kwargs)\r\n File
\"/usr/lib/python2.6/site-packages/pexpect/__init__.py\", line 246, in
_run\r\n index = child.expect(patterns)\r\n File
\"/usr/lib/python2.6/site-packages/pexpect/__init__.py\", line 1451, in
expect\r\n timeout, searchwindowsize)\r\n File
\"/usr/lib/python2.6/site-packages/pexpect/__init__.py\", line 1466, in
expect_list\r\n timeout, searchwindowsize)\r\n File
\"/usr/lib/python2.6/site-packages/pexpect/__init__.py\", line 1535, in
expect_loop\r\n c = self.read_nonblocking(self.maxread, timeout)\r\n
File \"/usr/lib/python2.6/site-packages/pexpect/__init__.py\", line 984, in
read_nonblocking\r\n s = self._coerce_read_string(s)\r\n File
\"/usr/lib/python2.6/site-packages/pexpect/__init__.py\", line 1797, in
_coerce_read_string\r\n return self._decoder.decode(s, final=False)\r\n
File \"/usr/lib64/python2.6/codecs.py\", line 296, in decode\r\n
(result, consumed) = self._buffer_decode(data, self.errors,
final)\r\nUnicodeDecodeError: 'utf8' codec can't decode byte 0xd2 in
position 1166: invalid continuation byte\r\n", "msg": "MODULE FAILURE",
"parsed": false}
You can try to alter TTY height and avoid 'More'-thing:
command: /bin/bash -c "stty rows 2000,2000; ./install.sh"

Resources