#!/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