From ac198ef7cfbfa9a34c9cc1d5b301caa1dacaa513 Mon Sep 17 00:00:00 2001 From: Hedde van der Heide Date: Fri, 22 Apr 2022 13:30:10 +0200 Subject: [PATCH] Large overhaul of naming conventions and code clean ups --- src/README.rst | 9 ++-- src/SECURITY.md | 13 ++++++ src/netbox_slm/__init__.py | 4 +- src/netbox_slm/api/views.py | 15 +++---- src/netbox_slm/fields.py | 50 --------------------- src/netbox_slm/filters.py | 75 ------------------------------- src/netbox_slm/filtersets.py | 23 ++++++++++ src/netbox_slm/forms.py | 87 ++++++++++++++++++++---------------- src/netbox_slm/models.py | 12 ++--- src/netbox_slm/navigation.py | 1 + src/netbox_slm/urls.py | 1 + src/netbox_slm/views.py | 79 ++++++++++++++------------------ src/setup.cfg | 2 +- 13 files changed, 138 insertions(+), 233 deletions(-) create mode 100644 src/SECURITY.md delete mode 100644 src/netbox_slm/fields.py delete mode 100644 src/netbox_slm/filters.py create mode 100644 src/netbox_slm/filtersets.py diff --git a/src/README.rst b/src/README.rst index 0142fc2..151d732 100644 --- a/src/README.rst +++ b/src/README.rst @@ -6,11 +6,14 @@ Software Lifecycle Management -------------- +Known Issues +~~~~~~~~~~~~~~~~~~ +- WARNING: This plugin is only tested with Netbox 3.2 at this time. +- CSV/Bulk imports for SoftwareProduct, Version and Installation are currently broken (WIP) + Installation Guide ~~~~~~~~~~~~~~~~~~ -WARNING: This plugin is only tested with Netbox 3.2 at this time. - When using the Docker version of Netbox, first follow the `netbox-docker quickstart `__ instructions to clone the netbox-docker repo and set up the @@ -22,7 +25,7 @@ to install the Netbox SLM plugin: 1. Add ``netbox_slm`` to the ``PLUGINS`` list in ``configuration/extra.py``. -2. Create a ``plugin_requirements.txt`` with ``netbox-slm==0.98`` as +2. Create a ``plugin_requirements.txt`` with ``netbox-slm==0.99`` as contents. 3. Create a ``Dockerfile-SLM`` with contents: diff --git a/src/SECURITY.md b/src/SECURITY.md new file mode 100644 index 0000000..b713346 --- /dev/null +++ b/src/SECURITY.md @@ -0,0 +1,13 @@ +# Security Policy + +## Current status + +Netbox SLM is a plugin for Netbox with mostly default configuration. We advise to monitor Netbox security findings and update accordingly. + +## Supported Versions + +Only the latest version of Netbox SLM is currently being supported with security updates. We aim to keep the plugin compatible with the latest Netbox version, but have no incentive to patch older tags at this point. + +## Reporting a Vulnerability + +Please report security vulnerabilities by opening a ticket. The aim is to get back to you within 24 hours with a confirmation of the issue and a brief action plan or a request for more information. diff --git a/src/netbox_slm/__init__.py b/src/netbox_slm/__init__.py index 759e653..e25b235 100644 --- a/src/netbox_slm/__init__.py +++ b/src/netbox_slm/__init__.py @@ -5,7 +5,7 @@ class SLMConfig(PluginConfig): name = 'netbox_slm' verbose_name = 'Software Lifecycle Management' description = 'Software Lifecycle Management' - version = '0.98' + version = '0.99' author = 'Hedde van der Heide' author_email = 'hedde.vanderheide@ictu.nl' base_url = 'slm' @@ -14,4 +14,4 @@ class SLMConfig(PluginConfig): 'version_info': False } -config = SLMConfig \ No newline at end of file +config = SLMConfig diff --git a/src/netbox_slm/api/views.py b/src/netbox_slm/api/views.py index 8fbd9b4..021c22a 100644 --- a/src/netbox_slm/api/views.py +++ b/src/netbox_slm/api/views.py @@ -1,14 +1,11 @@ -from rest_framework import serializers -from rest_framework.decorators import action -from rest_framework.response import Response +from netbox.api.viewsets import NetBoxModelViewSet from rest_framework.routers import APIRootView -from netbox.api.viewsets import NetBoxModelViewSet from netbox_slm.api.serializers import ( SoftwareProductSerializer, SoftwareProductVersionSerializer, SoftwareProductInstallationSerializer, ) -from netbox_slm.filters import ( - SoftwareProductFilter, SoftwareProductVersionFilter, SoftwareProductInstallationFilter, +from netbox_slm.filtersets import ( + SoftwareProductFilterSet, SoftwareProductVersionFilterSet, SoftwareProductInstallationFilterSet, ) from netbox_slm.models import ( SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation, @@ -27,16 +24,16 @@ class NetboxSLMRootView(APIRootView): class SoftwareProductViewSet(NetBoxModelViewSet): queryset = SoftwareProduct.objects.all() serializer_class = SoftwareProductSerializer - filterset_class = SoftwareProductFilter + filterset_class = SoftwareProductFilterSet class SoftwareProductVersionViewSet(NetBoxModelViewSet): queryset = SoftwareProductVersion.objects.all() serializer_class = SoftwareProductVersionSerializer - filterset_class = SoftwareProductVersionFilter + filterset_class = SoftwareProductVersionFilterSet class SoftwareProductInstallationViewSet(NetBoxModelViewSet): queryset = SoftwareProductInstallation.objects.all() serializer_class = SoftwareProductInstallationSerializer - filterset_class = SoftwareProductInstallationFilter + filterset_class = SoftwareProductInstallationFilterSet diff --git a/src/netbox_slm/fields.py b/src/netbox_slm/fields.py deleted file mode 100644 index ec70426..0000000 --- a/src/netbox_slm/fields.py +++ /dev/null @@ -1,50 +0,0 @@ -from django.forms import BoundField -from django.urls import reverse - -from utilities.forms import DynamicModelMultipleChoiceField - - -class CustomDynamicModelMultipleChoiceField(DynamicModelMultipleChoiceField): - def get_bound_field(self, form, field_name): - bound_field = BoundField(form, self, field_name) - - # Set initial value based on prescribed child fields (if not already set) - if not self.initial and self.initial_params: - filter_kwargs = {} - for kwarg, child_field in self.initial_params.items(): - value = form.initial.get(child_field.lstrip("$")) - if value: - filter_kwargs[kwarg] = value - if filter_kwargs: - self.initial = self.queryset.filter(**filter_kwargs).first() - - # Modify the QuerySet of the field before we return it. Limit choices to any data already bound: Options - # will be populated on-demand via the APISelect widget. - data = bound_field.value() - - if data: - field_name = getattr(self, "to_field_name") or "pk" - filter = self.filter(field_name=field_name) - try: - self.queryset = filter.filter(self.queryset, data) - except (TypeError, ValueError): - # Catch any error caused by invalid initial data passed from the user - self.queryset = self.queryset.none() - else: - self.queryset = self.queryset.none() - - # Set the data URL on the APISelect widget (if not already set) - widget = bound_field.field.widget - if not widget.attrs.get("data-url"): - app_label = self.queryset.model._meta.app_label - model_name = self.queryset.model._meta.model_name - - # - # Custom url for work with plugin api - # - data_url = reverse( - "plugins-api:{}-api:{}-list".format(app_label, model_name) - ) - widget.attrs["data-url"] = data_url - - return bound_field diff --git a/src/netbox_slm/filters.py b/src/netbox_slm/filters.py deleted file mode 100644 index 0a6b83d..0000000 --- a/src/netbox_slm/filters.py +++ /dev/null @@ -1,75 +0,0 @@ -import django_filters -from django.db.models import Q -from django.utils.translation import gettext as _ -from extras.filters import TagFilter -from netbox.filtersets import NetBoxModelFilterSet -from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation -from utilities.forms import DynamicModelMultipleChoiceField - - -class BaseFilter(NetBoxModelFilterSet): - q = django_filters.CharFilter( - method="search", - label="Search", - ) - - -class SoftwareProductFilter(BaseFilter): - """Filter capabilities for SoftwareProduct instances.""" - - name = django_filters.CharFilter( - lookup_expr="icontains", - ) - # tag = TagFilter() - - class Meta: - model = SoftwareProduct - fields = ("name",) # "tag") - - def search(self, queryset, name, value): - """Perform the filtered search.""" - if not value.strip(): - return queryset - qs_filter = Q(name__icontains=value) # | Q(status__icontains=value) - return queryset.filter(qs_filter) - - -class SoftwareProductVersionFilter(BaseFilter): - name = django_filters.CharFilter( - lookup_expr="icontains", label="Version" - ) - # software_product_id = django_filters.ModelMultipleChoiceFilter( - # queryset=SoftwareProduct.objects.all(), - # label='SoftwareProduct (ID)', - # ) - # software_product = django_filters.ModelMultipleChoiceFilter( - # field_name='software_product__name', - # queryset=SoftwareProduct.objects.all(), - # to_field_name='name', - # label='SoftwareProduct (name)', - # ) - # tag = TagFilter() - - class Meta: - model = SoftwareProductVersion - fields = ("name", "software_product") # "tag") - - def search(self, queryset, name, value): - """Perform the filtered search.""" - if not value.strip(): - return queryset - qs_filter = Q(name__icontains=value) # | Q(status__icontains=value) - return queryset.filter(qs_filter) - - -class SoftwareProductInstallationFilter(BaseFilter): - class Meta: - model = SoftwareProductInstallation - fields = tuple() # "tag") - - def search(self, queryset, name, value): - """Perform the filtered search.""" - if not value.strip(): - return queryset - qs_filter = Q(software_product__name__icontains=value) | Q(version__name__icontains=value) - return queryset.filter(qs_filter) diff --git a/src/netbox_slm/filtersets.py b/src/netbox_slm/filtersets.py new file mode 100644 index 0000000..31ca9e1 --- /dev/null +++ b/src/netbox_slm/filtersets.py @@ -0,0 +1,23 @@ +from netbox.filtersets import NetBoxModelFilterSet +from netbox_slm.models import * + + +class SoftwareProductFilterSet(NetBoxModelFilterSet): + """Filter capabilities for SoftwareProduct instances.""" + class Meta: + model = SoftwareProduct + fields = tuple() + + +class SoftwareProductVersionFilterSet(NetBoxModelFilterSet): + """Filter capabilities for SoftwareProductVersion instances.""" + class Meta: + model = SoftwareProductVersion + fields = tuple() + + +class SoftwareProductInstallationFilterSet(NetBoxModelFilterSet): + """Filter capabilities for SoftwareProductInstallation instances.""" + class Meta: + model = SoftwareProductInstallation + fields = tuple() diff --git a/src/netbox_slm/forms.py b/src/netbox_slm/forms.py index 52cf869..12c9814 100644 --- a/src/netbox_slm/forms.py +++ b/src/netbox_slm/forms.py @@ -1,16 +1,18 @@ from django import forms +from django.db.models import Q from django.urls import reverse_lazy from django.utils.translation import gettext as _ + +from dcim.models import Manufacturer, Device from netbox.forms import ( NetBoxModelForm, NetBoxModelCSVForm, NetBoxModelBulkEditForm, NetBoxModelFilterSetForm, ) -from dcim.models import Manufacturer, Device from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation from utilities.forms import ( - BootstrapMixin, DynamicModelChoiceField, APISelect, DynamicModelMultipleChoiceField + DynamicModelChoiceField, APISelect, TagFilterField ) from virtualization.models import VirtualMachine @@ -21,9 +23,6 @@ class SoftwareProductForm(NetBoxModelForm): manufacturer = DynamicModelChoiceField( queryset=Manufacturer.objects.all(), required=False, - # initial_params={ - # 'device_types': 'device_type' - # } ) class Meta: @@ -32,24 +31,26 @@ class SoftwareProductForm(NetBoxModelForm): class SoftwareProductFilterForm(NetBoxModelFilterSetForm): - """Form for filtering SoftwareProduct instances.""" - model = SoftwareProduct - - q = forms.CharField(required=False, label="Search") - - name = forms.CharField( - required=False, - label="Name", + fieldsets = ( + (None, ('q', 'tag')), ) - # tag = TagFilterField(SoftwareProduct) + tag = TagFilterField(model) + + def search(self, queryset, name, value): + """Perform the filtered search.""" + if not value.strip(): + return queryset + qs_filter = Q(name__icontains=value) | \ + Q(manufacturer__name__icontains=value) + return queryset.filter(qs_filter) class SoftwareProductCSVForm(NetBoxModelCSVForm): class Meta: model = SoftwareProduct - fields = ("name",) + fields = ("name", "manufacturer",) class SoftwareProductBulkEditForm(NetBoxModelBulkEditForm): @@ -78,24 +79,23 @@ class SoftwareProductVersionForm(NetBoxModelForm): model = SoftwareProductVersion fields = ("name", "software_product", "tags") - # def clean(self): - # import pdb;pdb.set_trace() - # return super(SoftwareProductVersionForm, self).clean() - class SoftwareProductVersionFilterForm(NetBoxModelFilterSetForm): - """Form for filtering SoftwareProductVersion instances.""" - model = SoftwareProductVersion - - q = forms.CharField(required=False, label="Search") - - name = forms.CharField( - required=False, - label="Name", + fieldsets = ( + (None, ('q', 'tag')), ) - # tag = TagFilterField(SoftwareProduct) + tag = TagFilterField(model) + + def search(self, queryset, name, value): + """Perform the filtered search.""" + if not value.strip(): + return queryset + qs_filter = Q(name__icontains=value) | \ + Q(software_product__name__icontains=value) | \ + Q(software_product__manufacturer__name__icontains=value) + return queryset.filter(qs_filter) class SoftwareProductVersionCSVForm(NetBoxModelCSVForm): @@ -121,16 +121,10 @@ class SoftwareProductInstallationForm(NetBoxModelForm): device = DynamicModelChoiceField( queryset=Device.objects.all(), required=False, - # initial_params={ - # 'device_types': 'device_type' - # } ) virtualmachine = DynamicModelChoiceField( queryset=VirtualMachine.objects.all(), required=False, - # initial_params={ - # 'device_types': 'device_type' - # } ) software_product = DynamicModelChoiceField( queryset=SoftwareProduct.objects.all(), @@ -154,6 +148,14 @@ class SoftwareProductInstallationForm(NetBoxModelForm): model = SoftwareProductInstallation fields = ("device", "virtualmachine", "software_product", "version", "tags") + def clean_version(self): + version = self.cleaned_data['version'] + software_product = self.cleaned_data['software_product'] + if version not in software_product.softwareproductversion_set.all(): + raise forms.ValidationError(_(f"Version `{version}` doesn't exist on {software_product}, make sure you've " + f"selected a compatible version or first select the software product.")) + return version + def clean(self): if not any([self.cleaned_data['device'], self.cleaned_data['virtualmachine']]): raise forms.ValidationError(_("Installation requires atleast one virtualmachine or device destination.")) @@ -161,13 +163,21 @@ class SoftwareProductInstallationForm(NetBoxModelForm): class SoftwareProductInstallationFilterForm(NetBoxModelFilterSetForm): - """Form for filtering SoftwareProductInstallation instances.""" - model = SoftwareProductInstallation + fieldsets = ( + (None, ('q', 'tag')), + ) - q = forms.CharField(required=False, label="Search") + tag = TagFilterField(model) - # tag = TagFilterField(SoftwareProduct) + def search(self, queryset, name, value): + """Perform the filtered search.""" + if not value.strip(): + return queryset + qs_filter = Q(software_product__name__icontains=value) | \ + Q(software_product__manufacturer__name__icontains=value) | \ + Q(version__name__icontains=value) + return queryset.filter(qs_filter) class SoftwareProductInstallationCSVForm(NetBoxModelCSVForm): @@ -189,4 +199,3 @@ class SoftwareProductInstallationBulkEditForm(NetBoxModelBulkEditForm): fieldsets = ( (None, ('software_product', 'version',)), ) - # nullable_fields = ('',) \ No newline at end of file diff --git a/src/netbox_slm/models.py b/src/netbox_slm/models.py index 37f56c4..bf6545f 100644 --- a/src/netbox_slm/models.py +++ b/src/netbox_slm/models.py @@ -1,16 +1,12 @@ from django.db import models from django.urls import reverse, reverse_lazy from django.utils import safestring -# from extras.utils import extras_features # todo this changed in netbox 3.1.X and higher, currently not used but checkout why + from netbox.models import NetBoxModel from utilities.querysets import RestrictedQuerySet -# @extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class SoftwareProduct(NetBoxModel): - """ - A SoftwareProduct represents ... - """ name = models.CharField(max_length=128) description = models.CharField(max_length=255, null=True, blank=True) @@ -36,7 +32,6 @@ class SoftwareProduct(NetBoxModel): )) if count else "0" -# @extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class SoftwareProductVersion(NetBoxModel): software_product = models.ForeignKey( to='netbox_slm.SoftwareProduct', @@ -60,7 +55,6 @@ class SoftwareProductVersion(NetBoxModel): )) if count else "0" -# @extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class SoftwareProductInstallation(NetBoxModel): device = models.ForeignKey( to='dcim.Device', @@ -71,8 +65,8 @@ class SoftwareProductInstallation(NetBoxModel): virtualmachine = models.ForeignKey( to='virtualization.VirtualMachine', on_delete=models.PROTECT, - null = True, - blank = True + null=True, + blank=True ) software_product = models.ForeignKey( to='netbox_slm.SoftwareProduct', diff --git a/src/netbox_slm/navigation.py b/src/netbox_slm/navigation.py index d34579b..71c25de 100644 --- a/src/netbox_slm/navigation.py +++ b/src/netbox_slm/navigation.py @@ -1,6 +1,7 @@ from extras.plugins import PluginMenuButton, PluginMenuItem from utilities.choices import ButtonColorChoices + menu_items = ( PluginMenuItem( link='plugins:netbox_slm:softwareproduct_list', diff --git a/src/netbox_slm/urls.py b/src/netbox_slm/urls.py index 67a32f7..7e3e2d9 100644 --- a/src/netbox_slm/urls.py +++ b/src/netbox_slm/urls.py @@ -1,5 +1,6 @@ from django.urls import path from netbox.views.generic import ObjectChangeLogView + from netbox_slm import views from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation diff --git a/src/netbox_slm/views.py b/src/netbox_slm/views.py index 504d093..898e540 100644 --- a/src/netbox_slm/views.py +++ b/src/netbox_slm/views.py @@ -1,25 +1,19 @@ from netbox.views import generic -from netbox_slm.filters import SoftwareProductFilter, SoftwareProductVersionFilter, SoftwareProductInstallationFilter -from netbox_slm.forms import ( - SoftwareProductForm, SoftwareProductFilterForm, SoftwareProductCSVForm, SoftwareProductBulkEditForm, - SoftwareProductVersionForm, SoftwareProductVersionFilterForm, SoftwareProductVersionCSVForm, - SoftwareProductVersionBulkEditForm, SoftwareProductInstallationForm, SoftwareProductInstallationFilterForm, - SoftwareProductInstallationCSVForm, SoftwareProductInstallationBulkEditForm -) +from netbox_slm import filtersets +from netbox_slm import forms +from netbox_slm import tables from netbox_slm.models import ( SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation ) -from netbox_slm.tables import SoftwareProductTable, SoftwareProductVersionTable, SoftwareProductInstallationTable class SoftwareProductListView(generic.ObjectListView): """View for listing all existing SoftwareProducts.""" queryset = SoftwareProduct.objects.all() - filterset = SoftwareProductFilter - filterset_form = SoftwareProductFilterForm - table = SoftwareProductTable - # template_name = "netbox_slm/object_list.html" + filterset = filtersets.SoftwareProductFilterSet + filterset_form = forms.SoftwareProductFilterForm + table = tables.SoftwareProductTable class SoftwareProductView(generic.ObjectView): @@ -36,7 +30,7 @@ class SoftwareProductEditView(generic.ObjectEditView): """View for editing and creating a SoftwareProduct instance.""" queryset = SoftwareProduct.objects.all() - form = SoftwareProductForm + form = forms.SoftwareProductForm class SoftwareProductDeleteView(generic.ObjectDeleteView): @@ -47,30 +41,30 @@ class SoftwareProductDeleteView(generic.ObjectDeleteView): class SoftwareProductBulkImportView(generic.BulkImportView): queryset = SoftwareProduct.objects.all() - model_form = SoftwareProductCSVForm - table = SoftwareProductTable + model_form = forms.SoftwareProductCSVForm + table = tables.SoftwareProductTable class SoftwareProductBulkEditView(generic.BulkEditView): queryset = SoftwareProduct.objects.all() - filterset = SoftwareProductFilter - table = SoftwareProductTable - form = SoftwareProductBulkEditForm + filterset = filtersets.SoftwareProductFilterSet + filterset_form = forms.SoftwareProductFilterForm + table = tables.SoftwareProductTable + form = forms.SoftwareProductBulkEditForm class SoftwareProductBulkDeleteView(generic.BulkDeleteView): queryset = SoftwareProduct.objects.all() - table = SoftwareProductTable + table = tables.SoftwareProductTable class SoftwareProductVersionListView(generic.ObjectListView): """View for listing all existing SoftwareProductVersions.""" queryset = SoftwareProductVersion.objects.all() - filterset = SoftwareProductVersionFilter - filterset_form = SoftwareProductVersionFilterForm - table = SoftwareProductVersionTable - # template_name = "netbox_slm/object_list.html" + filterset = filtersets.SoftwareProductVersionFilterSet + filterset_form = forms.SoftwareProductVersionFilterForm + table = tables.SoftwareProductVersionTable class SoftwareProductVersionView(generic.ObjectView): @@ -87,7 +81,7 @@ class SoftwareProductVersionEditView(generic.ObjectEditView): """View for editing and creating a SoftwareProductVersion instance.""" queryset = SoftwareProductVersion.objects.all() - form = SoftwareProductVersionForm + form = forms.SoftwareProductVersionForm class SoftwareProductVersionDeleteView(generic.ObjectDeleteView): @@ -98,30 +92,29 @@ class SoftwareProductVersionDeleteView(generic.ObjectDeleteView): class SoftwareProductVersionBulkImportView(generic.BulkImportView): queryset = SoftwareProductVersion.objects.all() - model_form = SoftwareProductVersionCSVForm - table = SoftwareProductVersionTable + model_form = forms.SoftwareProductVersionCSVForm + table = tables.SoftwareProductVersionTable class SoftwareProductVersionBulkEditView(generic.BulkEditView): queryset = SoftwareProductVersion.objects.all() - filterset = SoftwareProductVersionFilter - table = SoftwareProductVersionTable - form = SoftwareProductVersionBulkEditForm + filterset = filtersets.SoftwareProductVersionFilterSet + table = tables.SoftwareProductVersionTable + form = forms.SoftwareProductVersionBulkEditForm class SoftwareProductVersionBulkDeleteView(generic.BulkDeleteView): queryset = SoftwareProductVersion.objects.all() - table = SoftwareProductVersionTable + table = tables.SoftwareProductVersionTable class SoftwareProductInstallationListView(generic.ObjectListView): """View for listing all existing SoftwareProductInstallations.""" queryset = SoftwareProductInstallation.objects.all() - filterset = SoftwareProductInstallationFilter - filterset_form = SoftwareProductInstallationFilterForm - table = SoftwareProductInstallationTable - # template_name = "netbox_slm/object_list.html" + filterset = filtersets.SoftwareProductInstallationFilterSet + filterset_form = forms.SoftwareProductInstallationFilterForm + table = tables.SoftwareProductInstallationTable class SoftwareProductInstallationView(generic.ObjectView): @@ -129,16 +122,12 @@ class SoftwareProductInstallationView(generic.ObjectView): queryset = SoftwareProductInstallation.objects.all() - # def get_extra_context(self, request, instance): - # records = instance.record_set.all() - # return {"records": records} - class SoftwareProductInstallationEditView(generic.ObjectEditView): """View for editing and creating a SoftwareProductInstallation instance.""" queryset = SoftwareProductInstallation.objects.all() - form = SoftwareProductInstallationForm + form = forms.SoftwareProductInstallationForm class SoftwareProductInstallationDeleteView(generic.ObjectDeleteView): @@ -149,17 +138,17 @@ class SoftwareProductInstallationDeleteView(generic.ObjectDeleteView): class SoftwareProductInstallationBulkImportView(generic.BulkImportView): queryset = SoftwareProductInstallation.objects.all() - model_form = SoftwareProductInstallationCSVForm - table = SoftwareProductInstallationTable + model_form = forms.SoftwareProductInstallationCSVForm + table = tables.SoftwareProductInstallationTable class SoftwareProductInstallationBulkEditView(generic.BulkEditView): queryset = SoftwareProductInstallation.objects.all() - filterset = SoftwareProductInstallationFilter - table = SoftwareProductInstallationTable - form = SoftwareProductInstallationBulkEditForm + filterset = filtersets.SoftwareProductInstallationFilterSet + table = tables.SoftwareProductInstallationTable + form = forms.SoftwareProductInstallationBulkEditForm class SoftwareProductInstallationBulkDeleteView(generic.BulkDeleteView): queryset = SoftwareProductInstallation.objects.all() - table = SoftwareProductInstallationTable + table = tables.SoftwareProductInstallationTable diff --git a/src/setup.cfg b/src/setup.cfg index fce5cb5..d290fa1 100644 --- a/src/setup.cfg +++ b/src/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = netbox-slm -version = 0.98 +version = 0.99 description = Software Lifecycle Management Netbox Plugin. # long_description = file: README.rst # long_description_content_type='text/x-rst'