docker-to-netbox/docker-to-netbox.py
2025-05-14 12:00:14 +00:00

152 lines
4.9 KiB
Python

#!/usr/bin/env python3
import docker
import requests
import socket
import netifaces
import os
from pathlib import Path
import sys
CONFIG_PATH = "/etc/netbox-docker-sync.conf"
# === Konfigurationsdatei lesen ===
def read_config(filepath):
if not Path(filepath).is_file():
print(f"[FEHLER] Datei '{filepath}' nicht gefunden.")
sys.exit(1)
config = {}
try:
with open(filepath) as f:
for line in f:
line = line.strip()
if not line or line.startswith("#"):
continue
if "=" in line:
key, value = line.split("=", 1)
config[key.strip()] = value.strip()
except Exception as e:
print(f"[FEHLER] Fehler beim Lesen der Datei: {e}")
sys.exit(1)
# Pflichtfelder prüfen
required_keys = ["NETBOX_CLUSTER_ID", "NETBOX_URL", "NETBOX_TOKEN"]
for key in required_keys:
if key not in config or not config[key]:
print(f"[FEHLER] Konfigurationswert fehlt oder leer: {key}")
sys.exit(1)
# Cluster ID validieren
if not config["NETBOX_CLUSTER_ID"].isdigit():
print("[FEHLER] NETBOX_CLUSTER_ID ist keine gültige Zahl.")
sys.exit(1)
config["NETBOX_CLUSTER_ID"] = int(config["NETBOX_CLUSTER_ID"])
return config
# === Konfiguration laden ===
cfg = read_config(CONFIG_PATH)
CLUSTER_ID = cfg["NETBOX_CLUSTER_ID"]
NETBOX_URL = cfg["NETBOX_URL"]
NETBOX_TOKEN = cfg["NETBOX_TOKEN"]
NETBOX_HEADERS = {
"Authorization": f"Token {NETBOX_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json"
}
# === Standard-Netzwerkinterface automatisch ermitteln ===
def detect_default_interface():
try:
gws = netifaces.gateways()
return gws['default'][netifaces.AF_INET][1]
except Exception as e:
print(f"[!] Konnte Standard-Interface nicht ermitteln: {e}")
return "lo"
DOCKER_IFACE = os.environ.get("DOCKER_IFACE") or detect_default_interface()
# === IP-Adresse des Dockerhosts korrekt ermitteln ===
def get_host_ip():
try:
ip = netifaces.ifaddresses(DOCKER_IFACE)[netifaces.AF_INET][0]['addr']
if ip.startswith("127.") or ip == "":
raise ValueError("Ungültige IP-Adresse erkannt.")
return ip
except Exception as e:
print(f"[!] Konnte Host-IP über Interface {DOCKER_IFACE} nicht ermitteln: {e}")
return "0.0.0.0"
# Docker-Client initialisieren
client = docker.from_env()
DOCKER_HOSTNAME = socket.gethostname()
DOCKER_HOST_IP = get_host_ip()
# === Container erfassen und mit NetBox abgleichen ===
for container in client.containers.list():
name = container.name
image = container.image.tags[0] if container.image.tags else "unknown"
container_ip = container.attrs["NetworkSettings"]["IPAddress"]
# --- Hier Labels auslesen ---
labels = container.labels
project = labels.get("com.docker.compose.project", "")
service = labels.get("com.docker.compose.service", "")
version = labels.get("com.docker.compose.version", "")
# Exposed Ports auslesen (nur IPv4)
ports_raw = container.attrs["NetworkSettings"]["Ports"] or {}
exposed_ports = []
for port, mappings in ports_raw.items():
if mappings:
for mapping in mappings:
host_ip = mapping['HostIp']
if host_ip == "0.0.0.0" or host_ip.count(".") == 3:
exposed_ports.append(f"{host_ip}:{mapping['HostPort']} -> {port}")
else:
exposed_ports.append(f"(no mapping) -> {port}")
ports_string = ", ".join(exposed_ports)
print(f"[→] Verarbeite Container: {name} ({container_ip}) Ports: {ports_string}")
# Payload für NetBox-Eintrag
vm_payload = {
"name": name,
"status": "active",
"cluster": CLUSTER_ID,
"custom_fields": {
"container_ip": container_ip,
"docker_host": DOCKER_HOSTNAME,
"docker_host_ip": DOCKER_HOST_IP,
"docker_image": image,
"docker_ports": ports_string,
"compose_project": project,
"compose_service": service,
"stack_version": version
}
}
# Existiert die VM bereits in NetBox?
r = requests.get(
f"{NETBOX_URL}/virtualization/virtual-machines/?name={name}",
headers=NETBOX_HEADERS
)
if r.ok and r.json()["count"] > 0:
vm_id = r.json()["results"][0]["id"]
update = requests.patch(
f"{NETBOX_URL}/virtualization/virtual-machines/{vm_id}/",
headers=NETBOX_HEADERS,
json=vm_payload
)
print(f"[=] Aktualisiert: {name} ({update.status_code})")
else:
create = requests.post(
f"{NETBOX_URL}/virtualization/virtual-machines/",
headers=NETBOX_HEADERS,
json=vm_payload
)
print(f"[+] Erstellt: {name} ({create.status_code})")