diff --git a/Orchestrators/sunday_morning_coffee_report.sh b/Orchestrators/sunday_morning_coffee_report.sh index 9341fbb..a259cf6 100644 --- a/Orchestrators/sunday_morning_coffee_report.sh +++ b/Orchestrators/sunday_morning_coffee_report.sh @@ -156,41 +156,54 @@ fi # ----------------------------------------------------------------------------------------------- section "📀 ARRAY" -# unRAID array info — parse var/local/emhttp correctly +# unRAID array info — parse disks.ini correctly +# Format uses ["diskN"] ["parity"] ["parity2"] etc. if [[ -f /var/local/emhttp/disks.ini ]]; then - # Count data disks only (disk1, disk2 etc — not parity, cache, flash) - DISK_COUNT=$(grep -c "^\[disk[0-9]" /var/local/emhttp/disks.ini 2>/dev/null || echo "?") - PARITY_COUNT=$(grep -c "^\[parity" /var/local/emhttp/disks.ini 2>/dev/null || echo 0) - line "Array disks: $DISK_COUNT data + $PARITY_COUNT parity" + DISK_COUNT=$(grep -c '^\["disk[0-9]' /var/local/emhttp/disks.ini 2>/dev/null || echo "?") + PARITY_COUNT=$(grep -c '^\["parity' /var/local/emhttp/disks.ini 2>/dev/null || echo "?") + DISK_COUNT="${DISK_COUNT//[^0-9]/}"; DISK_COUNT="${DISK_COUNT:-?}" + PARITY_COUNT="${PARITY_COUNT//[^0-9]/}"; PARITY_COUNT="${PARITY_COUNT:-?}" + line "Array: ${DISK_COUNT} data disks + ${PARITY_COUNT} parity" else - line "Array: disks.ini not found — array may not be started" + line "Array: disks.ini not found" fi -# Parity status from var/local/emhttp -PARITY_FILE="/var/local/emhttp/parity-date.txt" -if [[ -f "$PARITY_FILE" ]]; then - PARITY_INFO=$(cat "$PARITY_FILE" 2>/dev/null) - if echo "$PARITY_INFO" | grep -q "progress"; then - finding "Parity check in progress" - else - PARITY_DATE=$(echo "$PARITY_INFO" | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2}' | head -1) - PARITY_ERRORS=$(echo "$PARITY_INFO" | grep -oE 'errors=[0-9]+' | cut -d= -f2 || echo 0) - PARITY_ERRORS="${PARITY_ERRORS:-0}" +# Parity status from parity-checks.log +PARITY_LOG="/boot/config/parity-checks.log" +if [[ -f "$PARITY_LOG" ]]; then + LAST_CHECK=$(tail -1 "$PARITY_LOG" 2>/dev/null) + if [[ -n "$LAST_CHECK" ]]; then + PARITY_DATE=$(echo "$LAST_CHECK" | cut -d'|' -f1 | xargs) + PARITY_ERRORS=$(echo "$LAST_CHECK" | cut -d'|' -f4) + PARITY_ACTION=$(echo "$LAST_CHECK" | cut -d'|' -f6) + PARITY_DURATION=$(echo "$LAST_CHECK" | cut -d'|' -f2) + PARITY_DUR_HR=$(( PARITY_DURATION / 3600 )) + PARITY_DUR_MIN=$(( (PARITY_DURATION % 3600) / 60 )) + + # Negative = sync corrections (not data errors), positive = real errors if [[ "$PARITY_ERRORS" -gt 0 ]]; then - issue "Parity: $PARITY_ERRORS errors on last check (${PARITY_DATE:-unknown})" + issue "Parity: $PARITY_ERRORS errors on last check ($PARITY_DATE)" + elif [[ "$PARITY_ERRORS" -lt 0 ]]; then + CORRECTIONS=$(( PARITY_ERRORS * -1 )) + line "Parity: OK — last: $PARITY_DATE ($PARITY_ACTION, ${PARITY_DUR_HR}h${PARITY_DUR_MIN}m, $CORRECTIONS correction(s)) ✅" else - line "Parity: OK — last check: ${PARITY_DATE:-unknown}, 0 errors ✅" + line "Parity: OK — last: $PARITY_DATE ($PARITY_ACTION, ${PARITY_DUR_HR}h${PARITY_DUR_MIN}m, 0 errors) ✅" + fi + + # Check if currently running + RESYNC=$(grep "^mdResync=" /var/local/emhttp/var.ini 2>/dev/null | cut -d= -f2 | tr -d '"') + if [[ "$RESYNC" != "0" ]]; then + finding "Parity check currently in progress" fi fi else - line "Parity: no check recorded yet" + line "Parity: no check log found" fi # ZFS pool health — filter ignored pools if command -v zpool >/dev/null 2>&1; then while IFS=$'\t' read -r pool health; do [[ -z "$pool" ]] && continue - # Skip pools in ZFS_REPORT_IGNORE_POOLS SKIP=false for ignore in "${ZFS_REPORT_IGNORE_POOLS[@]:-}"; do [[ "$pool" == "$ignore" ]] && SKIP=true && break @@ -204,10 +217,12 @@ if command -v zpool >/dev/null 2>&1; then done < <(zpool list -H -o name,health 2>/dev/null) fi -# Drive temperatures from SMART — fixed parsing +# Drive temperatures from SMART — build lookup table first then display per disk if command -v smartctl >/dev/null 2>&1; then + declare -A DISK_TEMPS TEMP_WARN=false - TEMP_SUMMARY="" + ALL_NORMAL=true + for disk in /dev/sd? /dev/nvme?; do [[ ! -e "$disk" ]] && continue DISK_NAME=$(basename "$disk") @@ -217,38 +232,54 @@ if command -v smartctl >/dev/null 2>&1; then done [[ "$SKIP" == true ]] && continue - # Try multiple SMART temp attribute names — different drives use different fields TEMP=$(smartctl -A "$disk" 2>/dev/null | awk ' /^190 / || /^194 / { print $10; exit } /Temperature_Celsius/ { print $10; exit } /Airflow_Temperature/ { print $10; exit } ') - - # NVMe uses different output format if [[ -z "$TEMP" ]]; then TEMP=$(smartctl -A "$disk" 2>/dev/null | \ awk '/^Temperature:/ { print $2; exit }') fi - - # Strip any non-numeric chars TEMP="${TEMP//[^0-9]/}" [[ -z "$TEMP" ]] && continue - TEMP_INT="$TEMP" - if [[ "$TEMP_INT" -ge "${SMART_TEMP_CRIT:-55}" ]]; then - issue "Drive $DISK_NAME: ${TEMP_INT}°C — CRITICAL" + DISK_TEMPS["$DISK_NAME"]="$TEMP" + + if [[ "$TEMP" -ge "${SMART_TEMP_CRIT:-55}" ]]; then + issue "Drive $DISK_NAME: ${TEMP}°C — CRITICAL" TEMP_WARN=true - elif [[ "$TEMP_INT" -ge "${SMART_TEMP_WARN:-45}" ]]; then - finding "Drive $DISK_NAME: ${TEMP_INT}°C — warm" + ALL_NORMAL=false + elif [[ "$TEMP" -ge "${SMART_TEMP_WARN:-45}" ]]; then + finding "Drive $DISK_NAME: ${TEMP}°C — warm" TEMP_WARN=true + ALL_NORMAL=false fi - [[ -n "$TEMP_SUMMARY" ]] && TEMP_SUMMARY+=", " - TEMP_SUMMARY+="${DISK_NAME}:${TEMP_INT}°C" done - if [[ "$TEMP_WARN" == false ]]; then + + if [[ "$ALL_NORMAL" == true ]]; then line "Drive temps: all normal ✅" fi - [[ -n "$TEMP_SUMMARY" ]] && line "Temps: $TEMP_SUMMARY" + + # Show per-disk summary with device name from disks.ini + temp + if [[ -f /var/local/emhttp/disks.ini ]] && [[ ${#DISK_TEMPS[@]} -gt 0 ]]; then + # Parse disk → device mapping from disks.ini + DISK_SUMMARY="" + CURRENT_DISK="" + while IFS= read -r ini_line; do + if echo "$ini_line" | grep -qE '^\["(disk[0-9]+|parity[0-9]?|cache)"\]'; then + CURRENT_DISK=$(echo "$ini_line" | grep -o '"[^"]*"' | head -1 | tr -d '"') + elif echo "$ini_line" | grep -q '^device='; then + DEV=$(echo "$ini_line" | cut -d= -f2 | tr -d '"') + TEMP="${DISK_TEMPS[$DEV]:-}" + if [[ -n "$TEMP" ]]; then + [[ -n "$DISK_SUMMARY" ]] && DISK_SUMMARY+=", " + DISK_SUMMARY+="${CURRENT_DISK}(${DEV}):${TEMP}°C" + fi + fi + done < /var/local/emhttp/disks.ini + [[ -n "$DISK_SUMMARY" ]] && line "Temps: $DISK_SUMMARY" + fi fi # ----------------------------------------------------------------------------------------------- @@ -493,7 +524,7 @@ ROOTFS_PCT_CR=$(df / --output=pcent 2>/dev/null | tail -1 | tr -d ' %') MEM_AVAIL_CR=$(awk '/MemAvailable/ {printf "%.1f", $2/1048576}' /proc/meminfo) MEM_TOTAL_CR=$(awk '/MemTotal/ {printf "%.0f", $2/1048576}' /proc/meminfo) LOAD_CR=$(awk '{print $1}' /proc/loadavg) -ZOMBIE_CR=$(ps aux | awk '{print $8}' | grep -c "^Z$" 2>/dev/null || echo 0) +ZOMBIE_CR=$(ps aux | awk '{print $8}' | grep -c "^Z$" 2>/dev/null) ZOMBIE_CR="${ZOMBIE_CR//[^0-9]/}" ZOMBIE_CR="${ZOMBIE_CR:-0}" ARC_GB_CR="n/a"