mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Initial work on #3538: script execution API
This commit is contained in:
@ -200,6 +200,36 @@ class ReportDetailSerializer(ReportSerializer):
|
||||
result = ReportResultSerializer()
|
||||
|
||||
|
||||
#
|
||||
# Scripts
|
||||
#
|
||||
|
||||
class ScriptSerializer(serializers.Serializer):
|
||||
id = serializers.SerializerMethodField(read_only=True)
|
||||
name = serializers.SerializerMethodField(read_only=True)
|
||||
description = serializers.SerializerMethodField(read_only=True)
|
||||
vars = serializers.SerializerMethodField(read_only=True)
|
||||
|
||||
def get_id(self, instance):
|
||||
return '{}.{}'.format(instance.__module__, instance.__name__)
|
||||
|
||||
def get_name(self, instance):
|
||||
return getattr(instance.Meta, 'name', instance.__name__)
|
||||
|
||||
def get_description(self, instance):
|
||||
return getattr(instance.Meta, 'description', '')
|
||||
|
||||
def get_vars(self, instance):
|
||||
return {
|
||||
k: v.__class__.__name__ for k, v in instance._get_vars().items()
|
||||
}
|
||||
|
||||
|
||||
class ScriptInputSerializer(serializers.Serializer):
|
||||
data = serializers.JSONField()
|
||||
commit = serializers.BooleanField()
|
||||
|
||||
|
||||
#
|
||||
# Change logging
|
||||
#
|
||||
|
@ -38,6 +38,9 @@ router.register(r'config-contexts', views.ConfigContextViewSet)
|
||||
# Reports
|
||||
router.register(r'reports', views.ReportViewSet, basename='report')
|
||||
|
||||
# Scripts
|
||||
router.register(r'scripts', views.ScriptViewSet, basename='script')
|
||||
|
||||
# Change logging
|
||||
router.register(r'object-changes', views.ObjectChangeViewSet)
|
||||
|
||||
|
@ -3,6 +3,7 @@ from collections import OrderedDict
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db.models import Count
|
||||
from django.http import Http404
|
||||
from rest_framework import status
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.exceptions import PermissionDenied
|
||||
from rest_framework.response import Response
|
||||
@ -13,6 +14,7 @@ from extras.models import (
|
||||
ConfigContext, CustomFieldChoice, ExportTemplate, Graph, ImageAttachment, ObjectChange, ReportResult, Tag,
|
||||
)
|
||||
from extras.reports import get_report, get_reports
|
||||
from extras.scripts import get_script, get_scripts
|
||||
from utilities.api import FieldChoicesViewSet, IsAuthenticatedOrLoginNotRequired, ModelViewSet
|
||||
from . import serializers
|
||||
|
||||
@ -222,6 +224,53 @@ class ReportViewSet(ViewSet):
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
#
|
||||
# Scripts
|
||||
#
|
||||
|
||||
class ScriptViewSet(ViewSet):
|
||||
permission_classes = [IsAuthenticatedOrLoginNotRequired]
|
||||
_ignore_model_permissions = True
|
||||
exclude_from_schema = True
|
||||
lookup_value_regex = '[^/]+' # Allow dots
|
||||
|
||||
def _get_script(self, pk):
|
||||
module_name, script_name = pk.split('.')
|
||||
script = get_script(module_name, script_name)
|
||||
if script is None:
|
||||
raise Http404
|
||||
return script
|
||||
|
||||
def list(self, request):
|
||||
|
||||
flat_list = []
|
||||
for script_list in get_scripts().values():
|
||||
flat_list.extend(script_list.values())
|
||||
|
||||
serializer = serializers.ScriptSerializer(flat_list, many=True, context={'request': request})
|
||||
|
||||
return Response(serializer.data)
|
||||
|
||||
def retrieve(self, request, pk):
|
||||
script = self._get_script(pk)
|
||||
serializer = serializers.ScriptSerializer(script, context={'request': request})
|
||||
|
||||
return Response(serializer.data)
|
||||
|
||||
def post(self, request, pk):
|
||||
"""
|
||||
Run a Script identified as "<module>.<script>".
|
||||
"""
|
||||
script = self._get_script(pk)()
|
||||
serializer = serializers.ScriptInputSerializer(data=request.data)
|
||||
|
||||
if serializer.is_valid():
|
||||
script.run(serializer.data['data'])
|
||||
return Response(script.log)
|
||||
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
#
|
||||
# Change logging
|
||||
#
|
||||
|
@ -220,16 +220,17 @@ class BaseScript:
|
||||
def __str__(self):
|
||||
return getattr(self.Meta, 'name', self.__class__.__name__)
|
||||
|
||||
def _get_vars(self):
|
||||
@classmethod
|
||||
def _get_vars(cls):
|
||||
vars = OrderedDict()
|
||||
|
||||
# Infer order from Meta.field_order (Python 3.5 and lower)
|
||||
field_order = getattr(self.Meta, 'field_order', [])
|
||||
field_order = getattr(cls.Meta, 'field_order', [])
|
||||
for name in field_order:
|
||||
vars[name] = getattr(self, name)
|
||||
vars[name] = getattr(cls, name)
|
||||
|
||||
# Default to order of declaration on class
|
||||
for name, attr in self.__class__.__dict__.items():
|
||||
for name, attr in cls.__dict__.items():
|
||||
if name not in vars and issubclass(attr.__class__, ScriptVariable):
|
||||
vars[name] = attr
|
||||
|
||||
@ -361,6 +362,9 @@ def run_script(script, data, files, commit=True):
|
||||
|
||||
|
||||
def get_scripts():
|
||||
"""
|
||||
Return a dict of dicts mapping all scripts to their modules.
|
||||
"""
|
||||
scripts = OrderedDict()
|
||||
|
||||
# Iterate through all modules within the reports path. These are the user-created files in which reports are
|
||||
@ -375,3 +379,13 @@ def get_scripts():
|
||||
scripts[module_name] = module_scripts
|
||||
|
||||
return scripts
|
||||
|
||||
|
||||
def get_script(module_name, script_name):
|
||||
"""
|
||||
Retrieve a script class by module and name. Returns None if the script does not exist.
|
||||
"""
|
||||
scripts = get_scripts()
|
||||
module = scripts.get(module_name)
|
||||
if module:
|
||||
return module.get(script_name)
|
||||
|
Reference in New Issue
Block a user