Add push monitor script deployment via SSH
All checks were successful
Build and Push Container / build (push) Successful in 33s

- Add push_scripts.py with bash templates for heartbeat, disk, memory, cpu, and updates monitoring
- Modify kuma_client.py to return push token from created monitors
- Add deploy_push_script() to monitors.py for SSH-based script deployment
- Add heartbeat push_metric type to Claude agent suggestions
- Add /api/monitors/<id>/deploy-script and /api/monitors/deploy-all-scripts endpoints
- Update frontend to show push monitors with deployment status and retry buttons

Scripts are deployed to /usr/local/bin/kuma-push-{metric}-{id}.sh with cron entries.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Debian
2026-01-05 08:36:48 +00:00
parent 908d147235
commit ae814c1aea
7 changed files with 577 additions and 21 deletions

View File

@@ -0,0 +1,165 @@
"""Bash script templates for push monitor cronjobs."""
from typing import Optional
def get_push_url(kuma_url: str, push_token: str) -> str:
"""Build the full push URL for Uptime Kuma."""
base = kuma_url.rstrip("/")
return f"{base}/api/push/{push_token}"
SCRIPT_TEMPLATES = {
"heartbeat": '''#!/bin/bash
# Kuma Push Monitor - Heartbeat
# Simple "I'm alive" ping for remote/unreachable hosts
PUSH_URL="{push_url}"
HOSTNAME=$(hostname)
curl -fsS -m 10 -o /dev/null "$PUSH_URL?status=up&msg=${{HOSTNAME}}%20alive"
''',
"disk": '''#!/bin/bash
# Kuma Push Monitor - Disk Usage
# Fails if any mount exceeds threshold
PUSH_URL="{push_url}"
THRESHOLD=90
# Find worst disk usage (excluding tmpfs, devtmpfs, etc.)
WORST=$(df -h -x tmpfs -x devtmpfs -x squashfs 2>/dev/null | awk 'NR>1 && $5+0 > 0 {{gsub(/%/,"",$5); print $5, $6}}' | sort -rn | head -1)
USAGE=$(echo "$WORST" | awk '{{print $1}}')
MOUNT=$(echo "$WORST" | awk '{{print $2}}')
if [ -z "$USAGE" ]; then
curl -fsS -m 10 -o /dev/null "$PUSH_URL?status=up&msg=No%20disks%20found"
elif [ "$USAGE" -gt "$THRESHOLD" ]; then
curl -fsS -m 10 -o /dev/null "$PUSH_URL?status=down&msg=Disk%20$MOUNT%20at%20${{USAGE}}%25"
else
curl -fsS -m 10 -o /dev/null "$PUSH_URL?status=up&msg=Disk%20OK%20(worst:%20${{USAGE}}%25%20on%20$MOUNT)"
fi
''',
"memory": '''#!/bin/bash
# Kuma Push Monitor - Memory Usage
# Fails if memory usage exceeds threshold
PUSH_URL="{push_url}"
THRESHOLD=90
USAGE=$(free | awk '/Mem:/ {{printf "%.0f", $3/$2 * 100}}')
if [ "$USAGE" -gt "$THRESHOLD" ]; then
curl -fsS -m 10 -o /dev/null "$PUSH_URL?status=down&msg=Memory%20at%20${{USAGE}}%25"
else
curl -fsS -m 10 -o /dev/null "$PUSH_URL?status=up&msg=Memory%20OK%20(${{USAGE}}%25)"
fi
''',
"cpu": '''#!/bin/bash
# Kuma Push Monitor - CPU Load
# Fails if 5-minute load average exceeds threshold (as % of cores)
PUSH_URL="{push_url}"
THRESHOLD=95
CORES=$(nproc)
LOAD=$(awk '{{print $2}}' /proc/loadavg) # 5-min average
PERCENT=$(awk "BEGIN {{printf \\"%.0f\\", ($LOAD / $CORES) * 100}}")
if [ "$PERCENT" -gt "$THRESHOLD" ]; then
curl -fsS -m 10 -o /dev/null "$PUSH_URL?status=down&msg=CPU%20load%20${{PERCENT}}%25%20(${{LOAD}}/${{CORES}})"
else
curl -fsS -m 10 -o /dev/null "$PUSH_URL?status=up&msg=CPU%20OK%20(${{PERCENT}}%25)"
fi
''',
"updates": '''#!/bin/bash
# Kuma Push Monitor - Security Updates
# Fails if security updates are pending
PUSH_URL="{push_url}"
# Try apt (Debian/Ubuntu)
if command -v apt-get &>/dev/null; then
apt-get update -qq 2>/dev/null
SECURITY=$(apt-get -s upgrade 2>/dev/null | grep -c "^Inst.*security" || echo "0")
if [ "$SECURITY" -gt 0 ]; then
curl -fsS -m 10 -o /dev/null "$PUSH_URL?status=down&msg=${{SECURITY}}%20security%20updates%20pending"
else
curl -fsS -m 10 -o /dev/null "$PUSH_URL?status=up&msg=No%20security%20updates"
fi
# Try dnf (RHEL/Fedora)
elif command -v dnf &>/dev/null; then
SECURITY=$(dnf check-update --security 2>/dev/null | grep -c "^[a-zA-Z]" || echo "0")
if [ "$SECURITY" -gt 0 ]; then
curl -fsS -m 10 -o /dev/null "$PUSH_URL?status=down&msg=${{SECURITY}}%20security%20updates%20pending"
else
curl -fsS -m 10 -o /dev/null "$PUSH_URL?status=up&msg=No%20security%20updates"
fi
# Try yum (older RHEL/CentOS)
elif command -v yum &>/dev/null; then
SECURITY=$(yum check-update --security 2>/dev/null | grep -c "^[a-zA-Z]" || echo "0")
if [ "$SECURITY" -gt 0 ]; then
curl -fsS -m 10 -o /dev/null "$PUSH_URL?status=down&msg=${{SECURITY}}%20security%20updates%20pending"
else
curl -fsS -m 10 -o /dev/null "$PUSH_URL?status=up&msg=No%20security%20updates"
fi
else
curl -fsS -m 10 -o /dev/null "$PUSH_URL?status=up&msg=Unknown%20package%20manager"
fi
''',
}
def generate_script(push_metric: str, push_url: str) -> Optional[str]:
"""Generate a push monitoring script for the given metric type.
Args:
push_metric: One of 'heartbeat', 'disk', 'memory', 'cpu', 'updates'
push_url: The full Uptime Kuma push URL including token
Returns:
The bash script content, or None if invalid metric type
"""
template = SCRIPT_TEMPLATES.get(push_metric)
if not template:
return None
return template.format(push_url=push_url)
def get_script_filename(push_metric: str, monitor_id: int) -> str:
"""Generate the script filename for a push monitor.
Args:
push_metric: The metric type
monitor_id: The Uptime Kuma monitor ID
Returns:
Filename like 'kuma-push-disk-123.sh'
"""
return f"kuma-push-{push_metric}-{monitor_id}.sh"
def get_script_path(push_metric: str, monitor_id: int) -> str:
"""Generate the full path for a push monitor script.
Args:
push_metric: The metric type
monitor_id: The Uptime Kuma monitor ID
Returns:
Full path like '/usr/local/bin/kuma-push-disk-123.sh'
"""
return f"/usr/local/bin/{get_script_filename(push_metric, monitor_id)}"
def get_cronjob_entry(push_metric: str, monitor_id: int, interval_minutes: int = 5) -> str:
"""Generate the crontab entry for a push monitor script.
Args:
push_metric: The metric type
monitor_id: The Uptime Kuma monitor ID
interval_minutes: How often to run (default 5 minutes)
Returns:
Crontab entry string
"""
script_path = get_script_path(push_metric, monitor_id)
return f"*/{interval_minutes} * * * * {script_path} >/dev/null 2>&1"