retention.py 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import os
  2. import re
  3. from datetime import datetime, timedelta
  4. def apply_retention(job, new_archive_name, backup_dir):
  5. """Applique la politique de rétention après une sauvegarde réussie."""
  6. archives = _list_archives_for_job(job, backup_dir)
  7. if job.retention_mode == "count":
  8. to_delete = _retention_count(archives, job.retention_value)
  9. elif job.retention_mode == "daily":
  10. to_delete = _retention_daily(archives, job.retention_value)
  11. else:
  12. return []
  13. deleted = []
  14. for archive_filename in to_delete:
  15. base = os.path.splitext(archive_filename)[0]
  16. for ext in (".tar", ".info.json"):
  17. full = os.path.join(backup_dir, base + ext)
  18. if os.path.exists(full):
  19. os.remove(full)
  20. deleted.append(base + ext)
  21. return deleted
  22. def _list_archives_for_job(job, backup_dir):
  23. """Liste les archives correspondant à ce job, triées par date (plus ancienne en premier)."""
  24. from flask import current_app
  25. instance = current_app.config["INSTANCE_NAME"]
  26. if job.type == "ynh_app":
  27. import json
  28. cfg = json.loads(job.config_json or "{}")
  29. app_id = cfg.get("app_id", "")
  30. prefix = f"{instance}_{app_id}_"
  31. elif job.type == "ynh_system":
  32. prefix = f"{instance}_system_"
  33. else:
  34. prefix = f"{instance}_{job.name.lower().replace(' ', '-')}_"
  35. archives = []
  36. for fname in os.listdir(backup_dir):
  37. if fname.startswith(prefix) and fname.endswith(".tar"):
  38. archives.append(fname)
  39. archives.sort(key=_extract_date)
  40. return archives
  41. def _extract_date(filename):
  42. match = re.search(r'(\d{8})', filename)
  43. if match:
  44. try:
  45. return datetime.strptime(match.group(1), "%Y%m%d")
  46. except ValueError:
  47. pass
  48. return datetime.min
  49. def _retention_count(archives, keep_n):
  50. if len(archives) <= keep_n:
  51. return []
  52. return archives[: len(archives) - keep_n]
  53. def _retention_daily(archives, days):
  54. cutoff = datetime.utcnow() - timedelta(days=days)
  55. to_delete = []
  56. seen_dates = set()
  57. for archive in reversed(archives):
  58. date = _extract_date(archive)
  59. if date < cutoff:
  60. to_delete.append(archive)
  61. continue
  62. date_key = date.date()
  63. if date_key in seen_dates:
  64. to_delete.append(archive)
  65. else:
  66. seen_dates.add(date_key)
  67. return to_delete