aboutsummaryrefslogtreecommitdiffstats
path: root/cmd/zpool
diff options
context:
space:
mode:
authorTony Hutter <[email protected]>2023-12-21 10:53:16 -0800
committerGitHub <[email protected]>2023-12-21 10:53:16 -0800
commita9520e6e5961d9fc58d71b97652773d0f5fee1ff (patch)
tree05592b2a5e896114942dda1f6e7770cd9fc1405c /cmd/zpool
parent6930ecbb75e17f6d6cc7686211d4f3b34852795b (diff)
zpool: Add slot power control, print power status
Add `zpool` flags to control the slot power to drives. This assumes your SAS or NVMe enclosure supports slot power control via sysfs. The new `--power` flag is added to `zpool offline|online|clear`: zpool offline --power <pool> <device> Turn off device slot power zpool online --power <pool> <device> Turn on device slot power zpool clear --power <pool> [device] Turn on device slot power If the ZPOOL_AUTO_POWER_ON_SLOT env var is set, then the '--power' option is automatically implied for `zpool online` and `zpool clear` and does not need to be passed. zpool status also gets a --power option to print the slot power status. Reviewed-by: Brian Behlendorf <[email protected]> Reviewed-by: Mart Frauenlob <[email protected]> Signed-off-by: Tony Hutter <[email protected]> Closes #15662
Diffstat (limited to 'cmd/zpool')
-rw-r--r--cmd/zpool/os/freebsd/zpool_vdev_os.c14
-rw-r--r--cmd/zpool/os/linux/zpool_vdev_os.c255
-rw-r--r--cmd/zpool/zpool_iter.c4
-rw-r--r--cmd/zpool/zpool_main.c239
-rw-r--r--cmd/zpool/zpool_util.h3
5 files changed, 486 insertions, 29 deletions
diff --git a/cmd/zpool/os/freebsd/zpool_vdev_os.c b/cmd/zpool/os/freebsd/zpool_vdev_os.c
index 231ca97f1..9dd733989 100644
--- a/cmd/zpool/os/freebsd/zpool_vdev_os.c
+++ b/cmd/zpool/os/freebsd/zpool_vdev_os.c
@@ -124,3 +124,17 @@ check_file(const char *file, boolean_t force, boolean_t isspare)
{
return (check_file_generic(file, force, isspare));
}
+
+int
+zpool_power_current_state(zpool_handle_t *zhp, char *vdev)
+{
+ /* Enclosure slot power not supported on FreeBSD yet */
+ return (-1);
+}
+
+int
+zpool_power(zpool_handle_t *zhp, char *vdev, boolean_t turn_on)
+{
+ /* Enclosure slot power not supported on FreeBSD yet */
+ return (ENOTSUP);
+}
diff --git a/cmd/zpool/os/linux/zpool_vdev_os.c b/cmd/zpool/os/linux/zpool_vdev_os.c
index 7f4486e06..006a3a7d8 100644
--- a/cmd/zpool/os/linux/zpool_vdev_os.c
+++ b/cmd/zpool/os/linux/zpool_vdev_os.c
@@ -416,3 +416,258 @@ check_file(const char *file, boolean_t force, boolean_t isspare)
{
return (check_file_generic(file, force, isspare));
}
+
+/*
+ * Read from a sysfs file and return an allocated string. Removes
+ * the newline from the end of the string if there is one.
+ *
+ * Returns a string on success (which must be freed), or NULL on error.
+ */
+static char *zpool_sysfs_gets(char *path)
+{
+ int fd;
+ struct stat statbuf;
+ char *buf = NULL;
+ ssize_t count = 0;
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return (NULL);
+
+ if (fstat(fd, &statbuf) != 0) {
+ close(fd);
+ return (NULL);
+ }
+
+ buf = calloc(sizeof (*buf), statbuf.st_size + 1);
+ if (buf == NULL) {
+ close(fd);
+ return (NULL);
+ }
+
+ /*
+ * Note, we can read less bytes than st_size, and that's ok. Sysfs
+ * files will report their size is 4k even if they only return a small
+ * string.
+ */
+ count = read(fd, buf, statbuf.st_size);
+ if (count < 0) {
+ /* Error doing read() or we overran the buffer */
+ close(fd);
+ free(buf);
+ return (NULL);
+ }
+
+ /* Remove trailing newline */
+ if (buf[count - 1] == '\n')
+ buf[count - 1] = 0;
+
+ close(fd);
+
+ return (buf);
+}
+
+/*
+ * Write a string to a sysfs file.
+ *
+ * Returns 0 on success, non-zero otherwise.
+ */
+static int zpool_sysfs_puts(char *path, char *str)
+{
+ FILE *file;
+
+ file = fopen(path, "w");
+ if (!file) {
+ return (-1);
+ }
+
+ if (fputs(str, file) < 0) {
+ fclose(file);
+ return (-2);
+ }
+ fclose(file);
+ return (0);
+}
+
+/* Given a vdev nvlist_t, rescan its enclosure sysfs path */
+static void
+rescan_vdev_config_dev_sysfs_path(nvlist_t *vdev_nv)
+{
+ update_vdev_config_dev_sysfs_path(vdev_nv,
+ fnvlist_lookup_string(vdev_nv, ZPOOL_CONFIG_PATH),
+ ZPOOL_CONFIG_VDEV_ENC_SYSFS_PATH);
+}
+
+/*
+ * Given a power string: "on", "off", "1", or "0", return 0 if it's an
+ * off value, 1 if it's an on value, and -1 if the value is unrecognized.
+ */
+static int zpool_power_parse_value(char *str)
+{
+ if ((strcmp(str, "off") == 0) || (strcmp(str, "0") == 0))
+ return (0);
+
+ if ((strcmp(str, "on") == 0) || (strcmp(str, "1") == 0))
+ return (1);
+
+ return (-1);
+}
+
+/*
+ * Given a vdev string return an allocated string containing the sysfs path to
+ * its power control file. Also do a check if the power control file really
+ * exists and has correct permissions.
+ *
+ * Example returned strings:
+ *
+ * /sys/class/enclosure/0:0:122:0/10/power_status
+ * /sys/bus/pci/slots/10/power
+ *
+ * Returns allocated string on success (which must be freed), NULL on failure.
+ */
+static char *
+zpool_power_sysfs_path(zpool_handle_t *zhp, char *vdev)
+{
+ const char *enc_sysfs_dir = NULL;
+ char *path = NULL;
+ nvlist_t *vdev_nv = zpool_find_vdev(zhp, vdev, NULL, NULL, NULL);
+
+ if (vdev_nv == NULL) {
+ return (NULL);
+ }
+
+ /* Make sure we're getting the updated enclosure sysfs path */
+ rescan_vdev_config_dev_sysfs_path(vdev_nv);
+
+ if (nvlist_lookup_string(vdev_nv, ZPOOL_CONFIG_VDEV_ENC_SYSFS_PATH,
+ &enc_sysfs_dir) != 0) {
+ return (NULL);
+ }
+
+ if (asprintf(&path, "%s/power_status", enc_sysfs_dir) == -1)
+ return (NULL);
+
+ if (access(path, W_OK) != 0) {
+ free(path);
+ path = NULL;
+ /* No HDD 'power_control' file, maybe it's NVMe? */
+ if (asprintf(&path, "%s/power", enc_sysfs_dir) == -1) {
+ return (NULL);
+ }
+
+ if (access(path, R_OK | W_OK) != 0) {
+ /* Not NVMe either */
+ free(path);
+ return (NULL);
+ }
+ }
+
+ return (path);
+}
+
+/*
+ * Given a path to a sysfs power control file, return B_TRUE if you should use
+ * "on/off" words to control it, or B_FALSE otherwise ("0/1" to control).
+ */
+static boolean_t
+zpool_power_use_word(char *sysfs_path)
+{
+ if (strcmp(&sysfs_path[strlen(sysfs_path) - strlen("power_status")],
+ "power_status") == 0) {
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
+
+/*
+ * Check the sysfs power control value for a vdev.
+ *
+ * Returns:
+ * 0 - Power is off
+ * 1 - Power is on
+ * -1 - Error or unsupported
+ */
+int
+zpool_power_current_state(zpool_handle_t *zhp, char *vdev)
+{
+ char *val;
+ int rc;
+
+ char *path = zpool_power_sysfs_path(zhp, vdev);
+ if (path == NULL)
+ return (-1);
+
+ val = zpool_sysfs_gets(path);
+ if (val == NULL) {
+ free(path);
+ return (-1);
+ }
+
+ rc = zpool_power_parse_value(val);
+ free(val);
+ free(path);
+ return (rc);
+}
+
+/*
+ * Turn on or off the slot to a device
+ *
+ * Device path is the full path to the device (like /dev/sda or /dev/sda1).
+ *
+ * Return code:
+ * 0: Success
+ * ENOTSUP: Power control not supported for OS
+ * EBADSLT: Couldn't read current power state
+ * ENOENT: No sysfs path to power control
+ * EIO: Couldn't write sysfs power value
+ * EBADE: Sysfs power value didn't change
+ */
+int
+zpool_power(zpool_handle_t *zhp, char *vdev, boolean_t turn_on)
+{
+ char *sysfs_path;
+ const char *val;
+ int rc;
+ int timeout_ms;
+
+ rc = zpool_power_current_state(zhp, vdev);
+ if (rc == -1) {
+ return (EBADSLT);
+ }
+
+ /* Already correct value? */
+ if (rc == (int)turn_on)
+ return (0);
+
+ sysfs_path = zpool_power_sysfs_path(zhp, vdev);
+ if (sysfs_path == NULL)
+ return (ENOENT);
+
+ if (zpool_power_use_word(sysfs_path)) {
+ val = turn_on ? "on" : "off";
+ } else {
+ val = turn_on ? "1" : "0";
+ }
+
+ rc = zpool_sysfs_puts(sysfs_path, (char *)val);
+
+ free(sysfs_path);
+ if (rc != 0) {
+ return (EIO);
+ }
+
+ /*
+ * Wait up to 30 seconds for sysfs power value to change after
+ * writing it.
+ */
+ timeout_ms = zpool_getenv_int("ZPOOL_POWER_ON_SLOT_TIMEOUT_MS", 30000);
+ for (int i = 0; i < MAX(1, timeout_ms / 200); i++) {
+ rc = zpool_power_current_state(zhp, vdev);
+ if (rc == (int)turn_on)
+ return (0); /* success */
+
+ fsleep(0.200); /* 200ms */
+ }
+
+ /* sysfs value never changed */
+ return (EBADE);
+}
diff --git a/cmd/zpool/zpool_iter.c b/cmd/zpool/zpool_iter.c
index 506b529dc..ae2e9da91 100644
--- a/cmd/zpool/zpool_iter.c
+++ b/cmd/zpool/zpool_iter.c
@@ -554,6 +554,10 @@ for_each_vdev_run_cb(void *zhp_data, nvlist_t *nv, void *cb_vcdl)
if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) != 0)
return (1);
+ /* Make sure we're getting the updated enclosure sysfs path */
+ update_vdev_config_dev_sysfs_path(nv, path,
+ ZPOOL_CONFIG_VDEV_ENC_SYSFS_PATH);
+
nvlist_lookup_string(nv, ZPOOL_CONFIG_VDEV_ENC_SYSFS_PATH,
&vdev_enc_sysfs_path);
diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c
index cf66953a8..b347cfba9 100644
--- a/cmd/zpool/zpool_main.c
+++ b/cmd/zpool/zpool_main.c
@@ -353,7 +353,7 @@ get_usage(zpool_help_t idx)
return (gettext("\tattach [-fsw] [-o property=value] "
"<pool> <device> <new-device>\n"));
case HELP_CLEAR:
- return (gettext("\tclear [-nF] <pool> [device]\n"));
+ return (gettext("\tclear [[--power]|[-nF]] <pool> [device]\n"));
case HELP_CREATE:
return (gettext("\tcreate [-fnd] [-o property=value] ... \n"
"\t [-O file-system-property=value] ... \n"
@@ -389,9 +389,11 @@ get_usage(zpool_help_t idx)
"[-T d|u] [pool] ... \n"
"\t [interval [count]]\n"));
case HELP_OFFLINE:
- return (gettext("\toffline [-f] [-t] <pool> <device> ...\n"));
+ return (gettext("\toffline [--power]|[[-f][-t]] <pool> "
+ "<device> ...\n"));
case HELP_ONLINE:
- return (gettext("\tonline [-e] <pool> <device> ...\n"));
+ return (gettext("\tonline [--power][-e] <pool> <device> "
+ "...\n"));
case HELP_REPLACE:
return (gettext("\treplace [-fsw] [-o property=value] "
"<pool> <device> [new-device]\n"));
@@ -410,7 +412,7 @@ get_usage(zpool_help_t idx)
return (gettext("\ttrim [-dw] [-r <rate>] [-c | -s] <pool> "
"[<device> ...]\n"));
case HELP_STATUS:
- return (gettext("\tstatus [-c [script1,script2,...]] "
+ return (gettext("\tstatus [--power] [-c [script1,script2,...]] "
"[-igLpPstvxD] [-T d|u] [pool] ... \n"
"\t [interval [count]]\n"));
case HELP_UPGRADE:
@@ -517,6 +519,77 @@ print_vdev_prop_cb(int prop, void *cb)
}
/*
+ * Given a leaf vdev name like 'L5' return its VDEV_CONFIG_PATH like
+ * '/dev/disk/by-vdev/L5'.
+ */
+static const char *
+vdev_name_to_path(zpool_handle_t *zhp, char *vdev)
+{
+ nvlist_t *vdev_nv = zpool_find_vdev(zhp, vdev, NULL, NULL, NULL);
+ if (vdev_nv == NULL) {
+ return (NULL);
+ }
+ return (fnvlist_lookup_string(vdev_nv, ZPOOL_CONFIG_PATH));
+}
+
+static int
+zpool_power_on(zpool_handle_t *zhp, char *vdev)
+{
+ return (zpool_power(zhp, vdev, B_TRUE));
+}
+
+static int
+zpool_power_on_and_disk_wait(zpool_handle_t *zhp, char *vdev)
+{
+ int rc;
+
+ rc = zpool_power_on(zhp, vdev);
+ if (rc != 0)
+ return (rc);
+
+ zpool_disk_wait(vdev_name_to_path(zhp, vdev));
+
+ return (0);
+}
+
+static int
+zpool_power_on_pool_and_wait_for_devices(zpool_handle_t *zhp)
+{
+ nvlist_t *nv;
+ const char *path = NULL;
+ int rc;
+
+ /* Power up all the devices first */
+ FOR_EACH_REAL_LEAF_VDEV(zhp, nv) {
+ path = fnvlist_lookup_string(nv, ZPOOL_CONFIG_PATH);
+ if (path != NULL) {
+ rc = zpool_power_on(zhp, (char *)path);
+ if (rc != 0) {
+ return (rc);
+ }
+ }
+ }
+
+ /*
+ * Wait for their devices to show up. Since we powered them on
+ * at roughly the same time, they should all come online around
+ * the same time.
+ */
+ FOR_EACH_REAL_LEAF_VDEV(zhp, nv) {
+ path = fnvlist_lookup_string(nv, ZPOOL_CONFIG_PATH);
+ zpool_disk_wait(path);
+ }
+
+ return (0);
+}
+
+static int
+zpool_power_off(zpool_handle_t *zhp, char *vdev)
+{
+ return (zpool_power(zhp, vdev, B_FALSE));
+}
+
+/*
* Display usage message. If we're inside a command, display only the usage for
* that command. Otherwise, iterate over the entire command table and display
* a complete usage message.
@@ -2093,6 +2166,7 @@ typedef struct status_cbdata {
boolean_t cb_print_vdev_init;
boolean_t cb_print_vdev_trim;
vdev_cmd_data_list_t *vcdl;
+ boolean_t cb_print_power;
} status_cbdata_t;
/* Return 1 if string is NULL, empty, or whitespace; return 0 otherwise. */
@@ -2378,6 +2452,26 @@ print_status_config(zpool_handle_t *zhp, status_cbdata_t *cb, const char *name,
else
printf(" %5s", rbuf);
}
+ if (cb->cb_print_power) {
+ if (children == 0) {
+ /* Only leaf vdevs have physical slots */
+ switch (zpool_power_current_state(zhp, (char *)
+ fnvlist_lookup_string(nv,
+ ZPOOL_CONFIG_PATH))) {
+ case 0:
+ printf_color(ANSI_RED, " %5s",
+ gettext("off"));
+ break;
+ case 1:
+ printf(" %5s", gettext("on"));
+ break;
+ default:
+ printf(" %5s", "-");
+ }
+ } else {
+ printf(" %5s", "-");
+ }
+ }
}
if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT,
@@ -5430,19 +5524,6 @@ get_interval_count_filter_guids(int *argc, char **argv, float *interval,
}
/*
- * Floating point sleep(). Allows you to pass in a floating point value for
- * seconds.
- */
-static void
-fsleep(float sec)
-{
- struct timespec req;
- req.tv_sec = floor(sec);
- req.tv_nsec = (sec - (float)req.tv_sec) * NANOSEC;
- nanosleep(&req, NULL);
-}
-
-/*
* Terminal height, in rows. Returns -1 if stdout is not connected to a TTY or
* if we were unable to determine its size.
*/
@@ -6950,10 +7031,12 @@ zpool_do_split(int argc, char **argv)
return (ret);
}
-
+#define POWER_OPT 1024
/*
- * zpool online <pool> <device> ...
+ * zpool online [--power] <pool> <device> ...
+ *
+ * --power: Power on the enclosure slot to the drive (if possible)
*/
int
zpool_do_online(int argc, char **argv)
@@ -6964,13 +7047,21 @@ zpool_do_online(int argc, char **argv)
int ret = 0;
vdev_state_t newstate;
int flags = 0;
+ boolean_t is_power_on = B_FALSE;
+ struct option long_options[] = {
+ {"power", no_argument, NULL, POWER_OPT},
+ {0, 0, 0, 0}
+ };
/* check options */
- while ((c = getopt(argc, argv, "e")) != -1) {
+ while ((c = getopt_long(argc, argv, "e", long_options, NULL)) != -1) {
switch (c) {
case 'e':
flags |= ZFS_ONLINE_EXPAND;
break;
+ case POWER_OPT:
+ is_power_on = B_TRUE;
+ break;
case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
optopt);
@@ -6978,6 +7069,9 @@ zpool_do_online(int argc, char **argv)
}
}
+ if (libzfs_envvar_is_set("ZPOOL_AUTO_POWER_ON_SLOT"))
+ is_power_on = B_TRUE;
+
argc -= optind;
argv += optind;
@@ -6999,6 +7093,18 @@ zpool_do_online(int argc, char **argv)
for (i = 1; i < argc; i++) {
vdev_state_t oldstate;
boolean_t avail_spare, l2cache;
+ int rc;
+
+ if (is_power_on) {
+ rc = zpool_power_on_and_disk_wait(zhp, argv[i]);
+ if (rc == ENOTSUP) {
+ (void) fprintf(stderr,
+ gettext("Power control not supported\n"));
+ }
+ if (rc != 0)
+ return (rc);
+ }
+
nvlist_t *tgt = zpool_find_vdev(zhp, argv[i], &avail_spare,
&l2cache, NULL);
if (tgt == NULL) {
@@ -7044,12 +7150,15 @@ zpool_do_online(int argc, char **argv)
}
/*
- * zpool offline [-ft] <pool> <device> ...
+ * zpool offline [-ft]|[--power] <pool> <device> ...
+ *
*
* -f Force the device into a faulted state.
*
* -t Only take the device off-line temporarily. The offline/faulted
* state will not be persistent across reboots.
+ *
+ * --power Power off the enclosure slot to the drive (if possible)
*/
int
zpool_do_offline(int argc, char **argv)
@@ -7060,9 +7169,15 @@ zpool_do_offline(int argc, char **argv)
int ret = 0;
boolean_t istmp = B_FALSE;
boolean_t fault = B_FALSE;
+ boolean_t is_power_off = B_FALSE;
+
+ struct option long_options[] = {
+ {"power", no_argument, NULL, POWER_OPT},
+ {0, 0, 0, 0}
+ };
/* check options */
- while ((c = getopt(argc, argv, "ft")) != -1) {
+ while ((c = getopt_long(argc, argv, "ft", long_options, NULL)) != -1) {
switch (c) {
case 'f':
fault = B_TRUE;
@@ -7070,6 +7185,9 @@ zpool_do_offline(int argc, char **argv)
case 't':
istmp = B_TRUE;
break;
+ case POWER_OPT:
+ is_power_off = B_TRUE;
+ break;
case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
optopt);
@@ -7077,6 +7195,20 @@ zpool_do_offline(int argc, char **argv)
}
}
+ if (is_power_off && fault) {
+ (void) fprintf(stderr,
+ gettext("-0 and -f cannot be used together\n"));
+ usage(B_FALSE);
+ return (1);
+ }
+
+ if (is_power_off && istmp) {
+ (void) fprintf(stderr,
+ gettext("-0 and -t cannot be used together\n"));
+ usage(B_FALSE);
+ return (1);
+ }
+
argc -= optind;
argv += optind;
@@ -7096,8 +7228,22 @@ zpool_do_offline(int argc, char **argv)
return (1);
for (i = 1; i < argc; i++) {
- if (fault) {
- uint64_t guid = zpool_vdev_path_to_guid(zhp, argv[i]);
+ uint64_t guid = zpool_vdev_path_to_guid(zhp, argv[i]);
+ if (is_power_off) {
+ /*
+ * Note: we have to power off first, then set REMOVED,
+ * or else zpool_vdev_set_removed_state() returns
+ * EAGAIN.
+ */
+ ret = zpool_power_off(zhp, argv[i]);
+ if (ret != 0) {
+ (void) fprintf(stderr, "%s %s %d\n",
+ gettext("unable to power off slot for"),
+ argv[i], ret);
+ }
+ zpool_vdev_set_removed_state(zhp, guid, VDEV_AUX_NONE);
+
+ } else if (fault) {
vdev_aux_t aux;
if (istmp == B_FALSE) {
/* Force the fault to persist across imports */
@@ -7120,7 +7266,7 @@ zpool_do_offline(int argc, char **argv)
}
/*
- * zpool clear <pool> [device]
+ * zpool clear [-nF]|[--power] <pool> [device]
*
* Clear all errors associated with a pool or a particular device.
*/
@@ -7132,13 +7278,20 @@ zpool_do_clear(int argc, char **argv)
boolean_t dryrun = B_FALSE;
boolean_t do_rewind = B_FALSE;
boolean_t xtreme_rewind = B_FALSE;
+ boolean_t is_power_on = B_FALSE;
uint32_t rewind_policy = ZPOOL_NO_REWIND;
nvlist_t *policy = NULL;
zpool_handle_t *zhp;
char *pool, *device;
+ struct option long_options[] = {
+ {"power", no_argument, NULL, POWER_OPT},
+ {0, 0, 0, 0}
+ };
+
/* check options */
- while ((c = getopt(argc, argv, "FnX")) != -1) {
+ while ((c = getopt_long(argc, argv, "FnX", long_options,
+ NULL)) != -1) {
switch (c) {
case 'F':
do_rewind = B_TRUE;
@@ -7149,6 +7302,9 @@ zpool_do_clear(int argc, char **argv)
case 'X':
xtreme_rewind = B_TRUE;
break;
+ case POWER_OPT:
+ is_power_on = B_TRUE;
+ break;
case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
optopt);
@@ -7156,6 +7312,9 @@ zpool_do_clear(int argc, char **argv)
}
}
+ if (libzfs_envvar_is_set("ZPOOL_AUTO_POWER_ON_SLOT"))
+ is_power_on = B_TRUE;
+
argc -= optind;
argv += optind;
@@ -7196,6 +7355,14 @@ zpool_do_clear(int argc, char **argv)
return (1);
}
+ if (is_power_on) {
+ if (device == NULL) {
+ zpool_power_on_pool_and_wait_for_devices(zhp);
+ } else {
+ zpool_power_on_and_disk_wait(zhp, device);
+ }
+ }
+
if (zpool_clear(zhp, device, policy) != 0)
ret = 1;
@@ -8908,6 +9075,10 @@ status_callback(zpool_handle_t *zhp, void *data)
printf_color(ANSI_BOLD, " %5s", gettext("SLOW"));
}
+ if (cbp->cb_print_power) {
+ printf_color(ANSI_BOLD, " %5s", gettext("POWER"));
+ }
+
if (cbp->vcdl != NULL)
print_cmd_columns(cbp->vcdl, 0);
@@ -8954,8 +9125,8 @@ status_callback(zpool_handle_t *zhp, void *data)
}
/*
- * zpool status [-c [script1,script2,...]] [-igLpPstvx] [-T d|u] [pool] ...
- * [interval [count]]
+ * zpool status [-c [script1,script2,...]] [-igLpPstvx] [--power] [-T d|u] ...
+ * [pool] [interval [count]]
*
* -c CMD For each vdev, run command CMD
* -i Display vdev initialization status.
@@ -8969,6 +9140,7 @@ status_callback(zpool_handle_t *zhp, void *data)
* -D Display dedup status (undocumented)
* -t Display vdev TRIM status.
* -T Display a timestamp in date(1) or Unix format
+ * --power Display vdev enclosure slot power status
*
* Describes the health status of all pools or some subset.
*/
@@ -8982,8 +9154,14 @@ zpool_do_status(int argc, char **argv)
status_cbdata_t cb = { 0 };
char *cmd = NULL;
+ struct option long_options[] = {
+ {"power", no_argument, NULL, POWER_OPT},
+ {0, 0, 0, 0}
+ };
+
/* check options */
- while ((c = getopt(argc, argv, "c:igLpPsvxDtT:")) != -1) {
+ while ((c = getopt_long(argc, argv, "c:igLpPsvxDtT:", long_options,
+ NULL)) != -1) {
switch (c) {
case 'c':
if (cmd != NULL) {
@@ -9042,6 +9220,9 @@ zpool_do_status(int argc, char **argv)
case 'T':
get_timestamp_arg(*optarg);
break;
+ case POWER_OPT:
+ cb.cb_print_power = B_TRUE;
+ break;
case '?':
if (optopt == 'c') {
print_zpool_script_list("status");
diff --git a/cmd/zpool/zpool_util.h b/cmd/zpool/zpool_util.h
index db8e631dc..7f5406f06 100644
--- a/cmd/zpool/zpool_util.h
+++ b/cmd/zpool/zpool_util.h
@@ -138,6 +138,9 @@ int check_file(const char *file, boolean_t force, boolean_t isspare);
void after_zpool_upgrade(zpool_handle_t *zhp);
int check_file_generic(const char *file, boolean_t force, boolean_t isspare);
+int zpool_power(zpool_handle_t *zhp, char *vdev, boolean_t turn_on);
+int zpool_power_current_state(zpool_handle_t *zhp, char *vdev);
+
#ifdef __cplusplus
}
#endif