Execution
Date 22 Jul 2025 13:07:04 +0100
Duration 00:00:05.41
Controller ssh-gw-4.layershift.com
User root
Versions
Ansible 2.16.11
ara 1.7.2 / 1.7.2
Python 3.10.10
Summary
2 Hosts
16 Tasks
16 Results
1 Plays
1 Files
0 Records

File: /home/ssh-gateway/ansible/kuly/get_mac_addresses.yaml

---
- name: Gather all physical interfaces and write to single local file
  hosts: all
  become: true
  gather_facts: false

  # We'll collect results in a global fact on localhost
  vars:
    ignored_prefixes:
      - "lo"
      - "docker"
      - "veth"
      - "virbr"
      - "wl"
      - "wlan"
      - "tun"
      - "tap"

  # Pre-tasks: Gather data per host
  pre_tasks:
    - name: Get default route interface
      command: ip route show default
      register: default_route
      changed_when: false

    - name: Extract main interface from default route
      set_fact:
        main_interface: >-
          {{
            (default_route.stdout.split('dev')[1].split()[0])
            if 'dev' in default_route.stdout else ''
          }}
      when: default_route.stdout != ""

    - name: Fail if no default interface found
      fail:
        msg: "No default route found."
      when: default_route.stdout == "" or ("dev" not in default_route.stdout)

    # Check if managed by NetworkManager as a bridge
    - name: Check active nmcli connections (type:device)
      command: nmcli -t -f TYPE,DEVICE connection show --active
      register: nmcli_connections
      changed_when: false
      ignore_errors: yes

    - name: Determine if main_interface is a bridge
      set_fact:
        is_bridge: >-
          {%
            set result = []
          %}{%
            for line in (nmcli_connections.stdout_lines | default([]))
          %}{%
            set parts = line.split(':')
          %}{%
            if parts[1] == main_interface and parts[0] == 'bridge'
          %}{%
            set _ = result.append(1)
          %}{% endif %}{% endfor %}
          {{ true if result else false }}
      when: nmcli_connections.rc == 0

    - name: Set is_bridge to false if nmcli failed
      set_fact:
        is_bridge: false
      when: nmcli_connections.rc != 0

    # Get slave interface via nmcli if it's a bridge
    - name: Get slave connections (name:device:master)
      command: nmcli -t -f NAME,DEVICE,MASTER connection show --active
      register: nmcli_slaves
      when: is_bridge | bool
      changed_when: false
      ignore_errors: yes

    - name: Find first physical slave interface for the bridge
      set_fact:
        physical_interface: >-
          {%
            set found = []
          %}{%
            for line in (nmcli_slaves.stdout_lines | default([]))
          %}{%
            set parts = line.split(':')
          %}{%
            if parts[2] == main_interface
          %}{%
            set _ = found.append(parts[1])
          %}{% break %}{% endif %}{% endfor %}
          {{ found[0] if found else '' }}
      when: is_bridge | bool

    - name: Use main interface as physical if not a bridge
      set_fact:
        physical_interface: "{{ main_interface }}"
      when: not (is_bridge | bool)

    - name: Fail if no physical interface found from bridge
      fail:
        msg: "Could not find physical interface enslaved to bridge {{ main_interface }}"
      when: is_bridge | bool and (physical_interface is undefined or physical_interface == '')

    # Validate interface is not ignored
    - name: Check if physical interface should be ignored
      set_fact:
        is_ignored: "{{ (ignored_prefixes | select('regex', '^' ~ physical_interface) | list | length) > 0 }}"

    - name: Fail if interface is ignored
      fail:
        msg: "Physical interface {{ physical_interface }} is ignored."
      when: is_ignored | bool

    - name: Verify physical interface exists
      stat:
        path: "/sys/class/net/{{ physical_interface }}"
      register: phys_stat
      failed_when: not phys_stat.stat.exists

    - name: Get MAC address
      command: "cat /sys/class/net/{{ physical_interface }}/address"
      register: mac_result
      changed_when: false

    - name: Set final facts for reporting
      set_fact:
        report_line: "{{ inventory_hostname }} - {{ physical_interface }} - {{ mac_result.stdout }}"

  # Actual task runs only on localhost at the end
  tasks:
    - name: Collect all report lines and write to single file
      delegate_to: localhost
      run_once: true
      copy:
        content: |
          # Hostname - Interface - MAC Address
          {% for host in groups['all'] %}
          {{ hostvars[host].report_line }}
          {% endfor %}
        dest: all_macs.txt
        force: yes

    - name: Confirm output
      delegate_to: localhost
      run_once: true
      debug:
        msg: "All MAC addresses written to all_macs.txt"