zfs: extract filesystem version code to separate file & add filtering support

This commit is contained in:
Christian Schwarz 2017-06-22 21:49:14 +02:00
parent 2b6f3ece6b
commit c22190e981
4 changed files with 116 additions and 98 deletions

View File

@ -41,7 +41,7 @@ func (h Handler) HandleFilesystemVersionsRequest(r rpc.FilesystemVersionsRequest
h.Logger.Printf("allowed: %#v\n", r.Filesystem)
// find our versions
if versions, err = zfs.ZFSListFilesystemVersions(r.Filesystem); err != nil {
if versions, err = zfs.ZFSListFilesystemVersions(r.Filesystem, nil); err != nil {
h.Logger.Printf("our versions error: %#v\n", err)
return
}

View File

@ -114,7 +114,7 @@ func doPull(pull PullContext) (err error) {
var versions []zfs.FilesystemVersion
if m.LocalExists {
if versions, err = zfs.ZFSListFilesystemVersions(m.Local); err != nil {
if versions, err = zfs.ZFSListFilesystemVersions(m.Local, nil); err != nil {
log("cannot get filesystem versions, stopping...: %v\n", m.Local.ToString(), m, err)
return false
}

View File

@ -1,55 +1,9 @@
package zfs
import (
"bytes"
"errors"
"fmt"
"sort"
"strconv"
"strings"
)
type VersionType string
const (
Bookmark VersionType = "bookmark"
Snapshot = "snapshot"
)
func (t VersionType) DelimiterChar() string {
switch t {
case Bookmark:
return "#"
case Snapshot:
return "@"
default:
panic(fmt.Sprintf("unexpected VersionType %#v", t))
}
return ""
}
type FilesystemVersion struct {
Type VersionType
// Display name. Should not be used for identification, only for user output
Name string
// GUID as exported by ZFS. Uniquely identifies a snapshot across pools
Guid uint64
// The TXG in which the snapshot was created. For bookmarks,
// this is the GUID of the snapshot it was initially tied to.
CreateTXG uint64
}
func (v FilesystemVersion) ToAbsPath(p DatasetPath) string {
var b bytes.Buffer
b.WriteString(p.ToString())
b.WriteString(v.Type.DelimiterChar())
b.WriteString(v.Name)
return b.String()
}
type fsbyCreateTXG []FilesystemVersion
func (l fsbyCreateTXG) Len() int { return len(l) }
@ -105,56 +59,6 @@ type FilesystemDiff struct {
MRCAPathRight []FilesystemVersion
}
func ZFSListFilesystemVersions(fs DatasetPath) (res []FilesystemVersion, err error) {
var fieldLines [][]string
fieldLines, err = ZFSList(
[]string{"name", "guid", "createtxg"},
"-r", "-d", "1",
"-t", "bookmark,snapshot",
"-s", "createtxg", fs.ToString())
if err != nil {
return
}
res = make([]FilesystemVersion, len(fieldLines))
for i, line := range fieldLines {
if len(line[0]) < 3 {
err = errors.New(fmt.Sprintf("snapshot or bookmark name implausibly short: %s", line[0]))
return
}
snapSplit := strings.SplitN(line[0], "@", 2)
bookmarkSplit := strings.SplitN(line[0], "#", 2)
if len(snapSplit)*len(bookmarkSplit) != 2 {
err = errors.New(fmt.Sprintf("dataset cannot be snapshot and bookmark at the same time: %s", line[0]))
return
}
var v FilesystemVersion
if len(snapSplit) == 2 {
v.Name = snapSplit[1]
v.Type = Snapshot
} else {
v.Name = bookmarkSplit[1]
v.Type = Bookmark
}
if v.Guid, err = strconv.ParseUint(line[1], 10, 64); err != nil {
err = errors.New(fmt.Sprintf("cannot parse GUID: %s", err.Error()))
return
}
if v.CreateTXG, err = strconv.ParseUint(line[2], 10, 64); err != nil {
err = errors.New(fmt.Sprintf("cannot parse CreateTXG: %s", err.Error()))
return
}
res[i] = v
}
return
}
// we must assume left and right are ordered ascendingly by ZFS_PROP_CREATETXG and that
// names are unique (bas ZFS_PROP_GUID replacement)
func MakeFilesystemDiff(left, right []FilesystemVersion) (diff FilesystemDiff) {

114
zfs/versions.go Normal file
View File

@ -0,0 +1,114 @@
package zfs
import (
"bytes"
"errors"
"fmt"
"strconv"
"strings"
)
type VersionType string
const (
Bookmark VersionType = "bookmark"
Snapshot = "snapshot"
)
func (t VersionType) DelimiterChar() string {
switch t {
case Bookmark:
return "#"
case Snapshot:
return "@"
default:
panic(fmt.Sprintf("unexpected VersionType %#v", t))
}
return ""
}
type FilesystemVersion struct {
Type VersionType
// Display name. Should not be used for identification, only for user output
Name string
// GUID as exported by ZFS. Uniquely identifies a snapshot across pools
Guid uint64
// The TXG in which the snapshot was created. For bookmarks,
// this is the GUID of the snapshot it was initially tied to.
CreateTXG uint64
}
func (v FilesystemVersion) ToAbsPath(p DatasetPath) string {
var b bytes.Buffer
b.WriteString(p.ToString())
b.WriteString(v.Type.DelimiterChar())
b.WriteString(v.Name)
return b.String()
}
type FilesystemVersionFilter interface {
Filter(fsv FilesystemVersion) (accept bool, err error)
}
func ZFSListFilesystemVersions(fs DatasetPath, filter FilesystemVersionFilter) (res []FilesystemVersion, err error) {
var fieldLines [][]string
fieldLines, err = ZFSList(
[]string{"name", "guid", "createtxg"},
"-r", "-d", "1",
"-t", "bookmark,snapshot",
"-s", "createtxg", fs.ToString())
if err != nil {
return
}
res = make([]FilesystemVersion, 0, len(fieldLines))
for _, line := range fieldLines {
if len(line[0]) < 3 {
err = errors.New(fmt.Sprintf("snapshot or bookmark name implausibly short: %s", line[0]))
return
}
snapSplit := strings.SplitN(line[0], "@", 2)
bookmarkSplit := strings.SplitN(line[0], "#", 2)
if len(snapSplit)*len(bookmarkSplit) != 2 {
err = errors.New(fmt.Sprintf("dataset cannot be snapshot and bookmark at the same time: %s", line[0]))
return
}
var v FilesystemVersion
if len(snapSplit) == 2 {
v.Name = snapSplit[1]
v.Type = Snapshot
} else {
v.Name = bookmarkSplit[1]
v.Type = Bookmark
}
if v.Guid, err = strconv.ParseUint(line[1], 10, 64); err != nil {
err = errors.New(fmt.Sprintf("cannot parse GUID: %s", err.Error()))
return
}
if v.CreateTXG, err = strconv.ParseUint(line[2], 10, 64); err != nil {
err = errors.New(fmt.Sprintf("cannot parse CreateTXG: %s", err.Error()))
return
}
accept := true
if filter != nil {
accept, err = filter.Filter(v)
if err != nil {
err = fmt.Errorf("error executing filter: %s", err)
return nil, err
}
}
if accept {
res = append(res, v)
}
}
return
}