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.
 
 
 
 
 
 

786 lines
21 KiB

  1. # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  2. # License: MIT. See LICENSE
  3. # imports - standard imports
  4. import gzip
  5. import os
  6. from calendar import timegm
  7. from datetime import datetime
  8. from glob import glob
  9. from shutil import which
  10. # imports - third party imports
  11. import click
  12. # imports - module imports
  13. import frappe
  14. from frappe import conf
  15. from frappe.utils import get_file_size, get_url, now, now_datetime, cint
  16. from frappe.utils.password import get_encryption_key
  17. # backup variable for backwards compatibility
  18. verbose = False
  19. compress = False
  20. _verbose = verbose
  21. base_tables = ["__Auth", "__global_search", "__UserSettings"]
  22. class BackupGenerator:
  23. """
  24. This class contains methods to perform On Demand Backup
  25. To initialize, specify (db_name, user, password, db_file_name=None, db_host="localhost")
  26. If specifying db_file_name, also append ".sql.gz"
  27. """
  28. def __init__(
  29. self,
  30. db_name,
  31. user,
  32. password,
  33. backup_path=None,
  34. backup_path_db=None,
  35. backup_path_files=None,
  36. backup_path_private_files=None,
  37. db_host="localhost",
  38. db_port=None,
  39. db_type="mariadb",
  40. backup_path_conf=None,
  41. ignore_conf=False,
  42. compress_files=False,
  43. include_doctypes="",
  44. exclude_doctypes="",
  45. verbose=False,
  46. ):
  47. global _verbose
  48. self.compress_files = compress_files or compress
  49. self.db_host = db_host
  50. self.db_port = db_port
  51. self.db_name = db_name
  52. self.db_type = db_type
  53. self.user = user
  54. self.password = password
  55. self.backup_path = backup_path
  56. self.backup_path_conf = backup_path_conf
  57. self.backup_path_db = backup_path_db
  58. self.backup_path_files = backup_path_files
  59. self.backup_path_private_files = backup_path_private_files
  60. self.ignore_conf = ignore_conf
  61. self.include_doctypes = include_doctypes
  62. self.exclude_doctypes = exclude_doctypes
  63. self.partial = False
  64. if not self.db_type:
  65. self.db_type = "mariadb"
  66. if not self.db_port:
  67. if self.db_type == "mariadb":
  68. self.db_port = 3306
  69. if self.db_type == "postgres":
  70. self.db_port = 5432
  71. site = frappe.local.site or frappe.generate_hash(length=8)
  72. self.site_slug = site.replace(".", "_")
  73. self.verbose = verbose
  74. self.setup_backup_directory()
  75. self.setup_backup_tables()
  76. _verbose = verbose
  77. def setup_backup_directory(self):
  78. specified = (
  79. self.backup_path
  80. or self.backup_path_db
  81. or self.backup_path_files
  82. or self.backup_path_private_files
  83. or self.backup_path_conf
  84. )
  85. if not specified:
  86. backups_folder = get_backup_path()
  87. if not os.path.exists(backups_folder):
  88. os.makedirs(backups_folder, exist_ok=True)
  89. else:
  90. if self.backup_path:
  91. os.makedirs(self.backup_path, exist_ok=True)
  92. for file_path in set(
  93. [
  94. self.backup_path_files,
  95. self.backup_path_db,
  96. self.backup_path_private_files,
  97. self.backup_path_conf,
  98. ]
  99. ):
  100. if file_path:
  101. dir = os.path.dirname(file_path)
  102. os.makedirs(dir, exist_ok=True)
  103. def setup_backup_tables(self):
  104. """Sets self.backup_includes, self.backup_excludes based on passed args"""
  105. existing_tables = frappe.db.get_tables()
  106. def get_tables(doctypes):
  107. tables = []
  108. for doctype in doctypes:
  109. if not doctype:
  110. continue
  111. table = frappe.utils.get_table_name(doctype)
  112. if table in existing_tables:
  113. tables.append(table)
  114. return tables
  115. passed_tables = {
  116. "include": get_tables(self.include_doctypes.strip().split(",")),
  117. "exclude": get_tables(self.exclude_doctypes.strip().split(",")),
  118. }
  119. specified_tables = get_tables(frappe.conf.get("backup", {}).get("includes", []))
  120. include_tables = (specified_tables + base_tables) if specified_tables else []
  121. conf_tables = {
  122. "include": include_tables,
  123. "exclude": get_tables(frappe.conf.get("backup", {}).get("excludes", [])),
  124. }
  125. self.backup_includes = passed_tables["include"]
  126. self.backup_excludes = passed_tables["exclude"]
  127. if not (self.backup_includes or self.backup_excludes) and not self.ignore_conf:
  128. self.backup_includes = self.backup_includes or conf_tables["include"]
  129. self.backup_excludes = self.backup_excludes or conf_tables["exclude"]
  130. self.partial = (self.backup_includes or self.backup_excludes) and not self.ignore_conf
  131. @property
  132. def site_config_backup_path(self):
  133. # For backwards compatibility
  134. click.secho(
  135. "BackupGenerator.site_config_backup_path has been deprecated in favour of"
  136. " BackupGenerator.backup_path_conf",
  137. fg="yellow",
  138. )
  139. return getattr(self, "backup_path_conf", None)
  140. def get_backup(self, older_than=24, ignore_files=False, force=False):
  141. """
  142. Takes a new dump if existing file is old
  143. and sends the link to the file as email
  144. """
  145. # Check if file exists and is less than a day old
  146. # If not Take Dump
  147. if not force:
  148. (
  149. last_db,
  150. last_file,
  151. last_private_file,
  152. site_config_backup_path,
  153. ) = self.get_recent_backup(older_than)
  154. else:
  155. last_db, last_file, last_private_file, site_config_backup_path = (
  156. False,
  157. False,
  158. False,
  159. False,
  160. )
  161. if not (
  162. self.backup_path_conf
  163. and self.backup_path_db
  164. and self.backup_path_files
  165. and self.backup_path_private_files
  166. ):
  167. self.set_backup_file_name()
  168. if not (last_db and last_file and last_private_file and site_config_backup_path):
  169. self.take_dump()
  170. self.copy_site_config()
  171. if not ignore_files:
  172. self.backup_files()
  173. if frappe.get_system_settings("encrypt_backup"):
  174. self.backup_encryption()
  175. else:
  176. self.backup_path_files = last_file
  177. self.backup_path_db = last_db
  178. self.backup_path_private_files = last_private_file
  179. self.backup_path_conf = site_config_backup_path
  180. def set_backup_file_name(self):
  181. partial = "-partial" if self.partial else ""
  182. ext = "tgz" if self.compress_files else "tar"
  183. enc = "-enc" if frappe.get_system_settings("encrypt_backup") else ""
  184. self.todays_date = now_datetime().strftime("%Y%m%d_%H%M%S")
  185. for_conf = f"{self.todays_date}-{self.site_slug}-site_config_backup{enc}.json"
  186. for_db = f"{self.todays_date}-{self.site_slug}{partial}-database{enc}.sql.gz"
  187. for_public_files = f"{self.todays_date}-{self.site_slug}-files{enc}.{ext}"
  188. for_private_files = f"{self.todays_date}-{self.site_slug}-private-files{enc}.{ext}"
  189. backup_path = self.backup_path or get_backup_path()
  190. if not self.backup_path_conf:
  191. self.backup_path_conf = os.path.join(backup_path, for_conf)
  192. if not self.backup_path_db:
  193. self.backup_path_db = os.path.join(backup_path, for_db)
  194. if not self.backup_path_files:
  195. self.backup_path_files = os.path.join(backup_path, for_public_files)
  196. if not self.backup_path_private_files:
  197. self.backup_path_private_files = os.path.join(backup_path, for_private_files)
  198. def backup_encryption(self):
  199. """
  200. Encrypt all the backups created using gpg.
  201. """
  202. paths = (self.backup_path_db, self.backup_path_files, self.backup_path_private_files)
  203. for path in paths:
  204. if os.path.exists(path):
  205. cmd_string = (
  206. "gpg --yes --passphrase {passphrase} --pinentry-mode loopback -c {filelocation}"
  207. )
  208. try:
  209. command = cmd_string.format(
  210. passphrase=get_encryption_key(),
  211. filelocation=path,
  212. )
  213. frappe.utils.execute_in_shell(command)
  214. os.rename(path + ".gpg", path)
  215. except Exception as err:
  216. print(err)
  217. click.secho("Error occurred during encryption. Files are stored without encryption.", fg="red")
  218. def get_recent_backup(self, older_than, partial=False):
  219. backup_path = get_backup_path()
  220. if not frappe.get_system_settings("encrypt_backup"):
  221. file_type_slugs = {
  222. "database": "*-{{}}-{}database.sql.gz".format('*' if partial else ''),
  223. "public": "*-{}-files.tar",
  224. "private": "*-{}-private-files.tar",
  225. "config": "*-{}-site_config_backup.json",
  226. }
  227. else:
  228. file_type_slugs = {
  229. "database": "*-{{}}-{}database.enc.sql.gz".format('*' if partial else ''),
  230. "public": "*-{}-files.enc.tar",
  231. "private": "*-{}-private-files.enc.tar",
  232. "config": "*-{}-site_config_backup.json",
  233. }
  234. def backup_time(file_path):
  235. file_name = file_path.split(os.sep)[-1]
  236. file_timestamp = file_name.split("-")[0]
  237. return timegm(datetime.strptime(file_timestamp, "%Y%m%d_%H%M%S").utctimetuple())
  238. def get_latest(file_pattern):
  239. file_pattern = os.path.join(backup_path, file_pattern.format(self.site_slug))
  240. file_list = glob(file_pattern)
  241. if file_list:
  242. return max(file_list, key=backup_time)
  243. def old_enough(file_path):
  244. if file_path:
  245. if not os.path.isfile(file_path) or is_file_old(file_path, older_than):
  246. return None
  247. return file_path
  248. latest_backups = {
  249. file_type: get_latest(pattern) for file_type, pattern in file_type_slugs.items()
  250. }
  251. recent_backups = {
  252. file_type: old_enough(file_name) for file_type, file_name in latest_backups.items()
  253. }
  254. return (
  255. recent_backups.get("database"),
  256. recent_backups.get("public"),
  257. recent_backups.get("private"),
  258. recent_backups.get("config"),
  259. )
  260. def zip_files(self):
  261. # For backwards compatibility - pre v13
  262. click.secho(
  263. "BackupGenerator.zip_files has been deprecated in favour of"
  264. " BackupGenerator.backup_files",
  265. fg="yellow",
  266. )
  267. return self.backup_files()
  268. def get_summary(self):
  269. summary = {
  270. "config": {
  271. "path": self.backup_path_conf,
  272. "size": get_file_size(self.backup_path_conf, format=True),
  273. },
  274. "database": {
  275. "path": self.backup_path_db,
  276. "size": get_file_size(self.backup_path_db, format=True),
  277. },
  278. }
  279. if os.path.exists(self.backup_path_files) and os.path.exists(
  280. self.backup_path_private_files
  281. ):
  282. summary.update(
  283. {
  284. "public": {
  285. "path": self.backup_path_files,
  286. "size": get_file_size(self.backup_path_files, format=True),
  287. },
  288. "private": {
  289. "path": self.backup_path_private_files,
  290. "size": get_file_size(self.backup_path_private_files, format=True),
  291. },
  292. }
  293. )
  294. return summary
  295. def print_summary(self):
  296. backup_summary = self.get_summary()
  297. print("Backup Summary for {0} at {1}".format(frappe.local.site, now()))
  298. title = max(len(x) for x in backup_summary)
  299. path = max(len(x["path"]) for x in backup_summary.values())
  300. for _type, info in backup_summary.items():
  301. template = "{{0:{0}}}: {{1:{1}}} {{2}}".format(title, path)
  302. print(template.format(_type.title(), info["path"], info["size"]))
  303. def backup_files(self):
  304. for folder in ("public", "private"):
  305. files_path = frappe.get_site_path(folder, "files")
  306. backup_path = (
  307. self.backup_path_files if folder == "public" else self.backup_path_private_files
  308. )
  309. if self.compress_files:
  310. cmd_string = "tar cf - {1} | gzip > {0}"
  311. else:
  312. cmd_string = "tar -cf {0} {1}"
  313. frappe.utils.execute_in_shell(
  314. cmd_string.format(backup_path, files_path),
  315. verbose=self.verbose,
  316. low_priority=True
  317. )
  318. def copy_site_config(self):
  319. site_config_backup_path = self.backup_path_conf
  320. site_config_path = os.path.join(frappe.get_site_path(), "site_config.json")
  321. with open(site_config_backup_path, "w") as n, open(site_config_path) as c:
  322. n.write(c.read())
  323. def take_dump(self):
  324. import frappe.utils
  325. from frappe.utils.change_log import get_app_branch
  326. db_exc = {
  327. "mariadb": ("mysqldump", which("mysqldump")),
  328. "postgres": ("pg_dump", which("pg_dump")),
  329. }[self.db_type]
  330. gzip_exc = which("gzip")
  331. if not (gzip_exc and db_exc[1]):
  332. _exc = "gzip" if not gzip_exc else db_exc[0]
  333. frappe.throw(
  334. f"{_exc} not found in PATH! This is required to take a backup.",
  335. exc=frappe.ExecutableNotFound
  336. )
  337. db_exc = db_exc[0]
  338. database_header_content = [
  339. f"Backup generated by Frappe {frappe.__version__} on branch {get_app_branch('frappe') or 'N/A'}",
  340. "",
  341. ]
  342. # escape reserved characters
  343. args = frappe._dict(
  344. [item[0], frappe.utils.esc(str(item[1]), "$ ")]
  345. for item in self.__dict__.copy().items()
  346. )
  347. if self.backup_includes:
  348. backup_info = ("Backing Up Tables: ", ", ".join(self.backup_includes))
  349. elif self.backup_excludes:
  350. backup_info = ("Skipping Tables: ", ", ".join(self.backup_excludes))
  351. if self.partial:
  352. if self.verbose:
  353. print(''.join(backup_info), "\n")
  354. database_header_content.extend([
  355. f"Partial Backup of Frappe Site {frappe.local.site}",
  356. ("Backup contains: " if self.backup_includes else "Backup excludes: ") + backup_info[1],
  357. "",
  358. ])
  359. generated_header = "\n".join(f"-- {x}" for x in database_header_content) + "\n"
  360. with gzip.open(args.backup_path_db, "wt") as f:
  361. f.write(generated_header)
  362. if self.db_type == "postgres":
  363. if self.backup_includes:
  364. args["include"] = " ".join(
  365. ["--table='public.\"{0}\"'".format(table) for table in self.backup_includes]
  366. )
  367. elif self.backup_excludes:
  368. args["exclude"] = " ".join(
  369. ["--exclude-table-data='public.\"{0}\"'".format(table) for table in self.backup_excludes]
  370. )
  371. cmd_string = (
  372. "{db_exc} postgres://{user}:{password}@{db_host}:{db_port}/{db_name}"
  373. " {include} {exclude} | {gzip} >> {backup_path_db}"
  374. )
  375. else:
  376. if self.backup_includes:
  377. args["include"] = " ".join(["'{0}'".format(x) for x in self.backup_includes])
  378. elif self.backup_excludes:
  379. args["exclude"] = " ".join(
  380. [
  381. "--ignore-table='{0}.{1}'".format(frappe.conf.db_name, table)
  382. for table in self.backup_excludes
  383. ]
  384. )
  385. cmd_string = (
  386. "{db_exc} --single-transaction --quick --lock-tables=false -u {user}"
  387. " -p{password} {db_name} -h {db_host} -P {db_port} {include} {exclude}"
  388. " | {gzip} >> {backup_path_db}"
  389. )
  390. command = cmd_string.format(
  391. user=args.user,
  392. password=args.password,
  393. db_exc=db_exc,
  394. db_host=args.db_host,
  395. db_port=args.db_port,
  396. db_name=args.db_name,
  397. backup_path_db=args.backup_path_db,
  398. exclude=args.get("exclude", ""),
  399. include=args.get("include", ""),
  400. gzip=gzip_exc,
  401. )
  402. if self.verbose:
  403. print(command + "\n")
  404. frappe.utils.execute_in_shell(command, low_priority=True)
  405. def send_email(self):
  406. """
  407. Sends the link to backup file located at erpnext/backups
  408. """
  409. from frappe.email import get_system_managers
  410. recipient_list = get_system_managers()
  411. db_backup_url = get_url(
  412. os.path.join("backups", os.path.basename(self.backup_path_db))
  413. )
  414. files_backup_url = get_url(
  415. os.path.join("backups", os.path.basename(self.backup_path_files))
  416. )
  417. msg = """Hello,
  418. Your backups are ready to be downloaded.
  419. 1. [Click here to download the database backup](%(db_backup_url)s)
  420. 2. [Click here to download the files backup](%(files_backup_url)s)
  421. This link will be valid for 24 hours. A new backup will be available for
  422. download only after 24 hours.""" % {
  423. "db_backup_url": db_backup_url,
  424. "files_backup_url": files_backup_url,
  425. }
  426. datetime_str = datetime.fromtimestamp(os.stat(self.backup_path_db).st_ctime)
  427. subject = (
  428. datetime_str.strftime("%d/%m/%Y %H:%M:%S") + """ - Backup ready to be downloaded"""
  429. )
  430. frappe.sendmail(recipients=recipient_list, message=msg, subject=subject)
  431. return recipient_list
  432. @frappe.whitelist()
  433. def fetch_latest_backups(partial=False):
  434. """Fetches paths of the latest backup taken in the last 30 days
  435. Only for: System Managers
  436. Returns:
  437. dict: relative Backup Paths
  438. """
  439. frappe.only_for("System Manager")
  440. odb = BackupGenerator(
  441. frappe.conf.db_name,
  442. frappe.conf.db_name,
  443. frappe.conf.db_password,
  444. db_host=frappe.db.host,
  445. db_type=frappe.conf.db_type,
  446. db_port=frappe.conf.db_port,
  447. )
  448. database, public, private, config = odb.get_recent_backup(older_than=24 * 30, partial=partial)
  449. return {"database": database, "public": public, "private": private, "config": config}
  450. def scheduled_backup(
  451. older_than=6,
  452. ignore_files=False,
  453. backup_path=None,
  454. backup_path_db=None,
  455. backup_path_files=None,
  456. backup_path_private_files=None,
  457. backup_path_conf=None,
  458. ignore_conf=False,
  459. include_doctypes="",
  460. exclude_doctypes="",
  461. compress=False,
  462. force=False,
  463. verbose=False,
  464. ):
  465. """this function is called from scheduler
  466. deletes backups older than 7 days
  467. takes backup"""
  468. odb = new_backup(
  469. older_than=older_than,
  470. ignore_files=ignore_files,
  471. backup_path=backup_path,
  472. backup_path_db=backup_path_db,
  473. backup_path_files=backup_path_files,
  474. backup_path_private_files=backup_path_private_files,
  475. backup_path_conf=backup_path_conf,
  476. ignore_conf=ignore_conf,
  477. include_doctypes=include_doctypes,
  478. exclude_doctypes=exclude_doctypes,
  479. compress=compress,
  480. force=force,
  481. verbose=verbose,
  482. )
  483. return odb
  484. def new_backup(
  485. older_than=6,
  486. ignore_files=False,
  487. backup_path=None,
  488. backup_path_db=None,
  489. backup_path_files=None,
  490. backup_path_private_files=None,
  491. backup_path_conf=None,
  492. ignore_conf=False,
  493. include_doctypes="",
  494. exclude_doctypes="",
  495. compress=False,
  496. force=False,
  497. verbose=False,
  498. ):
  499. delete_temp_backups()
  500. odb = BackupGenerator(
  501. frappe.conf.db_name,
  502. frappe.conf.db_name,
  503. frappe.conf.db_password,
  504. db_host=frappe.db.host,
  505. db_port=frappe.db.port,
  506. db_type=frappe.conf.db_type,
  507. backup_path=backup_path,
  508. backup_path_db=backup_path_db,
  509. backup_path_files=backup_path_files,
  510. backup_path_private_files=backup_path_private_files,
  511. backup_path_conf=backup_path_conf,
  512. ignore_conf=ignore_conf,
  513. include_doctypes=include_doctypes,
  514. exclude_doctypes=exclude_doctypes,
  515. verbose=verbose,
  516. compress_files=compress,
  517. )
  518. odb.get_backup(older_than, ignore_files, force=force)
  519. return odb
  520. def delete_temp_backups(older_than=24):
  521. """
  522. Cleans up the backup_link_path directory by deleting older files
  523. """
  524. older_than = cint(frappe.conf.keep_backups_for_hours) or older_than
  525. backup_path = get_backup_path()
  526. if os.path.exists(backup_path):
  527. file_list = os.listdir(get_backup_path())
  528. for this_file in file_list:
  529. this_file_path = os.path.join(get_backup_path(), this_file)
  530. if is_file_old(this_file_path, older_than):
  531. os.remove(this_file_path)
  532. def is_file_old(file_path, older_than=24):
  533. """
  534. Checks if file exists and is older than specified hours
  535. Returns ->
  536. True: file does not exist or file is old
  537. False: file is new
  538. """
  539. if os.path.isfile(file_path):
  540. from datetime import timedelta
  541. # Get timestamp of the file
  542. file_datetime = datetime.fromtimestamp(os.stat(file_path).st_ctime)
  543. if datetime.today() - file_datetime >= timedelta(hours=older_than):
  544. if _verbose:
  545. print(f"File {file_path} is older than {older_than} hours")
  546. return True
  547. else:
  548. if _verbose:
  549. print(f"File {file_path} is recent")
  550. return False
  551. else:
  552. if _verbose:
  553. print(f"File {file_path} does not exist")
  554. return True
  555. def get_backup_path():
  556. backup_path = frappe.utils.get_site_path(conf.get("backup_path", "private/backups"))
  557. return backup_path
  558. @frappe.whitelist()
  559. def get_backup_encryption_key():
  560. frappe.only_for("System Manager")
  561. return frappe.conf.encryption_key
  562. class Backup:
  563. def __init__(self, file_path):
  564. self.file_path = file_path
  565. def backup_decryption(self,passphrase):
  566. """
  567. Decrypts backup at the given path using the passphrase.
  568. """
  569. if not os.path.exists(self.file_path):
  570. print("Invalid path", self.file_path)
  571. return
  572. else:
  573. os.rename(self.file_path, self.file_path + ".gpg")
  574. file_path = self.file_path + ".gpg"
  575. cmd_string = (
  576. "gpg --yes --passphrase {passphrase} --pinentry-mode loopback -o {decrypted_file} -d {file_location}"
  577. )
  578. command = cmd_string.format(
  579. passphrase=passphrase,
  580. file_location=file_path,
  581. decrypted_file=file_path.rstrip(".gpg"),
  582. )
  583. frappe.utils.execute_in_shell(command)
  584. def decryption_rollback(self):
  585. """
  586. Checks if the decrypted file exists at the given path.
  587. if exists
  588. Renames the orginal encrypted file.
  589. else
  590. Removes the decrypted file and rename the original file.
  591. """
  592. if os.path.exists(self.file_path + ".gpg"):
  593. if os.path.exists(self.file_path):
  594. os.remove(self.file_path)
  595. if os.path.exists(self.file_path.rstrip(".gz")):
  596. os.remove(self.file_path.rstrip(".gz"))
  597. os.rename(self.file_path + ".gpg", self.file_path)
  598. def backup(
  599. with_files=False,
  600. backup_path_db=None,
  601. backup_path_files=None,
  602. backup_path_private_files=None,
  603. backup_path_conf=None,
  604. quiet=False,
  605. ):
  606. "Backup"
  607. odb = scheduled_backup(
  608. ignore_files=not with_files,
  609. backup_path_db=backup_path_db,
  610. backup_path_files=backup_path_files,
  611. backup_path_private_files=backup_path_private_files,
  612. backup_path_conf=backup_path_conf,
  613. force=True,
  614. )
  615. return {
  616. "backup_path_db": odb.backup_path_db,
  617. "backup_path_files": odb.backup_path_files,
  618. "backup_path_private_files": odb.backup_path_private_files,
  619. }
  620. if __name__ == "__main__":
  621. import sys
  622. cmd = sys.argv[1]
  623. db_type = "mariadb"
  624. try:
  625. db_type = sys.argv[6]
  626. except IndexError:
  627. pass
  628. db_port = 3306
  629. try:
  630. db_port = int(sys.argv[7])
  631. except IndexError:
  632. pass
  633. if cmd == "is_file_old":
  634. odb = BackupGenerator(
  635. sys.argv[2],
  636. sys.argv[3],
  637. sys.argv[4],
  638. sys.argv[5] or "localhost",
  639. db_type=db_type,
  640. db_port=db_port,
  641. )
  642. is_file_old(odb.db_file_name)
  643. if cmd == "get_backup":
  644. odb = BackupGenerator(
  645. sys.argv[2],
  646. sys.argv[3],
  647. sys.argv[4],
  648. sys.argv[5] or "localhost",
  649. db_type=db_type,
  650. db_port=db_port,
  651. )
  652. odb.get_backup()
  653. if cmd == "take_dump":
  654. odb = BackupGenerator(
  655. sys.argv[2],
  656. sys.argv[3],
  657. sys.argv[4],
  658. sys.argv[5] or "localhost",
  659. db_type=db_type,
  660. db_port=db_port,
  661. )
  662. odb.take_dump()
  663. if cmd == "send_email":
  664. odb = BackupGenerator(
  665. sys.argv[2],
  666. sys.argv[3],
  667. sys.argv[4],
  668. sys.argv[5] or "localhost",
  669. db_type=db_type,
  670. db_port=db_port,
  671. )
  672. odb.send_email()
  673. if cmd == "delete_temp_backups":
  674. delete_temp_backups()