No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

backr.py 7.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. #!/usr/bin/env python
  2. # backr backup tool in python
  3. import os
  4. import datetime
  5. import hashlib
  6. import sys
  7. import tarfile
  8. import shutil
  9. from distutils.dir_util import copy_tree
  10. import cPickle as pickle
  11. # Take Arguments
  12. for i in range(len(sys.argv)):
  13. if "-h" in sys.argv or "--help" in sys.argv:
  14. print "backr simple backup tool"
  15. print "usage: backr.py [-h|--help] [-c|--compress] [-d|--default]"
  16. print "[-h|--help] - print this help"
  17. print "[-c|--compress] - use compression, do not prompt user"
  18. print "[-d|--default] - backup to default location (~/backrs, do not prompt user"
  19. sys.exit(0)
  20. if "-d" in sys.argv or "--default" in sys.argv:
  21. prompt_for_location = False
  22. else:
  23. prompt_for_location = True
  24. if "-c" in sys.argv or "--compress" in sys.argv:
  25. use_compression = True
  26. prompt_for_compression = False
  27. else:
  28. prompt_for_compression = True
  29. # Define a function for asking the user yes or no questions
  30. # Taken from: http://stackoverflow.com/questions/3041986/ddg#3041990
  31. def query_yes_no(question):
  32. default = None
  33. valid = {"yes": True, "y": True, "ye": True,
  34. "no": False, "n": False}
  35. prompt = " [y/n] "
  36. while True:
  37. sys.stdout.write(question + prompt)
  38. choice = raw_input().lower()
  39. if default is not None and choice == '':
  40. return valid[default]
  41. elif choice in valid:
  42. return valid[choice]
  43. else:
  44. sys.stdout.write("Please respond with 'yes' or 'no' "
  45. "(or 'y' or 'n').\n")
  46. # Define a function for asking the user for a comment
  47. # This comment will be part of a directory name
  48. # In UNIX a filename can contain everything except for "/" or NULL
  49. # And we don't have to worry about NULL because this will be appended to the date
  50. def get_comment():
  51. while True:
  52. comment = raw_input("Enter a comment for this backup (or leave blank): ")
  53. if "/" in comment:
  54. print "comment cannot contain '/'"
  55. else:
  56. return comment
  57. # Define a function to make a tarball, used for the compression feature
  58. def make_tarfile(output_filename, source_dir):
  59. with tarfile.open(output_filename, "w:gz") as tar:
  60. tar.add(source_dir, arcname=os.path.basename(source_dir))
  61. # Main Function
  62. def main():
  63. # use_compression was defined by an argument in the initial for loop
  64. global use_compression
  65. if prompt_for_compression:
  66. use_compression = query_yes_no("Use compression?")
  67. # Determining save location:
  68. # Check for .backr_location file with stores save location after first run
  69. home = os.path.expanduser("~")
  70. default_location = home+"/backrs"
  71. if os.path.isfile(".backr-location"):
  72. with open('.backr-location', 'r') as myfile:
  73. backup_location = myfile.read()
  74. # If .backr_location does not exit, prompt the user for a location and save
  75. # it to the file, later this should be done entirely as an argument to make
  76. # backr more portable
  77. else:
  78. if not prompt_for_location:
  79. backup_location = default_location
  80. else:
  81. backup_location = raw_input("Enter a location to save (or leave blank for default "+default_location+"): ")
  82. # If the user leaves the location blank, use the default (~/backrs)
  83. if backup_location == "":
  84. backup_location = default_location
  85. # If the user-provided location exists, write it to the file
  86. if os.path.isdir(backup_location):
  87. f = open('.backr-location', 'w')
  88. f.write(backup_location)
  89. f.close()
  90. else:
  91. # Create the folder only if it is ~/backrs
  92. if backup_location == default_location:
  93. os.makedirs(backup_location)
  94. else:
  95. print backup_location + " does not exist"
  96. sys.exit(1)
  97. # Output
  98. print "will save to "+backup_location
  99. # Get Comment
  100. comment = get_comment()
  101. # Set some varibles:
  102. # Set current working directory
  103. cwd = os.getcwd()
  104. # Set basename directory variable
  105. basename = os.path.basename(cwd)
  106. # Set current time in a possible dir format
  107. time = basename + "-" + datetime.datetime.now().strftime('%G-%b-%d-%I_%M%p_%S') + "-" + comment
  108. # Generate a hash of the current dir
  109. qhash = hashlib.sha1(cwd.encode("UTF-8")).hexdigest()[:7]
  110. # Set basehash var to basename and hash
  111. basehash = basename + "-" + qhash
  112. # Backup folder name will be /location/basename-hash/time
  113. # Add basehash to backup_location
  114. backup_location += "/" + basehash
  115. # We'll need a variable set to backup_location at the point later
  116. backbase = backup_location
  117. # At this point we know the save location like ~/backrs exists
  118. # We need to create the folder specifically for this folder
  119. # This creates ~/backrs/basename-hash
  120. if not os.path.exists(backup_location):
  121. os.makedirs(backup_location)
  122. # At time to backup_location to create a folder for this specific backup
  123. # backup_location is now, for example ~/backrs/basename-hash/basename-time-comment
  124. # Remember that "time" variable contains basename-time-comment
  125. backup_location += "/" + time
  126. # Create the basename-time-comment directory
  127. if not os.path.exists(backup_location):
  128. os.makedirs(backup_location)
  129. # This line does the actual backup, it copies the current directory to the backup location
  130. copy_tree(cwd, backup_location)
  131. # If use_compression was set, make a tarball of the backup
  132. # Then delete the original backup, then output that folder was backed up
  133. # Output varies depending on whether or not it was compressed
  134. if use_compression:
  135. make_tarfile(backbase+"/"+time+".tar.gz", backup_location)
  136. shutil.rmtree(backup_location)
  137. print "folder backed up to "+backbase+"/"+time+".tar.gz"
  138. else:
  139. print "folder backed up to "+backup_location
  140. # We use a file called backtrack.txt to keep track of how many backups
  141. # there are and their locations, this is necessary for restor.py
  142. # It is stored in the backbase, eg ~/backrs/basename-hash
  143. vc_file = backbase+"/backtrack.txt"
  144. # If the file does not exist we create it using pickle
  145. # We only write the backup location or, if compressed, the tarball location
  146. if not os.path.exists(vc_file):
  147. fw = open(vc_file, 'wb')
  148. if not use_compression:
  149. data = [backup_location]
  150. else:
  151. data = [backbase+"/"+time+".tar.gz"]
  152. pickle.dump(data, fw)
  153. fw.close()
  154. # If backtrack.txt already exists we use pickle to set its contents to the data var which we will add to
  155. else:
  156. data = pickle.load(open(vc_file, "rb"))
  157. if not use_compression:
  158. data += [backup_location]
  159. else:
  160. data += [backbase+"/"+time+".tar.gz"]
  161. fw = open(vc_file, 'wb')
  162. pickle.dump(data, fw)
  163. fw.close()
  164. # Use the except KeyboardInterrupt on main trick to allow the user to C-c anytime
  165. if __name__ == "__main__":
  166. try:
  167. main()
  168. except KeyboardInterrupt:
  169. print
  170. print "exit"
  171. sys.exit(0)