From b9373170e3e346733f5666dd80727cb6e05cd5d3 Mon Sep 17 00:00:00 2001 From: Olaf Faaland Date: Tue, 18 Jul 2017 18:11:08 -0700 Subject: [PATCH] Add zgenhostid utility script Turning the multihost property on requires that a hostid be set to allow ZFS to determine when a foreign system is attemping to import a pool. The error message instructing the user to set a hostid refers to genhostid(1). Genhostid(1) is not available on SUSE Linux. This commit adds a script modeled after genhostid(1) for those users. Zgenhostid checks for an /etc/hostid file; if it does not exist, it creates one and stores a value. If the user has provided a hostid as an argument, that value is used. Otherwise, a random hostid is generated and stored. This differs from the CENTOS 6/7 versions of genhostid, which overwrite the /etc/hostid file even though their manpages state otherwise. A man page for zgenhostid is added. The one for genhostid is in (1), but I put zgenhostid in (8) because I believe it's more appropriate. The mmp tests are modified to use zgenhostid to set the hostid instead of using the spl_hostid module parameter. zgenhostid will not replace an existing /etc/hostid file, so new mmp_clear_hostid calls are required. Reviewed-by: Giuseppe Di Natale Reviewed-by: Andreas Dilger Reviewed-by: Brian Behlendorf Signed-off-by: Olaf Faaland Closes #6358 Closes #6379 --- cmd/Makefile.am | 2 +- cmd/zgenhostid/Makefile.am | 1 + cmd/zgenhostid/zgenhostid | 61 ++++++++++++++++ cmd/zpool/zpool_main.c | 4 +- configure.ac | 1 + lib/libzfs/libzfs_pool.c | 2 +- man/man8/Makefile.am | 1 + man/man8/zgenhostid.8 | 72 +++++++++++++++++++ man/man8/zpool.8 | 2 +- tests/zfs-tests/include/commands.cfg | 1 + .../zfs-tests/tests/functional/mmp/mmp.kshlib | 10 ++- .../functional/mmp/mmp_active_import.ksh | 1 + .../functional/mmp/mmp_exported_import.ksh | 2 + .../functional/mmp/mmp_inactive_import.ksh | 2 + 14 files changed, 151 insertions(+), 11 deletions(-) create mode 100644 cmd/zgenhostid/Makefile.am create mode 100755 cmd/zgenhostid/zgenhostid create mode 100644 man/man8/zgenhostid.8 diff --git a/cmd/Makefile.am b/cmd/Makefile.am index 04aa7c633..f7d7db68c 100644 --- a/cmd/Makefile.am +++ b/cmd/Makefile.am @@ -1,3 +1,3 @@ SUBDIRS = zfs zpool zdb zhack zinject zstreamdump ztest zpios SUBDIRS += mount_zfs fsck_zfs zvol_id vdev_id arcstat dbufstat zed -SUBDIRS += arc_summary raidz_test +SUBDIRS += arc_summary raidz_test zgenhostid diff --git a/cmd/zgenhostid/Makefile.am b/cmd/zgenhostid/Makefile.am new file mode 100644 index 000000000..69c99ca9d --- /dev/null +++ b/cmd/zgenhostid/Makefile.am @@ -0,0 +1 @@ +dist_bin_SCRIPTS = zgenhostid diff --git a/cmd/zgenhostid/zgenhostid b/cmd/zgenhostid/zgenhostid new file mode 100755 index 000000000..db690eca3 --- /dev/null +++ b/cmd/zgenhostid/zgenhostid @@ -0,0 +1,61 @@ +#!/bin/bash + +# Emulate genhostid(1) available on RHEL/CENTOS, for use on distros +# which do not provide that utility. +# +# Usage: +# zgenhostid +# zgenhostid +# +# If /etc/hostid already exists and is size > 0, the script exits immediately +# and changes nothing. Unlike genhostid, this generates an error message. +# +# The first form generates a random hostid and stores it in /etc/hostid. +# The second form checks that the provided value is between 0x1 and 0xFFFFFFFF +# and if so, stores it in /etc/hostid. This form is not supported by +# genhostid(1). + +hostid_file=/etc/hostid + +function usage { + echo "$0 [value]" + echo "If $hostid_file is not present, store a hostid in it." >&2 + echo "The optional value must be an 8-digit hex number between" >&2 + echo "1 and 2^32-1. If no value is provided, a random one will" >&2 + echo "be generated. The value must be unique among your systems." >&2 +} + +# hostid(1) ignores contents of /etc/hostid if size < 4 bytes. It would +# be better if this checked size >= 4 bytes but it the method must be +# widely portable. +if [ -s $hostid_file ]; then + echo "$hostid_file already exists. No change made." >&2 + exit 1 +fi + +if [ -n "$1" ]; then + host_id=$1 +else + # $RANDOM goes from 0..32k-1 + number=$((((RANDOM % 4) * 32768 + RANDOM) * 32768 + RANDOM)) + host_id=$(printf "%08x" $number) +fi + +if egrep -o '^0{8}$' <<< $host_id >/dev/null 2>&1; then + usage + exit 2 +fi + +if ! egrep -o '^[a-fA-F0-9]{8}$' <<< $host_id >/dev/null 2>&1; then + usage + exit 3 +fi + +a=${host_id:6:2} +b=${host_id:4:2} +c=${host_id:2:2} +d=${host_id:0:2} + +echo -ne \\x$a\\x$b\\x$c\\x$d > $hostid_file + +exit 0 diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c index 8d04f7725..f597109d2 100644 --- a/cmd/zpool/zpool_main.c +++ b/cmd/zpool/zpool_main.c @@ -2177,7 +2177,7 @@ show_import(nvlist_t *config) break; case ZPOOL_STATUS_HOSTID_REQUIRED: (void) printf(gettext(" action: Set a unique system " - "hostid with the genhostid(1) command.\n")); + "hostid with the zgenhostid(8) command.\n")); break; default: (void) printf(gettext(" action: The pool cannot be " @@ -2304,7 +2304,7 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts, (void) fprintf(stderr, gettext("Cannot import '%s': " "pool has the multihost property on and the\n" "system's hostid is not set. Set a unique hostid " - "with the genhostid(1) command.\n"), name); + "with the zgenhostid(8) command.\n"), name); } else { char *hostname = ""; uint64_t timestamp = 0; diff --git a/configure.ac b/configure.ac index 514fcd98e..0c7977ef8 100644 --- a/configure.ac +++ b/configure.ac @@ -113,6 +113,7 @@ AC_CONFIG_FILES([ cmd/arc_summary/Makefile cmd/zed/Makefile cmd/raidz_test/Makefile + cmd/zgenhostid/Makefile contrib/Makefile contrib/bash_completion.d/Makefile contrib/dracut/Makefile diff --git a/lib/libzfs/libzfs_pool.c b/lib/libzfs/libzfs_pool.c index 58d91c8f4..73af75f9d 100644 --- a/lib/libzfs/libzfs_pool.c +++ b/lib/libzfs/libzfs_pool.c @@ -1865,7 +1865,7 @@ zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname, "the multihost property on and " "the\nsystem's hostid is not set. " "Set a unique system hostid with " - "the genhostid(1) command.\n")); + "the zgenhostid(8) command.\n")); } (void) zfs_error_aux(hdl, aux); diff --git a/man/man8/Makefile.am b/man/man8/Makefile.am index b89e34dfd..fa58a628d 100644 --- a/man/man8/Makefile.am +++ b/man/man8/Makefile.am @@ -4,6 +4,7 @@ dist_man_MANS = \ vdev_id.8 \ zdb.8 \ zfs.8 \ + zgenhostid.8 zinject.8 \ zpool.8 \ zstreamdump.8 diff --git a/man/man8/zgenhostid.8 b/man/man8/zgenhostid.8 new file mode 100644 index 000000000..2c86b8e28 --- /dev/null +++ b/man/man8/zgenhostid.8 @@ -0,0 +1,72 @@ +.\" +.\" CDDL HEADER START +.\" +.\" The contents of this file are subject to the terms of the +.\" Common Development and Distribution License (the "License"). +.\" You may not use this file except in compliance with the License. +.\" +.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +.\" or http://www.opensolaris.org/os/licensing. +.\" See the License for the specific language governing permissions +.\" and limitations under the License. +.\" +.\" When distributing Covered Code, include this CDDL HEADER in each +.\" file and include the License file at usr/src/OPENSOLARIS.LICENSE. +.\" If applicable, add the following below this CDDL HEADER, with the +.\" fields enclosed by brackets "[]" replaced with your own identifying +.\" information: Portions Copyright [yyyy] [name of copyright owner] +.\" +.\" CDDL HEADER END +.\" +.\" +.\" Copyright (c) 2017 by Lawrence Livermore National Security, LLC. +.\" +.Dd July 24, 2017 +.Dt ZGENHOSTID 8 SMM +.Os Linux +.Sh NAME +.Nm zgenhostid +.Nd generate and store a hostid in +.Em /etc/hostid +.Sh SYNOPSIS +.Nm +.Op Ar hostid +.Sh DESCRIPTION +If +.Em /etc/hostid +does not exist, create it and store a hostid in it. If the user provides +.Op Ar hostid +on the command line, store that value. Otherwise, randomly generate a +value to store. +.Pp +This emulates the +.Xr genhostid 1 +utility and is provided for use on systems which do not include the utility. +.Pp +.Sh OPTIONS +.Op Ar hostid +Specifies the value to be placed in +.Em /etc/hostid . +It must be a number with a value between 1 and 2^32-1. This value +.Sy must +be unique among your systems. It must be expressed in hexadecimal and be +exactly 8 digits long. +.Sh EXAMPLES +.Bl -tag -width Ds +.It Generate a random hostid and store it +.Bd -literal +# zgenhostid +.Ed +.It Record the libc-generated hostid in Em /etc/hostid +.Bd -literal +# zgenhostid $(hostid) +.Ed +.It Record a custom hostid (0xdeadbeef) in Em etc/hostid +.Bd -literal +# zgenhostid deadbeef +.Ed +.El +.Sh SEE ALSO +.Xr spl-module-parameters 5 , +.Xr genhostid 1 , +.Xr hostid 1 diff --git a/man/man8/zpool.8 b/man/man8/zpool.8 index 02853342c..5814b4125 100644 --- a/man/man8/zpool.8 +++ b/man/man8/zpool.8 @@ -732,7 +732,7 @@ in the man page. In order to enable this property each host must set a unique hostid. See .Xr genhostid 1 -and +.Xr zgenhostid 8 .Xr spl-module-paramters 5 for additional details. The default value is .Sy off . diff --git a/tests/zfs-tests/include/commands.cfg b/tests/zfs-tests/include/commands.cfg index 57b1bd315..32749028c 100644 --- a/tests/zfs-tests/include/commands.cfg +++ b/tests/zfs-tests/include/commands.cfg @@ -141,6 +141,7 @@ export ZFS_FILES='zdb arcstat.py dbufstat.py zed + zgenhostid zstreamdump' export ZFSTEST_FILES='chg_usr_exec diff --git a/tests/zfs-tests/tests/functional/mmp/mmp.kshlib b/tests/zfs-tests/tests/functional/mmp/mmp.kshlib index a81779b0c..ffe26dfde 100644 --- a/tests/zfs-tests/tests/functional/mmp/mmp.kshlib +++ b/tests/zfs-tests/tests/functional/mmp/mmp.kshlib @@ -79,12 +79,7 @@ function mmp_set_hostid { typeset hostid=$1 - a=${hostid:6:2} - b=${hostid:4:2} - c=${hostid:2:2} - d=${hostid:0:2} - - printf "\\x$a\\x$b\\x$c\\x$d" >$HOSTID_FILE + zgenhostid $1 if [ $(hostid) != "$hostid" ]; then return 1 @@ -107,10 +102,12 @@ function mmp_pool_create # pool dir log_must mkdir -p $dir log_must truncate -s $MINVDEVSIZE $dir/vdev1 $dir/vdev2 + log_must mmp_clear_hostid log_must mmp_set_hostid $HOSTID1 log_must zpool create -f $pool mirror $dir/vdev1 $dir/vdev2 log_must zpool set multihost=on $pool log_must zpool export $pool + log_must mmp_clear_hostid log_must mmp_set_hostid $HOSTID2 log_note "Starting ztest in the background as hostid $HOSTID1" @@ -146,6 +143,7 @@ function mmp_pool_set_hostid # pool hostid typeset pool=$1 typeset hostid=$2 + log_must mmp_clear_hostid log_must mmp_set_hostid $hostid log_must zpool export $pool log_must zpool import $pool diff --git a/tests/zfs-tests/tests/functional/mmp/mmp_active_import.ksh b/tests/zfs-tests/tests/functional/mmp/mmp_active_import.ksh index 92eb9ce2d..e2e30c7ea 100755 --- a/tests/zfs-tests/tests/functional/mmp/mmp_active_import.ksh +++ b/tests/zfs-tests/tests/functional/mmp/mmp_active_import.ksh @@ -86,6 +86,7 @@ log_must mmp_set_hostid $HOSTID1 MMP_IMPORTED_MSG="The pool can be imported" log_must check_pool_import $MMP_POOL "-d $MMP_DIR" "action" $MMP_IMPORTED_MSG +log_must mmp_clear_hostid log_must mmp_set_hostid $HOSTID2 MMP_IMPORTED_MSG="The pool was last accessed by another system." log_must check_pool_import $MMP_POOL "-d $MMP_DIR" "status" $MMP_IMPORTED_MSG diff --git a/tests/zfs-tests/tests/functional/mmp/mmp_exported_import.ksh b/tests/zfs-tests/tests/functional/mmp/mmp_exported_import.ksh index d65ca5b3d..6d34f7e77 100755 --- a/tests/zfs-tests/tests/functional/mmp/mmp_exported_import.ksh +++ b/tests/zfs-tests/tests/functional/mmp/mmp_exported_import.ksh @@ -62,6 +62,7 @@ done for opt in "" "-f"; do log_must mmp_pool_set_hostid $TESTPOOL $HOSTID1 log_must zpool export $TESTPOOL + log_must mmp_clear_hostid log_must mmp_set_hostid $HOSTID2 log_must import_no_activity_check $TESTPOOL $opt done @@ -87,6 +88,7 @@ done for opt in "" "-f"; do log_must mmp_pool_set_hostid $TESTPOOL $HOSTID1 log_must zpool export $TESTPOOL + log_must mmp_clear_hostid log_must mmp_set_hostid $HOSTID2 log_must import_no_activity_check $TESTPOOL $opt done diff --git a/tests/zfs-tests/tests/functional/mmp/mmp_inactive_import.ksh b/tests/zfs-tests/tests/functional/mmp/mmp_inactive_import.ksh index c944b6b28..71f3af5c5 100755 --- a/tests/zfs-tests/tests/functional/mmp/mmp_inactive_import.ksh +++ b/tests/zfs-tests/tests/functional/mmp/mmp_inactive_import.ksh @@ -60,6 +60,7 @@ done # 3. Verify multihost=off and hostids differ (no activity check) log_must zpool export -F $TESTPOOL +log_must mmp_clear_hostid log_must mmp_set_hostid $HOSTID2 log_mustnot import_no_activity_check $TESTPOOL "" log_must import_no_activity_check $TESTPOOL "-f" @@ -81,6 +82,7 @@ done # 6. Verify multihost=on and hostids differ (activity check) log_must zpool export -F $TESTPOOL +log_must mmp_clear_hostid log_must mmp_set_hostid $HOSTID2 log_mustnot import_activity_check $TESTPOOL "" log_must import_activity_check $TESTPOOL "-f" -- 2.39.2