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
|
||||
ansible.builtin.systemd:
|
||||
name: wol
|
||||
@@ -13,3 +17,4 @@
|
||||
- name: Trigger_udev_net
|
||||
ansible.builtin.command: udevadm trigger --subsystem-match=net
|
||||
changed_when: false
|
||||
|
||||
|
||||
276
tasks/main.yml
276
tasks/main.yml
@@ -8,19 +8,6 @@
|
||||
state: present
|
||||
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
|
||||
# ============================================================
|
||||
@@ -30,218 +17,79 @@
|
||||
- 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
|
||||
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
|
||||
- name: Display interfaces
|
||||
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
|
||||
{{ ansible_facts.interfaces }}
|
||||
|
||||
- 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:
|
||||
wol_mac_addresses: >-
|
||||
en_interfaces: >-
|
||||
{{
|
||||
wol_final_interfaces
|
||||
| map('extract', hostvars[inventory_hostname]['ansible_' ~ item] | default({}), 'macaddress')
|
||||
ansible_facts.interfaces
|
||||
| select('match', '^(eth|en)')
|
||||
| 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
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
Wake-on-LAN Configuration Summary:
|
||||
===================================
|
||||
Bridges Configured: {{ wol_bridges_list | join(', ') }}
|
||||
Physical Interfaces: {{ wol_final_interfaces | join(', ') }}
|
||||
Physical Interfaces: {{ en_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 %}
|
||||
MAC Addresses: {{ wol_mac_addresses | join(', ') }}
|
||||
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