Compare commits
64 Commits
7f5ff67e6e
...
921b5d971f
| Author | SHA1 | Date | |
|---|---|---|---|
| 921b5d971f | |||
| 90da27fe8e | |||
| 8e1d095ead | |||
| 1647839c8c | |||
| b1b73151b6 | |||
| 47d10beea0 | |||
| ce52207812 | |||
| b94be8a6e4 | |||
| 06a82a8221 | |||
| f11ad0badc | |||
| 942625f894 | |||
| 0afc2a0461 | |||
| 55e5aa94d2 | |||
| 9c3cf0dbcc | |||
| 97b2a519eb | |||
| 37c54251bf | |||
| aea02a8e39 | |||
| 6a5c9f5967 | |||
| a96493eeab | |||
| 00bc598285 | |||
| 39430b88f3 | |||
| 2dc6fd8158 | |||
| a26cb5d0cf | |||
| a8d178ad6b | |||
| 95f52e061b | |||
| 95c1cd5312 | |||
| 7c9d61cbe5 | |||
| c35ff62880 | |||
| c3aa02447c | |||
| 1b96934164 | |||
| 4205a8fc8b | |||
| 114ca5af88 | |||
| 4ecc0d10f8 | |||
| ce888ccf84 | |||
| 6e8a9d7631 | |||
| 5f34c4e959 | |||
| 7a4c9c1583 | |||
| c3c783cec9 | |||
| d4b3c31c8e | |||
| dbdf1908e7 | |||
| 756e39a498 | |||
| 263d061cb1 | |||
| 01895c78c3 | |||
| 40f25bbd53 | |||
| 670021fcc8 | |||
| 5fd69e4ad7 | |||
| f6d35d0abb | |||
| c52fb6c563 | |||
| 672d0457fe | |||
| 1112e01cbf | |||
| 3125c4f230 | |||
| fb14e6701b | |||
| b7f743021f | |||
| 4c20e2a7ae | |||
| 91de8a4fa9 | |||
| a9fdcd6f56 | |||
| b797877b32 | |||
| 32562bf16a | |||
| d351ef134e | |||
| 6a873433f1 | |||
| fda4d88c81 | |||
| 22bd83b467 | |||
| bd00040e0d | |||
| 69594b54ed |
@@ -1,4 +1,8 @@
|
|||||||
---
|
---
|
||||||
|
- name: Reload systemd
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
daemon_reload: yes
|
||||||
|
|
||||||
- name: Reload systemd and restart WOL
|
- name: Reload systemd and restart WOL
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
name: wol
|
name: wol
|
||||||
@@ -13,3 +17,4 @@
|
|||||||
- name: Trigger_udev_net
|
- name: Trigger_udev_net
|
||||||
ansible.builtin.command: udevadm trigger --subsystem-match=net
|
ansible.builtin.command: udevadm trigger --subsystem-match=net
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
|
||||||
|
|||||||
276
tasks/main.yml
276
tasks/main.yml
@@ -8,19 +8,6 @@
|
|||||||
state: present
|
state: present
|
||||||
update_cache: true
|
update_cache: true
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# 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
|
# Detect physical NICs with WOL support using Ansible facts
|
||||||
# ============================================================
|
# ============================================================
|
||||||
@@ -30,218 +17,79 @@
|
|||||||
- network
|
- network
|
||||||
when: ansible_facts.interfaces is not defined
|
when: ansible_facts.interfaces is not defined
|
||||||
|
|
||||||
- name: Get all physical network interfaces with WOL support
|
- name: Display interfaces
|
||||||
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
|
|
||||||
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:
|
ansible.builtin.debug:
|
||||||
msg: >
|
msg: >
|
||||||
Interface {{ item.item }} WOL Status:
|
{{ ansible_facts.interfaces }}
|
||||||
{{ 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
|
# - name: Get interfaces starting with "en" or "eth"
|
||||||
|
# ansible.builtin.set_fact:
|
||||||
|
# en_interfaces: "{{ ansible_facts.interfaces | select('match', '^eth|^ens|^enp') | unique | list }}"
|
||||||
|
|
||||||
|
- name: Get interfaces starting with "en or "eth"
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
wol_mac_addresses: >-
|
en_interfaces: >-
|
||||||
{{
|
{{
|
||||||
wol_final_interfaces
|
ansible_facts.interfaces
|
||||||
| map('extract', hostvars[inventory_hostname]['ansible_' ~ item] | default({}), 'macaddress')
|
| select('match', '^(eth|en)')
|
||||||
| list
|
| list
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
- name: Display debug selected interfaces
|
||||||
|
ansible.builtin.debug:
|
||||||
|
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: "{{ en_interfaces }}"
|
||||||
|
register: wol_supported
|
||||||
|
changed_when: false
|
||||||
|
when: en_interfaces | length > 0
|
||||||
|
|
||||||
|
- 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: "{{ en_interfaces }}"
|
||||||
|
when: en_interfaces | length > 0
|
||||||
|
|
||||||
|
- name: "Set Wake-on-LAN to {{ wol_mode }}"
|
||||||
|
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.0 }}"
|
||||||
|
when:
|
||||||
|
- wol_mode not in item.1.stdout
|
||||||
|
- wol_mode in item.2.stdout
|
||||||
|
|
||||||
|
- 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.0 }}"
|
||||||
|
when:
|
||||||
|
- wol_mode == 'd'
|
||||||
|
- wol_mode not in item.1.stdout
|
||||||
|
|
||||||
|
# ... (everything before this stays unchanged)
|
||||||
|
|
||||||
|
- name: Get MAC addresses
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
wol_mac_addresses: >-
|
||||||
|
{{ wol_mac_addresses | default([]) + [ hostvars[inventory_hostname]['ansible_' ~ item].macaddress ] }}
|
||||||
|
loop: "{{ en_interfaces }}"
|
||||||
|
when: en_interfaces | length > 0
|
||||||
|
|
||||||
|
# ... (any other tasks between them remain unchanged)
|
||||||
|
|
||||||
- name: Report WOL configuration
|
- name: Report WOL configuration
|
||||||
ansible.builtin.debug:
|
ansible.builtin.debug:
|
||||||
msg: |
|
msg: |
|
||||||
Wake-on-LAN Configuration Summary:
|
Wake-on-LAN Configuration Summary:
|
||||||
===================================
|
===================================
|
||||||
Bridges Configured: {{ wol_bridges_list | join(', ') }}
|
Physical Interfaces: {{ en_interfaces | join(', ') }}
|
||||||
Physical Interfaces: {{ wol_final_interfaces | join(', ') }}
|
|
||||||
WOL Mode: {{ wol_mode }}
|
WOL Mode: {{ wol_mode }}
|
||||||
{% if wol_has_bond0 | default(false) %}
|
MAC Addresses: {{ wol_mac_addresses | join(', ') }}
|
||||||
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 %}
|
|
||||||
18
templates/wol-interfaces.service.j2
Normal file
18
templates/wol-interfaces.service.j2
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Set Wake-on-LAN on network interfaces
|
||||||
|
After=network.target
|
||||||
|
Wants=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/bin/bash -c '{% set cmds = [] %}
|
||||||
|
{% for intf, enabled, supported in en_interfaces | zip(wol_enabled.results, wol_supported.results) %}
|
||||||
|
{% if wol_mode in supported.stdout and wol_mode not in enabled.stdout %}
|
||||||
|
{% set _ = cmds.append("/sbin/ethtool -s " ~ intf ~ " wol " ~ wol_mode) %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{{ cmds | join("; ") if cmds | length > 0 else ":" }}'
|
||||||
|
RemainAfterExit=yes
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
Reference in New Issue
Block a user