f1449285ba
- api/wmt.py: add GET /api/wmt/client/version and GET /api/wmt/client/download endpoints; rewrite submit_update_request with dedup logic - web/wmt.py: add releases, releases_upload, releases_delete, releases_build routes; build-from-folder excludes hidden/data/venv/pyc files - web/main.py: admin per-device delete route; clear-device-logs route; pass devices list to admin template - templates/wmt/releases.html: new release management page (current release info, upload form, build-from-folder card) - templates/admin.html: replace nuclear clear-devices with clear-logs + per-device delete table - templates/base.html: add Client Releases nav link in WMT sidebar section - templates/ansible/execute.html: add Update WMT Code playbook card - ansible/playbooks/update_wmt_code.yml: rsync WMT_project to clients excluding data/; backs up app.py; restarts wmt service - ansible_service.py: register update_wmt_code description - .gitignore: whitelist update_wmt_code.yml
307 lines
12 KiB
YAML
307 lines
12 KiB
YAML
---
|
||
# Update WMT client code from the controller's WMT_project folder
|
||
# ──────────────────────────────────────────────────────────────────────────
|
||
# Use this for devices that have not yet received the HTTP auto-update,
|
||
# or whenever you need to force a code push from the server.
|
||
#
|
||
# What this playbook does:
|
||
# 1. Ensure WMT directory exists on the target
|
||
# 2. Back up the current app.py as app.py.bak.<version>
|
||
# 3. Copy everything from /home/pi/Desktop/WMT_project/ on the CONTROLLER
|
||
# → /home/pi/Desktop/WMT/ on the TARGET
|
||
# The data/ directory on the target is fully preserved (never touched)
|
||
# 4. Fix file ownership
|
||
# 5. Restart the wmt systemd service
|
||
#
|
||
# The data/ directory (config.txt, idmasa.txt, tag.txt, log.txt, device_info.txt)
|
||
# is intentionally excluded — device-specific settings stay intact.
|
||
#
|
||
# Run via: Ansible > Playbooks > "Update WMT Code"
|
||
# ──────────────────────────────────────────────────────────────────────────
|
||
|
||
- name: Update WMT client code from WMT_project folder
|
||
hosts: all
|
||
gather_facts: false
|
||
become: false
|
||
|
||
vars:
|
||
controller_src: /home/pi/Desktop/WMT_project/
|
||
wmt_dir: /home/pi/Desktop/WMT
|
||
|
||
tasks:
|
||
|
||
# ── 1. Ensure WMT directory exists ────────────────────────────────────
|
||
- name: Ensure WMT directory exists on target
|
||
file:
|
||
path: "{{ wmt_dir }}"
|
||
state: directory
|
||
owner: pi
|
||
group: pi
|
||
mode: '0755'
|
||
|
||
# ── 2. Back up current app.py ─────────────────────────────────────────
|
||
- name: Read first line of current app.py (for backup filename)
|
||
shell: head -1 {{ wmt_dir }}/app.py 2>/dev/null || echo "unknown"
|
||
register: local_first_line
|
||
changed_when: false
|
||
ignore_errors: true
|
||
|
||
- name: Extract local version number
|
||
set_fact:
|
||
local_version: >-
|
||
{{ local_first_line.stdout
|
||
| regex_search('version\s+([\d.]+)', '\1')
|
||
| first | default('old') }}
|
||
|
||
- name: Back up current app.py
|
||
copy:
|
||
src: "{{ wmt_dir }}/app.py"
|
||
dest: "{{ wmt_dir }}/app.py.bak.{{ local_version }}"
|
||
remote_src: true
|
||
owner: pi
|
||
group: pi
|
||
mode: preserve
|
||
ignore_errors: true
|
||
|
||
- name: Show backup info
|
||
debug:
|
||
msg: "Backed up app.py v{{ local_version }} → app.py.bak.{{ local_version }}"
|
||
|
||
# ── 3. Snapshot data/ before copy (audit) ────────────────────────────
|
||
- name: List current data/ files (audit)
|
||
shell: ls -1 {{ wmt_dir }}/data/ 2>/dev/null || echo "(empty or missing)"
|
||
register: data_files_before
|
||
changed_when: false
|
||
|
||
- name: Show data/ files that will be preserved
|
||
debug:
|
||
msg: "data/ contents (will NOT be changed): {{ data_files_before.stdout_lines }}"
|
||
|
||
# ── 4. Sync WMT_project → WMT on target, excluding data/ ─────────────
|
||
# synchronize uses rsync under the hood; delegate_to pushes
|
||
# from the controller to the target.
|
||
- name: Sync WMT_project to target (exclude data/ and junk files)
|
||
synchronize:
|
||
src: "{{ controller_src }}"
|
||
dest: "{{ wmt_dir }}/"
|
||
recursive: true
|
||
delete: false
|
||
checksum: true
|
||
rsync_opts:
|
||
- "--exclude=data/"
|
||
- "--exclude=.git/"
|
||
- "--exclude=.gitignore"
|
||
- "--exclude=__pycache__/"
|
||
- "--exclude=*.pyc"
|
||
- "--exclude=*.pyo"
|
||
- "--exclude=*.log"
|
||
- "--exclude=*.bak"
|
||
- "--exclude=venv/"
|
||
- "--exclude=.venv/"
|
||
- "--exclude=node_modules/"
|
||
- "--exclude=.*"
|
||
register: sync_result
|
||
|
||
- name: Show sync summary
|
||
debug:
|
||
msg: "Sync completed. Changed: {{ sync_result.changed }}"
|
||
|
||
# ── 5. Fix ownership ──────────────────────────────────────────────────
|
||
- name: Set correct ownership on WMT directory
|
||
become: true
|
||
file:
|
||
path: "{{ wmt_dir }}"
|
||
owner: pi
|
||
group: pi
|
||
recurse: true
|
||
|
||
# ── 6. Verify data/ is still intact ──────────────────────────────────
|
||
- name: List data/ files after update (verification)
|
||
shell: ls -1 {{ wmt_dir }}/data/ 2>/dev/null || echo "(empty)"
|
||
register: data_files_after
|
||
changed_when: false
|
||
|
||
- name: Show data/ contents after update (should match before)
|
||
debug:
|
||
msg: "{{ data_files_after.stdout_lines }}"
|
||
|
||
# ── 7. Restart WMT service ────────────────────────────────────────────
|
||
- name: Restart WMT systemd service
|
||
become: true
|
||
systemd:
|
||
name: wmt
|
||
state: restarted
|
||
enabled: true
|
||
register: service_result
|
||
ignore_errors: true
|
||
|
||
- name: Show service state
|
||
debug:
|
||
msg: "WMT service state: {{ service_result.status.ActiveState | default('unknown') }}"
|
||
when: service_result is not failed
|
||
|
||
- name: Warn if service restart failed
|
||
debug:
|
||
msg: "WARNING: wmt service restart failed – the device may need a manual reboot."
|
||
when: service_result is failed
|
||
|
||
vars:
|
||
wmt_dir: /home/pi/Desktop/WMT
|
||
tmp_zip: /tmp/wmt_update.zip
|
||
# Controller address – override on CLI with -e "server_url=http://..."
|
||
server_url: "http://{{ hostvars[inventory_hostname]['ansible_host'] | default(ansible_host) | regex_replace('\\d+\\.\\d+$', '10.76.157.1') }}"
|
||
|
||
tasks:
|
||
|
||
# ── 0. Resolve server URL ─────────────────────────────────────────────
|
||
# The monitoring server address is read from the device's own config.txt
|
||
# so we don't have to hard-code it here.
|
||
- name: Read server_host from WMT config.txt
|
||
shell: |
|
||
grep -E '^\s*server_host\s*=' {{ wmt_dir }}/data/config.txt 2>/dev/null \
|
||
| head -1 | awk -F'=' '{print $2}' | tr -d ' \r\n'
|
||
register: cfg_server_host
|
||
changed_when: false
|
||
ignore_errors: true
|
||
|
||
- name: Read server_port from WMT config.txt
|
||
shell: |
|
||
grep -E '^\s*server_port\s*=' {{ wmt_dir }}/data/config.txt 2>/dev/null \
|
||
| head -1 | awk -F'=' '{print $2}' | tr -d ' \r\n'
|
||
register: cfg_server_port
|
||
changed_when: false
|
||
ignore_errors: true
|
||
|
||
- name: Set monitoring server base URL
|
||
set_fact:
|
||
monitoring_base: "http://{{ cfg_server_host.stdout | default('rpi-ansible') }}:{{ cfg_server_port.stdout | default('5000') }}"
|
||
|
||
- name: Show resolved server URL
|
||
debug:
|
||
msg: "Monitoring server: {{ monitoring_base }}"
|
||
|
||
# ── 1. Check latest version on server ────────────────────────────────
|
||
- name: Query latest WMT version from monitoring server
|
||
uri:
|
||
url: "{{ monitoring_base }}/api/wmt/client/version"
|
||
method: GET
|
||
return_content: true
|
||
timeout: 15
|
||
register: version_response
|
||
ignore_errors: true
|
||
|
||
- name: Show server version info
|
||
debug:
|
||
msg: "Server release: v{{ version_response.json.version | default('unknown') }} ({{ version_response.json.filename | default('n/a') }})"
|
||
when: version_response is not failed
|
||
|
||
- name: Fail if server version endpoint unreachable
|
||
fail:
|
||
msg: "Cannot reach {{ monitoring_base }}/api/wmt/client/version – is the server running?"
|
||
when: version_response is failed
|
||
|
||
# ── 2. Get current local version ─────────────────────────────────────
|
||
- name: Read first line of local app.py
|
||
shell: head -1 {{ wmt_dir }}/app.py 2>/dev/null || echo "unknown"
|
||
register: local_first_line
|
||
changed_when: false
|
||
|
||
- name: Extract local version number
|
||
set_fact:
|
||
local_version: "{{ local_first_line.stdout | regex_search('version\\s+([\\d.]+)', '\\1') | first | default('0') }}"
|
||
|
||
- name: Show local version
|
||
debug:
|
||
msg: "Local version: {{ local_version }} | Server version: {{ version_response.json.version }}"
|
||
|
||
# ── 3. Ensure WMT directory exists ───────────────────────────────────
|
||
- name: Ensure WMT directory exists
|
||
file:
|
||
path: "{{ wmt_dir }}"
|
||
state: directory
|
||
owner: pi
|
||
group: pi
|
||
mode: '0755'
|
||
|
||
# ── 4. Download release zip ───────────────────────────────────────────
|
||
- name: Download WMT release zip from monitoring server
|
||
get_url:
|
||
url: "{{ monitoring_base }}/api/wmt/client/download"
|
||
dest: "{{ tmp_zip }}"
|
||
force: true
|
||
timeout: 120
|
||
mode: '0644'
|
||
|
||
# ── 5. Back up current app.py ─────────────────────────────────────────
|
||
- name: Back up current app.py
|
||
copy:
|
||
src: "{{ wmt_dir }}/app.py"
|
||
dest: "{{ wmt_dir }}/app.py.bak.{{ local_version }}"
|
||
remote_src: true
|
||
owner: pi
|
||
group: pi
|
||
mode: preserve
|
||
ignore_errors: true
|
||
|
||
# ── 6. Extract zip – skip data/ directory ─────────────────────────────
|
||
- name: Extract WMT release zip (preserving data/ directory)
|
||
shell: |
|
||
cd {{ wmt_dir }}
|
||
python3 - <<'EOF'
|
||
import zipfile, os, sys
|
||
zip_path = "{{ tmp_zip }}"
|
||
dest = "{{ wmt_dir }}"
|
||
skipped = 0
|
||
extracted = 0
|
||
with zipfile.ZipFile(zip_path, 'r') as zf:
|
||
for member in zf.infolist():
|
||
p = member.filename.replace('\\', '/')
|
||
if p.startswith('data/') or p == 'data':
|
||
skipped += 1
|
||
continue
|
||
zf.extract(member, dest)
|
||
extracted += 1
|
||
print(f"Extracted {extracted} files, skipped {skipped} data/ entries")
|
||
EOF
|
||
register: extract_result
|
||
changed_when: true
|
||
|
||
- name: Show extraction result
|
||
debug:
|
||
msg: "{{ extract_result.stdout }}"
|
||
|
||
# ── 7. Fix ownership ──────────────────────────────────────────────────
|
||
- name: Set correct ownership on WMT directory
|
||
become: true
|
||
file:
|
||
path: "{{ wmt_dir }}"
|
||
owner: pi
|
||
group: pi
|
||
recurse: true
|
||
|
||
# ── 8. Clean up temp zip ──────────────────────────────────────────────
|
||
- name: Remove temporary zip file
|
||
file:
|
||
path: "{{ tmp_zip }}"
|
||
state: absent
|
||
|
||
# ── 9. Restart WMT service ────────────────────────────────────────────
|
||
- name: Restart WMT systemd service
|
||
become: true
|
||
systemd:
|
||
name: wmt
|
||
state: restarted
|
||
enabled: true
|
||
register: service_result
|
||
ignore_errors: true
|
||
|
||
- name: Show service restart result
|
||
debug:
|
||
msg: "Service state: {{ service_result.status.ActiveState | default('unknown') }}"
|
||
when: service_result is not failed
|
||
|
||
- name: Warn if service restart failed
|
||
debug:
|
||
msg: "WARNING: wmt service restart failed – the device may need a manual reboot."
|
||
when: service_result is failed
|