patchcenter/tools/move_chassis.py
Admin MPCZ 987e21377b Add move_esxi_extras + move_chassis scripts
move_esxi_extras: identifie ESXi par description (patch_owner_details
contient ESXi/hebergeant/hyperviseur) pour les PDP BAC_* et autres
hyperviseurs non presents dans le CSV Hyperviseur iTop. Deplace vers
hypervisors (kind=hypervisor).

move_chassis: deplace les chassis (CPEM*) vers une table chassis dediee
(non patchables, pas d'agent Qualys).
2026-04-14 18:09:26 +02:00

94 lines
3.8 KiB
Python

"""Move chassis (enclosures hardware type CPEM*) vers table chassis dediee.
Les chassis ne sont pas des OS patchables (pas d'agent Qualys, pas de SSH).
On les sort de servers pour garder servers propre, mais on garde la tracabilite CMDB.
Usage:
python tools/move_chassis.py [--dry-run] [--pattern CPEM%]
"""
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("--dry-run", action="store_true")
parser.add_argument("--pattern", default="CPEM%",
help="ILIKE pattern pour hostname chassis (default: CPEM%%)")
args = parser.parse_args()
engine = create_engine(DATABASE_URL)
print(f"[INFO] DB: {DATABASE_URL.split('@')[-1]}")
print(f"[INFO] Pattern: {args.pattern}")
conn = engine.connect().execution_options(isolation_level="AUTOCOMMIT")
conn.execute(text("""
CREATE TABLE IF NOT EXISTS chassis (
id SERIAL PRIMARY KEY,
hostname citext UNIQUE NOT NULL,
fqdn varchar(255),
site varchar(100),
domain_ltd varchar(50),
description text,
responsable_nom text,
moved_from_server_id integer,
created_at timestamptz DEFAULT now()
)
"""))
rows = conn.execute(text(
"SELECT id, hostname, fqdn, site, domain_ltd, patch_owner_details, responsable_nom "
"FROM servers WHERE hostname ILIKE :p "
" OR patch_owner_details ILIKE '%chassis%' "
" OR patch_owner_details ILIKE '%enclosure%'"
), {"p": args.pattern}).fetchall()
print(f"[INFO] {len(rows)} candidats chassis trouves")
moved = 0
skipped = 0
for r in rows:
if args.dry_run:
print(f" DRY: {r.hostname} -- {(r.patch_owner_details or '')[:80]}")
continue
try:
conn.execute(text("""
INSERT INTO chassis
(hostname, fqdn, site, domain_ltd, description, responsable_nom, moved_from_server_id)
VALUES (:h, :fqdn, :site, :domain, :desc, :resp, :sid)
ON CONFLICT (hostname) DO UPDATE SET
description=EXCLUDED.description,
moved_from_server_id=EXCLUDED.moved_from_server_id
"""), {
"h": r.hostname, "fqdn": r.fqdn, "site": r.site,
"domain": r.domain_ltd, "desc": r.patch_owner_details,
"resp": r.responsable_nom, "sid": r.id,
})
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": r.id})
except Exception:
pass
conn.execute(text("DELETE FROM servers WHERE id=:sid"), {"sid": r.id})
moved += 1
except Exception as e:
print(f" [ERR] {r.hostname}: {str(e)[:150]}")
skipped += 1
conn.close()
print(f"[DONE] Deplaces: {moved} | Ignores: {skipped}")
if __name__ == "__main__":
main()