109 lines
4.2 KiB
Python
109 lines
4.2 KiB
Python
"""Sync-Logik: liest Subnetze + Adressen aus phpipam und schreibt sie als
|
||
ein Outline-Dokument pro Subnetz."""
|
||
from __future__ import annotations
|
||
|
||
from .config import settings
|
||
from .outline_client import OutlineClient
|
||
from .phpipam_client import PhpIpamClient
|
||
|
||
|
||
def _subnet_title(subnet: dict) -> str:
|
||
cidr = f"{subnet.get('subnet')}/{subnet.get('mask')}"
|
||
desc = subnet.get("description") or ""
|
||
return f"{cidr} – {desc}".rstrip(" –") if desc else cidr
|
||
|
||
|
||
def _subnet_markdown(subnet: dict, addresses: list[dict], section_name: str | None) -> str:
|
||
cidr = f"{subnet.get('subnet')}/{subnet.get('mask')}"
|
||
lines: list[str] = []
|
||
lines.append(f"# {cidr}")
|
||
lines.append("")
|
||
lines.append(f"- **Beschreibung:** {subnet.get('description') or '–'}")
|
||
if section_name:
|
||
lines.append(f"- **Section:** {section_name}")
|
||
if subnet.get("vlanId") and subnet.get("vlanId") != "0":
|
||
lines.append(f"- **VLAN-ID:** {subnet.get('vlanId')}")
|
||
if subnet.get("location"):
|
||
lines.append(f"- **Standort:** {subnet.get('location')}")
|
||
lines.append("")
|
||
|
||
if not addresses:
|
||
lines.append("_Keine belegten Adressen in phpipam hinterlegt._")
|
||
return "\n".join(lines)
|
||
|
||
lines.append("| IP | Hostname | Beschreibung | MAC | Owner |")
|
||
lines.append("|---|---|---|---|---|")
|
||
for addr in sorted(addresses, key=lambda a: tuple(int(o) for o in a["ip"].split("."))):
|
||
lines.append(
|
||
"| {ip} | {hostname} | {desc} | {mac} | {owner} |".format(
|
||
ip=addr.get("ip", ""),
|
||
hostname=addr.get("hostname") or "",
|
||
desc=(addr.get("description") or "").replace("\n", " "),
|
||
mac=addr.get("mac") or "",
|
||
owner=addr.get("owner") or "",
|
||
)
|
||
)
|
||
return "\n".join(lines)
|
||
|
||
|
||
def run_sync(dry_run: bool | None = None) -> list[dict]:
|
||
"""Führt den Sync durch. Gibt eine Liste von Ergebnis-Infos je Subnetz zurück."""
|
||
dry_run = settings.sync_dry_run if dry_run is None else dry_run
|
||
results: list[dict] = []
|
||
section_cache: dict[str, str | None] = {}
|
||
|
||
with PhpIpamClient() as ipam:
|
||
subnets = ipam.list_subnets()
|
||
|
||
outline = None if dry_run else OutlineClient()
|
||
collection_id = settings.outline_collection_id
|
||
parent_document_id = settings.outline_parent_document_id
|
||
try:
|
||
if outline is not None:
|
||
collection_id = outline.resolve_collection_id(settings.outline_collection_id)
|
||
if parent_document_id:
|
||
parent_document_id = outline.resolve_document_id(parent_document_id)
|
||
|
||
for subnet in subnets:
|
||
section_id = subnet.get("sectionId")
|
||
if section_id not in section_cache:
|
||
section_cache[section_id] = ipam.get_section_name(section_id) if section_id else None
|
||
section_name = section_cache.get(section_id)
|
||
|
||
addresses = ipam.list_addresses(subnet["id"])
|
||
title = _subnet_title(subnet)
|
||
text = _subnet_markdown(subnet, addresses, section_name)
|
||
|
||
if dry_run:
|
||
results.append(
|
||
{
|
||
"subnet_id": subnet["id"],
|
||
"title": title,
|
||
"address_count": len(addresses),
|
||
"action": "dry-run",
|
||
}
|
||
)
|
||
continue
|
||
|
||
doc, created = outline.upsert_document( # type: ignore[union-attr]
|
||
title,
|
||
text,
|
||
collection_id,
|
||
parent_document_id,
|
||
)
|
||
results.append(
|
||
{
|
||
"subnet_id": subnet["id"],
|
||
"title": title,
|
||
"address_count": len(addresses),
|
||
"action": "created" if created else "updated",
|
||
"outline_document_id": doc.get("id"),
|
||
"outline_url": doc.get("url"),
|
||
}
|
||
)
|
||
finally:
|
||
if outline is not None:
|
||
outline.close()
|
||
|
||
return results
|