Below are couple of IP addresses and their telnet response (output)
telnet 10.9.9.112 22
Trying 10.9.9.112......
telnet 10.9.9.143 22
Trying 10.9.9.143.....
telnet: connect to address 10.9.9.143: Connection refused.
For the first IP 10.9.9.112 there is no connection and firewall blocks any connection from source to destination. The output simply says Trying .... and stays that way without printing anything else.
For the second IP 10.9.9.143 i get Connection refused immediately in the output and the control back to the prompt.
I wish to grab both scenarios in when condition and perform different activities for both the cases.
I tried to use Ansible's telnet module but I don't know how to grab both the different outputs in the registered variable.
In my case it prints the same message for both the IPs.
Ansible output for first ip:
TASK [debug] *************************************
ok: [localhost] => {
"msg": "HERE:{u'msg': u'Timeout when waiting for 10.9.9.112', u'failed': True, 'changed': False, u'elapsed': 4}"
Ansible Output for second ip:
TASK [debug] *************************************
ok: [localhost] => {
"msg": "HERE:{u'msg': u'Timeout when waiting for 10.9.9.143', u'failed': True, 'changed': False, u'elapsed': 3}"
The only difference I see is the value for elapsed.
Here is my playbook.
---
- name: "Play 1"
hosts: localhost
tasks:
- wait_for:
hosts: "{{ item }}"
port: 22
state: started
delay: 3
timeout: 90
ignore_errors: yes
register: telnetout
loop:
- 10.9.9.112
- 10.9.9.143
- debug:
msg: "HERE: {{ telnetout }}"
telnet module unfortunately does not record Connection Refused message in the output.
We have to use raw module instead like below.
---
- name: "Play 1"
hosts: localhost
tasks:
- raw: "timeout --signal=9 2 telnet {{ item }} 22"
ignore_errors: yes
register: telnetout
loop:
- 10.9.9.112
- 10.9.9.143
- debug:
msg: "HERE: {{ telnetout }}"
Related
I am executing a PS script on a windows host and want to store its stdout in a file on an ansible local machine. I have a playbook like following:
---
- name: Check Antivirus software
hosts: all
become: false
gather_facts: no
tasks:
- name: Get AV details
win_shell: |
echo "script printing data on stdout"
register: result
- name: save response
copy:
content: '{{ result.stdout }}'
dest: '{{ response_file }}'
delegate_to: localhost
From the above playbook, 1st task gets executed without any issues. But 2nd task gives the following error.
TASK [save response] *******************************************************************************************************************************************
fatal: [20.15.102.192 -> localhost]: UNREACHABLE! => {"changed": false, "msg": "ntlm: HTTPSConnectionPool(host='localhost', port=5986): Max retries exceeded with url: /wsman (Caused by NewConnectionError('<urllib3.connection.VerifiedHTTPSConnection object at 0x7f4940760208>: Failed to establish a new connection: [Errno 111] Connection refused',))", "unreachable": true}
I also tried local_action which is also giving the same error.
I trigger multiple Tomcat startup scripts and then need to check if all process listens on their specific port across multiple hosts in the quickest time possible.
For the test case, I m writing 3 scripts that run on a single host and listen on ports 4443, 4445, 4447 respectively as below.
/tmp/startapp1.sh
while test 1 # infinite loop
sleep 10
do
nc -l localhost 4443 > /tmp/app1.log
done
/tmp/startapp2.sh
while test 1 # infinite loop
sleep 30
do
nc -l localhost 4445 > /tmp/app2.log
done
/tmp/startapp3.sh
while test 1 # infinite loop
sleep 20
do
nc -l localhost 4447 > /tmp/app3.log
done
Below is my code to trigger the script and check if the telnet is successful:
main.yml
- include_tasks: "internal.yml"
loop:
- /tmp/startapp1.sh 4443
- /tmp/startapp2.sh 4445
- /tmp/startapp3.sh 4447
internal.yml
- shell: "{{ item.split()[0] }}"
async: 600
poll: 0
- name: DEBUG CHECK TELNET
shell: "telnet {{ item.split()[1] }}"
delegate_to: localhost
register: telnetcheck
until: telnetcheck.rc == 0
async: 600
poll: 0
delay: 6
retries: 10
- name: Result of TELNET
async_status:
jid: "{{ item.ansible_job_id }}"
register: _jobs
until: _jobs.finished
delay: 6
retries: 10
with_items: "{{ telnetcheck.results }}"
To run: ansible-playbook main.yml
Requirement: the above three scripts should start along with telnet check in about 30 seconds.
Thus, the basic check that needs to be done here is telnet until: telnetcheck.rc == 0 but due to async the telnet shell module does not have entries for rc and hence I get the below error:
"msg": "The conditional check 'telnetcheck.rc == 0' failed. The error was: error while evaluating conditional (telnetcheck.rc == 0): 'dict object' has no attribute 'rc'"
In the above code where and how can I check if telnet had succeeded i.e telnetcheck.rc == 0 and make sure the requirement is met?
Currently I am not aware a solution with which one could start a shell script and wait for a status of it in one task. It might be possible to just change the shell script according the necessary behavior and let it provide self checks and exit codes. Or you could implement two or more tasks, whereby one is executing the shell script and the others later check on certain conditions.
Regarding your requirement
wait until telnet localhost 8076 is LISTENING (successful).
you may have a look into the module wait_for.
---
- hosts: localhost
become: false
gather_facts: false
tasks:
- name: "Test connection to local port"
wait_for:
host: localhost
port: 8076
delay: 0
timeout: 3
active_connection_states: SYN_RECV
check_mode: false # because remote module (wait_for) does not support it
register: result
- name: Show result
debug:
msg: "{{ result }}"
Further Q&A
How to use Ansible module wait_for together with loop?
Firewall Functional Test
An other approach of testing from Control Node on Remote Node if there is a LISTENER on localhost could be
---
- hosts: test.example.com
become: true
gather_facts: false
vars:
PORT: "8076"
tasks:
- name: "Check for LISTENER on remote localhost"
shell:
cmd: "lsof -Pi TCP:{{ PORT }}"
changed_when: false
check_mode: false
register: result
failed_when: result.rc != 0 and result.rc != 1
- name: Report missing LISTENER
debug:
msg: "No LISTENER on PORT {{ PORT }}"
when: result.rc == 1
Using an asynchronous action and an until in the same task makes nearly no sense.
As for your requirement to have the answer in the quickest time possible, you will have to rethink it through. With your three ports case, if you want them all to be opened before you move on the task, it will always be as slow as the slowest port to open, no matter what. Even if the first we probe is indeed the slowest, the two other will then probe in no time, so, trying to optimise it in an async is, to my point of view, an unnecessary optimisation.
Either you want to use until, and then each port probe would be stuck until they answer, or you want to run them asynchronously and the async_status will catch the return as it should if you wrap the telnet in a shell until loop.
In your until loop, the issue is that the return code won't be set until the command does indeed return, so you just have to check if the rc key of the dictionary is defined.
Mind that for all the examples below, I am manually opening port with nc -l -p <port>, this is why they do gradually open.
With until:
- shell: "telnet localhost {{ item.split()[1] }}"
delegate_to: localhost
register: telnetcheck
until:
- telnetcheck.rc is defined
- telnetcheck.rc == 0
delay: 6
retries: 10
This will yield:
TASK [shell] *****************************************************************
FAILED - RETRYING: [localhost]: shell (10 retries left).
changed: [localhost] => (item=/tmp/startapp1.sh 4443)
FAILED - RETRYING: [localhost]: shell (10 retries left).
changed: [localhost] => (item=/tmp/startapp2.sh 4445)
FAILED - RETRYING: [localhost]: shell (10 retries left).
changed: [localhost] => (item=/tmp/startapp3.sh 4447)
With async:
- shell: "until telnet 127.0.0.1 {{ item.split()[1] }}; do sleep 2; done"
delegate_to: localhost
register: telnetcheck
async: 600
poll: 0
- async_status:
jid: "{{ item.ansible_job_id }}"
register: _jobs
until: _jobs.finished
delay: 6
retries: 10
loop: "{{ telnetcheck.results }}"
loop_control:
label: "{{ item.item }}"
This will yield:
TASK [shell] *****************************************************************
changed: [localhost] => (item=/tmp/startapp1.sh 4443)
changed: [localhost] => (item=/tmp/startapp2.sh 4445)
changed: [localhost] => (item=/tmp/startapp3.sh 4447)
TASK [async_status] **********************************************************
FAILED - RETRYING: [localhost]: async_status (10 retries left).
changed: [localhost] => (item=/tmp/startapp1.sh 4443)
FAILED - RETRYING: [localhost]: async_status (10 retries left).
changed: [localhost] => (item=/tmp/startapp2.sh 4445)
FAILED - RETRYING: [localhost]: async_status (10 retries left).
changed: [localhost] => (item=/tmp/startapp3.sh 4447)
This said, you have to seriously consider #U880D's answer, as this is a more native answer for Ansible:
- wait_for:
host: localhost
port: "{{ item.split()[1] }}"
delay: 6
timeout: 60
This will yield:
TASK [wait_for] **************************************************************
ok: [localhost] => (item=/tmp/startapp1.sh 4443)
ok: [localhost] => (item=/tmp/startapp2.sh 4445)
ok: [localhost] => (item=/tmp/startapp3.sh 4447)
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 10 months ago.
Improve this question
I am new to ansible and need help here.
There is a file with 500+ remote_host:port line entries like below.
remote_host1:port1
remote_host2:port2
remote_host3:port1
Using ansible how to loop over the lines from file, split the lines with 2 variables remot_host and port, login to remote_host and using nc -k -l port start listening on the port, verify connectivity nc -vz remote_host port from a given host, kill the nc command from remote host.
So gar I have used wait_for module to verify connectivity of single remote_host and its port.
For example, given the file
shell> cat remote-hosts.txt
test_11:22
test_12:22
test_13:80
Use the module wait_for. The playbook below
- hosts: localhost
tasks:
- block:
- wait_for:
host: "{{ item.split(':').0 }}"
port: "{{ item.split(':').1|int }}"
timeout: 5
loop: "{{ lookup('file', 'remote-hosts.txt').splitlines() }}"
rescue:
- debug:
msg: "{{ ansible_failed_result.results|selectattr('failed') }}"
gives (abridged)
TASK [wait_for] ******************************************************************************
ok: [localhost] => (item=test_11:22)
ok: [localhost] => (item=test_12:22)
failed: [localhost] (item=test_13:80) => changed=false
ansible_loop_var: item
elapsed: 6
item: test_13:80
msg: Timeout when waiting for test_13:80
TASK [debug] *********************************************************************************
ok: [localhost] =>
msg:
- ansible_loop_var: item
changed: false
elapsed: 6
failed: true
invocation:
module_args:
active_connection_states:
- ESTABLISHED
- FIN_WAIT1
- FIN_WAIT2
- SYN_RECV
- SYN_SENT
- TIME_WAIT
connect_timeout: 5
delay: 0
exclude_hosts: null
host: test_13
msg: null
path: null
port: 80
search_regex: null
sleep: 1
state: started
timeout: 5
item: test_13:80
msg: Timeout when waiting for test_13:80
Is this the proper behavior that task with "delegate_to: "localhost"" is trying to ssh "localhost" and not from the "localhost" (ansible master) to the remote's ssh port?
Playbook fails on:
"Timeout when waiting for localhost:2022"
Here example configuration I reproduce it with:
Inventory file:
[testremote]
192.168.170.113 ansible_user=ja
Ansible config file:
[defaults]
host_key_checking = False
inventory = hosts
callback_enabled = profile_tasks
ansible_port = 2022
Playbook file:
- hosts: testremote
gather_facts: false
vars:
desired_port: 2022
tasks:
- name: check if ssh is running on {{ desired_port }}
delegate_to: localhost
wait_for:
port: "{{ desired_port }}"
host: "{{ ansible_host }}"
timeout: 10
ignore_errors: true
register: desired_port_check
- when: desired_port_check is success
block:
- debug:
msg: "ssh is running on desired port"
- name: configure ansible to use port {{ desired_port }}
set_fact:
ansible_port: "{{ desired_port }}"
- name: run a command on the target host
command: uptime
register: uptime
- debug:
msg: "{{ uptime.stdout }}"
Remote host is accessible on desired port already:
[ansible]$ ssh -p 2022 ja#testremote date
Sun Jun 20 16:40:36 CEST 2021
[ansible]$ ping testremote
PING testremote (192.168.170.113) 56(84) bytes of data.
64 bytes from testremote (192.168.170.113): icmp_seq=1 ttl=63 time=1.14 ms
And result when playbook is run:
[ansible]$ ansible-playbook test_playbook.yml
PLAY [testremote] **********************************************************************************************************************************************************
TASK [check if ssh is running on 2022] *************************************************************************************************************************************
fatal: [192.168.170.113 -> localhost]: FAILED! => {"changed": false, "elapsed": 10, "msg": "Timeout when waiting for localhost:2022"}
...ignoring
TASK [debug] ***************************************************************************************************************************************************************
skipping: [192.168.170.113]
TASK [configure ansible to use port 2022] **********************************************************************************************************************************
skipping: [192.168.170.113]
TASK [run a command on the target host] ************************************************************************************************************************************
fatal: [192.168.170.113]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host 192.168.170.113 port 22: Connection refused", "unreachable": true}
PLAY RECAP *****************************************************************************************************************************************************************
192.168.170.113 : ok=1 changed=0 unreachable=1 failed=0 skipped=2 rescued=0 ignored=1
I get list of IP address in test.text file from which I am trying to get the IP in loop
and then try to get in group or variable and use it as hosts (dynamic_groups)
Below is my playlist
---
- name: provision stack
hosts: localhost
connection: local
gather_facts: no
serial: 1
tasks:
- name: Get Instance IP Addresses From File
shell: cat /home/user/test.text
register: serverlist
- debug: msg={{ serverlist.stdout_lines }}
- name: Add Instance IP Addresses to temporary inventory groups
add_host:
groups: dynamic_groups
hostname: "{{item}}"
with_items: serverlist.stdout_lines
- hosts: dynamic_groups
become: yes
become_user: root
become_method: sudo
gather_facts: True
serial: 1
vars:
ansible_connection: "{{ connection_type }}"
ansible_ssh_user: "{{ ssh_user_name }}"
ansible_ssh_private_key_file: "{{ ssh_private_key_file }}"
tasks:
.....
.....
After running above playbbok I am getting below error
TASK [debug] *****************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
"192.168.1.10",
"192.168.1.11",
"192.168.1.50"
]
}
TASK [Add Instance IP Addresses to temporary inventory groups] ***************************************************************************************************************************************************************************
changed: [localhost] => (item=serverlist.stdout_lines)
PLAY [dynamic_groups] *********************************************************************************************************************************************************************************************************************
TASK [Some Command] **********************************************************************************************************************************************************************************************************************
fatal: [serverlist.stdout_lines]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: Could not resolve hostname serverlist.stdout_lines: Name or service not known", "unreachable": true}
What Am I missing here?
Below is correct way to use variable
- name: Add Instance IP Addresses to temporary inventory groups
add_host:
groups: working_hosts
hostname: "{{item}}"
with_items: "{{ serverlist.stdout_lines }}"
It should solve your problem.
As reported in fatal error message "Failed to connect to the host via ssh: ssh: Could not resolve hostname serverlist.stdout_lines", it is trying to connect to "serverlist.stdout_lines", not to a valid IP.
This is caused by an error when passing variable to with_items. In your task:
with_items: serverlist.stdout_lines
it is passing serverlist.stdout_lines string and not its value.
With_items requires variable definition using "{{ ... }}" (https://docs.ansible.com/ansible/2.7/user_guide/playbooks_loops.html#with-items).
This is the correct way for your task:
- name: Add Instance IP Addresses to temporary inventory groups
add_host:
groups: dynamic_groups
hostname: "{{item}}"
with_items: "{{ serverlist.stdout_lines }}"
You can simply use ansible-playbook -i inventory_file_name playbook.yaml for this. inventory_file is the file containing your groups and ips.