feat: add NetBox plugin store
CI / php-store (push) Waiting to run
CI / python-components (push) Waiting to run

This commit is contained in:
2026-08-24 20:51:25 +02:00
commit f36d6be511
135 changed files with 15160 additions and 0 deletions
@@ -0,0 +1,86 @@
from __future__ import annotations
from typing import Any
from django.db import transaction
from django.utils import timezone
from .client import CatalogPlugin
from .lifecycle import LifecycleRequest
from .models import LifecycleAudit, ManagedPlugin
from .redaction import redact_data, redact_text
class DjangoLifecycleRepository:
def has_other_pending_mutation(self, slug: str, audit_id: int) -> bool:
return LifecycleAudit.objects.filter(
slug=slug,
dry_run=False,
status=LifecycleAudit.Status.HANDED_OFF,
).exclude(pk=audit_id).exists()
def create_audit(self, request: LifecycleRequest, actor_id: int | None) -> int:
audit = LifecycleAudit.objects.create(
actor_id=actor_id,
slug=request.slug,
action=request.action,
requested_version=request.version,
dry_run=request.dry_run,
request_data={
"slug": request.slug,
"action": request.action,
"version": request.version,
"dry_run": request.dry_run,
},
)
return audit.pk
def mark_running(self, audit_id: int) -> None:
LifecycleAudit.objects.filter(pk=audit_id).update(
status=LifecycleAudit.Status.RUNNING,
started=timezone.now(),
error="",
)
def mark_success(self, audit_id: int, result: dict[str, Any]) -> None:
LifecycleAudit.objects.filter(pk=audit_id).update(
status=LifecycleAudit.Status.SUCCEEDED,
result_data=redact_data(result),
completed=timezone.now(),
)
def mark_handed_off(self, audit_id: int, operation_id: str, result: dict[str, Any]) -> None:
LifecycleAudit.objects.filter(pk=audit_id).update(
status=LifecycleAudit.Status.HANDED_OFF,
external_operation_id=operation_id,
result_data=redact_data(result),
)
def mark_failed(self, audit_id: int, error: str, result: dict[str, Any]) -> None:
LifecycleAudit.objects.filter(pk=audit_id).update(
status=LifecycleAudit.Status.FAILED,
error=redact_text(error, limit=8_000),
result_data=redact_data(result),
completed=timezone.now(),
)
@transaction.atomic
def update_status(self, plugin: CatalogPlugin, **values: Any) -> None:
safe_values = {
"name": plugin.name,
"package_name": plugin.package_name,
"import_name": plugin.import_name,
"repository_url": plugin.repository_url,
"available_version": plugin.latest_version,
"last_checked": timezone.now(),
**values,
}
if "last_error" in safe_values:
safe_values["last_error"] = redact_text(safe_values["last_error"], limit=4_000)
obj = ManagedPlugin.objects.select_for_update().filter(slug=plugin.slug).first()
if obj is None:
ManagedPlugin.objects.create(slug=plugin.slug, **safe_values)
else:
for key, value in safe_values.items():
setattr(obj, key, value)
obj.save(update_fields=tuple(safe_values) + ("updated",))