Enclosure LED fixes

- Pass $VDEV_ENC_SYSFS_PATH to 'zpool [iostat|status] -c' to include
  enclosure LED sysfs path.

- Set LEDs correctly after import.  This includes clearing any erroniously
  set LEDs prior to the import, and setting the LED for any UNAVAIL drives.

- Include symlink for vdev_attach-led.sh in Makefile.am.

- Print the VDEV path in all-syslog.sh, and fix it so the pool GUID actually
  prints.    

Reviewed-by: Don Brady <don.brady@intel.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Tony Hutter <hutter2@llnl.gov>
Closes #5716 
Closes #5751
This commit is contained in:
Tony Hutter 2017-02-10 16:09:45 -08:00 committed by Brian Behlendorf
parent 65a736bc0d
commit b291029e86
8 changed files with 203 additions and 50 deletions

View File

@ -67,7 +67,9 @@ dist_zedexec_SCRIPTS = \
zed.d/scrub_finish-notify.sh \ zed.d/scrub_finish-notify.sh \
zed.d/statechange-led.sh \ zed.d/statechange-led.sh \
zed.d/statechange-notify.sh \ zed.d/statechange-notify.sh \
zed.d/vdev_clear-led.sh zed.d/vdev_clear-led.sh \
zed.d/vdev_attach-led.sh \
zed.d/pool_import-led.sh
zedconfdefaults = \ zedconfdefaults = \
all-syslog.sh \ all-syslog.sh \
@ -76,7 +78,9 @@ zedconfdefaults = \
scrub_finish-notify.sh \ scrub_finish-notify.sh \
statechange-led.sh \ statechange-led.sh \
statechange-notify.sh \ statechange-notify.sh \
vdev_clear-led.sh vdev_clear-led.sh \
vdev_attach-led.sh \
pool_import-led.sh
install-data-hook: install-data-hook:
$(MKDIR_P) "$(DESTDIR)$(zedconfdir)" $(MKDIR_P) "$(DESTDIR)$(zedconfdir)"

View File

@ -6,6 +6,7 @@
. "${ZED_ZEDLET_DIR}/zed-functions.sh" . "${ZED_ZEDLET_DIR}/zed-functions.sh"
zed_log_msg "eid=${ZEVENT_EID}" "class=${ZEVENT_SUBCLASS}" \ zed_log_msg "eid=${ZEVENT_EID}" "class=${ZEVENT_SUBCLASS}" \
"${ZEVENT_POOL:+"pool=${ZEVENT_POOL}"}" \ "${ZEVENT_POOL_GUID:+"pool_guid=${ZEVENT_POOL_GUID}"}" \
"${ZEVENT_VDEV_PATH:+"vdev_path=${ZEVENT_VDEV_PATH}"}" \
"${ZEVENT_VDEV_STATE_STR:+"vdev_state=${ZEVENT_VDEV_STATE_STR}"}" "${ZEVENT_VDEV_STATE_STR:+"vdev_state=${ZEVENT_VDEV_STATE_STR}"}"
exit 0 exit 0

View File

@ -0,0 +1 @@
statechange-led.sh

View File

@ -2,13 +2,19 @@
# #
# Turn off/on the VDEV's enclosure fault LEDs when the pool's state changes. # Turn off/on the VDEV's enclosure fault LEDs when the pool's state changes.
# #
# Turn LED on if the VDEV becomes faulted or degraded, and turn it back off # Turn the VDEV's fault LED on if it becomes FAULTED, DEGRADED or UNAVAIL.
# when it's online again. It will also turn on the LED (or keep it on) if # Turn the LED off when it's back ONLINE again.
# the drive becomes unavailable, unless the drive was in was a previously
# online state (online->unavail is a normal state transition during an
# autoreplace).
# #
# This script requires that your enclosure be supported by the # 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 # Linux SCSI enclosure services (ses) driver. The script will do nothing
# if you have no enclosure, or if your enclosure isn't supported. # if you have no enclosure, or if your enclosure isn't supported.
# #
@ -16,8 +22,8 @@
# 0: enclosure led successfully set # 0: enclosure led successfully set
# 1: enclosure leds not not available # 1: enclosure leds not not available
# 2: enclosure leds administratively disabled # 2: enclosure leds administratively disabled
# 3: ZED didn't pass enclosure sysfs path # 3: The led sysfs path passed from ZFS does not exist
# 4: Enclosure sysfs path doesn't exist # 4: $ZPOOL not set
[ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc" [ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc"
. "${ZED_ZEDLET_DIR}/zed-functions.sh" . "${ZED_ZEDLET_DIR}/zed-functions.sh"
@ -30,15 +36,54 @@ if [ "${ZED_USE_ENCLOSURE_LEDS}" != "1" ] ; then
exit 2 exit 2
fi fi
[ -n "${ZEVENT_VDEV_ENC_SYSFS_PATH}" ] || exit 3 zed_check_cmd "$ZPOOL" || exit 4
[ -e "${ZEVENT_VDEV_ENC_SYSFS_PATH}/fault" ] || exit 4 # Global used in set_led debug print
vdev=""
# Turn on/off enclosure LEDs # set_led (file)
function led #
# 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/fault" file="$1"
val=$2 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 # We want to check the current state first, since writing to the
# 'fault' entry always always causes a SES command, even if the # 'fault' entry always always causes a SES command, even if the
@ -53,28 +98,88 @@ function led
fi fi
if [ "$current" != "$val" ] ; then if [ "$current" != "$val" ] ; then
# Set the value twice. I've seen enclosures that were set_led "$file" "$val"
# flakey about setting it the first time.
echo "$val" > "$file"
echo "$val" > "$file"
fi fi
} }
# Decide whether to turn on/off an LED based on the state function state_to_val {
# Pass in path name and fault string ("ONLINE"/"FAULTED"/"DEGRADED"...etc) state="$1"
if [ "$state" == "FAULTED" ] || [ "$state" == "DEGRADED" ] || \
[ "$state" == "UNAVAIL" ] ; then
echo 1
elif [ "$state" == "ONLINE" ] ; then
echo 0
fi
}
# process_pool ([pool])
# #
# We only turn on LEDs when a drive becomes FAULTED, DEGRADED, or UNAVAIL and # Iterate through a pool (or pools) and set the VDEV's enclosure slot LEDs to
# only turn it on when it comes back ONLINE. All other states are ignored, and # the VDEV's state.
# keep the previous LED state. #
function process { # Arguments
path="$1" # pool: Optional pool name. If not specified, iterate though all pools.
fault=$2 #
if [ "$fault" == "FAULTED" ] || [ "$fault" == "DEGRADED" ] || \ # Return
[ "$fault" == "UNAVAIL" ] ; then # 0 on success, 3 on missing sysfs path
led "$path" 1 #
elif [ "$fault" == "ONLINE" ] ; then function process_pool
led "$path" 0 {
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 fi
} }
process "$ZEVENT_VDEV_ENC_SYSFS_PATH" "$ZEVENT_VDEV_STATE_STR" 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

View File

@ -413,3 +413,25 @@ zed_rate_limit()
zed_unlock "${lockfile}" "${lockfile_fd}" zed_unlock "${lockfile}" "${lockfile_fd}"
return "${rv}" return "${rv}"
} }
# zed_guid_to_pool (guid)
#
# Convert a pool GUID into its pool name (like "tank")
# Arguments
# guid: pool GUID (decimal or hex)
#
# Return
# Pool name
#
function zed_guid_to_pool
{
if [ -z "$1" ] ; then
return
fi
guid=$(printf "%llu" $1)
if [ ! -z "$guid" ] ; then
$ZPOOL get -H -ovalue,name guid | awk '$1=='$guid' {print $2}'
fi
}

View File

@ -332,9 +332,11 @@ vdev_run_cmd_thread(void *cb_cmd_data)
char cmd[_POSIX_ARG_MAX]; char cmd[_POSIX_ARG_MAX];
/* Set our VDEV_PATH and VDEV_UPATH env vars and run command */ /* Set our VDEV_PATH and VDEV_UPATH env vars and run command */
if (snprintf(cmd, sizeof (cmd), "VDEV_PATH=%s && VDEV_UPATH=%s && %s", if (snprintf(cmd, sizeof (cmd), "VDEV_PATH=%s && VDEV_UPATH=\"%s\" && "
data->path, data->upath ? data->upath : "\"\"", data->cmd) >= "VDEV_ENC_SYSFS_PATH=\"%s\" && %s", data->path ? data->path : "",
sizeof (cmd)) { data->upath ? data->upath : "",
data->vdev_enc_sysfs_path ? data->vdev_enc_sysfs_path : "",
data->cmd) >= sizeof (cmd)) {
/* Our string was truncated */ /* Our string was truncated */
return; return;
} }
@ -364,11 +366,15 @@ for_each_vdev_run_cb(zpool_handle_t *zhp, nvlist_t *nv, void *cb_vcdl)
vdev_cmd_data_t *data; vdev_cmd_data_t *data;
char *path = NULL; char *path = NULL;
char *vname = NULL; char *vname = NULL;
char *vdev_enc_sysfs_path = NULL;
int i, match = 0; int i, match = 0;
if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) != 0) if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) != 0)
return (1); return (1);
nvlist_lookup_string(nv, ZPOOL_CONFIG_VDEV_ENC_SYSFS_PATH,
&vdev_enc_sysfs_path);
/* Spares show more than once if they're in use, so skip if exists */ /* Spares show more than once if they're in use, so skip if exists */
for (i = 0; i < vcdl->count; i++) { for (i = 0; i < vcdl->count; i++) {
if ((strcmp(vcdl->data[i].path, path) == 0) && if ((strcmp(vcdl->data[i].path, path) == 0) &&
@ -406,6 +412,10 @@ for_each_vdev_run_cb(zpool_handle_t *zhp, nvlist_t *nv, void *cb_vcdl)
data->path = strdup(path); data->path = strdup(path);
data->upath = zfs_get_underlying_path(path); data->upath = zfs_get_underlying_path(path);
data->cmd = vcdl->cmd; data->cmd = vcdl->cmd;
if (vdev_enc_sysfs_path)
data->vdev_enc_sysfs_path = strdup(vdev_enc_sysfs_path);
else
data->vdev_enc_sysfs_path = NULL;
vcdl->count++; vcdl->count++;
@ -500,6 +510,7 @@ free_vdev_cmd_data_list(vdev_cmd_data_list_t *vcdl)
free(vcdl->data[i].pool); free(vcdl->data[i].pool);
free(vcdl->data[i].upath); free(vcdl->data[i].upath);
free(vcdl->data[i].line); free(vcdl->data[i].line);
free(vcdl->data[i].vdev_enc_sysfs_path);
} }
free(vcdl->data); free(vcdl->data);
free(vcdl); free(vcdl);

View File

@ -80,6 +80,7 @@ typedef struct vdev_cmd_data
char *upath; /* vdev underlying path */ char *upath; /* vdev underlying path */
char *pool; /* Pool name */ char *pool; /* Pool name */
char *cmd; /* backpointer to cmd */ char *cmd; /* backpointer to cmd */
char *vdev_enc_sysfs_path; /* enclosure sysfs path (if any) */
} vdev_cmd_data_t; } vdev_cmd_data_t;
typedef struct vdev_cmd_data_list typedef struct vdev_cmd_data_list

View File

@ -1548,12 +1548,16 @@ base 1024. To get the raw values, use the \fB-p\fR flag.
Run a command on each vdev and include first line of output Run a command on each vdev and include first line of output
.sp .sp
The \fB-c\fR option allows you to run an arbitrary command on each vdev and The \fB-c\fR option allows you to run an arbitrary command on each vdev and
display the first line of output in zpool iostat. The environment vars display the first line of output in zpool iostat. The following environment
\fBVDEV_PATH\fR and \fBVDEV_UPATH\fR are set to the vdev's path and "underlying vars are set before running each command:
path" before running the command. For device mapper, multipath, or partitioned .sp
vdevs, \fBVDEV_UPATH\fR is the actual underlying /dev/sd* disk. This can be \fB$VDEV_PATH\fR: Full path to the vdev.
useful if the command you're running requires a /dev/sd* device. Commands run .LP
in parallel for each vdev for performance. \fB$VDEV_UPATH\fR: "Underlying path" to the vdev. For device mapper, multipath, or
partitioned vdevs, \fBVDEV_UPATH\fR is the actual underlying /dev/sd* disk.
This can be useful if the command you're running requires a /dev/sd* device.
.LP
\fB$VDEV_ENC_SYSFS_PATH\fR: The sysfs path to the vdev's enclosure LEDs (if any).
.RE .RE
.sp .sp
@ -2116,12 +2120,16 @@ If a scrub or resilver is in progress, this command reports the percentage done
Run a command on each vdev and include first line of output Run a command on each vdev and include first line of output
.sp .sp
The \fB-c\fR option allows you to run an arbitrary command on each vdev and The \fB-c\fR option allows you to run an arbitrary command on each vdev and
display the first line of output in zpool status. The environment vars display the first line of output in zpool iostat. The following environment
\fBVDEV_PATH\fR and \fBVDEV_UPATH\fR are set to the vdev's path and "underlying vars are set before running each command:
path" before running the command. For device mapper, multipath, or partitioned .sp
vdevs, \fBVDEV_UPATH\fR is the actual underlying /dev/sd* disk. This can be \fB$VDEV_PATH\fR: Full path to the vdev.
useful if the command you're running requires a /dev/sd* device. Commands run .LP
in parallel for each vdev for performance. \fB$VDEV_UPATH\fR: "Underlying path" to the vdev. For device mapper, multipath, or
partitioned vdevs, \fBVDEV_UPATH\fR is the actual underlying /dev/sd* disk.
This can be useful if the command you're running requires a /dev/sd* device.
.LP
\fB$VDEV_ENC_SYSFS_PATH\fR: The sysfs path to the vdev's enclosure LEDs (if any).
.RE .RE
.sp .sp