first commit
This commit is contained in:
277
files/ansible-playbooks/deploy-scroll24.yml
Normal file
277
files/ansible-playbooks/deploy-scroll24.yml
Normal file
@@ -0,0 +1,277 @@
|
||||
---
|
||||
- name: Deploy scroll24 script and cron configuration
|
||||
hosts: all
|
||||
gather_facts: no
|
||||
|
||||
vars:
|
||||
ssh_user: "{{ ansible_user | default('root') }}"
|
||||
ssh_pass: "{{ ansible_password | default(ansible_ssh_pass) }}"
|
||||
|
||||
# Version tag for scroll24 deployment (operator-controlled)
|
||||
scroll24_version: "v1.1"
|
||||
|
||||
# Controller-side source paths (same place as wifidebug.sh)
|
||||
src_script: "files/scroll24.sh"
|
||||
src_cron_snippet: "files/crond-root-scroll24"
|
||||
|
||||
# Remote destinations
|
||||
remote_script: "/root/scroll24.sh"
|
||||
remote_crontab: "/etc/crontabs/root"
|
||||
tmp_cron_snippet: "/tmp/crond-root-scroll24.snippet"
|
||||
tmp_cron_new: "/tmp/cron.root.new"
|
||||
crontab_backup_dir: "/etc/crontabs"
|
||||
|
||||
# RabbitMQ (same contract/style as your other playbooks)
|
||||
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:
|
||||
|
||||
##########################################################################
|
||||
# 0. Controller sanity check for tools
|
||||
##########################################################################
|
||||
- name: Verify controller tools (sshpass, sha256sum/busybox)
|
||||
delegate_to: localhost
|
||||
shell: |
|
||||
command -v sshpass >/dev/null 2>&1 \
|
||||
&& (command -v sha256sum >/dev/null 2>&1 || command -v busybox >/dev/null 2>&1)
|
||||
args: { executable: /bin/bash }
|
||||
register: ctrl_tools
|
||||
changed_when: false
|
||||
failed_when: ctrl_tools.rc != 0
|
||||
|
||||
##########################################################################
|
||||
# 1. Upload /root/scroll24.sh if changed (sha256 verified)
|
||||
##########################################################################
|
||||
- name: Compute local sha256 of scroll24.sh
|
||||
delegate_to: localhost
|
||||
command: sha256sum {{ src_script }}
|
||||
register: sha_local
|
||||
changed_when: false
|
||||
|
||||
- name: Extract local sha256 digest (regex)
|
||||
delegate_to: localhost
|
||||
set_fact:
|
||||
local_hash: "{{ (sha_local.stdout | default('')) | regex_search('([A-Fa-f0-9]{64})') | default('') }}"
|
||||
|
||||
- name: Compute remote sha256 of /root/scroll24.sh
|
||||
raw: "sha256sum {{ remote_script }} 2>/dev/null || busybox sha256sum {{ remote_script }} 2>/dev/null || true"
|
||||
register: sha_remote
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Extract remote sha256 digest (regex)
|
||||
set_fact:
|
||||
remote_hash: "{{ (sha_remote.stdout | default('')) | regex_search('([A-Fa-f0-9]{64})') | default('') }}"
|
||||
|
||||
- name: Decide if scroll24.sh needs upload
|
||||
set_fact:
|
||||
script_changed: "{{ (remote_hash | length == 0) or (local_hash != remote_hash) }}"
|
||||
|
||||
- name: Upload scroll24.sh via scp (if needed)
|
||||
when: script_changed | bool
|
||||
delegate_to: localhost
|
||||
command: >
|
||||
sshpass -p {{ ssh_pass | quote }}
|
||||
scp -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
|
||||
{{ src_script }}
|
||||
{{ ssh_user }}@{{ ansible_host }}:{{ remote_script }}
|
||||
register: scp_scroll24
|
||||
changed_when: true
|
||||
|
||||
- name: Ensure /root/scroll24.sh permissions and ownership
|
||||
raw: "chown root:root {{ remote_script }} && chmod 0755 {{ remote_script }}"
|
||||
changed_when: script_changed | bool
|
||||
|
||||
- name: Recompute remote sha256 after upload
|
||||
when: script_changed | bool
|
||||
raw: "sha256sum {{ remote_script }} || busybox sha256sum {{ remote_script }}"
|
||||
register: sha_remote_after
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Extract remote-after sha256 digest (regex)
|
||||
when: script_changed | bool
|
||||
set_fact:
|
||||
remote_hash_after: "{{ (sha_remote_after.stdout | default('')) | regex_search('([A-Fa-f0-9]{64})') | default('') }}"
|
||||
|
||||
- name: Fail if scroll24.sh sha256 mismatch after upload
|
||||
when: script_changed | bool and (remote_hash_after | default('')) != (local_hash | default(''))
|
||||
fail:
|
||||
msg: "sha256 mismatch between controller and remote scroll24.sh"
|
||||
|
||||
##########################################################################
|
||||
# 1b. Extract the period (minutes) from the first line of the snippet
|
||||
##########################################################################
|
||||
- name: Extract period minutes from first snippet line (*/N …)
|
||||
delegate_to: localhost
|
||||
shell: |
|
||||
awk 'NR==1{
|
||||
f=$1;
|
||||
if (f ~ /^\*\/[0-9]+$/) { gsub("^\\*/","",f); print f; exit }
|
||||
else if (f ~ /^[0-9]+$/) { print f; exit }
|
||||
else { print ""; exit }
|
||||
}' {{ src_cron_snippet }}
|
||||
args: { executable: /bin/bash }
|
||||
register: cron_period_cmd
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Set cron_period fact
|
||||
delegate_to: localhost
|
||||
set_fact:
|
||||
cron_period: "{{ (cron_period_cmd.stdout | trim) }}"
|
||||
|
||||
##########################################################################
|
||||
# 2. Upload cron snippet to device /tmp (operator-provided content)
|
||||
##########################################################################
|
||||
- name: Push cron snippet to device tmp
|
||||
delegate_to: localhost
|
||||
command: >
|
||||
sshpass -p {{ ssh_pass | quote }}
|
||||
scp -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
|
||||
{{ src_cron_snippet }}
|
||||
{{ ssh_user }}@{{ ansible_host }}:{{ tmp_cron_snippet }}
|
||||
register: scp_snippet
|
||||
changed_when: true
|
||||
|
||||
##########################################################################
|
||||
# 3. Crontab subset logic (only scroll24.sh lines)
|
||||
##########################################################################
|
||||
- name: Ensure /etc/crontabs/root exists (with perms)
|
||||
raw: |
|
||||
if [ ! -f {{ remote_crontab }} ]; then
|
||||
touch {{ remote_crontab }};
|
||||
fi
|
||||
chown root:root {{ remote_crontab }};
|
||||
chmod 0644 {{ remote_crontab }};
|
||||
changed_when: false
|
||||
|
||||
- name: Extract current scroll24 lines from crontab
|
||||
raw: "grep -F 'scroll24.sh' {{ remote_crontab }} || true"
|
||||
register: cron_subset
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Normalize controller snippet (for compare only)
|
||||
delegate_to: localhost
|
||||
shell: |
|
||||
grep -v '^[[:space:]]*$' {{ src_cron_snippet }} \
|
||||
| sed 's/[[:space:]]\+/ /g' | sed 's/[[:space:]]*$//' | sort -u
|
||||
register: norm_snippet
|
||||
changed_when: false
|
||||
|
||||
- name: Normalize device scroll24 subset (for compare only)
|
||||
delegate_to: localhost
|
||||
shell: |
|
||||
printf "%s\n" "{{ cron_subset.stdout | default('') }}" \
|
||||
| grep -v '^[[:space:]]*$' \
|
||||
| sed 's/[[:space:]]\+/ /g' | sed 's/[[:space:]]*$//' | sort -u
|
||||
register: norm_remote_subset
|
||||
changed_when: false
|
||||
|
||||
- name: Decide if crontab needs update (subset compare)
|
||||
delegate_to: localhost
|
||||
set_fact:
|
||||
crontab_changed: "{{ (norm_snippet.stdout | trim) != (norm_remote_subset.stdout | trim) }}"
|
||||
|
||||
- name: Backup current crontab (timestamped)
|
||||
when: crontab_changed | bool
|
||||
raw: "cp -a {{ remote_crontab }} {{ crontab_backup_dir }}/root.bak.$(date +%Y%m%d%H%M%S)"
|
||||
changed_when: true
|
||||
|
||||
- name: Replace scroll24 lines in crontab (preserve all others; tidy splice)
|
||||
when: crontab_changed | bool
|
||||
raw: "grep -v 'scroll24\\.sh' {{ remote_crontab }} > {{ tmp_cron_new }} && awk 'BEGIN{for(i=1;i<=NR;i++)a[i]=$0} {a[NR]=$0} END{e=NR; while(e>0 && a[e] ~ /^[[:space:]]*$/){e--}; for(i=1;i<=e;i++) print a[i]}' {{ tmp_cron_new }} > {{ tmp_cron_new }}.trim && mv {{ tmp_cron_new }}.trim {{ tmp_cron_new }} && cat {{ tmp_cron_snippet }} >> {{ tmp_cron_new }} && printf '\\n' >> {{ tmp_cron_new }} && mv {{ tmp_cron_new }} {{ remote_crontab }} && chown root:root {{ remote_crontab }} && chmod 0644 {{ remote_crontab }}"
|
||||
changed_when: true
|
||||
|
||||
##########################################################################
|
||||
# 4. Restart crond if script or cron changed (with :51–:59 guard)
|
||||
##########################################################################
|
||||
- name: Check if restart required
|
||||
set_fact:
|
||||
need_restart: "{{ (script_changed | bool) or (crontab_changed | bool) }}"
|
||||
|
||||
- name: Get current seconds
|
||||
when: need_restart | bool
|
||||
raw: "date +%S"
|
||||
register: nowsec
|
||||
changed_when: false
|
||||
|
||||
- name: Sleep 10s if seconds 51-59
|
||||
when: need_restart | bool and (nowsec.stdout | int >= 51)
|
||||
pause:
|
||||
seconds: 10
|
||||
|
||||
- name: Restart crond via move/move
|
||||
when: need_restart | bool
|
||||
raw: "mv /tmp/launchd/services/crond /root/crond && sleep 1 && mv /root/crond /tmp/launchd/services/crond"
|
||||
register: crond_restart
|
||||
changed_when: true
|
||||
failed_when: false
|
||||
|
||||
- name: Verify crond is running
|
||||
when: need_restart | bool
|
||||
raw: "pgrep -f '/usr/sbin/crond' || busybox pgrep crond || echo missing"
|
||||
register: crond_pid
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
##########################################################################
|
||||
# 5. Final journals to RabbitMQ (controller)
|
||||
##########################################################################
|
||||
- name: Publish final scroll24 deployment journal
|
||||
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: "{{ {
|
||||
'inscope_device': (ansible_hostname | default(inventory_hostname)),
|
||||
'task_name': 'journal_add',
|
||||
'task_result': (
|
||||
'scroll24: '
|
||||
~ (script_changed | ternary('script installed/updated; ', 'script up-to-date; '))
|
||||
~ (crontab_changed | ternary('cron updated; ', 'cron already matches; '))
|
||||
~ (need_restart | ternary('crond restarted; ', 'crond unchanged; '))
|
||||
)
|
||||
} | to_json }}"
|
||||
payload_encoding: "string"
|
||||
changed_when: false
|
||||
|
||||
- name: Set NetBox custom field scroll24 -> "<version>_<period>"
|
||||
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: "{{ {
|
||||
'inscope_device': (ansible_hostname | default(inventory_hostname)),
|
||||
'task_name': 'custom_field_set',
|
||||
'task_add1': 'scroll24',
|
||||
'task_result': scroll24_version ~ '_' ~ (cron_period | default(''))
|
||||
} | to_json }}"
|
||||
payload_encoding: "string"
|
||||
changed_when: false
|
||||
|
||||
Reference in New Issue
Block a user