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.

backups.py 6.1 KiB

14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. """
  2. This module handles the On Demand Backup utility
  3. To setup in defs set:
  4. backup_path: path where backups will be taken (for eg /backups)
  5. backup_link_path: download link for backups (eg /var/www/wnframework/backups)
  6. backup_url: base url of the backup folder (eg http://mysite.com/backups)
  7. """
  8. #Imports
  9. import os, webnotes
  10. from datetime import datetime
  11. #Global constants
  12. from webnotes.defs import backup_path, backup_link_path, backup_url
  13. verbose = 0
  14. #-------------------------------------------------------------------------------
  15. class BackupGenerator:
  16. """
  17. This class contains methods to perform On Demand Backup
  18. To initialize, specify (db_name, user, password, db_file_name=None)
  19. If specifying db_file_name, also append ".sql.gz"
  20. """
  21. def __init__(self, db_name, user, password, db_file_name=None):
  22. self.db_name = db_name.replace('$', '\$')
  23. self.user = user
  24. self.password = password
  25. self.db_file_name = db_file_name and db_file_name \
  26. or (os.path.join(backup_path, self.db_name + ".sql.gz"))
  27. def take_dump(self):
  28. """
  29. Dumps a db via mysqldump
  30. """
  31. os.system("""mysqldump -u %(user)s -p%(password)s %(db_name)s |
  32. gzip -c > %(db_file_name)s""" % self.__dict__)
  33. def copy_to_backup_link(self):
  34. """
  35. Copies the backup file from backup path to shared link path
  36. It also gives the file a random name, thus hiding the db_name
  37. """
  38. import random
  39. todays_date = "".join(str(datetime.date(datetime.today())).split("-"))
  40. random_number = str(int(random.random()*99999999))
  41. #Generate a random name using today's date and a 8 digit random number
  42. random_name = todays_date + "_" + random_number + ".sql.gz"
  43. os.system("""cp -f %(src_file)s %(dest_file)s""" % \
  44. {"src_file":self.db_file_name,
  45. "dest_file":os.path.join(backup_link_path, random_name)})
  46. if verbose: print "file copied"
  47. return random_name
  48. def get_recipients(self):
  49. """
  50. Get recepient's email address
  51. """
  52. #import webnotes.db
  53. #webnotes.conn = webnotes.db.Database(use_default=1)
  54. recipient_list = webnotes.conn.sql(\
  55. """SELECT parent FROM tabUserRole
  56. WHERE role='System Manager'
  57. AND parent!='Administrator'
  58. AND parent IN
  59. (SELECT email FROM tabProfile WHERE enabled=1)""")
  60. return [i[0] for i in recipient_list]
  61. def send_email(self, backup_file):
  62. """
  63. Sends the link to backup file located at erpnext/backups
  64. """
  65. file_url = os.path.join(backup_url, backup_file)
  66. from webnotes.utils.email_lib import sendmail
  67. recipient_list = self.get_recipients()
  68. msg = """<a href=%(file_url)s>Click here to begin downloading\
  69. your backup</a>
  70. This link will be valid for 24 hours.
  71. Also, a new backup will be available for download (if requested)\
  72. only after 24 hours.""" % {"file_url":file_url}
  73. datetime_str = datetime.fromtimestamp(os.stat(self.db_file_name.replace('\$', '$')).st_ctime)
  74. subject = datetime_str.strftime("%d/%m/%Y %H:%M:%S") + """ - Backup ready to be downloaded"""
  75. sendmail(recipients=recipient_list, msg=msg, subject=subject)
  76. return recipient_list
  77. def get_backup(self):
  78. """
  79. Takes a new dump if existing file is old
  80. and sends the link to the file as email
  81. """
  82. #Check if file exists and is less than a day old
  83. #If not Take Dump
  84. if is_file_old(self.db_file_name):
  85. self.take_dump()
  86. #Copy file to backup_link_path
  87. #This is a security hole. When the user calls get_backup repeatedly
  88. #a file will be generated each time.
  89. backup_file = self.copy_to_backup_link()
  90. #Email Link
  91. recipient_list = self.send_email(backup_file)
  92. return recipient_list
  93. #-------------------------------------------------------------------------------
  94. def get_backup():
  95. """
  96. This function is executed when the user clicks on
  97. Toos > Download Backup
  98. """
  99. #if verbose: print webnotes.conn.cur_db_name + " " + webnotes.defs.db_password
  100. odb = BackupGenerator(webnotes.conn.cur_db_name, webnotes.conn.cur_db_name,\
  101. get_db_password(webnotes.conn.cur_db_name))
  102. recipient_list = odb.get_backup()
  103. delete_temp_backups()
  104. webnotes.msgprint("""A download link to your backup will be emailed \
  105. to you shortly on the following email address:
  106. %s""" % (', '.join(recipient_list)))
  107. def get_db_password(db_name):
  108. """
  109. Get db password from defs
  110. """
  111. from webnotes import defs
  112. if hasattr(defs, 'get_db_password'):
  113. return defs.get_db_password(db_name)
  114. if hasattr(defs, 'db_password'):
  115. return defs.db_password
  116. def delete_temp_backups():
  117. """
  118. Cleans up the backup_link_path directory by deleting files older than 24 hours
  119. """
  120. file_list = os.listdir(backup_link_path)
  121. for this_file in file_list:
  122. this_file_path = os.path.join(backup_link_path, this_file)
  123. if is_file_old(this_file_path):
  124. os.remove(this_file_path)
  125. def is_file_old(db_file_name, older_than=24):
  126. """
  127. Checks if file exists and is older than specified hours
  128. Returns ->
  129. True: file does not exist or file is old
  130. False: file is new
  131. """
  132. if os.path.isfile(db_file_name):
  133. from datetime import timedelta
  134. import time
  135. #Get timestamp of the file
  136. file_datetime = datetime.fromtimestamp\
  137. (os.stat(db_file_name).st_ctime)
  138. if datetime.today() - file_datetime >= timedelta(hours = older_than):
  139. if verbose: print "File is old"
  140. return True
  141. else:
  142. if verbose: print "File is recent"
  143. return False
  144. else:
  145. if verbose: print "File does not exist"
  146. return True
  147. #-------------------------------------------------------------------------------
  148. if __name__ == "__main__":
  149. """
  150. is_file_old db_name user password
  151. get_backup db_name user password
  152. """
  153. import sys
  154. cmd = sys.argv[1]
  155. if cmd == "is_file_old":
  156. odb = BackupGenerator(sys.argv[2], sys.argv[3], sys.argv[4])
  157. is_file_old(odb.db_file_name)
  158. if cmd == "get_backup":
  159. odb = BackupGenerator(sys.argv[2], sys.argv[3], sys.argv[4])
  160. odb.get_backup()
  161. if cmd == "take_dump":
  162. odb = BackupGenerator(sys.argv[2], sys.argv[3], sys.argv[4])
  163. odb.take_dump()
  164. if cmd == "send_email":
  165. odb = BackupGenerator(sys.argv[2], sys.argv[3], sys.argv[4])
  166. odb.send_email("abc.sql.gz")
  167. if cmd == "delete_temp_backups":
  168. delete_temp_backups()