|
|
@@ -1,19 +1,70 @@
|
|
|
-from django.utils.translation import gettext as _
|
|
|
+import logging
|
|
|
+
|
|
|
+from django.core.files.storage import storages
|
|
|
+from django.db import IntegrityError
|
|
|
+from django.utils.translation import gettext_lazy as _
|
|
|
from drf_spectacular.utils import extend_schema_field
|
|
|
from rest_framework import serializers
|
|
|
|
|
|
from core.api.serializers_.jobs import JobSerializer
|
|
|
-from extras.models import Script
|
|
|
+from core.choices import ManagedFileRootPathChoices
|
|
|
+from extras.models import Script, ScriptModule
|
|
|
from netbox.api.serializers import ValidatedModelSerializer
|
|
|
from utilities.datetime import local_now
|
|
|
|
|
|
+logger = logging.getLogger(__name__)
|
|
|
+
|
|
|
__all__ = (
|
|
|
'ScriptDetailSerializer',
|
|
|
'ScriptInputSerializer',
|
|
|
+ 'ScriptModuleSerializer',
|
|
|
'ScriptSerializer',
|
|
|
)
|
|
|
|
|
|
|
|
|
+class ScriptModuleSerializer(ValidatedModelSerializer):
|
|
|
+ file = serializers.FileField(write_only=True)
|
|
|
+ file_path = serializers.CharField(read_only=True)
|
|
|
+
|
|
|
+ class Meta:
|
|
|
+ model = ScriptModule
|
|
|
+ fields = ['id', 'display', 'file_path', 'file', 'created', 'last_updated']
|
|
|
+ brief_fields = ('id', 'display')
|
|
|
+
|
|
|
+ def validate(self, data):
|
|
|
+ # ScriptModule.save() sets file_root; inject it here so full_clean() succeeds.
|
|
|
+ # Pop 'file' before model instantiation — ScriptModule has no such field.
|
|
|
+ file = data.pop('file', None)
|
|
|
+ data['file_root'] = ManagedFileRootPathChoices.SCRIPTS
|
|
|
+ data = super().validate(data)
|
|
|
+ data.pop('file_root', None)
|
|
|
+ if file is not None:
|
|
|
+ data['file'] = file
|
|
|
+ return data
|
|
|
+
|
|
|
+ def create(self, validated_data):
|
|
|
+ file = validated_data.pop('file')
|
|
|
+ storage = storages.create_storage(storages.backends["scripts"])
|
|
|
+ validated_data['file_path'] = storage.save(file.name, file)
|
|
|
+ created = False
|
|
|
+ try:
|
|
|
+ instance = super().create(validated_data)
|
|
|
+ created = True
|
|
|
+ return instance
|
|
|
+ except IntegrityError as e:
|
|
|
+ if 'file_path' in str(e):
|
|
|
+ raise serializers.ValidationError(
|
|
|
+ _("A script module with this file name already exists.")
|
|
|
+ )
|
|
|
+ raise
|
|
|
+ finally:
|
|
|
+ if not created and (file_path := validated_data.get('file_path')):
|
|
|
+ try:
|
|
|
+ storage.delete(file_path)
|
|
|
+ except Exception:
|
|
|
+ logger.warning(f"Failed to delete orphaned script file '{file_path}' from storage.")
|
|
|
+
|
|
|
+
|
|
|
class ScriptSerializer(ValidatedModelSerializer):
|
|
|
description = serializers.SerializerMethodField(read_only=True)
|
|
|
vars = serializers.SerializerMethodField(read_only=True)
|