Add hypervisors table + move script (preserves VM->hypervisor link via vcenter_vm_name)
This commit is contained in:
parent
683a86346d
commit
0a00c401d7
126
tools/move_hypervisors.py
Normal file
126
tools/move_hypervisors.py
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
"""Move hypervisors from servers table to a dedicated hypervisors table.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python tools/move_hypervisors.py <hyperviseur_csv_path>
|
||||||
|
"""
|
||||||
|
import csv
|
||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
from sqlalchemy import create_engine, text
|
||||||
|
|
||||||
|
DATABASE_URL = os.getenv("DATABASE_URL_DEMO") or os.getenv("DATABASE_URL") \
|
||||||
|
or "postgresql://patchcenter:PatchCenter2026!@localhost:5432/patchcenter_demo"
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("csv_path", help="Hyperviseur CSV iTop")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
engine = create_engine(DATABASE_URL)
|
||||||
|
print(f"[INFO] DB: {DATABASE_URL.split('@')[-1]}")
|
||||||
|
print(f"[INFO] CSV: {args.csv_path}")
|
||||||
|
|
||||||
|
with open(args.csv_path, "r", encoding="utf-8-sig", newline="") as f:
|
||||||
|
reader = csv.DictReader(f)
|
||||||
|
rows = list(reader)
|
||||||
|
print(f"[INFO] {len(rows)} lignes CSV")
|
||||||
|
|
||||||
|
conn = engine.connect().execution_options(isolation_level="AUTOCOMMIT")
|
||||||
|
|
||||||
|
# Crée la table si pas existante
|
||||||
|
conn.execute(text("""
|
||||||
|
CREATE TABLE IF NOT EXISTS hypervisors (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
hostname citext UNIQUE NOT NULL,
|
||||||
|
fqdn varchar(255),
|
||||||
|
cluster_name varchar(100),
|
||||||
|
underlying_server varchar(100),
|
||||||
|
site varchar(100),
|
||||||
|
domain_ltd varchar(50),
|
||||||
|
description text,
|
||||||
|
responsable_nom text,
|
||||||
|
moved_from_server_id integer,
|
||||||
|
created_at timestamptz DEFAULT now()
|
||||||
|
)
|
||||||
|
"""))
|
||||||
|
|
||||||
|
moved = 0
|
||||||
|
not_in_servers = 0
|
||||||
|
skipped = 0
|
||||||
|
|
||||||
|
for r in rows:
|
||||||
|
hostname = (r.get("Nom") or r.get("Hostname") or "").strip()
|
||||||
|
if not hostname or hostname in ("(null)",) or not any(c.isalpha() for c in hostname):
|
||||||
|
skipped += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
srv = conn.execute(text(
|
||||||
|
"SELECT id, fqdn, site, domain_ltd, patch_owner_details, responsable_nom FROM servers WHERE hostname=:h"
|
||||||
|
), {"h": hostname}).fetchone()
|
||||||
|
|
||||||
|
if not srv:
|
||||||
|
# Pas en base servers, on insère quand même dans hypervisors
|
||||||
|
try:
|
||||||
|
conn.execute(text("""
|
||||||
|
INSERT INTO hypervisors (hostname, cluster_name, underlying_server, description, responsable_nom)
|
||||||
|
VALUES (:h, :cl, :srv, :desc, :resp)
|
||||||
|
ON CONFLICT (hostname) DO NOTHING
|
||||||
|
"""), {
|
||||||
|
"h": hostname,
|
||||||
|
"cl": (r.get("vCluster->Nom") or "").strip()[:100] or None,
|
||||||
|
"srv": (r.get("Serveur->Nom") or "").strip()[:100] or None,
|
||||||
|
"desc": (r.get("Description") or "").strip() or None,
|
||||||
|
"resp": None,
|
||||||
|
})
|
||||||
|
not_in_servers += 1
|
||||||
|
except Exception as e:
|
||||||
|
print(f" [ERR insert] {hostname}: {str(e)[:120]}")
|
||||||
|
skipped += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Insert dans hypervisors
|
||||||
|
conn.execute(text("""
|
||||||
|
INSERT INTO hypervisors
|
||||||
|
(hostname, fqdn, cluster_name, underlying_server, site, domain_ltd,
|
||||||
|
description, responsable_nom, moved_from_server_id)
|
||||||
|
VALUES (:h, :fqdn, :cl, :usrv, :site, :domain, :desc, :resp, :sid)
|
||||||
|
ON CONFLICT (hostname) DO UPDATE SET
|
||||||
|
cluster_name=EXCLUDED.cluster_name,
|
||||||
|
underlying_server=EXCLUDED.underlying_server,
|
||||||
|
moved_from_server_id=EXCLUDED.moved_from_server_id
|
||||||
|
"""), {
|
||||||
|
"h": hostname, "fqdn": srv.fqdn,
|
||||||
|
"cl": (r.get("vCluster->Nom") or "").strip()[:100] or None,
|
||||||
|
"usrv": (r.get("Serveur->Nom") or "").strip()[:100] or None,
|
||||||
|
"site": srv.site, "domain": srv.domain_ltd,
|
||||||
|
"desc": (r.get("Description") or srv.patch_owner_details or "").strip()[:1000] or None,
|
||||||
|
"resp": srv.responsable_nom,
|
||||||
|
"sid": srv.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
# Nettoie les FK puis delete du servers
|
||||||
|
for tbl, col in [("server_ips", "server_id"), ("qualys_assets", "server_id"),
|
||||||
|
("server_audit", "server_id"), ("server_audit_full", "server_id"),
|
||||||
|
("server_correspondance", "server_id"), ("cluster_members", "server_id"),
|
||||||
|
("patch_history", "server_id"), ("patch_sessions", "server_id"),
|
||||||
|
("patch_validation", "server_id"), ("quickwin_entries", "server_id"),
|
||||||
|
("quickwin_server_config", "server_id"), ("server_specifics", "server_id"),
|
||||||
|
("server_pairs", "server_a_id"), ("server_pairs", "server_b_id")]:
|
||||||
|
try:
|
||||||
|
conn.execute(text(f"DELETE FROM {tbl} WHERE {col}=:sid"), {"sid": srv.id})
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
conn.execute(text("DELETE FROM servers WHERE id=:sid"), {"sid": srv.id})
|
||||||
|
moved += 1
|
||||||
|
except Exception as e:
|
||||||
|
print(f" [ERR move] {hostname}: {str(e)[:150]}")
|
||||||
|
skipped += 1
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
print(f"[DONE] Deplaces: {moved} | Inseres directement (pas en servers): {not_in_servers} | Ignores: {skipped}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
Reference in New Issue
Block a user