mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-26 17:26:10 -06:00
Add add/delete views for reports & scripts
This commit is contained in:
parent
1daac5f781
commit
b2bba7fc40
@ -3,12 +3,14 @@ import copy
|
||||
from django import forms
|
||||
|
||||
from core.models import *
|
||||
from extras.forms.mixins import SyncedDataMixin
|
||||
from netbox.forms import NetBoxModelForm
|
||||
from netbox.registry import registry
|
||||
from utilities.forms import BootstrapMixin, CommentField, get_field_value
|
||||
from utilities.forms import CommentField, get_field_value
|
||||
|
||||
__all__ = (
|
||||
'DataSourceForm',
|
||||
'ManagedFileForm',
|
||||
)
|
||||
|
||||
|
||||
@ -73,3 +75,26 @@ class DataSourceForm(NetBoxModelForm):
|
||||
self.instance.parameters = parameters
|
||||
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class ManagedFileForm(SyncedDataMixin, NetBoxModelForm):
|
||||
upload_file = forms.FileField(
|
||||
required=False
|
||||
)
|
||||
|
||||
fieldsets = (
|
||||
('File Upload', ('upload_file',)),
|
||||
('Data Source', ('data_source', 'data_file')),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = ManagedFile
|
||||
fields = ('data_source', 'data_file')
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
|
||||
if self.cleaned_data.get('upload_file') and self.cleaned_data.get('data_file'):
|
||||
raise forms.ValidationError("Cannot upload a file and sync from an existing file")
|
||||
|
||||
return self.cleaned_data
|
||||
|
@ -315,3 +315,14 @@ class DataFile(models.Model):
|
||||
self.data = f.read()
|
||||
|
||||
return is_modified
|
||||
|
||||
def write_to_disk(self, path, overwrite=False):
|
||||
"""
|
||||
Write the object's data to disk at the specified path
|
||||
"""
|
||||
# Check whether file already exists
|
||||
if os.path.isfile(path) and not overwrite:
|
||||
raise FileExistsError()
|
||||
|
||||
with open(path, 'wb+') as new_file:
|
||||
new_file.write(self.data)
|
||||
|
@ -71,3 +71,14 @@ class ManagedFile(SyncedDataMixin, models.Model):
|
||||
'scripts': settings.SCRIPTS_ROOT,
|
||||
'reports': settings.REPORTS_ROOT,
|
||||
}[self.file_root]
|
||||
|
||||
def sync_data(self):
|
||||
if self.data_file:
|
||||
self.file_path = self.data_path
|
||||
self.data_file.write_to_disk(self.full_path, overwrite=True)
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
# Delete file from disk
|
||||
os.remove(self.full_path)
|
||||
|
||||
return super().delete(*args, **kwargs)
|
||||
|
@ -21,9 +21,6 @@ urlpatterns = (
|
||||
|
||||
# Managed files
|
||||
path('files/', views.ManagedFileListView.as_view(), name='managedfile_list'),
|
||||
# path('files/add/', views.ManagedFileEditView.as_view(), name='managedfile_add'),
|
||||
# path('files/edit/', views.ManagedFileBulkEditView.as_view(), name='managedfile_bulk_edit'),
|
||||
# path('files/delete/', views.ManagedFileBulkDeleteView.as_view(), name='managedfile_bulk_delete'),
|
||||
path('files/<int:pk>/', include(get_model_urls('core', 'managedfile'))),
|
||||
|
||||
)
|
||||
|
@ -128,35 +128,9 @@ class DataFileBulkDeleteView(generic.BulkDeleteView):
|
||||
|
||||
class ManagedFileListView(generic.ObjectListView):
|
||||
queryset = ManagedFile.objects.all()
|
||||
# filterset = filtersets.ManagedFileFilterSet
|
||||
# filterset_form = forms.ManagedFileFilterForm
|
||||
table = tables.ManagedFileTable
|
||||
|
||||
|
||||
@register_model_view(ManagedFile)
|
||||
class ManagedFileView(generic.ObjectView):
|
||||
queryset = ManagedFile.objects.all()
|
||||
|
||||
|
||||
# @register_model_view(ManagedFile, 'edit')
|
||||
# class ManagedFileEditView(generic.ObjectEditView):
|
||||
# queryset = ManagedFile.objects.all()
|
||||
# form = forms.ManagedFileForm
|
||||
|
||||
|
||||
@register_model_view(ManagedFile, 'delete')
|
||||
class ManagedFileDeleteView(generic.ObjectDeleteView):
|
||||
queryset = ManagedFile.objects.all()
|
||||
|
||||
|
||||
# class ManagedFileBulkEditView(generic.BulkEditView):
|
||||
# queryset = ManagedFile.objects.all()
|
||||
# # filterset = filtersets.ManagedFileFilterSet
|
||||
# table = tables.ManagedFileTable
|
||||
# form = forms.ManagedFileBulkEditForm
|
||||
|
||||
|
||||
# class ManagedFileBulkDeleteView(generic.BulkDeleteView):
|
||||
# queryset = ManagedFile.objects.all()
|
||||
# # filterset = filtersets.ManagedFileFilterSet
|
||||
# table = tables.ManagedFileTable
|
||||
|
@ -17,6 +17,10 @@ WEBHOOK_EVENT_TYPES = {
|
||||
EVENT_JOB_END: 'job_ended',
|
||||
}
|
||||
|
||||
# Managed files
|
||||
REPORTS_ROOT_NAME = 'reports'
|
||||
SCRIPTS_ROOT_NAME = 'scripts'
|
||||
|
||||
# Dashboard
|
||||
DEFAULT_DASHBOARD = [
|
||||
{
|
||||
|
@ -836,7 +836,7 @@ class Script(JobResultsMixin, WebhooksMixin, models.Model):
|
||||
managed = False
|
||||
|
||||
|
||||
class ScriptModuleManager(models.Manager):
|
||||
class ScriptModuleManager(models.Manager.from_queryset(RestrictedQuerySet)):
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().filter(file_root='scripts')
|
||||
@ -851,6 +851,10 @@ class ScriptModule(JobResultsMixin, WebhooksMixin, PythonModuleMixin, ManagedFil
|
||||
class Meta:
|
||||
proxy = True
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.file_root = SCRIPTS_ROOT_NAME
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
|
||||
#
|
||||
# Reports
|
||||
@ -864,7 +868,7 @@ class Report(JobResultsMixin, WebhooksMixin, models.Model):
|
||||
managed = False
|
||||
|
||||
|
||||
class ReportModuleManager(models.Manager):
|
||||
class ReportModuleManager(models.Manager.from_queryset(RestrictedQuerySet)):
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().filter(file_root='reports')
|
||||
@ -878,3 +882,7 @@ class ReportModule(JobResultsMixin, WebhooksMixin, PythonModuleMixin, ManagedFil
|
||||
|
||||
class Meta:
|
||||
proxy = True
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.file_root = REPORTS_ROOT_NAME
|
||||
return super().save(*args, **kwargs)
|
||||
|
@ -96,17 +96,21 @@ urlpatterns = [
|
||||
path('reports/', views.ReportListView.as_view(), name='report_list'),
|
||||
path('reports/results/<int:job_result_pk>/', views.ReportResultView.as_view(), name='report_result'),
|
||||
re_path(r'^reports/(?P<module>.([^.]+)).(?P<name>.(.+))/', views.ReportView.as_view(), name='report'),
|
||||
path('reports/add/', views.ScriptModuleCreateView.as_view(), name='reportmodule_add'),
|
||||
path('reports/<int:pk>/', include(get_model_urls('extras', 'reportmodule'))),
|
||||
|
||||
# Scripts
|
||||
path('scripts/', views.ScriptListView.as_view(), name='script_list'),
|
||||
path('scripts/results/<int:job_result_pk>/', views.ScriptResultView.as_view(), name='script_result'),
|
||||
re_path(r'^scripts/(?P<module>.([^.]+)).(?P<name>.(.+))/', views.ScriptView.as_view(), name='script'),
|
||||
path('scripts/add/', views.ScriptModuleCreateView.as_view(), name='scriptmodule_add'),
|
||||
path('scripts/<int:pk>/', include(get_model_urls('extras', 'scriptmodule'))),
|
||||
|
||||
# Job results
|
||||
path('job-results/', views.JobResultListView.as_view(), name='jobresult_list'),
|
||||
path('job-results/delete/', views.JobResultBulkDeleteView.as_view(), name='jobresult_bulk_delete'),
|
||||
path('job-results/<int:pk>/delete/', views.JobResultDeleteView.as_view(), name='jobresult_delete'),
|
||||
|
||||
# Scripts
|
||||
path('scripts/', views.ScriptListView.as_view(), name='script_list'),
|
||||
path('scripts/results/<int:job_result_pk>/', views.ScriptResultView.as_view(), name='script_result'),
|
||||
re_path(r'^scripts/(?P<module>.([^.]+)).(?P<name>.(.+))/', views.ScriptView.as_view(), name='script'),
|
||||
|
||||
# Markdown
|
||||
path('render/markdown/', views.RenderMarkdownView.as_view(), name="render_markdown")
|
||||
]
|
||||
|
@ -7,6 +7,7 @@ from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.urls import reverse
|
||||
from django.views.generic import View
|
||||
|
||||
from core.forms import ManagedFileForm
|
||||
from extras.dashboard.forms import DashboardWidgetAddForm, DashboardWidgetForm
|
||||
from extras.dashboard.utils import get_widget_class
|
||||
from netbox.views import generic
|
||||
@ -18,6 +19,7 @@ from utilities.utils import copy_safe_request, count_related, get_viewname, norm
|
||||
from utilities.views import ContentTypePermissionRequiredMixin, register_model_view
|
||||
from . import filtersets, forms, tables
|
||||
from .choices import JobResultStatusChoices
|
||||
from .constants import SCRIPTS_ROOT_NAME, REPORTS_ROOT_NAME
|
||||
from .forms.reports import ReportForm
|
||||
from .models import *
|
||||
from .reports import get_report, get_reports, run_report
|
||||
@ -790,6 +792,21 @@ class DashboardWidgetDeleteView(LoginRequiredMixin, View):
|
||||
# Reports
|
||||
#
|
||||
|
||||
@register_model_view(ReportModule, 'edit')
|
||||
class ReportModuleCreateView(generic.ObjectEditView):
|
||||
queryset = ReportModule.objects.all()
|
||||
form = ManagedFileForm
|
||||
|
||||
def alter_object(self, obj, *args, **kwargs):
|
||||
obj.file_root = REPORTS_ROOT_NAME
|
||||
return obj
|
||||
|
||||
|
||||
@register_model_view(ReportModule, 'delete')
|
||||
class ReportModuleDeleteView(generic.ObjectDeleteView):
|
||||
queryset = ReportModule.objects.all()
|
||||
|
||||
|
||||
class ReportListView(ContentTypePermissionRequiredMixin, View):
|
||||
"""
|
||||
Retrieve all of the available reports from disk and the recorded JobResult (if any) for each.
|
||||
@ -819,6 +836,7 @@ class ReportListView(ContentTypePermissionRequiredMixin, View):
|
||||
ret.append((module, module_reports))
|
||||
|
||||
return render(request, 'extras/report_list.html', {
|
||||
'model': ReportModule,
|
||||
'reports': ret,
|
||||
})
|
||||
|
||||
@ -924,6 +942,21 @@ class ReportResultView(ContentTypePermissionRequiredMixin, View):
|
||||
# Scripts
|
||||
#
|
||||
|
||||
@register_model_view(ScriptModule, 'edit')
|
||||
class ScriptModuleCreateView(generic.ObjectEditView):
|
||||
queryset = ScriptModule.objects.all()
|
||||
form = ManagedFileForm
|
||||
|
||||
def alter_object(self, obj, *args, **kwargs):
|
||||
obj.file_root = SCRIPTS_ROOT_NAME
|
||||
return obj
|
||||
|
||||
|
||||
@register_model_view(ScriptModule, 'delete')
|
||||
class ScriptModuleDeleteView(generic.ObjectDeleteView):
|
||||
queryset = ScriptModule.objects.all()
|
||||
|
||||
|
||||
class GetScriptMixin:
|
||||
def _get_script(self, name, module=None):
|
||||
if module is None:
|
||||
@ -957,6 +990,7 @@ class ScriptListView(ContentTypePermissionRequiredMixin, View):
|
||||
script.result = results.get(script.full_name)
|
||||
|
||||
return render(request, 'extras/script_list.html', {
|
||||
'model': ScriptModule,
|
||||
'scripts': scripts,
|
||||
})
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
{% extends 'base/layout.html' %}
|
||||
{% load buttons %}
|
||||
{% load helpers %}
|
||||
{% load perms %}
|
||||
|
||||
{% block title %}Reports{% endblock %}
|
||||
|
||||
@ -11,6 +13,15 @@
|
||||
</ul>
|
||||
{% endblock tabs %}
|
||||
|
||||
{% block controls %}
|
||||
<div class="controls">
|
||||
<div class="control-group">
|
||||
{% block extra_controls %}{% endblock %}
|
||||
{% add_button model %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock controls %}
|
||||
|
||||
{% block content-wrapper %}
|
||||
<div class="tab-content">
|
||||
{% if reports %}
|
||||
|
@ -1,8 +1,18 @@
|
||||
{% extends 'base/layout.html' %}
|
||||
{% load buttons %}
|
||||
{% load helpers %}
|
||||
|
||||
{% block title %}Scripts{% endblock %}
|
||||
|
||||
{% block controls %}
|
||||
<div class="controls">
|
||||
<div class="control-group">
|
||||
{% block extra_controls %}{% endblock %}
|
||||
{% add_button model %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock controls %}
|
||||
|
||||
{% block tabs %}
|
||||
<ul class="nav nav-tabs px-3">
|
||||
<li class="nav-item" role="presentation">
|
||||
|
Loading…
Reference in New Issue
Block a user