Files
phpipam-outline-sync/app/sync.py
T
2026-07-24 08:38:28 +00:00

109 lines
4.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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