fix tests and cleanup

This commit is contained in:
John Anderson 2020-06-29 14:34:42 -04:00
parent 1681dbfa39
commit f48a079ae6
11 changed files with 29 additions and 111 deletions

View File

@ -284,21 +284,12 @@ class ScriptSerializer(serializers.Serializer):
lookup_field='full_name', lookup_field='full_name',
lookup_url_kwarg='pk' lookup_url_kwarg='pk'
) )
id = serializers.SerializerMethodField(read_only=True) id = serializers.CharField(read_only=True, source="full_name")
name = serializers.SerializerMethodField(read_only=True) name = serializers.CharField(read_only=True)
description = serializers.SerializerMethodField(read_only=True) description = serializers.CharField(read_only=True)
vars = serializers.SerializerMethodField(read_only=True) vars = serializers.SerializerMethodField(read_only=True)
result = NestedJobResultSerializer() result = NestedJobResultSerializer()
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): def get_vars(self, instance):
return { return {
k: v.__class__.__name__ for k, v in instance._get_vars().items() k: v.__class__.__name__ for k, v in instance._get_vars().items()

View File

@ -17,6 +17,7 @@ from extras.models import (
from extras.reports import get_report, get_reports from extras.reports import get_report, get_reports
from extras.scripts import get_script, get_scripts, run_script from extras.scripts import get_script, get_scripts, run_script
from utilities.api import IsAuthenticatedOrLoginNotRequired, ModelViewSet from utilities.api import IsAuthenticatedOrLoginNotRequired, ModelViewSet
from utilities.utils import copy_safe_request
from . import serializers from . import serializers
@ -304,12 +305,12 @@ class ScriptViewSet(ViewSet):
script.full_name, script.full_name,
script_content_type, script_content_type,
request.user, request.user,
data=form.cleaned_data, data=data,
request=copy_safe_request(request), request=copy_safe_request(request),
commit=commit commit=commit
) )
script.result = job_result script.result = job_result
serializer = serializers.ScriptDetailSerializer(script) serializer = serializers.ScriptDetailSerializer(script, context={'request': request})
return Response(serializer.data) return Response(serializer.data)

View File

@ -573,25 +573,6 @@ class Script(models.Model):
class Meta: class Meta:
managed = False managed = False
@classmethod
def get_absolute_url_from_job_result(cls, job_result):
"""
Given a JobResult that links to this content type, return URL to an instance which corresponds to that job
result, i.e. for historical records
"""
if job_result.obj_type.model_class() != cls:
return None
module, script_name = job_result.name.split('.')
return reverse(
'extras:script_history_detail',
kwargs={
'module': module,
'script_name': script_name,
'job_id': job_result.job_id
}
)
# #
# Reports # Reports
@ -606,23 +587,6 @@ class Report(models.Model):
class Meta: class Meta:
managed = False managed = False
@classmethod
def get_absolute_url_from_job_result(cls, job_result):
"""
Given a JobResult that links to this content type, return URL to an instance which corresponds to that job
result, i.e. for historical records
"""
if job_result.obj_type.model_class() != cls:
return None
return reverse(
'extras:report_history_detail',
kwargs={
'name': job_result.name,
'job_id': job_result.job_id
}
)
# #
# Job results # Job results
@ -676,12 +640,6 @@ class JobResult(models.Model):
def __str__(self): def __str__(self):
return str(self.job_id) return str(self.job_id)
def get_absolute_url(self):
"""
Job results are accessed only under the context of the content type they link to
"""
return self.obj_type.model_class().get_absolute_url_from_job_result(self)
@property @property
def duration(self): def duration(self):
if not self.completed: if not self.completed:
@ -692,7 +650,6 @@ class JobResult(models.Model):
return f"{int(minutes)} minutes, {seconds:.2f} seconds" return f"{int(minutes)} minutes, {seconds:.2f} seconds"
@classmethod @classmethod
def enqueue_job(cls, func, name, obj_type, user, *args, **kwargs): def enqueue_job(cls, func, name, obj_type, user, *args, **kwargs):
""" """

View File

@ -273,12 +273,20 @@ class BaseScript:
self.source = inspect.getsource(self.__class__) self.source = inspect.getsource(self.__class__)
def __str__(self): def __str__(self):
return self.name
@classproperty
def name(self):
return getattr(self.Meta, 'name', self.__class__.__name__) return getattr(self.Meta, 'name', self.__class__.__name__)
@classproperty @classproperty
def full_name(self): def full_name(self):
return '.'.join([self.__module__, self.__name__]) return '.'.join([self.__module__, self.__name__])
@classproperty
def description(self):
return getattr(self.Meta, 'description', '')
@classmethod @classmethod
def module(cls): def module(cls):
return cls.__module__ return cls.__module__

View File

@ -61,28 +61,6 @@ OBJECTCHANGE_REQUEST_ID = """
<a href="{% url 'extras:objectchange_list' %}?request_id={{ value }}">{{ value }}</a> <a href="{% url 'extras:objectchange_list' %}?request_id={{ value }}">{{ value }}</a>
""" """
JOB_RESULT_CREATED = """
<a href="{{ record.get_absolute_url }}">{{ value|date:"SHORT_DATETIME_FORMAT" }}</a>
"""
JOB_RESULT_COMPLETED = """
<span>{% if value %}{{ value|date:"SHORT_DATETIME_FORMAT" }}{% else %}{% endif %}</span>
"""
JOB_RESULT_STATUS = """
{% if record.status == 'failed' %}
<label class="label label-danger">Failed</label>
{% elif record.status == 'pending' %}
<label class="label label-default">Pending</label>
{% elif record.status == 'running' %}
<label class="label label-warning">Running</label>
{% elif record.status == 'completed' %}
<label class="label label-success">Passed</label>
{% else %}
<label class="label label-default">N/A</label>
{% endif %}
"""
class TagTable(BaseTable): class TagTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
@ -155,21 +133,3 @@ class ObjectChangeTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = ObjectChange model = ObjectChange
fields = ('time', 'user_name', 'action', 'changed_object_type', 'object_repr', 'request_id') fields = ('time', 'user_name', 'action', 'changed_object_type', 'object_repr', 'request_id')
class JobResultHistoryTable(BaseTable):
created = tables.TemplateColumn(
template_code=JOB_RESULT_CREATED,
verbose_name='Run'
)
completed = tables.TemplateColumn(
template_code=JOB_RESULT_COMPLETED
)
status = tables.TemplateColumn(
template_code=JOB_RESULT_STATUS
)
class Meta(BaseTable.Meta):
model = JobResult
fields = ('created', 'completed', 'status')

View File

@ -10,6 +10,7 @@ from extras.api.views import ScriptViewSet
from extras.models import ConfigContext, Graph, ExportTemplate, Tag from extras.models import ConfigContext, Graph, ExportTemplate, Tag
from extras.scripts import BooleanVar, IntegerVar, Script, StringVar from extras.scripts import BooleanVar, IntegerVar, Script, StringVar
from utilities.testing import APITestCase, APIViewTestCases from utilities.testing import APITestCase, APIViewTestCases
from utilities.utils import copy_safe_request
class AppTest(APITestCase): class AppTest(APITestCase):
@ -263,13 +264,7 @@ class ScriptTest(APITestCase):
response = self.client.post(url, data, format='json', **self.header) response = self.client.post(url, data, format='json', **self.header)
self.assertHttpStatus(response, status.HTTP_200_OK) self.assertHttpStatus(response, status.HTTP_200_OK)
self.assertEqual(response.data['log'][0]['status'], 'info') self.assertEqual(response.data['result']['status']['value'], 'pending')
self.assertEqual(response.data['log'][0]['message'], script_data['var1'])
self.assertEqual(response.data['log'][1]['status'], 'success')
self.assertEqual(response.data['log'][1]['message'], script_data['var2'])
self.assertEqual(response.data['log'][2]['status'], 'failure')
self.assertEqual(response.data['log'][2]['message'], script_data['var3'])
self.assertEqual(response.data['output'], 'Script complete')
class CreatedUpdatedFilterTest(APITestCase): class CreatedUpdatedFilterTest(APITestCase):

View File

@ -501,7 +501,6 @@ class ScriptView(ContentTypePermissionRequiredMixin, GetScriptMixin, View):
if form.is_valid(): if form.is_valid():
commit = form.cleaned_data.pop('_commit') commit = form.cleaned_data.pop('_commit')
#output, execution_time = run_script(script, form.cleaned_data, request, commit)
script_content_type = ContentType.objects.get(app_label='extras', model='script') script_content_type = ContentType.objects.get(app_label='extras', model='script')
job_result = JobResult.enqueue_job( job_result = JobResult.enqueue_job(

View File

@ -1,6 +1,7 @@
from collections import OrderedDict from collections import OrderedDict
from django.conf import settings from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.db.models import Count, F from django.db.models import Count, F
from django.shortcuts import render from django.shortcuts import render
from django.views.generic import View from django.views.generic import View
@ -24,6 +25,7 @@ from dcim.tables import (
CableTable, DeviceTable, DeviceTypeTable, PowerFeedTable, RackTable, RackGroupTable, SiteTable, CableTable, DeviceTable, DeviceTypeTable, PowerFeedTable, RackTable, RackGroupTable, SiteTable,
VirtualChassisTable, VirtualChassisTable,
) )
from extras.choices import JobResultStatusChoices
from extras.models import ObjectChange, JobResult from extras.models import ObjectChange, JobResult
from ipam.filters import AggregateFilterSet, IPAddressFilterSet, PrefixFilterSet, VLANFilterSet, VRFFilterSet from ipam.filters import AggregateFilterSet, IPAddressFilterSet, PrefixFilterSet, VLANFilterSet, VRFFilterSet
from ipam.models import Aggregate, IPAddress, Prefix, VLAN, VRF from ipam.models import Aggregate, IPAddress, Prefix, VLAN, VRF
@ -187,6 +189,13 @@ class HomeView(View):
pk__lt=F('_connected_interface') pk__lt=F('_connected_interface')
) )
# Report Results
report_content_type = ContentType.objects.get(app_label='extras', model='report')
report_results = JobResult.objects.filter(
obj_type=report_content_type,
status__in=JobResultStatusChoices.TERMINAL_STATE_CHOICES
).defer('data')[:10]
stats = { stats = {
# Organization # Organization
@ -241,7 +250,7 @@ class HomeView(View):
return render(request, self.template_name, { return render(request, self.template_name, {
'search_form': SearchForm(), 'search_form': SearchForm(),
'stats': stats, 'stats': stats,
'report_results': [],#ReportResult.objects.order_by('-created')[:10], 'report_results': report_results,
'changelog': changelog[:15], 'changelog': changelog[:15],
'new_release': new_release, 'new_release': new_release,
}) })

View File

@ -30,9 +30,6 @@ $(document).ready(function(){
url: url + pending_result_id + '/', url: url + pending_result_id + '/',
method: 'GET', method: 'GET',
dataType: 'json', dataType: 'json',
beforeSend: function(xhr, settings) {
xhr.setRequestHeader("X-CSRFToken", "{{ csrf_token }}");
},
context: this, context: this,
success: function(data) { success: function(data) {
updatePendingStatusLabel(data.status); updatePendingStatusLabel(data.status);

View File

@ -280,8 +280,8 @@
<table class="table table-hover panel-body"> <table class="table table-hover panel-body">
{% for result in report_results %} {% for result in report_results %}
<tr> <tr>
<td><a href="{% url 'extras:report' name=result.report %}">{{ result.report }}</a></td> <td><a href="{% url 'extras:report' name=result.name %}">{{ result.name }}</a></td>
<td class="text-right"><span title="{{ result.created }}">{% include 'extras/inc/report_label.html' %}</span></td> <td class="text-right"><span title="{{ result.created }}">{% include 'extras/inc/job_label.html' %}</span></td>
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>

View File

@ -12,6 +12,7 @@ from dcim.choices import CableLengthUnitChoices
from extras.utils import is_taggable from extras.utils import is_taggable
from utilities.constants import HTTP_REQUEST_META_SAFE_COPY from utilities.constants import HTTP_REQUEST_META_SAFE_COPY
def csv_format(data): def csv_format(data):
""" """
Encapsulate any data which contains a comma within double quotes. Encapsulate any data which contains a comma within double quotes.