Upgrade Clone Tool to v2 (use rsync)
This commit is contained in:
parent
8c6bdb6c5f
commit
e76a157b79
|
@ -47,18 +47,9 @@ def set_clone_error(error_msg):
|
||||||
return False
|
return False
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def check_pid(pid):
|
def wait_on_clone_error_dismiss():
|
||||||
try:
|
while os.path.isfile("/tmp/.clone_error"):
|
||||||
os.kill(pid, 0)
|
time.sleep(1)
|
||||||
except OSError:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def send_usr1_sig(process_id):
|
|
||||||
while check_pid(process_id):
|
|
||||||
os.kill(process_id, signal.SIGUSR1)
|
|
||||||
time.sleep(3)
|
|
||||||
|
|
||||||
def get_drive_size(drive):
|
def get_drive_size(drive):
|
||||||
size = -1
|
size = -1
|
||||||
|
@ -136,6 +127,8 @@ def main():
|
||||||
reset_clone_confirm()
|
reset_clone_confirm()
|
||||||
reset_clone_rescan()
|
reset_clone_rescan()
|
||||||
os.system("umount /mnt/hdd")
|
os.system("umount /mnt/hdd")
|
||||||
|
os.system("umount /tmp/drive1")
|
||||||
|
os.system("umount /tmp/drive2")
|
||||||
os.system("rm /tmp/.clone_target_drive_has_mynode")
|
os.system("rm /tmp/.clone_target_drive_has_mynode")
|
||||||
|
|
||||||
# Detect drives
|
# Detect drives
|
||||||
|
@ -148,6 +141,7 @@ def main():
|
||||||
print_and_log("Clone tool did not find 2 drives!")
|
print_and_log("Clone tool did not find 2 drives!")
|
||||||
set_clone_state("error")
|
set_clone_state("error")
|
||||||
set_clone_error("Clone tool needs 2 drives! Found {}.".format(drive_count))
|
set_clone_error("Clone tool needs 2 drives! Found {}.".format(drive_count))
|
||||||
|
wait_on_clone_error_dismiss()
|
||||||
return
|
return
|
||||||
|
|
||||||
# Detect Source and Target Drives
|
# Detect Source and Target Drives
|
||||||
|
@ -165,6 +159,7 @@ def main():
|
||||||
if target_found:
|
if target_found:
|
||||||
set_clone_state("error")
|
set_clone_state("error")
|
||||||
set_clone_error("Two target drives found. Is myNode drive missing?")
|
set_clone_error("Two target drives found. Is myNode drive missing?")
|
||||||
|
wait_on_clone_error_dismiss()
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
target_found = True
|
target_found = True
|
||||||
|
@ -174,6 +169,7 @@ def main():
|
||||||
if target_found:
|
if target_found:
|
||||||
set_clone_state("error")
|
set_clone_state("error")
|
||||||
set_clone_error("Two target drives found. Is myNode drive missing?")
|
set_clone_error("Two target drives found. Is myNode drive missing?")
|
||||||
|
wait_on_clone_error_dismiss()
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
target_found = True
|
target_found = True
|
||||||
|
@ -202,6 +198,7 @@ def main():
|
||||||
if target_found:
|
if target_found:
|
||||||
set_clone_state("error")
|
set_clone_state("error")
|
||||||
set_clone_error("Two target drives found. Is myNode drive missing?")
|
set_clone_error("Two target drives found. Is myNode drive missing?")
|
||||||
|
wait_on_clone_error_dismiss()
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
target_found = True
|
target_found = True
|
||||||
|
@ -221,82 +218,97 @@ def main():
|
||||||
while not os.path.isfile("/tmp/.clone_confirm") and not os.path.isfile("/tmp/.clone_rescan"):
|
while not os.path.isfile("/tmp/.clone_confirm") and not os.path.isfile("/tmp/.clone_rescan"):
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
# User asked for rescan - recursively call main
|
# User asked for rescan, return, script will re-run right away
|
||||||
if os.path.isfile("/tmp/.clone_rescan"):
|
if os.path.isfile("/tmp/.clone_rescan"):
|
||||||
main()
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# Clone drives
|
# Setup for clone
|
||||||
set_clone_state("in_progress")
|
set_clone_state("in_progress")
|
||||||
|
os.system(f"mkdir -p /tmp/drive1")
|
||||||
|
os.system(f"mkdir -p /tmp/drive2")
|
||||||
|
os.system(f"umount /dev/{mynode_drive}1")
|
||||||
|
os.system(f"umount /dev/{target_drive}1")
|
||||||
|
|
||||||
|
# Update partitions (removes all + makes new without removing data)
|
||||||
|
print_and_log("Formatting Drive...")
|
||||||
|
os.system("echo 'Formatting drive...' > /tmp/.clone_progress")
|
||||||
|
subprocess.check_output(f"wipefs -a /dev/{target_drive}", shell=True)
|
||||||
|
time.sleep(2)
|
||||||
|
subprocess.check_output(f"/usr/bin/format_drive.sh {target_drive}", shell=True)
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
# Make new partition on dest drive
|
||||||
|
print_and_log("Creating Partition...")
|
||||||
|
os.system("echo 'Creating Partition...' > /tmp/.clone_progress")
|
||||||
|
subprocess.check_output(f"mkfs.ext4 -F -L myNode /dev/{target_drive}1", shell=True)
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
# Mounting Partitions
|
||||||
|
print_and_log("Mounting Partitions...")
|
||||||
|
os.system("echo 'Mounting Partitions...' > /tmp/.clone_progress")
|
||||||
|
subprocess.check_output(f"mount /dev/{mynode_drive}1 /tmp/drive1", shell=True)
|
||||||
|
subprocess.check_output(f"mount /dev/{target_drive}1 /tmp/drive2", shell=True)
|
||||||
|
|
||||||
|
# Clone drives
|
||||||
os.system("echo 'Starting clone.' > /tmp/.clone_progress")
|
os.system("echo 'Starting clone.' > /tmp/.clone_progress")
|
||||||
try:
|
try:
|
||||||
cmd = ["dd","bs=64K",f"if=/dev/{mynode_drive}",f"of=/dev/{target_drive}","conv=sync,noerror"]
|
#cmd = ["dd","bs=64K",f"if=/dev/{mynode_drive}",f"of=/dev/{target_drive}","conv=sync,noerror"]
|
||||||
#cmd = ["dd","bs=512",f"if=/dev/zero",f"of=/dev/null","count=5999999","conv=sync,noerror"]
|
#cmd = ["dd","bs=512",f"if=/dev/zero",f"of=/dev/null","count=5999999","conv=sync,noerror"]
|
||||||
dd = subprocess.Popen(cmd, stderr=subprocess.PIPE)
|
cmd = ["rsync","-avxHAX","--info=progress2",f"/tmp/drive1/","/tmp/drive2/"]
|
||||||
print_and_log("DD PID: {}".format(dd.pid))
|
clone_process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
thread = Thread(target=send_usr1_sig, args=(dd.pid,))
|
print_and_log("CLONE PID: {}".format(clone_process.pid))
|
||||||
thread.start()
|
for l in clone_process.stdout:
|
||||||
for l in dd.stderr:
|
|
||||||
l = l.decode("utf-8")
|
l = l.decode("utf-8")
|
||||||
if 'bytes' in l:
|
if 'xfr#' in l:
|
||||||
|
logline = "UNKNOWN"
|
||||||
|
try:
|
||||||
|
lines = l.split("\r")
|
||||||
|
logline = lines[len(lines)-1].strip()
|
||||||
|
parts = logline.split()
|
||||||
|
logline = parts[0] + " bytes copied<br/>" + parts[1] + "<br/>" + parts[2] + "<br/>" + parts[3]
|
||||||
|
except Exception as e:
|
||||||
|
logline = "Clone status parse error: ".format(str(e))
|
||||||
try:
|
try:
|
||||||
out_fd = open('/tmp/.clone_progress','w')
|
out_fd = open('/tmp/.clone_progress','w')
|
||||||
out_fd.write(l)
|
out_fd.write(logline)
|
||||||
out_fd.close()
|
out_fd.close()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print_and_log("Write Exception: " + str(e))
|
print_and_log("Write Exception: " + str(e))
|
||||||
|
|
||||||
while dd.poll() is None:
|
while clone_process.poll() is None:
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
print_and_log("Waiting on dd exit...")
|
print_and_log("Waiting on rsync exit...")
|
||||||
|
|
||||||
print_and_log("DD RET CODE: {}".format(dd.returncode))
|
print_and_log("CLONE RET CODE: {}".format(clone_process.returncode))
|
||||||
if dd.returncode != 0:
|
if clone_process.returncode != 0:
|
||||||
# DD had an error - log it
|
# Clone had an error - log it
|
||||||
if dd.stderr != None:
|
if clone_process.stderr != None:
|
||||||
for l in dd.stderr:
|
for l in clone_process.stderr:
|
||||||
print_and_log("DD STDERR: "+l.decode("utf-8"))
|
print_and_log("CLONE STDERR: "+l.decode("utf-8"))
|
||||||
if dd.stdout != None:
|
if clone_process.stdout != None:
|
||||||
for l in dd.stdout:
|
for l in clone_process.stdout:
|
||||||
print_and_log("DD STDOUT: "+l.decode("utf-8"))
|
print_and_log("CLONE STDOUT: "+l.decode("utf-8"))
|
||||||
set_clone_state("error")
|
set_clone_state("error")
|
||||||
set_clone_error("DD failed with return code {}".format(dd.returncode))
|
set_clone_error("Clone failed with return code {}".format(clone_process.returncode))
|
||||||
|
wait_on_clone_error_dismiss()
|
||||||
return
|
return
|
||||||
print_and_log("DD IS COMPLETE")
|
|
||||||
|
|
||||||
# PAUSE IF DD WAS SUCCESSFUL
|
print_and_log("CLONE IS COMPLETE")
|
||||||
# if dd.returncode == 0:
|
|
||||||
# set_clone_state("error")
|
|
||||||
# set_clone_error("DD WAS SUCCESSFUL!!!! Remove temp code.")
|
|
||||||
# while True:
|
|
||||||
# time.sleep(60)
|
|
||||||
|
|
||||||
# Update partitions (removes all + makes new without removing data)
|
|
||||||
print_and_log("Updating Partitions...")
|
|
||||||
os.system("echo 'Updating partitions...' > /tmp/.clone_progress")
|
|
||||||
subprocess.check_output(f"/usr/bin/format_drive.sh {target_drive}", shell=True)
|
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
|
|
||||||
# Resize filesystem to fill up whole drive
|
|
||||||
print_and_log("Resizing Filesystem...")
|
|
||||||
os.system("echo 'Resizing filesystem...' > /tmp/.clone_progress")
|
|
||||||
os.system(f"partprobe /dev/{target_drive}")
|
|
||||||
time.sleep(2)
|
|
||||||
subprocess.check_output(f"e2fsck -y -f /dev/{target_drive}1", shell=True)
|
|
||||||
subprocess.check_output(f"resize2fs /dev/{target_drive}1", shell=True)
|
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
print_and_log("CalledProcessError")
|
print_and_log("CalledProcessError")
|
||||||
print_and_log(e.stderr)
|
print_and_log(e.stderr)
|
||||||
print_and_log(e.stdout)
|
print_and_log(e.stdout)
|
||||||
set_clone_state("error")
|
set_clone_state("error")
|
||||||
set_clone_error("Clone failed: {}".format(e))
|
set_clone_error("Clone failed: {}".format(e))
|
||||||
|
wait_on_clone_error_dismiss()
|
||||||
return
|
return
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
set_clone_state("error")
|
set_clone_state("error")
|
||||||
set_clone_error("Clone failed: {}".format(e))
|
set_clone_error("Clone failed: {}".format(e))
|
||||||
|
wait_on_clone_error_dismiss()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
# Complete - wait for reboot
|
# Complete - wait for reboot
|
||||||
set_clone_state("complete")
|
set_clone_state("complete")
|
||||||
print_and_log("Clone Complete!")
|
print_and_log("Clone Complete!")
|
||||||
|
@ -312,3 +324,4 @@ if __name__ == "__main__":
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print_and_log("Exception: {}".format(str(e)))
|
print_and_log("Exception: {}".format(str(e)))
|
||||||
set_clone_error("Exception: {}".format(str(e)))
|
set_clone_error("Exception: {}".format(str(e)))
|
||||||
|
wait_on_clone_error_dismiss()
|
|
@ -110,7 +110,6 @@ if [ -f /home/bitcoin/open_clone_tool ]; then
|
||||||
sync
|
sync
|
||||||
while [ 1 ]; do
|
while [ 1 ]; do
|
||||||
python3 /usr/bin/clone_drive.py || true
|
python3 /usr/bin/clone_drive.py || true
|
||||||
sleep 60s
|
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
@ -248,12 +248,21 @@ def index():
|
||||||
}
|
}
|
||||||
return render_template('state.html', **templateData)
|
return render_template('state.html', **templateData)
|
||||||
elif clone_state == CLONE_STATE_ERROR:
|
elif clone_state == CLONE_STATE_ERROR:
|
||||||
|
# Error is being cleared
|
||||||
|
if request.args.get('clone_clear_error'):
|
||||||
|
os.system("rm /tmp/.clone_error")
|
||||||
|
time.sleep(3)
|
||||||
|
return redirect("/")
|
||||||
|
|
||||||
|
# Show Error
|
||||||
error = get_clone_error()
|
error = get_clone_error()
|
||||||
msg = ""
|
msg = ""
|
||||||
msg += "Clone Error<br/></br>"
|
msg += "Clone Error<br/></br>"
|
||||||
msg += error
|
msg += error
|
||||||
msg += "<br/><br/><br/><small>Retrying in one minute.<br/><br/><br/>"
|
msg += "<br/><br/><br/>"
|
||||||
msg += "<a class='ui-button ui-widget ui-corner-all mynode_button_small' href='/settings/reboot-device'>Exit Clone Tool</a>"
|
msg += "<a class='ui-button ui-widget ui-corner-all mynode_button_small' style='width: 120px;' href='/?clone_clear_error=1'>Try Again</a>"
|
||||||
|
msg += "<br/><br/>"
|
||||||
|
msg += "<a class='ui-button ui-widget ui-corner-all mynode_button_small' style='width: 120px;' href='/settings/reboot-device'>Exit Clone Tool</a>"
|
||||||
templateData = {
|
templateData = {
|
||||||
"title": "myNode Clone Tool",
|
"title": "myNode Clone Tool",
|
||||||
"header_text": "Cloning Tool",
|
"header_text": "Cloning Tool",
|
||||||
|
|
Loading…
Reference in New Issue
Block a user