tw22
This commit is contained in:
149
NighMedicusRestoreTW22/05 FullCodeNightlyRestore.py
Normal file
149
NighMedicusRestoreTW22/05 FullCodeNightlyRestore.py
Normal file
@@ -0,0 +1,149 @@
|
||||
#!/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}")
|
||||
Reference in New Issue
Block a user