Add new fields to models and upgrade to NetBox v3.7.2 (#36)

Closes #10, #26, #35
This commit is contained in:
wkoot
2024-02-13 12:11:49 +01:00
parent 1411e3a5cb
commit 230b3b5115
23 changed files with 471 additions and 104 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
from extras.plugins import PluginConfig
__version__ = "1.5"
__version__ = "1.6.0"
class SLMConfig(PluginConfig):
+48 -32
View File
@@ -4,6 +4,39 @@ from netbox.api.serializers import NetBoxModelSerializer
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation, SoftwareLicense
class SoftwareLicenseSerializer(NetBoxModelSerializer):
display = serializers.SerializerMethodField()
url = serializers.HyperlinkedIdentityField(view_name="plugins-api:netbox_slm-api:softwarelicense-detail")
class Meta:
model = SoftwareLicense
fields = [
"id",
"display",
"url",
"name",
"description",
"type",
"stored_location",
"stored_location_url",
"start_date",
"expiration_date",
"support",
"license_amount",
"software_product",
"version",
"installation",
"tags",
"comments",
"custom_field_data",
"created",
"last_updated",
]
def get_display(self, obj):
return f"{obj}"
class SoftwareProductSerializer(NetBoxModelSerializer):
display = serializers.SerializerMethodField()
url = serializers.HyperlinkedIdentityField(view_name="plugins-api:netbox_slm-api:softwareproduct-detail")
@@ -15,7 +48,10 @@ class SoftwareProductSerializer(NetBoxModelSerializer):
"display",
"url",
"name",
"manufacturer",
"description",
"tags",
"comments",
"custom_field_data",
"created",
"last_updated",
@@ -25,28 +61,6 @@ class SoftwareProductSerializer(NetBoxModelSerializer):
return f"{obj.manufacturer} - {obj}"
class SoftwareProductVersionSerializer(NetBoxModelSerializer):
display = serializers.SerializerMethodField()
url = serializers.HyperlinkedIdentityField(view_name="plugins-api:netbox_slm-api:softwareproductversion-detail")
class Meta:
model = SoftwareProductVersion
fields = [
"id",
"display",
"url",
"name",
"software_product",
"tags",
"custom_field_data",
"created",
"last_updated",
]
def get_display(self, obj):
return f"{obj}"
class SoftwareProductInstallationSerializer(NetBoxModelSerializer):
display = serializers.SerializerMethodField()
url = serializers.HyperlinkedIdentityField(
@@ -65,6 +79,7 @@ class SoftwareProductInstallationSerializer(NetBoxModelSerializer):
"software_product",
"version",
"tags",
"comments",
"custom_field_data",
"created",
"last_updated",
@@ -74,26 +89,27 @@ class SoftwareProductInstallationSerializer(NetBoxModelSerializer):
return f"{obj}"
class SoftwareLicenseSerializer(NetBoxModelSerializer):
class SoftwareProductVersionSerializer(NetBoxModelSerializer):
display = serializers.SerializerMethodField()
url = serializers.HyperlinkedIdentityField(view_name="plugins-api:netbox_slm-api:softwarelicense-detail")
url = serializers.HyperlinkedIdentityField(view_name="plugins-api:netbox_slm-api:softwareproductversion-detail")
class Meta:
model = SoftwareLicense
model = SoftwareProductVersion
fields = [
"id",
"display",
"url",
"name",
"description",
"type",
"stored_location",
"start_date",
"expiration_date",
"release_date",
"documentation_url",
"end_of_support",
"filename",
"file_checksum",
"file_link",
"release_type",
"software_product",
"version",
"installation",
"tags",
"comments",
"custom_field_data",
"created",
"last_updated",
+9 -1
View File
@@ -15,7 +15,12 @@ class SoftwareProductFilterSet(NetBoxModelFilterSet):
"""Perform the filtered search."""
if not value.strip():
return queryset
qs_filter = Q(name__icontains=value) | Q(manufacturer__name__icontains=value)
qs_filter = (
Q(name__icontains=value)
| Q(description__icontains=value)
| Q(manufacturer__name__icontains=value)
| Q(comments__icontains=value)
)
return queryset.filter(qs_filter)
@@ -34,6 +39,7 @@ class SoftwareProductVersionFilterSet(NetBoxModelFilterSet):
Q(name__icontains=value)
| Q(software_product__name__icontains=value)
| Q(software_product__manufacturer__name__icontains=value)
| Q(comments__icontains=value)
)
return queryset.filter(qs_filter)
@@ -53,6 +59,7 @@ class SoftwareProductInstallationFilterSet(NetBoxModelFilterSet):
Q(software_product__name__icontains=value)
| Q(software_product__manufacturer__name__icontains=value)
| Q(version__name__icontains=value)
| Q(comments__icontains=value)
)
return queryset.filter(qs_filter)
@@ -75,5 +82,6 @@ class SoftwareLicenseFilterSet(NetBoxModelFilterSet):
| Q(installation__device__name__icontains=value)
| Q(installation__virtualmachine__name__icontains=value)
| Q(installation__cluster__name__icontains=value)
| Q(comments__icontains=value)
)
return queryset.filter(qs_filter)
+9 -14
View File
@@ -1,16 +1,17 @@
from django.forms import DateField
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
from netbox.forms import NetBoxModelForm, NetBoxModelImportForm, NetBoxModelBulkEditForm, NetBoxModelFilterSetForm
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation, SoftwareLicense
from utilities.forms.fields import DynamicModelChoiceField, TagFilterField, LaxURLField
from utilities.forms.fields import CommentField, DynamicModelChoiceField, TagFilterField, LaxURLField
from utilities.forms.widgets import APISelect, DatePicker
class SoftwareLicenseForm(NetBoxModelForm):
"""Form for creating a new SoftwareLicense object."""
comments = CommentField()
stored_location_url = LaxURLField(required=False)
software_product = DynamicModelChoiceField(
@@ -36,8 +37,8 @@ class SoftwareLicenseForm(NetBoxModelForm):
"software_product": "$software_product",
},
)
start_date = DateField(label=_("Start date"), required=False, widget=DatePicker())
expiration_date = DateField(label=_("Expiration date"), required=False, widget=DatePicker())
start_date = DateField(required=False, widget=DatePicker())
expiration_date = DateField(required=False, widget=DatePicker())
class Meta:
model = SoftwareLicense
@@ -49,25 +50,19 @@ class SoftwareLicenseForm(NetBoxModelForm):
"stored_location_url",
"start_date",
"expiration_date",
"support",
"license_amount",
"software_product",
"version",
"installation",
"tags",
"comments",
)
class SoftwareLicenseFilterForm(NetBoxModelFilterSetForm):
model = SoftwareLicense
fieldsets = (
(
None,
(
"q",
"tag",
),
),
)
fieldsets = ((None, ("q", "tag")),)
tag = TagFilterField(model)
+11 -3
View File
@@ -3,12 +3,14 @@ from django import forms
from dcim.models import Manufacturer
from netbox.forms import NetBoxModelForm, NetBoxModelImportForm, NetBoxModelBulkEditForm, NetBoxModelFilterSetForm
from netbox_slm.models import SoftwareProduct
from utilities.forms.fields import DynamicModelChoiceField, TagFilterField
from utilities.forms.fields import CommentField, DynamicModelChoiceField, TagFilterField
class SoftwareProductForm(NetBoxModelForm):
"""Form for creating a new SoftwareProduct object."""
comments = CommentField()
manufacturer = DynamicModelChoiceField(
queryset=Manufacturer.objects.all(),
required=True,
@@ -16,13 +18,18 @@ class SoftwareProductForm(NetBoxModelForm):
class Meta:
model = SoftwareProduct
fields = ("name", "manufacturer", "description", "tags")
fields = (
"name",
"description",
"manufacturer",
"tags",
"comments",
)
class SoftwareProductFilterForm(NetBoxModelFilterSetForm):
model = SoftwareProduct
fieldsets = ((None, ("q", "tag")),)
tag = TagFilterField(model)
@@ -31,6 +38,7 @@ class SoftwareProductImportForm(NetBoxModelImportForm):
model = SoftwareProduct
fields = (
"name",
"description",
"manufacturer",
)
@@ -1,11 +1,10 @@
from django import forms
from django.urls import reverse_lazy
from django.utils.translation import gettext as _
from dcim.models import Device
from netbox.forms import NetBoxModelForm, NetBoxModelImportForm, NetBoxModelBulkEditForm, NetBoxModelFilterSetForm
from netbox_slm.models import SoftwareProductInstallation, SoftwareProduct, SoftwareProductVersion
from utilities.forms.fields import DynamicModelChoiceField, TagFilterField
from utilities.forms.fields import CommentField, DynamicModelChoiceField, TagFilterField
from utilities.forms.widgets import APISelect
from virtualization.models import VirtualMachine, Cluster
@@ -13,6 +12,8 @@ from virtualization.models import VirtualMachine, Cluster
class SoftwareProductInstallationForm(NetBoxModelForm):
"""Form for creating a new SoftwareProductInstallation object."""
comments = CommentField()
device = DynamicModelChoiceField(queryset=Device.objects.all(), required=False)
virtualmachine = DynamicModelChoiceField(queryset=VirtualMachine.objects.all(), required=False)
cluster = DynamicModelChoiceField(queryset=Cluster.objects.all(), required=False)
@@ -32,33 +33,30 @@ class SoftwareProductInstallationForm(NetBoxModelForm):
class Meta:
model = SoftwareProductInstallation
fields = ("device", "virtualmachine", "cluster", "software_product", "version", "tags")
fields = (
"device",
"virtualmachine",
"cluster",
"software_product",
"version",
"tags",
"comments",
)
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."
)
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
class SoftwareProductInstallationFilterForm(NetBoxModelFilterSetForm):
model = SoftwareProductInstallation
fieldsets = (
(
None,
(
"q",
"tag",
),
),
)
fieldsets = ((None, ("q", "tag")),)
tag = TagFilterField(model)
+19 -6
View File
@@ -1,17 +1,19 @@
from django import forms
from django.urls import reverse_lazy
from django.utils.translation import gettext as _
from netbox.forms import NetBoxModelForm, NetBoxModelImportForm, NetBoxModelBulkEditForm, NetBoxModelFilterSetForm
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion
from utilities.forms.fields import DynamicModelChoiceField, TagFilterField
from utilities.forms.widgets import APISelect
from utilities.forms.fields import CommentField, DynamicModelChoiceField, TagFilterField
from utilities.forms.widgets import APISelect, DatePicker
class SoftwareProductVersionForm(NetBoxModelForm):
"""Form for creating a new SoftwareProductVersion object."""
name = forms.CharField(label=_("Version"))
comments = CommentField()
release_date = forms.DateField(required=False, widget=DatePicker())
end_of_support = forms.DateField(required=False, widget=DatePicker())
software_product = DynamicModelChoiceField(
queryset=SoftwareProduct.objects.all(),
@@ -20,13 +22,24 @@ class SoftwareProductVersionForm(NetBoxModelForm):
class Meta:
model = SoftwareProductVersion
fields = ("name", "software_product", "tags")
fields = (
"name",
"release_date",
"documentation_url",
"end_of_support",
"filename",
"file_checksum",
"file_link",
"release_type",
"software_product",
"tags",
"comments",
)
class SoftwareProductVersionFilterForm(NetBoxModelFilterSetForm):
model = SoftwareProductVersion
fieldsets = ((None, ("q", "tag")),)
tag = TagFilterField(model)
@@ -0,0 +1,79 @@
# Generated by Django 4.2.9 on 2024-02-07 21:01
from django.db import migrations, models
import netbox_slm.models
class Migration(migrations.Migration):
dependencies = [
('netbox_slm', '0007_softwareproductinstallation_cluster'),
]
operations = [
migrations.AddField(
model_name='softwarelicense',
name='comments',
field=models.TextField(blank=True),
),
migrations.AddField(
model_name='softwarelicense',
name='license_amount',
field=models.PositiveIntegerField(blank=True, default=None, null=True),
),
migrations.AddField(
model_name='softwarelicense',
name='support',
field=models.BooleanField(blank=True, default=None, null=True),
),
migrations.AddField(
model_name='softwareproduct',
name='comments',
field=models.TextField(blank=True),
),
migrations.AddField(
model_name='softwareproductinstallation',
name='comments',
field=models.TextField(blank=True),
),
migrations.AddField(
model_name='softwareproductversion',
name='comments',
field=models.TextField(blank=True),
),
migrations.AddField(
model_name='softwareproductversion',
name='documentation_url',
field=netbox_slm.models.LaxURLField(blank=True, max_length=1024, null=True),
),
migrations.AddField(
model_name='softwareproductversion',
name='end_of_support',
field=models.DateField(blank=True, null=True),
),
migrations.AddField(
model_name='softwareproductversion',
name='file_checksum',
field=models.CharField(blank=True, max_length=128, null=True),
),
migrations.AddField(
model_name='softwareproductversion',
name='file_link',
field=netbox_slm.models.LaxURLField(blank=True, max_length=1024, null=True),
),
migrations.AddField(
model_name='softwareproductversion',
name='filename',
field=models.CharField(blank=True, max_length=64, null=True),
),
migrations.AddField(
model_name='softwareproductversion',
name='release_date',
field=models.DateField(blank=True, null=True),
),
migrations.AddField(
model_name='softwareproductversion',
name='release_type',
field=models.CharField(default='S', max_length=3),
),
]
+29 -2
View File
@@ -18,8 +18,9 @@ class LaxURLField(models.URLField):
class SoftwareProduct(NetBoxModel):
name = models.CharField(max_length=128)
description = models.CharField(max_length=255, null=True, blank=True)
comments = models.TextField(blank=True)
description = models.CharField(max_length=255, null=True, blank=True)
manufacturer = models.ForeignKey(to="dcim.Manufacturer", on_delete=models.PROTECT, null=True, blank=True)
objects = RestrictedQuerySet.as_manager()
@@ -44,8 +45,29 @@ class SoftwareProduct(NetBoxModel):
)
class SoftwareReleaseTypes(models.TextChoices):
ALPHA = "A", "Alpha"
BETA = "B", "Beta"
RELEASE_CANDIDATE = "RC", "Release candidate"
STABLE = "S", "Stable release"
class SoftwareProductVersion(NetBoxModel):
name = models.CharField(max_length=64)
comments = models.TextField(blank=True)
release_date = models.DateField(null=True, blank=True)
documentation_url = LaxURLField(max_length=1024, null=True, blank=True)
end_of_support = models.DateField(null=True, blank=True)
filename = models.CharField(max_length=64, null=True, blank=True)
file_checksum = models.CharField(max_length=128, null=True, blank=True)
file_link = LaxURLField(max_length=1024, null=True, blank=True)
release_type = models.CharField(
max_length=3,
choices=SoftwareReleaseTypes.choices,
default=SoftwareReleaseTypes.STABLE,
)
software_product = models.ForeignKey(
to="netbox_slm.SoftwareProduct",
@@ -75,6 +97,8 @@ class SoftwareProductVersion(NetBoxModel):
class SoftwareProductInstallation(NetBoxModel):
comments = models.TextField(blank=True)
device = models.ForeignKey(to="dcim.Device", on_delete=models.PROTECT, null=True, blank=True)
virtualmachine = models.ForeignKey(
to="virtualization.VirtualMachine", on_delete=models.PROTECT, null=True, blank=True
@@ -118,13 +142,16 @@ class SoftwareProductInstallation(NetBoxModel):
class SoftwareLicense(NetBoxModel):
name = models.CharField(max_length=128)
comments = models.TextField(blank=True)
description = models.CharField(max_length=255, null=True, blank=True)
type = models.CharField(max_length=128)
stored_location = models.CharField(max_length=255, null=True, blank=True)
stored_location_url = LaxURLField(max_length=1024, null=True, blank=True)
start_date = models.DateField(null=True, blank=True)
expiration_date = models.DateField(null=True, blank=True)
support = models.BooleanField(default=None, null=True, blank=True)
license_amount = models.PositiveIntegerField(default=None, null=True, blank=True)
software_product = models.ForeignKey(to="netbox_slm.SoftwareProduct", on_delete=models.PROTECT)
version = models.ForeignKey(to="netbox_slm.SoftwareProductVersion", on_delete=models.PROTECT, null=True, blank=True)
+9 -3
View File
@@ -11,7 +11,7 @@ class SoftwareProductTable(NetBoxTable):
pk = ToggleColumn()
name = tables.LinkColumn()
manufacturer = tables.Column(accessor="manufacturer", linkify=True)
installations = tables.Column(accessor="get_installation_count", verbose_name="Installations")
installations = tables.Column(accessor="get_installation_count")
tags = columns.TagColumn(url_name="plugins:netbox_slm:softwareproduct_list")
@@ -51,10 +51,10 @@ class SoftwareProductVersionTable(NetBoxTable):
"""Table for displaying SoftwareProductVersion objects."""
pk = ToggleColumn()
name = tables.LinkColumn(verbose_name="Version")
name = tables.LinkColumn()
software_product = tables.Column(accessor="software_product", linkify=True)
manufacturer = tables.Column(accessor="software_product__manufacturer", linkify=True)
installations = tables.Column(accessor="get_installation_count", verbose_name="Installations")
installations = tables.Column(accessor="get_installation_count")
tags = columns.TagColumn(url_name="plugins:netbox_slm:softwareproductversion_list")
@@ -65,6 +65,9 @@ class SoftwareProductVersionTable(NetBoxTable):
"name",
"software_product",
"manufacturer",
"release_date",
"end_of_support",
"release_type",
"installations",
"tags",
)
@@ -73,6 +76,7 @@ class SoftwareProductVersionTable(NetBoxTable):
"name",
"software_product",
"manufacturer",
"release_date",
"installations",
"tags",
)
@@ -172,6 +176,8 @@ class SoftwareLicenseTable(NetBoxTable):
"software_product",
"version",
"installation",
"support",
"license_amount",
"tags",
)
default_columns = (
@@ -49,6 +49,14 @@
<th scope="row">Version</th>
<td>{{ object.version }}</td>
</tr>
<tr>
<th scope="row">Support</th>
<td>{{ object.support }}</td>
</tr>
<tr>
<th scope="row">License amount</th>
<td>{{ object.license_amount }}</td>
</tr>
<tr>
<th scope="row">Installation</th>
<td>{{ object.installation }}</td>
@@ -58,6 +66,7 @@
</div>
{% include 'inc/panels/custom_fields.html' %}
{% include 'inc/panels/tags.html' %}
{% include 'inc/panels/comments.html' %}
{% plugin_left_page object %}
</div>
</div>
@@ -42,6 +42,7 @@
</div>
{% include 'inc/panels/custom_fields.html' %}
{% include 'inc/panels/tags.html' %}
{% include 'inc/panels/comments.html' %}
{% plugin_left_page object %}
</div>
</div>
@@ -42,6 +42,7 @@
</div>
{% include 'inc/panels/custom_fields.html' %}
{% include 'inc/panels/tags.html' %}
{% include 'inc/panels/comments.html' %}
{% plugin_left_page object %}
</div>
</div>
@@ -17,17 +17,48 @@
<th scope="row">Name</th>
<td>{{ object.name }}</td>
</tr>
<tr>
<th scope="row">Release date</th>
<td>{{ object.release_date }}</td>
</tr>
<tr>
<th scope="row">Documentation url</th>
{% if object.documentation_url %}
<td><a href="{{ object.documentation_url }}">{{ object.documentation_url }}</a></td>
{% else %}
<td>{{ object.documentation_url }}</td>
{% endif %}
</tr>
<tr>
<th scope="row">End of support</th>
<td>{{ object.end_of_support }}</td>
</tr>
<tr>
<th scope="row">Filename</th>
{% if object.file_link %}
<td><a href="{{ object.file_link }}">{{ object.filename }}</a></td>
{% else %}
<td>{{ object.filename }}</td>
{% endif %}
</tr>
<tr>
<th scope="row">File checksum</th>
<td>{{ object.file_checksum }}</td>
</tr>
<tr>
<th scope="row">Release type</th>
<td>{{ object.get_release_type_display }}</td>
</tr>
<tr>
<th scope="row">Installations</th>
<td>
{{ object.get_installation_count }}
</td>
<td>{{ object.get_installation_count }}</td>
</tr>
</table>
</div>
</div>
{% include 'inc/panels/custom_fields.html' %}
{% include 'inc/panels/tags.html' %}
{% include 'inc/panels/comments.html' %}
{% plugin_left_page object %}
</div>
</div>