first commit

This commit is contained in:
ansible user
2025-10-22 14:11:49 +02:00
commit 110301f862
75 changed files with 20802 additions and 0 deletions

View File

@@ -0,0 +1,441 @@
---
- name: Deploy WiFi debug (single device, linear)
hosts: all
gather_facts: no
vars:
remote_syslog_ip: "102.38.125.161"
# Program version (0)
wdbg_version: "v17"
ssh_user: "{{ ansible_user | default('root') }}"
ssh_pass: "{{ ansible_password | default(ansible_ssh_pass) }}"
# RabbitMQ (use controls exchange + queue_controls like the reference) (e)
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) }}"
rmq_pass: "{{ lookup('env','RMQ_PASS') | default('change_me', true) }}"
rmq_vhost: "{{ lookup('env','RMQ_VHOST') | default('app', true) }}"
rmq_exchange: "{{ lookup('env','RMQ_EXCHANGE') | default('controls', true) }}"
control_queue: "{{ lookup('env','CONTROL_QUEUE') | default('queue_controls', true) }}"
tasks:
- block:
# --- SSH reachability check ---
- name: Check SSH connectivity (raw ping)
raw: "echo ping"
register: ping_result
ignore_errors: true
- block:
############ step 1
- name: Check if crontab launch is already present in sysstart.lua
raw: |
grep -F 'crond -f' /usr/share/config/sysstart.lua >/dev/null 2>&1 && echo PRESENT || echo ABSENT
register: sysstart_cron_check
changed_when: false
failed_when: false
- name: Announce presence and skip edits
when: (sysstart_cron_check.stdout | trim) == 'PRESENT'
debug:
msg: "crontab is present in sysstart.lua; skipping download/edit/upload."
- name: Fetch sysstart.lua via scp (password auth)
when: (sysstart_cron_check.stdout | trim) != 'PRESENT'
delegate_to: localhost
command: >
sshpass -p {{ ssh_pass | quote }}
scp -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
{{ ssh_user }}@{{ ansible_host }}:/usr/share/config/sysstart.lua
/tmp/{{ inventory_hostname }}_sysstart.lua
register: scp_get
retries: 3
delay: 2
until: scp_get.rc == 0
- name: Edit the file locally
when: (sysstart_cron_check.stdout | trim) != 'PRESENT'
delegate_to: localhost
lineinfile:
path: "/tmp/{{ inventory_hostname }}_sysstart.lua"
line: 'launchd.set_process("crond", "/usr/sbin/crond -f")'
insertbefore: '^tasks\.start\(ctx,\s*"finished"\)'
- name: Push the file back (write to -2)
when: (sysstart_cron_check.stdout | trim) != 'PRESENT'
delegate_to: localhost
command: >
sshpass -p {{ ssh_pass | quote }}
scp -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
/tmp/{{ inventory_hostname }}_sysstart.lua
{{ ssh_user }}@{{ ansible_host }}:/usr/share/config/sysstart.lua
register: scp_put
retries: 3
delay: 2
until: scp_put.rc == 0
############ step 2
- name: Compute MD5 of local wifidebug.sh
delegate_to: localhost
command: md5sum files/wifidebug.sh
register: md5_local_wdbg
changed_when: false
- name: Compute MD5 of remote /root/wifidebug.sh
raw: "md5sum /root/wifidebug.sh || busybox md5sum /root/wifidebug.sh"
register: md5_remote_wdbg
changed_when: false
failed_when: false
- name: Decide if wifidebug.sh needs upload
set_fact:
upload_wdbg: >-
{{ (md5_remote_wdbg.rc != 0)
or ((md5_local_wdbg.stdout.split()[0])
!= (md5_remote_wdbg.stdout.split()[0] if (md5_remote_wdbg.stdout is defined) else '')) }}
- name: Upload wifidebug.sh via scp (overwrite if changed)
when: upload_wdbg | bool
delegate_to: localhost
command: >
sshpass -p {{ ssh_pass | quote }}
scp -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
files/wifidebug.sh
{{ ssh_user }}@{{ ansible_host }}:/root/wifidebug.sh
register: scp_wifidebug
retries: 3
delay: 2
until: scp_wifidebug.rc == 0
- name: Ensure /root/wifidebug.sh is executable and owned by root
raw: |
chown root:root /root/wifidebug.sh && chmod 0755 /root/wifidebug.sh
############ step 3
- name: Ensure /etc/crontabs/root exists (touch with perms)
raw: |
if [ ! -f /etc/crontabs/root ]; then
touch /etc/crontabs/root
fi
chown root:root /etc/crontabs/root
chmod 0644 /etc/crontabs/root
- name: Check if cron line already present
raw: |
grep -Fxq '*/1 * * * * /root/wifidebug.sh' /etc/crontabs/root
register: cron_grep
failed_when: false
changed_when: false
- name: Upload snippet crond-root to /tmp (only if missing)
when: cron_grep.rc != 0
delegate_to: localhost
command: >
sshpass -p {{ ssh_pass | quote }}
scp -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
files/crond-root
{{ ssh_user }}@{{ ansible_host }}:/tmp/crond-root.snippet
- name: Append snippet to /etc/crontabs/root (only if missing)
when: cron_grep.rc != 0
raw: |
cat /tmp/crond-root.snippet >> /etc/crontabs/root && rm -f /tmp/crond-root.snippet
register: cron_append
changed_when: true
############ step 4
- name: Skip config.json edit if remote_syslog already matches
raw: |
EN_OK=$(cat /tmp/config.json | grep -A9 -m1 'remote_syslog' | grep 'enabled' | grep true | wc -l || echo 0)
SRV_OK=$(cat /tmp/config.json | grep -A9 -m1 'remote_syslog' | grep 'server' | grep '{{ remote_syslog_ip }}' | wc -l || echo 0)
if [ "$EN_OK" -eq 1 ] && [ "$SRV_OK" -eq 1 ]; then
echo "SKIP"
else
echo "EDIT"
fi
register: remote_syslog_check
changed_when: false
- name: Set do_edit flag from skip check
set_fact:
do_edit: "{{ (remote_syslog_check.stdout | default('EDIT')) | trim != 'SKIP' }}"
- name: Fetch /tmp/config.json to controller
when: do_edit
delegate_to: localhost
command: >
sshpass -p {{ ssh_pass | quote }}
scp -q -o StrictHostKeyChecking=no
{{ ssh_user }}@{{ ansible_host }}:/tmp/config.json
/tmp/{{ inventory_hostname }}_config.json
- name: Strict in-place style-preserving edit (services.remote_syslog only)
when: do_edit
delegate_to: localhost
shell: |
awk '
BEGIN { in_services=0; in_rs=0; depth=0; done_enabled=0; done_server=0 }
{
line = $0
if (!in_services && $0 ~ /"services"[[:space:]]*:/) { in_services=1 }
if (in_services && !in_rs && $0 ~ /"remote_syslog"[[:space:]]*:/) { in_rs=1; depth=0 }
if (in_rs) {
if (!done_enabled && line ~ /^[[:space:]]*"enabled"[[:space:]]*:[[:space:]]*false([[:space:]]*,?[[:space:]]*)$/) {
line = gensub(/^([[:space:]]*"enabled"[[:space:]]*:[[:space:]]*)false([[:space:]]*,?[[:space:]]*)$/, "\\1true\\2", 1, line)
done_enabled=1
}
if (!done_server && line ~ /^[[:space:]]*"server"[[:space:]]*:[[:space:]]*""([[:space:]]*,?[[:space:]]*)$/) {
line = gensub(/^([[:space:]]*"server"[[:space:]]*:[[:space:]]*)""([[:space:]]*,?[[:space:]]*)$/, "\\1\"102.38.125.161\"\\2", 1, line)
done_server=1
}
}
print line
if (in_rs) {
opens = gsub(/{/, "{", $0)
closes = gsub(/}/, "}", $0)
depth += (opens - closes)
if (depth <= 0 && $0 ~ /}/) { in_rs=0 }
}
}
' "/tmp/{{ inventory_hostname }}_config.json" \
> "/tmp/{{ inventory_hostname }}_config.json.new"
args:
executable: /bin/bash
- name: Push edited config.json.new to remote temp
when: do_edit
delegate_to: localhost
shell: |
sshpass -p {{ ssh_pass | quote }} scp -q -o StrictHostKeyChecking=no \
"/tmp/{{ inventory_hostname }}_config.json.new" \
{{ ssh_user }}@{{ ansible_host }}:/tmp/config.json.new
args:
executable: /bin/bash
- name: Compute MD5 of local .new
when: do_edit
delegate_to: localhost
command: md5sum "/tmp/{{ inventory_hostname }}_config.json.new"
register: md5_local
changed_when: false
- name: Compute MD5 of remote .new (no Python)
when: do_edit
raw: "md5sum /tmp/config.json.new || busybox md5sum /tmp/config.json.new"
register: md5_remote
changed_when: false
- name: Fail if MD5 mismatch
when:
- do_edit
- (md5_local.stdout.split()[0]) != (md5_remote.stdout.split()[0] if (md5_remote.stdout is defined) else 'BAD')
fail:
msg: "MD5 mismatch between controller and remote copy — aborting replace."
- name: Commit new config.json (overwrite original)
when: do_edit
raw: |
cp -a /tmp/config.json /tmp/config.json.bak.$(date +%Y%m%d%H%M%S)
mv /tmp/config.json.new /tmp/config.json
- name: Apply config to runtime (sysconf -w)
when: do_edit
raw: |
sysconf -w || /usr/sbin/sysconf -w
register: sysconf_result
changed_when: true
failed_when: sysconf_result.rc not in [0]
# (d) Pre-restart notice to control queue
- name: Build pre-restart journal (explain why)
when: do_edit
delegate_to: localhost
set_fact:
prereboot_payload:
inscope_device: "{{ ansible_hostname | default(inventory_hostname) }}"
task_name: "journal_add"
task_result: >-
wifidebug: will restart services because remote_syslog settings were updated and applied (sysconf -w).
- name: Publish pre-restart journal to control queue
when: do_edit
delegate_to: localhost
uri:
url: "http://{{ rmq_host }}:{{ rmq_port }}/api/exchanges/{{ rmq_vhost | urlencode }}/{{ rmq_exchange | urlencode }}/publish"
method: POST
user: "{{ rmq_user }}"
password: "{{ rmq_pass }}"
force_basic_auth: true
status_code: 200
headers:
content-type: "application/json"
body_format: json
body:
properties:
content_type: "application/json"
routing_key: "{{ control_queue }}"
payload: "{{ prereboot_payload | to_json }}"
payload_encoding: "string"
register: rmq_preboot
changed_when: (rmq_preboot.json is defined) and (rmq_preboot.json.routed | default(false) | bool)
# ======== SURGICAL CHANGE: restart kick + verification like multissidfix ========
- name: Trigger full restart (system-stop; system-start) from controller with 10s cap
when: do_edit
delegate_to: localhost
shell: "timeout 10s sshpass -p {{ ssh_pass | quote }} ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ServerAliveInterval=2 -o ServerAliveCountMax=1 {{ ssh_user }}@{{ ansible_host }} '/usr/sbin/system-stop ; sleep 1 ; /usr/sbin/system-start'"
args:
executable: /bin/bash
register: restart_kick
changed_when: true
failed_when: false
# probe SSH with nc: up to 24 tries, 5s each (nc timeout -w 3)
- name: Probe SSH with nc (24 tries, 5s each)
when: do_edit
delegate_to: localhost
shell: "nc -z -w 3 {{ ansible_host }} 22"
register: nc_probe
retries: 24
delay: 5
until: nc_probe.rc == 0
changed_when: false
failed_when: false
- name: Set ssh_up fact (from nc probe)
when: do_edit
delegate_to: localhost
set_fact:
ssh_up: "{{ (nc_probe.rc | default(1)) == 0 }}"
- name: Fail if SSH did not return after restart
when:
- do_edit
- not ssh_up | bool
fail:
msg: "wifidebug: restart issued; SSH did not return after 24 x 5s checks."
# ======== END OF SURGICAL CHANGE ========
- name: Set result status (success deployed or no change)
set_fact:
result_status: "{{ 'SUCCESS_DEPLOYED' if (upload_wdbg | bool) else 'SUCCESS_NO_CHANGE' }}"
when: ping_result is succeeded
- name: Set status fact (no ssh)
when: ping_result is failed
set_fact:
result_status: "NO_SSH"
rescue:
- name: Mark result as failed
set_fact:
result_status: "FAILED during {{ ansible_failed_task.name }}"
always:
- name: Compute inscope device
set_fact:
inscope_device_name: "{{ ansible_hostname | default(inventory_hostname) }}"
# (a) Set custom field wifidebug -> v15 on control queue
- name: Build custom-field payload (wifidebug -> version)
delegate_to: localhost
set_fact:
wdbg_cf_payload:
inscope_device: "{{ inscope_device_name }}"
task_name: "deploy_wifidebug"
task_result: "{{ wdbg_version }}"
- name: Publish custom-field update to control queue
delegate_to: localhost
uri:
url: "http://{{ rmq_host }}:{{ rmq_port }}/api/exchanges/{{ rmq_vhost | urlencode }}/{{ rmq_exchange | urlencode }}/publish"
method: POST
user: "{{ rmq_user }}"
password: "{{ rmq_pass }}"
force_basic_auth: true
status_code: 200
headers:
content-type: "application/json"
body_format: json
body:
properties:
content_type: "application/json"
routing_key: "{{ control_queue }}"
payload: "{{ wdbg_cf_payload | to_json }}"
payload_encoding: "string"
register: rmq_cf
changed_when: (rmq_cf.json is defined) and (rmq_cf.json.routed | default(false) | bool)
# (b)(c) Final wrap-up journal "wifidebug: ..." with actions performed
- name: Build actions list
set_fact:
_actions_list: >-
{{
[]
+ ((upload_wdbg | default(false) | bool) | ternary(['uploaded wifidebug.sh'], []))
+ (((cron_grep is defined) and ((cron_grep.rc | default(0)) != 0)) | ternary(['added crontab entry'], []))
+ ((((sysstart_cron_check.stdout | default('PRESENT')) | trim) != 'PRESENT') | ternary(['updated sysstart.lua (crond launch)'], []))
+ ((do_edit | default(false) | bool) | ternary(['updated remote_syslog + applied config (sysconf -w)'], []))
+ ((do_edit | default(false) | bool) | ternary(['restarted services'], []))
}}
- name: Build actions string
set_fact:
_actions_str: "{{ ((_actions_list | default([])) | length > 0) | ternary((_actions_list | join(', ')), 'no changes needed') }}"
- name: Build wrap-up journal payload
delegate_to: localhost
set_fact:
wrap_payload:
inscope_device: "{{ inscope_device_name }}"
task_name: "journal_add"
task_result: >-
wifidebug: {{ 'success' if (result_status == 'SUCCESS_DEPLOYED' or result_status == 'SUCCESS_NO_CHANGE') else result_status | lower }}
— actions: {{ _actions_str }}
- name: Publish wrap-up journal to control queue
delegate_to: localhost
uri:
url: "http://{{ rmq_host }}:{{ rmq_port }}/api/exchanges/{{ rmq_vhost | urlencode }}/{{ rmq_exchange | urlencode }}/publish"
method: POST
user: "{{ rmq_user }}"
password: "{{ rmq_pass }}"
force_basic_auth: true
status_code: 200
headers:
content-type: "application/json"
body_format: json
body:
properties:
content_type: "application/json"
routing_key: "{{ control_queue }}"
payload: "{{ wrap_payload | to_json }}"
payload_encoding: "string"
register: rmq_wrap
changed_when: (rmq_wrap.json is defined) and (rmq_wrap.json.routed | default(false) | bool)
# Local summary (kept for operator visibility)
- name: Summary
debug:
msg:
- "result_status: {{ result_status }}"
- "we're good"