Want 'zfs send -b'

This change implements 'zfs send -b' which can be used to send only
received property values whether or not they are overridden by local
settings.

This can be very useful during "restore" operations from a backup pool
because it allows to send only the property values originally sent
from the backup source, even though they were later modified on the
destination either by a 'zfs set' operation, explicit 'zfs inherit' or
overridden during the receive process via 'zfs receive -o|-x'.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: loli10K <ezomori.nozomu@gmail.com>
Closes #7156
This commit is contained in:
LOLi
2018-02-21 21:32:06 +01:00
committed by Brian Behlendorf
parent b0918402dc
commit faa97c1619
9 changed files with 257 additions and 120 deletions
+1 -1
View File
@@ -223,7 +223,7 @@ tags = ['functional', 'cli_root', 'zfs_rollback']
tests = ['zfs_send_001_pos', 'zfs_send_002_pos', 'zfs_send_003_pos',
'zfs_send_004_neg', 'zfs_send_005_pos', 'zfs_send_006_pos',
'zfs_send_007_pos', 'zfs_send_encrypted', 'zfs_send_raw',
'zfs_send_sparse']
'zfs_send_sparse', 'zfs_send-b']
tags = ['functional', 'cli_root', 'zfs_send']
[tests/functional/cli_root/zfs_set]
@@ -45,105 +45,6 @@ function cleanup
log_must zfs destroy -r -f $dest
}
#
# Verify property $2 is set from source $4 on dataset $1 and has value $3.
#
# $1 checked dataset
# $2 user property
# $3 property value
# $4 source
#
function check_prop_source
{
typeset dataset="$1"
typeset prop="$2"
typeset value="$3"
typeset source="$4"
typeset chk_value=$(get_prop "$prop" "$dataset")
typeset chk_source=$(get_source "$prop" "$dataset")
if [[ "$chk_value" != "$value" || "$chk_source" != "$4" ]]
then
return 1
else
return 0
fi
}
#
# Verify target dataset $1 inherit property $2 from dataset $3.
#
# $1 checked dataset
# $2 property
# $3 inherited dataset
#
function check_prop_inherit
{
typeset checked_dtst="$1"
typeset prop="$2"
typeset inherited_dtst="$3"
typeset inherited_value=$(get_prop "$prop" "$inherited_dtst")
typeset value=$(get_prop "$prop" "$checked_dtst")
typeset source=$(get_source "$prop" "$checked_dtst")
if [[ "$value" != "$inherited_value" || \
"$source" != "inherited from $inherited_dtst" ]]
then
return 1
else
return 0
fi
}
#
# Verify property $2 received value on dataset $1 has value $3
#
# $1 checked dataset
# $2 property name
# $3 checked value
#
function check_prop_received
{
typeset dataset="$1"
typeset prop="$2"
typeset value="$3"
received=$(zfs get -H -o received "$prop" "$dataset")
if (($? != 0)); then
log_fail "Unable to get $prop received value for dataset " \
"$dataset"
fi
if [[ "$received" == "$value" ]]
then
return 0
else
return 1
fi
}
#
# Verify user property $2 is not set on dataset $1
#
# $1 checked dataset
# $2 property name
#
function check_prop_missing
{
typeset dataset="$1"
typeset prop="$2"
value=$(zfs get -H -o value "$prop" "$dataset")
if (($? != 0)); then
log_fail "Unable to get $prop value for dataset $dataset"
fi
if [[ "-" == "$value" ]]
then
return 0
else
return 1
fi
}
log_assert "ZFS receive property override and exclude options work as expected."
log_onexit cleanup
@@ -12,4 +12,5 @@ dist_pkgdata_SCRIPTS = \
zfs_send_007_pos.ksh \
zfs_send_encrypted.ksh \
zfs_send_raw.ksh \
zfs_send_sparse.ksh
zfs_send_sparse.ksh \
zfs_send-b.ksh
@@ -0,0 +1,103 @@
#!/bin/ksh -p
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
#
# Copyright 2018, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
#
. $STF_SUITE/include/libtest.shlib
. $STF_SUITE/tests/functional/cli_root/zfs_set/zfs_set_common.kshlib
#
# DESCRIPTION:
# 'zfs send -b' should works as expected.
#
# STRATEGY:
# 1. Create a source dataset and set some properties
# 2. Verify command line options interact with '-b' correctly
# 3. Send the dataset and its properties to a new "backup" destination
# 4. Set some properties on the new "backup" dataset
# 5. Restore the "backup" dataset to a new destination
# 6. Verify only original (received) properties are sent from "backup"
#
verify_runnable "both"
function cleanup
{
for ds in "$SENDFS" "$BACKUP" "$RESTORE"; do
datasetexists $ds && log_must zfs destroy -r $ds
done
}
log_assert "'zfs send -b' should work as expected."
log_onexit cleanup
SENDFS="$TESTPOOL/sendfs"
BACKUP="$TESTPOOL/backup"
RESTORE="$TESTPOOL/restore"
# 1. Create a source dataset and set some properties
log_must zfs create $SENDFS
log_must zfs snapshot "$SENDFS@s1"
log_must zfs bookmark "$SENDFS@s1" "$SENDFS#bm"
log_must zfs snapshot "$SENDFS@s2"
log_must zfs set "compression=gzip" $SENDFS
log_must zfs set "org.zfsonlinux:prop=val" $SENDFS
log_must zfs set "org.zfsonlinux:snapprop=val" "$SENDFS@s1"
# 2. Verify command line options interact with '-b' correctly
typeset opts=("" "p" "Rp" "cew" "nv" "D" "DLPRcenpvw")
for opt in ${opts[@]}; do
log_must eval "zfs send -b$opt $SENDFS@s1 > /dev/null"
log_must eval "zfs send -b$opt -i $SENDFS@s1 $SENDFS@s2 > /dev/null"
log_must eval "zfs send -b$opt -I $SENDFS@s1 $SENDFS@s2 > /dev/null"
done
for opt in ${opts[@]}; do
log_mustnot eval "zfs send -b$opt $SENDFS > /dev/null"
log_mustnot eval "zfs send -b$opt $SENDFS#bm > /dev/null"
log_mustnot eval "zfs send -b$opt -i $SENDFS#bm $SENDFS@s2 > /dev/null"
done
# Do 3..6 in a loop to verify various combination of "zfs send" options
typeset opts=("" "p" "R" "pR" "cew")
for opt in ${opts[@]}; do
# 3. Send the dataset and its properties to a new "backup" destination
# NOTE: only need to send properties (-p) here
log_must eval "zfs send -p $SENDFS@s1 | zfs recv $BACKUP"
# 4. Set some properties on the new "backup" dataset
# NOTE: override "received" values and set some new properties as well
log_must zfs set "compression=lz4" $BACKUP
log_must zfs set "exec=off" $BACKUP
log_must zfs set "org.zfsonlinux:prop=newval" $BACKUP
log_must zfs set "org.zfsonlinux:newprop=newval" $BACKUP
log_must zfs set "org.zfsonlinux:snapprop=newval" "$BACKUP@s1"
log_must zfs set "org.zfsonlinux:newsnapprop=newval" "$BACKUP@s1"
# 5. Restore the "backup" dataset to a new destination
log_must eval "zfs send -b$opt $BACKUP@s1 | zfs recv $RESTORE"
# 6. Verify only original (received) properties are sent from "backup"
log_must eval "check_prop_source $RESTORE compression gzip received"
log_must eval "check_prop_source $RESTORE org.zfsonlinux:prop val received"
log_must eval "check_prop_source $RESTORE@s1 org.zfsonlinux:snapprop val received"
log_must eval "check_prop_source $RESTORE exec on default"
log_must eval "check_prop_missing $RESTORE org.zfsonlinux:newprop"
log_must eval "check_prop_missing $RESTORE@s1 org.zfsonlinux:newsnapprop"
# cleanup
log_must zfs destroy -r $BACKUP
log_must zfs destroy -r $RESTORE
done
log_pass "'zfs send -b' works as expected."
@@ -267,3 +267,110 @@ function get_source
echo "$source"
}
#
# Verify property $2 is set from source $4 on dataset $1 and has value $3.
#
# $1 checked dataset
# $2 user property
# $3 property value
# $4 source
#
# Returns: 0 if both expected source and value match, 1 otherwise
#
function check_prop_source
{
typeset dataset="$1"
typeset prop="$2"
typeset value="$3"
typeset source="$4"
typeset chk_value=$(get_prop "$prop" "$dataset")
typeset chk_source=$(get_source "$prop" "$dataset")
if [[ "$chk_value" != "$value" || "$chk_source" != "$4" ]]
then
return 1
else
return 0
fi
}
#
# Verify target dataset $1 inherit property $2 from dataset $3.
#
# $1 checked dataset
# $2 property
# $3 inherited dataset
#
# Returns: 0 if property has expected value and is inherited, 1 otherwise
#
function check_prop_inherit
{
typeset checked_dtst="$1"
typeset prop="$2"
typeset inherited_dtst="$3"
typeset inherited_value=$(get_prop "$prop" "$inherited_dtst")
typeset value=$(get_prop "$prop" "$checked_dtst")
typeset source=$(get_source "$prop" "$checked_dtst")
if [[ "$value" != "$inherited_value" || \
"$source" != "inherited from $inherited_dtst" ]]
then
return 1
else
return 0
fi
}
#
# Verify property $2 received value on dataset $1 has value $3
#
# $1 checked dataset
# $2 property name
# $3 checked value
#
# Returns: 0 if property has expected value and is received, 1 otherwise
#
function check_prop_received
{
typeset dataset="$1"
typeset prop="$2"
typeset value="$3"
received=$(zfs get -H -o received "$prop" "$dataset")
if (($? != 0)); then
log_fail "Unable to get $prop received value for dataset " \
"$dataset"
fi
if [[ "$received" == "$value" ]]
then
return 0
else
return 1
fi
}
#
# Verify user property $2 is not set on dataset $1
#
# $1 checked dataset
# $2 property name
#
# Returns: 0 if property is missing (not set), 1 otherwise
#
function check_prop_missing
{
typeset dataset="$1"
typeset prop="$2"
value=$(zfs get -H -o value "$prop" "$dataset")
if (($? != 0)); then
log_fail "Unable to get $prop value for dataset $dataset"
fi
if [[ "-" == "$value" ]]
then
return 0
else
return 1
fi
}