Files
ansible_proxmox_WOL/tasks/main.yml
Jose 71bd365483 refactor ♻️: Refactor regex and filter logic for cleaner bridge link extraction
This refactoring improves the efficiency and readability of the regex patterns used for extracting bridge links by simplifying the filter logic.
2025-12-23 22:22:40 +01:00

236 lines
7.9 KiB
YAML

---
# ============================================================
# Install required packages
# ============================================================
- name: Install required packages
ansible.builtin.apt:
name: ethtool
state: present
update_cache: yes
# ============================================================
# 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 for all bridges (including bond0)
# ============================================================
- name: Get bridge link information
ansible.builtin.command: "bridge link show"
register: bridge_links
changed_when: false
check_mode: false
- name: Get bond info
ansible.builtin.command: "cat /proc/net/bonding/bond0"
register: bond_info
changed_when: false
failed_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+:')
| first | default('')
)
})
}}
loop: "{{ wol_bridges_list }}"
loop_control:
label: "{{ item }}"
- name: Check for bond0 backing
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)
when: bond_info.rc == 0
# ============================================================
# 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 or set wol_interfaces explicitly.
Bridge output:
{{ bridge_links.stdout_lines | join('\n') }}
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 }}"
# ============================================================
# Validate WOL capability
# ============================================================
- name: Check WOL capability on all detected NICs
ansible.builtin.command: "ethtool {{ item }}"
register: wol_capabilities_check
changed_when: false
failed_when: false
loop: "{{ wol_final_interfaces }}"
loop_control:
label: "{{ item }}"
- name: Validate all NICs support WOL
ansible.builtin.assert:
that:
- item.stdout is search('Supports Wake-on:.*[gGdDpPuU]')
fail_msg: "Interface {{ item.item }} does not support Wake-on-LAN"
loop: "{{ wol_capabilities_check.results }}"
loop_control:
label: "{{ item.item }}"
# ============================================================
# 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 %}
register: udev_rules_changed
- name: Reload udev rules
ansible.builtin.command: udevadm control --reload
changed_when: false
when: udev_rules_changed is changed
- name: Trigger udev for network interfaces
ansible.builtin.command: udevadm trigger --subsystem-match=net
changed_when: false
when: udev_rules_changed is changed
# ============================================================
# 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 %}