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
|
// for nonexistent FS, isPlaceholder == false && err == nil
|
||||||
func ZFSIsPlaceholderFilesystem(p *DatasetPath) (isPlaceholder bool, err error) {
|
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 {
|
if err == io.ErrUnexpectedEOF {
|
||||||
// interpret this as an early exit of the zfs binary due to the fs not existing
|
// interpret this as an early exit of the zfs binary due to the fs not existing
|
||||||
return false, nil
|
return false, nil
|
||||||
|
@ -24,13 +24,13 @@ func ZFSGetReplicationCursor(fs *DatasetPath) (*FilesystemVersion, error) {
|
|||||||
|
|
||||||
func ZFSSetReplicationCursor(fs *DatasetPath, snapname string) (guid uint64, err error) {
|
func ZFSSetReplicationCursor(fs *DatasetPath, snapname string) (guid uint64, err error) {
|
||||||
snapPath := fmt.Sprintf("%s@%s", fs.ToString(), snapname)
|
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 {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
snapGuid, err := strconv.ParseUint(propsSnap.Get("guid"), 10, 64)
|
snapGuid, err := strconv.ParseUint(propsSnap.Get("guid"), 10, 64)
|
||||||
bookmarkPath := fmt.Sprintf("%s#%s", fs.ToString(), ReplicationCursorBookmarkName)
|
bookmarkPath := fmt.Sprintf("%s#%s", fs.ToString(), ReplicationCursorBookmarkName)
|
||||||
propsBookmark, err := zfsGet(bookmarkPath, []string{"createtxg"})
|
propsBookmark, err := zfsGet(bookmarkPath, []string{"createtxg"}, sourceAny)
|
||||||
_, bookmarkNotExistErr := err.(*DatasetDoesNotExist)
|
_, bookmarkNotExistErr := err.(*DatasetDoesNotExist)
|
||||||
if err != nil && !bookmarkNotExistErr {
|
if err != nil && !bookmarkNotExistErr {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
44
zfs/zfs.go
44
zfs/zfs.go
@ -487,7 +487,7 @@ func zfsSet(path string, props *ZFSProperties) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ZFSGet(fs *DatasetPath, props []string) (*ZFSProperties, 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)`)
|
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 (d *DatasetDoesNotExist) Error() string { return fmt.Sprintf("dataset %q does not exist", d.Path) }
|
||||||
|
|
||||||
func zfsGet(path string, props []string) (*ZFSProperties, error) {
|
type zfsPropertySource uint
|
||||||
args := []string{"get", "-Hp", "-o", "property,value", strings.Join(props, ","), path}
|
|
||||||
|
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...)
|
cmd := exec.Command(ZFS_BINARY, args...)
|
||||||
stdout, err := cmd.Output()
|
stdout, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -524,12 +546,20 @@ func zfsGet(path string, props []string) (*ZFSProperties, error) {
|
|||||||
res := &ZFSProperties{
|
res := &ZFSProperties{
|
||||||
make(map[string]string, len(lines)),
|
make(map[string]string, len(lines)),
|
||||||
}
|
}
|
||||||
|
allowedPrefixes := allowedSources.zfsGetSourceFieldPrefixes()
|
||||||
for _, line := range lines[:len(lines)-1] {
|
for _, line := range lines[:len(lines)-1] {
|
||||||
fields := strings.Fields(line)
|
fields := strings.FieldsFunc(line, func(r rune) bool {
|
||||||
if len(fields) != 2 {
|
return r == '\t'
|
||||||
return nil, fmt.Errorf("zfs get did not return property value pairs")
|
})
|
||||||
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
res.m[fields[0]] = fields[1]
|
|
||||||
}
|
}
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
@ -30,3 +30,41 @@ func TestDatasetPathTrimNPrefixComps(t *testing.T) {
|
|||||||
p.TrimNPrefixComps((1))
|
p.TrimNPrefixComps((1))
|
||||||
assert.True(t, p.Empty(), "empty trimming shouldn't do harm")
|
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