1010
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
# ptsd-migration.yml — PPPoE to super DHCP | Phase 0 connectivity + Phase 1 commands (initially uptime)
|
||||
# ptsd-migration.yml — PPPoE to super DHCP
|
||||
# Phase 0: connectivity + safe identity guard
|
||||
# Phase 1: wrapper (no device changes yet; uptime will be introduced later)
|
||||
|
||||
- name: "PPPoE to super DHCP | Phase 0 | Prove reliable DEV2 connectivity (no device commands)"
|
||||
- name: "PPPoE to super DHCP | Phase 0+1 | Reliable DEV2 connectivity + wrapper"
|
||||
hosts: all
|
||||
gather_facts: no
|
||||
|
||||
@@ -31,6 +33,7 @@
|
||||
- "basicpass2"
|
||||
|
||||
# SSH options used from controller
|
||||
# LogLevel=ERROR is critical: suppresses hostkey "Permanently added..." lines that poison parsing.
|
||||
ssh_opts_common: >-
|
||||
-o PreferredAuthentications=password
|
||||
-o PubkeyAuthentication=no
|
||||
@@ -38,6 +41,8 @@
|
||||
-o UserKnownHostsFile=/dev/null
|
||||
-o NumberOfPasswordPrompts=1
|
||||
-o ConnectTimeout=30
|
||||
-o ConnectionAttempts=1
|
||||
-o LogLevel=ERROR
|
||||
|
||||
# Debugging toggle
|
||||
debugging: true
|
||||
@@ -120,7 +125,6 @@
|
||||
- "LLDP IPv6 candidate={{ lldp_dev2_ip6 | default('<none>') }}"
|
||||
|
||||
# ============================ PRIMARY PATH DECISION (same as indoor-restart-cloudagent) ============================
|
||||
# If LLDP gives 192.168.* we use it as dev2_host for the tunnel target
|
||||
- name: Override dev2_host from LLDP when candidate is 192.168.x.x
|
||||
when: (lldp_ip_class | trim) == "192_168" and (lldp_dev2_ip | trim | length > 0)
|
||||
delegate_to: localhost
|
||||
@@ -128,7 +132,6 @@
|
||||
dev2_host: "{{ lldp_dev2_ip | trim }}"
|
||||
changed_when: false
|
||||
|
||||
# Direct LLDP only if 10.x
|
||||
- name: Set connection method initial (direct_lldp if 10.x else tunnel)
|
||||
delegate_to: localhost
|
||||
ansible.builtin.set_fact:
|
||||
@@ -144,7 +147,6 @@
|
||||
HOST="{{ lldp_dev2_ip }}"
|
||||
sshpass -f basicpass ssh {{ ssh_opts_common }} \
|
||||
-o AddressFamily=inet \
|
||||
-o ConnectionAttempts=1 \
|
||||
"{{ dev2_ssh_user }}@${HOST}" echo OK >/dev/null 2>&1
|
||||
args: { executable: /bin/bash }
|
||||
register: dev2_lldp_try_basicpass
|
||||
@@ -166,7 +168,6 @@
|
||||
HOST="{{ lldp_dev2_ip }}"
|
||||
sshpass -f basicpass2 ssh {{ ssh_opts_common }} \
|
||||
-o AddressFamily=inet \
|
||||
-o ConnectionAttempts=1 \
|
||||
"{{ dev2_ssh_user }}@${HOST}" echo OK >/dev/null 2>&1
|
||||
args: { executable: /bin/bash }
|
||||
register: dev2_lldp_try_basicpass2
|
||||
@@ -327,7 +328,6 @@
|
||||
PORT="{{ _local_port }}"
|
||||
sshpass -f basicpass ssh {{ ssh_opts_common }} \
|
||||
-o AddressFamily=inet \
|
||||
-o ConnectionAttempts=1 \
|
||||
-p "$PORT" root@127.0.0.1 echo OK >/dev/null 2>&1
|
||||
args: { executable: /bin/bash }
|
||||
register: dev2_try_basicpass
|
||||
@@ -349,7 +349,6 @@
|
||||
PORT="{{ _local_port }}"
|
||||
sshpass -f basicpass2 ssh {{ ssh_opts_common }} \
|
||||
-o AddressFamily=inet \
|
||||
-o ConnectionAttempts=1 \
|
||||
-p "$PORT" root@127.0.0.1 echo OK >/dev/null 2>&1
|
||||
args: { executable: /bin/bash }
|
||||
register: dev2_try_basicpass2
|
||||
@@ -370,33 +369,56 @@
|
||||
dev2_passfile_used_tunnel: "NONE"
|
||||
changed_when: false
|
||||
|
||||
# Safety guard (only if we discovered MAC)
|
||||
- name: Read remote eth0 MAC via tunnel (guard)
|
||||
when: dev2_conn_method == "tunnel" and dev2_passfile_used_tunnel != "NONE" and (dev2_mac | default('') | trim | length > 0)
|
||||
# ============================ SAFETY GUARD: MAC CHECK (only if tunnel auth succeeded) ============================
|
||||
- name: Read remote eth0 MAC via tunnel (guard, sanitized)
|
||||
when:
|
||||
- dev2_conn_method == "tunnel"
|
||||
- (dev2_passfile_used_tunnel | default('NONE')) != "NONE"
|
||||
- (dev2_mac | default('') | length) > 0
|
||||
delegate_to: localhost
|
||||
ansible.builtin.shell: |
|
||||
set -e
|
||||
PORT="{{ _local_port }}"
|
||||
sshpass -f "{{ dev2_passfile_used_tunnel }}" ssh {{ ssh_opts_common }} \
|
||||
-o AddressFamily=inet \
|
||||
-o ConnectionAttempts=1 \
|
||||
-p "$PORT" "{{ dev2_ssh_user }}@127.0.0.1" \
|
||||
"cat /sys/class/net/eth0/address 2>/dev/null || echo" 2>&1
|
||||
"cat /sys/class/net/eth0/address 2>/dev/null || ip link show eth0 2>/dev/null" 2>&1
|
||||
args: { executable: /bin/bash }
|
||||
register: dev2_eth0_mac_read
|
||||
register: dev2_eth0_mac_raw
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Normalize remote eth0 MAC (extract last MAC-like token)
|
||||
when:
|
||||
- dev2_conn_method == "tunnel"
|
||||
- (dev2_passfile_used_tunnel | default('NONE')) != "NONE"
|
||||
- (dev2_mac | default('') | length) > 0
|
||||
delegate_to: localhost
|
||||
ansible.builtin.set_fact:
|
||||
remote_eth0_mac: >-
|
||||
{{
|
||||
(
|
||||
(dev2_eth0_mac_raw.stdout | default('') | regex_replace('\r','')) ~ "\n" ~
|
||||
(dev2_eth0_mac_raw.stderr | default('') | regex_replace('\r',''))
|
||||
)
|
||||
| regex_findall('([0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5})')
|
||||
| last
|
||||
| default('')
|
||||
| lower
|
||||
}}
|
||||
changed_when: false
|
||||
|
||||
- name: Abort if remote eth0 MAC != discovered DEV2 MAC
|
||||
when: dev2_conn_method == "tunnel" and dev2_passfile_used_tunnel != "NONE" and (dev2_mac | default('') | trim | length > 0) and ((dev2_eth0_mac_read.stdout | default('') | trim | lower) != (dev2_mac | trim | lower))
|
||||
when:
|
||||
- dev2_conn_method == "tunnel"
|
||||
- (dev2_passfile_used_tunnel | default('NONE')) != "NONE"
|
||||
- (dev2_mac | default('') | length) > 0
|
||||
- (remote_eth0_mac | default('') | length) > 0
|
||||
- (remote_eth0_mac | lower) != (dev2_mac | lower)
|
||||
ansible.builtin.fail:
|
||||
msg: >
|
||||
msg: >-
|
||||
Safety stop: tunnel reached wrong device.
|
||||
expected_dev2_mac={{ dev2_mac | trim }},
|
||||
remote_eth0_mac={{ dev2_eth0_mac_read.stdout | default('') | trim }}.
|
||||
expected_dev2_mac={{ dev2_mac }}, remote_eth0_mac={{ remote_eth0_mac }}
|
||||
|
||||
# ============================ FALLBACKS (only if tunnel/direct failed) ============================
|
||||
# LLDP IPv4 fallback is only for 10.x in sot-updater; keep that behavior:
|
||||
- name: Try DEV2 login via LLDP IPv4 10.x (fallback if tunnel auth failed)
|
||||
when:
|
||||
- dev2_conn_method == "tunnel"
|
||||
@@ -409,7 +431,7 @@
|
||||
for f in {{ dev2_passfiles | join(' ') }}; do
|
||||
if timeout 20s sshpass -f "$f" ssh {{ ssh_opts_common }} \
|
||||
-o AddressFamily=inet \
|
||||
-o ConnectionAttempts=1 -o ConnectTimeout=10 \
|
||||
-o ConnectTimeout=10 \
|
||||
"{{ dev2_ssh_user }}@${HOST}" echo OK >/dev/null 2>&1; then
|
||||
echo "$f"; exit 0
|
||||
fi
|
||||
@@ -431,7 +453,6 @@
|
||||
dev2_passfile_used_lldp4: "{{ (dev2_auth_lldp4.rc == 0) | ternary((dev2_auth_lldp4.stdout | trim), 'NONE') }}"
|
||||
changed_when: false
|
||||
|
||||
# LLDP IPv6 nested fallback (last resort): only if tunnel and lldp4 failed
|
||||
- name: Copy DEV2 passfiles to DEV1 for IPv6 nested SSH (last resort)
|
||||
when:
|
||||
- dev2_conn_method == "tunnel"
|
||||
@@ -457,10 +478,10 @@
|
||||
IP6="{{ lldp_dev2_ip6 }}"
|
||||
for f in {{ dev2_passfiles | join(' ') }}; do
|
||||
if sshpass -p '{{ dev1_pass }}' ssh {{ ssh_opts_common }} \
|
||||
-o ConnectionAttempts=1 \
|
||||
"{{ dev1_user }}@{{ ansible_host|default(inventory_hostname) }}" \
|
||||
"timeout {{ ssh_timeout }}s sshpass -f '/tmp/${f}' ssh \
|
||||
-o StrictHostKeyChecking=no -o PubkeyAuthentication=no \
|
||||
-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR \
|
||||
-o PubkeyAuthentication=no \
|
||||
-o PreferredAuthentications=password -o NumberOfPasswordPrompts=1 \
|
||||
-o ConnectTimeout={{ ssh_timeout }} \
|
||||
'{{ dev2_ssh_user }}@\[${IP6}%{{ dev1_iface }}\]' 'echo OK' " \
|
||||
@@ -527,14 +548,13 @@
|
||||
- "lldp ipv4={{ lldp_dev2_ip | default('') }}"
|
||||
- "lldp ipv6={{ lldp_dev2_ip6 | default('') }}"
|
||||
|
||||
# ---- Wrapper: produce a single executable snippet in dev2_exec_cmd ----
|
||||
# ---- Wrapper: produce a single executable snippet in dev2_exec_cmd (for Phase 1 commands later) ----
|
||||
- name: Build DEV2 exec wrapper (controller-side) for Phase 1 commands
|
||||
delegate_to: localhost
|
||||
ansible.builtin.set_fact:
|
||||
dev2_exec_cmd: |
|
||||
set -e
|
||||
MODE="{{ dev2_conn_final }}"
|
||||
# DEV2_CMD is injected by Phase 1 tasks
|
||||
if [ -z "${DEV2_CMD:-}" ]; then
|
||||
echo "ERROR: DEV2_CMD is empty" >&2
|
||||
exit 2
|
||||
@@ -546,7 +566,6 @@
|
||||
PASS="{{ dev2_passfile_used_tunnel }}"
|
||||
sshpass -f "$PASS" ssh {{ ssh_opts_common }} \
|
||||
-o AddressFamily=inet \
|
||||
-o ConnectionAttempts=1 \
|
||||
-p "$PORT" "{{ dev2_ssh_user }}@127.0.0.1" \
|
||||
"{{ pathprefix }} ${DEV2_CMD}" 2>&1
|
||||
;;
|
||||
@@ -555,7 +574,6 @@
|
||||
PASS="{{ dev2_passfile_used_direct }}"
|
||||
sshpass -f "$PASS" ssh {{ ssh_opts_common }} \
|
||||
-o AddressFamily=inet \
|
||||
-o ConnectionAttempts=1 \
|
||||
"{{ dev2_ssh_user }}@${HOST}" \
|
||||
"{{ pathprefix }} ${DEV2_CMD}" 2>&1
|
||||
;;
|
||||
@@ -564,7 +582,6 @@
|
||||
PASS="{{ dev2_passfile_used_lldp4 }}"
|
||||
sshpass -f "$PASS" ssh {{ ssh_opts_common }} \
|
||||
-o AddressFamily=inet \
|
||||
-o ConnectionAttempts=1 \
|
||||
"{{ dev2_ssh_user }}@${HOST}" \
|
||||
"{{ pathprefix }} ${DEV2_CMD}" 2>&1
|
||||
;;
|
||||
@@ -572,10 +589,10 @@
|
||||
IP6="{{ lldp_dev2_ip6 }}"
|
||||
F="{{ dev2_passfile_used_lldp6 }}"
|
||||
sshpass -p "{{ dev1_pass }}" ssh {{ ssh_opts_common }} \
|
||||
-o ConnectionAttempts=1 \
|
||||
"{{ dev1_user }}@{{ ansible_host | default(inventory_hostname) }}" \
|
||||
"sshpass -f '/tmp/${F}' ssh \
|
||||
-o StrictHostKeyChecking=no -o PubkeyAuthentication=no \
|
||||
-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR \
|
||||
-o PubkeyAuthentication=no \
|
||||
-o PreferredAuthentications=password -o NumberOfPasswordPrompts=1 \
|
||||
-o ConnectTimeout={{ ssh_timeout }} \
|
||||
'{{ dev2_ssh_user }}@\[${IP6}%{{ dev1_iface }}\]' \
|
||||
@@ -588,31 +605,6 @@
|
||||
esac
|
||||
changed_when: false
|
||||
|
||||
|
||||
- name: "PPPoE to super DHCP | Phase 1 | On-device actions (initially uptime)"
|
||||
hosts: all
|
||||
gather_facts: no
|
||||
|
||||
tasks:
|
||||
# NOTE: Phase 1 assumes Phase 0 facts are still available in-play execution.
|
||||
# If your runner executes plays independently, merge into one play.
|
||||
- name: DEV2 | exec wrapper run (Phase 1)
|
||||
delegate_to: localhost
|
||||
ansible.builtin.shell: |
|
||||
export DEV2_CMD="uptime"
|
||||
{{ dev2_exec_cmd }}
|
||||
args: { executable: /bin/bash }
|
||||
register: dev2_exec
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: DEV2 | uptime (stdout)
|
||||
delegate_to: localhost
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "DEV2 uptime via {{ dev2_conn_final }}:"
|
||||
- "{{ dev2_exec.stdout | default('') | regex_replace('\r','') }}"
|
||||
|
||||
post_tasks:
|
||||
- name: Cleanup note
|
||||
delegate_to: localhost
|
||||
|
||||
Reference in New Issue
Block a user