mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2024-12-26 03:09:34 +03:00
Add zfs_prepare_disk script for disk firmware install
Have libzfs call a special `zfs_prepare_disk` script before a disk is included into the pool. The user can edit this script to add things like a disk firmware update or a disk health check. Use of the script is totally optional. See the zfs_prepare_disk manpage for full details. Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: Tony Hutter <hutter2@llnl.gov> Closes #15243
This commit is contained in:
parent
4647353c8b
commit
b53077a9e7
@ -146,6 +146,17 @@ zfs_unavail_pool(zpool_handle_t *zhp, void *data)
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write an array of strings to the zed log
|
||||||
|
*/
|
||||||
|
static void lines_to_zed_log_msg(char **lines, int lines_cnt)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < lines_cnt; i++) {
|
||||||
|
zed_log_msg(LOG_INFO, "%s", lines[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Two stage replace on Linux
|
* Two stage replace on Linux
|
||||||
* since we get disk notifications
|
* since we get disk notifications
|
||||||
@ -200,6 +211,8 @@ zfs_process_add(zpool_handle_t *zhp, nvlist_t *vdev, boolean_t labeled)
|
|||||||
boolean_t is_mpath_wholedisk = B_FALSE;
|
boolean_t is_mpath_wholedisk = B_FALSE;
|
||||||
uint_t c;
|
uint_t c;
|
||||||
vdev_stat_t *vs;
|
vdev_stat_t *vs;
|
||||||
|
char **lines = NULL;
|
||||||
|
int lines_cnt = 0;
|
||||||
|
|
||||||
if (nvlist_lookup_string(vdev, ZPOOL_CONFIG_PATH, &path) != 0)
|
if (nvlist_lookup_string(vdev, ZPOOL_CONFIG_PATH, &path) != 0)
|
||||||
return;
|
return;
|
||||||
@ -383,6 +396,22 @@ zfs_process_add(zpool_handle_t *zhp, nvlist_t *vdev, boolean_t labeled)
|
|||||||
|
|
||||||
if (is_mpath_wholedisk) {
|
if (is_mpath_wholedisk) {
|
||||||
/* Don't label device mapper or multipath disks. */
|
/* Don't label device mapper or multipath disks. */
|
||||||
|
zed_log_msg(LOG_INFO,
|
||||||
|
" it's a multipath wholedisk, don't label");
|
||||||
|
if (zpool_prepare_disk(zhp, vdev, "autoreplace", &lines,
|
||||||
|
&lines_cnt) != 0) {
|
||||||
|
zed_log_msg(LOG_INFO,
|
||||||
|
" zpool_prepare_disk: could not "
|
||||||
|
"prepare '%s' (%s)", fullpath,
|
||||||
|
libzfs_error_description(g_zfshdl));
|
||||||
|
if (lines_cnt > 0) {
|
||||||
|
zed_log_msg(LOG_INFO,
|
||||||
|
" zfs_prepare_disk output:");
|
||||||
|
lines_to_zed_log_msg(lines, lines_cnt);
|
||||||
|
}
|
||||||
|
libzfs_free_str_array(lines, lines_cnt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
} else if (!labeled) {
|
} else if (!labeled) {
|
||||||
/*
|
/*
|
||||||
* we're auto-replacing a raw disk, so label it first
|
* we're auto-replacing a raw disk, so label it first
|
||||||
@ -405,10 +434,18 @@ zfs_process_add(zpool_handle_t *zhp, nvlist_t *vdev, boolean_t labeled)
|
|||||||
* If this is a request to label a whole disk, then attempt to
|
* If this is a request to label a whole disk, then attempt to
|
||||||
* write out the label.
|
* write out the label.
|
||||||
*/
|
*/
|
||||||
if (zpool_label_disk(g_zfshdl, zhp, leafname) != 0) {
|
if (zpool_prepare_and_label_disk(g_zfshdl, zhp, leafname,
|
||||||
zed_log_msg(LOG_INFO, " zpool_label_disk: could not "
|
vdev, "autoreplace", &lines, &lines_cnt) != 0) {
|
||||||
|
zed_log_msg(LOG_INFO,
|
||||||
|
" zpool_prepare_and_label_disk: could not "
|
||||||
"label '%s' (%s)", leafname,
|
"label '%s' (%s)", leafname,
|
||||||
libzfs_error_description(g_zfshdl));
|
libzfs_error_description(g_zfshdl));
|
||||||
|
if (lines_cnt > 0) {
|
||||||
|
zed_log_msg(LOG_INFO,
|
||||||
|
" zfs_prepare_disk output:");
|
||||||
|
lines_to_zed_log_msg(lines, lines_cnt);
|
||||||
|
}
|
||||||
|
libzfs_free_str_array(lines, lines_cnt);
|
||||||
|
|
||||||
(void) zpool_vdev_online(zhp, fullpath,
|
(void) zpool_vdev_online(zhp, fullpath,
|
||||||
ZFS_ONLINE_FORCEFAULT, &newstate);
|
ZFS_ONLINE_FORCEFAULT, &newstate);
|
||||||
@ -468,6 +505,8 @@ zfs_process_add(zpool_handle_t *zhp, nvlist_t *vdev, boolean_t labeled)
|
|||||||
DEV_BYID_PATH, new_devid);
|
DEV_BYID_PATH, new_devid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
libzfs_free_str_array(lines, lines_cnt);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Construct the root vdev to pass to zpool_vdev_attach(). While adding
|
* Construct the root vdev to pass to zpool_vdev_attach(). While adding
|
||||||
* the entire vdev structure is harmless, we construct a reduced set of
|
* the entire vdev structure is harmless, we construct a reduced set of
|
||||||
|
@ -443,37 +443,22 @@ vdev_run_cmd(vdev_cmd_data_t *data, char *cmd)
|
|||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
char *argv[2] = {cmd};
|
char *argv[2] = {cmd};
|
||||||
char *env[5] = {(char *)"PATH=/bin:/sbin:/usr/bin:/usr/sbin"};
|
char **env;
|
||||||
char **lines = NULL;
|
char **lines = NULL;
|
||||||
int lines_cnt = 0;
|
int lines_cnt = 0;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* Setup our custom environment variables */
|
env = zpool_vdev_script_alloc_env(data->pool, data->path, data->upath,
|
||||||
rc = asprintf(&env[1], "VDEV_PATH=%s",
|
data->vdev_enc_sysfs_path, NULL, NULL);
|
||||||
data->path ? data->path : "");
|
if (env == NULL)
|
||||||
if (rc == -1) {
|
|
||||||
env[1] = NULL;
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
|
|
||||||
rc = asprintf(&env[2], "VDEV_UPATH=%s",
|
|
||||||
data->upath ? data->upath : "");
|
|
||||||
if (rc == -1) {
|
|
||||||
env[2] = NULL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = asprintf(&env[3], "VDEV_ENC_SYSFS_PATH=%s",
|
|
||||||
data->vdev_enc_sysfs_path ?
|
|
||||||
data->vdev_enc_sysfs_path : "");
|
|
||||||
if (rc == -1) {
|
|
||||||
env[3] = NULL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Run the command */
|
/* Run the command */
|
||||||
rc = libzfs_run_process_get_stdout_nopath(cmd, argv, env, &lines,
|
rc = libzfs_run_process_get_stdout_nopath(cmd, argv, env, &lines,
|
||||||
&lines_cnt);
|
&lines_cnt);
|
||||||
|
|
||||||
|
zpool_vdev_script_free_env(env);
|
||||||
|
|
||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
@ -485,10 +470,6 @@ vdev_run_cmd(vdev_cmd_data_t *data, char *cmd)
|
|||||||
out:
|
out:
|
||||||
if (lines != NULL)
|
if (lines != NULL)
|
||||||
libzfs_free_str_array(lines, lines_cnt);
|
libzfs_free_str_array(lines, lines_cnt);
|
||||||
|
|
||||||
/* Start with i = 1 since env[0] was statically allocated */
|
|
||||||
for (i = 1; i < ARRAY_SIZE(env); i++)
|
|
||||||
free(env[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -126,6 +126,10 @@ vdev_cmd_data_list_t *all_pools_for_each_vdev_run(int argc, char **argv,
|
|||||||
|
|
||||||
void free_vdev_cmd_data_list(vdev_cmd_data_list_t *vcdl);
|
void free_vdev_cmd_data_list(vdev_cmd_data_list_t *vcdl);
|
||||||
|
|
||||||
|
void free_vdev_cmd_data(vdev_cmd_data_t *data);
|
||||||
|
|
||||||
|
int vdev_run_cmd_simple(char *path, char *cmd);
|
||||||
|
|
||||||
int check_device(const char *path, boolean_t force,
|
int check_device(const char *path, boolean_t force,
|
||||||
boolean_t isspare, boolean_t iswholedisk);
|
boolean_t isspare, boolean_t iswholedisk);
|
||||||
boolean_t check_sector_size_database(char *path, int *sector_size);
|
boolean_t check_sector_size_database(char *path, int *sector_size);
|
||||||
|
@ -936,6 +936,15 @@ zero_label(const char *path)
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
lines_to_stderr(char *lines[], int lines_cnt)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < lines_cnt; i++) {
|
||||||
|
fprintf(stderr, "%s\n", lines[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Go through and find any whole disks in the vdev specification, labelling them
|
* Go through and find any whole disks in the vdev specification, labelling them
|
||||||
* as appropriate. When constructing the vdev spec, we were unable to open this
|
* as appropriate. When constructing the vdev spec, we were unable to open this
|
||||||
@ -947,7 +956,7 @@ zero_label(const char *path)
|
|||||||
* need to get the devid after we label the disk.
|
* need to get the devid after we label the disk.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
make_disks(zpool_handle_t *zhp, nvlist_t *nv)
|
make_disks(zpool_handle_t *zhp, nvlist_t *nv, boolean_t replacing)
|
||||||
{
|
{
|
||||||
nvlist_t **child;
|
nvlist_t **child;
|
||||||
uint_t c, children;
|
uint_t c, children;
|
||||||
@ -1032,6 +1041,8 @@ make_disks(zpool_handle_t *zhp, nvlist_t *nv)
|
|||||||
*/
|
*/
|
||||||
if (!is_exclusive && !is_spare(NULL, udevpath)) {
|
if (!is_exclusive && !is_spare(NULL, udevpath)) {
|
||||||
char *devnode = strrchr(devpath, '/') + 1;
|
char *devnode = strrchr(devpath, '/') + 1;
|
||||||
|
char **lines = NULL;
|
||||||
|
int lines_cnt = 0;
|
||||||
|
|
||||||
ret = strncmp(udevpath, UDISK_ROOT, strlen(UDISK_ROOT));
|
ret = strncmp(udevpath, UDISK_ROOT, strlen(UDISK_ROOT));
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
@ -1043,9 +1054,27 @@ make_disks(zpool_handle_t *zhp, nvlist_t *nv)
|
|||||||
/*
|
/*
|
||||||
* When labeling a pool the raw device node name
|
* When labeling a pool the raw device node name
|
||||||
* is provided as it appears under /dev/.
|
* is provided as it appears under /dev/.
|
||||||
|
*
|
||||||
|
* Note that 'zhp' will be NULL when we're creating a
|
||||||
|
* pool.
|
||||||
*/
|
*/
|
||||||
if (zpool_label_disk(g_zfs, zhp, devnode) == -1)
|
if (zpool_prepare_and_label_disk(g_zfs, zhp, devnode,
|
||||||
|
nv, zhp == NULL ? "create" :
|
||||||
|
replacing ? "replace" : "add", &lines,
|
||||||
|
&lines_cnt) != 0) {
|
||||||
|
(void) fprintf(stderr,
|
||||||
|
gettext(
|
||||||
|
"Error preparing/labeling disk.\n"));
|
||||||
|
if (lines_cnt > 0) {
|
||||||
|
(void) fprintf(stderr,
|
||||||
|
gettext("zfs_prepare_disk output:\n"));
|
||||||
|
lines_to_stderr(lines, lines_cnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
libzfs_free_str_array(lines, lines_cnt);
|
||||||
return (-1);
|
return (-1);
|
||||||
|
}
|
||||||
|
libzfs_free_str_array(lines, lines_cnt);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Wait for udev to signal the device is available
|
* Wait for udev to signal the device is available
|
||||||
@ -1082,19 +1111,19 @@ make_disks(zpool_handle_t *zhp, nvlist_t *nv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (c = 0; c < children; c++)
|
for (c = 0; c < children; c++)
|
||||||
if ((ret = make_disks(zhp, child[c])) != 0)
|
if ((ret = make_disks(zhp, child[c], replacing)) != 0)
|
||||||
return (ret);
|
return (ret);
|
||||||
|
|
||||||
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES,
|
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES,
|
||||||
&child, &children) == 0)
|
&child, &children) == 0)
|
||||||
for (c = 0; c < children; c++)
|
for (c = 0; c < children; c++)
|
||||||
if ((ret = make_disks(zhp, child[c])) != 0)
|
if ((ret = make_disks(zhp, child[c], replacing)) != 0)
|
||||||
return (ret);
|
return (ret);
|
||||||
|
|
||||||
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE,
|
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE,
|
||||||
&child, &children) == 0)
|
&child, &children) == 0)
|
||||||
for (c = 0; c < children; c++)
|
for (c = 0; c < children; c++)
|
||||||
if ((ret = make_disks(zhp, child[c])) != 0)
|
if ((ret = make_disks(zhp, child[c], replacing)) != 0)
|
||||||
return (ret);
|
return (ret);
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
@ -1752,7 +1781,7 @@ split_mirror_vdev(zpool_handle_t *zhp, char *newname, nvlist_t *props,
|
|||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!flags.dryrun && make_disks(zhp, newroot) != 0) {
|
if (!flags.dryrun && make_disks(zhp, newroot, B_FALSE) != 0) {
|
||||||
nvlist_free(newroot);
|
nvlist_free(newroot);
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
@ -1873,7 +1902,7 @@ make_root_vdev(zpool_handle_t *zhp, nvlist_t *props, int force, int check_rep,
|
|||||||
/*
|
/*
|
||||||
* Run through the vdev specification and label any whole disks found.
|
* Run through the vdev specification and label any whole disks found.
|
||||||
*/
|
*/
|
||||||
if (!dryrun && make_disks(zhp, newroot) != 0) {
|
if (!dryrun && make_disks(zhp, newroot, replacing) != 0) {
|
||||||
nvlist_free(newroot);
|
nvlist_free(newroot);
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ AM_CPPFLAGS += -D_REENTRANT
|
|||||||
AM_CPPFLAGS += -D_FILE_OFFSET_BITS=64
|
AM_CPPFLAGS += -D_FILE_OFFSET_BITS=64
|
||||||
AM_CPPFLAGS += -D_LARGEFILE64_SOURCE
|
AM_CPPFLAGS += -D_LARGEFILE64_SOURCE
|
||||||
AM_CPPFLAGS += -DLIBEXECDIR=\"$(libexecdir)\"
|
AM_CPPFLAGS += -DLIBEXECDIR=\"$(libexecdir)\"
|
||||||
|
AM_CPPFLAGS += -DZFSEXECDIR=\"$(zfsexecdir)\"
|
||||||
AM_CPPFLAGS += -DRUNSTATEDIR=\"$(runstatedir)\"
|
AM_CPPFLAGS += -DRUNSTATEDIR=\"$(runstatedir)\"
|
||||||
AM_CPPFLAGS += -DSBINDIR=\"$(sbindir)\"
|
AM_CPPFLAGS += -DSBINDIR=\"$(sbindir)\"
|
||||||
AM_CPPFLAGS += -DSYSCONFDIR=\"$(sysconfdir)\"
|
AM_CPPFLAGS += -DSYSCONFDIR=\"$(sysconfdir)\"
|
||||||
|
@ -35,6 +35,7 @@ usr/bin/zvol_wait
|
|||||||
usr/lib/modules-load.d/ lib/
|
usr/lib/modules-load.d/ lib/
|
||||||
usr/lib/zfs-linux/zpool.d/
|
usr/lib/zfs-linux/zpool.d/
|
||||||
usr/lib/zfs-linux/zpool_influxdb
|
usr/lib/zfs-linux/zpool_influxdb
|
||||||
|
usr/lib/zfs-linux/zfs_prepare_disk
|
||||||
usr/sbin/arc_summary
|
usr/sbin/arc_summary
|
||||||
usr/sbin/arcstat
|
usr/sbin/arcstat
|
||||||
usr/sbin/dbufstat
|
usr/sbin/dbufstat
|
||||||
@ -88,6 +89,7 @@ usr/share/man/man8/zfs-wait.8
|
|||||||
usr/share/man/man8/zfs-zone.8
|
usr/share/man/man8/zfs-zone.8
|
||||||
usr/share/man/man8/zfs.8
|
usr/share/man/man8/zfs.8
|
||||||
usr/share/man/man8/zfs_ids_to_path.8
|
usr/share/man/man8/zfs_ids_to_path.8
|
||||||
|
usr/share/man/man8/zfs_prepare_disk.8
|
||||||
usr/share/man/man7/zfsconcepts.7
|
usr/share/man/man7/zfsconcepts.7
|
||||||
usr/share/man/man7/zfsprops.7
|
usr/share/man/man7/zfsprops.7
|
||||||
usr/share/man/man8/zgenhostid.8
|
usr/share/man/man8/zgenhostid.8
|
||||||
|
@ -326,6 +326,15 @@ _LIBZFS_H nvlist_t *zpool_find_vdev_by_physpath(zpool_handle_t *, const char *,
|
|||||||
boolean_t *, boolean_t *, boolean_t *);
|
boolean_t *, boolean_t *, boolean_t *);
|
||||||
_LIBZFS_H int zpool_label_disk(libzfs_handle_t *, zpool_handle_t *,
|
_LIBZFS_H int zpool_label_disk(libzfs_handle_t *, zpool_handle_t *,
|
||||||
const char *);
|
const char *);
|
||||||
|
_LIBZFS_H int zpool_prepare_disk(zpool_handle_t *zhp, nvlist_t *vdev_nv,
|
||||||
|
const char *prepare_str, char **lines[], int *lines_cnt);
|
||||||
|
_LIBZFS_H int zpool_prepare_and_label_disk(libzfs_handle_t *hdl,
|
||||||
|
zpool_handle_t *, const char *, nvlist_t *vdev_nv, const char *prepare_str,
|
||||||
|
char **lines[], int *lines_cnt);
|
||||||
|
_LIBZFS_H char ** zpool_vdev_script_alloc_env(const char *pool_name,
|
||||||
|
const char *vdev_path, const char *vdev_upath,
|
||||||
|
const char *vdev_enc_sysfs_path, const char *opt_key, const char *opt_val);
|
||||||
|
_LIBZFS_H void zpool_vdev_script_free_env(char **env);
|
||||||
_LIBZFS_H uint64_t zpool_vdev_path_to_guid(zpool_handle_t *zhp,
|
_LIBZFS_H uint64_t zpool_vdev_path_to_guid(zpool_handle_t *zhp,
|
||||||
const char *path);
|
const char *path);
|
||||||
|
|
||||||
|
@ -514,6 +514,8 @@
|
|||||||
<elf-symbol name='zpool_open' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
<elf-symbol name='zpool_open' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||||
<elf-symbol name='zpool_open_canfail' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
<elf-symbol name='zpool_open_canfail' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||||
<elf-symbol name='zpool_pool_state_to_name' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
<elf-symbol name='zpool_pool_state_to_name' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||||
|
<elf-symbol name='zpool_prepare_and_label_disk' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||||
|
<elf-symbol name='zpool_prepare_disk' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||||
<elf-symbol name='zpool_print_unsup_feat' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
<elf-symbol name='zpool_print_unsup_feat' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||||
<elf-symbol name='zpool_prop_align_right' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
<elf-symbol name='zpool_prop_align_right' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||||
<elf-symbol name='zpool_prop_column_name' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
<elf-symbol name='zpool_prop_column_name' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||||
@ -561,6 +563,8 @@
|
|||||||
<elf-symbol name='zpool_vdev_remove' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
<elf-symbol name='zpool_vdev_remove' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||||
<elf-symbol name='zpool_vdev_remove_cancel' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
<elf-symbol name='zpool_vdev_remove_cancel' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||||
<elf-symbol name='zpool_vdev_remove_wanted' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
<elf-symbol name='zpool_vdev_remove_wanted' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||||
|
<elf-symbol name='zpool_vdev_script_alloc_env' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||||
|
<elf-symbol name='zpool_vdev_script_free_env' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||||
<elf-symbol name='zpool_vdev_split' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
<elf-symbol name='zpool_vdev_split' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||||
<elf-symbol name='zpool_wait' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
<elf-symbol name='zpool_wait' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||||
<elf-symbol name='zpool_wait_status' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
<elf-symbol name='zpool_wait_status' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||||
|
@ -2071,3 +2071,196 @@ printf_color(const char *color, const char *format, ...)
|
|||||||
|
|
||||||
return (rc);
|
return (rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* PATH + 5 env vars + a NULL entry = 7 */
|
||||||
|
#define ZPOOL_VDEV_SCRIPT_ENV_COUNT 7
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There's a few places where ZFS will call external scripts (like the script
|
||||||
|
* in zpool.d/ and `zfs_prepare_disk`). These scripts are called with a
|
||||||
|
* reduced $PATH, and some vdev specific environment vars set. This function
|
||||||
|
* will allocate an populate the environment variable array that is passed to
|
||||||
|
* these scripts. The user must free the arrays with zpool_vdev_free_env() when
|
||||||
|
* they are done.
|
||||||
|
*
|
||||||
|
* The following env vars will be set (but value could be blank):
|
||||||
|
*
|
||||||
|
* POOL_NAME
|
||||||
|
* VDEV_PATH
|
||||||
|
* VDEV_UPATH
|
||||||
|
* VDEV_ENC_SYSFS_PATH
|
||||||
|
*
|
||||||
|
* In addition, you can set an optional environment variable named 'opt_key'
|
||||||
|
* to 'opt_val' if you want.
|
||||||
|
*
|
||||||
|
* Returns allocated env[] array on success, NULL otherwise.
|
||||||
|
*/
|
||||||
|
char **
|
||||||
|
zpool_vdev_script_alloc_env(const char *pool_name,
|
||||||
|
const char *vdev_path, const char *vdev_upath,
|
||||||
|
const char *vdev_enc_sysfs_path, const char *opt_key, const char *opt_val)
|
||||||
|
{
|
||||||
|
char **env = NULL;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
env = calloc(ZPOOL_VDEV_SCRIPT_ENV_COUNT, sizeof (*env));
|
||||||
|
if (!env)
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
|
env[0] = strdup("PATH=/bin:/sbin:/usr/bin:/usr/sbin");
|
||||||
|
if (!env[0])
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
/* Setup our custom environment variables */
|
||||||
|
rc = asprintf(&env[1], "POOL_NAME=%s", pool_name ? pool_name : "");
|
||||||
|
if (rc == -1) {
|
||||||
|
env[1] = NULL;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = asprintf(&env[2], "VDEV_PATH=%s", vdev_path ? vdev_path : "");
|
||||||
|
if (rc == -1) {
|
||||||
|
env[2] = NULL;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = asprintf(&env[3], "VDEV_UPATH=%s", vdev_upath ? vdev_upath : "");
|
||||||
|
if (rc == -1) {
|
||||||
|
env[3] = NULL;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = asprintf(&env[4], "VDEV_ENC_SYSFS_PATH=%s",
|
||||||
|
vdev_enc_sysfs_path ? vdev_enc_sysfs_path : "");
|
||||||
|
if (rc == -1) {
|
||||||
|
env[4] = NULL;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opt_key != NULL) {
|
||||||
|
rc = asprintf(&env[5], "%s=%s", opt_key,
|
||||||
|
opt_val ? opt_val : "");
|
||||||
|
if (rc == -1) {
|
||||||
|
env[5] = NULL;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (env);
|
||||||
|
|
||||||
|
error:
|
||||||
|
for (int i = 0; i < ZPOOL_VDEV_SCRIPT_ENV_COUNT; i++)
|
||||||
|
free(env[i]);
|
||||||
|
|
||||||
|
free(env);
|
||||||
|
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free the env[] array that was allocated by zpool_vdev_script_alloc_env().
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
zpool_vdev_script_free_env(char **env)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < ZPOOL_VDEV_SCRIPT_ENV_COUNT; i++)
|
||||||
|
free(env[i]);
|
||||||
|
|
||||||
|
free(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare a disk by (optionally) running a program before labeling the disk.
|
||||||
|
* This can be useful for installing disk firmware or doing some pre-flight
|
||||||
|
* checks on the disk before it becomes part of the pool. The program run is
|
||||||
|
* located at ZFSEXECDIR/zfs_prepare_disk
|
||||||
|
* (E.x: /usr/local/libexec/zfs/zfs_prepare_disk).
|
||||||
|
*
|
||||||
|
* Return 0 on success, non-zero on failure.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
zpool_prepare_disk(zpool_handle_t *zhp, nvlist_t *vdev_nv,
|
||||||
|
const char *prepare_str, char **lines[], int *lines_cnt)
|
||||||
|
{
|
||||||
|
const char *script_path = ZFSEXECDIR "/zfs_prepare_disk";
|
||||||
|
const char *pool_name;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
/* Path to script and a NULL entry */
|
||||||
|
char *argv[2] = {(char *)script_path};
|
||||||
|
char **env = NULL;
|
||||||
|
const char *path = NULL, *enc_sysfs_path = NULL;
|
||||||
|
char *upath;
|
||||||
|
*lines_cnt = 0;
|
||||||
|
|
||||||
|
if (access(script_path, X_OK) != 0) {
|
||||||
|
/* No script, nothing to do */
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
(void) nvlist_lookup_string(vdev_nv, ZPOOL_CONFIG_PATH, &path);
|
||||||
|
(void) nvlist_lookup_string(vdev_nv, ZPOOL_CONFIG_VDEV_ENC_SYSFS_PATH,
|
||||||
|
&enc_sysfs_path);
|
||||||
|
|
||||||
|
upath = zfs_get_underlying_path(path);
|
||||||
|
pool_name = zhp ? zpool_get_name(zhp) : NULL;
|
||||||
|
|
||||||
|
env = zpool_vdev_script_alloc_env(pool_name, path, upath,
|
||||||
|
enc_sysfs_path, "VDEV_PREPARE", prepare_str);
|
||||||
|
|
||||||
|
free(upath);
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
return (ENOMEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = libzfs_run_process_get_stdout(script_path, argv, env, lines,
|
||||||
|
lines_cnt);
|
||||||
|
|
||||||
|
zpool_vdev_script_free_env(env);
|
||||||
|
|
||||||
|
return (rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Optionally run a script and then label a disk. The script can be used to
|
||||||
|
* prepare a disk for inclusion into the pool. For example, it might update
|
||||||
|
* the disk's firmware or check its health.
|
||||||
|
*
|
||||||
|
* The 'name' provided is the short name, stripped of any leading
|
||||||
|
* /dev path, and is passed to zpool_label_disk. vdev_nv is the nvlist for
|
||||||
|
* the vdev. prepare_str is a string that gets passed as the VDEV_PREPARE
|
||||||
|
* env variable to the script.
|
||||||
|
*
|
||||||
|
* The following env vars are passed to the script:
|
||||||
|
*
|
||||||
|
* POOL_NAME: The pool name (blank during zpool create)
|
||||||
|
* VDEV_PREPARE: Reason why the disk is being prepared for inclusion:
|
||||||
|
* "create", "add", "replace", or "autoreplace"
|
||||||
|
* VDEV_PATH: Path to the disk
|
||||||
|
* VDEV_UPATH: One of the 'underlying paths' to the disk. This is
|
||||||
|
* useful for DM devices.
|
||||||
|
* VDEV_ENC_SYSFS_PATH: Path to the disk's enclosure sysfs path, if available.
|
||||||
|
*
|
||||||
|
* Note, some of these values can be blank.
|
||||||
|
*
|
||||||
|
* Return 0 on success, non-zero otherwise.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
zpool_prepare_and_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp,
|
||||||
|
const char *name, nvlist_t *vdev_nv, const char *prepare_str,
|
||||||
|
char **lines[], int *lines_cnt)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
char vdev_path[MAXPATHLEN];
|
||||||
|
(void) snprintf(vdev_path, sizeof (vdev_path), "%s/%s", DISK_ROOT,
|
||||||
|
name);
|
||||||
|
|
||||||
|
/* zhp will be NULL when creating a pool */
|
||||||
|
rc = zpool_prepare_disk(zhp, vdev_nv, prepare_str, lines, lines_cnt);
|
||||||
|
if (rc != 0)
|
||||||
|
return (rc);
|
||||||
|
|
||||||
|
rc = zpool_label_disk(hdl, zhp, name);
|
||||||
|
return (rc);
|
||||||
|
}
|
||||||
|
@ -62,6 +62,7 @@ dist_man_MANS = \
|
|||||||
%D%/man8/zfs-userspace.8 \
|
%D%/man8/zfs-userspace.8 \
|
||||||
%D%/man8/zfs-wait.8 \
|
%D%/man8/zfs-wait.8 \
|
||||||
%D%/man8/zfs_ids_to_path.8 \
|
%D%/man8/zfs_ids_to_path.8 \
|
||||||
|
%D%/man8/zfs_prepare_disk.8 \
|
||||||
%D%/man8/zgenhostid.8 \
|
%D%/man8/zgenhostid.8 \
|
||||||
%D%/man8/zinject.8 \
|
%D%/man8/zinject.8 \
|
||||||
%D%/man8/zpool.8 \
|
%D%/man8/zpool.8 \
|
||||||
|
1
man/man8/.gitignore
vendored
1
man/man8/.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
/zed.8
|
/zed.8
|
||||||
/zfs-mount-generator.8
|
/zfs-mount-generator.8
|
||||||
|
/zfs_prepare_disk.8
|
||||||
|
70
man/man8/zfs_prepare_disk.8.in
Normal file
70
man/man8/zfs_prepare_disk.8.in
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
.\"
|
||||||
|
.\" Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
|
||||||
|
.\" Copyright (C) 2023 Lawrence Livermore National Security, LLC.
|
||||||
|
.\" Refer to the OpenZFS git commit log for authoritative copyright attribution.
|
||||||
|
.\"
|
||||||
|
.\" The contents of this file are subject to the terms of the
|
||||||
|
.\" Common Development and Distribution License Version 1.0 (CDDL-1.0).
|
||||||
|
.\" You can obtain a copy of the license from the top-level file
|
||||||
|
.\" "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
|
||||||
|
.\" You may not use this file except in compliance with the license.
|
||||||
|
.\"
|
||||||
|
.\" Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049)
|
||||||
|
.\"
|
||||||
|
.Dd August 30, 2023
|
||||||
|
.Dt ZFS_PREPARE_DISK 8
|
||||||
|
.Os
|
||||||
|
.
|
||||||
|
.Sh NAME
|
||||||
|
.Nm zfs_prepare_disk
|
||||||
|
.Nd special script that gets run before bringing a disk into a pool
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
.Nm
|
||||||
|
is an optional script that gets called by libzfs before bringing a disk into a
|
||||||
|
pool.
|
||||||
|
It can be modified by the user to run whatever commands are necessary to prepare
|
||||||
|
a disk for inclusion into the pool.
|
||||||
|
For example, users can add lines to
|
||||||
|
.Nm zfs_prepare_disk
|
||||||
|
to do things like update the drive's firmware or check the drive's health.
|
||||||
|
.Nm zfs_prepare_disk
|
||||||
|
is optional and can be removed if not needed.
|
||||||
|
libzfs will look for the script at @zfsexecdir@/zfs_prepare_disk.
|
||||||
|
.
|
||||||
|
.Ss Properties
|
||||||
|
.Nm zfs_prepare_disk
|
||||||
|
will be passed the following environment variables:
|
||||||
|
.sp
|
||||||
|
.Bl -tag -compact -width "VDEV_ENC_SYSFS_PATH"
|
||||||
|
.
|
||||||
|
.It Nm POOL_NAME
|
||||||
|
.No Name of the pool
|
||||||
|
.It Nm VDEV_PATH
|
||||||
|
.No Path to the disk (like /dev/sda)
|
||||||
|
.It Nm VDEV_PREPARE
|
||||||
|
.No Reason why the disk is being prepared for inclusion
|
||||||
|
('create', 'add', 'replace', or 'autoreplace').
|
||||||
|
This can be useful if you only want the script to be run under certain actions.
|
||||||
|
.It Nm VDEV_UPATH
|
||||||
|
.No Path to one of the underlying devices for the
|
||||||
|
disk.
|
||||||
|
For multipath this would return one of the /dev/sd* paths to the disk.
|
||||||
|
If the device is not a device mapper device, then
|
||||||
|
.Nm VDEV_UPATH
|
||||||
|
just returns the same value as
|
||||||
|
.Nm VDEV_PATH
|
||||||
|
.It Nm VDEV_ENC_SYSFS_PATH
|
||||||
|
.No Path to the disk's enclosure sysfs path, if available
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
Note that some of these variables may have a blank value.
|
||||||
|
.Nm POOL_NAME
|
||||||
|
is blank at pool creation time, for example.
|
||||||
|
.Sh ENVIRONMENT
|
||||||
|
.Nm zfs_prepare_disk
|
||||||
|
runs with a limited $PATH.
|
||||||
|
.Sh EXIT STATUS
|
||||||
|
.Nm zfs_prepare_disk
|
||||||
|
should return 0 on success, non-zero otherwise.
|
||||||
|
If non-zero is returned, the disk will not be included in the pool.
|
||||||
|
.
|
@ -20,6 +20,8 @@ scripts_scripts = \
|
|||||||
|
|
||||||
if CONFIG_USER
|
if CONFIG_USER
|
||||||
dist_scripts_SCRIPTS = $(scripts_scripts)
|
dist_scripts_SCRIPTS = $(scripts_scripts)
|
||||||
|
dist_zfsexec_SCRIPTS = \
|
||||||
|
%D%/zfs_prepare_disk
|
||||||
else
|
else
|
||||||
dist_noinst_SCRIPTS += $(scripts_scripts)
|
dist_noinst_SCRIPTS += $(scripts_scripts)
|
||||||
endif
|
endif
|
||||||
|
17
scripts/zfs_prepare_disk
Executable file
17
scripts/zfs_prepare_disk
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# This is an optional helper script that is automatically called by libzfs
|
||||||
|
# before a disk is about to be added into the pool. It can be modified by
|
||||||
|
# the user to run whatever commands are necessary to prepare a disk for
|
||||||
|
# inclusion into the pool. For example, users can add lines to this
|
||||||
|
# script to do things like update the drive's firmware or check the drive's
|
||||||
|
# health. The script is optional and can be removed if it is not needed.
|
||||||
|
#
|
||||||
|
# See the zfs_prepare_disk(8) man page for details.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# echo "Prepare disk $VDEV_PATH ($VDEV_UPATH) for $VDEV_PREPARE in $POOL_NAME"
|
||||||
|
#
|
||||||
|
|
||||||
|
exit 0
|
Loading…
Reference in New Issue
Block a user