Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions netbox/extras/models/scripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class ScriptModule(PythonModuleMixin, JobsMixin, ManagedFile):
Proxy model for script module files.
"""
objects = ScriptModuleManager()
error = None

class Meta:
proxy = True
Expand All @@ -118,6 +119,7 @@ def _get_name(cls):
try:
module = self.get_module()
except Exception as e:
self.error = e
logger.debug(f"Failed to load script: {self.python_name} error: {e}")
module = None

Expand Down
33 changes: 27 additions & 6 deletions netbox/extras/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1040,12 +1040,27 @@ def get(self, request):
})


class ScriptView(generic.ObjectView):
class BaseScriptView(generic.ObjectView):
queryset = Script.objects.all()

def _get_script_class(self, script):
"""
Return an instance of the Script's Python class
"""
if script_class := script.python_class:
return script_class()


class ScriptView(BaseScriptView):

def get(self, request, **kwargs):
script = self.get_object(**kwargs)
script_class = script.python_class()
script_class = self._get_script_class(script)
if not script_class:
return render(request, 'extras/script.html', {
'script': script,
})

form = script_class.as_form(initial=normalize_querydict(request.GET))

return render(request, 'extras/script.html', {
Expand All @@ -1057,11 +1072,16 @@ def get(self, request, **kwargs):

def post(self, request, **kwargs):
script = self.get_object(**kwargs)
script_class = script.python_class()

if not request.user.has_perm('extras.run_script', obj=script):
return HttpResponseForbidden()

script_class = self._get_script_class(script)
if not script_class:
return render(request, 'extras/script.html', {
'script': script,
})

form = script_class.as_form(request.POST, request.FILES)

# Allow execution only if RQ worker process is running
Expand Down Expand Up @@ -1091,21 +1111,22 @@ def post(self, request, **kwargs):
})


class ScriptSourceView(generic.ObjectView):
class ScriptSourceView(BaseScriptView):
queryset = Script.objects.all()

def get(self, request, **kwargs):
script = self.get_object(**kwargs)
script_class = self._get_script_class(script)

return render(request, 'extras/script/source.html', {
'script': script,
'script_class': script.python_class(),
'script_class': script_class,
'job_count': script.jobs.count(),
'tab': 'source',
})


class ScriptJobsView(generic.ObjectView):
class ScriptJobsView(BaseScriptView):
queryset = Script.objects.all()

def get(self, request, **kwargs):
Expand Down
65 changes: 35 additions & 30 deletions netbox/templates/extras/script.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,38 +14,43 @@
{% trans "You do not have permission to run scripts" %}.
</div>
{% endif %}
<form action="" method="post" enctype="multipart/form-data" class="object-edit">
{% csrf_token %}
<div class="field-group my-4">
{# Render grouped fields according to declared fieldsets #}
{% for group, fields in script_class.get_fieldsets %}
{% if fields %}
<div class="field-group mb-5">
<div class="row">
<h5 class="col-9 offset-3">{{ group }}</h5>
{% if form %}
<form action="" method="post" enctype="multipart/form-data" class="object-edit">
{% csrf_token %}
<div class="field-group my-4">
{# Render grouped fields according to declared fieldsets #}
{% for group, fields in script_class.get_fieldsets %}
{% if fields %}
<div class="field-group mb-5">
<div class="row">
<h5 class="col-9 offset-3">{{ group }}</h5>
</div>
{% for name in fields %}
{% with field=form|getfield:name %}
{% render_field field %}
{% endwith %}
{% endfor %}
</div>
{% for name in fields %}
{% with field=form|getfield:name %}
{% render_field field %}
{% endwith %}
{% endfor %}
</div>
{% endif %}
{% endfor %}
</div>
<div class="text-end">
<a href="{% url 'extras:script_list' %}" class="btn btn-outline-secondary">{% trans "Cancel" %}</a>
{% if not request.user|can_run:script or not script.is_executable %}
<button class="btn btn-primary" disabled>
<i class="mdi mdi-play"></i> {% trans "Run Script" %}
</button>
{% else %}
<button type="submit" name="_run" class="btn btn-primary">
<i class="mdi mdi-play"></i> {% trans "Run Script" %}
</button>
{% endif %}
{% endfor %}
</div>
<div class="text-end">
<a href="{% url 'extras:script_list' %}" class="btn btn-outline-secondary">{% trans "Cancel" %}</a>
{% if not request.user|can_run:script or not script.is_executable %}
<button class="btn btn-primary" disabled>
<i class="mdi mdi-play"></i> {% trans "Run Script" %}
</button>
{% else %}
<button type="submit" name="_run" class="btn btn-primary">
<i class="mdi mdi-play"></i> {% trans "Run Script" %}
</button>
{% endif %}
</div>
</form>
</div>
</form>
{% else %}
<p>{% trans "Error loading script" %}.</p>
<pre class="block">{{ script.module.error }}</pre>
{% endif %}
</div>
</div>
{% endblock content %}
12 changes: 10 additions & 2 deletions netbox/templates/extras/script/source.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
{% extends 'extras/script/base.html' %}
{% load i18n %}

{% block content %}
<code class="h6 my-3 d-block">{{ script_class.filename }}</code>
<pre class="block">{{ script_class.source }}</pre>

{% if script_class %}
<code class="h6 my-3 d-block">{{ script_class.filename }}</code>
<pre class="block">{{ script_class.source }}</pre>
{% else %}
<p>{% trans "Error loading script" %}.</p>
<pre class="block">{{ script.module.error }}</pre>
{% endif %}

{% endblock %}