Reputation: 303
I have a list of servers that only part of them are running port 9090. I need to create two tasks, each should loop the server's hostnames. the first task should define inside a register the first server hostname that was found running port 9090 , the second task should define in a register all server's hostnames that are running port 9090. If no server is running port 9090 the tasks should fail
I have nc installed on the server and I thought about using a shell with the following task:
server_details: - server1 - server2 - server3 - server4 - server5 - name: locate servers running port 9090 shell: nc -zv {{ item.hostname }} 9090 register: results_port_9090 with_items: - "{{ server_details }}" ignore_errors: yes
But I don't know how to filter the answer and add the hostname to a new register - I assume an additional task is required that runs on the existing register and creates a new register with the relevant outputs
For example:
server1
server2
server3 #running 9090
server4
server5 #running 9090
Upvotes: 1
Views: 733
Reputation: 838
To get the open ports use listen_ports_facts
- hosts: all
gather_facts: no
vars:
check_port: 9090 ################ desired port ################
tasks:
- name: Gather facts on listening ports
community.general.listen_ports_facts:
################ Filter ports and store them in a variable ################
- name: List all ports
set_fact:
ports_list: "{{ (ansible_facts.tcp_listen + ansible_facts.udp_listen) | map(attribute='port') | unique | sort | list }}"
# Check if the desired port exists in the listed ports and write output according to that #
- name: Check if the desired port exists in the listed ports
debug:
msg: |
{% if check_port in ports_list %}
{{ inventory_hostname }} #running {{check_port}}
{% else %}
{{ inventory_hostname }} #not running {{check_port}}
{% endif %}
register: checkoutput
################ Combine the output for all hosts ################
- name: Combine the output for all hosts
debug:
msg: "{{ ansible_play_hosts | map('extract', hostvars, 'checkoutput') | map(attribute='msg') | list }}"
register: finaloutput
run_once: yes
The list of ports in debug mode:
TASK [List all ports] *********************************************************************
task path: /root/ansible-test/dictionary.yml:10
ok: [test-001] => {
"ansible_facts": {
"ports_list": [
22,
68,
111,
2049,
34007,
37539,
38611,
41237,
45851,
46679,
48941,
50640,
52215,
52637,
52772,
54317,
54750,
56105,
58060,
58842
]
},
"changed": false
}
ok: [test-002] => {
"ansible_facts": {
"ports_list": [
22,
68,
111,
323,
782,
9090,
35717,
35799,
36247,
44387
]
},
"changed": false
}
TASK [Check if the desired port exists in the listed ports] ******************************
ok: [test-001] => {
"msg": "test-001 #not running 9090\n"
}
ok: [test-002] => {
"msg": "test-002 #running 9090\n"
}
TASK [Combine the output for all hosts] **************************************************
ok: [test-001] => {
"msg": [
"test-001 #not running 9090\n",
"test-002 #running 9090\n"
]
}
Upvotes: 0
Reputation: 68034
Q: "If no server is running port
the play should fail."
A: Use wait_for to test a port. For example, given the variables
server_details: [test_11, test_12, test_13]
time_out: 3
fail: "{{ out.results|items2dict(key_name='item', value_name='failed') }}"
The task below
- wait_for:
host: "{{ item }}"
port: "{{ test_port }}"
timeout: "{{ time_out }}"
loop: "{{ server_details }}"
register: out
gives, for example when test_port=22
fail:
test_11: false
test_12: false
test_13: false
This means that port 22 is open at all remote hosts. Test it
- name: "If no server is running port {{ test_port }} the tasks should fail."
fail:
msg: "No server is running port {{ test_port }}"
when: fail.values()|list is all
Example of a complete playbook for testing
If you want to use the module command
, instead of wait_for
, the second block provides the same functionality. Use the tags to select the block. Optionally, enable debug to see the dictionaries
shell> cat pb.yml
- hosts: localhost
vars:
server_details: [test_11, test_12, test_13]
time_out: 3
fail: "{{ out.results|items2dict(key_name='item', value_name='failed') }}"
rc: "{{ out.results|items2dict(key_name='item', value_name='rc') }}"
tasks:
- assert:
that: test_port is defined
fail_msg: The variable test_port is mandatory.
tags: always
- block:
- wait_for:
host: "{{ item }}"
port: "{{ test_port }}"
timeout: "{{ time_out }}"
loop: "{{ server_details }}"
register: out
always:
- debug:
var: fail
when: debug|d(false)|bool
- name: "If no server is running port {{ test_port }} the tasks should fail."
fail:
msg: "No server is running port {{ test_port }}"
when: fail.values()|list is all
tags: t1
- block:
- command: "nc -w {{ time_out }} -zv {{ item }} {{ test_port }}"
loop: "{{ server_details }}"
register: out
always:
- debug:
var: rc
when: debug|d(false)|bool
- name: "If no server is running port {{ test_port }} the tasks should fail."
fail:
msg: "No server is running port {{ test_port }}"
when: rc.values()|list is all
tags: t2
wait_for
port 22shell> ansible-playbook pb.yml -t t1 -e test_port=22 -e debug=true
PLAY [localhost] *****************************************************************************
TASK [assert] ********************************************************************************
ok: [localhost] => changed=false
msg: All assertions passed
TASK [wait_for] ******************************************************************************
ok: [localhost] => (item=test_11)
ok: [localhost] => (item=test_12)
ok: [localhost] => (item=test_13)
TASK [debug] *********************************************************************************
ok: [localhost] =>
fail:
test_11: false
test_12: false
test_13: false
TASK [If no server is running port 22 the tasks should fail.] ********************************
skipping: [localhost]
PLAY RECAP ***********************************************************************************
localhost: ok=3 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
wait_for
port 80shell> ansible-playbook pb.yml -t t1 -e test_port=80 -e debug=true
PLAY [localhost] *****************************************************************************
TASK [assert] ********************************************************************************
ok: [localhost] => changed=false
msg: All assertions passed
TASK [wait_for] ******************************************************************************
failed: [localhost] (item=test_11) => changed=false
ansible_loop_var: item
elapsed: 4
item: test_11
msg: Timeout when waiting for test_11:80
failed: [localhost] (item=test_12) => changed=false
ansible_loop_var: item
elapsed: 4
item: test_12
msg: Timeout when waiting for test_12:80
failed: [localhost] (item=test_13) => changed=false
ansible_loop_var: item
elapsed: 4
item: test_13
msg: Timeout when waiting for test_13:80
TASK [debug] *********************************************************************************
ok: [localhost] =>
fail:
test_11: true
test_12: true
test_13: true
TASK [If no server is running port 80 the tasks should fail.] ********************************
fatal: [localhost]: FAILED! => changed=false
msg: No server is running port 80
PLAY RECAP ***********************************************************************************
localhost: ok=2 changed=0 unreachable=0 failed=2 skipped=0 rescued=0 ignored=0
command
port 22shell> ansible-playbook pb.yml -t t2 -e test_port=22 -e debug=true
PLAY [localhost] *****************************************************************************
TASK [assert] ********************************************************************************
ok: [localhost] => changed=false
msg: All assertions passed
TASK [command] *******************************************************************************
changed: [localhost] => (item=test_11)
changed: [localhost] => (item=test_12)
changed: [localhost] => (item=test_13)
TASK [debug] *********************************************************************************
ok: [localhost] =>
rc:
test_11: 0
test_12: 0
test_13: 0
TASK [If no server is running port 22 the tasks should fail.] ********************************
skipping: [localhost]
PLAY RECAP ***********************************************************************************
localhost: ok=3 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
command
port 80shell> ansible-playbook pb.yml -t t2 -e test_port=80 -e debug=true
PLAY [localhost] *****************************************************************************
TASK [assert] ********************************************************************************
ok: [localhost] => changed=false
msg: All assertions passed
TASK [command] *******************************************************************************
failed: [localhost] (item=test_11) => changed=true
ansible_loop_var: item
cmd:
- nc
- -w
- '3'
- -zv
- test_11
- '80'
delta: '0:00:03.006955'
end: '2022-08-21 17:11:30.449997'
item: test_11
msg: non-zero return code
rc: 1
start: '2022-08-21 17:11:27.443042'
stderr: 'nc: connect to test_11 port 80 (tcp) timed out: Operation now in progress'
stderr_lines: <omitted>
stdout: ''
stdout_lines: <omitted>
failed: [localhost] (item=test_12) => changed=true
ansible_loop_var: item
cmd:
- nc
- -w
- '3'
- -zv
- test_12
- '80'
delta: '0:00:03.005854'
end: '2022-08-21 17:11:33.656814'
item: test_12
msg: non-zero return code
rc: 1
start: '2022-08-21 17:11:30.650960'
stderr: 'nc: connect to test_12 port 80 (tcp) timed out: Operation now in progress'
stderr_lines: <omitted>
stdout: ''
stdout_lines: <omitted>
failed: [localhost] (item=test_13) => changed=true
ansible_loop_var: item
cmd:
- nc
- -w
- '3'
- -zv
- test_13
- '80'
delta: '0:00:03.009158'
end: '2022-08-21 17:11:36.860258'
item: test_13
msg: non-zero return code
rc: 1
start: '2022-08-21 17:11:33.851100'
stderr: 'nc: connect to test_13 port 80 (tcp) timed out: Operation now in progress'
stderr_lines: <omitted>
stdout: ''
stdout_lines: <omitted>
TASK [debug] *********************************************************************************
ok: [localhost] =>
rc:
test_11: 1
test_12: 1
test_13: 1
TASK [If no server is running port 80 the tasks should fail.] ********************************
fatal: [localhost]: FAILED! => changed=false
msg: No server is running port 80
PLAY RECAP ***********************************************************************************
localhost: ok=2 changed=0 unreachable=0 failed=2 skipped=0 rescued=0 ignored=0
Upvotes: 2
Reputation: 11280
Your thinking is correct. What you can use to filter results is the rc (return code) of the shell
command.
When nc
succeeds to open a connection, the exist code would be 0, otherwise usually 1. You should iterate the returned list, and check for exit codes, creating a new array.
You can do something like this:
- name: look for live services
set_fact:
live_services: "{{ live_services|default([]) + [item] }}
when: item.rc == 0
loop:
- "{{ results_port_9090 }}"
Then, to get the first server the has this service running you can check if this array isn't empty and if so, the first server would be placed at live_services[0]
.
Upvotes: 0