Rescue block for unreachable hosts

Is it possible to have a playbook, perhaps something very simple like gather_facts run and setup a rescue block to run specific commands on any unreachable hosts?

what I am thinking of is the scenario of talking to a virtual machine in vmware and the guest NIC has become disconnected, I would like to use the vmware module to re-able the guest NIC on any unreachable hosts. It will probably end up doing further vmware guest tasks but trying to start simple.

---
- name: Ansible playbook with block and rescue
  hosts: your_server_group
  tasks:
    - name: Block including tasks to execute
      block:
        - name: Gather facts
          setup:

      rescue:
        - name: Display a debug message when a host is unreachable
          debug:
            msg: "Host {{ inventory_hostname }} is unreachable"

        - name: Clear host errors
          meta: clear_host_errors

        - name: Re-enable network card
          delegate_to: localhost
          vmware_guest_network:
            hostname: "{{ vcenter_hostname }}"
            username: "{{ vcenter_username }}"
            password: "{{ vcenter_password }}"
            validate_certs: no
            name: "{{ inventory_hostname }}"
            networks:
              - name: VM Network
                state: present
                connected: true
                start_connected: true

Does something like this sound feasible?

You can utilize the wait_for_connection module:

- hosts: host1,host2
  gather_facts: false
  tasks:
    - block:                                                                    
        - wait_for_connection:
            delay: 1
            timeout: 10
        - gather_facts:
      rescue:
        - debug:
            msg: "{{ inventory_hostname }} is unreachable"

Or you can use the unreachable test for this. The rough example could be:

- hosts: localhost
  gather_facts: false
  tasks:
    - ping:                                                             
      delegate_to: "{{ hostname}}"
      ignore_unreachable: true
      register: r
      loop_control:
        loop_var: hostname
      loop:
        - host1
        - host2

    - name: process (print in this case) unreachable hosts
      debug:
        msg: "{{ item['hostname'] }}"
      loop: "{{ r['results']|select('unreachable') }}"

You could also utilize the ansible_play_hosts_all and ansible_play_hosts variables, doing the difference operation on them would give both failed and unreachable hosts (combined), in case you don’t mind this including the failed hosts as well.

5 Likes

Thanks @mkrizek that got me onto the right track to do some proper testing. So far I have this code:

- name: Ansible playbook without the block and rescue
  hosts: all
  tasks:
    - name: Gather facts
      ansible.builtin.setup:
      ignore_unreachable: true
      register: gathered_facts

    - name: Clear host errors
      ansible.builtin.meta: clear_host_errors

    - name: Display a debug message when a host is unreachable
      ansible.builtin.debug:
        msg: "{{ inventory_hostname }} is unreachable, oh no!"
      when: gathered_facts is ansible.builtin.unreachable

    - name: Re-enable network card
      delegate_to: localhost
      community.vmware.vmware_guest_network:
        hostname: "{{ vcenter_hostname }}"
        username: "{{ vcenter_username }}"
        password: "{{ vcenter_password }}"
        validate_certs: no
        name: "{{ inventory_hostname }}"
        networks:
          - name: VM Network
            state: present
            connected: true
            start_connected: true
      when: gathered_facts is ansible.builtin.unreachable

I’ve tested it by making an inventory with 2 hosts which are invalid and 2 hosts which are fine and run the playbook against said inventory. It comes up when it tries to run the setup module and correctly shows the hosts as unreachable before it clears the meta and runs the next task to display the debug for the unreachable hosts. I haven’t tried to run the last vmware task as yet, but the debug works and they have the same when condition so as a proof of concept this should be all good.

Would have been nice to use the block and a rescue approach, i’m just not sure how I could set conditions on the rescue to activate if it saw an unreachable host or if that’s an option at all.

1 Like

Extending your example, you can use the fail module to trigger the rescue section:

- hosts: host 
  gather_facts: false
  tasks:
    - block:
        - gather_facts:
          ignore_unreachable: true
          register: r
        - fail:
          when: r is unreachable
      rescue:
        - debug:
            msg: "cannot connect to {{ inventory_hostname }}"

But using the wait_for_connection module from my first example that also uses rescue seems more elegant to me.

1 Like