Initial commit

This commit is contained in:
sven committed 2026-08-16 13:10:26 +02:00
commit 4ff9eac274
45 files changed
+2911

No files matched your search

+193
View File
@@ -0,0 +1,193 @@
from django.contrib import messages
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.translation import gettext_lazy as _
from netbox.views import generic
from utilities.views import register_model_view
from .engine.resolver import Resolver
from .filtersets import ServiceFilterSet, ServiceTemplateFilterSet
from .forms import (
ResolveForm,
ServiceBulkEditForm,
ServiceFilterSetForm,
ServiceForm,
ServiceTemplateBulkEditForm,
ServiceTemplateFilterSetForm,
ServiceTemplateForm,
ServiceTemplateImportForm,
)
from .models import Service, ServiceTemplate
from .tables import ServiceTable, ServiceTemplateTable
def _build_preview(result):
"""Transform result.planned_objects into a list of dicts with pre-computed action."""
preview = []
for node_name, obj in result.planned_objects:
ct = ContentType.objects.get_for_model(obj.__class__)
key = (ct.id, obj.pk)
action = "add" if key in result.to_add else "keep"
preview.append({"node": node_name, "object": obj, "action": action})
return preview
# ════════════════════════════════════════════════════════════════════
# Service Template
# ════════════════════════════════════════════════════════════════════
@register_model_view(ServiceTemplate, "list", path="", detail=False)
class ServiceTemplateListView(generic.ObjectListView):
queryset = ServiceTemplate.objects.all()
table = ServiceTemplateTable
filterset = ServiceTemplateFilterSet
filterset_form = ServiceTemplateFilterSetForm
@register_model_view(ServiceTemplate, "add", path="add", detail=False)
@register_model_view(ServiceTemplate, "edit")
class ServiceTemplateEditView(generic.ObjectEditView):
queryset = ServiceTemplate.objects.all()
form = ServiceTemplateForm
@register_model_view(ServiceTemplate, "delete")
class ServiceTemplateDeleteView(generic.ObjectDeleteView):
queryset = ServiceTemplate.objects.all()
@register_model_view(ServiceTemplate, "bulk_edit", path="edit", detail=False)
class ServiceTemplateBulkEditView(generic.BulkEditView):
queryset = ServiceTemplate.objects.all()
table = ServiceTemplateTable
form = ServiceTemplateBulkEditForm
@register_model_view(ServiceTemplate, "bulk_delete", path="delete", detail=False)
class ServiceTemplateBulkDeleteView(generic.BulkDeleteView):
queryset = ServiceTemplate.objects.all()
table = ServiceTemplateTable
@register_model_view(ServiceTemplate, "bulk_import", path="import", detail=False)
class ServiceTemplateBulkImportView(generic.BulkImportView):
queryset = ServiceTemplate.objects.all()
model_form = ServiceTemplateImportForm
table = ServiceTemplateTable
@register_model_view(ServiceTemplate)
class ServiceTemplateView(generic.ObjectView):
queryset = ServiceTemplate.objects.all()
def get_extra_context(self, request, instance):
context = super().get_extra_context(request, instance)
context["is_locked"] = instance.is_locked
return context
# ════════════════════════════════════════════════════════════════════
# Service
# ════════════════════════════════════════════════════════════════════
@register_model_view(Service, "list", path="", detail=False)
class ServiceListView(generic.ObjectListView):
queryset = Service.objects.all()
table = ServiceTable
filterset = ServiceFilterSet
filterset_form = ServiceFilterSetForm
@register_model_view(Service, "add", path="add", detail=False)
@register_model_view(Service, "edit")
class ServiceEditView(generic.ObjectEditView):
queryset = Service.objects.all()
form = ServiceForm
@register_model_view(Service, "delete")
class ServiceDeleteView(generic.ObjectDeleteView):
queryset = Service.objects.all()
@register_model_view(Service, "bulk_edit", path="edit", detail=False)
class ServiceBulkEditView(generic.BulkEditView):
queryset = Service.objects.all()
table = ServiceTable
form = ServiceBulkEditForm
@register_model_view(Service, "bulk_delete", path="delete", detail=False)
class ServiceBulkDeleteView(generic.BulkDeleteView):
queryset = Service.objects.all()
table = ServiceTable
@register_model_view(Service)
class ServiceView(generic.ObjectView):
queryset = Service.objects.all()
def get_extra_context(self, request, instance):
context = super().get_extra_context(request, instance)
context["bindings"] = instance.bindings.select_related(
"content_type",
).all()
return context
@register_model_view(Service, "resolve", path="resolve")
class ServiceResolveView(generic.ObjectView):
"""Dry-run preview (GET) and commit (POST) for service resolution."""
queryset = Service.objects.all()
def get(self, request, pk):
service = get_object_or_404(self.queryset, pk=pk)
resolver = Resolver()
try:
result = resolver.resolve(service, mode="dry_run", user=request.user)
except Exception as exc:
messages.error(request, f"Resolution failed: {exc}")
return redirect("plugins:netbox_services:service", pk=service.pk)
return render(request, "netbox_services/service_resolve.html", {
"object": service,
"service": service,
"result": result,
"planned_preview": _build_preview(result),
"form": ResolveForm(initial={"mode": "commit"}),
"active_tab": "resolve",
})
def post(self, request, pk):
service = get_object_or_404(self.queryset, pk=pk)
form = ResolveForm(request.POST)
mode = form.cleaned_data.get("mode", "dry_run") if form.is_valid() else "dry_run"
resolver = Resolver()
try:
result = resolver.resolve(service, mode=mode, user=request.user)
except Exception as exc:
messages.error(request, f"Resolution failed: {exc}")
return redirect("plugins:netbox_services:service", pk=service.pk)
if mode == "commit":
messages.success(
request,
_("Resolution committed: {added} added, {removed} removed, {kept} unchanged.").format(
added=len(result.to_add),
removed=len(result.to_remove),
kept=result.total_planned - len(result.to_add),
),
)
return redirect("plugins:netbox_services:service", pk=service.pk)
return render(request, "netbox_services/service_resolve.html", {
"object": service,
"service": service,
"result": result,
"planned_preview": _build_preview(result),
"form": ResolveForm(initial={"mode": "commit"}),
"active_tab": "resolve",
})