Advertisement
Guest User

Untitled

a guest
Jul 12th, 2017
86
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.62 KB | None | 0 0
  1. #!/usr/bin/env python
  2. """
  3. Simple Python wrapper around lftp to mirror a remote ftp onto a local folder.
  4.  
  5. New files are downloaded, deleted files are marked for removal in a TO_DELETE.sh
  6. script.
  7. """
  8. import os
  9. import pprint
  10. import re
  11. import subprocess
  12. import sys
  13. import urllib.parse
  14.  
  15.  
  16. def get_diff(local_dir, remote_dir, host, user, password):
  17. """
  18. Get a diff between the local copy and the remote server.
  19.  
  20. Params:
  21. - local_dir is the path to the local copy.
  22. - remote_dir is the path to the folder to replicate on the distant ftp.
  23. - host is the ftp host.
  24. - user is the user to authenticate with.
  25. - password is the associated password.
  26.  
  27. Returns a diff ie a list of lftp commands.
  28. """
  29. parts_per_file = 10 # Number of parts per file
  30. parallel = 2 # Number of parallel transfers
  31.  
  32. # The --dry-run generates lftp commands that we parse into a diff list.
  33. lftp_input = """
  34. open {host}
  35. user {user} \"{password}\"
  36. mirror --dry-run --delete -c --use-pget-n={parts_per_file} --parallel={parallel} {remote_dir} {local_dir}
  37. bye""".format(host=host, user=user, password=password,
  38. parts_per_file=parts_per_file, parallel=parallel,
  39. remote_dir=remote_dir, local_dir=local_dir)
  40. process = subprocess.run(["lftp", "-f" "/dev/stdin"],
  41. input=lftp_input.encode("utf-8"),
  42. stdout=subprocess.PIPE)
  43. stdout = process.stdout.decode("utf-8").strip()
  44. if stdout != "":
  45. diff = stdout.split("\n")
  46. else:
  47. diff = []
  48. return diff
  49.  
  50.  
  51. def apply_diff(diff):
  52. """
  53. Apply a diff (list of lftp commands).
  54.  
  55. Params:
  56. - diff is the list of lftp commands to apply.
  57. """
  58. lftp_input = "set xfer:clobber on\n"
  59. lftp_input += "\n".join(diff)
  60. process = subprocess.run(["lftp", "-f" "/dev/stdin"],
  61. input=lftp_input.encode("utf-8"))
  62.  
  63.  
  64. def extract_rm_commands(diff):
  65. """
  66. Extract removal commands (rm) from the diff.
  67.  
  68. Params:
  69. - diff is the list of lftp commands to apply.
  70.  
  71. Returns a tuple (rm_commands, diff) where rm_commands is a list of rm
  72. commands to apply, in sh format, and diff is a list of lftp commands to
  73. apply without any removal command.
  74. """
  75. rm_commands = [i for i in diff if i.startswith("rm")]
  76. diff = [i for i in diff if i not in rm_commands]
  77. # Replace file: URLs by quoted non-urlencoded paths
  78. rm_commands = [re.sub(r"file:(.*)",
  79. lambda x: "\"%s\"" % (urllib.parse.unquote(x.group(1))),
  80. i)
  81. for i in rm_commands]
  82. return rm_commands, diff
  83.  
  84.  
  85. def uniq_append(file, content):
  86. """
  87. Append to a file, ensuring there are not any duplicate lines.
  88. """
  89. # Ensure directory exists
  90. if not os.path.isdir(os.path.dirname(file)):
  91. os.mkdir(os.path.dirname(file))
  92. # Add \n to the commands in content, as diff is not \n-terminated.
  93. content = [i + "\n" for i in content]
  94. # Add to content all the previous content
  95. if os.path.isfile(file):
  96. with open(file, "r") as fh:
  97. content.extend(fh.readlines())
  98. # Remove empty lines
  99. content = [i for i in content if i.strip() != ""]
  100. # Use a set to uniqify it
  101. content = set(content)
  102. # Trick to ensure the rm command associated to the TO_DELETE file is always
  103. # at the bottom. We remove it from content.
  104. try:
  105. content.remove("rm %s\n" % (file,))
  106. except KeyError:
  107. pass
  108. # Write new content
  109. with open(file, "w") as fh:
  110. for line in content:
  111. fh.write(line)
  112. # And we ensure to write the rm command associated with the TO_DELETE
  113. # file at the end.
  114. fh.write("rm %s\n" % (file,))
  115.  
  116.  
  117. if __name__ == "__main__":
  118. if len(sys.argv) < 6:
  119. sys.exit("Usage: %s LOCAL_DIR REMOTE_DIR HOST USER PASS" %
  120. (sys.argv[0],))
  121.  
  122. local_dir = sys.argv[1]
  123. remote_dir = sys.argv[2]
  124. host = sys.argv[3]
  125. user = sys.argv[4]
  126. password = sys.argv[5]
  127.  
  128. print("Fetching diff…")
  129. diff = get_diff(local_dir, remote_dir,
  130. host, user, password)
  131. print("Diff is:")
  132. pprint.pprint(diff)
  133. print()
  134.  
  135. # Save rm commands in a script
  136. rm_commands, diff = extract_rm_commands(diff)
  137. rm_script = os.path.normpath(local_dir + "/TO_DELETE.sh")
  138. uniq_append(rm_script, rm_commands)
  139.  
  140. if len(diff) > 0:
  141. print("Applying get commands in diff…")
  142. apply_diff(diff)
  143. else:
  144. print("No new files to get.")
  145.  
  146. if len(rm_commands) > 0:
  147. print("Rm commands in diff were saved to %s." % (rm_script,))
  148.  
  149. print("Done!")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement