Fixed problem identifying some mechanical drives as SSDs

Added -x option to control the badblocks -e option, allowing extended testing.
Added smartctl to the list of dependencies.
Changed disk type detection so that we assume all drives are mechanical drives
unless they explicitly return 'Solid State Drive' for Rotational Rate.
Removed datestamp from every line of log output, only emitting it in log headers.
Minor reformatting.
This commit is contained in:
Keith Nash
2020-10-05 23:41:41 -05:00
committed by GitHub
parent 84134fe241
commit 7f6792d6e3

View File

@@ -1,10 +1,35 @@
#!/bin/sh #!/bin/sh
################################################################################
#
# disk-burnin.sh
#
################################################################################
################################################################################
# PRE-EXECUTION VALIDATION
################################################################################
# Check if running as root
if [ "$(id -u)" -ne 0 ]; then
echo "ERROR: Must be run as root" >&2
exit 2
fi
# Check required dependencies
readonly DEPENDENCIES="awk badblocks grep sed sleep smartctl"
for dependency in ${DEPENDENCIES}; do
if ! command -v "${dependency}" > /dev/null 2>&1; then
echo "ERROR: Command '${dependency}' not found" >&2
exit 2
fi
done
readonly USAGE=\ readonly USAGE=\
"NAME "NAME
$(basename "$0") -- disk burn-in program $(basename "$0") -- disk burn-in program
SYNOPSIS SYNOPSIS
$(basename "$0") [-h] [-e] [-f] [-o <directory>] <disk> $(basename "$0") [-h] [-e] [-f] [-o <directory>] [-x] <disk>
DESCRIPTION DESCRIPTION
A script to simplify the process of burning-in disks. Only intended for use A script to simplify the process of burning-in disks. Only intended for use
@@ -19,12 +44,13 @@ DESCRIPTION
In order to perform tests on drives, you will need to provide the -f option. In order to perform tests on drives, you will need to provide the -f option.
OPTIONS OPTIONS
-h show help text -h Show help text
-e show extended help text -e Show extended help text
-f run in destructive, non-dry mode -f Force script to run in destructive mode
ALL DATA ON THE DISK WILL BE LOST! ALL DATA ON THE DISK WILL BE LOST!
-o <directory> write log files to <directory> (default: $(pwd)) -o <directory> Write log files to <directory> (default: $(pwd))
<disk> disk to burn-in (/dev/ may be omitted) -x Run full pass of badblocks instead of exiting on first error
<disk> Disk to burn-in (/dev/ may be omitted)
EXAMPLES EXAMPLES
$(basename "$0") sda $(basename "$0") sda
@@ -184,14 +210,21 @@ VERSIONS
Check for root privileges during runtime. Check for root privileges during runtime.
Add option parsing, most notably (-h)elp and -f for non-dry-run mode. Add option parsing, most notably (-h)elp and -f for non-dry-run mode.
Add dry_run_wrapper() function. Add dry_run_wrapper() function.
Add disk type detection to skip badblocks for non-mechanical drives." Add disk type detection to skip badblocks for non-mechanical drives.
################################################################################ KN, 5 Oct 2020
# PRE-EXECUTION VALIDATION Added -x option to control the badblocks -e option, allowing extended testing.
################################################################################ Added smartctl to the list of dependencies.
Changed disk type detection so that we assume all drives are mechanical drives
unless they explicitly return 'Solid State Drive' for Rotational Rate.
Removed datestamp from every line of log output, only emitting it in log headers.
Minor reformatting."
# badblocks default -e option is 1, stop testing if a single error occurs
BB_E_ARG=1
# parse options # parse options
while getopts ':hefo:' option; do while getopts ':hefo:x' option; do
case "${option}" in case "${option}" in
h) echo "${USAGE}" h) echo "${USAGE}"
exit exit
@@ -204,6 +237,8 @@ while getopts ':hefo:' option; do
;; ;;
o) LOG_DIR="${OPTARG}" o) LOG_DIR="${OPTARG}"
;; ;;
x) BB_E_ARG=0
;;
:) printf 'Missing argument for -%s\n' "${OPTARG}" >&2 :) printf 'Missing argument for -%s\n' "${OPTARG}" >&2
echo "${USAGE}" >&2 echo "${USAGE}" >&2
exit 2 exit 2
@@ -217,30 +252,17 @@ done
shift $(( OPTIND - 1 )) shift $(( OPTIND - 1 ))
if [ -z "$1" ]; then if [ -z "$1" ]; then
echo "Missing option: <disk>" >&2 echo "ERROR: Missing disk argument" >&2
echo "${USAGE}" >&2 echo "${USAGE}" >&2
exit 2 exit 2
fi fi
# Check required dependencies
readonly DEPENDENCIES="awk badblocks grep sed sleep"
for dependency in ${DEPENDENCIES}; do
if ! command -v "${dependency}" > /dev/null 2>&1; then
echo "Command '${dependency}' not found" >&2
exit 2
fi
done
# Check if running as root
if [ "$(id -u)" -ne 0 ]; then
echo "Please run as root" >&2
exit 2
fi
################################################################################ ################################################################################
# CONSTANTS # CONSTANTS
################################################################################ ################################################################################
readonly BB_E_ARG
# Drive to burn-in # Drive to burn-in
DRIVE="$1" DRIVE="$1"
# prepend /dev/ if necessary # prepend /dev/ if necessary
@@ -249,6 +271,12 @@ if ! printf '%s' "${DRIVE}" | grep "/dev/\w*" > /dev/null 2>&1; then
fi fi
readonly DRIVE readonly DRIVE
if [ ! -e "$DRIVE" ]; then
echo "ERROR: Disk does not exist: $DRIVE" >&2
echo "${USAGE}" >&2
exit 2
fi
# Set to working directory if -o <directory> wasn't provided # Set to working directory if -o <directory> wasn't provided
[ -z "${LOG_DIR}" ] && LOG_DIR="$(pwd)" [ -z "${LOG_DIR}" ] && LOG_DIR="$(pwd)"
# Trim trailing slashes # Trim trailing slashes
@@ -314,12 +342,11 @@ DISK_MODEL="$(get_smart_info_value "Device Model")"
[ -z "${DISK_MODEL}" ] && DISK_MODEL="$(get_smart_info_value "Model Number")" [ -z "${DISK_MODEL}" ] && DISK_MODEL="$(get_smart_info_value "Model Number")"
readonly DISK_MODEL readonly DISK_MODEL
# Get disk type # Get disk type; unless we get 'Solid State Device' as return value, assume
# we have a mechanical drive.
DISK_TYPE="$(get_smart_info_value "Rotation Rate")" DISK_TYPE="$(get_smart_info_value "Rotation Rate")"
if printf '%s' "${DISK_TYPE}" | grep "rpm" > /dev/null 2>&1; then if printf '%s' "${DISK_TYPE}" | grep -i "solid_state_device" > /dev/null 2>&1; then
DISK_TYPE="mechanical" DISK_TYPE="SSD"
else
DISK_TYPE="non-mechanical"
fi fi
readonly DISK_TYPE readonly DISK_TYPE
@@ -358,10 +385,10 @@ readonly BB_File="${LOG_DIR}/burnin-${DISK_MODEL}_${SERIAL_NUMBER}.bb"
# Outputs: # Outputs:
# Write message to stdout and log file. # Write message to stdout and log file.
################################################## ##################################################
log_info() log_info() {
{ # now="$(date +"%F %T %Z")"
now="$(date +"%F %T %Z")" # printf "%s\n" "[${now}] $1" | tee -a "${LOG_FILE}"
printf "%s\n" "[${now}] $1" | tee -a "${LOG_FILE}" printf "%s\n" "$1" | tee -a "${LOG_FILE}"
} }
################################################## ##################################################
@@ -369,10 +396,9 @@ log_info()
# Arguments: # Arguments:
# Message to log. # Message to log.
################################################## ##################################################
log_header() log_header() {
{
log_info "+-----------------------------------------------------------------------------" log_info "+-----------------------------------------------------------------------------"
log_info "+ $1" log_info "+ $1: $(date)"
log_info "+-----------------------------------------------------------------------------" log_info "+-----------------------------------------------------------------------------"
} }
@@ -422,8 +448,7 @@ cleanup_log() {
# Arguments: # Arguments:
# Command to run. # Command to run.
################################################## ##################################################
dry_run_wrapper() dry_run_wrapper() {
{
if [ -z "$DRY_RUN" ]; then if [ -z "$DRY_RUN" ]; then
log_info "DRY RUN: $*" log_info "DRY RUN: $*"
return 0 return 0
@@ -451,7 +476,7 @@ dry_run_wrapper()
################################################## ##################################################
log_runtime_info() { log_runtime_info() {
log_info "Host: ${HOSTNAME}" log_info "Host: ${HOSTNAME}"
log_info "OS Flavor: ${OS_FLAVOR}" log_info "OS: ${OS_FLAVOR}"
log_info "Drive: ${DRIVE}" log_info "Drive: ${DRIVE}"
log_info "Disk Type: ${DISK_TYPE}" log_info "Disk Type: ${DISK_TYPE}"
log_info "Drive Model: ${DISK_MODEL}" log_info "Drive Model: ${DISK_MODEL}"
@@ -476,8 +501,7 @@ log_runtime_info() {
# 0 if success or failure. # 0 if success or failure.
# 1 if timeout threshold exceeded. # 1 if timeout threshold exceeded.
################################################## ##################################################
poll_selftest_complete() poll_selftest_complete() {
{
l_poll_duration_seconds=0 l_poll_duration_seconds=0
while [ "${l_poll_duration_seconds}" -lt "${POLL_TIMEOUT_SECONDS}" ]; do while [ "${l_poll_duration_seconds}" -lt "${POLL_TIMEOUT_SECONDS}" ]; do
smartctl --all "${DRIVE}" \ smartctl --all "${DRIVE}" \
@@ -512,8 +536,7 @@ poll_selftest_complete()
# - long # - long
# Test duration in seconds. # Test duration in seconds.
################################################## ##################################################
run_smart_test() run_smart_test() {
{
log_header "Running SMART $1 test" log_header "Running SMART $1 test"
dry_run_wrapper "smartctl --test=\"$1\" \"${DRIVE}\"" dry_run_wrapper "smartctl --test=\"$1\" \"${DRIVE}\""
log_info "SMART $1 test started, awaiting completion for $2 seconds ..." log_info "SMART $1 test started, awaiting completion for $2 seconds ..."
@@ -533,11 +556,10 @@ run_smart_test()
# Arguments: # Arguments:
# None # None
################################################## ##################################################
run_badblocks_test() run_badblocks_test() {
{
log_header "Running badblocks test" log_header "Running badblocks test"
if [ "${DISK_TYPE}" = "mechanical" ]; then if [ "${DISK_TYPE}" != "SSD" ]; then
dry_run_wrapper "badblocks -b 4096 -wsv -e 1 -o \"${BB_File}\" \"${DRIVE}\"" dry_run_wrapper "badblocks -b 4096 -wsv -e ${BB_E_ARG} -o \"${BB_File}\" \"${DRIVE}\""
else else
log_info "SKIPPED: badblocks for ${DISK_TYPE} device" log_info "SKIPPED: badblocks for ${DISK_TYPE} device"
fi fi
@@ -553,7 +575,7 @@ run_badblocks_test()
# None # None
################################################## ##################################################
log_full_device_info() { log_full_device_info() {
log_header "SMART and non-SMART information" log_header "Drive information"
dry_run_wrapper "smartctl --xall --vendorattribute=7,hex48 \"${DRIVE}\" | tee -a \"${LOG_FILE}\"" dry_run_wrapper "smartctl --xall --vendorattribute=7,hex48 \"${DRIVE}\" | tee -a \"${LOG_FILE}\""
} }