--- - name: Synology Healthcheck hosts: synology gather_facts: yes become: false vars: ts_candidates: - /var/packages/Tailscale/target/bin/tailscale - /usr/bin/tailscale tasks: # ---------- System info ---------- - name: DSM version ansible.builtin.shell: | set -e if [ -f /etc.defaults/VERSION ]; then . /etc.defaults/VERSION echo "${productversion:-unknown} (build ${buildnumber:-unknown})" else echo "unknown" fi register: dsm_version changed_when: false failed_when: false - name: Uptime (pretty) ansible.builtin.command: uptime -p register: uptime_pretty changed_when: false failed_when: false - name: Load averages ansible.builtin.command: cat /proc/loadavg register: loadavg changed_when: false failed_when: false - name: Memory summary (MB) ansible.builtin.command: free -m register: mem changed_when: false failed_when: false # ---------- Storage ---------- - name: Disk usage of root (/) ansible.builtin.shell: df -P / | awk 'NR==2 {print $5}' | tr -d '%' register: root_usage changed_when: false failed_when: false - name: Disk usage of /volume1 (if present) ansible.builtin.shell: | if mountpoint -q /volume1; then df -P /volume1 | awk 'NR==2 {print $5}' | tr -d '%' fi register: vol1_usage changed_when: false failed_when: false - name: RAID status (/proc/mdstat) ansible.builtin.command: cat /proc/mdstat register: mdstat changed_when: false failed_when: false # ---------- Tailscale (optional) ---------- - name: Detect Tailscale binary path (first that exists) ansible.builtin.shell: | for p in {{ ts_candidates | join(' ') }}; do [ -x "$p" ] && echo "$p" && exit 0 done echo "" register: ts_bin changed_when: false failed_when: false - name: Get Tailscale IPv4 (if tailscale present) ansible.builtin.command: "{{ ts_bin.stdout }} ip -4" register: ts_ip changed_when: false failed_when: false when: ts_bin.stdout | length > 0 - name: Get Tailscale self status (brief) ansible.builtin.command: "{{ ts_bin.stdout }} status --self" register: ts_status changed_when: false failed_when: false when: ts_bin.stdout | length > 0 # ---------- Assertions (lightweight, no sudo) ---------- - name: Check RAID not degraded/resyncing ansible.builtin.assert: that: - mdstat.stdout is not search('degraded', ignorecase=True) - mdstat.stdout is not search('resync', ignorecase=True) success_msg: "RAID OK" fail_msg: "RAID issue detected (degraded or resync) — check Storage Manager" changed_when: false - name: Check root FS usage < 90% ansible.builtin.assert: that: - (root_usage.stdout | default('0')) | int < 90 success_msg: "Root filesystem usage OK ({{ root_usage.stdout | default('n/a') }}%)" fail_msg: "Root filesystem high ({{ root_usage.stdout | default('n/a') }}%)" changed_when: false - name: Check /volume1 usage < 90% (if present) ansible.builtin.assert: that: - (vol1_usage.stdout | default('0')) | int < 90 success_msg: "/volume1 usage OK ({{ vol1_usage.stdout | default('n/a') }}%)" fail_msg: "/volume1 usage high ({{ vol1_usage.stdout | default('n/a') }}%)" when: vol1_usage.stdout is defined and vol1_usage.stdout != "" changed_when: false # ---------- Summary (shows the results) ---------- - name: Summary ansible.builtin.debug: msg: | Host: {{ inventory_hostname }} DSM: {{ dsm_version.stdout | default('unknown') }} Uptime: {{ uptime_pretty.stdout | default('n/a') }} Load: {{ loadavg.stdout | default('n/a') }} Memory (MB): {{ (mem.stdout | default('n/a')) | indent(2) }} Root usage: {{ root_usage.stdout | default('n/a') }}% Volume1 usage: {{ (vol1_usage.stdout | default('n/a')) if (vol1_usage.stdout is defined and vol1_usage.stdout != "") else 'n/a' }}% RAID (/proc/mdstat): {{ (mdstat.stdout | default('n/a')) | indent(2) }} Tailscale: binary: {{ (ts_bin.stdout | default('not found')) if ts_bin.stdout|length > 0 else 'not found' }} ip: {{ ts_ip.stdout | default('n/a') }} self: {{ (ts_status.stdout | default('n/a')) | indent(2) }}