🎬 ARR Suite Template Bootstrap - Complete Media Automation Stack Features: - 16 production services (Prowlarr, Sonarr, Radarr, Plex, etc.) - One-command Ansible deployment - VPN-protected downloads via Gluetun - Tailscale secure access - Production-ready security (UFW, Fail2Ban) - Automated backups and monitoring - Comprehensive documentation Ready for customization and deployment to any VPS. Co-authored-by: openhands <openhands@all-hands.dev>
210 lines
6.9 KiB
Django/Jinja
210 lines
6.9 KiB
Django/Jinja
#!/bin/bash
|
|
# Disk usage monitoring script for Arrs Media Stack
|
|
# Generated by Ansible
|
|
|
|
LOG_DIR="{{ docker_root }}/logs/system"
|
|
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
|
|
DISK_LOG="$LOG_DIR/disk-usage-$(date '+%Y%m%d').log"
|
|
|
|
# Ensure log directory exists
|
|
mkdir -p "$LOG_DIR"
|
|
|
|
# Function to log with timestamp
|
|
log_disk() {
|
|
echo "[$TIMESTAMP] $1" >> "$DISK_LOG"
|
|
}
|
|
|
|
# Disk usage thresholds
|
|
WARNING_THRESHOLD=80
|
|
CRITICAL_THRESHOLD=90
|
|
|
|
log_disk "=== DISK USAGE MONITORING ==="
|
|
|
|
# Monitor main directories
|
|
DIRECTORIES=(
|
|
"{{ docker_root }}"
|
|
"{{ media_root }}"
|
|
"/var/lib/docker"
|
|
"/tmp"
|
|
"/var/log"
|
|
)
|
|
|
|
for dir in "${DIRECTORIES[@]}"; do
|
|
if [[ -d "$dir" ]]; then
|
|
USAGE=$(df "$dir" | tail -1)
|
|
FILESYSTEM=$(echo "$USAGE" | awk '{print $1}')
|
|
TOTAL=$(echo "$USAGE" | awk '{print $2}')
|
|
USED=$(echo "$USAGE" | awk '{print $3}')
|
|
AVAILABLE=$(echo "$USAGE" | awk '{print $4}')
|
|
PERCENT=$(echo "$USAGE" | awk '{print $5}' | cut -d'%' -f1)
|
|
|
|
# Convert to human readable
|
|
TOTAL_GB=$((TOTAL / 1024 / 1024))
|
|
USED_GB=$((USED / 1024 / 1024))
|
|
AVAILABLE_GB=$((AVAILABLE / 1024 / 1024))
|
|
|
|
log_disk "DISK_USAGE $dir - Filesystem: $FILESYSTEM, Total: ${TOTAL_GB}GB, Used: ${USED_GB}GB (${PERCENT}%), Available: ${AVAILABLE_GB}GB"
|
|
|
|
# Check thresholds
|
|
if [[ $PERCENT -ge $CRITICAL_THRESHOLD ]]; then
|
|
log_disk "CRITICAL_ALERT $dir disk usage is ${PERCENT}% (>=${CRITICAL_THRESHOLD}%)"
|
|
elif [[ $PERCENT -ge $WARNING_THRESHOLD ]]; then
|
|
log_disk "WARNING_ALERT $dir disk usage is ${PERCENT}% (>=${WARNING_THRESHOLD}%)"
|
|
fi
|
|
else
|
|
log_disk "DIRECTORY_NOT_FOUND $dir does not exist"
|
|
fi
|
|
done
|
|
|
|
# Monitor specific subdirectories in Docker root
|
|
log_disk "=== DOCKER SUBDIRECTORY USAGE ==="
|
|
DOCKER_SUBDIRS=(
|
|
"{{ docker_root }}/sonarr"
|
|
"{{ docker_root }}/radarr"
|
|
"{{ docker_root }}/lidarr"
|
|
"{{ docker_root }}/bazarr"
|
|
"{{ docker_root }}/prowlarr"
|
|
"{{ docker_root }}/compose"
|
|
"{{ docker_root }}/logs"
|
|
)
|
|
|
|
for subdir in "${DOCKER_SUBDIRS[@]}"; do
|
|
if [[ -d "$subdir" ]]; then
|
|
SIZE=$(du -sh "$subdir" 2>/dev/null | cut -f1)
|
|
log_disk "SUBDIR_SIZE $subdir: $SIZE"
|
|
fi
|
|
done
|
|
|
|
# Monitor media subdirectories
|
|
log_disk "=== MEDIA DIRECTORY USAGE ==="
|
|
MEDIA_SUBDIRS=(
|
|
"{{ media_root }}/movies"
|
|
"{{ media_root }}/tv"
|
|
"{{ media_root }}/music"
|
|
"{{ media_root }}/downloads"
|
|
)
|
|
|
|
for subdir in "${MEDIA_SUBDIRS[@]}"; do
|
|
if [[ -d "$subdir" ]]; then
|
|
SIZE=$(du -sh "$subdir" 2>/dev/null | cut -f1)
|
|
FILE_COUNT=$(find "$subdir" -type f 2>/dev/null | wc -l)
|
|
log_disk "MEDIA_SIZE $subdir: $SIZE ($FILE_COUNT files)"
|
|
else
|
|
log_disk "MEDIA_DIR_NOT_FOUND $subdir does not exist"
|
|
fi
|
|
done
|
|
|
|
# Docker system disk usage
|
|
if command -v docker >/dev/null 2>&1; then
|
|
log_disk "=== DOCKER SYSTEM USAGE ==="
|
|
|
|
# Docker system df
|
|
DOCKER_DF=$(docker system df --format "{{ '{{.Type}}' }}\t{{ '{{.TotalCount}}' }}\t{{ '{{.Active}}' }}\t{{ '{{.Size}}' }}\t{{ '{{.Reclaimable}}' }}" 2>/dev/null)
|
|
if [[ -n "$DOCKER_DF" ]]; then
|
|
echo "$DOCKER_DF" | while IFS=$'\t' read -r type total active size reclaimable; do
|
|
log_disk "DOCKER_USAGE $type - Total: $total, Active: $active, Size: $size, Reclaimable: $reclaimable"
|
|
done
|
|
fi
|
|
|
|
# Container sizes
|
|
cd {{ docker_compose_dir }}
|
|
SERVICES=("sonarr" "radarr" "lidarr" "bazarr" "prowlarr" "watchtower")
|
|
|
|
for service in "${SERVICES[@]}"; do
|
|
CONTAINER_ID=$(docker-compose ps -q "$service" 2>/dev/null)
|
|
if [[ -n "$CONTAINER_ID" ]]; then
|
|
CONTAINER_SIZE=$(docker inspect "$CONTAINER_ID" --format='{{ "{{.SizeRw}}" }}' 2>/dev/null)
|
|
if [[ -n "$CONTAINER_SIZE" && "$CONTAINER_SIZE" != "null" ]]; then
|
|
CONTAINER_SIZE_MB=$((CONTAINER_SIZE / 1024 / 1024))
|
|
log_disk "CONTAINER_SIZE $service: ${CONTAINER_SIZE_MB}MB"
|
|
fi
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Large files detection
|
|
log_disk "=== LARGE FILES DETECTION ==="
|
|
LARGE_FILES=$(find {{ docker_root }} -type f -size +100M 2>/dev/null | head -10)
|
|
if [[ -n "$LARGE_FILES" ]]; then
|
|
echo "$LARGE_FILES" | while IFS= read -r file; do
|
|
SIZE=$(du -sh "$file" 2>/dev/null | cut -f1)
|
|
log_disk "LARGE_FILE $file: $SIZE"
|
|
done
|
|
else
|
|
log_disk "LARGE_FILES No files larger than 100MB found in {{ docker_root }}"
|
|
fi
|
|
|
|
# Log files size monitoring
|
|
log_disk "=== LOG FILES MONITORING ==="
|
|
LOG_DIRS=(
|
|
"{{ docker_root }}/logs"
|
|
"{{ docker_root }}/sonarr/logs"
|
|
"{{ docker_root }}/radarr/logs"
|
|
"{{ docker_root }}/lidarr/logs"
|
|
"{{ docker_root }}/bazarr/logs"
|
|
"{{ docker_root }}/prowlarr/logs"
|
|
"/var/log"
|
|
)
|
|
|
|
for log_dir in "${LOG_DIRS[@]}"; do
|
|
if [[ -d "$log_dir" ]]; then
|
|
LOG_SIZE=$(du -sh "$log_dir" 2>/dev/null | cut -f1)
|
|
LOG_COUNT=$(find "$log_dir" -name "*.log" -o -name "*.txt" 2>/dev/null | wc -l)
|
|
log_disk "LOG_DIR_SIZE $log_dir: $LOG_SIZE ($LOG_COUNT log files)"
|
|
|
|
# Find large log files
|
|
LARGE_LOGS=$(find "$log_dir" -name "*.log" -o -name "*.txt" -size +10M 2>/dev/null)
|
|
if [[ -n "$LARGE_LOGS" ]]; then
|
|
echo "$LARGE_LOGS" | while IFS= read -r logfile; do
|
|
SIZE=$(du -sh "$logfile" 2>/dev/null | cut -f1)
|
|
log_disk "LARGE_LOG $logfile: $SIZE"
|
|
done
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# Disk I/O statistics
|
|
if command -v iostat >/dev/null 2>&1; then
|
|
log_disk "=== DISK I/O STATISTICS ==="
|
|
IOSTAT_OUTPUT=$(iostat -d 1 1 | tail -n +4)
|
|
echo "$IOSTAT_OUTPUT" | while IFS= read -r line; do
|
|
if [[ -n "$line" && "$line" != *"Device"* ]]; then
|
|
log_disk "DISK_IO $line"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Cleanup recommendations
|
|
log_disk "=== CLEANUP RECOMMENDATIONS ==="
|
|
|
|
# Check for old Docker images
|
|
if command -v docker >/dev/null 2>&1; then
|
|
DANGLING_IMAGES=$(docker images -f "dangling=true" -q | wc -l)
|
|
if [[ $DANGLING_IMAGES -gt 0 ]]; then
|
|
log_disk "CLEANUP_RECOMMENDATION $DANGLING_IMAGES dangling Docker images can be removed with 'docker image prune'"
|
|
fi
|
|
|
|
UNUSED_VOLUMES=$(docker volume ls -f "dangling=true" -q | wc -l)
|
|
if [[ $UNUSED_VOLUMES -gt 0 ]]; then
|
|
log_disk "CLEANUP_RECOMMENDATION $UNUSED_VOLUMES unused Docker volumes can be removed with 'docker volume prune'"
|
|
fi
|
|
fi
|
|
|
|
# Check for old log files
|
|
OLD_LOGS=$(find {{ docker_root }}/logs -name "*.log" -mtime +30 2>/dev/null | wc -l)
|
|
if [[ $OLD_LOGS -gt 0 ]]; then
|
|
log_disk "CLEANUP_RECOMMENDATION $OLD_LOGS log files older than 30 days can be cleaned up"
|
|
fi
|
|
|
|
# Check for compressed logs
|
|
COMPRESSED_LOGS=$(find {{ docker_root }}/logs -name "*.gz" -mtime +90 2>/dev/null | wc -l)
|
|
if [[ $COMPRESSED_LOGS -gt 0 ]]; then
|
|
log_disk "CLEANUP_RECOMMENDATION $COMPRESSED_LOGS compressed log files older than 90 days can be removed"
|
|
fi
|
|
|
|
log_disk "=== END DISK USAGE MONITORING ==="
|
|
|
|
# Cleanup old disk usage logs (keep 7 days)
|
|
find "$LOG_DIR" -name "disk-usage-*.log" -mtime +7 -delete 2>/dev/null
|
|
|
|
exit 0 |