diff --git a/defaults/main.yml b/defaults/main.yml index ab5fd10..669d4f4 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -1,8 +1,6 @@ # defaults/main.yml -# Network interface to enable Wake-on-LAN on -wol_interface: "" # user override (physical NIC) -wol_detected_bridge: "" -wol_detected_phy: "" +# Bridge interface to enable Wake-on-LAN on +wol_bridge: vmbr0 # WOL mode: # g = magic packet (most common) @@ -12,4 +10,4 @@ wol_mode: "g" wol_verify: true # Report MAC address for WOL senders -wol_report_mac: true \ No newline at end of file +wol_report_mac: true diff --git a/tasks/main.yml b/tasks/main.yml index 1b30dc3..59015e7 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -5,156 +5,110 @@ state: present update_cache: yes -- name: Gather network facts - ansible.builtin.setup: - gather_subset: - - network +# ------------------------------------------------------------ +# Map vmbr0 → backing physical NIC +# ------------------------------------------------------------ +- name: Get bridge link information + ansible.builtin.command: "bridge link show" + register: bridge_links + changed_when: false + failed_when: false -- name: Detect default route interface +- name: Detect physical NIC backing vmbr0 ansible.builtin.set_fact: - wol_detected_interface: "{{ ansible_default_ipv4.interface }}" - when: wol_interface | default('') | length == 0 - -- name: Debug facts interface - ansible.builtin.debug: - msg: - - "Detected bridge {{ wol_detected_interface }}" - -# - name: Debug facts -# ansible.builtin.debug: -# msg: -# - "{{ hostvars[inventory_hostname]['ansible_' + wol_detected_interface][interfaces] }}" - -# - name: Detect physical NIC enslaved to bridge -# ansible.builtin.set_fact: -# wol_detected_phy: "{{ item }}" -# loop: "{{ ansible_interfaces }}" -# when: -# - wol_interface | default('') | length == 0 -# - item != 'lo' -# - item is not match('^v') -# - item is match('^e') -# - hostvars[inventory_hostname]['ansible_' + item] is defined -# - hostvars[inventory_hostname]['ansible_' + item].module is defined -# - hostvars[inventory_hostname]['ansible_' + item].pciid is defined -# - hostvars[inventory_hostname]['ansible_' + item].type is defined -# - hostvars[inventory_hostname]['ansible_' + item].type == 'ether' - - - -# - name: Detect physical NIC behind bridge -# ansible.builtin.set_fact: -# wol_detected_phy: "{{ ansible_facts[wol_detected_bridge].interfaces[0] }}" -# when: -# - wol_interface | default('') | length == 0 -# - ansible_facts[wol_detected_bridge] is defined -# - ansible_facts[wol_detected_bridge].interfaces is defined -# - ansible_facts[wol_detected_bridge].interfaces | length > 0 - -- name: Detect physical NICs in bridge - ansible.builtin.set_fact: - wol_bridge_phys: >- + wol_detected_phy: >- {{ - ansible_facts[wol_detected_interface].interfaces - | select('match', '^(e|en)') - | list + bridge_links.stdout_lines + | select('search', wol_bridge) + | select('search', 'master ' ~ wol_bridge) + | map('regex_search', '^\\d+: ([^:@]+)', '\\1') + | select('string') + | first }} -- name: Debug WoL interface selection - ansible.builtin.debug: - msg: - - "Detected interface: {{ wol_detected_interface }}" - - "Detected physical NICs: {{ wol_detected_phys }}" - -- name: Select physical NIC if exactly one found - ansible.builtin.set_fact: - wol_detected_phy: "{{ wol_bridge_phys[0] }}" - when: wol_bridge_phys | length == 1 - -- name: Select final WoL interface - ansible.builtin.set_fact: - wol_final_interface: >- - {{ - wol_interface - if wol_interface | default('') | length > 0 - else wol_detected_phy - }} - -- name: Fail if multiple NICs are attached to bridge +- name: Fail if vmbr0 backing NIC could not be detected ansible.builtin.fail: msg: > - Multiple physical NICs found in {{ wol_detected_bridge }}: - {{ wol_bridge_phys }}. + Unable to detect physical NIC backing {{ wol_bridge }}. Please set wol_interface explicitly. - when: wol_bridge_phys | length > 1 + when: wol_detected_phy | default('') | length == 0 -- name: Fail if no physical NIC was detected - ansible.builtin.fail: - msg: > - Unable to detect a physical NIC for Wake-on-LAN. - Detected bridge: {{ wol_detected_bridge | default('none') }}. - Please set wol_interface explicitly. - when: wol_final_interface | default('') | length == 0 - -- name: Check WOL support on interface - ansible.builtin.command: ethtool {{ wol_final_interface }} +# ------------------------------------------------------------ +# Validate WOL capability +# ------------------------------------------------------------ +- name: Check WOL capability on detected NIC + ansible.builtin.command: "ethtool {{ wol_detected_phy }}" register: wol_capabilities changed_when: false - name: Fail if NIC does not support Wake-on-LAN ansible.builtin.fail: - msg: "Interface {{ wol_final_interface }} does not support Wake-on-LAN." - when: "'Wake-on:' not in wol_capabilities.stdout" + msg: "Interface {{ wol_detected_phy }} does not support Wake-on-LAN." + when: "'Supports Wake-on: g' not in wol_capabilities.stdout" -- name: Create systemd service for Wake-on-LAN - ansible.builtin.template: - src: wol.service.j2 - dest: /etc/systemd/system/wol.service +# ------------------------------------------------------------ +# Enable WOL immediately +# ------------------------------------------------------------ +- name: Enable Wake-on-LAN immediately + ansible.builtin.command: > + ethtool -s {{ wol_detected_phy }} wol {{ wol_mode }} + changed_when: false + +# ------------------------------------------------------------ +# Persist WOL via udev (correct + recommended) +# ------------------------------------------------------------ +- name: Create udev rule to persist Wake-on-LAN + ansible.builtin.copy: + dest: /etc/udev/rules.d/90-wol.rules owner: root group: root mode: '0644' - notify: - - Reload systemd and restart WOL + content: | + ACTION=="add", SUBSYSTEM=="net", KERNEL=="{{ wol_detected_phy }}", RUN+="/sbin/ethtool -s {{ wol_detected_phy }} wol {{ wol_mode }}" -- name: Enable WOL immediately (without reboot) - ansible.builtin.command: ethtool -s {{ wol_final_interface }} wol {{ wol_mode }} +- name: Reload udev rules + ansible.builtin.command: udevadm control --reload changed_when: false +- name: Trigger udev for network interfaces + ansible.builtin.command: udevadm trigger --subsystem-match=net + changed_when: false + +# ------------------------------------------------------------ +# Verification & Reporting +# ------------------------------------------------------------ - name: Verify Wake-on-LAN status - ansible.builtin.command: ethtool {{ wol_final_interface }} + ansible.builtin.command: "ethtool {{ wol_detected_phy }}" register: wol_status changed_when: false when: wol_verify -- name: Get MAC address of WOL interface - set_fact: - wol_mac_address: >- - {{ - ansible_facts['ansible_' + wol_final_interface]['macaddress'] - | default('') - }} +- name: Get MAC address + ansible.builtin.set_fact: + wol_mac_address: "{{ ansible_facts.interfaces[wol_detected_phy].macaddress | default('') }}" + +- name: Fail if MAC address could not be detected + ansible.builtin.fail: + msg: "Unable to determine MAC address for interface {{ wol_detected_phy }}" + when: + - wol_report_mac + - wol_mac_address | length == 0 - name: Show WOL status ansible.builtin.debug: msg: "{{ wol_status.stdout_lines }}" when: wol_verify -- name: Fail if MAC address could not be detected - fail: - msg: "Unable to determine MAC address for interface {{ wol_final_interface }}" - when: - - wol_report_mac - - wol_mac_address | length == 0 - - name: Report Wake-on-LAN sender details ansible.builtin.debug: msg: - - "Wake-on-LAN enabled on interface: {{ wol_final_interface }}" + - "Wake-on-LAN enabled successfully" + - "Bridge: {{ wol_bridge }}" + - "Physical NIC: {{ wol_detected_phy }}" - "MAC address: {{ wol_mac_address }}" - - "Example WOL commands:" + - "Example commands:" - " wakeonlan {{ wol_mac_address }}" - " etherwake {{ wol_mac_address }}" - "Home Assistant:" - " mac: {{ wol_mac_address }}" when: wol_report_mac - diff --git a/templates/wol.service.j2 b/templates/wol.service.j2 index 1b8c35a..57ddb60 100644 --- a/templates/wol.service.j2 +++ b/templates/wol.service.j2 @@ -1,10 +1,11 @@ [Unit] -Description=Enable Wake-on-LAN on {{ wol_interface_final }} +Description=Enable Wake-on-LAN on {{ wol_final_interface }} After=network.target [Service] Type=oneshot -ExecStart=/sbin/ethtool -s {{ wol_interface_final }} wol {{ wol_mode }} +ExecStart=/sbin/ethtool -s {{ wol_final_interface }} wol {{ wol_mode }} +Restart=always [Install] WantedBy=multi-user.target