Automated merge with file:///home/lupine/Development/bigv-repos/flexnbd-c-sockutil
This commit is contained in:
@@ -21,6 +21,13 @@ class Environment
|
||||
@fake_pid = nil
|
||||
end
|
||||
|
||||
def proxy1(port=@port2)
|
||||
@nbd1.proxy(@ip, port)
|
||||
end
|
||||
def proxy2(port=@port1)
|
||||
@nbd2.proxy(@ip, port)
|
||||
end
|
||||
|
||||
|
||||
def serve1(*acl)
|
||||
@nbd1.serve(@filename1, *acl)
|
||||
|
@@ -13,10 +13,8 @@ addr, port = *ARGV
|
||||
|
||||
client = FakeSource.new( addr, port, "Timed out connecting", "127.0.0.6" )
|
||||
sleep( 0.25 )
|
||||
client.ensure_disconnected
|
||||
|
||||
rsp = client.disconnected? ? 0 : 1
|
||||
client.close
|
||||
exit(0)
|
||||
|
||||
|
||||
exit(rsp)
|
||||
|
||||
|
@@ -8,6 +8,10 @@ class FileWriter
|
||||
@pattern = ""
|
||||
end
|
||||
|
||||
def size
|
||||
@blocksize * @pattern.split("").size
|
||||
end
|
||||
|
||||
# We write in fixed block sizes, given by "blocksize"
|
||||
# _ means skip a block
|
||||
# 0 means write a block full of zeroes
|
||||
|
@@ -241,6 +241,15 @@ module FlexNBD
|
||||
"#{acl.join(' ')}"
|
||||
end
|
||||
|
||||
def proxy_cmd( connect_ip, connect_port )
|
||||
"#{bin} proxy "\
|
||||
"--addr #{ip} "\
|
||||
"--port #{port} "\
|
||||
"--conn-addr #{connect_ip} "\
|
||||
"--conn-port #{connect_port} "\
|
||||
"#{@debug}"
|
||||
end
|
||||
|
||||
|
||||
def read_cmd( offset, length )
|
||||
"#{bin} read "\
|
||||
@@ -319,6 +328,7 @@ module FlexNBD
|
||||
sleep 0.1
|
||||
end
|
||||
|
||||
|
||||
start_wait_thread( @pid )
|
||||
at_exit { kill }
|
||||
end
|
||||
@@ -336,6 +346,31 @@ module FlexNBD
|
||||
run_serve_cmd( listen_cmd( file, acl ) )
|
||||
end
|
||||
|
||||
def tcp_server_open?
|
||||
# raises if the other side doesn't accept()
|
||||
sock = TCPSocket.new(ip, port) rescue nil
|
||||
|
||||
success = !!sock
|
||||
( sock.close rescue nil) if sock
|
||||
success
|
||||
end
|
||||
|
||||
def proxy( connect_ip, connect_port )
|
||||
cmd = proxy_cmd( connect_ip, connect_port )
|
||||
debug( cmd )
|
||||
|
||||
@pid = @executor.run( cmd )
|
||||
|
||||
until tcp_server_open?
|
||||
pid, status = Process.wait2(@pid, Process::WNOHANG)
|
||||
raise "server did not start (#{cmd})" if pid
|
||||
sleep 0.1
|
||||
end
|
||||
|
||||
start_wait_thread( @pid )
|
||||
at_exit { kill }
|
||||
end
|
||||
|
||||
|
||||
def start_wait_thread( pid )
|
||||
@wait_thread = Thread.start do
|
||||
|
@@ -32,6 +32,9 @@ module FlexNBD
|
||||
end
|
||||
|
||||
read_constants()
|
||||
|
||||
REQUEST_MAGIC = "\x25\x60\x95\x13" unless defined?(REQUEST_MAGIC)
|
||||
REPLY_MAGIC = "\x67\x44\x66\x98" unless defined?(REPLY_MAGIC)
|
||||
|
||||
end # module FlexNBD
|
||||
|
||||
|
||||
|
@@ -56,8 +56,6 @@ module FlexNBD
|
||||
}
|
||||
end
|
||||
|
||||
REPLY_MAGIC="\x67\x44\x66\x98"
|
||||
|
||||
def write_error( handle )
|
||||
write_reply( handle, 1 )
|
||||
end
|
||||
@@ -76,7 +74,7 @@ module FlexNBD
|
||||
if opts[:magic] == :wrong
|
||||
write_rand( @sock, 4 )
|
||||
else
|
||||
@sock.write( REPLY_MAGIC )
|
||||
@sock.write( ::FlexNBD::REPLY_MAGIC )
|
||||
end
|
||||
|
||||
@sock.write( [err].pack("N") )
|
||||
@@ -93,6 +91,10 @@ module FlexNBD
|
||||
@sock.read( len )
|
||||
end
|
||||
|
||||
def write_data( len )
|
||||
@sock.write( len )
|
||||
end
|
||||
|
||||
|
||||
def self.parse_be64(str)
|
||||
raise "String is the wrong length: 8 bytes expected (#{str.length} received)" unless
|
||||
@@ -161,3 +163,4 @@ module FlexNBD
|
||||
|
||||
end # module FakeDest
|
||||
end # module FlexNBD
|
||||
|
||||
|
@@ -30,7 +30,7 @@ module FlexNBD
|
||||
|
||||
|
||||
def read_hello()
|
||||
timing_out( FlexNBD::MS_HELLO_TIME_SECS,
|
||||
timing_out( ::FlexNBD::MS_HELLO_TIME_SECS,
|
||||
"Timed out waiting for hello." ) do
|
||||
fail "No hello." unless (hello = @sock.read( 152 )) &&
|
||||
hello.length==152
|
||||
@@ -47,15 +47,14 @@ module FlexNBD
|
||||
end
|
||||
|
||||
|
||||
def send_request( type, handle="myhandle", from=0, len=0 )
|
||||
def send_request( type, handle="myhandle", from=0, len=0, magic=REQUEST_MAGIC )
|
||||
fail "Bad handle" unless handle.length == 8
|
||||
|
||||
@sock.write( "\x25\x60\x95\x13" )
|
||||
@sock.write( magic )
|
||||
@sock.write( [type].pack( 'N' ) )
|
||||
@sock.write( handle )
|
||||
@sock.write( "\x0"*4 )
|
||||
@sock.write( [from].pack( 'N' ) )
|
||||
@sock.write( [len ].pack( 'N' ) )
|
||||
@sock.write( [n64( from )].pack( 'q' ) )
|
||||
@sock.write( [len].pack( 'N' ) )
|
||||
end
|
||||
|
||||
|
||||
@@ -122,10 +121,10 @@ module FlexNBD
|
||||
end
|
||||
|
||||
|
||||
def ensure_disconnected
|
||||
Timeout.timeout( 2 ) do
|
||||
@sock.read(1)
|
||||
end
|
||||
def disconnected?
|
||||
result = nil
|
||||
Timeout.timeout( 2 ) { result = ( @sock.read(1) == nil ) }
|
||||
result
|
||||
end
|
||||
|
||||
|
||||
@@ -140,6 +139,22 @@ module FlexNBD
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# take a 64-bit number, turn it upside down (due to :
|
||||
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/11920
|
||||
# )
|
||||
def n64(b)
|
||||
((b & 0xff00000000000000) >> 56) |
|
||||
((b & 0x00ff000000000000) >> 40) |
|
||||
((b & 0x0000ff0000000000) >> 24) |
|
||||
((b & 0x000000ff00000000) >> 8) |
|
||||
((b & 0x00000000ff000000) << 8) |
|
||||
((b & 0x0000000000ff0000) << 24) |
|
||||
((b & 0x000000000000ff00) << 40) |
|
||||
((b & 0x00000000000000ff) << 56)
|
||||
end
|
||||
|
||||
end # class FakeSource
|
||||
end # module FlexNBD
|
||||
|
||||
|
199
tests/acceptance/test_proxy_mode.rb
Normal file
199
tests/acceptance/test_proxy_mode.rb
Normal file
@@ -0,0 +1,199 @@
|
||||
require 'test/unit'
|
||||
require 'environment'
|
||||
require 'flexnbd/fake_source'
|
||||
require 'flexnbd/fake_dest'
|
||||
|
||||
class TestProxyMode < Test::Unit::TestCase
|
||||
def setup
|
||||
super
|
||||
@env = Environment.new
|
||||
@env.writefile1( "0" * 16 )
|
||||
end
|
||||
|
||||
def teardown
|
||||
@env.cleanup
|
||||
super
|
||||
end
|
||||
|
||||
def with_proxied_client( override_size = nil )
|
||||
@env.serve1 unless @server_up
|
||||
@env.proxy2 unless @proxy_up
|
||||
@env.nbd2.can_die(0)
|
||||
client = FlexNBD::FakeSource.new(@env.ip, @env.port2, "Couldn't connect to proxy")
|
||||
begin
|
||||
|
||||
result = client.read_hello
|
||||
assert_equal "NBDMAGIC", result[:magic]
|
||||
assert_equal override_size || @env.file1.size, result[:size]
|
||||
|
||||
yield client
|
||||
ensure
|
||||
client.close rescue nil
|
||||
end
|
||||
end
|
||||
|
||||
def test_exits_with_error_when_cannot_connect_to_upstream_on_start
|
||||
assert_raises(RuntimeError) { @env.proxy1 }
|
||||
end
|
||||
|
||||
def test_read_requests_successfully_proxied
|
||||
with_proxied_client do |client|
|
||||
(0..3).each do |n|
|
||||
offset = n * 4096
|
||||
client.write_read_request(offset, 4096, "myhandle")
|
||||
rsp = client.read_response
|
||||
|
||||
assert_equal ::FlexNBD::REPLY_MAGIC, rsp[:magic]
|
||||
assert_equal "myhandle", rsp[:handle]
|
||||
assert_equal 0, rsp[:error]
|
||||
|
||||
orig_data = @env.file1.read(offset, 4096)
|
||||
data = client.read_raw(4096)
|
||||
|
||||
assert_equal 4096, orig_data.size
|
||||
assert_equal 4096, data.size
|
||||
|
||||
assert_equal( orig_data, data, "Returned data does not match" )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_write_requests_successfully_proxied
|
||||
with_proxied_client do |client|
|
||||
(0..3).each do |n|
|
||||
offset = n * 4096
|
||||
client.write(offset, "\xFF" * 4096)
|
||||
rsp = client.read_response
|
||||
|
||||
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
|
||||
assert_equal "myhandle", rsp[:handle]
|
||||
assert_equal 0, rsp[:error]
|
||||
|
||||
data = @env.file1.read(offset, 4096)
|
||||
assert_equal( ( "\xFF" * 4096 ), data, "Data not written" )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def make_fake_server
|
||||
server = FlexNBD::FakeDest.new(@env.ip, @env.port1)
|
||||
@server_up = true
|
||||
|
||||
# We return a thread here because accept() and connect() both block for us
|
||||
Thread.new do
|
||||
sc = server.accept # just tell the supervisor we're up
|
||||
sc.write_hello
|
||||
|
||||
[ server, sc ]
|
||||
end
|
||||
end
|
||||
|
||||
def test_read_request_retried_when_upstream_dies_partway
|
||||
maker = make_fake_server
|
||||
|
||||
with_proxied_client(4096) do |client|
|
||||
server, sc1 = maker.value
|
||||
|
||||
# Send the read request to the proxy
|
||||
client.write_read_request( 0, 4096 )
|
||||
|
||||
# ensure we're given the read request
|
||||
req1 = sc1.read_request
|
||||
assert_equal ::FlexNBD::REQUEST_MAGIC, req1[:magic]
|
||||
assert_equal ::FlexNBD::REQUEST_READ, req1[:type]
|
||||
assert_equal 0, req1[:from]
|
||||
assert_equal 4096, req1[:len]
|
||||
|
||||
# Kill the server again, now we're sure the read request has been sent once
|
||||
sc1.close
|
||||
|
||||
# We expect the proxy to reconnect without our client doing anything.
|
||||
sc2 = server.accept
|
||||
sc2.write_hello
|
||||
|
||||
# And once reconnected, it should resend an identical request.
|
||||
req2 = sc2.read_request
|
||||
assert_equal req1, req2
|
||||
|
||||
# The reply should be proxied back to the client.
|
||||
sc2.write_reply( req2[:handle] )
|
||||
sc2.write_data( "\xFF" * 4096 )
|
||||
|
||||
# Check it to make sure it's correct
|
||||
rsp = timeout(15) { client.read_response }
|
||||
assert_equal ::FlexNBD::REPLY_MAGIC, rsp[:magic]
|
||||
assert_equal 0, rsp[:error]
|
||||
assert_equal req1[:handle], rsp[:handle]
|
||||
|
||||
data = client.read_raw( 4096 )
|
||||
assert_equal( ("\xFF" * 4096), data, "Wrong data returned" )
|
||||
|
||||
sc2.close
|
||||
server.close
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def test_write_request_retried_when_upstream_dies_partway
|
||||
maker = make_fake_server
|
||||
|
||||
with_proxied_client(4096) do |client|
|
||||
server, sc1 = maker.value
|
||||
|
||||
# Send the read request to the proxy
|
||||
client.write( 0, ( "\xFF" * 4096 ) )
|
||||
|
||||
# ensure we're given the read request
|
||||
req1 = sc1.read_request
|
||||
assert_equal ::FlexNBD::REQUEST_MAGIC, req1[:magic]
|
||||
assert_equal ::FlexNBD::REQUEST_WRITE, req1[:type]
|
||||
assert_equal 0, req1[:from]
|
||||
assert_equal 4096, req1[:len]
|
||||
data1 = sc1.read_data( 4096 )
|
||||
assert_equal( ( "\xFF" * 4096 ), data1, "Data not proxied successfully" )
|
||||
|
||||
# Kill the server again, now we're sure the read request has been sent once
|
||||
sc1.close
|
||||
|
||||
# We expect the proxy to reconnect without our client doing anything.
|
||||
sc2 = server.accept
|
||||
sc2.write_hello
|
||||
|
||||
# And once reconnected, it should resend an identical request.
|
||||
req2 = sc2.read_request
|
||||
assert_equal req1, req2
|
||||
data2 = sc2.read_data( 4096 )
|
||||
assert_equal data1, data2
|
||||
|
||||
# The reply should be proxied back to the client.
|
||||
sc2.write_reply( req2[:handle] )
|
||||
|
||||
# Check it to make sure it's correct
|
||||
rsp = timeout(15) { client.read_response }
|
||||
assert_equal ::FlexNBD::REPLY_MAGIC, rsp[:magic]
|
||||
assert_equal 0, rsp[:error]
|
||||
assert_equal req1[:handle], rsp[:handle]
|
||||
|
||||
sc2.close
|
||||
server.close
|
||||
end
|
||||
end
|
||||
|
||||
def test_only_one_client_can_connect_to_proxy_at_a_time
|
||||
with_proxied_client do |client|
|
||||
|
||||
c2 = nil
|
||||
assert_raises(Timeout::Error) do
|
||||
timeout(1) do
|
||||
c2 = FlexNBD::FakeSource.new(@env.ip, @env.port2, "Couldn't connect to proxy (2)")
|
||||
c2.read_hello
|
||||
end
|
||||
end
|
||||
c2.close rescue nil if c2
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
86
tests/acceptance/test_serve_mode.rb
Normal file
86
tests/acceptance/test_serve_mode.rb
Normal file
@@ -0,0 +1,86 @@
|
||||
require 'test/unit'
|
||||
require 'environment'
|
||||
require 'flexnbd/fake_source'
|
||||
|
||||
class TestServeMode < Test::Unit::TestCase
|
||||
|
||||
def setup
|
||||
super
|
||||
@env = Environment.new
|
||||
@env.writefile1( "0" )
|
||||
@env.serve1
|
||||
end
|
||||
|
||||
def teardown
|
||||
@env.cleanup
|
||||
super
|
||||
end
|
||||
|
||||
def connect_to_server
|
||||
client = FlexNBD::FakeSource.new(@env.ip, @env.port1, "Connecting to server failed")
|
||||
begin
|
||||
result = client.read_hello
|
||||
assert_equal "NBDMAGIC", result[:magic]
|
||||
assert_equal @env.file1.size, result[:size]
|
||||
yield client
|
||||
ensure
|
||||
client.close rescue nil
|
||||
end
|
||||
end
|
||||
|
||||
def test_bad_request_magic_receives_error_response
|
||||
connect_to_server do |client|
|
||||
|
||||
# replace REQUEST_MAGIC with all 0s to make it look bad
|
||||
client.send_request( 0, "myhandle", 0, 0, "\x00\x00\x00\x00" )
|
||||
rsp = client.read_response
|
||||
|
||||
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
|
||||
assert_equal "myhandle", rsp[:handle]
|
||||
assert rsp[:error] != 0, "Server sent success reply back: #{rsp[:error]}"
|
||||
|
||||
# The client should be disconnected now
|
||||
assert client.disconnected?, "Server not disconnected"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def test_read_request_out_of_bounds_receives_error_response
|
||||
connect_to_server do |client|
|
||||
client.write_read_request( @env.file1.size, 4096 )
|
||||
rsp = client.read_response
|
||||
|
||||
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
|
||||
assert_equal "myhandle", rsp[:handle]
|
||||
assert rsp[:error] != 0, "Server sent success reply back: #{rsp[:error]}"
|
||||
|
||||
# Ensure we're not disconnected by sending a request. We don't care about
|
||||
# whether the reply is good or not, here.
|
||||
client.write_read_request( 0, 4096 )
|
||||
rsp = client.read_response
|
||||
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
|
||||
end
|
||||
end
|
||||
|
||||
def test_write_request_out_of_bounds_receives_error_response
|
||||
connect_to_server do |client|
|
||||
client.write( @env.file1.size, "\x00" * 4096 )
|
||||
rsp = client.read_response
|
||||
|
||||
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
|
||||
assert_equal "myhandle", rsp[:handle]
|
||||
assert rsp[:error] != 0, "Server sent success reply back: #{rsp[:error]}"
|
||||
|
||||
# Ensure we're not disconnected by sending a request. We don't care about
|
||||
# whether the reply is good or not, here.
|
||||
client.write( 0, "\x00" * @env.file1.size )
|
||||
rsp = client.read_response
|
||||
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
@@ -1,3 +1,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
#include "sockutil.h"
|
||||
|
||||
#include <check.h>
|
||||
|
Reference in New Issue
Block a user