review feedback:

This commit is contained in:
Arthur
2026-03-30 14:05:13 -07:00
parent 2a2f36c1ba
commit df216cc0f1
3 changed files with 46 additions and 4 deletions

View File

@@ -384,6 +384,29 @@ A calendar date. Returns a `datetime.date` object.
A complete date & time. Returns a `datetime.datetime` object.
## Uploading Scripts via the API
Script modules can be uploaded to NetBox via the REST API by sending a `multipart/form-data` POST request to `/api/extras/scripts/`. The caller must have the `extras.add_scriptmodule` and `core.add_managedfile` permissions.
```no-highlight
curl -X POST \
-H "Authorization: Token $TOKEN" \
-H "Accept: application/json; indent=4" \
-F "upload_file=@/path/to/myscript.py" \
http://netbox/api/extras/scripts/
```
Alternatively, a script module can be linked to an existing data source and data file instead of uploading a file directly:
```no-highlight
curl -X POST \
-H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json; indent=4" \
http://netbox/api/extras/scripts/ \
--data '{"data_source": 1, "data_file": 42}'
```
## Running Custom Scripts
!!! note

View File

@@ -47,6 +47,10 @@ class ScriptModuleSerializer(ValidatedModelSerializer):
raise serializers.ValidationError(
_("Cannot upload a file and sync from an existing data file.")
)
if upload_file and data.get('data_source'):
raise serializers.ValidationError(
_("Cannot upload a file and sync from a data source.")
)
if self.instance is None and not upload_file and not data.get('data_file'):
raise serializers.ValidationError(
_("Must upload a file or select a data file to sync.")

View File

@@ -267,8 +267,6 @@ class ConfigTemplateViewSet(SyncedDataMixin, ConfigTemplateRenderMixin, NetBoxMo
@extend_schema_view(
create=extend_schema(request=serializers.ScriptModuleSerializer),
update=extend_schema(request=serializers.ScriptInputSerializer),
partial_update=extend_schema(request=serializers.ScriptInputSerializer),
)
class ScriptViewSet(ModelViewSet):
permission_classes = [IsAuthenticatedOrLoginNotRequired]
@@ -284,8 +282,15 @@ class ScriptViewSet(ModelViewSet):
# Restrict the view's QuerySet to allow only the permitted objects
if request.user.is_authenticated and self.action != 'create':
action = 'run' if request.method == 'POST' else 'view'
self.queryset = self.queryset.restrict(request.user, action)
if self.action == 'destroy':
perm_action = 'delete'
elif self.action in ('update', 'partial_update'):
perm_action = 'change'
elif request.method == 'POST':
perm_action = 'run'
else:
perm_action = 'view'
self.queryset = self.queryset.restrict(request.user, perm_action)
def create(self, request, *args, **kwargs):
"""
@@ -305,6 +310,16 @@ class ScriptViewSet(ModelViewSet):
return Response(serializer.data, status=status.HTTP_201_CREATED)
def update(self, request, *args, **kwargs):
if not request.user.has_perm('extras.change_script'):
raise PermissionDenied(_("This user does not have permission to modify scripts."))
return super().update(request, *args, **kwargs)
def destroy(self, request, *args, **kwargs):
if not request.user.has_perm('extras.delete_script'):
raise PermissionDenied(_("This user does not have permission to delete scripts."))
return super().destroy(request, *args, **kwargs)
def _get_script(self, pk):
# If pk is numeric, retrieve script by ID
if pk.isnumeric():