Files
medicus/NighMedicusRestoreTW22/05 FullCodeNightlyRestore.py
2025-11-04 09:36:26 +01:00

150 lines
5.4 KiB
Python
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Nightly Medicus automatic restore
---------------------------------
Workflow:
1⃣ Kill all Medicus processes
2⃣ Restart Firebird service (to release all open connections)
3⃣ Clean restore folder
4⃣ Find and extract newest Medicus backup (.fbk or .zip)
5⃣ Restore database using gbak (Firebird service kept running)
6⃣ Restart Firebird again to refresh state
7⃣ Log all actions with timestamps
"""
import os
import psutil
import shutil
import zipfile
import subprocess
from pathlib import Path
from datetime import datetime
import time
# =========================================
# 🔧 CONFIGURATION
# =========================================
FIREBIRD_GBAK = Path(r"C:\Program Files\Firebird\Firebird_2_5_CGM\bin\gbak.exe")
BACKUP_DIR = Path(r"G:\OnedriveOrdinace\OneDrive\MedicusBackup")
RESTORE_DIR = Path(r"Z:\Medicus 3\restore")
TARGET_DB = Path(r"Z:\Medicus 3\data\MEDICUS.FDB")
SERVICE_NAME = "FirebirdServerCGM"
USER = "SYSDBA"
PASSWORD = "masterkey"
LOG_FILE = RESTORE_DIR / f"nightly_restore_{datetime.now():%Y%m%d_%H%M%S}.log"
# =========================================
# 🪓 1) Kill Medicus processes
# =========================================
def kill_medicus():
targets = ["Medicus.exe", "MedicusServer.exe", "MedicusUpdater.exe"]
with open(LOG_FILE, "w", encoding="utf-8") as log:
log.write(f"==== NIGHTLY RESTORE START {datetime.now():%Y-%m-%d %H:%M:%S} ====\n")
for proc in psutil.process_iter(["name", "pid"]):
try:
if proc.info["name"] and proc.info["name"].lower() in [t.lower() for t in targets]:
log.write(f"Killing {proc.info['name']} (PID {proc.pid})\n")
proc.kill()
except (psutil.NoSuchProcess, psutil.AccessDenied):
continue
log.write("All Medicus processes terminated.\n")
# =========================================
# ⚙️ 2) Firebird service control
# =========================================
def service_cmd(action):
cmd = f'net {action} "{SERVICE_NAME}"'
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
return result.returncode == 0, result.stdout + result.stderr
def restart_firebird(label):
ok, out = service_cmd("stop")
with open(LOG_FILE, "a", encoding="utf-8") as log:
log.write(f"\n--- Restart Firebird ({label}) ---\n")
log.write(f"Stopping {SERVICE_NAME}...\n{out}\n")
time.sleep(5)
ok, out = service_cmd("start")
with open(LOG_FILE, "a", encoding="utf-8") as log:
log.write(f"Starting {SERVICE_NAME}...\n{out}\n")
# =========================================
# 🧹 3) Clean restore folder
# =========================================
def clean_restore_folder():
for item in RESTORE_DIR.glob("*"):
try:
if item.is_file():
item.unlink()
elif item.is_dir():
shutil.rmtree(item)
except Exception as e:
with open(LOG_FILE, "a", encoding="utf-8") as log:
log.write(f"⚠️ Could not delete {item}: {e}\n")
# =========================================
# 💾 4) Find newest backup and prepare .fbk
# =========================================
def prepare_backup():
backups = list(BACKUP_DIR.glob("Medicus_*.fbk")) + list(BACKUP_DIR.glob("Medicus_*.zip"))
if not backups:
raise SystemExit(f"❌ No Medicus backups found in {BACKUP_DIR}")
latest = max(backups, key=lambda f: f.stat().st_mtime)
fbk_path = RESTORE_DIR / (latest.stem + ".fbk")
with open(LOG_FILE, "a", encoding="utf-8") as log:
log.write(f"\nUsing backup: {latest}\n")
if latest.suffix.lower() == ".fbk":
shutil.copy2(latest, fbk_path)
else:
with zipfile.ZipFile(latest, "r") as zf:
fbk_files = [n for n in zf.namelist() if n.lower().endswith(".fbk")]
if not fbk_files:
raise SystemExit("❌ ZIP file does not contain .fbk!")
zf.extract(fbk_files[0], RESTORE_DIR)
extracted = RESTORE_DIR / fbk_files[0]
if extracted != fbk_path:
shutil.move(extracted, fbk_path)
return fbk_path
# =========================================
# 🧩 5) Restore database using gbak
# =========================================
def restore_database(fbk_path):
cmd = f'"{FIREBIRD_GBAK}" -rep "{fbk_path}" "{TARGET_DB}" -user {USER} -pas {PASSWORD}'
with open(LOG_FILE, "a", encoding="utf-8") as log:
log.write(f"\nRunning restore command:\n{cmd}\n")
result = subprocess.run(cmd, shell=True, text=True, capture_output=True)
with open(LOG_FILE, "a", encoding="utf-8") as log:
log.write(result.stdout)
log.write(result.stderr)
log.write(f"Return code: {result.returncode}\n")
if result.returncode == 0:
print("✅ Medicus database successfully restored!")
else:
print("❌ Restore failed! Check log for details.")
# =========================================
# 🚀 MAIN WORKFLOW
# =========================================
if __name__ == "__main__":
kill_medicus()
restart_firebird("before restore")
clean_restore_folder()
fbk = prepare_backup()
restore_database(fbk)
restart_firebird("after restore")
with open(LOG_FILE, "a", encoding="utf-8") as log:
log.write(f"\n==== RESTORE COMPLETED {datetime.now():%Y-%m-%d %H:%M:%S} ====\n")
print(f"📝 Full log saved to {LOG_FILE}")