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 import forms
from django.conf import settings 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 .constants import LOG_DEFAULT, LOG_FAILURE, LOG_INFO, LOG_SUCCESS, LOG_WARNING
from .forms import ScriptForm from .forms import ScriptForm
class OptionalBooleanField(forms.BooleanField):
required = False
# #
# Script variables # Script variables
# #
class ScriptVariable: class ScriptVariable:
"""
Base model for script variables
"""
form_field = forms.CharField form_field = forms.CharField
def __init__(self, label='', description=''): def __init__(self, label='', description='', default=None, required=True):
# Default field attributes # Default field attributes
if not hasattr(self, 'field_attrs'): self.field_attrs = {
self.field_attrs = {} 'help_text': description,
'required': required
}
if label: if label:
self.field_attrs['label'] = label self.field_attrs['label'] = label
if description: if default:
self.field_attrs['help_text'] = description self.field_attrs['initial'] = default
def as_field(self): def as_field(self):
""" """
@ -34,26 +44,62 @@ class ScriptVariable:
class StringVar(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): class IntegerVar(ScriptVariable):
"""
Integer representation. Can enforce minimum/maximum values.
"""
form_field = forms.IntegerField 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): class BooleanVar(ScriptVariable):
form_field = forms.BooleanField """
field_attrs = { Boolean representation (true/false). Renders as a checkbox.
'required': False """
} form_field = OptionalBooleanField
class ObjectVar(ScriptVariable): class ObjectVar(ScriptVariable):
"""
NetBox object representation. The provided QuerySet will determine the choices available.
"""
form_field = forms.ModelChoiceField form_field = forms.ModelChoiceField
def __init__(self, queryset, *args, **kwargs): def __init__(self, queryset, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
# Queryset for field choices
self.field_attrs['queryset'] = queryset self.field_attrs['queryset'] = queryset
@ -61,7 +107,6 @@ class Script:
""" """
Custom scripts inherit this object. Custom scripts inherit this object.
""" """
def __init__(self): def __init__(self):
# Initiate the log # Initiate the log
@ -80,7 +125,7 @@ class Script:
# TODO: This should preserve var ordering # TODO: This should preserve var ordering
return inspect.getmembers(self, is_variable) return inspect.getmembers(self, is_variable)
def run(self, context): def run(self, data):
raise NotImplementedError("The script must define a run() method.") raise NotImplementedError("The script must define a run() method.")
def as_form(self, data=None): def as_form(self, data=None):

View File

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

View File

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