Files
ansible_proxmox_WOL/tasks/main.yml

256 lines
8.7 KiB
YAML
Raw Normal View History

---
# ============================================================
# Install required packages
# ============================================================
- name: Install required packages
ansible.builtin.apt:
name: ethtool
state: present
update_cache: yes
# ============================================================
# Normalize and validate configuration
# ============================================================
- name: Set WOL bridges list (handle string or list)
ansible.builtin.set_fact:
wol_bridges_list: "{{ wol_bridges if wol_bridges is iterable and wol_bridges is not string else [wol_bridges] }}"
- name: Validate WOL bridges list is not empty
ansible.builtin.assert:
that:
- wol_bridges_list | length > 0
fail_msg: "wol_bridges must contain at least one bridge interface"
# ============================================================
# Detect physical NICs with WOL support using Ansible facts
# ============================================================
- name: Gather network interface facts
ansible.builtin.setup:
gather_subset:
- network
when: ansible_facts.interfaces is not defined
- name: Get all physical network interfaces with WOL support
ansible.builtin.set_fact:
wol_capable_interfaces: >-
{{
ansible_facts.interfaces
| map('extract', hostvars[inventory_hostname]['ansible_' ~ item] | default({}))
| selectattr('type', 'defined')
| selectattr('type', 'equalto', 'ether')
| selectattr('device', 'defined')
| rejectattr('device', 'search', '^(veth|tap|fw|lo|docker|br)')
| map(attribute='device')
| list
}}
- name: Validate WOL capability using ethtool for detected interfaces
ansible.builtin.command: "ethtool {{ item }}"
register: wol_capabilities_check
changed_when: false
failed_when: false
loop: "{{ wol_capable_interfaces }}"
loop_control:
label: "{{ item }}"
- name: Filter interfaces that actually support WOL
ansible.builtin.set_fact:
wol_supported_interfaces: >-
{{
wol_capabilities_check.results
| selectattr('stdout', 'search', 'Supports Wake-on:.*[gGdDpPuU]')
| map(attribute='item')
| list
}}
- name: Fail if no interfaces support WOL
ansible.builtin.assert:
that:
- wol_supported_interfaces | length > 0
fail_msg: "No network interfaces found that support Wake-on-LAN. Check BIOS settings and NIC capabilities."
# ============================================================
# Map bridges to physical NICs using Ansible facts
# ============================================================
- name: Get bridge link information
ansible.builtin.command: "bridge link show"
register: bridge_links
changed_when: false
check_mode: false
- name: Initialize detected interfaces dictionary
ansible.builtin.set_fact:
wol_detected_interfaces: {}
- name: Detect physical NIC for each bridge
ansible.builtin.set_fact:
wol_detected_interfaces: >-
{{
wol_detected_interfaces | combine({
item: (
bridge_links.stdout_lines
| select('search', 'master ' ~ item)
| map('regex_replace', '^\\d+: ([a-z0-9@.]+):.*$', '\\1')
| reject('search', '^(veth|tap|fw)')
| reject('search', '^\\d+:')
| select('in', wol_supported_interfaces)
| first | default('')
)
})
}}
loop: "{{ wol_bridges_list }}"
loop_control:
label: "{{ item }}"
- name: Check for bond0 backing
block:
- name: Detect if any bridge is backed by bond0
ansible.builtin.set_fact:
wol_has_bond0: "{{ wol_detected_interfaces.values() | select('search', 'bond0') | length > 0 }}"
- name: Extract bond0 slaves if present
ansible.builtin.set_fact:
wol_bond0_slaves: >-
{{
(bond_info.stdout | regex_findall('Slave Interface: ([a-zA-Z0-9]+)')) | list
}}
when: wol_has_bond0 | default(false)
when: bond_info.rc == 0
# ============================================================
# Validate configuration and resolve to physical NICs
# ============================================================
- name: Fail if any bridge backing NIC could not be detected
ansible.builtin.fail:
msg: >
Unable to detect physical NIC backing bridge(s): {{ unresolved_bridges | join(', ') }}.
Please verify bridges exist and are backed by WOL-capable interfaces.
Available WOL-capable interfaces: {{ wol_supported_interfaces | join(', ') }}
vars:
unresolved_bridges: "{{ wol_detected_interfaces | dict2items | selectattr('value', 'equalto', '') | map(attribute='key') | list }}"
when: unresolved_bridges | length > 0
- name: Build final WOL interfaces list (deduped physical NICs)
ansible.builtin.set_fact:
wol_final_interfaces: "{{ wol_detected_interfaces.values() | unique | list }}"
# ============================================================
# Check current WOL status to ensure idempotency
# ============================================================
- name: Get current WOL status
ansible.builtin.command: "ethtool {{ item }}"
register: wol_current_status
changed_when: false
failed_when: false
loop: "{{ wol_final_interfaces }}"
loop_control:
label: "{{ item }}"
- name: Build list of NICs needing WOL enabled
ansible.builtin.set_fact:
wol_needs_enable: >-
{{
wol_current_status.results
| selectattr('stdout', 'search', 'Wake-on: [^g]')
| map(attribute='item')
| list
}}
# ============================================================
# Enable WOL immediately (only if needed)
# ============================================================
- name: Enable Wake-on-LAN immediately on NICs
ansible.builtin.command: "ethtool -s {{ item }} wol {{ wol_mode }}"
register: wol_enable_result
changed_when: true
loop: "{{ wol_needs_enable }}"
loop_control:
label: "{{ item }}"
when: wol_needs_enable | length > 0
# ============================================================
# Persist WOL via udev rules (safe and idempotent)
# ============================================================
- name: Create udev rule content
ansible.builtin.set_fact:
wol_udev_rules: >-
{{
wol_final_interfaces
| map('regex_replace', '^(.+)$', 'ACTION=="add", SUBSYSTEM=="net", KERNEL=="\1", RUN+="/sbin/ethtool -s \1 wol {{ wol_mode }}"')
| list
}}
- name: Create/Update udev rules file
ansible.builtin.copy:
dest: /etc/udev/rules.d/90-wol.rules
owner: root
group: root
mode: "0644"
content: |
# Wake-on-LAN udev rules - Auto-generated by Ansible
# Applies to: {{ wol_final_interfaces | join(', ') }}
{% for rule in wol_udev_rules %}
{{ rule }}
{% endfor %}
register: udev_rules_changed
- name: Reload udev rules
ansible.builtin.command: udevadm control --reload
changed_when: false
when: udev_rules_changed is changed
- name: Trigger udev for network interfaces
ansible.builtin.command: udevadm trigger --subsystem-match=net
changed_when: false
when: udev_rules_changed is changed
# ============================================================
# Verification & Reporting
# ============================================================
- name: Verify Wake-on-LAN status
ansible.builtin.command: "ethtool {{ item }}"
register: wol_status
changed_when: false
loop: "{{ wol_final_interfaces }}"
loop_control:
label: "{{ item }}"
when: wol_verify
- name: Display WOL status per interface
ansible.builtin.debug:
msg: >
Interface {{ item.item }} WOL Status:
{{ item.stdout_lines | select('search', 'Wake-on:') | first | default('Status Unknown') }}
loop: "{{ wol_status.results | default([]) }}"
loop_control:
label: "{{ item.item }}"
when: wol_verify
- name: Get MAC addresses for all interfaces
ansible.builtin.set_fact:
wol_mac_addresses: >-
{{
wol_final_interfaces
| map('extract', hostvars[inventory_hostname]['ansible_' ~ item] | default({}), 'macaddress')
| list
}}
- name: Report WOL configuration
ansible.builtin.debug:
msg: |
Wake-on-LAN Configuration Summary:
===================================
Bridges Configured: {{ wol_bridges_list | join(', ') }}
Physical Interfaces: {{ wol_final_interfaces | join(', ') }}
WOL Mode: {{ wol_mode }}
{% if wol_has_bond0 | default(false) %}
Bond0 Detected: Yes
Bond0 Slaves: {{ wol_bond0_slaves | join(', ') }}
{% endif %}
{% if wol_report_mac and wol_mac_addresses | length > 0 %}
MAC Addresses:
{% for iface, mac in (wol_final_interfaces | zip(wol_mac_addresses) | list) %}
- {{ iface }}: {{ mac | default('Unable to detect') }}
{% endfor %}
{% endif %}