mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-24 17:38:37 -06:00
fix tests and cleanup
This commit is contained in:
parent
1681dbfa39
commit
f48a079ae6
@ -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()
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
@ -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__
|
||||||
|
@ -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')
|
|
||||||
|
|
||||||
|
@ -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):
|
||||||
|
@ -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(
|
||||||
|
@ -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,
|
||||||
})
|
})
|
||||||
|
@ -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);
|
||||||
|
@ -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>
|
||||||
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user