Linux Network Block Device server in Python
This is a simplified version based on Kragen Sitaker's http://lists.canonical.org/pipermail/kragen-hacks/2004-May/000397.html
Close is never actually called, at least not on the same connection -- linux C nbd-client -d seems to stall, perhaps it tries to open another socket?
This code doesn't check for error conditions, failed reads/writes, past end of disk, etc.
It prints io requests, you can analyze filesystem and user program io patterns.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | #!/usr/bin/python
import struct, socket, sys, time
# Working: nbd protocol, read/write serving up files, error handling, file size detection, in theory, large file support... not really, so_reuseaddr, nonforking
def recvall(sock, length):
rv = []
while sum(map(len, rv)) < length:
rv.append(sock.recv(length-sum(map(len, rv))))
assert rv[-1], "no more data to read"
return ''.join(rv)
def serveclient():
READ, WRITE, CLOSE = 0,1,2
"Serves a single client until it exits."
afile.seek(0, 2)
asock.send('NBDMAGIC\x00\x00\x42\x02\x81\x86\x12\x53' + struct.pack('>Q', afile.tell()) + '\0'*128);
while True:
header = recvall(asock, struct.calcsize('>LL8sQL'))
magic, request, handle, offset, dlen = struct.unpack('>LL8sQL', header)
assert magic == 0x25609513
if request == READ:
afile.seek(offset)
asock.send('gDf\x98\0\0\0\0'+handle)
asock.send(afile.read(dlen))
print "read\t0x%08x\t0x%08x" % (offset, dlen), time.time()
elif request == WRITE:
afile.seek(offset)
afile.write(recvall(asock, dlen))
afile.flush()
asock.send('gDf\x98\0\0\0\0'+handle)
print "write\t0x%08x\t0x%08x" % (offset, dlen), time.time()
elif request == CLOSE:
asock.close()
print "closed"
return
else:
print "ignored request", request
if __name__ == '__main__':
"Given a port and a filename, serves up the file."
afile = file(sys.argv[2], 'rb+')
lsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
lsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
lsock.bind(('', int(sys.argv[1])))
lsock.listen(5)
while True:
(asock, addr) = lsock.accept()
print "connection from", addr
serveclient()
|