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()
|
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
|
# Change logging
|
||||||
#
|
#
|
||||||
|
@ -38,6 +38,9 @@ router.register(r'config-contexts', views.ConfigContextViewSet)
|
|||||||
# Reports
|
# Reports
|
||||||
router.register(r'reports', views.ReportViewSet, basename='report')
|
router.register(r'reports', views.ReportViewSet, basename='report')
|
||||||
|
|
||||||
|
# Scripts
|
||||||
|
router.register(r'scripts', views.ScriptViewSet, basename='script')
|
||||||
|
|
||||||
# Change logging
|
# Change logging
|
||||||
router.register(r'object-changes', views.ObjectChangeViewSet)
|
router.register(r'object-changes', views.ObjectChangeViewSet)
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ from collections import OrderedDict
|
|||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
|
from rest_framework import status
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.exceptions import PermissionDenied
|
from rest_framework.exceptions import PermissionDenied
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
@ -13,6 +14,7 @@ from extras.models import (
|
|||||||
ConfigContext, CustomFieldChoice, ExportTemplate, Graph, ImageAttachment, ObjectChange, ReportResult, Tag,
|
ConfigContext, CustomFieldChoice, ExportTemplate, Graph, ImageAttachment, ObjectChange, ReportResult, Tag,
|
||||||
)
|
)
|
||||||
from extras.reports import get_report, get_reports
|
from extras.reports import get_report, get_reports
|
||||||
|
from extras.scripts import get_script, get_scripts
|
||||||
from utilities.api import FieldChoicesViewSet, IsAuthenticatedOrLoginNotRequired, ModelViewSet
|
from utilities.api import FieldChoicesViewSet, IsAuthenticatedOrLoginNotRequired, ModelViewSet
|
||||||
from . import serializers
|
from . import serializers
|
||||||
|
|
||||||
@ -222,6 +224,53 @@ class ReportViewSet(ViewSet):
|
|||||||
return Response(serializer.data)
|
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
|
# Change logging
|
||||||
#
|
#
|
||||||
|
@ -220,16 +220,17 @@ class BaseScript:
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return getattr(self.Meta, 'name', self.__class__.__name__)
|
return getattr(self.Meta, 'name', self.__class__.__name__)
|
||||||
|
|
||||||
def _get_vars(self):
|
@classmethod
|
||||||
|
def _get_vars(cls):
|
||||||
vars = OrderedDict()
|
vars = OrderedDict()
|
||||||
|
|
||||||
# Infer order from Meta.field_order (Python 3.5 and lower)
|
# 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:
|
for name in field_order:
|
||||||
vars[name] = getattr(self, name)
|
vars[name] = getattr(cls, name)
|
||||||
|
|
||||||
# Default to order of declaration on class
|
# 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):
|
if name not in vars and issubclass(attr.__class__, ScriptVariable):
|
||||||
vars[name] = attr
|
vars[name] = attr
|
||||||
|
|
||||||
@ -361,6 +362,9 @@ def run_script(script, data, files, commit=True):
|
|||||||
|
|
||||||
|
|
||||||
def get_scripts():
|
def get_scripts():
|
||||||
|
"""
|
||||||
|
Return a dict of dicts mapping all scripts to their modules.
|
||||||
|
"""
|
||||||
scripts = OrderedDict()
|
scripts = OrderedDict()
|
||||||
|
|
||||||
# Iterate through all modules within the reports path. These are the user-created files in which reports are
|
# 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
|
scripts[module_name] = module_scripts
|
||||||
|
|
||||||
return 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