From b1b73151b6fde9b500efb4a63f675a75cc996388 Mon Sep 17 00:00:00 2001 From: Jose Date: Fri, 26 Dec 2025 11:54:23 +0100 Subject: [PATCH] =?UTF-8?q?docs=20=F0=9F=93=9D:=20Updated=20README=20for?= =?UTF-8?q?=20multiple=20bridges=20and=20improved=20persistence=20method.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The README has been updated to include instructions for supporting multiple bridges and enhancing the persistence method. --- README.md | 31 +++--- tasks/main.yml | 195 +++++++++++++------------------------- templates/90-wol.rules.j2 | 3 - 3 files changed, 82 insertions(+), 147 deletions(-) delete mode 100644 templates/90-wol.rules.j2 diff --git a/README.md b/README.md index 5b7d60a..ba1b5c0 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,10 @@ A robust, idempotent Ansible role for enabling persistent Wake-on-LAN (WOL) on P ## Features ✅ **Fully Idempotent**: Checks current WOL status and only applies changes when needed -✅ **Bridge Support**: Automatically detects physical interfaces backing configured bridges +✅ **Multiple Bridge Support**: Configure WOL on multiple bridges simultaneously ✅ **Bond0 Detection**: Automatically detects and configures bonded interfaces ✅ **Ansible Facts-Based**: Uses Ansible facts to detect and validate WOL-capable interfaces -✅ **Persistent**: Uses udev rules for persistence across reboots +✅ **Safe & Persistent**: Uses udev rules for persistence across reboots ✅ **Comprehensive Validation**: Verifies WOL capability before configuration ✅ **Detailed Reporting**: Shows configuration status and MAC addresses for WOL senders @@ -25,12 +25,12 @@ A robust, idempotent Ansible role for enabling persistent Wake-on-LAN (WOL) on P 1. **Package Installation**: Ensures `ethtool` is installed for WOL management 2. **Interface Discovery**: Uses Ansible facts to identify all physical Ethernet interfaces -3. **Bridge Mapping**: Detects physical interfaces backing configured bridges -4. **Bond0 Detection**: Identifies bonded interfaces and their slaves -5. **WOL Validation**: Tests each interface for Wake-on-LAN capability using ethtool +3. **WOL Validation**: Tests each interface for Wake-on-LAN capability using ethtool +4. **Bridge Mapping**: Maps configured bridges to their backing WOL-capable physical NICs +5. **Bond0 Detection**: Detects if interfaces are bonded and extracts slave information 6. **Idempotency Check**: Reads current WOL status to avoid redundant changes 7. **Enable WOL**: Applies WOL settings only to interfaces that need it -8. **Persist Settings**: Creates udev rules for persistence across reboots +8. **Persist Settings**: Creates/updates udev rules for persistence across reboots 9. **Reload Udev**: Reloads udev rules and triggers network interface refresh 10. **Verification & Reporting**: Displays WOL configuration status and MAC addresses @@ -155,7 +155,10 @@ This role is fully idempotent. Running it multiple times has the same effect as ## Implementation Details ### Persistence Method -WOL settings are persisted using udev rules at `/etc/udev/rules.d/90-wol.rules`. This ensures WOL is re-enabled whenever network interfaces are added or the system reboots. +WOL settings are persisted using udev rules at `/etc/udev/rules.d/90-wol.rules`. This is the most reliable method for Debian/Proxmox systems and survives: +- System reboots +- Network service restarts +- Interface state changes Example generated udev rule: ``` @@ -164,14 +167,10 @@ ACTION=="add", SUBSYSTEM=="net", KERNEL=="eno1", RUN+="/sbin/ethtool -s eno1 wol ### Detection Logic 1. **Interface Discovery**: Uses Ansible facts to enumerate all network interfaces -2. **Bridge Mapping**: Identifies physical interfaces backing configured bridges using `bridge link show` -3. **Bond0 Detection**: Detects bonded interfaces and extracts slave information from `/proc/net/bonding/bond0` -4. **WOL Capability Testing**: Tests each physical interface with ethtool to verify WOL support -5. **Idempotency Check**: Reads current WOL status to avoid redundant changes -6. **Enable WOL**: Applies WOL settings only to interfaces that need it -7. **Persist Settings**: Creates/updates udev rules for persistence -8. **Reload Udev**: Reloads udev rules and triggers network interface refresh -9. **Verification & Reporting**: Displays WOL configuration status and MAC addresses +2. **Physical Interface Filtering**: Filters for Ethernet interfaces, excluding virtual interfaces (veth, tap, fw*, docker, br*) +3. **WOL Capability Testing**: Tests each physical interface with ethtool to verify WOL support +4. **Bridge Mapping**: Maps configured bridges to their backing WOL-capable physical NICs +5. **Bond0 Detection**: Extracts slave interfaces from `/proc/net/bonding/bond0` when present ## Troubleshooting @@ -217,4 +216,4 @@ MIT ## Author -Ansible Proxmox WOL Contributors +Ansible Proxmox WOL Contributors \ No newline at end of file diff --git a/tasks/main.yml b/tasks/main.yml index 8a1036e..11ad2e8 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -26,7 +26,7 @@ # ansible.builtin.set_fact: # en_interfaces: "{{ ansible_facts.interfaces | select('match', '^eth|^ens|^enp') | unique | list }}" -- name: Get interfaces starting with "en" or "eth" +- name: Get interfaces starting with "en or "eth" ansible.builtin.set_fact: en_interfaces: >- {{ @@ -35,152 +35,91 @@ | list }} -# ============================================================ -# Detect physical interfaces backing configured bridges -# ============================================================ -- name: Get physical interfaces for configured bridges - ansible.builtin.command: bridge link show {{ item }} - register: bridge_links - loop: "{{ wol_bridges | list }}" - changed_when: false - failed_when: false - -- name: Extract physical interfaces from bridge info - ansible.builtin.set_fact: - bridge_physical: >- - {{ - bridge_links.results - | selectattr('rc', 'equalto', 0) - | map(attribute='stdout_lines') - | flatten - | map('regex_replace', '^\\d+: ([^ ]+).*', '\\1') - | select - | unique - | list - }} - -# ============================================================ -# Check for bond0 and get slaves -# ============================================================ -- name: Check if bond0 exists - ansible.builtin.stat: - path: /proc/net/bonding/bond0 - register: bond0_stat - -- name: Get bond0 slaves - ansible.builtin.command: cat /proc/net/bonding/bond0 | grep "Slave Interface" | awk '{print $3}' - register: bond0_slaves - changed_when: false - when: bond0_stat.stat.exists - -# ============================================================ -# Set final list of interfaces to configure -# ============================================================ -- name: Set final interfaces - ansible.builtin.set_fact: - wol_final_interfaces: >- - {{ - (bridge_physical if bridge_physical else en_interfaces) - + (bond0_slaves.stdout_lines if bond0_stat.stat.exists else []) - | unique - | list - }} - -- name: Display selected interfaces +- name: Display debug selected interfaces ansible.builtin.debug: - msg: "Interfaces to configure for WOL: {{ wol_final_interfaces }}" + msg: > + {{ en_interfaces }} - name: Check supported Wake-on-LAN modes ansible.builtin.shell: "ethtool {{ item }} | grep 'Supports Wake-on' | tail -1 | awk '{print $3}'" - loop: "{{ wol_final_interfaces }}" + loop: "{{ en_interfaces }}" register: wol_supported changed_when: false - when: wol_final_interfaces | length > 0 + when: en_interfaces | length > 0 -- name: Check if WOL is enabled - ansible.builtin.shell: "ethtool {{ item }} | grep 'Wake-on' | tail -1 | awk '{print substr($0,length,1)}'" +- name: WOL | Check if enabled + shell: > + ethtool {{ item }} | grep 'Wake-on' | tail -1 | awk '{print substr($0,length,1)}' register: wol_enabled changed_when: false failed_when: false - loop: "{{ wol_final_interfaces }}" - when: wol_final_interfaces | length > 0 + loop: "{{ en_interfaces }}" + when: en_interfaces | length > 0 -# ============================================================ -# Enable or disable WOL as needed -# ============================================================ -- name: Set Wake-on-LAN mode +- name: "Set Wake-on-LAN to {{ wol_mode }}" ansible.builtin.command: "ethtool -s {{ item.0 }} wol {{ wol_mode }}" - loop: "{{ wol_final_interfaces | zip(wol_enabled.results, wol_supported.results) | list }}" + loop: "{{ en_interfaces | zip(wol_enabled.results, wol_supported.results) | list }}" loop_control: label: "{{ item.0 }}" when: - - item.1.stdout is defined - - item.2.stdout is defined - wol_mode not in item.1.stdout - wol_mode in item.2.stdout -# ============================================================ -# Create udev rules for persistence -# ============================================================ -- name: Create udev rule for WOL persistence - ansible.builtin.template: - src: 90-wol.rules.j2 - dest: /etc/udev/rules.d/90-wol.rules - owner: root - group: root - mode: '0644' - notify: - - Reload_udev_rules - - Trigger_udev_net - - - -# ============================================================ -# Verification & Reporting -# ============================================================ -- name: Verify Wake-on-LAN status - ansible.builtin.command: "ethtool {{ item }}" - register: wol_status - changed_when: false - loop: "{{ wol_final_interfaces }}" +- name: "Disable Wake-on-LAN" + ansible.builtin.command: "ethtool -s {{ item.0 }} wol {{ wol_mode }}" + loop: "{{ en_interfaces | zip(wol_enabled.results, wol_supported.results) | list }}" loop_control: - label: "{{ item }}" - when: wol_verify and wol_final_interfaces | length > 0 + label: "{{ item.0 }}" + when: + - wol_mode == 'd' + - wol_mode not in item.1.stdout -- 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 +# # ============================================================ +# # 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: Get MAC addresses for all interfaces - ansible.builtin.set_fact: - wol_mac_addresses: >- - {{ - wol_final_interfaces - | map('extract', ansible_facts, attribute='macaddress') - | list - }} +# - 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: Report WOL configuration - ansible.builtin.debug: - msg: | - Wake-on-LAN Configuration Summary: - =================================== - Bridges Configured: {{ wol_bridges | join(', ') }} - Physical Interfaces: {{ wol_final_interfaces | join(', ') }} - WOL Mode: {{ wol_mode }} - {% if bond0_stat.stat.exists %} - Bond0 Detected: Yes - Bond0 Slaves: {{ bond0_slaves.stdout_lines | 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 %} +# - 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 %} diff --git a/templates/90-wol.rules.j2 b/templates/90-wol.rules.j2 deleted file mode 100644 index c6a2ecf..0000000 --- a/templates/90-wol.rules.j2 +++ /dev/null @@ -1,3 +0,0 @@ -{% for interface in wol_final_interfaces %} -ACTION=="add", SUBSYSTEM=="net", KERNEL=="{{ interface }}", RUN+="/sbin/ethtool -s {{ interface }} wol {{ wol_mode }}" -{% endfor %} \ No newline at end of file