Estimate snapshot send size

This commit is contained in:
Faruk Kasumovic 2018-07-11 16:20:37 +02:00
parent 5d716ef4ec
commit 46ec88c0a8
2 changed files with 63 additions and 46 deletions

View File

@ -5,13 +5,18 @@ package zfs
// #include "common.h" // #include "common.h"
// #include "zpool.h" // #include "zpool.h"
// #include "zfs.h" // #include "zfs.h"
// #include <memory.h>
// #include <string.h>
import "C" import "C"
import ( import (
"fmt" "fmt"
"io/ioutil"
"os" "os"
"path" "path"
"regexp"
"strconv"
"strings" "strings"
"syscall" "time"
"unsafe" "unsafe"
) )
@ -23,11 +28,11 @@ type SendFlags struct {
Dedup bool Dedup bool
Props bool Props bool
DryRun bool DryRun bool
// Parsable bool Parsable bool
// Progress bool Progress bool
LargeBlock bool LargeBlock bool
EmbedData bool EmbedData bool
// Compress bool Compress bool
} }
type RecvFlags struct { type RecvFlags struct {
@ -58,11 +63,11 @@ func to_sendflags_t(flags *SendFlags) (cflags *C.sendflags_t) {
cflags.dedup = to_boolean_t(flags.Dedup) cflags.dedup = to_boolean_t(flags.Dedup)
cflags.props = to_boolean_t(flags.Props) cflags.props = to_boolean_t(flags.Props)
cflags.dryrun = to_boolean_t(flags.DryRun) cflags.dryrun = to_boolean_t(flags.DryRun)
// cflags.parsable = to_boolean_t(flags.Parsable) cflags.parsable = to_boolean_t(flags.Parsable)
// cflags.progress = to_boolean_t(flags.Progress) cflags.progress = to_boolean_t(flags.Progress)
cflags.largeblock = to_boolean_t(flags.LargeBlock) cflags.largeblock = to_boolean_t(flags.LargeBlock)
cflags.embed_data = to_boolean_t(flags.EmbedData) cflags.embed_data = to_boolean_t(flags.EmbedData)
// cflags.compress = to_boolean_t(flags.Compress) cflags.compress = to_boolean_t(flags.Compress)
return return
} }
@ -199,53 +204,61 @@ func (d *Dataset) SendFrom(FromName string, outf *os.File, flags SendFlags) (err
return return
} }
func (d *Dataset) SendSize(FromName string, flags SendFlags) (size uint64, err error) { // SendSize - estimate snapshot size to transfer
var porigin Property func (d *Dataset) SendSize(FromName string, flags SendFlags) (size int64, err error) {
var from Dataset var r, w *os.File
var dpath string errch := make(chan error)
if dpath, err = d.Path(); err != nil { defer func() {
select {
case <-errch:
default:
}
close(errch)
}()
flags.DryRun = true
flags.Verbose = true
flags.Progress = true
flags.Parsable = true
if r, w, err = os.Pipe(); err != nil {
return return
} }
zc := C.new_zfs_cmd() defer r.Close()
defer C.free(unsafe.Pointer(zc)) go func() {
dpath = strings.Split(dpath, "@")[0] var tmpe error
if len(FromName) > 0 { saveOut := C.dup(C.fileno(C.stdout))
if res := C.dup2(C.int(w.Fd()), C.fileno(C.stdout)); res < 0 {
tmpe = fmt.Errorf("Redirection of zfslib stdout failed %d", res)
} else {
tmpe = d.send(FromName, w, &flags)
C.fflush(C.stdout)
C.dup2(saveOut, C.fileno(C.stdout))
}
w.Close()
errch <- tmpe
}()
if FromName[0] == '#' || FromName[0] == '@' { r.SetReadDeadline(time.Now().Add(15 * time.Second))
FromName = dpath + FromName var data []byte
} if data, err = ioutil.ReadAll(r); err != nil {
porigin, _ = d.GetProperty(DatasetPropOrigin) return
if len(porigin.Value) > 0 && porigin.Value == FromName { }
FromName = "" // parse size
flags.FromOrigin = true var sizeRe *regexp.Regexp
} if sizeRe, err = regexp.Compile("size[ \t]*([0-9]+)"); err != nil {
if from, err = DatasetOpen(FromName); err != nil { return
}
matches := sizeRe.FindAllSubmatch(data, 3)
if len(matches) > 0 && len(matches[0]) > 1 {
if size, err = strconv.ParseInt(
string(matches[0][1]), 10, 64); err != nil {
return return
} }
zc.zc_fromobj = C.zfs_prop_get_int(from.list.zh, C.ZFS_PROP_OBJSETID)
from.Close()
} else {
zc.zc_fromobj = 0
} }
zc.zc_obj = C.uint64_t(to_boolean_t(flags.FromOrigin)) err = <-errch
zc.zc_sendobj = C.zfs_prop_get_int(d.list.zh, C.ZFS_PROP_OBJSETID)
zc.zc_guid = 1
zc.zc_flags = 0
if flags.LargeBlock {
zc.zc_flags |= C.LZC_SEND_FLAG_LARGE_BLOCK
}
if flags.EmbedData {
zc.zc_flags |= C.LZC_SEND_FLAG_EMBED_DATA
}
// C.estimate_ioctl(d.list.zhp, prevsnap_obj, to_boolean_t(flags.FromOrigin), lzc_send_flags, unsafe.Pointer(&size))
if ec, e := C.estimate_send_size(zc); ec != 0 {
err = fmt.Errorf("Failed to estimate send size. %s %d", e.Error(), e.(syscall.Errno))
}
size = uint64(zc.zc_objset_type)
return return
} }
// Receive - receive snapshot stream
func (d *Dataset) Receive(inf *os.File, flags RecvFlags) (err error) { func (d *Dataset) Receive(inf *os.File, flags RecvFlags) (err error) {
var dpath string var dpath string
if dpath, err = d.Path(); err != nil { if dpath, err = d.Path(); err != nil {

6
zfs.c
View File

@ -256,6 +256,10 @@ struct zfs_cmd *new_zfs_cmd(){
} }
int estimate_send_size(struct zfs_cmd *zc) { int estimate_send_size(struct zfs_cmd *zc) {
return zfs_ioctl(libzfsHandle, ZFS_IOC_SEND, zc); int rc = zfs_ioctl(libzfsHandle, ZFS_IOC_SEND, zc);
if (rc != 0) {
rc = errno;
}
return rc;
} }