Add options for script vars; include script output

This commit is contained in:
Jeremy Stretch 2019-08-09 13:56:37 -04:00
parent a25a27f31f
commit 9d054fb345
3 changed files with 66 additions and 13 deletions

View File

@ -4,27 +4,37 @@ import pkgutil
from django import forms
from django.conf import settings
from django.core.validators import RegexValidator
from .constants import LOG_DEFAULT, LOG_FAILURE, LOG_INFO, LOG_SUCCESS, LOG_WARNING
from .forms import ScriptForm
class OptionalBooleanField(forms.BooleanField):
required = False
#
# Script variables
#
class ScriptVariable:
"""
Base model for script variables
"""
form_field = forms.CharField
def __init__(self, label='', description=''):
def __init__(self, label='', description='', default=None, required=True):
# Default field attributes
if not hasattr(self, 'field_attrs'):
self.field_attrs = {}
self.field_attrs = {
'help_text': description,
'required': required
}
if label:
self.field_attrs['label'] = label
if description:
self.field_attrs['help_text'] = description
if default:
self.field_attrs['initial'] = default
def as_field(self):
"""
@ -34,26 +44,62 @@ class ScriptVariable:
class StringVar(ScriptVariable):
pass
"""
Character string representation. Can enforce minimum/maximum length and/or regex validation.
"""
def __init__(self, min_length=None, max_length=None, regex=None, *args, **kwargs):
super().__init__(*args, **kwargs)
# Optional minimum/maximum lengths
if min_length:
self.field_attrs['min_length'] = min_length
if max_length:
self.field_attrs['max_length'] = max_length
# Optional regular expression validation
if regex:
self.field_attrs['validators'] = [
RegexValidator(
regex=regex,
message='Invalid value. Must match regex: {}'.format(regex),
code='invalid'
)
]
class IntegerVar(ScriptVariable):
"""
Integer representation. Can enforce minimum/maximum values.
"""
form_field = forms.IntegerField
def __init__(self, min_value=None, max_value=None, *args, **kwargs):
super().__init__(*args, **kwargs)
# Optional minimum/maximum values
if min_value:
self.field_attrs['min_value'] = min_value
if max_value:
self.field_attrs['max_value'] = max_value
class BooleanVar(ScriptVariable):
form_field = forms.BooleanField
field_attrs = {
'required': False
}
"""
Boolean representation (true/false). Renders as a checkbox.
"""
form_field = OptionalBooleanField
class ObjectVar(ScriptVariable):
"""
NetBox object representation. The provided QuerySet will determine the choices available.
"""
form_field = forms.ModelChoiceField
def __init__(self, queryset, *args, **kwargs):
super().__init__(*args, **kwargs)
# Queryset for field choices
self.field_attrs['queryset'] = queryset
@ -61,7 +107,6 @@ class Script:
"""
Custom scripts inherit this object.
"""
def __init__(self):
# Initiate the log
@ -80,7 +125,7 @@ class Script:
# TODO: This should preserve var ordering
return inspect.getmembers(self, is_variable)
def run(self, context):
def run(self, data):
raise NotImplementedError("The script must define a run() method.")
def as_form(self, data=None):

View File

@ -396,14 +396,16 @@ class ScriptView(LoginRequiredMixin, View):
script = self._get_script(module, name)
form = script.as_form(request.POST)
output = None
if form.is_valid():
with transaction.atomic():
script.run(form.cleaned_data)
output = script.run(form.cleaned_data)
return render(request, 'extras/script.html', {
'module': module,
'script': script,
'form': form,
'output': output,
})

View File

@ -21,6 +21,9 @@
<li role="presentation" class="active">
<a href="#run" role="tab" data-toggle="tab" class="active">Run</a>
</li>
<li role="presentation"{% if not output %} class="disabled"{% endif %}>
<a href="#output" role="tab" data-toggle="tab">Output</a>
</li>
<li role="presentation">
<a href="#source" role="tab" data-toggle="tab">Source</a>
</li>
@ -69,6 +72,9 @@
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="output">
<pre>{{ output }}</pre>
</div>
<div role="tabpanel" class="tab-pane" id="source">
<strong>{{ script.filename }}</strong>
<pre>{{ script.source }}</pre>