Large overhaul of naming conventions and code clean ups
This commit is contained in:
+6
-3
@@ -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
|
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
|
When using the Docker version of Netbox, first follow the `netbox-docker
|
||||||
quickstart <https://github.com/netbox-community/netbox-docker#quickstart>`__
|
quickstart <https://github.com/netbox-community/netbox-docker#quickstart>`__
|
||||||
instructions to clone the netbox-docker repo and set up the
|
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
|
1. Add ``netbox_slm`` to the ``PLUGINS`` list in
|
||||||
``configuration/extra.py``.
|
``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.
|
contents.
|
||||||
3. Create a ``Dockerfile-SLM`` with contents:
|
3. Create a ``Dockerfile-SLM`` with contents:
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -5,7 +5,7 @@ class SLMConfig(PluginConfig):
|
|||||||
name = 'netbox_slm'
|
name = 'netbox_slm'
|
||||||
verbose_name = 'Software Lifecycle Management'
|
verbose_name = 'Software Lifecycle Management'
|
||||||
description = 'Software Lifecycle Management'
|
description = 'Software Lifecycle Management'
|
||||||
version = '0.98'
|
version = '0.99'
|
||||||
author = 'Hedde van der Heide'
|
author = 'Hedde van der Heide'
|
||||||
author_email = 'hedde.vanderheide@ictu.nl'
|
author_email = 'hedde.vanderheide@ictu.nl'
|
||||||
base_url = 'slm'
|
base_url = 'slm'
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
from rest_framework import serializers
|
from netbox.api.viewsets import NetBoxModelViewSet
|
||||||
from rest_framework.decorators import action
|
|
||||||
from rest_framework.response import Response
|
|
||||||
from rest_framework.routers import APIRootView
|
from rest_framework.routers import APIRootView
|
||||||
|
|
||||||
from netbox.api.viewsets import NetBoxModelViewSet
|
|
||||||
from netbox_slm.api.serializers import (
|
from netbox_slm.api.serializers import (
|
||||||
SoftwareProductSerializer, SoftwareProductVersionSerializer, SoftwareProductInstallationSerializer,
|
SoftwareProductSerializer, SoftwareProductVersionSerializer, SoftwareProductInstallationSerializer,
|
||||||
)
|
)
|
||||||
from netbox_slm.filters import (
|
from netbox_slm.filtersets import (
|
||||||
SoftwareProductFilter, SoftwareProductVersionFilter, SoftwareProductInstallationFilter,
|
SoftwareProductFilterSet, SoftwareProductVersionFilterSet, SoftwareProductInstallationFilterSet,
|
||||||
)
|
)
|
||||||
from netbox_slm.models import (
|
from netbox_slm.models import (
|
||||||
SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation,
|
SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation,
|
||||||
@@ -27,16 +24,16 @@ class NetboxSLMRootView(APIRootView):
|
|||||||
class SoftwareProductViewSet(NetBoxModelViewSet):
|
class SoftwareProductViewSet(NetBoxModelViewSet):
|
||||||
queryset = SoftwareProduct.objects.all()
|
queryset = SoftwareProduct.objects.all()
|
||||||
serializer_class = SoftwareProductSerializer
|
serializer_class = SoftwareProductSerializer
|
||||||
filterset_class = SoftwareProductFilter
|
filterset_class = SoftwareProductFilterSet
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductVersionViewSet(NetBoxModelViewSet):
|
class SoftwareProductVersionViewSet(NetBoxModelViewSet):
|
||||||
queryset = SoftwareProductVersion.objects.all()
|
queryset = SoftwareProductVersion.objects.all()
|
||||||
serializer_class = SoftwareProductVersionSerializer
|
serializer_class = SoftwareProductVersionSerializer
|
||||||
filterset_class = SoftwareProductVersionFilter
|
filterset_class = SoftwareProductVersionFilterSet
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductInstallationViewSet(NetBoxModelViewSet):
|
class SoftwareProductInstallationViewSet(NetBoxModelViewSet):
|
||||||
queryset = SoftwareProductInstallation.objects.all()
|
queryset = SoftwareProductInstallation.objects.all()
|
||||||
serializer_class = SoftwareProductInstallationSerializer
|
serializer_class = SoftwareProductInstallationSerializer
|
||||||
filterset_class = SoftwareProductInstallationFilter
|
filterset_class = SoftwareProductInstallationFilterSet
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -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)
|
|
||||||
@@ -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()
|
||||||
+48
-39
@@ -1,16 +1,18 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
|
from django.db.models import Q
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
|
from dcim.models import Manufacturer, Device
|
||||||
from netbox.forms import (
|
from netbox.forms import (
|
||||||
NetBoxModelForm,
|
NetBoxModelForm,
|
||||||
NetBoxModelCSVForm,
|
NetBoxModelCSVForm,
|
||||||
NetBoxModelBulkEditForm,
|
NetBoxModelBulkEditForm,
|
||||||
NetBoxModelFilterSetForm,
|
NetBoxModelFilterSetForm,
|
||||||
)
|
)
|
||||||
from dcim.models import Manufacturer, Device
|
|
||||||
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
|
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
BootstrapMixin, DynamicModelChoiceField, APISelect, DynamicModelMultipleChoiceField
|
DynamicModelChoiceField, APISelect, TagFilterField
|
||||||
)
|
)
|
||||||
from virtualization.models import VirtualMachine
|
from virtualization.models import VirtualMachine
|
||||||
|
|
||||||
@@ -21,9 +23,6 @@ class SoftwareProductForm(NetBoxModelForm):
|
|||||||
manufacturer = DynamicModelChoiceField(
|
manufacturer = DynamicModelChoiceField(
|
||||||
queryset=Manufacturer.objects.all(),
|
queryset=Manufacturer.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
# initial_params={
|
|
||||||
# 'device_types': 'device_type'
|
|
||||||
# }
|
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -32,24 +31,26 @@ class SoftwareProductForm(NetBoxModelForm):
|
|||||||
|
|
||||||
|
|
||||||
class SoftwareProductFilterForm(NetBoxModelFilterSetForm):
|
class SoftwareProductFilterForm(NetBoxModelFilterSetForm):
|
||||||
"""Form for filtering SoftwareProduct instances."""
|
|
||||||
|
|
||||||
model = SoftwareProduct
|
model = SoftwareProduct
|
||||||
|
fieldsets = (
|
||||||
q = forms.CharField(required=False, label="Search")
|
(None, ('q', 'tag')),
|
||||||
|
|
||||||
name = forms.CharField(
|
|
||||||
required=False,
|
|
||||||
label="Name",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# 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 SoftwareProductCSVForm(NetBoxModelCSVForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = SoftwareProduct
|
model = SoftwareProduct
|
||||||
fields = ("name",)
|
fields = ("name", "manufacturer",)
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductBulkEditForm(NetBoxModelBulkEditForm):
|
class SoftwareProductBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
@@ -78,24 +79,23 @@ class SoftwareProductVersionForm(NetBoxModelForm):
|
|||||||
model = SoftwareProductVersion
|
model = SoftwareProductVersion
|
||||||
fields = ("name", "software_product", "tags")
|
fields = ("name", "software_product", "tags")
|
||||||
|
|
||||||
# def clean(self):
|
|
||||||
# import pdb;pdb.set_trace()
|
|
||||||
# return super(SoftwareProductVersionForm, self).clean()
|
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductVersionFilterForm(NetBoxModelFilterSetForm):
|
class SoftwareProductVersionFilterForm(NetBoxModelFilterSetForm):
|
||||||
"""Form for filtering SoftwareProductVersion instances."""
|
|
||||||
|
|
||||||
model = SoftwareProductVersion
|
model = SoftwareProductVersion
|
||||||
|
fieldsets = (
|
||||||
q = forms.CharField(required=False, label="Search")
|
(None, ('q', 'tag')),
|
||||||
|
|
||||||
name = forms.CharField(
|
|
||||||
required=False,
|
|
||||||
label="Name",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# 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):
|
class SoftwareProductVersionCSVForm(NetBoxModelCSVForm):
|
||||||
@@ -121,16 +121,10 @@ class SoftwareProductInstallationForm(NetBoxModelForm):
|
|||||||
device = DynamicModelChoiceField(
|
device = DynamicModelChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
# initial_params={
|
|
||||||
# 'device_types': 'device_type'
|
|
||||||
# }
|
|
||||||
)
|
)
|
||||||
virtualmachine = DynamicModelChoiceField(
|
virtualmachine = DynamicModelChoiceField(
|
||||||
queryset=VirtualMachine.objects.all(),
|
queryset=VirtualMachine.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
# initial_params={
|
|
||||||
# 'device_types': 'device_type'
|
|
||||||
# }
|
|
||||||
)
|
)
|
||||||
software_product = DynamicModelChoiceField(
|
software_product = DynamicModelChoiceField(
|
||||||
queryset=SoftwareProduct.objects.all(),
|
queryset=SoftwareProduct.objects.all(),
|
||||||
@@ -154,6 +148,14 @@ class SoftwareProductInstallationForm(NetBoxModelForm):
|
|||||||
model = SoftwareProductInstallation
|
model = SoftwareProductInstallation
|
||||||
fields = ("device", "virtualmachine", "software_product", "version", "tags")
|
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):
|
def clean(self):
|
||||||
if not any([self.cleaned_data['device'], self.cleaned_data['virtualmachine']]):
|
if not any([self.cleaned_data['device'], self.cleaned_data['virtualmachine']]):
|
||||||
raise forms.ValidationError(_("Installation requires atleast one virtualmachine or device destination."))
|
raise forms.ValidationError(_("Installation requires atleast one virtualmachine or device destination."))
|
||||||
@@ -161,13 +163,21 @@ class SoftwareProductInstallationForm(NetBoxModelForm):
|
|||||||
|
|
||||||
|
|
||||||
class SoftwareProductInstallationFilterForm(NetBoxModelFilterSetForm):
|
class SoftwareProductInstallationFilterForm(NetBoxModelFilterSetForm):
|
||||||
"""Form for filtering SoftwareProductInstallation instances."""
|
|
||||||
|
|
||||||
model = SoftwareProductInstallation
|
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):
|
class SoftwareProductInstallationCSVForm(NetBoxModelCSVForm):
|
||||||
@@ -189,4 +199,3 @@ class SoftwareProductInstallationBulkEditForm(NetBoxModelBulkEditForm):
|
|||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, ('software_product', 'version',)),
|
(None, ('software_product', 'version',)),
|
||||||
)
|
)
|
||||||
# nullable_fields = ('',)
|
|
||||||
@@ -1,16 +1,12 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
from django.utils import safestring
|
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 netbox.models import NetBoxModel
|
||||||
from utilities.querysets import RestrictedQuerySet
|
from utilities.querysets import RestrictedQuerySet
|
||||||
|
|
||||||
|
|
||||||
# @extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks')
|
|
||||||
class SoftwareProduct(NetBoxModel):
|
class SoftwareProduct(NetBoxModel):
|
||||||
"""
|
|
||||||
A SoftwareProduct represents ...
|
|
||||||
"""
|
|
||||||
name = models.CharField(max_length=128)
|
name = models.CharField(max_length=128)
|
||||||
description = models.CharField(max_length=255, null=True, blank=True)
|
description = models.CharField(max_length=255, null=True, blank=True)
|
||||||
|
|
||||||
@@ -36,7 +32,6 @@ class SoftwareProduct(NetBoxModel):
|
|||||||
)) if count else "0"
|
)) if count else "0"
|
||||||
|
|
||||||
|
|
||||||
# @extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks')
|
|
||||||
class SoftwareProductVersion(NetBoxModel):
|
class SoftwareProductVersion(NetBoxModel):
|
||||||
software_product = models.ForeignKey(
|
software_product = models.ForeignKey(
|
||||||
to='netbox_slm.SoftwareProduct',
|
to='netbox_slm.SoftwareProduct',
|
||||||
@@ -60,7 +55,6 @@ class SoftwareProductVersion(NetBoxModel):
|
|||||||
)) if count else "0"
|
)) if count else "0"
|
||||||
|
|
||||||
|
|
||||||
# @extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks')
|
|
||||||
class SoftwareProductInstallation(NetBoxModel):
|
class SoftwareProductInstallation(NetBoxModel):
|
||||||
device = models.ForeignKey(
|
device = models.ForeignKey(
|
||||||
to='dcim.Device',
|
to='dcim.Device',
|
||||||
@@ -71,8 +65,8 @@ class SoftwareProductInstallation(NetBoxModel):
|
|||||||
virtualmachine = models.ForeignKey(
|
virtualmachine = models.ForeignKey(
|
||||||
to='virtualization.VirtualMachine',
|
to='virtualization.VirtualMachine',
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
null = True,
|
null=True,
|
||||||
blank = True
|
blank=True
|
||||||
)
|
)
|
||||||
software_product = models.ForeignKey(
|
software_product = models.ForeignKey(
|
||||||
to='netbox_slm.SoftwareProduct',
|
to='netbox_slm.SoftwareProduct',
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from extras.plugins import PluginMenuButton, PluginMenuItem
|
from extras.plugins import PluginMenuButton, PluginMenuItem
|
||||||
from utilities.choices import ButtonColorChoices
|
from utilities.choices import ButtonColorChoices
|
||||||
|
|
||||||
|
|
||||||
menu_items = (
|
menu_items = (
|
||||||
PluginMenuItem(
|
PluginMenuItem(
|
||||||
link='plugins:netbox_slm:softwareproduct_list',
|
link='plugins:netbox_slm:softwareproduct_list',
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
from netbox.views.generic import ObjectChangeLogView
|
from netbox.views.generic import ObjectChangeLogView
|
||||||
|
|
||||||
from netbox_slm import views
|
from netbox_slm import views
|
||||||
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
|
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
|
||||||
|
|
||||||
|
|||||||
+34
-45
@@ -1,25 +1,19 @@
|
|||||||
from netbox.views import generic
|
from netbox.views import generic
|
||||||
from netbox_slm.filters import SoftwareProductFilter, SoftwareProductVersionFilter, SoftwareProductInstallationFilter
|
from netbox_slm import filtersets
|
||||||
from netbox_slm.forms import (
|
from netbox_slm import forms
|
||||||
SoftwareProductForm, SoftwareProductFilterForm, SoftwareProductCSVForm, SoftwareProductBulkEditForm,
|
from netbox_slm import tables
|
||||||
SoftwareProductVersionForm, SoftwareProductVersionFilterForm, SoftwareProductVersionCSVForm,
|
|
||||||
SoftwareProductVersionBulkEditForm, SoftwareProductInstallationForm, SoftwareProductInstallationFilterForm,
|
|
||||||
SoftwareProductInstallationCSVForm, SoftwareProductInstallationBulkEditForm
|
|
||||||
)
|
|
||||||
from netbox_slm.models import (
|
from netbox_slm.models import (
|
||||||
SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
|
SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
|
||||||
)
|
)
|
||||||
from netbox_slm.tables import SoftwareProductTable, SoftwareProductVersionTable, SoftwareProductInstallationTable
|
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductListView(generic.ObjectListView):
|
class SoftwareProductListView(generic.ObjectListView):
|
||||||
"""View for listing all existing SoftwareProducts."""
|
"""View for listing all existing SoftwareProducts."""
|
||||||
|
|
||||||
queryset = SoftwareProduct.objects.all()
|
queryset = SoftwareProduct.objects.all()
|
||||||
filterset = SoftwareProductFilter
|
filterset = filtersets.SoftwareProductFilterSet
|
||||||
filterset_form = SoftwareProductFilterForm
|
filterset_form = forms.SoftwareProductFilterForm
|
||||||
table = SoftwareProductTable
|
table = tables.SoftwareProductTable
|
||||||
# template_name = "netbox_slm/object_list.html"
|
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductView(generic.ObjectView):
|
class SoftwareProductView(generic.ObjectView):
|
||||||
@@ -36,7 +30,7 @@ class SoftwareProductEditView(generic.ObjectEditView):
|
|||||||
"""View for editing and creating a SoftwareProduct instance."""
|
"""View for editing and creating a SoftwareProduct instance."""
|
||||||
|
|
||||||
queryset = SoftwareProduct.objects.all()
|
queryset = SoftwareProduct.objects.all()
|
||||||
form = SoftwareProductForm
|
form = forms.SoftwareProductForm
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductDeleteView(generic.ObjectDeleteView):
|
class SoftwareProductDeleteView(generic.ObjectDeleteView):
|
||||||
@@ -47,30 +41,30 @@ class SoftwareProductDeleteView(generic.ObjectDeleteView):
|
|||||||
|
|
||||||
class SoftwareProductBulkImportView(generic.BulkImportView):
|
class SoftwareProductBulkImportView(generic.BulkImportView):
|
||||||
queryset = SoftwareProduct.objects.all()
|
queryset = SoftwareProduct.objects.all()
|
||||||
model_form = SoftwareProductCSVForm
|
model_form = forms.SoftwareProductCSVForm
|
||||||
table = SoftwareProductTable
|
table = tables.SoftwareProductTable
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductBulkEditView(generic.BulkEditView):
|
class SoftwareProductBulkEditView(generic.BulkEditView):
|
||||||
queryset = SoftwareProduct.objects.all()
|
queryset = SoftwareProduct.objects.all()
|
||||||
filterset = SoftwareProductFilter
|
filterset = filtersets.SoftwareProductFilterSet
|
||||||
table = SoftwareProductTable
|
filterset_form = forms.SoftwareProductFilterForm
|
||||||
form = SoftwareProductBulkEditForm
|
table = tables.SoftwareProductTable
|
||||||
|
form = forms.SoftwareProductBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductBulkDeleteView(generic.BulkDeleteView):
|
class SoftwareProductBulkDeleteView(generic.BulkDeleteView):
|
||||||
queryset = SoftwareProduct.objects.all()
|
queryset = SoftwareProduct.objects.all()
|
||||||
table = SoftwareProductTable
|
table = tables.SoftwareProductTable
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductVersionListView(generic.ObjectListView):
|
class SoftwareProductVersionListView(generic.ObjectListView):
|
||||||
"""View for listing all existing SoftwareProductVersions."""
|
"""View for listing all existing SoftwareProductVersions."""
|
||||||
|
|
||||||
queryset = SoftwareProductVersion.objects.all()
|
queryset = SoftwareProductVersion.objects.all()
|
||||||
filterset = SoftwareProductVersionFilter
|
filterset = filtersets.SoftwareProductVersionFilterSet
|
||||||
filterset_form = SoftwareProductVersionFilterForm
|
filterset_form = forms.SoftwareProductVersionFilterForm
|
||||||
table = SoftwareProductVersionTable
|
table = tables.SoftwareProductVersionTable
|
||||||
# template_name = "netbox_slm/object_list.html"
|
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductVersionView(generic.ObjectView):
|
class SoftwareProductVersionView(generic.ObjectView):
|
||||||
@@ -87,7 +81,7 @@ class SoftwareProductVersionEditView(generic.ObjectEditView):
|
|||||||
"""View for editing and creating a SoftwareProductVersion instance."""
|
"""View for editing and creating a SoftwareProductVersion instance."""
|
||||||
|
|
||||||
queryset = SoftwareProductVersion.objects.all()
|
queryset = SoftwareProductVersion.objects.all()
|
||||||
form = SoftwareProductVersionForm
|
form = forms.SoftwareProductVersionForm
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductVersionDeleteView(generic.ObjectDeleteView):
|
class SoftwareProductVersionDeleteView(generic.ObjectDeleteView):
|
||||||
@@ -98,30 +92,29 @@ class SoftwareProductVersionDeleteView(generic.ObjectDeleteView):
|
|||||||
|
|
||||||
class SoftwareProductVersionBulkImportView(generic.BulkImportView):
|
class SoftwareProductVersionBulkImportView(generic.BulkImportView):
|
||||||
queryset = SoftwareProductVersion.objects.all()
|
queryset = SoftwareProductVersion.objects.all()
|
||||||
model_form = SoftwareProductVersionCSVForm
|
model_form = forms.SoftwareProductVersionCSVForm
|
||||||
table = SoftwareProductVersionTable
|
table = tables.SoftwareProductVersionTable
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductVersionBulkEditView(generic.BulkEditView):
|
class SoftwareProductVersionBulkEditView(generic.BulkEditView):
|
||||||
queryset = SoftwareProductVersion.objects.all()
|
queryset = SoftwareProductVersion.objects.all()
|
||||||
filterset = SoftwareProductVersionFilter
|
filterset = filtersets.SoftwareProductVersionFilterSet
|
||||||
table = SoftwareProductVersionTable
|
table = tables.SoftwareProductVersionTable
|
||||||
form = SoftwareProductVersionBulkEditForm
|
form = forms.SoftwareProductVersionBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductVersionBulkDeleteView(generic.BulkDeleteView):
|
class SoftwareProductVersionBulkDeleteView(generic.BulkDeleteView):
|
||||||
queryset = SoftwareProductVersion.objects.all()
|
queryset = SoftwareProductVersion.objects.all()
|
||||||
table = SoftwareProductVersionTable
|
table = tables.SoftwareProductVersionTable
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductInstallationListView(generic.ObjectListView):
|
class SoftwareProductInstallationListView(generic.ObjectListView):
|
||||||
"""View for listing all existing SoftwareProductInstallations."""
|
"""View for listing all existing SoftwareProductInstallations."""
|
||||||
|
|
||||||
queryset = SoftwareProductInstallation.objects.all()
|
queryset = SoftwareProductInstallation.objects.all()
|
||||||
filterset = SoftwareProductInstallationFilter
|
filterset = filtersets.SoftwareProductInstallationFilterSet
|
||||||
filterset_form = SoftwareProductInstallationFilterForm
|
filterset_form = forms.SoftwareProductInstallationFilterForm
|
||||||
table = SoftwareProductInstallationTable
|
table = tables.SoftwareProductInstallationTable
|
||||||
# template_name = "netbox_slm/object_list.html"
|
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductInstallationView(generic.ObjectView):
|
class SoftwareProductInstallationView(generic.ObjectView):
|
||||||
@@ -129,16 +122,12 @@ class SoftwareProductInstallationView(generic.ObjectView):
|
|||||||
|
|
||||||
queryset = SoftwareProductInstallation.objects.all()
|
queryset = SoftwareProductInstallation.objects.all()
|
||||||
|
|
||||||
# def get_extra_context(self, request, instance):
|
|
||||||
# records = instance.record_set.all()
|
|
||||||
# return {"records": records}
|
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductInstallationEditView(generic.ObjectEditView):
|
class SoftwareProductInstallationEditView(generic.ObjectEditView):
|
||||||
"""View for editing and creating a SoftwareProductInstallation instance."""
|
"""View for editing and creating a SoftwareProductInstallation instance."""
|
||||||
|
|
||||||
queryset = SoftwareProductInstallation.objects.all()
|
queryset = SoftwareProductInstallation.objects.all()
|
||||||
form = SoftwareProductInstallationForm
|
form = forms.SoftwareProductInstallationForm
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductInstallationDeleteView(generic.ObjectDeleteView):
|
class SoftwareProductInstallationDeleteView(generic.ObjectDeleteView):
|
||||||
@@ -149,17 +138,17 @@ class SoftwareProductInstallationDeleteView(generic.ObjectDeleteView):
|
|||||||
|
|
||||||
class SoftwareProductInstallationBulkImportView(generic.BulkImportView):
|
class SoftwareProductInstallationBulkImportView(generic.BulkImportView):
|
||||||
queryset = SoftwareProductInstallation.objects.all()
|
queryset = SoftwareProductInstallation.objects.all()
|
||||||
model_form = SoftwareProductInstallationCSVForm
|
model_form = forms.SoftwareProductInstallationCSVForm
|
||||||
table = SoftwareProductInstallationTable
|
table = tables.SoftwareProductInstallationTable
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductInstallationBulkEditView(generic.BulkEditView):
|
class SoftwareProductInstallationBulkEditView(generic.BulkEditView):
|
||||||
queryset = SoftwareProductInstallation.objects.all()
|
queryset = SoftwareProductInstallation.objects.all()
|
||||||
filterset = SoftwareProductInstallationFilter
|
filterset = filtersets.SoftwareProductInstallationFilterSet
|
||||||
table = SoftwareProductInstallationTable
|
table = tables.SoftwareProductInstallationTable
|
||||||
form = SoftwareProductInstallationBulkEditForm
|
form = forms.SoftwareProductInstallationBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductInstallationBulkDeleteView(generic.BulkDeleteView):
|
class SoftwareProductInstallationBulkDeleteView(generic.BulkDeleteView):
|
||||||
queryset = SoftwareProductInstallation.objects.all()
|
queryset = SoftwareProductInstallation.objects.all()
|
||||||
table = SoftwareProductInstallationTable
|
table = tables.SoftwareProductInstallationTable
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
name = netbox-slm
|
name = netbox-slm
|
||||||
version = 0.98
|
version = 0.99
|
||||||
description = Software Lifecycle Management Netbox Plugin.
|
description = Software Lifecycle Management Netbox Plugin.
|
||||||
# long_description = file: README.rst
|
# long_description = file: README.rst
|
||||||
# long_description_content_type='text/x-rst'
|
# long_description_content_type='text/x-rst'
|
||||||
|
|||||||
Reference in New Issue
Block a user