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.
 
 
 
 
 
 

217 regels
7.3 KiB

  1. # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. """This module handles the On Demand Backup utility"""
  4. from __future__ import unicode_literals
  5. #Imports
  6. from frappe import _
  7. import os, frappe
  8. from datetime import datetime
  9. from frappe.utils import cstr, get_url
  10. #Global constants
  11. verbose = 0
  12. from frappe import conf
  13. #-------------------------------------------------------------------------------
  14. class BackupGenerator:
  15. """
  16. This class contains methods to perform On Demand Backup
  17. To initialize, specify (db_name, user, password, db_file_name=None, db_host="localhost")
  18. If specifying db_file_name, also append ".sql.gz"
  19. """
  20. def __init__(self, db_name, user, password, backup_path_db=None, backup_path_files=None, db_host="localhost"):
  21. self.db_host = db_host
  22. self.db_name = db_name
  23. self.user = user
  24. self.password = password
  25. self.backup_path_files = backup_path_files
  26. self.backup_path_db = backup_path_db
  27. def get_backup(self, older_than=24, ignore_files=False):
  28. """
  29. Takes a new dump if existing file is old
  30. and sends the link to the file as email
  31. """
  32. #Check if file exists and is less than a day old
  33. #If not Take Dump
  34. last_db, last_file = self.get_recent_backup(older_than)
  35. if not (self.backup_path_files and self.backup_path_db):
  36. self.set_backup_file_name()
  37. if not (last_db and last_file):
  38. self.take_dump()
  39. if not ignore_files:
  40. self.zip_files()
  41. else:
  42. self.backup_path_files = last_file
  43. self.backup_path_db = last_db
  44. def set_backup_file_name(self):
  45. import random
  46. todays_date = "".join(str(datetime.date(datetime.today())).split("-"))
  47. random_number = str(int(random.random()*99999999))
  48. #Generate a random name using today's date and a 8 digit random number
  49. for_db = todays_date + "_" + random_number + "_database.sql.gz"
  50. for_files = todays_date + "_" + random_number + "_files.tar"
  51. backup_path = get_backup_path()
  52. if not self.backup_path_db:
  53. self.backup_path_db = os.path.join(backup_path, for_db)
  54. if not self.backup_path_files:
  55. self.backup_path_files = os.path.join(backup_path, for_files)
  56. def get_recent_backup(self, older_than):
  57. file_list = os.listdir(get_backup_path())
  58. backup_path_files = None
  59. backup_path_db = None
  60. for this_file in file_list:
  61. this_file = cstr(this_file)
  62. this_file_path = os.path.join(get_backup_path(), this_file)
  63. if not is_file_old(this_file_path, older_than):
  64. if "_files" in this_file_path:
  65. backup_path_files = this_file_path
  66. if "_database" in this_file_path:
  67. backup_path_db = this_file_path
  68. return (backup_path_db, backup_path_files)
  69. def zip_files(self):
  70. files_path = frappe.get_site_path("public", "files")
  71. cmd_string = """tar -cf %s %s""" % (self.backup_path_files, files_path)
  72. err, out = frappe.utils.execute_in_shell(cmd_string)
  73. def take_dump(self):
  74. import frappe.utils
  75. # escape reserved characters
  76. args = dict([item[0], frappe.utils.esc(item[1], '$ ')]
  77. for item in self.__dict__.copy().items())
  78. cmd_string = """mysqldump --single-transaction --quick --lock-tables=false -u %(user)s -p%(password)s %(db_name)s -h %(db_host)s | gzip -c > %(backup_path_db)s""" % args
  79. err, out = frappe.utils.execute_in_shell(cmd_string)
  80. def send_email(self):
  81. """
  82. Sends the link to backup file located at erpnext/backups
  83. """
  84. from frappe.email import sendmail, get_system_managers
  85. recipient_list = get_system_managers()
  86. db_backup_url = get_url(os.path.join('backups', os.path.basename(self.backup_path_db)))
  87. files_backup_url = get_url(os.path.join('backups', os.path.basename(self.backup_path_files)))
  88. msg = """Hello,
  89. Your backups are ready to be downloaded.
  90. 1. [Click here to download the database backup](%(db_backup_url)s)
  91. 2. [Click here to download the files backup](%(files_backup_url)s)
  92. This link will be valid for 24 hours. A new backup will be available for
  93. download only after 24 hours.""" % {
  94. "db_backup_url": db_backup_url,
  95. "files_backup_url": files_backup_url
  96. }
  97. datetime_str = datetime.fromtimestamp(os.stat(self.backup_path_db).st_ctime)
  98. subject = datetime_str.strftime("%d/%m/%Y %H:%M:%S") + """ - Backup ready to be downloaded"""
  99. sendmail(recipients=recipient_list, msg=msg, subject=subject)
  100. return recipient_list
  101. @frappe.whitelist()
  102. def get_backup():
  103. """
  104. This function is executed when the user clicks on
  105. Toos > Download Backup
  106. """
  107. #if verbose: print frappe.db.cur_db_name + " " + conf.db_password
  108. delete_temp_backups()
  109. odb = BackupGenerator(frappe.conf.db_name, frappe.conf.db_name,\
  110. frappe.conf.db_password, db_host = frappe.db.host)
  111. odb.get_backup()
  112. recipient_list = odb.send_email()
  113. frappe.msgprint(_("Download link for your backup will be emailed on the following email address: {0}").format(', '.join(recipient_list)))
  114. def scheduled_backup(older_than=6, ignore_files=False, backup_path_db=None, backup_path_files=None):
  115. """this function is called from scheduler
  116. deletes backups older than 7 days
  117. takes backup"""
  118. odb = new_backup(older_than, ignore_files, backup_path_db=backup_path_db, backup_path_files=backup_path_files)
  119. return odb
  120. def new_backup(older_than=6, ignore_files=False, backup_path_db=None, backup_path_files=None):
  121. delete_temp_backups(older_than=168)
  122. odb = BackupGenerator(frappe.conf.db_name, frappe.conf.db_name,\
  123. frappe.conf.db_password,
  124. backup_path_db=backup_path_db, backup_path_files=backup_path_files, db_host = frappe.db.host)
  125. odb.get_backup(older_than, ignore_files)
  126. return odb
  127. def delete_temp_backups(older_than=24):
  128. """
  129. Cleans up the backup_link_path directory by deleting files older than 24 hours
  130. """
  131. file_list = os.listdir(get_backup_path())
  132. for this_file in file_list:
  133. this_file_path = os.path.join(get_backup_path(), this_file)
  134. if is_file_old(this_file_path, older_than):
  135. os.remove(this_file_path)
  136. def is_file_old(db_file_name, older_than=24):
  137. """
  138. Checks if file exists and is older than specified hours
  139. Returns ->
  140. True: file does not exist or file is old
  141. False: file is new
  142. """
  143. if os.path.isfile(db_file_name):
  144. from datetime import timedelta
  145. #Get timestamp of the file
  146. file_datetime = datetime.fromtimestamp\
  147. (os.stat(db_file_name).st_ctime)
  148. if datetime.today() - file_datetime >= timedelta(hours = older_than):
  149. if verbose: print "File is old"
  150. return True
  151. else:
  152. if verbose: print "File is recent"
  153. return False
  154. else:
  155. if verbose: print "File does not exist"
  156. return True
  157. def get_backup_path():
  158. backup_path = frappe.utils.get_site_path(conf.get("backup_path", "private/backups"))
  159. return backup_path
  160. #-------------------------------------------------------------------------------
  161. if __name__ == "__main__":
  162. """
  163. is_file_old db_name user password db_host
  164. get_backup db_name user password db_host
  165. """
  166. import sys
  167. cmd = sys.argv[1]
  168. if cmd == "is_file_old":
  169. odb = BackupGenerator(sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5] or "localhost")
  170. is_file_old(odb.db_file_name)
  171. if cmd == "get_backup":
  172. odb = BackupGenerator(sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5] or "localhost")
  173. odb.get_backup()
  174. if cmd == "take_dump":
  175. odb = BackupGenerator(sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5] or "localhost")
  176. odb.take_dump()
  177. if cmd == "send_email":
  178. odb = BackupGenerator(sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5] or "localhost")
  179. odb.send_email("abc.sql.gz")
  180. if cmd == "delete_temp_backups":
  181. delete_temp_backups()