diff --git a/files/ansible-playbooks/sot-updater.yml b/files/ansible-playbooks/sot-updater.yml index a8d7598..da77cf0 100644 --- a/files/ansible-playbooks/sot-updater.yml +++ b/files/ansible-playbooks/sot-updater.yml @@ -1,35 +1,27 @@ --- -# updater.yml — read Dev1 & Dev2 firmware versions and publish to controls → netbox-reporter -# Run: nbplay updater.yml +# sot-updater.yml — Read Dev1 & Dev2 firmware and publish to controls → netbox-reporter +# Run: nbplay sot-updater.yml - name: Read fw on Dev1 + Dev2, publish NetBox custom fields hosts: all gather_facts: no vars: - # Busybox-safe PATH prefix for raw calls on DEV1 (as in update-rebootin222) + # Busybox-safe PATH prefix for raw calls on DEV1 pathprefix: "PATH=/sbin:/usr/sbin:/bin:/usr/bin:$PATH; " - # DEV1 credentials (as in update-indoor) + # DEV1 credentials for tunnel dev1_user: "root" dev1_pass: "wavewave" - # Tunnel target DEV2 behind DEV1 (as in update-indoor) + # Tunnel target DEV2 behind DEV1 dev2_host: "192.168.1.1" dev2_port: 22 dev2_ssh_user: "root" dev2_side_ip: "192.168.1.11/24" dev1_iface: "br-wan" - # Password files sequence (as in update-indoor: try basicpass then basicpass2) - # NOTE: The selection below mirrors the exact “try #1 → select → try #2 → select → NONE” flow from update-indoor. - # Do not alter ordering. - # (Values are file names as used in your repo/environment.) - # We do not loop; we replicate the same task structure. - # The read of firmux uses whichever got selected. - # — Pavel’s rule: on-device commands remain immutable; we’re only orchestrating controller-side steps here. - - # Rabbit journaling (mirrors update-indoor / rebootin222) + # Rabbit journaling defaults rmq_host: "{{ lookup('env','RMQ_HOST') | default('10.210.12.2', true) }}" rmq_port: "{{ lookup('env','RMQ_PORT') | default('15672', true) }}" rmq_user: "{{ lookup('env','RMQ_USER') | default('admin', true) }}" @@ -39,12 +31,25 @@ control_queue: "{{ lookup('env','CONTROLQUEUE') | default('queue_controls', true) }}" tasks: - # ----------------------------- DEV1: read current banner (exactly like update-rebootin222) ----------------------------- + # ───────────────────────── Progress: start ───────────────────────── + - name: ⚙️ Start | Device processing begins (Dev1 phase) + debug: + msg: + - "Device: {{ inventory_hostname }}" + - "Phase: Dev1 (read banner & publish fw_version)" + + # ───────────────────────── DEV1 ───────────────────────── - name: Dev1 | Read first /etc/banner line containing 'rev' ansible.builtin.raw: "{{ pathprefix }} cat /etc/banner | grep -i rev | head -n1" register: dev1_banner changed_when: false + - name: Dev1 | Debug banner output + debug: + msg: + - "Dev1 banner raw: {{ (dev1_banner.stdout | default('') | trim) | regex_replace('\\n',' ') }}" + + # Keep the agreed format EXACTLY "X.Y.Z rev NNNN" (no timestamp). - name: Dev1 | Extract 'X.Y.Z rev NNNN' from banner (no timestamp) delegate_to: localhost set_fact: @@ -54,6 +59,36 @@ | regex_replace('^(.+?[Rr][Ee][Vv]\\.??\\s*[0-9]+).*$', '\\1') }} + - name: Dev1 | Debug normalized fw string to be published + delegate_to: localhost + debug: + msg: + - "Dev1 fw_version (clean): {{ dev1_fw_clean }}" + + # Build a preview of the EXACT payload string (for troubleshooting). + - name: Dev1 | Build payload preview string + delegate_to: localhost + set_fact: + dev1_payload_preview: >- + {{ + { + "properties": {"content_type": "application/json"}, + "routing_key": control_queue, + "payload": ({ + "inscope_device": (ansible_hostname | default(inventory_hostname)), + "task_name": "custom_field_set", + "task_add1": "fw_version", + "task_result": dev1_fw_clean + } | to_json), + "payload_encoding": "string" + } | to_json + }} + + - name: Dev1 | Debug EXACT publish body (payload preview) + delegate_to: localhost + debug: + var: dev1_payload_preview + - name: Publish → controls | custom_field_set fw_version (Dev1) delegate_to: localhost ansible.builtin.uri: @@ -75,9 +110,22 @@ 'task_result': dev1_fw_clean } | to_json }}" payload_encoding: "string" + register: dev1_publish_resp changed_when: false - # ----------------------------- TUNNEL PREP (update-indoor blocks copied) ----------------------------- + - name: Dev1 | Debug publish API response + delegate_to: localhost + debug: + msg: + - "Dev1 publish response: {{ dev1_publish_resp.json | default(dev1_publish_resp) }}" + + - name: ✅ Progress | Completed Dev1, moving to tunnel & Dev2 phase + debug: + msg: + - "Device: {{ inventory_hostname }}" + - "Phase switch: Dev1 done → Dev2 (tunnel, auth, read indoor_fwver)" + + # ───────────────────────── TUNNEL PREP ───────────────────────── - name: Add temporary IP on DEV1 (tolerate 'File exists') ansible.builtin.raw: > {{ pathprefix }} @@ -88,6 +136,13 @@ and ('File exists' not in (add_ip.stdout | default(''))) and ('File exists' not in (add_ip.stderr | default(''))) + - name: Debug | temp IP add result + debug: + msg: + - "Add IP rc={{ add_ip.rc }}" + - "stdout={{ (add_ip.stdout | default('')) | trim }}" + - "stderr={{ (add_ip.stderr | default('')) | trim }}" + - name: Pick a free local TCP port for the tunnel (controller) delegate_to: localhost ansible.builtin.shell: | @@ -111,6 +166,12 @@ register: pick_port changed_when: false + - name: Debug | chosen local tunnel port + delegate_to: localhost + debug: + msg: + - "Chosen local port: {{ pick_port.stdout | trim }}" + - name: Stop if no free local port was found ansible.builtin.meta: end_host when: (pick_port.stdout | trim | length) == 0 @@ -130,6 +191,12 @@ set_fact: _ctrl_sock: "{{ _ctrl_dir }}/ssh_tunnel_ctl" + - name: Debug | ControlMaster paths + delegate_to: localhost + debug: + msg: + - "_ctrl_dir={{ _ctrl_dir }}" + - "_ctrl_sock={{ _ctrl_sock }}" - name: Start SSH ControlMaster and forward 127.0.0.1:local → DEV2:22 via DEV1 delegate_to: localhost @@ -148,6 +215,14 @@ register: start_tunnel changed_when: true + - name: Debug | tunnel start status + delegate_to: localhost + debug: + msg: + - "Started ControlMaster tunnel (rc={{ start_tunnel.rc | default('n/a') }})" + - "stdout={{ (start_tunnel.stdout | default('')) | trim }}" + - "stderr={{ (start_tunnel.stderr | default('')) | trim }}" + - name: Probe TCP reachability to DEV2 through the tunnel delegate_to: localhost ansible.builtin.shell: "nc -z -w5 127.0.0.1 {{ _local_port }}" @@ -155,11 +230,25 @@ changed_when: false ignore_errors: true + - name: Debug | tunnel probe result + delegate_to: localhost + debug: + msg: + - "Tunnel TCP probe rc={{ nc_probe.rc }} (0=OK)" + - "stdout={{ (nc_probe.stdout | default('')) | trim }}" + - "stderr={{ (nc_probe.stderr | default('')) | trim }}" + - name: Stop if tunnel TCP probe failed ansible.builtin.meta: end_host when: nc_probe.rc != 0 - # ----------------------------- DEV2 AUTH PICK (exact task sequence from update-indoor) ----------------------------- + - name: ✅ Progress | Tunnel OK, proceeding to Dev2 auth and reads + debug: + msg: + - "Device: {{ inventory_hostname }}" + - "Phase: Dev2 (auth selection, firmux/banner read)" + + # ───────────────────────── DEV2 AUTH PICK (as per update-indoor) ───────────────────────── - name: Try DEV2 login with 'basicpass' (root) delegate_to: localhost ansible.builtin.shell: | @@ -175,6 +264,11 @@ changed_when: false ignore_errors: true + - name: Debug | basicpass try rc + delegate_to: localhost + debug: + msg: "DEV2 'basicpass' try rc={{ dev2_try_basicpass.rc }} (0=OK)" + - name: Select 'basicpass' if previous login succeeded when: dev2_try_basicpass.rc == 0 delegate_to: localhost @@ -198,6 +292,11 @@ changed_when: false ignore_errors: true + - name: Debug | basicpass2 try rc + delegate_to: localhost + debug: + msg: "DEV2 'basicpass2' try rc={{ dev2_try_basicpass2.rc | default('n/a') }} (0=OK)" + - name: Select 'basicpass2' if previous login succeeded when: dev2_passfile_used is not defined and dev2_try_basicpass2.rc == 0 delegate_to: localhost @@ -212,7 +311,12 @@ dev2_passfile_used: "NONE" changed_when: false - # ----------------------------- DEV2 firmware read (update-indoor firmux primary check) ----------------------------- + - name: Debug | selected passfile + delegate_to: localhost + debug: + msg: "DEV2 passfile used: {{ dev2_passfile_used }}" + + # ───────────────────────── DEV2 READS ───────────────────────── - name: Dev2 | Read /usr/lib/release/firmux (if present) when: dev2_passfile_used != "NONE" delegate_to: localhost @@ -259,7 +363,13 @@ else (dev2_banner.stdout | default('') | trim) }} - - name: Dev2 | Normalize to X.Y.Z-rNNNN (convert trailing 'rev NNNN' → '-rNNNN') + - name: Dev2 | Debug raw indoor fw string + delegate_to: localhost + debug: + msg: + - "Dev2 indoor_fw_raw: {{ indoor_fw_raw }}" + + - name: Dev2 | Normalize to 'X.Y.Z rev NNNN' or already '-rNNNN' style delegate_to: localhost ansible.builtin.set_fact: indoor_fw_norm: >- @@ -271,6 +381,43 @@ ) }} + - name: Dev2 | Debug normalized indoor fw string + delegate_to: localhost + debug: + msg: + - "Dev2 indoor_fw_norm: {{ indoor_fw_norm }}" + + # Build payload preview (exact string). + - name: Dev2 | Build payload preview string + when: + - dev2_passfile_used != "NONE" + - indoor_fw_norm is defined + - (indoor_fw_norm | length) > 0 + delegate_to: localhost + set_fact: + dev2_payload_preview: >- + {{ + { + "properties": {"content_type": "application/json"}, + "routing_key": control_queue, + "payload": ({ + "inscope_device": (ansible_hostname | default(inventory_hostname)), + "task_name": "custom_field_set", + "task_add1": "indoor_fwver", + "task_result": indoor_fw_norm + } | to_json), + "payload_encoding": "string" + } | to_json + }} + + - name: Dev2 | Debug EXACT publish body (payload preview) + when: + - dev2_passfile_used != "NONE" + - dev2_payload_preview is defined + delegate_to: localhost + debug: + var: dev2_payload_preview + - name: Publish → controls | custom_field_set indoor_fwver (Dev2) when: - dev2_passfile_used != "NONE" @@ -296,9 +443,22 @@ 'task_result': indoor_fw_norm } | to_json }}" payload_encoding: "string" + register: dev2_publish_resp changed_when: false + - name: Dev2 | Debug publish API response + when: dev2_publish_resp is defined + delegate_to: localhost + debug: + msg: + - "Dev2 publish response: {{ dev2_publish_resp.json | default(dev2_publish_resp) }}" + post_tasks: + - name: ✅ Progress | Finishing up (cleanup) + debug: + msg: + - "Cleanup phase: closing ControlMaster & removing temp IP" + - name: Close SSH ControlMaster (best-effort) delegate_to: localhost ansible.builtin.shell: "ssh -S '{{ _ctrl_sock | default('/dev/null') }}' -O exit 2>/dev/null || true" @@ -321,3 +481,16 @@ failed_when: del_ip.rc != 0 and ('Cannot assign requested address' not in (del_ip.stdout | default(''))) and ('Cannot assign requested address' not in (del_ip.stderr | default(''))) + + - name: Debug | temp IP del result + debug: + msg: + - "Del IP rc={{ del_ip.rc }}" + - "stdout={{ (del_ip.stdout | default('')) | trim }}" + - "stderr={{ (del_ip.stderr | default('')) | trim }}" + + - name: ✅ Progress | Completed device processing + debug: + msg: + - "Device: {{ inventory_hostname }}" + - "Status: DONE (Dev1+Dev2 processed)"