prototype resumable send & recv
This commit is contained in:
@@ -87,6 +87,46 @@ func (h Handler) HandleIncrementalTransferRequest(r rpc.IncrementalTransferReque
|
||||
|
||||
}
|
||||
|
||||
func (h Handler) HandleResumeTransferRequest(r rpc.ResumeTransferRequest) (stream io.Reader, err error) {
|
||||
// decode receive_resume_token: zfs send -nvP <token>
|
||||
// A) use exit code to determine if could send (exit != 0 means could not send)
|
||||
// B) check ACL if the filesystem in toname field (it is a snapshot so strip its snapshot name first)
|
||||
// is allowed for the given client
|
||||
/*
|
||||
|
||||
# zfs send -nvt will print nvlist contents on ZoL and FreeBSD, should be good enough
|
||||
# will print regardless of whether it can send or not -> good for us
|
||||
# need clean way to extract that...
|
||||
# expect 'resume token contents:\nnvlist version: 0\n'
|
||||
# parse everything after that is tab-indented as key value pairs separated by = sign
|
||||
# => return dict
|
||||
|
||||
zfs send -nv -t 1-c6491acbe-c8-789c636064000310a500c4ec50360710e72765a526973030b0419460caa7a515a79630c001489e0d493ea9b224b5182451885d7f497e7a69660a0343f79b1b9a8a2b3db65b20c97382e5f312735319188a4bf28b12d353f5931293b34b0b8af5ab8a520b72f4d3f2f31d8a53f35220660300c1091dbe
|
||||
resume token contents:
|
||||
nvlist version: 0
|
||||
object = 0x6
|
||||
offset = 0x0
|
||||
bytes = 0x7100
|
||||
toguid = 0xb748a92129d8ec8b
|
||||
toname = storage/backups/zrepl/foo@send
|
||||
cannot resume send: 'storage/backups/zrepl/foo@send' used in the initial send no longer exists
|
||||
|
||||
zfs send -nvt 1-ebbbbea7e-f0-789c636064000310a501c49c50360710a715e5e7a69766a6304041f79b1b9a8a2b3db62b00d9ec48eaf293b252934b181858a0ea30e4d3d28a534b18e00024cf86249f5459925a0ca44fc861d75f920f71c59c5fdf6f7b3eea32b24092e704cbe725e6a632301497e41725a6a7ea27252667971614eb5715a516e4e8a7e5e73b14a7e6a51881cd06005a222749
|
||||
resume token contents:
|
||||
nvlist version: 0
|
||||
fromguid = 0xb748a92129d8ec8b #NOTE the fromguid field which is only in this one, so don't hardcode
|
||||
object = 0x4
|
||||
offset = 0x0
|
||||
bytes = 0x1ec8
|
||||
toguid = 0x328ae249dbf7fa9c
|
||||
toname = storage/backups/zrepl/foo@send2
|
||||
send from storage/backups/zrepl/foo@send to storage/backups/zrepl/foo@send2 estimated size is 1.02M
|
||||
|
||||
*/
|
||||
panic("not implemented")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (h Handler) HandlePullMeRequest(r rpc.PullMeRequest, clientIdentity string, client rpc.RPCRequester) (err error) {
|
||||
|
||||
// Check if we have a sink for this request
|
||||
|
||||
@@ -325,6 +325,87 @@ func doPull(pull PullContext) (err error) {
|
||||
log("local filesystem does not exist")
|
||||
case localState.Placeholder:
|
||||
log("local filesystem is marked as placeholder")
|
||||
case localState.ResumeToken != "":
|
||||
|
||||
log("local filesystem has receive_resume_token")
|
||||
if false { // TODO Check if managed resume is disabled (explain information leakage in docs!)
|
||||
log("managed resume tokens are disabled via zrepl config, assuming config change or external administrative action")
|
||||
log("policy forbids aborting the partial recv automatically, skipping this filesystem")
|
||||
log("for zrepl to resume replication for this dataset, administrators should finishing the recv manually or abort the recv via `zfs recv -A %s`", m.Local.ToString())
|
||||
return true // TODO right choice to allow children? ... should be, since the fs exists...
|
||||
}
|
||||
|
||||
log("decoding receive_resume_token")
|
||||
// TODO, see handler comments
|
||||
// TODO override logger with fromguid toguid
|
||||
|
||||
log("requesting resume of transfer")
|
||||
r := rpc.ResumeTransferRequest{m.Remote, localState.ResumeToken}
|
||||
stream, err := remote.ResumeTransferRequest(r)
|
||||
if err != nil {
|
||||
|
||||
log("resume transfer request failed: %s", err)
|
||||
rre, ok := err.(*rpc.ResumeTransferError)
|
||||
if !ok {
|
||||
log("skipping this filesystem, could be temporary issue")
|
||||
return true // TODO right choice to allow children
|
||||
}
|
||||
|
||||
// Determine if we should clear the resume token
|
||||
clearAndUseSnaps := false
|
||||
switch rre.Reason {
|
||||
case rpc.ResumeTransferErrorReasonNotImplemented:
|
||||
fallthrough
|
||||
case rpc.ResumeTransferErrorReasonDisabled:
|
||||
fallthrough
|
||||
case rpc.ResumeTransferErrorReasonZFSErrorPermanent:
|
||||
clearAndUseSnaps = true
|
||||
|
||||
case rpc.ResumeTransferErrorReasonZFSErrorMaybeTemporary:
|
||||
fallthrough
|
||||
default:
|
||||
clearAndUseSnaps = false
|
||||
}
|
||||
|
||||
if !clearAndUseSnaps {
|
||||
log("skipping this filesystem, error identified as temporary")
|
||||
return true // TODO right choice to allow children
|
||||
}
|
||||
|
||||
log("clearing local receive_resume_token")
|
||||
if err := zfs.ZFSRecvAbort(m.Local); err != nil {
|
||||
log("error clearing receive_resume_token: %s", err)
|
||||
return true // TODO right choice to allow children? the filesystem exists, so it should be ok
|
||||
}
|
||||
// TODO go back to top of function (put all this into separate function, then tail recursive call)
|
||||
// TODO return this_function()
|
||||
}
|
||||
|
||||
// TODO warning code duplication, see below. need to unify this
|
||||
log("invoking zfs receive")
|
||||
watcher := util.IOProgressWatcher{Reader: stream}
|
||||
watcher.KickOff(1*time.Second, func(p util.IOProgress) {
|
||||
log("progress on receive operation: %v bytes received", p.TotalRX)
|
||||
})
|
||||
|
||||
recvArgs := []string{"-u"}
|
||||
if localState.Placeholder {
|
||||
log("receive with forced rollback to replace placeholder filesystem")
|
||||
recvArgs = append(recvArgs, "-F")
|
||||
}
|
||||
|
||||
if err = zfs.ZFSRecv(m.Local, &watcher, recvArgs...); err != nil {
|
||||
log("error receiving stream: %s", err)
|
||||
return false
|
||||
}
|
||||
log("finished receiving stream, %v bytes total", watcher.Progress().TotalRX)
|
||||
|
||||
// TODO further problem property handling code must be duplicated here...
|
||||
|
||||
// TODO tail recursive call to this function, we might not be dony syncing yet
|
||||
// TODO return this_function()
|
||||
return true
|
||||
|
||||
default:
|
||||
log("local filesystem exists")
|
||||
log("requesting local filesystem versions")
|
||||
|
||||
Reference in New Issue
Block a user