diff --git a/files/ansible-playbooks/sot-updater.yml b/files/ansible-playbooks/sot-updater.yml index fd6ead6..007972d 100644 --- a/files/ansible-playbooks/sot-updater.yml +++ b/files/ansible-playbooks/sot-updater.yml @@ -1001,6 +1001,166 @@ failed_when: false ignore_errors: true + # ------ LLDP IPv6 fallback (Dev1 → Dev2 on link-local) ------ + + - name: Dev1 | Try LLDP-derived Dev2 IPv6 (auth to Dev2 failed, IPv4 LLDP unusable) + when: + - tunnel_ok | default(false) + - dev2_passfile_used == 'NONE' + - (dev2_passfile_used_lldp is not defined or dev2_passfile_used_lldp == 'NONE') + delegate_to: localhost + shell: | + DIGITS="{{ inventory_hostname | regex_replace('[^0-9]', '') }}" + sshpass -p '{{ dev1_pass }}' ssh {{ ssh_opts_common }} \ + "{{ dev1_user }}@{{ ansible_host|default(inventory_hostname) }}" \ + "cat /var/run/lldp_server.json 2>/dev/null | grep ${DIGITS} -A 15 | grep 'address_ipv6' | awk -F'\\\"' '{ print \\$4 }' | head -n1" + args: + executable: /bin/bash + register: lldp_ip6_raw + changed_when: false + failed_when: false + ignore_errors: true + + - name: Dev1 | Set LLDP IPv6 fact + when: + - tunnel_ok | default(false) + - dev2_passfile_used == 'NONE' + - (dev2_passfile_used_lldp is not defined or dev2_passfile_used_lldp == 'NONE') + delegate_to: localhost + set_fact: + lldp_dev2_ip6: "{{ lldp_ip6_raw.stdout | default('') | trim }}" + + - name: Dev1 | Show LLDP IPv6 candidate + when: + - tunnel_ok | default(false) + - dev2_passfile_used == 'NONE' + - (dev2_passfile_used_lldp is not defined or dev2_passfile_used_lldp == 'NONE') + delegate_to: localhost + debug: + msg: + - "LLDP-derived Dev2 IPv6 (best-effort): {{ lldp_dev2_ip6 | default('') }}" + + - name: Dev1 | Copy Dev2 passfiles for IPv6 LLDP use + when: + - tunnel_ok | default(false) + - dev2_passfile_used == 'NONE' + - (dev2_passfile_used_lldp is not defined or dev2_passfile_used_lldp == 'NONE') + - lldp_dev2_ip6 is defined + - lldp_dev2_ip6 | length > 0 + copy: + src: "{{ item }}" + dest: "/tmp/{{ item }}" + mode: "0600" + loop: "{{ dev2_passfiles }}" + ignore_errors: true + + - name: Dev1 | Try DEV2 login via LLDP IPv6 (nested SSH via Dev1) + when: + - tunnel_ok | default(false) + - dev2_passfile_used == 'NONE' + - (dev2_passfile_used_lldp is not defined or dev2_passfile_used_lldp == 'NONE') + - lldp_dev2_ip6 is defined + - lldp_dev2_ip6 | length > 0 + delegate_to: localhost + shell: | + IP6="{{ lldp_dev2_ip6 }}" + if [ -z "$IP6" ]; then + exit 1 + fi + + for f in {{ dev2_passfiles | join(' ') }}; do + if sshpass -p '{{ dev1_pass }}' ssh {{ ssh_opts_common }} \ + "{{ dev1_user }}@{{ ansible_host|default(inventory_hostname) }}" \ + "timeout {{ ssh_timeout }}s sshpass -f '/tmp/${f}' ssh \ + -o StrictHostKeyChecking=no -o PubkeyAuthentication=no \ + -o ConnectTimeout={{ ssh_timeout }} \ + '{{ dev2_ssh_user }}@\[${IP6}%{{ dev1_iface }}\]' 'echo ok' " \ + >/dev/null 2>&1; then + echo "$f" + exit 0 + fi + done + exit 1 + args: + executable: /bin/bash + register: dev2_auth_lldp6 + changed_when: false + failed_when: false + ignore_errors: true + + - name: Dev2 | LLDP IPv6 path debug + when: + - tunnel_ok | default(false) + - dev2_passfile_used == 'NONE' + - (dev2_passfile_used_lldp is not defined or dev2_passfile_used_lldp == 'NONE') + - lldp_dev2_ip6 is defined + - lldp_dev2_ip6 | length > 0 + delegate_to: localhost + debug: + msg: + - "LLDP IPv6 candidate={{ lldp_dev2_ip6 | default('') }}" + - "LLDP IPv6 auth rc={{ dev2_auth_lldp6.rc | default('n/a') }}" + - "LLDP IPv6 auth stdout='{{ dev2_auth_lldp6.stdout | default('') | trim }}'" + + - name: Set dev2_passfile_used_lldp6 + when: + - tunnel_ok | default(false) + - dev2_passfile_used == 'NONE' + - (dev2_passfile_used_lldp is not defined or dev2_passfile_used_lldp == 'NONE') + - lldp_dev2_ip6 is defined + - lldp_dev2_ip6 | length > 0 + delegate_to: localhost + set_fact: + dev2_passfile_used_lldp6: >- + {{ + ((dev2_auth_lldp6.rc | default(1)) == 0) + | ternary(dev2_auth_lldp6.stdout | default('') | trim, 'NONE') + }} + + - name: Dev2 | IPv6 LLDP passfile decision + when: + - tunnel_ok | default(false) + - dev2_passfile_used == 'NONE' + - (dev2_passfile_used_lldp is not defined or dev2_passfile_used_lldp == 'NONE') + - lldp_dev2_ip6 is defined + - lldp_dev2_ip6 | length > 0 + delegate_to: localhost + debug: + msg: + - "Using passfile for LLDP IPv6 path: {{ dev2_passfile_used_lldp6 | default('NONE') }}" + + - name: Dev2 | Read firmux (LLDP IPv6 via Dev1) + when: + - tunnel_ok | default(false) + - dev2_passfile_used == 'NONE' + - (dev2_passfile_used_lldp is not defined or dev2_passfile_used_lldp == 'NONE') + - lldp_dev2_ip6 is defined + - lldp_dev2_ip6 | length > 0 + - dev2_passfile_used_lldp6 is defined + - dev2_passfile_used_lldp6 != 'NONE' + delegate_to: localhost + shell: | + IP6="{{ lldp_dev2_ip6 }}" + F="{{ dev2_passfile_used_lldp6 }}" + if [ -z "$IP6" ] || [ -z "$F" ] || [ "$F" = "NONE" ]; then + exit 0 + fi + + sshpass -p '{{ dev1_pass }}' ssh {{ ssh_opts_common }} \ + "{{ dev1_user }}@{{ ansible_host|default(inventory_hostname) }}" \ + "sshpass -f '/tmp/${F}' ssh \ + -o StrictHostKeyChecking=no -o PubkeyAuthentication=no \ + -o ConnectTimeout={{ ssh_timeout }} \ + '{{ dev2_ssh_user }}@\[${IP6}%{{ dev1_iface }}\]' \ + 'cat /usr/lib/release/firmux 2>/dev/null || true'" + args: + executable: /bin/bash + register: dev2_firmux_lldp6 + changed_when: false + failed_when: false + ignore_errors: true + + - name: Dev2 | Read firmux (direct LLDP IP) when: - tunnel_ok | default(false) @@ -1018,17 +1178,24 @@ failed_when: false ignore_errors: true - # ------ Mux: choose whichever firmux output succeeded ------ - - name: Dev2 | Choose firmux output (tunnel or LLDP) + - name: Dev2 | Choose firmux output (tunnel, IPv4 LLDP, then IPv6 LLDP) delegate_to: localhost set_fact: dev2_firmux_text: >- {{ (dev2_firmux.stdout | default('') | trim) if ((dev2_firmux.stdout | default('') | trim | length) > 0) - else (dev2_firmux_lldp.stdout | default('') | trim) + else ( + (dev2_firmux_lldp.stdout | default('') | trim) + if ( + (dev2_firmux_lldp is defined) and + ((dev2_firmux_lldp.stdout | default('') | trim | length) > 0) + ) + else (dev2_firmux_lldp6.stdout | default('') | trim) + ) }} + - name: Dev2 | firmux selection debug delegate_to: localhost debug: @@ -1122,6 +1289,38 @@ failed_when: false ignore_errors: true + - name: Dev2 | Read fw_printenv size (LLDP IPv6 via Dev1) + when: + - tunnel_ok | default(false) + - dev2_passfile_used == 'NONE' + - (dev2_passfile_used_lldp is not defined or dev2_passfile_used_lldp == 'NONE') + - lldp_dev2_ip6 is defined + - lldp_dev2_ip6 | length > 0 + - dev2_passfile_used_lldp6 is defined + - dev2_passfile_used_lldp6 != 'NONE' + delegate_to: localhost + shell: | + IP6="{{ lldp_dev2_ip6 }}" + F="{{ dev2_passfile_used_lldp6 }}" + if [ -z "$IP6" ] || [ -z "$F" ] || [ "$F" = "NONE" ]; then + exit 0 + fi + + sshpass -p '{{ dev1_pass }}' ssh {{ ssh_opts_common }} \ + "{{ dev1_user }}@{{ ansible_host|default(inventory_hostname) }}" \ + "sshpass -f '/tmp/${F}' ssh \ + -o StrictHostKeyChecking=no -o PubkeyAuthentication=no \ + -o ConnectTimeout={{ ssh_timeout }} \ + '{{ dev2_ssh_user }}@\[${IP6}%{{ dev1_iface }}\]' \ + 'fw_printenv 2>/dev/null | wc -l'" + args: + executable: /bin/bash + register: dev2_bootenv_wc_lldp6 + changed_when: false + failed_when: false + ignore_errors: true + + - name: Dev2 | Select fw_printenv size output delegate_to: localhost set_fact: @@ -1140,10 +1339,19 @@ ((dev2_bootenv_wc_lldp.rc | default(1)) == 0) and ((dev2_bootenv_wc_lldp.stdout | default('') | trim | length) > 0) ) - else '' + else ( + (dev2_bootenv_wc_lldp6.stdout | default('') | trim) + if ( + (dev2_bootenv_wc_lldp6 is defined) and + ((dev2_bootenv_wc_lldp6.rc | default(1)) == 0) and + ((dev2_bootenv_wc_lldp6.stdout | default('') | trim | length) > 0) + ) + else '' + ) ) }} + - name: Dev2 | Normalize indoor_bootenv value delegate_to: localhost set_fact: