fix(servers): bulk + edit comparaisons domain/env/zone case-insensitive (BD mixte RECETTE/Recette/recette) + fallback bulk env_code si serveur sans domain_env_id + log INFO/WARNING + retour msg=bulk_<n_updated_reel>

This commit is contained in:
Pierre & Lumière 2026-05-05 15:19:03 +02:00
parent 5d3c07885d
commit 1b82440813
2 changed files with 63 additions and 25 deletions

View File

@ -239,41 +239,65 @@ async def servers_bulk(request: Request, db=Depends(get_db),
from sqlalchemy import text as sqlt
bulk_value_clean = (bulk_value or "").strip()
n_updated = 0
if bulk_field in ("tier", "etat", "patch_os_owner", "licence_support"):
db.execute(sqlt(f"UPDATE servers SET {bulk_field} = :val WHERE id = ANY(:ids)"),
{"val": bulk_value, "ids": ids})
res = db.execute(sqlt(f"UPDATE servers SET {bulk_field} = :val WHERE id = ANY(:ids)"),
{"val": bulk_value_clean, "ids": ids})
n_updated = res.rowcount or 0
elif bulk_field == "domain_code":
# Trouver le domain_env_id correspondant (prod par defaut)
# Tous les serveurs prennent le 1er domain_env de ce domaine
# (priorité Production > autres via display_order)
row = db.execute(sqlt("""
SELECT de.id FROM domain_environments de
JOIN domains d ON de.domain_id = d.id
JOIN environments e ON de.environment_id = e.id
WHERE d.code = :dc ORDER BY e.display_order LIMIT 1
"""), {"dc": bulk_value}).fetchone()
WHERE LOWER(d.code) = LOWER(:dc) ORDER BY e.display_order LIMIT 1
"""), {"dc": bulk_value_clean}).fetchone()
if row:
db.execute(sqlt("UPDATE servers SET domain_env_id = :deid WHERE id = ANY(:ids)"),
{"deid": row.id, "ids": ids})
res = db.execute(sqlt("UPDATE servers SET domain_env_id = :deid WHERE id = ANY(:ids)"),
{"deid": row.id, "ids": ids})
n_updated = res.rowcount or 0
else:
logger.warning(f"servers_bulk domain_code: aucun domain_environments pour {bulk_value_clean!r}")
elif bulk_field == "env_code":
# Pour chaque serveur, garder son domaine mais changer l'env
# Pour chaque serveur, garder son domaine actuel et changer l'env.
# Si serveur sans domain_env_id, fallback : 1er domain dispo + cet env.
for sid in ids:
srv = db.execute(sqlt("""
SELECT d.id as did FROM servers s
JOIN domain_environments de ON s.domain_env_id = de.id
JOIN domains d ON de.domain_id = d.id
SELECT s.id, s.domain_env_id, de.domain_id AS did
FROM servers s
LEFT JOIN domain_environments de ON s.domain_env_id = de.id
WHERE s.id = :sid
"""), {"sid": sid}).fetchone()
if srv:
if not srv:
continue
did = srv.did # peut être None si serveur sans domaine
if did:
de = db.execute(sqlt("""
SELECT de.id FROM domain_environments de
JOIN environments e ON de.environment_id = e.id
WHERE de.domain_id = :did AND e.code = :ec
"""), {"did": srv.did, "ec": bulk_value}).fetchone()
if de:
db.execute(sqlt("UPDATE servers SET domain_env_id = :deid WHERE id = :sid"),
{"deid": de.id, "sid": sid})
WHERE de.domain_id = :did AND LOWER(e.code) = LOWER(:ec)
"""), {"did": did, "ec": bulk_value_clean}).fetchone()
else:
# Fallback : on prend n'importe quel domain_env avec cet env
de = db.execute(sqlt("""
SELECT de.id FROM domain_environments de
JOIN environments e ON de.environment_id = e.id
WHERE LOWER(e.code) = LOWER(:ec)
ORDER BY de.id LIMIT 1
"""), {"ec": bulk_value_clean}).fetchone()
if de:
db.execute(sqlt("UPDATE servers SET domain_env_id = :deid WHERE id = :sid"),
{"deid": de.id, "sid": sid})
n_updated += 1
else:
logger.warning(f"servers_bulk env_code: pas de domain_env pour env {bulk_value_clean!r} (sid={sid}, did={did})")
db.commit()
return RedirectResponse(url=f"/servers?msg=bulk_{len(ids)}", status_code=303)
logger.info(f"servers_bulk: field={bulk_field} value={bulk_value_clean!r} ids={len(ids)} updated={n_updated}")
return RedirectResponse(url=f"/servers?msg=bulk_{n_updated}", status_code=303)
@router.post("/servers/{server_id}/sync-qualys", response_class=HTMLResponse)

View File

@ -208,25 +208,39 @@ def list_servers(db, filters, page=1, per_page=50, sort="hostname", sort_dir="as
def update_server(db, server_id, data, username):
"""Met a jour un serveur et log l'action"""
"""Met a jour un serveur et log l'action.
Comparaisons code domain/env/zone en case-insensitive (la BD mixte
'RECETTE'/'Recette'/'recette' n'a pas une casse cohérente)."""
import logging
log = logging.getLogger("patchcenter.server")
# Domain + Env -> domain_env_id
if data.get("domain_code") and data.get("env_code"):
dc = (data.get("domain_code") or "").strip()
ec = (data.get("env_code") or "").strip()
if dc and ec:
row = db.execute(text("""
SELECT de.id FROM domain_environments de
JOIN domains d ON de.domain_id = d.id
JOIN environments e ON de.environment_id = e.id
WHERE d.code = :dc AND e.code = :ec
"""), {"dc": data["domain_code"], "ec": data["env_code"]}).fetchone()
WHERE LOWER(d.code) = LOWER(:dc) AND LOWER(e.code) = LOWER(:ec)
"""), {"dc": dc, "ec": ec}).fetchone()
if row:
db.execute(text("UPDATE servers SET domain_env_id = :deid WHERE id = :id"),
{"deid": row.id, "id": server_id})
else:
log.warning(f"update_server({server_id}): pas de domain_environments pour ({dc!r}, {ec!r})")
# Zone
if data.get("zone"):
zrow = db.execute(text("SELECT id FROM zones WHERE name = :z"), {"z": data["zone"]}).fetchone()
# Zone (case-insensitive sur name)
zn = (data.get("zone") or "").strip()
if zn:
zrow = db.execute(text(
"SELECT id FROM zones WHERE LOWER(name) = LOWER(:z)"
), {"z": zn}).fetchone()
if zrow:
db.execute(text("UPDATE servers SET zone_id = :zid WHERE id = :id"),
{"zid": zrow.id, "id": server_id})
else:
log.warning(f"update_server({server_id}): zone {zn!r} introuvable")
# IPs (reelle + connexion)
update_server_ips(db, server_id, data.get("ip_reelle"), data.get("ip_connexion"))