Empty string in Ansible responses - ansible

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
]

Related

Ansible expect responses were feed twice

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

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
'

How do I pass boolean Environmental Variables to a `when` step in CircleCI?

I want to do something along the lines of
commands:
send-slack:
parameters:
condition:
type: env_var_name
steps:
- when:
# only send if it's true
condition: << parameters.condition >>
steps:
- run: # do some stuff if it's true
jobs:
deploy:
steps:
- run:
name: Prepare Message
command: |
# Do Some stuff dynamically to figure out what message to send
# and save it to success_message or failure_message
echo "export success_message=true" >> $BASH_ENV
echo "export failure_message=false" >> $BASH_ENV
- send-slack:
text: "yay"
condition: success_message
- send-slack:
text: "nay"
condition: failure_message
```
Based on this documentation, you cannot use environment variables as conditions in CircleCI. This is because the when logic is done when the configuration is processed (ie, before the job actually runs and the environment variables are set). As an alternative, I would add the logic to a separate run step (or the same initial one).
jobs:
deploy:
steps:
- run:
name: Prepare Message
command: |
# Do Some stuff dynamically to figure out what message to send
# and save it to success_message or failure_message
echo "export success_message=true" >> $BASH_ENV
echo "export failure_message=false" >> $BASH_ENV
- run:
name: Send Message
command: |
if $success_message; then
# Send success message
fi
if $failure_message; then
# Send failure message
fi
Here is a relevant ticket on the CircleCI discussion board.

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

Resources