186 lines
4.6 KiB
Bash
Executable File
186 lines
4.6 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# Turn off/on the VDEV's enclosure fault LEDs when the pool's state changes.
|
|
#
|
|
# Turn the VDEV's fault LED on if it becomes FAULTED, DEGRADED or UNAVAIL.
|
|
# Turn the LED off when it's back ONLINE again.
|
|
#
|
|
# This script run in two basic modes:
|
|
#
|
|
# 1. If $ZEVENT_VDEV_ENC_SYSFS_PATH and $ZEVENT_VDEV_STATE_STR are set, then
|
|
# only set the LED for that particular VDEV. This is the case for statechange
|
|
# events and some vdev_* events.
|
|
#
|
|
# 2. If those vars are not set, then check the state of all VDEVs in the pool,i
|
|
# and set the LEDs accordingly. This is the case for pool_import events.
|
|
#
|
|
# Note that this script requires that your enclosure be supported by the
|
|
# Linux SCSI enclosure services (ses) driver. The script will do nothing
|
|
# if you have no enclosure, or if your enclosure isn't supported.
|
|
#
|
|
# Exit codes:
|
|
# 0: enclosure led successfully set
|
|
# 1: enclosure leds not not available
|
|
# 2: enclosure leds administratively disabled
|
|
# 3: The led sysfs path passed from ZFS does not exist
|
|
# 4: $ZPOOL not set
|
|
|
|
[ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc"
|
|
. "${ZED_ZEDLET_DIR}/zed-functions.sh"
|
|
|
|
if [ ! -d /sys/class/enclosure ] ; then
|
|
exit 1
|
|
fi
|
|
|
|
if [ "${ZED_USE_ENCLOSURE_LEDS}" != "1" ] ; then
|
|
exit 2
|
|
fi
|
|
|
|
zed_check_cmd "$ZPOOL" || exit 4
|
|
|
|
# Global used in set_led debug print
|
|
vdev=""
|
|
|
|
# set_led (file)
|
|
#
|
|
# Write an enclosure sysfs file
|
|
#
|
|
# Arguments
|
|
# file: sysfs file to set (like /sys/class/enclosure/0:0:1:0/SLOT 10/fault)
|
|
# val: value to set it to
|
|
#
|
|
# Globals
|
|
# vdev: VDEV name for debug printing
|
|
#
|
|
# Return
|
|
# nothing
|
|
#
|
|
function set_led {
|
|
file="$1"
|
|
val="$2"
|
|
|
|
# Set the value twice. I've seen enclosures that were
|
|
# flakey about setting it the first time.
|
|
echo "$val" > "$file"
|
|
echo "$val" > "$file"
|
|
zed_log_msg "vdev $vdev set '$file' LED to $val"
|
|
}
|
|
|
|
# check_and_set_led (file, val)
|
|
#
|
|
# Read an enclosure sysfs file, and write it if it's not already set to 'val'
|
|
#
|
|
# Arguments
|
|
# file: sysfs file to set (like /sys/class/enclosure/0:0:1:0/SLOT 10/fault)
|
|
# val: value to set it to
|
|
#
|
|
# Return
|
|
# 0 on success, 3 on missing sysfs path
|
|
#
|
|
function check_and_set_led
|
|
{
|
|
file="$1"
|
|
val="$2"
|
|
if [ ! -e "$ZEVENT_VDEV_ENC_SYSFS_PATH/fault" ] ; then
|
|
return 3
|
|
fi
|
|
|
|
# We want to check the current state first, since writing to the
|
|
# 'fault' entry always always causes a SES command, even if the
|
|
# current state is already what you want.
|
|
current=$(cat "${file}")
|
|
|
|
# On some enclosures if you write 1 to fault, and read it back,
|
|
# it will return 2. Treat all non-zero values as 1 for
|
|
# simplicity.
|
|
if [ "$current" != "0" ] ; then
|
|
current=1
|
|
fi
|
|
|
|
if [ "$current" != "$val" ] ; then
|
|
set_led "$file" "$val"
|
|
fi
|
|
}
|
|
|
|
function state_to_val {
|
|
state="$1"
|
|
if [ "$state" == "FAULTED" ] || [ "$state" == "DEGRADED" ] || \
|
|
[ "$state" == "UNAVAIL" ] ; then
|
|
echo 1
|
|
elif [ "$state" == "ONLINE" ] ; then
|
|
echo 0
|
|
fi
|
|
}
|
|
|
|
# process_pool ([pool])
|
|
#
|
|
# Iterate through a pool (or pools) and set the VDEV's enclosure slot LEDs to
|
|
# the VDEV's state.
|
|
#
|
|
# Arguments
|
|
# pool: Optional pool name. If not specified, iterate though all pools.
|
|
#
|
|
# Return
|
|
# 0 on success, 3 on missing sysfs path
|
|
#
|
|
function process_pool
|
|
{
|
|
pool="$1"
|
|
rc=0
|
|
|
|
# Lookup all the current LED values and paths in parallel
|
|
cmd='echo led_token=$(cat "$VDEV_ENC_SYSFS_PATH/fault"),"$VDEV_ENC_SYSFS_PATH",'
|
|
out=$($ZPOOL status -vc "$cmd" $pool | grep 'led_token=')
|
|
while read -r vdev state read write chksum therest ; do
|
|
|
|
# Read out current LED value and path
|
|
tmp=$(echo "$therest" | sed 's/^.*led_token=//g')
|
|
IFS="," read -r current_val vdev_enc_sysfs_path <<< "$tmp"
|
|
|
|
if [ -z "$vdev_enc_sysfs_path" ] ; then
|
|
# Skip anything with no sysfs LED entries
|
|
continue
|
|
fi
|
|
|
|
# On some enclosures if you write 1 to fault, and read it back,
|
|
# it will return 2. Treat all non-zero values as 1 for
|
|
# simplicity.
|
|
if [ "$current_val" != "0" ] ; then
|
|
current_val=1
|
|
fi
|
|
|
|
val=$(state_to_val "$state")
|
|
|
|
if [ "$current_val" == "$val" ] ; then
|
|
# LED is already set correctly
|
|
continue
|
|
fi
|
|
|
|
if [ ! -e "$vdev_enc_sysfs_path/fault" ] ; then
|
|
rc=1
|
|
zed_log_msg "vdev $vdev '$file/fault' doesn't exist"
|
|
continue;
|
|
fi
|
|
|
|
set_led "$vdev_enc_sysfs_path/fault" $val
|
|
|
|
done <<< "$out"
|
|
if [ "$rc" == "0" ] ; then
|
|
return 0
|
|
else
|
|
# We didn't see a sysfs entry that we wanted to set
|
|
return 3
|
|
fi
|
|
}
|
|
|
|
if [ ! -z "$ZEVENT_VDEV_ENC_SYSFS_PATH" ] && [ ! -z "$ZEVENT_VDEV_STATE_STR" ] ; then
|
|
# Got a statechange for an individual VDEV
|
|
val=$(state_to_val "$ZEVENT_VDEV_STATE_STR")
|
|
|
|
vdev="$(basename $ZEVENT_VDEV_PATH)"
|
|
check_and_set_led "$ZEVENT_VDEV_ENC_SYSFS_PATH/fault" "$val"
|
|
else
|
|
# Process the entire pool
|
|
process_pool $(zed_guid_to_pool $ZEVENT_POOL_GUID)
|
|
fi
|