This commit is contained in:
2025-11-05 22:51:38 +02:00
parent bcefdd55c1
commit e064ffbeb2

View File

@@ -1,7 +1,6 @@
--- # sot-updater.yml — Read fw on Dev1 + Dev2, publish to NetBox via Rabbit (telemetry mode, no upgrade scheduling)
# sot-updater.yml — Read fw on Dev1 + Dev2, publish to NetBox via Rabbit (telemetry mode)
- name: Read fw on Dev1 + Dev2, publish NetBox custom fields (full base, AIRPINGs, soft-fail telemetry) - name: Read fw on Dev1 + Dev2, publish NetBox custom fields (full base, AIRPINGs, soft-fail telemetry, no upgrade scheduling)
hosts: all hosts: all
gather_facts: no gather_facts: no
@@ -37,6 +36,9 @@
rmq_exchange: "{{ lookup('env','RMQ_EXCHANGE') | default('controls', true) }}" rmq_exchange: "{{ lookup('env','RMQ_EXCHANGE') | default('controls', true) }}"
control_queue: "{{ lookup('env','CONTROLQUEUE') | default('queue_controls', true) }}" control_queue: "{{ lookup('env','CONTROLQUEUE') | default('queue_controls', true) }}"
# --- Retry attempt tracking (default 0 if missing) ---
requeue_attempt: "{{ (lookup('env','REQUEUE_ATTEMPT') | default('0', true)) | int }}"
pre_tasks: pre_tasks:
- name: "NB preflight | Verify script exists" - name: "NB preflight | Verify script exists"
delegate_to: localhost delegate_to: localhost
@@ -44,7 +46,7 @@
path: "/opt/containers/ansible-worker/app/nb_onedevice_update.py" path: "/opt/containers/ansible-worker/app/nb_onedevice_update.py"
register: nb_script register: nb_script
- name: "NB preflight | Abort softly if script missing (path typo?)" - name: "NB preflight | Abort softly if script missing"
when: not nb_script.stat.exists when: not nb_script.stat.exists
delegate_to: localhost delegate_to: localhost
ansible.builtin.debug: ansible.builtin.debug:
@@ -52,7 +54,7 @@
- "NB preflight skipped: /opt/containers/ansible-worker/app/nb_onedevice_update.py not found." - "NB preflight skipped: /opt/containers/ansible-worker/app/nb_onedevice_update.py not found."
- "Tip: adjust chdir/path or script name." - "Tip: adjust chdir/path or script name."
- name: "NB preflight | Run nb_onedevice_update.py for {{ inventory_hostname }} (chatty)" - name: "NB preflight | Run nb_onedevice_update.py for {{ inventory_hostname }}"
when: nb_script.stat.exists when: nb_script.stat.exists
delegate_to: localhost delegate_to: localhost
environment: environment:
@@ -70,7 +72,7 @@
- name: "NB preflight | Show results" - name: "NB preflight | Show results"
when: nb_script.stat.exists when: nb_script.stat.exists
delegate_to: localhost delegate_to: localhost
ansible.builtin.debug: debug:
msg: msg:
- "rc={{ nb_preflight.rc }}" - "rc={{ nb_preflight.rc }}"
- "stdout_lines:" - "stdout_lines:"
@@ -79,8 +81,58 @@
- "{{ (nb_preflight.stderr_lines | default(['<no stderr>'])) }}" - "{{ (nb_preflight.stderr_lines | default(['<no stderr>'])) }}"
- "raw stdout (joined): {{ nb_preflight.stdout | default('') | trim }}" - "raw stdout (joined): {{ nb_preflight.stdout | default('') | trim }}"
# --- Parse the OK line robustly (token-based) --- # --- Detect cloud failure (rc==3 or FAIL Cloud line) ---
- name: "NB preflight | Parse OK line (token)" - name: "NB preflight | Detect cloud failure"
when: nb_script.stat.exists
delegate_to: localhost
vars:
_lines: >-
{{ (nb_preflight.stdout | default('') | regex_replace('\r','')) | split('\n') | map('trim') | list }}
_cloud_fail_line: >-
{{ (_lines | select('match', '^FAIL\\s+Cloud\\b') | list | last | default('')) }}
_rc_is_3: "{{ (nb_preflight.rc | default(1)) | int == 3 }}"
set_fact:
cloud_fail_line: "{{ _cloud_fail_line }}"
cloud_bad: "{{ _rc_is_3 or (( _cloud_fail_line | length ) > 0) }}"
# --- Extract upgrade_cmd line if any ---
- name: "NB preflight | Extract upgrade_cmd line"
when: nb_script.stat.exists
delegate_to: localhost
set_fact:
nb_upgrade_line: >-
{{
(
nb_preflight.stdout_lines | default([]) | map('regex_replace','\r','') | map('trim')
| select('match', '^NB:\\s*upgrade_cmd\\s*=')
| list | first
) | default('')
}}
- name: "NB preflight | Parse upgrade_cmd value"
when: nb_upgrade_line | length > 0
delegate_to: localhost
shell: |
printf '%s\n' "{{ nb_upgrade_line }}" | awk -F'=' '{print $2}' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//'
args: { executable: /bin/bash }
register: up_cmd_sh
changed_when: false
- name: "NB preflight | Set parsed upgrade_cmd"
when: nb_upgrade_line | length > 0
delegate_to: localhost
set_fact:
nb_upgrade_cmd: "{{ up_cmd_sh.stdout | default('') | trim }}"
- name: "NB preflight | Debug parsed upgrade_cmd"
when: nb_upgrade_line | length > 0
delegate_to: localhost
debug:
msg:
- "upgrade_cmd='{{ nb_upgrade_cmd }}' (len={{ nb_upgrade_cmd|length }})"
# --- Parse OK line & detect IP change ---
- name: "NB preflight | Parse OK line"
when: nb_script.stat.exists when: nb_script.stat.exists
delegate_to: localhost delegate_to: localhost
vars: vars:
@@ -100,17 +152,16 @@
) )
}} }}
set_fact: set_fact:
nb_ok: "{{ (nb_preflight.rc | default(1)) == 0 and (nb_ok_line | length) > 0 }}" nb_ok: "{{ (nb_preflight.rc|default(1)) == 0 and (nb_ok_line|length)>0 }}"
nb_dev: "{{ (nb_tokens | first | default('')) if (nb_tokens|length>0) else '' }}" nb_dev: "{{ (nb_tokens|first|default('')) if (nb_tokens|length>0) else '' }}"
nb_ip: "{{ nb_kv.get('ip', '') }}" nb_ip: "{{ nb_kv.get('ip','') }}"
nb_fw: "{{ nb_kv.get('fw', '') }}" nb_fw: "{{ nb_kv.get('fw','') }}"
nb_node: "{{ nb_kv.get('node', '') }}" nb_node: "{{ nb_kv.get('node','') }}"
nb_sector: "{{ nb_kv.get('sector', '') }}" nb_sector: "{{ nb_kv.get('sector','') }}"
nb_small: "{{ nb_kv.get('small', '') }}" nb_small: "{{ nb_kv.get('small','') }}"
nb_ok_line: "{{ nb_ok_line }}" nb_ok_line: "{{ nb_ok_line }}"
# --- Detect "cloud vs NetBox (before update) was different" (regex-free, robust) - name: "NB preflight | Detect IP change"
- name: "NB preflight | Detect whether IP changed (pre-update)"
when: nb_script.stat.exists when: nb_script.stat.exists
delegate_to: localhost delegate_to: localhost
vars: vars:
@@ -124,40 +175,22 @@
nb_ip_changed: "{{ reason != 'none' }}" nb_ip_changed: "{{ reason != 'none' }}"
nb_change_reason: "{{ reason }}" nb_change_reason: "{{ reason }}"
# --- Detect cloud error (network/HTTP/500s) ---
- name: "NB preflight | Detect cloud error"
when: nb_script.stat.exists
delegate_to: localhost
vars:
out: "{{ (nb_preflight.stdout | default('') | regex_replace('\r','')) }}"
set_fact:
nb_cloud_error: "{{ (nb_preflight.rc | default(1)) != 0 or ('FAIL ' in out) }}"
nb_cloud_error_rc: "{{ nb_preflight.rc | default('n/a') }}"
- name: "NB preflight | Verdict" - name: "NB preflight | Verdict"
when: nb_script.stat.exists when: nb_script.stat.exists
delegate_to: localhost delegate_to: localhost
ansible.builtin.debug: debug:
msg: msg:
- >- - "Preflight verdict:"
NB preflight verdict: - "Cloud fail: {{ cloud_bad|default(false) }}"
{{ 'IP CHANGED (will requeue)' if (nb_ip_changed|default(false)) else - "IP changed: {{ nb_ip_changed|default(false) }}"
('CLOUD ERROR (will requeue)' if (nb_cloud_error|default(false)) else - "Reason: {{ nb_change_reason|default('none') }}"
'OK (continue)') }}
- "Reason (IP): {{ nb_change_reason | default('none') }}"
- "rc={{ nb_cloud_error_rc | default('n/a') }}"
- >-
First FAIL line (if any):
{{
((nb_preflight.stdout | default('')) | regex_replace('\r','')).
split('\n') | select('search','^FAIL ') | list | first | default('<none>')
}}
# --- If IP changed or cloud errored → publish a 3s delayed 'sot-updater' task and stop this host --- # --- Requeue control: only 3 attempts, 300s apart ---
- name: "NB preflight | Publish delayed requeue (3s) and stop" - name: "NB preflight | Publish delayed requeue (300s) if needed"
when: when:
- nb_script.stat.exists - nb_script.stat.exists
- (nb_ip_changed | default(false)) or (nb_cloud_error | default(false)) - (cloud_bad | default(false)) or (nb_ip_changed | default(false))
- (requeue_attempt | int) < 3
delegate_to: localhost delegate_to: localhost
uri: uri:
url: "http://{{ rmq_host }}:{{ rmq_port }}/api/exchanges/app/deviceconfig.delayed/publish" url: "http://{{ rmq_host }}:{{ rmq_port }}/api/exchanges/app/deviceconfig.delayed/publish"
@@ -167,16 +200,18 @@
force_basic_auth: true force_basic_auth: true
status_code: 200 status_code: 200
return_content: yes return_content: yes
headers: headers: { content-type: "application/json" }
content-type: "application/json"
body_format: json body_format: json
body: body:
properties: properties:
content_type: "application/json" content_type: "application/json"
headers: headers: { x-delay: 300000 } # 300 seconds = 5 minutes
x-delay: 3000
routing_key: "deviceconfig" routing_key: "deviceconfig"
payload: "{{ {'inscope_device': (ansible_hostname | default(inventory_hostname)), 'task_name': 'sot-updater'} | to_json }}" payload: "{{ {
'inscope_device': (ansible_hostname | default(inventory_hostname)),
'task_name': 'sot-updater',
'attempt': ((requeue_attempt | int) + 1)
} | to_json }}"
payload_encoding: "string" payload_encoding: "string"
register: rmq_requeue register: rmq_requeue
changed_when: false changed_when: false
@@ -185,28 +220,27 @@
- name: "NB preflight | Log requeue publish response" - name: "NB preflight | Log requeue publish response"
when: when:
- nb_script.stat.exists - nb_script.stat.exists
- (nb_ip_changed | default(false)) or (nb_cloud_error | default(false)) - ((cloud_bad | default(false)) or (nb_ip_changed | default(false)))
- (requeue_attempt | int) < 3
delegate_to: localhost delegate_to: localhost
ansible.builtin.debug: debug:
msg: msg:
- "RMQ publish URL: http://{{ rmq_host }}:{{ rmq_port }}/api/exchanges/app/deviceconfig.delayed/publish" - "Requeue published (attempt {{ requeue_attempt+1 }})"
- "HTTP status: {{ rmq_requeue.status | default('unknown') }}" - "HTTP: {{ rmq_requeue.status|default('n/a') }}"
- "Parsed JSON: {{ rmq_requeue.json | default('<none>') }}" - "JSON: {{ rmq_requeue.json|default('<none>') }}"
- "Raw content: {{ rmq_requeue.content | default('<none>') }}"
- name: "NB preflight | Stop further tasks for this host" - name: "NB preflight | Stop host after requeue"
when: when:
- nb_script.stat.exists - nb_script.stat.exists
- (nb_ip_changed | default(false)) or (nb_cloud_error | default(false)) - ((cloud_bad | default(false)) or (nb_ip_changed | default(false)))
meta: end_host meta: end_host
# --- If neither IP changed nor cloud error → optional 1s pause, then continue normally --- - name: "NB preflight | Pause 1s if OK"
- name: "NB preflight | Pause 1s"
when: when:
- nb_script.stat.exists - nb_script.stat.exists
- not ((nb_ip_changed | default(false)) or (nb_cloud_error | default(false))) - not ((cloud_bad | default(false)) or (nb_ip_changed | default(false)))
delegate_to: localhost delegate_to: localhost
ansible.builtin.pause: pause:
seconds: 1 seconds: 1
tasks: tasks:
@@ -214,7 +248,7 @@
debug: debug:
msg: msg:
- "Device: {{ inventory_hostname }}" - "Device: {{ inventory_hostname }}"
- "Mode: report-only (soft-fail; publish journals on failures)" - "Mode: report-only (soft-fail telemetry)"
# ----------------------- Temp IP on DEV1 ----------------------- # ----------------------- Temp IP on DEV1 -----------------------
- name: Add temporary IP on DEV1 - name: Add temporary IP on DEV1
@@ -223,85 +257,75 @@
failed_when: false failed_when: false
ignore_errors: true ignore_errors: true
# ---------------------------- Idempotent temp IP on DEV1 ----------------------------
- name: Add temporary IP on DEV1 (tolerate 'File exists') - name: Add temporary IP on DEV1 (tolerate 'File exists')
ansible.builtin.raw: > raw: >
{{ pathprefix }} {{ pathprefix }}
ip a add {{ dev2_side_ip }} dev {{ dev1_iface }} ip a add {{ dev2_side_ip }} dev {{ dev1_iface }}
register: add_ip register: add_ip
changed_when: add_ip.rc == 0 changed_when: add_ip.rc == 0
failed_when: > failed_when: >
add_ip.rc != 0 add_ip.rc != 0
and ('File exists' not in (add_ip.stdout | default(''))) and ('File exists' not in (add_ip.stdout|default('')))
and ('File exists' not in (add_ip.stderr | default(''))) and ('File exists' not in (add_ip.stderr|default('')))
- name: Debug result of adding temp IP to DEV1 - name: Debug temp IP result
ansible.builtin.debug: debug:
msg: msg:
- "add_ip.rc={{ add_ip.rc | default('') }}" - "add_ip.rc={{ add_ip.rc|default('') }}"
- "add_ip.stdout={{ (add_ip.stdout | default('')) | trim }}" - "add_ip.out={{ add_ip.stdout|default('')|trim }}"
- "add_ip.stderr={{ (add_ip.stderr | default('')) | trim }}" - "add_ip.err={{ add_ip.stderr|default('')|trim }}"
# ---------------------------- Discover MAC via bridge FDB and add static ARP ---------------------------- - name: Discover DEV2 MAC via bridge fdb
- name: Discover DEV2 MAC via bridge fdb on DEV1 (best-effort) raw: >
ansible.builtin.raw: >
{{ pathprefix }} {{ pathprefix }}
bridge fdb show {{ dev1_iface }} | grep eth0 | grep -v permanent | grep master | awk '{print $1}' | head -n1 bridge fdb show {{ dev1_iface }} | grep eth0 | grep -v permanent | grep master | awk '{print $1}' | head -n1
register: dev2_mac_scan register: dev2_mac_scan
changed_when: false changed_when: false
failed_when: false failed_when: false
- name: Capture discovered DEV2 MAC (if any) - name: Capture discovered MAC
ansible.builtin.set_fact: set_fact:
dev2_mac: "{{ (dev2_mac_scan.stdout | default('') ) | trim }}" dev2_mac: "{{ dev2_mac_scan.stdout|default('')|trim }}"
changed_when: false
- name: Clear existing ARP entry for DEV2 on DEV1 (best-effort) - name: Clear existing ARP entry
ansible.builtin.raw: > raw: >
{{ pathprefix }} {{ pathprefix }}
ip neigh del {{ dev2_host }} dev {{ dev1_iface }} 2>/dev/null || true ip neigh del {{ dev2_host }} dev {{ dev1_iface }} 2>/dev/null || true
register: dev2_arp_del
changed_when: false changed_when: false
failed_when: false failed_when: false
- name: Add static ARP entry on DEV1 (locks DEV2 IP → discovered MAC) - name: Add static ARP if MAC found
when: dev2_mac is defined and dev2_mac | length > 0 when: dev2_mac|length > 0
ansible.builtin.raw: > raw: >
{{ pathprefix }} {{ pathprefix }}
ip neigh add {{ dev2_host }} lladdr {{ dev2_mac }} dev {{ dev1_iface }} nud permanent ip neigh add {{ dev2_host }} lladdr {{ dev2_mac }} dev {{ dev1_iface }} nud permanent
register: dev2_arp_add register: dev2_arp_add
changed_when: dev2_arp_add.rc == 0 changed_when: dev2_arp_add.rc == 0
failed_when: > failed_when: >
dev2_arp_add.rc != 0 dev2_arp_add.rc != 0
and ('File exists' not in (dev2_arp_add.stdout | default(''))) and ('File exists' not in (dev2_arp_add.stdout|default('')))
and ('File exists' not in (dev2_arp_add.stderr | default(''))) and ('File exists' not in (dev2_arp_add.stderr|default('')))
- name: Debug ARP action summary on DEV1 - name: Debug ARP summary
ansible.builtin.debug: debug:
msg: msg:
- "dev2_mac={{ dev2_mac | default('UNSET') }}" - "dev2_mac={{ dev2_mac|default('unset') }}"
- "arp_add.rc={{ dev2_arp_add.rc | default('') }}" - "arp_add.rc={{ dev2_arp_add.rc|default('') }}"
- "arp_add.out={{ (dev2_arp_add.stdout | default('')) | trim }}" - "arp_add.out={{ dev2_arp_add.stdout|default('')|trim }}"
- "arp_add.err={{ (dev2_arp_add.stderr | default('')) | trim }}" - "arp_add.err={{ dev2_arp_add.stderr|default('')|trim }}"
- name: Note skipping static ARP add (no MAC discovered) - name: Note skipping static ARP
when: dev2_mac is not defined or dev2_mac | length == 0 when: dev2_mac|length == 0
ansible.builtin.debug: debug:
msg: "No suitable dynamic MAC found via bridge fdb; skipping static ARP add on DEV1" msg: "No MAC discovered; skipping static ARP add."
# ---------------------------- ARP refresh ---------------------------- - name: Refresh ARP
- name: Refresh ARP #1
ansible.builtin.raw: "{{ pathprefix }} arping -U -I {{ arping_iface }} {{ dev2_side_ip_addr }} -c 3"
failed_when: false
ignore_errors: true
- name: Refresh ARP #1
raw: "{{ pathprefix }} arping -U -I {{ arping_iface }} {{ dev2_side_ip_addr }} -c 3" raw: "{{ pathprefix }} arping -U -I {{ arping_iface }} {{ dev2_side_ip_addr }} -c 3"
failed_when: false failed_when: false
ignore_errors: true ignore_errors: true
# ----------------------- Local tunnel prep ----------------------- # ----------------------- Tunnel setup -----------------------
- name: Pick a free local TCP port for the tunnel - name: Pick free local port
delegate_to: localhost delegate_to: localhost
shell: | shell: |
for i in $(seq 1 50); do for i in $(seq 1 50); do
@@ -312,32 +336,25 @@
register: pick_port register: pick_port
failed_when: false failed_when: false
- name: Record chosen port and create control dir - name: Create control dir
delegate_to: localhost delegate_to: localhost
shell: "mktemp -d" shell: "mktemp -d"
register: mktemp_dir register: mktemp_dir
failed_when: false failed_when: false
- name: Set facts for tunnel paths - name: Set tunnel facts
delegate_to: localhost delegate_to: localhost
set_fact: set_fact:
_local_port: "{{ (pick_port.stdout | default('') | trim) }}" _local_port: "{{ pick_port.stdout|default('')|trim }}"
_ctrl_dir: "{{ (mktemp_dir.stdout | default('') | trim) }}" _ctrl_dir: "{{ mktemp_dir.stdout|default('')|trim }}"
_ctrl_sock: "{{ (mktemp_dir.stdout | default('') | trim) }}/ssh_tunnel_ctl" _ctrl_sock: "{{ mktemp_dir.stdout|default('')|trim }}/ssh_tunnel_ctl"
# ----------------------- AIRPING #2 ----------------------- - name: Start SSH tunnel
- name: Refresh ARP #2
raw: "{{ pathprefix }} arping -U -I {{ arping_iface }} {{ dev2_side_ip_addr }} -c 3"
failed_when: false
ignore_errors: true
# ----------------------- Start tunnel -----------------------
- name: Start SSH tunnel via DEV1
delegate_to: localhost delegate_to: localhost
shell: | shell: |
set -e set -e
USER="{{ dev1_user }}" USER="{{ dev1_user }}"
HOST="{{ ansible_host | default(inventory_hostname) }}" HOST="{{ ansible_host|default(inventory_hostname) }}"
sshpass -p '{{ dev1_pass }}' ssh -f -N {{ ssh_opts_common }} \ sshpass -p '{{ dev1_pass }}' ssh -f -N {{ ssh_opts_common }} \
-M -S "{{ _ctrl_sock }}" \ -M -S "{{ _ctrl_sock }}" \
-L "127.0.0.1:{{ _local_port }}:{{ dev2_host }}:{{ dev2_port }}" \ -L "127.0.0.1:{{ _local_port }}:{{ dev2_host }}:{{ dev2_port }}" \
@@ -358,21 +375,21 @@
set_fact: set_fact:
tunnel_ok: "{{ nc_probe.rc == 0 }}" tunnel_ok: "{{ nc_probe.rc == 0 }}"
# ----------------------- DEV1 banner ----------------------- # ----------------------- Dev1 banner -----------------------
- name: Dev1 | Probe banner - name: Dev1 | Probe banner
delegate_to: localhost delegate_to: localhost
shell: | shell: |
sshpass -p '{{ dev1_pass }}' ssh {{ ssh_opts_common }} \ sshpass -p '{{ dev1_pass }}' ssh {{ ssh_opts_common }} \
"{{ dev1_user }}@{{ ansible_host | default(inventory_hostname) }}" \ "{{ dev1_user }}@{{ ansible_host|default(inventory_hostname) }}" \
"cat /etc/banner | grep -i rev | head -n1" "cat /etc/banner | grep -i rev | head -n1"
register: dev1_banner register: dev1_banner
failed_when: false failed_when: false
ignore_errors: true ignore_errors: true
- name: Dev1 | Normalize banner → X.X.X-rYYYY (POSIX tools) - name: Dev1 | Normalize banner
delegate_to: localhost delegate_to: localhost
shell: | shell: |
printf '%s\n' "{{ dev1_banner.stdout | trim }}" \ printf '%s\n' "{{ dev1_banner.stdout|trim }}" \
| awk -F '|' '{print $1}' \ | awk -F '|' '{print $1}' \
| sed -E 's/[[:space:]]+rev[[:space:]]+/-r/' \ | sed -E 's/[[:space:]]+rev[[:space:]]+/-r/' \
| grep -Eo '[0-9]+\.[0-9]+\.[0-9]+-r[0-9]+' || true | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+-r[0-9]+' || true
@@ -382,17 +399,12 @@
- name: Dev1 | Set final fw string - name: Dev1 | Set final fw string
delegate_to: localhost delegate_to: localhost
set_fact: set_fact:
dev1_fw_clean: "{{ dev1_fw_clean_cmd.stdout | trim }}" dev1_fw_clean: "{{ dev1_fw_clean_cmd.stdout|trim }}"
- name: Debug Dev1 normalized firmware
delegate_to: localhost
debug:
msg: "Dev1 fw_version {{ dev1_fw_clean | default('N/A') }}"
- name: Publish Dev1 fw_version - name: Publish Dev1 fw_version
delegate_to: localhost delegate_to: localhost
uri: uri:
url: "http://{{ rmq_host }}:{{ rmq_port }}/api/exchanges/{{ rmq_vhost | urlencode }}/{{ rmq_exchange | urlencode }}/publish" url: "http://{{ rmq_host }}:{{ rmq_port }}/api/exchanges/{{ rmq_vhost|urlencode }}/{{ rmq_exchange|urlencode }}/publish"
method: POST method: POST
user: "{{ rmq_user }}" user: "{{ rmq_user }}"
password: "{{ rmq_pass }}" password: "{{ rmq_pass }}"
@@ -404,7 +416,7 @@
properties: { content_type: "application/json" } properties: { content_type: "application/json" }
routing_key: "{{ control_queue }}" routing_key: "{{ control_queue }}"
payload: "{{ { payload: "{{ {
'inscope_device': (ansible_hostname | default(inventory_hostname)), 'inscope_device': (ansible_hostname|default(inventory_hostname)),
'task_name': 'custom_field_set', 'task_name': 'custom_field_set',
'task_add1': 'fw_version', 'task_add1': 'fw_version',
'task_result': (dev1_fw_clean if (dev1_fw_clean|length>0) else 'unavailable') 'task_result': (dev1_fw_clean if (dev1_fw_clean|length>0) else 'unavailable')
@@ -412,9 +424,9 @@
payload_encoding: "string" payload_encoding: "string"
changed_when: false changed_when: false
# ----------------------- DEV2 auth ----------------------- # ----------------------- Dev2 auth + firmux -----------------------
- name: Try DEV2 login - name: Try DEV2 login
when: tunnel_ok | default(false) when: tunnel_ok|default(false)
delegate_to: localhost delegate_to: localhost
shell: | shell: |
for f in basicpass basicpass2; do for f in basicpass basicpass2; do
@@ -432,17 +444,10 @@
- name: Set dev2_passfile_used - name: Set dev2_passfile_used
delegate_to: localhost delegate_to: localhost
set_fact: set_fact:
dev2_passfile_used: "{{ (dev2_auth.rc == 0) | ternary(dev2_auth.stdout | trim, 'NONE') }}" dev2_passfile_used: "{{ (dev2_auth.rc == 0)|ternary(dev2_auth.stdout|trim,'NONE') }}"
# ----------------------- AIRPING #3 ----------------------- - name: Dev2 | Read firmux
- name: Refresh ARP #3 when: tunnel_ok|default(false) and dev2_passfile_used != 'NONE'
raw: "{{ pathprefix }} arping -U -I {{ arping_iface }} {{ dev2_side_ip_addr }} -c 3"
failed_when: false
ignore_errors: true
# ----------------------- DEV2 firmux (simplified, literal) -----------------------
- name: Dev2 | Read /usr/lib/release/firmux
when: tunnel_ok | default(false) and dev2_passfile_used != 'NONE'
delegate_to: localhost delegate_to: localhost
shell: | shell: |
PORT="{{ _local_port }}" PORT="{{ _local_port }}"
@@ -455,38 +460,26 @@
failed_when: false failed_when: false
ignore_errors: true ignore_errors: true
- name: Dev2 | Extract firmware version line (prefer 'rev', else first non-empty) - name: Dev2 | Extract firmware version line
delegate_to: localhost delegate_to: localhost
run_once: true run_once: true
set_fact: set_fact:
indoor_fw_norm: >- indoor_fw_norm: >-
{{ {{
( (
( (dev2_firmux.stdout|default('')|regex_replace('\r',''))|split('\n')
dev2_firmux.stdout | default('') | regex_replace('\r','') | map('trim') | select('truthy') | list
) | split('\n') ) | select('match','(?i).*\\brev\\s*[0-9]+.*')
| map('trim') | list | first | default(
| select('truthy') ((dev2_firmux.stdout|default('')|regex_replace('\r',''))
| list | split('\n')|map('trim')|select('truthy')|list|first|default(''))
) | select('match', '(?i).*\\brev\\s*[0-9]+.*') ) | trim
| list
| first
| default(
((dev2_firmux.stdout | default('') | regex_replace('\r',''))
| split('\n')
| map('trim')
| select('truthy')
| list
| first
| default(''))
)
| trim
}} }}
- name: Publish Dev2 indoor_fwver - name: Publish Dev2 indoor_fwver
delegate_to: localhost delegate_to: localhost
uri: uri:
url: "http://{{ rmq_host }}:{{ rmq_port }}/api/exchanges/{{ rmq_vhost | urlencode }}/{{ rmq_exchange | urlencode }}/publish" url: "http://{{ rmq_host }}:{{ rmq_port }}/api/exchanges/{{ rmq_vhost|urlencode }}/{{ rmq_exchange|urlencode }}/publish"
method: POST method: POST
user: "{{ rmq_user }}" user: "{{ rmq_user }}"
password: "{{ rmq_pass }}" password: "{{ rmq_pass }}"
@@ -498,7 +491,7 @@
properties: { content_type: "application/json" } properties: { content_type: "application/json" }
routing_key: "{{ control_queue }}" routing_key: "{{ control_queue }}"
payload: "{{ { payload: "{{ {
'inscope_device': (ansible_hostname | default(inventory_hostname)), 'inscope_device': (ansible_hostname|default(inventory_hostname)),
'task_name': 'custom_field_set', 'task_name': 'custom_field_set',
'task_add1': 'indoor_fwver', 'task_add1': 'indoor_fwver',
'task_result': (indoor_fw_norm if indoor_fw_norm|length>0 else 'unavailable') 'task_result': (indoor_fw_norm if indoor_fw_norm|length>0 else 'unavailable')
@@ -511,21 +504,21 @@
debug: debug:
msg: "Closing tunnel and removing temporary IP" msg: "Closing tunnel and removing temporary IP"
- name: Refresh ARP #4 - name: Refresh ARP (cleanup)
raw: "{{ pathprefix }} arping -U -I {{ arping_iface }} {{ dev2_side_ip_addr }} -c 3" raw: "{{ pathprefix }} arping -U -I {{ arping_iface }} {{ dev2_side_ip_addr }} -c 3"
failed_when: false failed_when: false
ignore_errors: true ignore_errors: true
- name: Close SSH ControlMaster - name: Close SSH ControlMaster
delegate_to: localhost delegate_to: localhost
shell: "ssh -S '{{ _ctrl_sock | default('/dev/null') }}' -O exit 2>/dev/null || true" shell: "ssh -S '{{ _ctrl_sock|default('/dev/null') }}' -O exit 2>/dev/null || true"
failed_when: false failed_when: false
ignore_errors: true ignore_errors: true
- name: Remove tunnel control dir - name: Remove control dir
delegate_to: localhost delegate_to: localhost
file: file:
path: "{{ _ctrl_dir | default('/tmp/none') }}" path: "{{ _ctrl_dir|default('/tmp/none') }}"
state: absent state: absent
ignore_errors: true ignore_errors: true
@@ -538,4 +531,4 @@
debug: debug:
msg: msg:
- "Device: {{ inventory_hostname }}" - "Device: {{ inventory_hostname }}"
- "Status: DONE" - "Status: DONE (telemetry only)"