mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-04-25 08:56:21 +00:00
update storage-overview.tsx
This commit is contained in:
@@ -70,7 +70,42 @@ _smart_disk_label() {
|
||||
|
||||
_smart_json_path() {
|
||||
local disk="$1"
|
||||
echo "${SMART_DIR}/$(basename "$disk").json"
|
||||
local test_type="${2:-short}"
|
||||
local disk_name
|
||||
disk_name=$(basename "$disk")
|
||||
local disk_dir="${SMART_DIR}/${disk_name}"
|
||||
local timestamp
|
||||
timestamp=$(date +%Y-%m-%dT%H-%M-%S)
|
||||
|
||||
# Create disk directory if it doesn't exist
|
||||
mkdir -p "$disk_dir"
|
||||
|
||||
echo "${disk_dir}/${timestamp}_${test_type}.json"
|
||||
}
|
||||
|
||||
_smart_get_latest_json() {
|
||||
local disk="$1"
|
||||
local disk_name
|
||||
disk_name=$(basename "$disk")
|
||||
local disk_dir="${SMART_DIR}/${disk_name}"
|
||||
|
||||
if [[ -d "$disk_dir" ]]; then
|
||||
# Get most recent JSON file (sorted by name = sorted by timestamp)
|
||||
ls -1 "${disk_dir}"/*.json 2>/dev/null | sort -r | head -1
|
||||
fi
|
||||
}
|
||||
|
||||
_smart_cleanup_old_jsons() {
|
||||
local disk="$1"
|
||||
local retention="${2:-10}" # Default: keep last 10
|
||||
local disk_name
|
||||
disk_name=$(basename "$disk")
|
||||
local disk_dir="${SMART_DIR}/${disk_name}"
|
||||
|
||||
if [[ -d "$disk_dir" && "$retention" -gt 0 ]]; then
|
||||
# List all JSON files sorted by name (oldest last), skip first $retention, delete rest
|
||||
ls -1 "${disk_dir}"/*.json 2>/dev/null | sort -r | tail -n +$((retention + 1)) | xargs -r rm -f
|
||||
fi
|
||||
}
|
||||
|
||||
_smart_ensure_packages() {
|
||||
@@ -146,7 +181,7 @@ while true; do
|
||||
DISK_SIZE=$(lsblk -dn -o SIZE "$SELECTED_DISK" 2>/dev/null | xargs)
|
||||
if ! dialog --backtitle "$BACKTITLE" \
|
||||
--title "$(translate 'Long Test — Background')" \
|
||||
--yesno "\n$(translate 'The long test runs directly on the disk hardware.')\n\n$(translate 'Disk:') $SELECTED_DISK ($DISK_SIZE)\n\n$(translate 'The test will continue even if you close this terminal.')\n$(translate 'Results will be saved automatically to:')\n$(_smart_json_path "$SELECTED_DISK")\n\n$(translate 'Start long test now?')" \
|
||||
--yesno "\n$(translate 'The long test runs directly on the disk hardware.')\n\n$(translate 'Disk:') $SELECTED_DISK ($DISK_SIZE)\n\n$(translate 'The test will continue even if you close this terminal.')\n$(translate 'Results will be saved automatically to:')\n$(_smart_json_path "$SELECTED_DISK" "long")\n\n$(translate 'Start long test now?')" \
|
||||
16 $UI_RESULT_W; then
|
||||
continue
|
||||
fi
|
||||
@@ -253,9 +288,10 @@ while true; do
|
||||
fi
|
||||
;;
|
||||
|
||||
# ── Long test (background) ──────────────────────────────
|
||||
long)
|
||||
JSON_PATH=$(_smart_json_path "$SELECTED_DISK")
|
||||
# ── Long test (background) ──────────────────────────────
|
||||
long)
|
||||
JSON_PATH=$(_smart_json_path "$SELECTED_DISK" "long")
|
||||
_smart_cleanup_old_jsons "$SELECTED_DISK"
|
||||
DISK_SAFE=$(printf '%q' "$SELECTED_DISK")
|
||||
JSON_SAFE=$(printf '%q' "$JSON_PATH")
|
||||
|
||||
@@ -309,7 +345,7 @@ while true; do
|
||||
while smartctl -c ${DISK_SAFE} 2>/dev/null | grep -qiE 'Self-test routine in progress|[1-9][0-9]?% of test remaining'; do
|
||||
sleep 60
|
||||
done
|
||||
smartctl --json=c ${DISK_SAFE} > ${JSON_SAFE} 2>/dev/null
|
||||
smartctl -a --json=c ${DISK_SAFE} > ${JSON_SAFE} 2>/dev/null
|
||||
|
||||
# Send notification when test completes
|
||||
if [[ -f \"${NOTIFY_SCRIPT}\" ]]; then
|
||||
@@ -380,11 +416,17 @@ while true; do
|
||||
|
||||
# ── Auto-export JSON (except long — handled by background monitor)
|
||||
if [[ "$ACTION" != "long" && "$ACTION" != "report" ]]; then
|
||||
JSON_PATH=$(_smart_json_path "$SELECTED_DISK")
|
||||
# Determine test type from ACTION (short test or status check)
|
||||
local json_test_type="short"
|
||||
[[ "$ACTION" == "status" ]] && json_test_type="status"
|
||||
|
||||
JSON_PATH=$(_smart_json_path "$SELECTED_DISK" "$json_test_type")
|
||||
_smart_cleanup_old_jsons "$SELECTED_DISK"
|
||||
|
||||
if _smart_is_nvme "$SELECTED_DISK"; then
|
||||
nvme smart-log -o json "$SELECTED_DISK" > "$JSON_PATH" 2>/dev/null
|
||||
else
|
||||
smartctl --json=c "$SELECTED_DISK" > "$JSON_PATH" 2>/dev/null
|
||||
smartctl -a --json=c "$SELECTED_DISK" > "$JSON_PATH" 2>/dev/null
|
||||
fi
|
||||
[[ -s "$JSON_PATH" ]] || rm -f "$JSON_PATH"
|
||||
fi
|
||||
|
||||
195
scripts/storage/smart-scheduled-test.sh
Normal file
195
scripts/storage/smart-scheduled-test.sh
Normal file
@@ -0,0 +1,195 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# ProxMenux - SMART Scheduled Test Runner
|
||||
# ==========================================================
|
||||
# Author : MacRimi
|
||||
# Copyright : (c) 2024 MacRimi
|
||||
# License : GPL-3.0
|
||||
# Version : 1.0
|
||||
# Last Updated: 13/04/2026
|
||||
# ==========================================================
|
||||
# Description:
|
||||
# Runs scheduled SMART tests based on configuration.
|
||||
# Called by cron jobs created by ProxMenux Monitor.
|
||||
# ==========================================================
|
||||
|
||||
# Configuration
|
||||
SMART_DIR="/usr/local/share/proxmenux/smart"
|
||||
LOG_DIR="/var/log/proxmenux"
|
||||
SCRIPT_NAME="smart-scheduled-test"
|
||||
|
||||
# Ensure log directory exists
|
||||
mkdir -p "$LOG_DIR"
|
||||
|
||||
# Logging function
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$SCRIPT_NAME] $1"
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
SCHEDULE_ID=""
|
||||
TEST_TYPE="short"
|
||||
RETENTION=10
|
||||
DISKS=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--schedule-id)
|
||||
SCHEDULE_ID="$2"
|
||||
shift 2
|
||||
;;
|
||||
--test-type)
|
||||
TEST_TYPE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--retention)
|
||||
RETENTION="$2"
|
||||
shift 2
|
||||
;;
|
||||
--disks)
|
||||
DISKS="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
log "Starting scheduled SMART test: schedule=$SCHEDULE_ID, type=$TEST_TYPE, retention=$RETENTION"
|
||||
|
||||
# Helper functions
|
||||
_is_nvme() {
|
||||
[[ "$1" == *nvme* ]]
|
||||
}
|
||||
|
||||
_get_json_path() {
|
||||
local disk="$1"
|
||||
local test_type="$2"
|
||||
local disk_name
|
||||
disk_name=$(basename "$disk")
|
||||
local disk_dir="${SMART_DIR}/${disk_name}"
|
||||
local timestamp
|
||||
timestamp=$(date +%Y-%m-%dT%H-%M-%S)
|
||||
|
||||
mkdir -p "$disk_dir"
|
||||
echo "${disk_dir}/${timestamp}_${test_type}.json"
|
||||
}
|
||||
|
||||
_cleanup_old_jsons() {
|
||||
local disk="$1"
|
||||
local retention="$2"
|
||||
local disk_name
|
||||
disk_name=$(basename "$disk")
|
||||
local disk_dir="${SMART_DIR}/${disk_name}"
|
||||
|
||||
if [[ -d "$disk_dir" && "$retention" -gt 0 ]]; then
|
||||
ls -1 "${disk_dir}"/*.json 2>/dev/null | sort -r | tail -n +$((retention + 1)) | xargs -r rm -f
|
||||
fi
|
||||
}
|
||||
|
||||
_run_test() {
|
||||
local disk="$1"
|
||||
local test_type="$2"
|
||||
local json_path="$3"
|
||||
|
||||
log "Running $test_type test on $disk"
|
||||
|
||||
if _is_nvme "$disk"; then
|
||||
# NVMe test
|
||||
local code=1
|
||||
[[ "$test_type" == "long" ]] && code=2
|
||||
|
||||
nvme device-self-test "$disk" --self-test-code=$code 2>/dev/null
|
||||
if [[ $? -ne 0 ]]; then
|
||||
log "ERROR: Failed to start NVMe test on $disk"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Wait for test to complete
|
||||
local sleep_interval=10
|
||||
[[ "$test_type" == "long" ]] && sleep_interval=60
|
||||
|
||||
sleep 5
|
||||
while true; do
|
||||
local op
|
||||
op=$(nvme self-test-log "$disk" -o json 2>/dev/null | grep -o '"Current Device Self-Test Operation":[0-9]*' | grep -o '[0-9]*$')
|
||||
[[ -z "$op" || "$op" -eq 0 ]] && break
|
||||
sleep $sleep_interval
|
||||
done
|
||||
|
||||
# Save results
|
||||
nvme smart-log -o json "$disk" > "$json_path" 2>/dev/null
|
||||
else
|
||||
# SATA/SAS test
|
||||
local test_flag="-t short"
|
||||
[[ "$test_type" == "long" ]] && test_flag="-t long"
|
||||
|
||||
smartctl $test_flag "$disk" 2>/dev/null
|
||||
if [[ $? -ne 0 && $? -ne 4 ]]; then
|
||||
log "ERROR: Failed to start SMART test on $disk"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Wait for test to complete
|
||||
local sleep_interval=10
|
||||
[[ "$test_type" == "long" ]] && sleep_interval=60
|
||||
|
||||
sleep 5
|
||||
while smartctl -c "$disk" 2>/dev/null | grep -qiE 'Self-test routine in progress|[1-9][0-9]?% of test remaining'; do
|
||||
sleep $sleep_interval
|
||||
done
|
||||
|
||||
# Save results
|
||||
smartctl -a --json=c "$disk" > "$json_path" 2>/dev/null
|
||||
fi
|
||||
|
||||
log "Test completed on $disk, results saved to $json_path"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Get list of disks to test
|
||||
get_disk_list() {
|
||||
if [[ -n "$DISKS" && "$DISKS" != "all" ]]; then
|
||||
# Use specified disks
|
||||
echo "$DISKS" | tr ',' '\n'
|
||||
else
|
||||
# Get all physical disks
|
||||
lsblk -dpno NAME,TYPE 2>/dev/null | awk '$2=="disk"{print $1}'
|
||||
fi
|
||||
}
|
||||
|
||||
# Main execution
|
||||
DISK_LIST=$(get_disk_list)
|
||||
TOTAL_DISKS=$(echo "$DISK_LIST" | wc -l)
|
||||
SUCCESS_COUNT=0
|
||||
FAIL_COUNT=0
|
||||
|
||||
log "Found $TOTAL_DISKS disk(s) to test"
|
||||
|
||||
for disk in $DISK_LIST; do
|
||||
# Skip if disk doesn't exist
|
||||
if [[ ! -b "$disk" ]]; then
|
||||
log "WARNING: Disk $disk not found, skipping"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Get JSON path and cleanup old files
|
||||
JSON_PATH=$(_get_json_path "$disk" "$TEST_TYPE")
|
||||
_cleanup_old_jsons "$disk" "$RETENTION"
|
||||
|
||||
# Run the test
|
||||
if _run_test "$disk" "$TEST_TYPE" "$JSON_PATH"; then
|
||||
((SUCCESS_COUNT++))
|
||||
else
|
||||
((FAIL_COUNT++))
|
||||
fi
|
||||
done
|
||||
|
||||
log "Scheduled test complete: $SUCCESS_COUNT succeeded, $FAIL_COUNT failed"
|
||||
|
||||
# TODO: Send notification if configured
|
||||
# This would integrate with the notification system
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user