diff --git a/tools/tag_obsolete_os.py b/tools/tag_obsolete_os.py new file mode 100644 index 0000000..aea9b73 --- /dev/null +++ b/tools/tag_obsolete_os.py @@ -0,0 +1,109 @@ +"""Tag servers avec OS en fin de support -> licence_support='obsolete'. + +Regles EOL (end of life) appliquees : + - RHEL 5, 6, 7 (7.9 EOL 2024-06-30) + - CentOS 5, 6, 7, 8 + - Oracle Linux 5, 6, 7 + - Windows Server 2003, 2008, 2012, 2012 R2 + - Debian 8, 9, 10 (Debian 10 EOL 2024-06-30) + - Ubuntu 12.04, 14.04, 16.04, 18.04 + - SUSE < 12 + +N'affecte que licence_support (pas etat). Le patching existant exclut deja +les serveurs avec licence_support='obsolete'. + +Usage: + python tools/tag_obsolete_os.py [--dry-run] [--overwrite] +""" +import os +import re +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" + +# Patterns regex (case-insensitive) sur os_version indiquant un OS EOL +EOL_PATTERNS = [ + (r"red\s*hat.*(?:release\s+)?[567]\b", "RHEL 5/6/7"), + (r"centos.*(?:release\s+)?[5678]\b", "CentOS 5/6/7/8"), + (r"oracle.*linux.*(?:release\s+)?[567]\b", "Oracle Linux 5/6/7"), + (r"windows.*2003", "Windows 2003"), + (r"windows.*2008", "Windows 2008"), + (r"windows.*2012", "Windows 2012"), + (r"debian.*(?:gnu/linux\s+)?[89]\b", "Debian 8/9"), + (r"debian.*(?:gnu/linux\s+)?10\b", "Debian 10"), + (r"ubuntu.*1[2468]\.04", "Ubuntu 12.04/14.04/16.04/18.04"), + (r"suse.*(?:enterprise.*)?(?:server.*)?(?:sp\d\s*)?1[01]\b", "SUSE 10/11"), +] + + +def classify(os_version): + if not os_version: + return None + v = os_version.lower() + for pattern, label in EOL_PATTERNS: + if re.search(pattern, v): + return label + return None + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--dry-run", action="store_true") + parser.add_argument("--overwrite", action="store_true", + help="Remplace licence_support='active' par 'obsolete' (sinon skip les active)") + args = parser.parse_args() + + engine = create_engine(DATABASE_URL) + print(f"[INFO] DB: {DATABASE_URL.split('@')[-1]}") + conn = engine.connect().execution_options(isolation_level="AUTOCOMMIT") + + rows = conn.execute(text(""" + SELECT id, hostname, os_version, licence_support + FROM servers + WHERE os_version IS NOT NULL AND os_version != '' + """)).fetchall() + print(f"[INFO] {len(rows)} serveurs avec os_version") + + stats = {"tagged": 0, "already_obsolete": 0, "not_eol": 0, "skipped_active": 0} + by_label = {} + + for r in rows: + label = classify(r.os_version) + if not label: + stats["not_eol"] += 1 + continue + if r.licence_support == "obsolete": + stats["already_obsolete"] += 1 + continue + # Si deja 'active' ou 'els', on ne touche pas sauf --overwrite + if r.licence_support in ("active", "els") and not args.overwrite: + stats["skipped_active"] += 1 + if args.dry_run: + print(f" SKIP (--overwrite pour forcer): {r.hostname:25s} " + f"[{r.licence_support}] {label} | {r.os_version[:60]}") + continue + + by_label[label] = by_label.get(label, 0) + 1 + if args.dry_run: + print(f" DRY: {r.hostname:25s} [{r.licence_support or 'NULL'}] -> obsolete " + f"({label}) | {r.os_version[:60]}") + else: + conn.execute(text("UPDATE servers SET licence_support='obsolete' WHERE id=:sid"), + {"sid": r.id}) + stats["tagged"] += 1 + + conn.close() + print(f"\n[DONE] Tagges obsolete: {stats['tagged']} | " + f"Deja obsolete: {stats['already_obsolete']} | " + f"Non-EOL: {stats['not_eol']} | " + f"Skip (active/els): {stats['skipped_active']}") + if by_label: + print("\nRepartition par famille EOL :") + for label, n in sorted(by_label.items(), key=lambda x: -x[1]): + print(f" {label:40s} {n}") + + +if __name__ == "__main__": + main()