2025-12-14 20:46:32 +01:00
|
|
|
---
|
2025-12-23 22:06:51 +01:00
|
|
|
# ============================================================
|
|
|
|
|
# Install required packages
|
|
|
|
|
# ============================================================
|
2025-12-14 20:46:32 +01:00
|
|
|
- name: Install required packages
|
2025-12-14 21:28:45 +01:00
|
|
|
ansible.builtin.apt:
|
2025-12-14 20:46:32 +01:00
|
|
|
name: ethtool
|
|
|
|
|
state: present
|
2025-12-24 07:37:56 +01:00
|
|
|
update_cache: true
|
2025-12-14 20:46:32 +01:00
|
|
|
|
2025-12-24 09:26:42 +01:00
|
|
|
# ============================================================
|
|
|
|
|
# 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
|
|
|
|
|
|
2025-12-24 10:32:25 +01:00
|
|
|
- name: Display interfaces
|
2025-12-24 09:26:42 +01:00
|
|
|
ansible.builtin.debug:
|
|
|
|
|
msg: >
|
2025-12-24 09:40:27 +01:00
|
|
|
{{ ansible_facts.interfaces }}
|
2025-12-24 09:26:42 +01:00
|
|
|
|
2025-12-24 10:32:25 +01:00
|
|
|
- name: Get interfaces starting with "en" or "eth"
|
2025-12-24 09:48:10 +01:00
|
|
|
ansible.builtin.set_fact:
|
2025-12-24 10:23:57 +01:00
|
|
|
en_interfaces: "{{ ansible_facts.interfaces | select('match', '^eth|^ens|^enp') | unique | list }}"
|
2025-12-24 09:45:10 +01:00
|
|
|
|
2025-12-24 10:32:25 +01:00
|
|
|
- name: Display selected interfaces
|
2025-12-24 09:50:39 +01:00
|
|
|
ansible.builtin.debug:
|
|
|
|
|
msg: >
|
|
|
|
|
{{ en_interfaces }}
|
|
|
|
|
|
2025-12-24 10:10:02 +01:00
|
|
|
- name: Validate WOL capability using ethtool for detected interfaces
|
2025-12-24 10:23:57 +01:00
|
|
|
ansible.builtin.command: "ethtool {{ item }}"
|
2025-12-24 10:10:02 +01:00
|
|
|
register: wol_capabilities_check
|
|
|
|
|
changed_when: false
|
|
|
|
|
failed_when: false
|
2025-12-24 10:23:57 +01:00
|
|
|
loop: "{{ en_interfaces }}"
|
|
|
|
|
when: en_interfaces | length > 0
|
2025-12-24 09:23:09 +01:00
|
|
|
|
2025-12-24 10:10:02 +01:00
|
|
|
- name: Display ethtool output for detected interfaces
|
|
|
|
|
ansible.builtin.debug:
|
|
|
|
|
msg: >
|
2025-12-24 10:28:24 +01:00
|
|
|
{{ wol_capabilities_check.results | map(attribute='stdout_lines') | list }}
|
2025-12-24 09:23:09 +01:00
|
|
|
|
|
|
|
|
# # ============================================================
|
|
|
|
|
# # 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
|
|
|
|
|
# when: bond_info.rc == 0
|
|
|
|
|
# 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)
|
|
|
|
|
|
|
|
|
|
# # ============================================================
|
|
|
|
|
# # 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 %}
|
|
|
|
|
# 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 }}"
|
|
|
|
|
# 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 %}
|