Add tag_obsolete_os: licence_support=obsolete pour OS EOL (RHEL 5/6/7, Win 2008/2012, Debian 8-10, etc.)

This commit is contained in:
Pierre & Lumière 2026-04-14 20:36:19 +02:00
parent 0cfdab7c61
commit 7f6ccc763b

109
tools/tag_obsolete_os.py Normal file
View File

@ -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()