From: Fan Yong Date: Sat, 12 Nov 2016 20:59:11 +0000 (+0800) Subject: LU-8900 snapshot: user space snapshot tools X-Git-Tag: 2.9.55~10 X-Git-Url: https://git.whamcloud.com/?a=commitdiff_plain;h=d73849a05e3ed1baf07b1fd80ffa055299539b4a;p=fs%2Flustre-release.git LU-8900 snapshot: user space snapshot tools The user space snapshot tools are implemented as part of the lctl tools set. They use other user-space tools, such as lctl commands for write barrier on all MDTs, fork/erase Lustre configuration, zfs/zpool commands for backend zfs snapshot. 1. Create the snapshot lctl snapshot_create [-b | --barrier [on | off]] [-c | --comment comment] <-F | --fsname fsname> [-h | --help] <-n | --name ssname> [-r | --rsh remote_shell] [-t | --timeout timeout] 2. Destroy the snapshot lctl snapshot_destroy [-f | --force] <-F | --fsname fsname> [-h | --help] <-n | --name ssname> [-r | --rsh remote_shell] 3. Modify the snapshot lctl snapshot_modify [-c | --comment comment] <-F | --fsname fsname> [-h | --help] <-n | --name ssname> [-N | --new new_ssname] [-r | --rsh remote_shell] 4. Query the snapshot(s) lctl snapshot_list [-d | --detail] <-F | --fsname fsname> [-h | --help] [-n | --name ssname] [-r | --rsh remote_shell] 5. Mount the snapshot lctl snapshot_mount <-F | --fsname fsname> [-h | --help] <-n | --name ssname> [-r | --rsh remote_shell] 6. Umount the snapshot lctl snapshot_umount <-F | --fsname fsname> [-h | --help] <-n | --name ssname> [-r | --rsh remote_shell] The system config information, such as each target's hostname, pool name, local filesystem name, role, index, and so on, will be written in the snapshot config file "/etc/ldev.conf". To be compatible with old snapshot usage, it will firstly check whether the "/etc/ldev.conf" exists or not, it yes, it will load the configuration from such file preferentially, otherwise, the file "/etc/lsnapshot/${fsname}.conf" will be used. Test-Parameters: alwaysuploadlogs envdefinitions=SLOW=yes mdtfilesystemtype=zfs mdsfilesystemtype=zfs ostfilesystemtype=zfs mdscount=2 mdtcount=4 testlist=sanity-lsnapshot Signed-off-by: Fan Yong Change-Id: I1191c2ea35c355e8554bd2bd022c1b9d32741392 Reviewed-on: https://review.whamcloud.com/24269 Tested-by: Jenkins Tested-by: Maloo Reviewed-by: Niu Yawei Reviewed-by: Lai Siyao Reviewed-by: Oleg Drokin --- diff --git a/lustre/mgs/mgs_barrier.c b/lustre/mgs/mgs_barrier.c index ef18273..db406a9 100644 --- a/lustre/mgs/mgs_barrier.c +++ b/lustre/mgs/mgs_barrier.c @@ -714,7 +714,8 @@ int mgs_iocontrol_barrier(const struct lu_env *env, if (unlikely(bc->bc_version != BARRIER_VERSION_V1)) RETURN(-EOPNOTSUPP); - if (unlikely(strnlen(bc->bc_name, sizeof(bc->bc_name)) > 8)) + if (unlikely(bc->bc_name[0] == '\0' || + strnlen(bc->bc_name, sizeof(bc->bc_name)) > 8)) RETURN(-EINVAL); switch (bc->bc_cmd) { diff --git a/lustre/mgs/mgs_llog.c b/lustre/mgs/mgs_llog.c index 0c38f74..eac29cc 100644 --- a/lustre/mgs/mgs_llog.c +++ b/lustre/mgs/mgs_llog.c @@ -4442,6 +4442,10 @@ int mgs_lcfg_fork(const struct lu_env *env, struct mgs_device *mgs, int rc = 0; ENTRY; + if (unlikely(!oldname || oldname[0] == '\0' || + !newname || newname[0] == '\0')) + RETURN(-EINVAL); + if (strcmp(oldname, newname) == 0) RETURN(-EINVAL); @@ -4512,6 +4516,9 @@ int mgs_lcfg_erase(const struct lu_env *env, struct mgs_device *mgs, int rc; ENTRY; + if (unlikely(!fsname || fsname[0] == '\0')) + RETURN(-EINVAL); + rc = mgs_erase_logs(env, mgs, fsname); RETURN(rc); diff --git a/lustre/tests/Makefile.am b/lustre/tests/Makefile.am index 0e3d416..d24289f 100644 --- a/lustre/tests/Makefile.am +++ b/lustre/tests/Makefile.am @@ -38,6 +38,7 @@ noinst_SCRIPTS += posix.sh sanity-scrub.sh scrub-performance.sh ha.sh noinst_SCRIPTS += sanity-lfsck.sh lfsck-performance.sh noinst_SCRIPTS += resolveip noinst_SCRIPTS += sanity-hsm.sh +noinst_SCRIPTS += sanity-lsnapshot.sh nobase_noinst_SCRIPTS = cfg/local.sh nobase_noinst_SCRIPTS += test-groups/regression test-groups/regression-mpi nobase_noinst_SCRIPTS += acl/make-tree acl/run cfg/ncli.sh diff --git a/lustre/tests/sanity-lsnapshot.sh b/lustre/tests/sanity-lsnapshot.sh new file mode 100644 index 0000000..985cf99 --- /dev/null +++ b/lustre/tests/sanity-lsnapshot.sh @@ -0,0 +1,331 @@ +#!/bin/bash +# +# Run select tests by setting ONLY, or as arguments to the script. +# Skip specific tests by setting EXCEPT. +# + +set -e + +ONLY=${ONLY:-"$*"} +ALWAYS_EXCEPT="$SANITY_LSNAPSHOT_EXCEPT" +[ "$SLOW" = "no" ] && EXCEPT_SLOW="" +# UPDATE THE COMMENT ABOVE WITH BUG NUMBERS WHEN CHANGING ALWAYS_EXCEPT! + +LUSTRE=${LUSTRE:-$(cd $(dirname $0)/..; echo $PWD)} +. $LUSTRE/tests/test-framework.sh +init_test_env $@ +. ${CONFIG:=$LUSTRE/tests/cfg/$NAME.sh} +init_logging + +require_dsh_mds || exit 0 +require_dsh_ost || exit 0 + +check_and_setup_lustre + +do_facet mgs $LCTL set_param debug=+snapshot +do_nodes $(comma_list $(mdts_nodes)) $LCTL set_param debug=+snapshot + +lss_gen_conf +lss_cleanup +build_test_filter + +test_0() { + echo "Create lss_0_0 with default" + lsnapshot_create -n lss_0_0 || + lss_err "(1) Fail to create lss_0_0 with default" + + echo "List lss_0_0" + lsnapshot_list -n lss_0_0 || + lss_err "(2) Fail to list lss_0_0" + + echo "Create lss_0_1 with barrier off" + lsnapshot_create -n lss_0_1 --barrier=off || + lss_err "(3) Fail to create lss_0_1 with barrier off" + + echo "List lss_0_1" + lsnapshot_list -n lss_0_1 || + lss_err "(4) Fail to list lss_0_1" + + echo "Create lss_0_2 with comment" + lsnapshot_create -n lss_0_2 -c "'This is test_0'" || + lss_err "(5) Fail to create lss_0_2 with comment" + + echo "List lss_0_2" + lsnapshot_list -n lss_0_2 | grep "This is test_0" || + lss_err "(6) Fail to list lss_0_2" + + echo "Create lss_0_2 with barrier on and comment" + lsnapshot_create -n lss_0_3 --barrier=on -c "'Another one'" || + lss_err "(7) Fail to create lss_0_3 with barrier and comment" + + echo "List lss_0_3" + lsnapshot_list -n lss_0_3 | grep "Another one" || + lss_err "(8) Fail to list lss_0_3" + + echo "Try to create lss_0_0 that exist already" + lsnapshot_create -n lss_0_0 && + lss_err "(9) Create snapshot with exist name should fail" || + true +} +run_test 0 "create lustre snapshot" + +test_1a() { + mkdir -p $DIR/$tdir || lss_err "(1) Fail to mkdir $DIR/$tdir" + rm -f $DIR/$tdir/test-framework.sh + cp $LUSTRE/tests/test-framework.sh $DIR/$tdir/ || + lss_err "(2) Fail to copy" + + cancel_lru_locks mdc + cancel_lru_locks osc + + echo "Create lss_1a_0" + lsnapshot_create -n lss_1a_0 || + lss_err "(3) Fail to create lss_1a_0" + + echo "Check whether mounted (1)" + lsnapshot_list -n lss_1a_0 -d | grep "mounted" && { + lsnapshot_list -n lss_1a_0 -d + lss_err "(4) Expect 'not mount', got 'mounted' for lss_1a_0" + } + + echo "Mount lss_1a_0" + lsnapshot_mount -n lss_1a_0 || + lss_err "(5) Fail to mount lss_1a_0" + + echo "Check whether mounted (2)" + local mcount=$(lsnapshot_list -n lss_1a_0 -d | grep "not mount" | wc -l) + [[ $mcount -ne 0 ]] && { + if combined_mgs_mds ; then + lsnapshot_list -n lss_1a_0 -d + lss_err "(6.1) Got unexpected 'not mount' for lss_1a_0" + fi + + [[ $mcount -gt 1 ]] && { + lsnapshot_list -n lss_1a_0 -d + lss_err "(6.2) Got unexpected 'not mount' for lss_1a_0" + } + + # The first 10 lines contains and only contains MGS mount status + lsnapshot_list -n lss_1a_0 -d | head -n 10 | + grep "not mount" || { + lsnapshot_list -n lss_1a_0 -d + lss_err "(6.3) Got unexpected 'not mount' for lss_1a_0" + } + } + + local ss_fsname=$(lsnapshot_list -n lss_1a_0 | + awk '/^snapshot_fsname/ { print $2 }') + local mntpt="/mnt/$ss_fsname" + local saved_fsname=$FSNAME + + mkdir -p $mntpt || + lss_err "(7) Fail to create mount point $mntpt" + FSNAME=$ss_fsname + echo "Mount client" + mount_client $mntpt ro || { + FSNAME=$saved_fsname + lss_err "(8) Fail to mount client for lss_1a_0" + } + + FSNAME=$saved_fsname + echo "Check whether the file in snapshot is the same as original one" + diff $DIR/$tdir/test-framework.sh $mntpt/$tdir/test-framework.sh || + lss_err "(9) files should be the same" + + echo "Modify the original file, and check again" + echo dummy >> $DIR/$tdir/test-framework.sh + diff $DIR/$tdir/test-framework.sh $mntpt/$tdir/test-framework.sh && + lss_err "(10) files should be different" + + umount $mntpt || + lss_err "(11) Fail to umount client for lss_1a_0" + + echo "Umount lss_1a_0" + lsnapshot_umount -n lss_1a_0 || + lss_err "(12) Fail to umount lss_1a_0" + + echo "Check whether mounted (3)" + lsnapshot_list -n lss_1a_0 -d | grep "mounted" && { + lsnapshot_list -n lss_1a_0 -d + lss_err "(13) Expect 'not mount', got 'mounted' for lss_1a_0" + } || true +} +run_test 1a "mount/umount lustre snapshot" + +test_1b() { + echo "Create lss_1b_0" + lsnapshot_create -n lss_1b_0 || + lss_err "(1) Fail to create lss_1b_0" + + echo "Check whether mounted (1)" + lsnapshot_list -n lss_1b_0 -d | grep "mounted" && { + lsnapshot_list -n lss_1b_0 -d + lss_err "(2) Expect 'not mount', got 'mounted' for lss_1b_0" + } + + stopall || lss_err "(3) Fail to stopall" + + echo "Mount lss_1b_0" + lsnapshot_mount -n lss_1b_0 || + lss_err "(4) Fail to mount lss_1b_0" + + echo "Check whether mounted (2)" + lsnapshot_list -n lss_1b_0 -d | grep "not mount" && { + lsnapshot_list -n lss_1b_0 -d + lss_err "(5) Expect 'mounted', got 'not mount' for lss_1b_0" + } + + echo "umount lss_1b_0" + lsnapshot_umount -n lss_1b_0 || + lss_err "(6) Fail to umount lss_1b_0" + + echo "Check whether mounted (3)" + lsnapshot_list -n lss_1b_0 -d | grep "mounted" && { + lsnapshot_list -n lss_1b_0 -d + lss_err "(7) Expect 'not mount', got 'mounted' for lss_1b_0" + } + + setupall || lss_err "(8) Fail to setupall" +} +run_test 1b "mount snapshot without original filesystem mounted" + +test_2() { + echo "Create lss_2_0" + lsnapshot_create -n lss_2_0 --barrier=off || + lss_err "(1) Fail to create lss_2_0" + + echo "List lss_2_0" + lsnapshot_list -n lss_2_0 || + lss_err "(2) Fail to list lss_2_0" + + echo "Destroy lss_2_0" + lsnapshot_destroy -n lss_2_0 || + lss_err "(3) Fail to destroy lss_2_0" + + echo "Try to list lss_2_0 after destroy" + lsnapshot_list -n lss_2_0 && + lss_err "(4) List lss_2_0 should fail after destroy" + + echo "Create lss_2_1" + lsnapshot_create -n lss_2_1 --barrier=off || + lss_err "(5) Fail to create lss_2_1" + + echo "List lss_2_1" + lsnapshot_list -n lss_2_1 || + lss_err "(6) Fail to list lss_2_1" + + echo "Mount lss_2_1" + lsnapshot_mount -n lss_2_1 || + lss_err "(7) Fail to mount lss_2_1" + + echo "Try to destroy lss_2_1 with mounted" + lsnapshot_destroy -n lss_2_1 && + lss_err "(8) Destroy mounted snapshot without -f should fail" + + echo "Destroy lss_2_1 by force with mounted" + lsnapshot_destroy -n lss_2_1 -f || + lss_err "(9) Destroy mounted snapshot with -f should succeed" + + echo "Try to list lss_2_1 after destroy" + lsnapshot_list -n lss_2_1 && + lss_err "(10) List lss_2_1 should fail after destroy" || true +} +run_test 2 "destroy lustre snapshot" + +test_3a() { + echo "Create lss_3a_0" + lsnapshot_create -n lss_3a_0 --barrier=off -c "'It is test_3a'" || + lss_err "(1) Fail to create lss_3a_0" + + echo "List lss_3a_0" + lsnapshot_list -n lss_3a_0 || + lss_err "(2) Fail to list lss_3a_0" + + local old_mtime=$(lsnapshot_list -n lss_3a_0 | + awk '/^modify_time/ { $1=""; print $0 }') + + echo "Rename lss_3a_0 to lss_3a_1" + lsnapshot_modify -n lss_3a_0 -N "lss_3a_1" || + lss_err "(3) Fail to rename lss_3a_0 to lss_3a_1" + + echo "Try to list lss_3a_0 after rename" + lsnapshot_list -n lss_3a_0 && + lss_err "(4) List lss_3a_0 should fail after rename" + + echo "List lss_3a_1" + lsnapshot_list -n lss_3a_1 || + lss_err "(5) Fail to list lss_3a_1" + + local new_mtime=$(lsnapshot_list -n lss_3a_1 | + awk '/^modify_time/ { $1=""; print $0 }') + echo "Check whether mtime has been changed" + [ "$old_mtime" != "$new_mtime" ] || + lss_err "(6) mtime should be changed because of rename" + + echo "Modify lss_3a_1's comment" + lsnapshot_modify -n lss_3a_1 -c "'Renamed from lss_3a_0'" || + lss_err "(7) Fail to change lss_3a_1's comment" + + echo "Check whether comment has been changed" + lsnapshot_list -n lss_3a_1 -d | grep "It is test_3a" && { + lsnapshot_list -n lss_3a_1 -d + lss_err "(8) The comment should have been changed" + } + + echo "Modify lss_3a_1's name and comment together" + lsnapshot_modify -n lss_3a_1 -N "lss_3a_2" -c "'Change again'" || + lss_err "(9) Fail to modify lss_3a_1" + + echo "Mount lss_3a_2" + lsnapshot_mount -n lss_3a_2 || + lss_err "(10) Fail to mount lss_3a_2" + + echo "Try to rename lss_3a_2 to lss_3a_3 with mounted" + lsnapshot_modify -n lss_3a_2 -N "lss_3a_3" && + lss_err "(11) Rename mounted snapshot lss_3a_2 should fail" + + echo "Modify lss_3a_2's comment with mounted" + lsnapshot_modify -n lss_3a_2 -c "'Change comment with mounted'" || + lss_err "(12) Change comment with mount should succeed" + + echo "Umount lss_3a_2" + lsnapshot_umount -n lss_3a_2 || + lss_err "(13) Fail to umount lss_3a_2" +} +run_test 3a "modify lustre snapshot" + +test_3b() { + echo "Create lss_3b_0" + lsnapshot_create -n lss_3b_0 --barrier=off -c "'It is test_3b'" || + lss_err "(1) Fail to create lss_3b_0" + + echo "List lss_3b_0" + lsnapshot_list -n lss_3b_0 || + lss_err "(2) Fail to list lss_3b_0" + + stopall || lss_err "(3) Fail to stopall" + + echo "Modify lss_3b_0's name and comment together" + lsnapshot_modify -n lss_3b_0 -N "lss_3b_1" -c "'Change again'" || + lss_err "(4) Fail to modify lss_3b_0" + + echo "Try to list lss_3b_0 after rename" + lsnapshot_list -n lss_3b_0 && + lss_err "(5) List lss_3b_0 should fail after rename" + + echo "Check whether comment has been changed" + lsnapshot_list -n lss_3b_1 -d | grep "It is test_3b" && { + lsnapshot_list -n lss_3b_1 -d + lss_err "(6) The comment should have been changed" + } + + setupall || lss_err "(7) Fail to setupall" +} +run_test 3b "modify snapshot without original filesystem mounted" + +lss_cleanup +do_facet mgs $LCTL set_param debug=-snapshot +do_nodes $(comma_list $(mdts_nodes)) $LCTL set_param debug=-snapshot +complete $SECONDS +check_and_cleanup_lustre +exit_status diff --git a/lustre/tests/test-framework.sh b/lustre/tests/test-framework.sh index 9725fa9..fa87bea 100755 --- a/lustre/tests/test-framework.sh +++ b/lustre/tests/test-framework.sh @@ -20,6 +20,10 @@ export JOBID_VAR=${JOBID_VAR:-"procname_uid"} # or "existing" or "disable" #export PDSH="pdsh -S -Rssh -w" export MOUNT_CMD=${MOUNT_CMD:-"mount -t lustre"} export UMOUNT=${UMOUNT:-"umount -d"} + +export LSNAPSHOT_CONF="/etc/ldev.conf" +export LSNAPSHOT_LOG="/var/log/lsnapshot.log" + # sles12 umount has a issue with -d option [ -e /etc/SuSE-release ] && grep -w VERSION /etc/SuSE-release | grep -wq 12 && { export UMOUNT="umount" @@ -1041,6 +1045,17 @@ zpool_name() { } # +# +# Get ZFS local fsname. +# +zfs_local_fsname() { + local facet=$1 + local lfsname=$(basename $(facet_device $facet)) + + echo -n $lfsname +} + +# # Create ZFS storage pool. # create_zpool() { @@ -7642,3 +7657,115 @@ killall_process () { do_nodes $clients "killall $signal $name" } + +lsnapshot_create() +{ + do_facet mgs "$LCTL snapshot_create -F $FSNAME $*" +} + +lsnapshot_destroy() +{ + do_facet mgs "$LCTL snapshot_destroy -F $FSNAME $*" +} + +lsnapshot_modify() +{ + do_facet mgs "$LCTL snapshot_modify -F $FSNAME $*" +} + +lsnapshot_list() +{ + do_facet mgs "$LCTL snapshot_list -F $FSNAME $*" +} + +lsnapshot_mount() +{ + do_facet mgs "$LCTL snapshot_mount -F $FSNAME $*" +} + +lsnapshot_umount() +{ + do_facet mgs "$LCTL snapshot_umount -F $FSNAME $*" +} + +lss_err() +{ + local msg=$1 + + do_facet mgs "cat $LSNAPSHOT_LOG" + error $msg +} + +lss_cleanup() +{ + echo "Cleaning test environment ..." + + # Every lsnapshot command takes exclusive lock with others, + # so can NOT destroy the snapshot during list with 'xargs'. + while true; do + local ssname=$(lsnapshot_list | grep snapshot_name | + grep lss_ | awk '{ print $2 }' | head -n 1) + [ -z "$ssname" ] && break + + lsnapshot_destroy -n $ssname -f || + lss_err "Fail to destroy $ssname by force" + done +} + +lss_gen_conf_one() +{ + local facet=$1 + local role=$2 + local idx=$3 + + local host=$(facet_active_host $facet) + local dir=$(dirname $(facet_vdevice $facet)) + local pool=$(zpool_name $facet) + local lfsname=$(zfs_local_fsname $facet) + local label=${FSNAME}-${role}$(printf '%04x' $idx) + + do_facet mgs \ + "echo '$host - $label zfs:${dir}/${pool}/${lfsname} - -' >> \ + $LSNAPSHOT_CONF" +} + +lss_gen_conf() +{ + do_facet mgs "rm -f $LSNAPSHOT_CONF" + echo "Generating $LSNAPSHOT_CONF on MGS ..." + + if ! combined_mgs_mds ; then + [ $(facet_fstype mgs) != zfs ] && + skip "Lustre snapshot 1 only works for ZFS backend" && + exit 0 + + local host=$(facet_active_host mgs) + local dir=$(dirname $(facet_vdevice mgs)) + local pool=$(zpool_name mgs) + local lfsname=$(zfs_local_fsname mgs) + + do_facet mgs \ + "echo '$host - MGS zfs:${dir}/${pool}/${lfsname} - -' \ + >> $LSNAPSHOT_CONF" || lss_err "generate lss conf (mgs)" + fi + + for num in `seq $MDSCOUNT`; do + [ $(facet_fstype mds$num) != zfs ] && + skip "Lustre snapshot 1 only works for ZFS backend" && + exit 0 + + lss_gen_conf_one mds$num MDT $((num - 1)) || + lss_err "generate lss conf (mds$num)" + done + + for num in `seq $OSTCOUNT`; do + [ $(facet_fstype ost$num) != zfs ] && + skip "Lustre snapshot 1 only works for ZFS backend" && + exit 0 + + lss_gen_conf_one ost$num OST $((num - 1)) || + lss_err "generate lss conf (ost$num)" + done + + do_facet mgs "cat $LSNAPSHOT_CONF" +} diff --git a/lustre/tests/test-groups/regression b/lustre/tests/test-groups/regression index 11ca1e3..d46c4d8 100644 --- a/lustre/tests/test-groups/regression +++ b/lustre/tests/test-groups/regression @@ -23,3 +23,4 @@ sgpdd-survey sanity-scrub sanity-lfsck sanity-hsm +sanity-lsnapshot diff --git a/lustre/utils/Makefile.am b/lustre/utils/Makefile.am index 45eba70..9e42a93 100644 --- a/lustre/utils/Makefile.am +++ b/lustre/utils/Makefile.am @@ -56,7 +56,7 @@ endif noinst_LIBRARIES = liblustreapitmp.a endif # UTILS -lctl_SOURCES = lustre_lfsck.c obd.c lustre_cfg.c lctl.c obdctl.h +lctl_SOURCES = lustre_lfsck.c obd.c lustre_cfg.c lctl.c obdctl.h lsnapshot.c lctl_LDADD := liblustreapi.a $(LIBPTLCTL) $(LIBREADLINE) $(PTHREAD_LIBS) lctl_DEPENDENCIES := $(LIBPTLCTL) liblustreapi.a diff --git a/lustre/utils/lctl.c b/lustre/utils/lctl.c index 91e03cf..0184412 100644 --- a/lustre/utils/lctl.c +++ b/lustre/utils/lctl.c @@ -260,6 +260,44 @@ command_t cmdlist[] = { "rescan the system to filter out inactive MDT(s) for barrier\n" "usage: barrier_rescan [timeout (in seconds)]"}, + /* Snapshot commands */ + {"=== Snapshot ==", NULL, 0, "Snapshot management"}, + {"snapshot_create", jt_snapshot_create, 0, + "create the snapshot\n" + "usage: snapshot_create [-b | --barrier [on | off]]\n" + " [-c | --comment comment]\n" + " <-F | --fsname fsname>\n" + " [-h | --help] <-n | --name ssname>\n" + " [-r | --rsh remote_shell]\n" + " [-t | --timeout timeout]"}, + {"snapshot_destroy", jt_snapshot_destroy, 0, + "destroy the snapshot\n" + "usage: snapshot_destroy [-f | --force]\n" + " <-F | --fsname fsname> [-h | --help]\n" + " <-n | --name ssname>\n" + " [-r | --rsh remote_shell]"}, + {"snapshot_modify", jt_snapshot_modify, 0, + "modify the snapshot\n" + "usage: snapshot_modify [-c | --comment comment]\n" + " <-F | --fsname fsname> [-h | --help]\n" + " <-n | --name ssname> [-N | --new new_ssname]\n" + " [-r | --rsh remote_shell]"}, + {"snapshot_list", jt_snapshot_list, 0, + "query the snapshot(s)\n" + "usage: snapshot_list [-d | --detail]\n" + " <-F | --fsname fsname> [-h | --help]\n" + " [-n | --name ssname] [-r | --rsh remote_shell]"}, + {"snapshot_mount", jt_snapshot_mount, 0, + "mount the snapshot\n" + "usage: snapshot_mount <-F | --fsname fsname> [-h | --help]\n" + " <-n | --name ssname>\n" + " [-r | --rsh remote_shell]"}, + {"snapshot_umount", jt_snapshot_umount, 0, + "umount the snapshot\n" + "usage: snapshot_umount <-F | --fsname fsname> [-h | --help]\n" + " <-n | --name ssname>\n" + " [-r | --rsh remote_shell]"}, + /* Nodemap commands */ {"=== Nodemap ===", NULL, 0, "nodemap management"}, {"nodemap_activate", jt_nodemap_activate, 0, diff --git a/lustre/utils/lsnapshot.c b/lustre/utils/lsnapshot.c new file mode 100644 index 0000000..cf4f8f5 --- /dev/null +++ b/lustre/utils/lsnapshot.c @@ -0,0 +1,2568 @@ +/* + * GPL HEADER START + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 only, + * as published by the Free Software Foundation. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License version 2 for more details. A copy is + * included in the COPYING file that accompanied this code. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * GPL HEADER END + */ +/* + * Copyright (c) 2016, Intel Corporation. + * + * lustre/utils/lsnapshot.c + * + * Author: Fan, Yong + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "obdctl.h" + +#define SNAPSHOT_CONF_DIR "/etc/lsnapshot" +#define LDEV_CONF "/etc/ldev.conf" +#define SNAPSHOT_LOG "/var/log/lsnapshot.log" +#define SNAPSHOT_MAGIC "0x14F711B9" +#define MAX_BUF_SIZE 4096 + +enum snapshot_role { + SR_MGS = 0x0001, + SR_MDT = 0x0002, + SR_OST = 0x0004, +}; + +struct snapshot_target { + struct list_head st_list; + /* Target node name. */ + char *st_host; + /* Where the pool is */ + char *st_dir; + /* The target pool name on the target node. */ + char *st_pool; + /* The backend filesystem name against the target pool. */ + char *st_filesystem; + int st_role; + unsigned int st_index; + unsigned int st_gen; + int st_line; + int st_status; + pid_t st_pid; + bool st_ignored; +}; + +struct snapshot_instance { + struct list_head si_mdts_list; + struct list_head si_osts_list; + struct snapshot_target *si_mgs; + struct snapshot_target *si_mdt0; + FILE *si_log_fp; + char *si_rsh; + char *si_fsname; + char *si_ssname; + char *si_new_ssname; + char *si_comment; + int si_conf_fd; + int si_timeout; + bool si_barrier; + bool si_detail; + bool si_force; +}; + +static const char snapshot_rsh_default[] = "ssh"; + +static char *snapshot_role2name(char *name, enum snapshot_role role, + __u32 index) +{ + if (role & SR_MDT) + snprintf(name, 8, "MDT%04x", index); + else if (role & SR_MGS) + snprintf(name, 4, "MGS"); + else + snprintf(name, 8, "OST%04x", index); + + return name; +} + +#define SNAPSHOT_ADD_LOG(si, format, ...) \ +do { \ + char buf[MAX_BUF_SIZE]; \ + char *ptr; \ + time_t tt; \ + \ + memset(buf, 0, sizeof(buf)); \ + time(&tt); \ + snprintf(buf, sizeof(buf) - 1, "%s", ctime(&tt)); \ + ptr = strrchr(buf, '\n'); \ + if (ptr) \ + *ptr = '\0'; \ + \ + fprintf(si->si_log_fp, "%s (%d:%s:%d:%s:%s): "format, buf, \ + getpid(), __func__, __LINE__, si->si_fsname, \ + si->si_rsh, ## __VA_ARGS__); \ +} while (0) + +char *snapshot_fgets(FILE *fp, char *buf, int buflen) +{ + char *ptr; + + memset(buf, 0, buflen); + if (fgets(buf, buflen, fp) == NULL) + return NULL; + + ptr = strchr(buf, '\n'); + if (ptr) + *ptr = '\0'; + + return buf; +} + +static int snapshot_exec(const char *cmd) +{ + int rc; + + errno = 0; + + /* system() return value depends on both the system() general framework, + * such as whether fork()/exec() success or fail, and the real @cmd exec + * result. Especially, if the @cmd is remote command, we may cannot know + * the real failure. */ + rc = system(cmd); + /* fork()/exec() error */ + if (rc == -1) + return errno != 0 ? -errno : -1; + + if (WIFEXITED(rc)) { + rc = WEXITSTATUS(rc); + if (rc > 0) + rc = -rc; + } else if (WIFSIGNALED(rc)) { + rc = -EINTR; + } else { + /* all other known or unknown cases. */ + rc = -EFAULT; + } + + return rc; +} + +static int snapshot_load_conf_ldev(struct snapshot_instance *si, char *buf, + struct snapshot_target *st, char **role) +{ + char *label = NULL; + char *device = NULL; + char *ignore = NULL; + char *ptr; + char *ptr1; + int len; + int rc; + + rc = sscanf(buf, "%ms %ms %ms %ms", + &st->st_host, &ignore, &label, &device); + if (rc < 4) { + rc = -EINVAL; + goto out; + } + + free(ignore); + + /* Format of device: + * [md|zfs:][pool_dir/]/ */ + ptr = strchr(device, ':'); + if (ptr) { + ptr++; + if (strncmp(device, "zfs:", strlen("zfs:")) != 0) { + rc = -EINVAL; + goto out; + } + } else { + ptr = device; + } + + ptr1 = strrchr(ptr, '/'); + /* "ptr1 - ptr + 1 == strlen(ptr)" means '/' is at the tail. */ + if (!ptr1 || ptr1 == ptr || ptr1 - ptr + 1 == strlen(ptr)) { + rc = -EINVAL; + goto out; + } + + len = strlen(ptr1); + st->st_filesystem = malloc(len); + if (!st->st_filesystem) { + rc = -ENOMEM; + goto out; + } + + *ptr1 = '\0'; + strncpy(st->st_filesystem, ptr1 + 1, len - 1); + st->st_filesystem[len - 1] = '\0'; + + if (*ptr == '/') { + ptr1 = strrchr(ptr, '/'); + *ptr1 = '\0'; + len = strlen(ptr); + st->st_dir = malloc(len + 1); + if (!st->st_dir) { + rc = -ENOMEM; + goto out; + } + + strncpy(st->st_dir, ptr, len); + st->st_dir[len] = '\0'; + ptr = ptr1 + 1; + } + + len = strlen(ptr); + st->st_pool = malloc(len + 1); + if (!st->st_pool) { + rc = -ENOMEM; + goto out; + } + + strncpy(st->st_pool, ptr, len); + st->st_pool[len] = '\0'; + + /* Format of label: + * fsname- or */ + ptr = strchr(label, '-'); + if (ptr) { + if (strncmp(si->si_fsname, label, ptr - label) != 0) { + /* This line is NOT for current filesystem .*/ + rc = -EAGAIN; + goto out; + } + + ptr++; + } else { + ptr = label; + } + + if (strlen(ptr) < 3 || strlen(ptr) > 7) { + rc = -EINVAL; + goto out; + } + + *role = malloc(4); + if (!*role) { + rc = -ENOMEM; + goto out; + } + + strncpy(*role, ptr, 3); + (*role)[3] = 0; + ptr += 3; + len = 0; + while (isxdigit(ptr[len])) { + if (isdigit(ptr[len])) + st->st_index = + st->st_index * 16 + ptr[len] - '0'; + else if (isupper(ptr[len])) + st->st_index = + st->st_index * 16 + ptr[len] - 'A' + 10; + else + st->st_index = + st->st_index * 16 + ptr[len] - 'a' + 10; + len++; + } + + if (len == 0) { + if (strncasecmp(*role, "MGS", 3) != 0) + rc = -EINVAL; + else + rc = 0; + + goto out; + } + + if (!isxdigit(ptr[len]) && ptr[len] != '\0') { + rc = -EINVAL; + goto out; + } + +out: + if (label) + free(label); + if (device) + free(device); + + return rc; +} + +/** + * For old snasphot tools, the configration is in /etc/lsnapshot/${fsname}.conf, + * the format is: + * + * + * For example: + * + * host-mdt1 /tmp myfs-mdt1 mdt1 MGS,MDT 0 + * host-mdt2 /tmp myfs-mdt2 mdt2 MDT 1 + * host-ost1 /tmp myfs-ost1 ost1 OST 0 + * host-ost2 /tmp myfs-ost2 ost2 OST 1 + * + * + * For new snasphot tools, the configration is in /etc/ldev.conf, which is not + * only for snapshot, but also for other purpose. The format is: + * foreign/-