/opt/netbox/report.py
class DokuReport(Script):
    class Meta:
        name = "Prüfung der gesamten Netbox Dokumentation"
        description = "Prüft alle Netbox Dokupunkte, ob die wie vorgeshenen pannsen."

    def test_device_tenants(self):
        """Prüft alle aktiven Devices auf zugewiesene Tenants."""
        active_devices = Device.objects.filter(status="active")

        for device in active_devices:
            if device.device_type.is_child_device:
                self.log_success("Child Device übersprungen.", obj=device)
                continue

            if device.tenant_id is None:
                # In NetBox v4: Erst die Nachricht, dann obj=device für die URL-Spalte
                self.log_failure("Device hat keinen Tenant zugewiesen.", obj=device)
            else:
                self.log_success("Tenant zugewiesen.", obj=device)

    def test_vm_tenants(self):
        """Prüft alle aktiven virtuellen Maschinen auf zugewiesene Tenants."""
        active_vms = VirtualMachine.objects.filter(status="active")

        for vm in active_vms:
            if vm.tenant_id is None:
                # In NetBox v4: Erst die Nachricht, dann obj=vm für die URL-Spalte
                self.log_failure("Virtual Machine hat keinen Tenant zugewiesen.", obj=vm)
            else:
                self.log_success("Tenant zugewiesen.", obj=vm)

    def test_device_descriptions(self):
        """Prüft alle aktiven Devices auf eine vorhandene Beschreibung und korrekte Interpunktion."""
        active_devices = Device.objects.filter(status="active")
        for device in active_devices:
            if not device.description or not device.description.strip():
                self.log_failure("Device Beschreibung ist leer.", obj=device)
            elif not device.description.strip().endswith('.'):
                self.log_warning("Beschreibung endet nicht mit einem Punkt.", obj=device)
            else:
                self.log_success("Beschreibung vorhanden und korrekt.", obj=device)

    def test_vm_descriptions(self):
        """Prüft alle aktiven virtuellen Maschinen auf eine vorhandene Beschreibung und korrekte Interpunktion."""
        active_vms = VirtualMachine.objects.filter(status="active")
        for vm in active_vms:
            if not vm.description or not vm.description.strip():
                self.log_failure("Virtual Machine Beschreibung ist leer.", obj=vm)
            elif not vm.description.strip().endswith('.'):
                self.log_warning("Beschreibung endet nicht mit einem Punkt.", obj=vm)
            else:
                self.log_success("Beschreibung vorhanden und korrekt.", obj=vm)

    def test_device_roles(self):
        """Prüft alle aktiven Devices auf eine zugewiesene Rolle."""
        active_devices = Device.objects.filter(status="active")
        for device in active_devices:
            if device.device_type.is_child_device:
                self.log_success("Child Device übersprungen.", obj=device)
                continue

            if device.role_id is None:
                self.log_failure("Device hat keine Rolle zugewiesen.", obj=device)
            else:
                self.log_success("Rolle zugewiesen.", obj=device)

    def test_vm_roles(self):
        """Prüft alle aktiven virtuellen Maschinen auf eine zugewiesene Rolle."""
        active_vms = VirtualMachine.objects.filter(status="active")
        for vm in active_vms:
            if vm.role_id is None:
                self.log_failure("Virtual Machine hat keine Rolle zugewiesen.", obj=vm)
            else:
                self.log_success("Rolle zugewiesen.", obj=vm)

    def test_device_sites(self):
        """Prüft alle aktiven Devices auf eine zugewiesene Site."""
        active_devices = Device.objects.filter(status="active")
        for device in active_devices:
            if device.device_type.is_child_device:
                self.log_success("Child Device übersprungen.", obj=device)
                continue

            if device.site_id is None:
                self.log_failure("Device hat keine Site zugewiesen.", obj=device)
            else:
                self.log_success("Site zugewiesen.", obj=device)

    def test_vm_sites(self):
        """Prüft alle aktiven virtuellen Maschinen auf eine zugewiesene Site."""
        active_vms = VirtualMachine.objects.filter(status="active")
        for vm in active_vms:
            # NetBox v4 erlaubt direkte site_id an der VM. Falls leer, prüfen wir das Cluster.
            if vm.site_id is not None:
                self.log_success("Site direkt an VM zugewiesen.", obj=vm)
            elif vm.cluster and vm.cluster.site_id is not None:
                self.log_success("Site über Cluster zugewiesen.", obj=vm)
            else:
                self.log_failure("Virtual Machine hat weder direkt noch über das Cluster eine Site zugewiesen.", obj=vm)

    def test_vm_host_devices(self):
        """Prüft, ob der VM das konkrete physische Host-Device (Hypervisor) zugewiesen wurde (außer OpenStack)."""
        active_vms = VirtualMachine.objects.filter(status="active")
        for vm in active_vms:
            # 1. Prüfen, ob die VM ein Cluster hat und ob der Cluster-Typ OpenStack ist
            if vm.cluster and vm.cluster.type:
                cluster_type_name = vm.cluster.type.name.lower()
                if "openstack" in cluster_type_name:
                    self.log_success("OpenStack VM - Physisches Host-Device nicht erforderlich.", obj=vm)
                    continue

            # 2. Reguläre Prüfung für alle anderen Cluster-Typen (VMware, Proxmox etc.)
            if vm.device_id is None:
                self.log_failure("Der VM ist kein physisches Host-Device (Server) zugewiesen.", obj=vm)
            else:
                self.log_success(f"Läuft auf Host-Device: {vm.device.name}", obj=vm)

    def test_device_platforms(self):
        """Prüft alle aktiven Devices auf eine zugewiesene Plattform."""
        active_devices = Device.objects.filter(status="active")
        for device in active_devices:
            if device.device_type.is_child_device:
                self.log_success("Child Device übersprungen.", obj=device)
                continue

            if device.platform_id is None:
                self.log_failure("Device hat keine Plattform zugewiesen.", obj=device)
            else:
                self.log_success("Plattform zugewiesen.", obj=device)

    def test_vm_platforms(self):
        """Prüft alle aktiven virtuellen Maschinen auf eine zugewiesene Plattform."""
        active_vms = VirtualMachine.objects.filter(status="active")
        for vm in active_vms:
            if vm.platform_id is None:
                self.log_failure("Virtual Machine hat keine Plattform zugewiesen.", obj=vm)
            else:
                self.log_success("Plattform zugewiesen.", obj=vm)

    def test_vm_vcpus(self):
        """Prüft, ob die Anzahl der vCPUs definiert und größer als 0 ist."""
        active_vms = VirtualMachine.objects.filter(status="active")
        for vm in active_vms:
            if vm.vcpus is None:
                self.log_failure("Anzahl der vCPUs ist nicht definiert.", obj=vm)
            elif vm.vcpus <= 0:
                self.log_failure(f"Ungültige vCPU-Anzahl definiert: {vm.vcpus}", obj=vm)
            else:
                self.log_success(f"vCPUs konfiguriert: {vm.vcpus}", obj=vm)

    def test_vm_memory(self):
        """Prüft, ob der Arbeitsspeicher (RAM) definiert und größer als 0 ist."""
        active_vms = VirtualMachine.objects.filter(status="active")
        for vm in active_vms:
            if vm.memory is None:
                self.log_failure("Arbeitsspeicher (RAM) ist nicht definiert.", obj=vm)
            elif vm.memory <= 0:
                self.log_failure(f"Ungültiger RAM-Wert definiert: {vm.memory} MB", obj=vm)
            else:
                self.log_success(f"RAM konfiguriert: {vm.memory} MB", obj=vm)

    def test_vm_disk(self):
        """Prüft, ob der Festplattenspeicher (Disk) definiert und größer als 0 ist."""
        active_vms = VirtualMachine.objects.filter(status="active")
        for vm in active_vms:
            if vm.disk is None:
                self.log_failure("Festplattenspeicher (Disk) ist nicht definiert.", obj=vm)
            elif vm.disk <= 0:
                self.log_failure(f"Ungültiger Disk-Wert definiert: {vm.disk} GB", obj=vm)
            else:
                self.log_success(f"Disk konfiguriert: {vm.disk} GB", obj=vm)