[#289] zfs: workaround for OpenZFS 0.7 dry send info with zero estimated size

fixes #289
This commit is contained in:
Christian Schwarz 2020-07-26 19:22:03 +02:00
parent 02db5994fe
commit 0ee7a49d31
2 changed files with 56 additions and 9 deletions

View File

@ -860,9 +860,9 @@ type DrySendInfo struct {
var ( var (
// keep same number of capture groups for unmarshalInfoLine homogeneity // keep same number of capture groups for unmarshalInfoLine homogeneity
sendDryRunInfoLineRegexFull = regexp.MustCompile(`^(full)\t()([^\t]+@[^\t]+)\t([0-9]+)$`) sendDryRunInfoLineRegexFull = regexp.MustCompile(`^(?P<type>full)\t()(?P<to>[^\t]+@[^\t]+)(\t(?P<size>[0-9]+))?$`)
// cannot enforce '[#@]' in incremental source, see test cases // cannot enforce '[#@]' in incremental source, see test cases
sendDryRunInfoLineRegexIncremental = regexp.MustCompile(`^(incremental)\t([^\t]+)\t([^\t]+@[^\t]+)\t([0-9]+)$`) sendDryRunInfoLineRegexIncremental = regexp.MustCompile(`^(?P<type>incremental)\t(?P<from>[^\t]+)\t(?P<to>[^\t]+@[^\t]+)(\t(?P<size>[0-9]+))?$`)
) )
// see test cases for example output // see test cases for example output
@ -890,30 +890,44 @@ func (s *DrySendInfo) unmarshalInfoLine(l string) (regexMatched bool, err error)
mFull := sendDryRunInfoLineRegexFull.FindStringSubmatch(l) mFull := sendDryRunInfoLineRegexFull.FindStringSubmatch(l)
mInc := sendDryRunInfoLineRegexIncremental.FindStringSubmatch(l) mInc := sendDryRunInfoLineRegexIncremental.FindStringSubmatch(l)
var matchingExpr *regexp.Regexp
var m []string var m []string
if mFull == nil && mInc == nil { if mFull == nil && mInc == nil {
return false, nil return false, nil
} else if mFull != nil && mInc != nil { } else if mFull != nil && mInc != nil {
panic(fmt.Sprintf("ambiguous ZFS dry send output: %q", l)) panic(fmt.Sprintf("ambiguous ZFS dry send output: %q", l))
} else if mFull != nil { } else if mFull != nil {
m = mFull matchingExpr, m = sendDryRunInfoLineRegexFull, mFull
} else if mInc != nil { } else if mInc != nil {
m = mInc matchingExpr, m = sendDryRunInfoLineRegexIncremental, mInc
} }
s.Type, err = DrySendTypeFromString(m[1])
fields := make(map[string]string, matchingExpr.NumSubexp())
for i, name := range matchingExpr.SubexpNames() {
if i != 0 {
fields[name] = m[i]
}
}
s.Type, err = DrySendTypeFromString(fields["type"])
if err != nil { if err != nil {
return true, err return true, err
} }
s.From = m[2] s.From = fields["from"]
s.To = m[3] s.To = fields["to"]
toFS, _, _, err := DecomposeVersionString(s.To) toFS, _, _, err := DecomposeVersionString(s.To)
if err != nil { if err != nil {
return true, fmt.Errorf("'to' is not a valid filesystem version: %s", err) return true, fmt.Errorf("'to' is not a valid filesystem version: %s", err)
} }
s.Filesystem = toFS s.Filesystem = toFS
s.SizeEstimate, err = strconv.ParseInt(m[4], 10, 64) if fields["size"] == "" {
// workaround for OpenZFS 0.7 prior to https://github.com/openzfs/zfs/commit/835db58592d7d947e5818eb7281882e2a46073e0#diff-66bd524398bcd2ac70d90925ab6d8073L1245
// see https://github.com/zrepl/zrepl/issues/289
fields["size"] = "0"
}
s.SizeEstimate, err = strconv.ParseInt(fields["size"], 10, 64)
if err != nil { if err != nil {
return true, fmt.Errorf("cannot not parse size: %s", err) return true, fmt.Errorf("cannot not parse size: %s", err)
} }

View File

@ -136,6 +136,18 @@ size 10511856
fullNoToken := ` fullNoToken := `
full zroot/test/a@3 10518512 full zroot/test/a@3 10518512
size 10518512 size 10518512
`
// zero-length incremental send on ZoL 0.7.12
// (it omits the size field as well as the size line if size is 0)
// see https://github.com/zrepl/zrepl/issues/289
// fixed in https://github.com/openzfs/zfs/commit/835db58592d7d947e5818eb7281882e2a46073e0#diff-66bd524398bcd2ac70d90925ab6d8073L1245
incZeroSized_0_7_12 := `
incremental p1 with/ spaces d1@1 with space p1 with/ spaces d1@2 with space
`
fullZeroSized_0_7_12 := `
full p1 with/ spaces d1@2 with space
` `
fullWithSpaces := "\nfull\tpool1/otherjob/ds with spaces@blaffoo\t12912\nsize\t12912\n" fullWithSpaces := "\nfull\tpool1/otherjob/ds with spaces@blaffoo\t12912\nsize\t12912\n"
@ -243,6 +255,25 @@ size 10518512
SizeEstimate: 624, SizeEstimate: 624,
}, },
}, },
{
name: "incrementalZeroSizedOpenZFS_pre0.7.12", in: incZeroSized_0_7_12,
exp: &DrySendInfo{
Type: DrySendTypeIncremental,
Filesystem: "p1 with/ spaces d1",
From: "p1 with/ spaces d1@1 with space",
To: "p1 with/ spaces d1@2 with space",
SizeEstimate: 0,
},
},
{
name: "fullZeroSizedOpenZFS_pre0.7.12", in: fullZeroSized_0_7_12,
exp: &DrySendInfo{
Type: DrySendTypeFull,
Filesystem: "p1 with/ spaces d1",
To: "p1 with/ spaces d1@2 with space",
SizeEstimate: 0,
},
},
} }
for _, tc := range tcs { for _, tc := range tcs {
@ -251,10 +282,12 @@ size 10518512
in := tc.in[1:] // strip first newline in := tc.in[1:] // strip first newline
var si DrySendInfo var si DrySendInfo
err := si.unmarshalZFSOutput([]byte(in)) err := si.unmarshalZFSOutput([]byte(in))
t.Logf("%#v", &si)
t.Logf("err=%T %s", err, err)
if tc.expErr { if tc.expErr {
assert.Error(t, err) assert.Error(t, err)
} }
t.Logf("%#v", &si)
if tc.exp != nil { if tc.exp != nil {
assert.Equal(t, tc.exp, &si) assert.Equal(t, tc.exp, &si)
} }