Ansible cli_command prompt not working as expected - ansible

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?

Related

Filtering out a value from a command_output with mixed format

Following is the output from an ansible task
"msg": [
"Executing script ment.sh with following arguments",
"Arguments passed : userName=root hostName=007 passWord=123 payload=/tmp/007.json.19.18 adminpass=abc",
"spawn ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root#007",
"Warning: Permanently added '007' (ECDSA) to the list of known hosts.",
"",
"",
"root#007's password: ",
"Last login: Thu Feb 9 17:52:48 2023 from 1.1.1.1",
"",
"\u001b]0;root#oak:~\u0007\u001b[?1034h[root#oak ~]# ",
"Second try on password",
"createdocker -j -r /tmp/json.19.18",
"Enter an initial password for Web Console account (admin):",
"Retype the password for Web Console account (admin):",
"User 'admin' created successfully...",
"{",
" \"jobId\" : \"dd370bce-03d7-4436-93b4-9a9ed7658394\",",
" \"status\" : \"Created\",",
" \"message\" : null,",
" \"reports\" : [ ],",
" \"createTimestamp\" : \"February 09, 2023 17:53:29 PM UTC\",",
" \"resourceList\" : [ ],",
" \"description\" : \"Provisioning service creation\",",
" \"updatedTime\" : \"February 09, 2023 17:53:29 PM UTC\",",
" \"jobType\" : null",
"}",
"",
"Successfully created provisioning job",
"exit",
"\u001b]0;root#oak:~\u0007[root#oak ~]# exit",
"logout",
"Connection to closed."
]
Can someone please help me get the jobId value i.e. "dd370bce-03d7-4436-93b4-9a9ed7658394" into a variable
I got it:
- name: Store jobId in a variable
debug:
msg: "{{ the_output | select('match','^.*\"jobId\"\\s*:\\s*\"(.*)\".*$') | regex_replace('jobId\" :', '') | regex_replace('[^a-zA-Z0-9-]', '') }}"
Ansible returns:
dd370bce-03d7-4436-93b4-9a9ed7658394

How can I get the private IP put into conf file ansible?

I have this role task file in roles/make_elasticsearch_conf/tasks/main.yml:
---
# tasks file for make_elasticsearch_conf
#
- name: Get private IP address
command:
cmd: "hostname -I | awk '{print $2}'"
register: "cluster_ip"
- name: Create /etc/elasticsearch/elasticsearch.yml File
ansible.builtin.template:
src: elasticsearch.yml.j2
dest: /etc/elasticsearch/elasticsearch.yml
I also have a template in roles/make_elasticsearch_conf/templates/elasticsearch.yml.j2:
cluster.name: {{ ansible_host }}
node.name: {{ ansible_host }}
network.host: {{ cluster_ip }}
I use it in this make_elastic_search_conf.yml playbook:
---
- name: Make Elastic Search Config.
hosts: all
become: True
gather_facts: True
roles:
- roles/make_elasticsearch_conf
When I run my playbook I get this error:
FAILED! => {"changed": true, "cmd": ["hostname", "-I", "|", "awk", "{print $2}"], "delta": "0:00:00.006257", "end": "2022-12-06 21:54:47.612238", "msg": "non-zero return code", "rc": 255, "start": "2022-12-06 21:54:47.605981", "stderr": "Usage: hostname [-b] {hostname|-F file} set host name (from file)\n hostname [-a|-A|-d|-f|-i|-I|-s|-y] display formatted name\n hostname display host name\n\n {yp,nis,}domainname {nisdomain|-F file} set NIS domain name (from file)\n {yp,nis,}domainname display NIS domain name\n\n dnsdomainname display dns domain name\n\n hostname -V|--version|-h|--help print info and exit\n\nProgram name:\n {yp,nis,}domainname=hostname -y\n dnsdomainname=hostname -d\n\nProgram options:\n -a, --alias alias names\n -A, --all-fqdns all long host names (FQDNs)\n -b, --boot set default hostname if none available\n -d, --domain DNS domain name\n -f, --fqdn, --long long host name (FQDN)\n -F, --file read host name or NIS domain name from given file\n -i, --ip-address addresses for the host name\n -I, --all-ip-addresses all addresses for the host\n -s, --short short host name\n -y, --yp, --nis NIS/YP domain name\n\nDescription:\n This command can get or set the host name or the NIS domain name. You can\n also get the DNS domain or the FQDN (fully qualified domain name).\n Unless you are using bind or NIS for host lookups you can change the\n FQDN (Fully Qualified Domain Name) and the DNS domain name (which is\n part of the FQDN) in the /etc/hosts file.", "stderr_lines": ["Usage: hostname [-b] {hostname|-F file} set host name (from file)", " hostname [-a|-A|-d|-f|-i|-I|-s|-y] display formatted name", " hostname display host name", "", " {yp,nis,}domainname {nisdomain|-F file} set NIS domain name (from file)", " {yp,nis,}domainname display NIS domain name", "", " dnsdomainname display dns domain name", "", " hostname -V|--version|-h|--help print info and exit", "", "Program name:", " {yp,nis,}domainname=hostname -y", " dnsdomainname=hostname -d", "", "Program options:", " -a, --alias alias names", " -A, --all-fqdns all long host names (FQDNs)", " -b, --boot set default hostname if none available", " -d, --domain DNS domain name", " -f, --fqdn, --long long host name (FQDN)", " -F, --file read host name or NIS domain name from given file", " -i, --ip-address addresses for the host name", " -I, --all-ip-addresses all addresses for the host", " -s, --short short host name", " -y, --yp, --nis NIS/YP domain name", "", "Description:", " This command can get or set the host name or the NIS domain name. You can", " also get the DNS domain or the FQDN (fully qualified domain name).", " Unless you are using bind or NIS for host lookups you can change the", " FQDN (Fully Qualified Domain Name) and the DNS domain name (which is", " part of the FQDN) in the /etc/hosts file."], "stdout": "", "stdout_lines": []}
I have tried all sorts of ways to get the private ip of the host but nothing I have tried gave the expected result.
The problem is caused by |.
As per the documentation of the command module:
If you want to run a command through the shell (say you are using <,> >, |, and so on), you actually want the ansible.builtin.shell module instead. Parsing shell metacharacters can lead to unexpected commands
being executed if quoting is not done correctly so it is more secure
to use the command module when possible.
You might want to use shell module. Or better get the IP address from the ansible facts.

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 Tower server claims it was sent a bad request

I have written a playbook to create credentials with custom credentials, below is the ansible playbook
---
- name: Trigger an Atower API
hosts: localhost
connection: local
tasks:
- name: Create a valid SCM credential from a private_key file
command: tower-cli credential create --organization "Default" --name "DevOps User" --credential-type "csa-test2" --inputs "{'user':'devops', 'stg01_ssh_key':\"$( sed -z 's/\n/\\n/g' test.pem )\" }"
no_log: false
I am getting this error related to quotaions
name", "DevOps User", "--credential-type", "csa-test2", "--inputs", "{'user':'devops', 'stg01_ssh_key':\"$( sed -z 's/\\n/\\n/g' test.pem )\" }"], "delta": "0:00:01.319114", "end": "2021-01-07 16:00:22.763388", "msg": "non-zero return code", "rc": 40, "start": "2021-01-07 16:00:21.444274", "stderr": "Error: The Tower server claims it was sent a bad request.\n\nPOST http://x.x.x.x:13080/api/v2/credentials/\nParams: None\nData: {\"inputs\": {\"stg01_ssh_key\": \"$( sed -z 's/\\n/\\n/g' test.pem )\", \"user\": \"devops\"}, \"credential_type\": 36, \"organization\": 1, \"name\": \"DevOps User\"}\n\nResponse: {\"inputs\":{\"stg01_ssh_key\":[\"Invalid certificate or key: $( sed -z 's/\\n/\\n/g' test.pem )...\"]}}", "stderr_lines": ["Error: The Tower server claims it was sent a bad request.", "", "POST http://x.x.x.x:13080/api/v2/credentials/", "Params: None", "Data: {\"inputs\": {\"stg01_ssh_key\": \"$( sed -z 's/\\n/\\n/g' test.pem )\", \"user\": \"devops\"}, \"credential_type\": 36, \"organization\": 1, \"name\": \"DevOps User\"}", "", "Response: {\"inputs\":{\"stg01_ssh_key\":[\"Invalid certificate or key: $( sed -z 's/\\n/\\n/g' test.pem )...\"]}}"], "stdout": "", "stdout_lines": []}
Also, when i try manually "tower-cli create" command, it is working fine. Can anyone help me in this. I dunno what is wrong here.
try to use shell instead of command, documentation says
The command(s) will not be processed through the shell, so variables like $HOSTNAME and operations like "*", "<", ">", "|", ";" and "&" will not work. Use the ansible.builtin.shell module if you need these features.
source: Ansible Documentation

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
]

Resources