
The three-way hand-off has a problem: there's no way to arrange for the state of the migration to be unambiguous in case of failure. If the final "disconnect" message is lost (as in, the destination never receives it whether it is sent by the sender or not), the destination has no option but to quit with an error status and let a human sort it out. However, at that point we can either arrange to have a .INCOMPLETE file still on disc or not - and it doesn't matter which we choose, we can still end up with dataloss by picking a specific calamity to have befallen the sender. Given this, it makes sense to fall back to a simpler protocol: just send all the data, then send a "disconnect" message. This has the same downside that we need a human to sort out specific failure cases, but combined with --unlink before sending "disconnect" (see next patch) it will always be possible for a human to disambiguate, whether the destination quit with an error status or not.
135 lines
2.7 KiB
Ruby
135 lines
2.7 KiB
Ruby
# encoding: utf-8
|
|
|
|
require 'socket'
|
|
require "timeout"
|
|
require 'flexnbd/constants'
|
|
|
|
module FlexNBD
|
|
class FakeSource
|
|
|
|
def initialize( addr, port, err_msg, source_addr=nil, source_port=0 )
|
|
timing_out( 2, err_msg ) do
|
|
@sock = if source_addr
|
|
TCPSocket.new( addr, port, source_addr, source_port )
|
|
else
|
|
TCPSocket.new( addr, port )
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
def close
|
|
@sock.close
|
|
end
|
|
|
|
|
|
def read_hello()
|
|
timing_out( FlexNBD::MS_HELLO_TIME_SECS,
|
|
"Timed out waiting for hello." ) do
|
|
fail "No hello." unless (hello = @sock.read( 152 )) &&
|
|
hello.length==152
|
|
|
|
magic_s = hello[0..7]
|
|
ignore_s= hello[8..15]
|
|
size_s = hello[16..23]
|
|
|
|
size_h, size_l = size_s.unpack("NN")
|
|
size = (size_h << 32) + size_l
|
|
|
|
return { :magic => magic_s, :size => size }
|
|
end
|
|
end
|
|
|
|
|
|
def send_request( type, handle="myhandle", from=0, len=0 )
|
|
fail "Bad handle" unless handle.length == 8
|
|
|
|
@sock.write( "\x25\x60\x95\x13" )
|
|
@sock.write( [type].pack( 'N' ) )
|
|
@sock.write( handle )
|
|
@sock.write( "\x0"*4 )
|
|
@sock.write( [from].pack( 'N' ) )
|
|
@sock.write( [len ].pack( 'N' ) )
|
|
end
|
|
|
|
|
|
def write_write_request( from, len, handle="myhandle" )
|
|
send_request( 1, handle, from, len )
|
|
end
|
|
|
|
|
|
def write_entrust_request( handle="myhandle" )
|
|
send_request( 65536, handle )
|
|
end
|
|
|
|
def write_disconnect_request( handle="myhandle" )
|
|
send_request( 2, handle )
|
|
end
|
|
|
|
def write_read_request( from, len, handle="myhandle" )
|
|
send_request( 0, "myhandle", from, len )
|
|
end
|
|
|
|
|
|
def write_data( data )
|
|
@sock.write( data )
|
|
end
|
|
|
|
|
|
# Handy utility
|
|
def read( from, len )
|
|
timing_out( 2, "Timed out reading" ) do
|
|
send_request( 0, "myhandle", from, len )
|
|
read_raw( len )
|
|
end
|
|
end
|
|
|
|
def read_raw( len )
|
|
@sock.read( len )
|
|
end
|
|
|
|
def send_mirror
|
|
read_hello()
|
|
write_write_request( 0, 8 )
|
|
write_data( "12345678" )
|
|
read_response()
|
|
write_disconnect_request()
|
|
close()
|
|
end
|
|
|
|
|
|
def read_response
|
|
magic = @sock.read(4)
|
|
error_s = @sock.read(4)
|
|
handle = @sock.read(8)
|
|
|
|
{
|
|
:magic => magic,
|
|
:error => error_s.unpack("N").first,
|
|
:handle => handle
|
|
}
|
|
end
|
|
|
|
|
|
def ensure_disconnected
|
|
Timeout.timeout( 2 ) do
|
|
@sock.read(1)
|
|
end
|
|
end
|
|
|
|
|
|
def timing_out( time, msg )
|
|
begin
|
|
Timeout.timeout( time ) do
|
|
yield
|
|
end
|
|
rescue Timeout::Error
|
|
$stderr.puts msg
|
|
exit 1
|
|
end
|
|
end
|
|
|
|
|
|
end # class FakeSource
|
|
end # module FlexNBD
|