patchcenter/app/services/quickwin_snapshot_service.py
Khalid MOUTAOUAKIL e96d79aae3 QuickWin: prereq/snapshot services, referentiel, logs, correspondance
- Split quickwin services: prereq, snapshot, log services
- Add referentiel router and template
- QuickWin detail: prereq/snapshot terminal divs for production
- Server edit partial updates
- QuickWin correspondance and logs templates
- Base template updates

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 18:13:00 +02:00

145 lines
4.9 KiB
Python

"""Service snapshot QuickWin — prise de snapshots VM via vSphere/pyvmomi
Ordre de recherche des VM sur les vCenters:
- Hors-prod: Senlis (vpgesavcs1) → Nanterre (vpmetavcs1) → DR (vpsicavcs1)
- Prod: Nanterre (vpmetavcs1) → Senlis (vpgesavcs1) → DR (vpsicavcs1)
Physiques: pas de snapshot, alerte Commvault."""
import ssl
import logging
from datetime import datetime
log = logging.getLogger("quickwin.snapshot")
try:
from pyVim.connect import SmartConnect, Disconnect
from pyVmomi import vim
PYVMOMI_OK = True
except ImportError:
PYVMOMI_OK = False
log.warning("pyvmomi non disponible — snapshots impossibles")
def _get_secret(db, key):
try:
from ..services.secrets_service import get_secret
return get_secret(db, key)
except Exception:
return None
def _connect_vcenter(endpoint, user, password):
"""Connexion a un vCenter. Retourne un ServiceInstance ou None."""
try:
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
si = SmartConnect(host=endpoint, user=user, pwd=password, sslContext=ctx)
return si
except Exception as e:
log.warning(f"Connexion vCenter {endpoint} echouee: {e}")
return None
def _find_vm(si, vm_name):
"""Cherche une VM par nom dans le vCenter. Retourne l'objet VM ou None."""
content = si.RetrieveContent()
container = content.viewManager.CreateContainerView(
content.rootFolder, [vim.VirtualMachine], True)
try:
for vm in container.view:
if vm.name.lower() == vm_name.lower():
return vm
finally:
container.Destroy()
return None
def _take_snapshot(vm, snap_name, description=""):
"""Prend un snapshot de la VM. Retourne (ok, message)."""
try:
task = vm.CreateSnapshot_Task(
name=snap_name,
description=description,
memory=False,
quiesce=True,
)
# Attendre la fin du task
while task.info.state in (vim.TaskInfo.State.queued, vim.TaskInfo.State.running):
import time
time.sleep(2)
if task.info.state == vim.TaskInfo.State.success:
return True, "Snapshot OK"
else:
err = str(task.info.error) if task.info.error else "Echec inconnu"
return False, f"Snapshot echoue: {err}"
except Exception as e:
return False, f"Erreur snapshot: {e}"
def get_vcenter_order(db, branch):
"""Retourne la liste ordonnee des vCenters selon la branche.
hprod: Senlis → Nanterre → DR
prod: Nanterre → Senlis → DR"""
from sqlalchemy import text
vcenters = db.execute(text(
"SELECT id, name, endpoint FROM vcenters WHERE is_active = true ORDER BY id"
)).fetchall()
vc_map = {}
for vc in vcenters:
ep = vc.endpoint.lower()
if "vpgesavcs1" in ep:
vc_map["senlis"] = vc
elif "vpmetavcs1" in ep:
vc_map["nanterre"] = vc
elif "vpsicavcs1" in ep:
vc_map["dr"] = vc
else:
vc_map.setdefault("other", []).append(vc)
if branch == "prod":
order = [vc_map.get("nanterre"), vc_map.get("senlis"), vc_map.get("dr")]
else:
order = [vc_map.get("senlis"), vc_map.get("nanterre"), vc_map.get("dr")]
return [v for v in order if v is not None]
def snapshot_server(hostname, vm_name, branch, db, snap_name=None):
"""Prend un snapshot pour un serveur.
Cherche la VM sur les vCenters dans l'ordre selon la branche.
Retourne dict: {ok, vcenter, detail, skipped}"""
if not PYVMOMI_OK:
return {"ok": False, "vcenter": "", "detail": "pyvmomi non installe", "skipped": True}
vc_user = _get_secret(db, "vcenter_user")
vc_pass = _get_secret(db, "vcenter_pass")
if not vc_user or not vc_pass:
return {"ok": False, "vcenter": "", "detail": "Credentials vCenter manquants (vcenter_user/vcenter_pass dans Settings > Secrets)", "skipped": True}
search_name = vm_name or hostname
if not snap_name:
snap_name = f"QW_{datetime.now().strftime('%Y%m%d_%H%M')}"
vcenters = get_vcenter_order(db, branch)
if not vcenters:
return {"ok": False, "vcenter": "", "detail": "Aucun vCenter actif configure", "skipped": True}
for vc in vcenters:
si = _connect_vcenter(vc.endpoint, vc_user, vc_pass)
if not si:
continue
try:
vm = _find_vm(si, search_name)
if vm:
ok, msg = _take_snapshot(vm, snap_name,
description=f"QuickWin auto-snapshot {hostname}")
return {"ok": ok, "vcenter": vc.name, "detail": msg}
finally:
try:
Disconnect(si)
except Exception:
pass
tried = ", ".join(vc.name for vc in vcenters)
return {"ok": False, "vcenter": "", "detail": f"VM '{search_name}' non trouvee sur: {tried}"}