diff --git a/README.md b/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/fundtracker/mutual-fund-tracker (5).zip b/fundtracker/mutual-fund-tracker (5).zip new file mode 100644 index 0000000..6012675 Binary files /dev/null and b/fundtracker/mutual-fund-tracker (5).zip differ diff --git a/zfs/nas16_zfs_scrub-1.sh b/zfs/nas16_zfs_scrub-1.sh deleted file mode 100644 index 917a1c8..0000000 --- a/zfs/nas16_zfs_scrub-1.sh +++ /dev/null @@ -1,322 +0,0 @@ -#!/bin/bash -# ============================================================================= -# ZFS Pool Scrub & Email Report — NAS16 -# Location: /usr/scripts/zfs/nas16_zfs_scrub.sh -# Schedule via OMV Scheduler (cron) -# ============================================================================= - -# ── Configuration ───────────────────────────────────────────────────────────── -HOSTNAME="NAS16" -REPORT_TO="rufus.king@kingdezigns.com" -REPORT_FROM="zfs-monitor@nas16.local" # Display only — Postfix uses your Gmail relay -SCRUB_WAIT_SECONDS=28800 # Max wait per pool scrub (8 hrs for large pools) -CAPACITY_WARN_THRESHOLD=80 # % capacity to flag as WARNING -SCRUB_AGE_WARN_DAYS=8 # Days since last scrub before flagging stale -LOG_FILE="/var/log/zfs_scrub_nas16.log" -# Note: Email is sent via OMV-configured Postfix (Gmail relay). No SMTP config needed here. -# ────────────────────────────────────────────────────────────────────────────── - -REPORT_TIME=$(date "+%Y-%m-%d %H:%M:%S") -POOLS=$(zpool list -H -o name) -POOL_COUNT=$(echo "$POOLS" | wc -l) - -log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"; } - -log "===== ZFS Scrub started on $HOSTNAME =====" - -# ── Run scrubs sequentially ─────────────────────────────────────────────────── -for pool in $POOLS; do - log "Starting scrub on pool: $pool" - zpool scrub "$pool" - - ELAPSED=0 - while true; do - SCRUB_STATE=$(zpool status "$pool" | grep -E "scan:" | awk '{print $2}') - [[ "$SCRUB_STATE" == "scrub" ]] || break - sleep 30 - ELAPSED=$((ELAPSED + 30)) - if [[ $ELAPSED -ge $SCRUB_WAIT_SECONDS ]]; then - log "WARNING: Scrub on $pool exceeded wait time. Moving on." - break - fi - done - log "Scrub complete (or timed out) for pool: $pool" -done - -# ── Gather data & build pool cards ─────────────────────────────────────────── - -OVERALL_STATUS="HEALTHY" # HEALTHY | WARNING | CRITICAL -POOLS_WITH_ISSUES=0 -POOL_CARDS_HTML="" - -for pool in $POOLS; do - STATUS_RAW=$(zpool list -H -o health "$pool") - SIZE=$(zpool list -H -o size "$pool") - USED=$(zpool list -H -o alloc "$pool") - FREE=$(zpool list -H -o free "$pool") - CAP=$(zpool list -H -o cap "$pool" | tr -d '%') - FRAG=$(zpool list -H -o frag "$pool") - SCRUB_LINE=$(zpool status "$pool" | grep -E "scan:") - ERRORS_LINE=$(zpool status "$pool" | grep "errors:") - CONFIG_BLOCK=$(zpool status "$pool" | awk '/config:/,/errors:/' | head -n -1) - FAULTED_LINES=$(echo "$CONFIG_BLOCK" | grep -E "FAULTED|DEGRADED|UNAVAIL|REMOVED" | grep -v "^$") - - # Per-pool severity - POOL_SEVERITY="HEALTHY" - ISSUE_DETAIL="" - - if [[ "$STATUS_RAW" == "DEGRADED" || "$STATUS_RAW" == "FAULTED" || "$STATUS_RAW" == "UNAVAIL" ]]; then - POOL_SEVERITY="CRITICAL" - OVERALL_STATUS="CRITICAL" - ISSUE_DETAIL="$FAULTED_LINES" - elif [[ "$CAP" -ge "$CAPACITY_WARN_THRESHOLD" ]]; then - POOL_SEVERITY="WARNING" - [[ "$OVERALL_STATUS" != "CRITICAL" ]] && OVERALL_STATUS="WARNING" - fi - - # Scrub error count - SCRUB_ERRORS=$(echo "$SCRUB_LINE" | grep -oP '\d+ errors' | head -1) - [[ -z "$SCRUB_ERRORS" ]] && SCRUB_ERRORS="0 errors" - - # Scrub date - SCRUB_DATE=$(echo "$SCRUB_LINE" | grep -oP '[A-Z][a-z]{2} \d+ \d+:\d+:\d+ \d{4}' | head -1) - [[ -z "$SCRUB_DATE" ]] && SCRUB_DATE="No scrub data" - - # Scrub age warning - SCRUB_AGE_NOTE="" - if [[ -n "$SCRUB_DATE" && "$SCRUB_DATE" != "No scrub data" ]]; then - SCRUB_EPOCH=$(date -d "$SCRUB_DATE" +%s 2>/dev/null) - NOW_EPOCH=$(date +%s) - DIFF_DAYS=$(( (NOW_EPOCH - SCRUB_EPOCH) / 86400 )) - if [[ $DIFF_DAYS -ge $SCRUB_AGE_WARN_DAYS ]]; then - SCRUB_AGE_NOTE=" — ⚠️ Last scrub was ${DIFF_DAYS} days ago" - [[ "$POOL_SEVERITY" == "HEALTHY" ]] && POOL_SEVERITY="WARNING" - [[ "$OVERALL_STATUS" != "CRITICAL" ]] && OVERALL_STATUS="WARNING" - fi - fi - - [[ "$POOL_SEVERITY" != "HEALTHY" ]] && POOLS_WITH_ISSUES=$((POOLS_WITH_ISSUES + 1)) - - # Colors - case "$STATUS_RAW" in - ONLINE) HEALTH_COLOR="#1a7f4b" ;; - DEGRADED) HEALTH_COLOR="#b91c1c" ;; - FAULTED) HEALTH_COLOR="#b91c1c" ;; - *) HEALTH_COLOR="#b45309" ;; - esac - - case "$POOL_SEVERITY" in - CRITICAL) CARD_BORDER="#fca5a5"; CARD_HEADER_BG="#fff1f2"; POOL_BADGE_BG="#b91c1c"; POOL_BADGE_LABEL="$STATUS_RAW" ;; - WARNING) CARD_BORDER="#fcd34d"; CARD_HEADER_BG="#fffbeb"; POOL_BADGE_BG="#b45309"; POOL_BADGE_LABEL="WARNING" ;; - *) CARD_BORDER="#86efac"; CARD_HEADER_BG="#f0fdf4"; POOL_BADGE_BG="#1a7f4b"; POOL_BADGE_LABEL="HEALTHY" ;; - esac - - CAP_COLOR="#111827" - [[ "$CAP" -ge "$CAPACITY_WARN_THRESHOLD" ]] && CAP_COLOR="#b45309" - [[ "$CAP" -ge 90 ]] && CAP_COLOR="#b91c1c" - - # Issue block - ISSUE_BLOCK="" - if [[ -n "$ISSUE_DETAIL" ]]; then - ESCAPED_DETAIL=$(echo "$ISSUE_DETAIL" | sed 's//\>/g') - ISSUE_BLOCK=" - -
$ESCAPED_DETAIL
- " - fi - - # Config block - ESCAPED_CONFIG=$(echo "$CONFIG_BLOCK" | sed 's//\>/g') - - POOL_CARDS_HTML+=" -
- - -
- - - - - -
- 🗄️ $pool - pool - - $POOL_BADGE_LABEL -
-
- - -
- - - - - - - - - - - - - - - $ISSUE_BLOCK -
-
Total
-
$SIZE
-
-
Used
-
$USED
-
-
Free
-
$FREE
-
-
Capacity
-
${CAP}%
-
-
Frag
-
$FRAG
-
-
Health
-
$STATUS_RAW
-
- - -
- 🔍 Last Scrub - Completed  |  $SCRUB_DATE  |  $SCRUB_ERRORS${SCRUB_AGE_NOTE} -
- - -
- ▶ vdev / drive configuration -
$ESCAPED_CONFIG
-
-
-
" -done - -# ── Overall banner & badge ──────────────────────────────────────────────────── -case "$OVERALL_STATUS" in - CRITICAL) - BADGE_HTML="
🚨 CRITICAL
" - BANNER_HTML="🚨  CRITICAL issue detected — immediate action required!" - ISSUES_COLOR="#b91c1c" - ;; - WARNING) - BADGE_HTML="
⚠️ WARNING
" - BANNER_HTML="⚠️  Warning condition detected — review recommended." - ISSUES_COLOR="#b45309" - ;; - *) - BADGE_HTML="
✅ ALL HEALTHY
" - BANNER_HTML="✅  All pools are healthy — no action required." - ISSUES_COLOR="#1a7f4b" - ;; -esac - -# ── Compose full HTML email ─────────────────────────────────────────────────── -HTML_BODY=$(cat < - - - - - -
- - - - - - - $BANNER_HTML - - - - - - - - - - -
- - - - - -
-
ZFS Pool Health Monitor
-
🔌 $HOSTNAME
-
$REPORT_TIME  |  $POOL_COUNT pool(s) monitored
-
$BADGE_HTML
-
- $POOL_CARDS_HTML -
-
-
📊 Report Summary
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Host$HOSTNAME
Report Time$REPORT_TIME
Pools Checked$POOL_COUNT
Pools with Issues$POOLS_WITH_ISSUES
Scrub Age Warning Threshold$SCRUB_AGE_WARN_DAYS days
Capacity Warning Threshold${CAPACITY_WARN_THRESHOLD}%
Overall Status$OVERALL_STATUS
-
-
- Automated ZFS Monitor  |  $HOSTNAME  |  KingDezigns Infrastructure -
-
- - -EOF -) - -# ── Send email via OMV Postfix (Gmail relay) ────────────────────────────────── -SUBJECT="[ZFS] $HOSTNAME — $OVERALL_STATUS | $REPORT_TIME" - -{ - echo "To: $REPORT_TO" - echo "From: $REPORT_FROM" - echo "Subject: $SUBJECT" - echo "MIME-Version: 1.0" - echo "Content-Type: text/html; charset=UTF-8" - echo "" - echo "$HTML_BODY" -} | sendmail -t - -log "Email sent to $REPORT_TO — Overall status: $OVERALL_STATUS" -log "===== ZFS Scrub complete on $HOSTNAME =====" diff --git a/zfs/nas16_zfs_scrub.sh b/zfs/nas16_zfs_scrub.sh index 0101bba..917a1c8 100644 --- a/zfs/nas16_zfs_scrub.sh +++ b/zfs/nas16_zfs_scrub.sh @@ -29,7 +29,6 @@ for pool in $POOLS; do log "Starting scrub on pool: $pool" zpool scrub "$pool" - # Wait for scrub to finish ELAPSED=0 while true; do SCRUB_STATE=$(zpool status "$pool" | grep -E "scan:" | awk '{print $2}') @@ -44,7 +43,7 @@ for pool in $POOLS; do log "Scrub complete (or timed out) for pool: $pool" done -# ── Gather data & build HTML report ────────────────────────────────────────── +# ── Gather data & build pool cards ─────────────────────────────────────────── OVERALL_STATUS="HEALTHY" # HEALTHY | WARNING | CRITICAL POOLS_WITH_ISSUES=0 @@ -62,7 +61,7 @@ for pool in $POOLS; do CONFIG_BLOCK=$(zpool status "$pool" | awk '/config:/,/errors:/' | head -n -1) FAULTED_LINES=$(echo "$CONFIG_BLOCK" | grep -E "FAULTED|DEGRADED|UNAVAIL|REMOVED" | grep -v "^$") - # Determine per-pool severity + # Per-pool severity POOL_SEVERITY="HEALTHY" ISSUE_DETAIL="" @@ -75,7 +74,7 @@ for pool in $POOLS; do [[ "$OVERALL_STATUS" != "CRITICAL" ]] && OVERALL_STATUS="WARNING" fi - # Scrub error count from status + # Scrub error count SCRUB_ERRORS=$(echo "$SCRUB_LINE" | grep -oP '\d+ errors' | head -1) [[ -z "$SCRUB_ERRORS" ]] && SCRUB_ERRORS="0 errors" @@ -98,7 +97,7 @@ for pool in $POOLS; do [[ "$POOL_SEVERITY" != "HEALTHY" ]] && POOLS_WITH_ISSUES=$((POOLS_WITH_ISSUES + 1)) - # Health value color + # Colors case "$STATUS_RAW" in ONLINE) HEALTH_COLOR="#1a7f4b" ;; DEGRADED) HEALTH_COLOR="#b91c1c" ;; @@ -106,97 +105,115 @@ for pool in $POOLS; do *) HEALTH_COLOR="#b45309" ;; esac - # Pool card header color case "$POOL_SEVERITY" in - CRITICAL) CARD_BG="#fff1f2"; BADGE_BG="#b91c1c"; BADGE_LABEL="$STATUS_RAW" ;; - WARNING) CARD_BG="#fffbeb"; BADGE_BG="#b45309"; BADGE_LABEL="WARNING" ;; - *) CARD_BG="#f0fdf4"; BADGE_BG="#1a7f4b"; BADGE_LABEL="HEALTHY" ;; + CRITICAL) CARD_BORDER="#fca5a5"; CARD_HEADER_BG="#fff1f2"; POOL_BADGE_BG="#b91c1c"; POOL_BADGE_LABEL="$STATUS_RAW" ;; + WARNING) CARD_BORDER="#fcd34d"; CARD_HEADER_BG="#fffbeb"; POOL_BADGE_BG="#b45309"; POOL_BADGE_LABEL="WARNING" ;; + *) CARD_BORDER="#86efac"; CARD_HEADER_BG="#f0fdf4"; POOL_BADGE_BG="#1a7f4b"; POOL_BADGE_LABEL="HEALTHY" ;; esac - # Capacity color CAP_COLOR="#111827" [[ "$CAP" -ge "$CAPACITY_WARN_THRESHOLD" ]] && CAP_COLOR="#b45309" - [[ "$CAP" -ge 90 ]] && CAP_COLOR="#b91c1c" + [[ "$CAP" -ge 90 ]] && CAP_COLOR="#b91c1c" - # Issue block (only shown when there's a problem) + # Issue block ISSUE_BLOCK="" if [[ -n "$ISSUE_DETAIL" ]]; then ESCAPED_DETAIL=$(echo "$ISSUE_DETAIL" | sed 's//\>/g') - ISSUE_BLOCK="
$ESCAPED_DETAIL
" + ISSUE_BLOCK=" + +
$ESCAPED_DETAIL
+ " fi # Config block ESCAPED_CONFIG=$(echo "$CONFIG_BLOCK" | sed 's//\>/g') POOL_CARDS_HTML+=" -
-
-
- 🗄️ $pool - pool -
- $BADGE_LABEL -
-
+
+ + +
- - - - - - - - - - -
-
Total Size
-
$SIZE
+
+ 🗄️ $pool + pool -
Used
-
$USED
-
-
Free
-
$FREE
-
-
Capacity
-
${CAP}%
-
-
Fragmentation
-
$FRAG
-
-
Health
-
$STATUS_RAW
+
+ $POOL_BADGE_LABEL
-
- 🔍 Last Scrub - Completed  |  $SCRUB_DATE  |  $SCRUB_ERRORS$SCRUB_AGE_NOTE +
+ + +
+ + + + + + + + + + + + + + + $ISSUE_BLOCK +
+
Total
+
$SIZE
+
+
Used
+
$USED
+
+
Free
+
$FREE
+
+
Capacity
+
${CAP}%
+
+
Frag
+
$FRAG
+
+
Health
+
$STATUS_RAW
+
+ + +
+ 🔍 Last Scrub + Completed  |  $SCRUB_DATE  |  $SCRUB_ERRORS${SCRUB_AGE_NOTE}
- $ISSUE_BLOCK -
- ▶ vdev / drive configuration + + +
+ ▶ vdev / drive configuration
$ESCAPED_CONFIG
" done -# ── Overall banner ──────────────────────────────────────────────────────────── +# ── Overall banner & badge ──────────────────────────────────────────────────── case "$OVERALL_STATUS" in CRITICAL) BADGE_HTML="
🚨 CRITICAL
" BANNER_HTML="🚨  CRITICAL issue detected — immediate action required!" + ISSUES_COLOR="#b91c1c" ;; WARNING) BADGE_HTML="
⚠️ WARNING
" BANNER_HTML="⚠️  Warning condition detected — review recommended." + ISSUES_COLOR="#b45309" ;; *) BADGE_HTML="
✅ ALL HEALTHY
" BANNER_HTML="✅  All pools are healthy — no action required." + ISSUES_COLOR="#1a7f4b" ;; esac @@ -210,6 +227,7 @@ HTML_BODY=$(cat < +
@@ -223,15 +241,18 @@ HTML_BODY=$(cat < + $BANNER_HTML + + +
$POOL_CARDS_HTML
-
📋 Report Summary
+
📊 Report Summary
@@ -250,7 +271,7 @@ HTML_BODY=$(cat < - + @@ -262,10 +283,16 @@ HTML_BODY=$(cat <Capacity Warning Threshold + + + + +
Host
Pools with Issues$POOLS_WITH_ISSUES$POOLS_WITH_ISSUES
${CAPACITY_WARN_THRESHOLD}%
Overall Status$OVERALL_STATUS
Automated ZFS Monitor  |  $HOSTNAME  |  KingDezigns Infrastructure