mirror of
https://github.com/zrepl/zrepl.git
synced 2025-01-22 06:09:45 +01:00
zfs: ability to specify sources for zfsGet
fix use for Placeholder, leave rest as previous behavior
This commit is contained in:
parent
975fdee217
commit
1323a30a0c
@ -248,7 +248,7 @@ func IsPlaceholder(p *DatasetPath, placeholderPropertyValue string) (isPlacehold
|
||||
|
||||
// for nonexistent FS, isPlaceholder == false && err == nil
|
||||
func ZFSIsPlaceholderFilesystem(p *DatasetPath) (isPlaceholder bool, err error) {
|
||||
props, err := ZFSGet(p, []string{ZREPL_PLACEHOLDER_PROPERTY_NAME})
|
||||
props, err := zfsGet(p.ToString(), []string{ZREPL_PLACEHOLDER_PROPERTY_NAME}, sourceLocal)
|
||||
if err == io.ErrUnexpectedEOF {
|
||||
// interpret this as an early exit of the zfs binary due to the fs not existing
|
||||
return false, nil
|
||||
|
@ -24,13 +24,13 @@ func ZFSGetReplicationCursor(fs *DatasetPath) (*FilesystemVersion, error) {
|
||||
|
||||
func ZFSSetReplicationCursor(fs *DatasetPath, snapname string) (guid uint64, err error) {
|
||||
snapPath := fmt.Sprintf("%s@%s", fs.ToString(), snapname)
|
||||
propsSnap, err := zfsGet(snapPath, []string{"createtxg", "guid"})
|
||||
propsSnap, err := zfsGet(snapPath, []string{"createtxg", "guid"}, sourceAny)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
snapGuid, err := strconv.ParseUint(propsSnap.Get("guid"), 10, 64)
|
||||
bookmarkPath := fmt.Sprintf("%s#%s", fs.ToString(), ReplicationCursorBookmarkName)
|
||||
propsBookmark, err := zfsGet(bookmarkPath, []string{"createtxg"})
|
||||
propsBookmark, err := zfsGet(bookmarkPath, []string{"createtxg"}, sourceAny)
|
||||
_, bookmarkNotExistErr := err.(*DatasetDoesNotExist)
|
||||
if err != nil && !bookmarkNotExistErr {
|
||||
return 0, err
|
||||
|
42
zfs/zfs.go
42
zfs/zfs.go
@ -487,7 +487,7 @@ func zfsSet(path string, props *ZFSProperties) (err error) {
|
||||
}
|
||||
|
||||
func ZFSGet(fs *DatasetPath, props []string) (*ZFSProperties, error) {
|
||||
return zfsGet(fs.ToString(), props)
|
||||
return zfsGet(fs.ToString(), props, sourceAny)
|
||||
}
|
||||
|
||||
var zfsGetDatasetDoesNotExistRegexp = regexp.MustCompile(`^cannot open '(\S+)': (dataset does not exist|no such pool or dataset)`)
|
||||
@ -498,8 +498,30 @@ type DatasetDoesNotExist struct {
|
||||
|
||||
func (d *DatasetDoesNotExist) Error() string { return fmt.Sprintf("dataset %q does not exist", d.Path) }
|
||||
|
||||
func zfsGet(path string, props []string) (*ZFSProperties, error) {
|
||||
args := []string{"get", "-Hp", "-o", "property,value", strings.Join(props, ","), path}
|
||||
type zfsPropertySource uint
|
||||
|
||||
const (
|
||||
sourceLocal zfsPropertySource = 1 << iota
|
||||
sourceDefault
|
||||
sourceInherited
|
||||
sourceNone
|
||||
sourceTemporary
|
||||
|
||||
sourceAny zfsPropertySource = ^zfsPropertySource(0)
|
||||
)
|
||||
|
||||
func (s zfsPropertySource) zfsGetSourceFieldPrefixes() []string {
|
||||
prefixes := make([]string, 0, 5)
|
||||
if s&sourceLocal != 0 {prefixes = append(prefixes, "local")}
|
||||
if s&sourceDefault != 0 {prefixes = append(prefixes, "default")}
|
||||
if s&sourceInherited != 0 {prefixes = append(prefixes, "inherited")}
|
||||
if s&sourceNone != 0 {prefixes = append(prefixes, "-")}
|
||||
if s&sourceTemporary != 0 { prefixes = append(prefixes, "temporary")}
|
||||
return prefixes
|
||||
}
|
||||
|
||||
func zfsGet(path string, props []string, allowedSources zfsPropertySource) (*ZFSProperties, error) {
|
||||
args := []string{"get", "-Hp", "-o", "property,value,source", strings.Join(props, ","), path}
|
||||
cmd := exec.Command(ZFS_BINARY, args...)
|
||||
stdout, err := cmd.Output()
|
||||
if err != nil {
|
||||
@ -524,12 +546,20 @@ func zfsGet(path string, props []string) (*ZFSProperties, error) {
|
||||
res := &ZFSProperties{
|
||||
make(map[string]string, len(lines)),
|
||||
}
|
||||
allowedPrefixes := allowedSources.zfsGetSourceFieldPrefixes()
|
||||
for _, line := range lines[:len(lines)-1] {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) != 2 {
|
||||
return nil, fmt.Errorf("zfs get did not return property value pairs")
|
||||
fields := strings.FieldsFunc(line, func(r rune) bool {
|
||||
return r == '\t'
|
||||
})
|
||||
if len(fields) != 3 {
|
||||
return nil, fmt.Errorf("zfs get did not return property,value,source tuples")
|
||||
}
|
||||
for _, p := range allowedPrefixes {
|
||||
if strings.HasPrefix(fields[2],p) {
|
||||
res.m[fields[0]] = fields[1]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
@ -30,3 +30,41 @@ func TestDatasetPathTrimNPrefixComps(t *testing.T) {
|
||||
p.TrimNPrefixComps((1))
|
||||
assert.True(t, p.Empty(), "empty trimming shouldn't do harm")
|
||||
}
|
||||
|
||||
func TestZFSPropertySource(t *testing.T) {
|
||||
|
||||
tcs := []struct{
|
||||
in zfsPropertySource
|
||||
exp []string
|
||||
}{
|
||||
{
|
||||
in: sourceAny,
|
||||
exp: []string{"local", "default", "inherited", "-", "temporary"},
|
||||
},
|
||||
{
|
||||
in: sourceTemporary,
|
||||
exp: []string{"temporary"},
|
||||
},
|
||||
{
|
||||
in: sourceLocal|sourceInherited,
|
||||
exp: []string{"local", "inherited"},
|
||||
},
|
||||
}
|
||||
|
||||
toSet := func(in []string) map[string]struct{} {
|
||||
m := make(map[string]struct{}, len(in))
|
||||
for _, s := range in {
|
||||
m[s] = struct{}{}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
|
||||
res := tc.in.zfsGetSourceFieldPrefixes()
|
||||
resSet := toSet(res)
|
||||
expSet := toSet(tc.exp)
|
||||
assert.Equal(t, expSet, resSet)
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user