Initial import from https://gist.github.com/1338263
commit
b8b945187e
1 changed files with 154 additions and 0 deletions
@ -0,0 +1,154 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
""" |
||||||
|
Synchronise block devices over the network |
||||||
|
|
||||||
|
Copyright 2006-2008 Justin Azoff <justin@bouncybouncy.net> |
||||||
|
Copyright 2011 Robert Coup <robert@coup.net.nz> |
||||||
|
License: GPL |
||||||
|
|
||||||
|
Getting started: |
||||||
|
|
||||||
|
* Copy blocksync.py to the home directory on the remote host |
||||||
|
* Make sure your remote user can either sudo or is root itself. |
||||||
|
* Make sure your local user can ssh to the remote host |
||||||
|
* Invoke: |
||||||
|
sudo python blocksync.py /dev/source user@remotehost /dev/dest |
||||||
|
""" |
||||||
|
|
||||||
|
import sys |
||||||
|
from zlib import adler32 |
||||||
|
import subprocess |
||||||
|
import time |
||||||
|
|
||||||
|
SAME = "same\n" |
||||||
|
DIFF = "diff\n" |
||||||
|
|
||||||
|
|
||||||
|
def do_open(f, mode): |
||||||
|
f = open(f, mode) |
||||||
|
f.seek(0, 2) |
||||||
|
size = f.tell() |
||||||
|
f.seek(0) |
||||||
|
return f, size |
||||||
|
|
||||||
|
|
||||||
|
def getblocks(f, blocksize): |
||||||
|
while 1: |
||||||
|
block = f.read(blocksize) |
||||||
|
if not block: |
||||||
|
break |
||||||
|
yield block |
||||||
|
|
||||||
|
|
||||||
|
def server(dev, blocksize): |
||||||
|
print dev, blocksize |
||||||
|
f, size = do_open(dev, 'r+') |
||||||
|
print size |
||||||
|
sys.stdout.flush() |
||||||
|
|
||||||
|
for block in getblocks(f, blocksize): |
||||||
|
print "%08x" % (adler32(block) & 0xFFFFFFFF) |
||||||
|
sys.stdout.flush() |
||||||
|
res = sys.stdin.readline() |
||||||
|
if res != SAME: |
||||||
|
newblock = sys.stdin.read(blocksize) |
||||||
|
f.seek(-blocksize, 1) |
||||||
|
f.write(newblock) |
||||||
|
|
||||||
|
|
||||||
|
def sync(srcdev, dsthost, dstdev=None, blocksize=1024 * 1024): |
||||||
|
|
||||||
|
if not dstdev: |
||||||
|
dstdev = srcdev |
||||||
|
|
||||||
|
print "Block size is %0.1f MB" % (float(blocksize) / (1024 * 1024)) |
||||||
|
cmd = ['ssh', '-c', 'blowfish', dsthost, 'sudo', 'python', 'blocksync.py', 'server', dstdev, '-b', str(blocksize)] |
||||||
|
print "Running: %s" % " ".join(cmd) |
||||||
|
|
||||||
|
p = subprocess.Popen(cmd, bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True) |
||||||
|
p_in, p_out = p.stdin, p.stdout |
||||||
|
|
||||||
|
line = p_out.readline() |
||||||
|
p.poll() |
||||||
|
if p.returncode is not None: |
||||||
|
print "Error connecting to or invoking blocksync on the remote host!" |
||||||
|
sys.exit(1) |
||||||
|
|
||||||
|
a, b = line.split() |
||||||
|
if a != dstdev: |
||||||
|
print "Dest device (%s) doesn't match with the remote host (%s)!" % (dstdev, a) |
||||||
|
sys.exit(1) |
||||||
|
if int(b) != blocksize: |
||||||
|
print "Source block size (%d) doesn't match with the remote host (%d)!" % (blocksize, int(b)) |
||||||
|
sys.exit(1) |
||||||
|
|
||||||
|
try: |
||||||
|
f, size = do_open(srcdev, 'r') |
||||||
|
except Exception, e: |
||||||
|
print "Error accessing source device! %s" % e |
||||||
|
sys.exit(1) |
||||||
|
|
||||||
|
line = p_out.readline() |
||||||
|
p.poll() |
||||||
|
if p.returncode is not None: |
||||||
|
print "Error accessing device on remote host!" |
||||||
|
sys.exit(1) |
||||||
|
remote_size = int(line) |
||||||
|
if size != remote_size: |
||||||
|
print "Source device size (%d) doesn't match remote device size (%d)!" % (size, remote_size) |
||||||
|
sys.exit(1) |
||||||
|
|
||||||
|
same_blocks = diff_blocks = 0 |
||||||
|
|
||||||
|
print "Starting sync..." |
||||||
|
t0 = time.time() |
||||||
|
t_last = t0 |
||||||
|
for i, l_block in enumerate(getblocks(f, blocksize)): |
||||||
|
l_sum = "%08x" % (adler32(l_block) & 0xFFFFFFFF) |
||||||
|
r_sum = p_out.readline().strip() |
||||||
|
|
||||||
|
if l_sum == r_sum: |
||||||
|
p_in.write(SAME) |
||||||
|
p_in.flush() |
||||||
|
same_blocks += 1 |
||||||
|
else: |
||||||
|
p_in.write(DIFF) |
||||||
|
p_in.flush() |
||||||
|
p_in.write(l_block) |
||||||
|
p_in.flush() |
||||||
|
diff_blocks += 1 |
||||||
|
|
||||||
|
t1 = time.time() |
||||||
|
if t1 - t_last > 1: |
||||||
|
rate = (i + 1.0) * blocksize / (1024.0 * 1024.0) / (t1 - t0) |
||||||
|
print "\rsame: %d, diff: %d, %d/%d, %5.1f MB/s" % (same_blocks, diff_blocks, same_blocks + diff_blocks, size / blocksize, rate), |
||||||
|
t_last = t1 |
||||||
|
|
||||||
|
print "\n\nCompleted in %d seconds" % (time.time() - t0) |
||||||
|
|
||||||
|
return same_blocks, diff_blocks |
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
from optparse import OptionParser |
||||||
|
parser = OptionParser(usage="%prog [options] /dev/source user@remotehost [/dev/dest]") |
||||||
|
parser.add_option("-b", "--blocksize", dest="blocksize", action="store", type="int", help="block size (bytes)", default=1024 * 1024) |
||||||
|
(options, args) = parser.parse_args() |
||||||
|
|
||||||
|
if len(args) < 2: |
||||||
|
parser.print_help() |
||||||
|
print __doc__ |
||||||
|
sys.exit(1) |
||||||
|
|
||||||
|
if args[0] == 'server': |
||||||
|
dstdev = args[1] |
||||||
|
server(dstdev, options.blocksize) |
||||||
|
else: |
||||||
|
srcdev = args[0] |
||||||
|
dsthost = args[1] |
||||||
|
if len(args) > 2: |
||||||
|
dstdev = args[2] |
||||||
|
else: |
||||||
|
dstdev = None |
||||||
|
sync(srcdev, dsthost, dstdev, options.blocksize) |
||||||
|
|
||||||
|
|
Loading…
Reference in new issue