-
Notifications
You must be signed in to change notification settings - Fork 8
/
Rclone.py
156 lines (120 loc) · 6.64 KB
/
Rclone.py
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import logging
import re
import os
import pexpect
import sys
import hashlib
class Rclone:
rcloneLogFilePath = None
bash = None
def __init__(self, rcloneLogFilePath=None):
self.rcloneLogFilePath = rcloneLogFilePath
self.bash = pexpect.spawn("/bin/bash", echo=False, env = {"SHELL": "/bin/bash", "HOME": "/root"})
if self.rcloneLogFilePath is not None:
self.bash.logfile = open(rcloneLogFilePath, 'wb')
logging.info("Rclone has been called")
def logBashToConsole(self, bool):
if (bool == True):
self.bash.logfile = sys.stdout.buffer
def transferSingleFileToRemoteRoot(self, rcloneSourceFile, rcloneRemote, verifyUpload=False, removeSourceFile=False):
rcloneSourceFileName = rcloneSourceFile
if rcloneSourceFileName.__contains__("/"):
rcloneSourceFileName = rcloneSourceFileName.split("/")[-1]
rcloneSourceFileName = re.sub('"', "", rcloneSourceFileName)
rcloneSourceFileName = '"' + rcloneSourceFileName + '"'
logging.info("\tStarting transfer of " + rcloneSourceFile + " to " + rcloneRemote + rcloneSourceFileName)
self.bash.sendline(
f"rclone copyto {rcloneSourceFile} {rcloneRemote}{rcloneSourceFileName} -P ; echo RCLONE FINISHED WITH STATUS $?")
while True:
result = self.bash.expect(["Transferring:\s*\*\s*", "RCLONE FINISHED WITH STATUS", pexpect.TIMEOUT])
if result == 0:
transferProgress = re.search("Transferred:\W*(.*)\n", self.bash.readline().decode("utf-8"))[1]
logging.info("\t" * 2 + rcloneSourceFileName + ":" + transferProgress)
elif result == 1:
logging.debug("\tFinished uploading")
logging.debug("\t\tSummary:\n" + self.bash.before.decode("utf-8"))
exitStatus = self.bash.readline().decode("utf-8").strip()
if exitStatus == "0":
logging.info("\tGdrive upload finished successfully")
if verifyUpload:
logging.info("\t"*2 + "Will start verification of upload")
verifyResult = self.verifyUploadedFile(rcloneSourceFile, rcloneRemote + rcloneSourceFileName)
if verifyResult:
logging.info("\t" * 3 + "Verification was successful")
if removeSourceFile:
logging.info("\t"*2 + "Removing source file")
os.remove(rcloneSourceFile)
if os.path.isfile(rcloneSourceFile):
logging.warning("\t"*3 + "Failed to remove source file:" + rcloneSourceFile)
else:
logging.info("\t" * 3 + "Source file was removed:" + rcloneSourceFile)
return True
else:
logging.error("\t" * 3 + "Verification of upload failed")
logging.error("\t" * 4 + "Local file:" + rcloneSourceFile)
logging.error("\t" * 4 + "Remote file:" + rcloneRemote + rcloneSourceFileName)
return False
else:
if removeSourceFile:
logging.info("\t" * 2 + "Removing source file")
os.remove(rcloneSourceFile)
if os.path.isfile(rcloneSourceFile):
logging.warning("\t" * 3 + "Failed to remove source file:" + rcloneSourceFile)
else:
logging.info("\t" * 3 + "Source file was removed:" + rcloneSourceFile)
return True
else:
logging.error("\tGdrive upload finished with error status:" + exitStatus)
sys.exit(exitStatus)
elif result == 2:
logging.error("\tGdrive upload timed out")
sys.exit(1)
def removeOldFiles(self, tresholdtime, rcloneRemote, dryRun:bool = True):
if dryRun:
logging.info("\tRunning dry run of deleting remote files older then " + tresholdtime)
logging.info(f"rclone delete --dry-run --min-age {tresholdtime} {rcloneRemote}; echo DELETEDONE")
self.bash.sendline(f"rclone delete --dry-run --min-age {tresholdtime} {rcloneRemote}; echo DELETEDONE")
else:
logging.info("\tDeleting remote files older then " + tresholdtime)
self.bash.sendline(f"rclone delete --min-age {tresholdtime} {rcloneRemote}; echo DELETEDONE")
result = self.bash.expect(["DELETEDONE", pexpect.TIMEOUT])
deleteOutput = self.bash.before.decode("utf-8")
logging.debug(deleteOutput)
if result == 0:
logging.info("\t Remote files were removed (Dry Run: " + str(dryRun) + ")")
return True
else:
bashOutput = self.bash.read_nonblocking(size=500, timeout=1)
logging.error("\tCould not delete old remote files, got:" + bashOutput)
sys.exit(1)
def verifyUploadedFile(self, rcloneSourceFile, rcloneRemoteFile):
logging.info("Verifying successful upload of files")
logging.info("\tLocal file:" + rcloneSourceFile)
logging.info("\tRemote file:" + rcloneRemoteFile)
md5 = hashlib.md5()
with open(rcloneSourceFile, 'rb') as f:
while True:
data = f.read(65536)
if not data:
break
md5.update(data)
localHash = md5.hexdigest()
remoteHash = ""
logging.debug("\t" * 2 + "Local file MD5 hash:" + localHash)
self.bash.sendline("rclone hashsum MD5 " + rcloneRemoteFile + "; echo HASHDONE")
result = self.bash.expect(["HASHDONE", pexpect.TIMEOUT])
if result == 0:
fileName = os.path.basename(rcloneSourceFile).replace(".", "\.")
bashOutput = self.bash.before.decode("utf-8")
remoteHash = re.search("(\w*)\W*" + fileName, bashOutput)[1]
logging.debug("\tRemote file MD5 hash:" + remoteHash)
else:
bashOutput = self.bash.read_nonblocking(size=500, timeout=1)
logging.error("\tCould not determine hash of remote file, got:" + bashOutput)
sys.exit(1)
if (remoteHash == localHash):
logging.info("\tVerification was successful, the hashes match")
return True
else:
logging.warning("\tVerification was unsuccessful, the hashes dont match")
return False