Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

backups.py 7.8 KiB

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