Skip to content

Commit

Permalink
Add Software Notice Model and Views
Browse files Browse the repository at this point in the history
  • Loading branch information
romanukes committed Jan 16, 2025
1 parent 7d003b0 commit 235ac13
Show file tree
Hide file tree
Showing 12 changed files with 547 additions and 0 deletions.
11 changes: 11 additions & 0 deletions nautobot_device_lifecycle_mgmt/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
HardwareLCM,
InventoryItemSoftwareValidationResult,
ProviderLCM,
SoftwareNotice,
ValidatedSoftwareLCM,
VulnerabilityLCM,
)
Expand All @@ -25,6 +26,16 @@ class Meta:
fields = "__all__"


class SoftwareNoticeSerializer(NautobotModelSerializer): # pylint: disable=R0901,too-few-public-methods
"""SoftwareNotice API serializer."""

class Meta:
"""Meta attributes."""

model = SoftwareNotice
fields = "__all__"


class ProviderLCMSerializer(NautobotModelSerializer): # pylint: disable=R0901,too-few-public-methods
"""API serializer."""

Expand Down
2 changes: 2 additions & 0 deletions nautobot_device_lifecycle_mgmt/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@
HardwareLCMView,
InventoryItemSoftwareValidationResultListViewSet,
ProviderLCMView,
SoftwareNoticeView,
ValidatedSoftwareLCMViewSet,
VulnerabilityLCMViewSet,
)

router = routers.DefaultRouter()

router.register("hardware", HardwareLCMView)
router.register("software-notice", SoftwareNoticeView)
router.register("contract", ContractLCMView)
router.register("provider", ProviderLCMView)
router.register("validated-software", ValidatedSoftwareLCMViewSet)
Expand Down
11 changes: 11 additions & 0 deletions nautobot_device_lifecycle_mgmt/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
HardwareLCMFilterSet,
InventoryItemSoftwareValidationResultFilterSet,
ProviderLCMFilterSet,
SoftwareNoticeFilterSet,
ValidatedSoftwareLCMFilterSet,
VulnerabilityLCMFilterSet,
)
Expand All @@ -21,6 +22,7 @@
HardwareLCM,
InventoryItemSoftwareValidationResult,
ProviderLCM,
SoftwareNotice,
ValidatedSoftwareLCM,
VulnerabilityLCM,
)
Expand All @@ -33,6 +35,7 @@
HardwareLCMSerializer,
InventoryItemSoftwareValidationResultSerializer,
ProviderLCMSerializer,
SoftwareNoticeSerializer,
ValidatedSoftwareLCMSerializer,
VulnerabilityLCMSerializer,
)
Expand All @@ -46,6 +49,14 @@ class HardwareLCMView(NautobotModelViewSet):
serializer_class = HardwareLCMSerializer


class SoftwareNoticeView(NautobotModelViewSet):
"""CRUD operations set for the Software Notice Lifecycle Management view."""

queryset = SoftwareNotice.objects.all()
filterset_class = SoftwareNoticeFilterSet
serializer_class = SoftwareNoticeSerializer


class ContractLCMView(NautobotModelViewSet):
"""CRUD operations set for the Contract Lifecycle Management view."""

Expand Down
127 changes: 127 additions & 0 deletions nautobot_device_lifecycle_mgmt/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
HardwareLCM,
InventoryItemSoftwareValidationResult,
ProviderLCM,
SoftwareNotice,
ValidatedSoftwareLCM,
VulnerabilityLCM,
)
Expand Down Expand Up @@ -136,6 +137,132 @@ def _expired_search(self, queryset, name, value): # pylint: disable=unused-argu
return queryset.filter(qs_filter)


class SoftwareNoticeFilterSet(NautobotFilterSet):
"""Filter for SoftwareNotice."""

q = SearchFilter(
filter_predicates={
"software_version__version": {
"lookup_expr": "icontains",
"preprocessor": str.strip,
},
"software_version__alias": {
"lookup_expr": "icontains",
"preprocessor": str.strip,
},
"device_type__model": {
"lookup_expr": "icontains",
"preprocessor": str.strip,
},
"device_type__part_number": {
"lookup_expr": "icontains",
"preprocessor": str.strip,
},
"comments": {
"lookup_expr": "icontains",
"preprocessor": str.strip,
},
"documentation_url": {
"lookup_expr": "icontains",
"preprocessor": str.strip,
},
"end_of_sale": {
"lookup_expr": "icontains",
"preprocessor": str.strip,
},
"end_of_support": {
"lookup_expr": "icontains",
"preprocessor": str.strip,
},
"end_of_sw_releases": {
"lookup_expr": "icontains",
"preprocessor": str.strip,
},
"end_of_security_patches": {
"lookup_expr": "icontains",
"preprocessor": str.strip,
},
}
)
software_version_id = django_filters.ModelMultipleChoiceFilter(
field_name="software_version",
queryset=SoftwareVersion.objects.all(),
label="Software Version",
)
software_version = django_filters.ModelMultipleChoiceFilter(
field_name="software_version", queryset=SoftwareVersion.objects.all(), label="Software Version"
)
software_version_version = django_filters.ModelMultipleChoiceFilter(
field_name="software_version__version",
queryset=SoftwareVersion.objects.all(),
to_field_name="version",
label="Software Version (String)",
)

device_type_id = django_filters.ModelMultipleChoiceFilter(
field_name="device_type",
queryset=DeviceType.objects.all(),
label="Device Type",
)
device_type = django_filters.ModelMultipleChoiceFilter(
field_name="device_type", queryset=DeviceType.objects.all(), label="Device Type"
)
device_type_model = django_filters.ModelMultipleChoiceFilter(
field_name="device_type__model",
queryset=DeviceType.objects.all(),
to_field_name="model",
label="Device Type (Model)",
)

documentation_url = django_filters.CharFilter(
lookup_expr="contains",
)

end_of_support = django_filters.DateFilter()
end_of_support__gte = django_filters.DateFilter(field_name="end_of_support", lookup_expr="gte")
end_of_support__lte = django_filters.DateFilter(field_name="end_of_support", lookup_expr="lte")
end_of_support__isnull = django_filters.BooleanFilter(field_name="end_of_support", lookup_expr="isnull")

end_of_sale = django_filters.DateFilter()
end_of_sale__gte = django_filters.DateFilter(field_name="end_of_sale", lookup_expr="gte")
end_of_sale__lte = django_filters.DateFilter(field_name="end_of_sale", lookup_expr="lte")
end_of_sale__isnull = django_filters.BooleanFilter(field_name="end_of_sale", lookup_expr="isnull")

end_of_security_patches = django_filters.DateFilter()
end_of_security_patches__gte = django_filters.DateFilter(field_name="end_of_security_patches", lookup_expr="gte")
end_of_security_patches__lte = django_filters.DateFilter(field_name="end_of_security_patches", lookup_expr="lte")
end_of_security_patches__isnull = django_filters.BooleanFilter(
field_name="end_of_security_patches", lookup_expr="isnull"
)

end_of_sw_releases = django_filters.DateFilter()
end_of_sw_releases__gte = django_filters.DateFilter(field_name="end_of_sw_releases", lookup_expr="gte")
end_of_sw_releases__lte = django_filters.DateFilter(field_name="end_of_sw_releases", lookup_expr="lte")
end_of_sw_releases__isnull = django_filters.BooleanFilter(field_name="end_of_sw_releases", lookup_expr="isnull")

expired = django_filters.BooleanFilter(method="_expired_search", label="Support Expired")

class Meta:
"""Meta attributes for filter."""

model = SoftwareNotice

fields = "__all__"

def _expired_search(self, queryset, name, value): # pylint: disable=unused-argument
"""Perform the filtered search."""
today = datetime.datetime.today().date()
# End of support dates less than today are expired.
# End of support dates greater than or equal to today are not expired.
# If the end of support date is null, the notice will never be expired.
qs_filter = None
if value:
qs_filter = Q(**{"end_of_support__lt": today})
if not value:
qs_filter = Q(**{"end_of_support__gte": today}) | Q(**{"end_of_support__isnull": True})
return queryset.filter(qs_filter)


class ValidatedSoftwareLCMFilterSet(NautobotFilterSet):
"""Filter for ValidatedSoftwareLCM."""

Expand Down
75 changes: 75 additions & 0 deletions nautobot_device_lifecycle_mgmt/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
HardwareLCM,
InventoryItemSoftwareValidationResult,
ProviderLCM,
SoftwareNotice,
ValidatedSoftwareLCM,
VulnerabilityLCM,
)
Expand Down Expand Up @@ -162,6 +163,80 @@ class HardwareLCMFilterForm(NautobotFilterForm):
documentation_url = forms.CharField(required=False, label="Documentation URL")


class SoftwareNoticeForm(NautobotModelForm):
"""SoftwareNotice Device Lifecycle creation/edit form."""

software_version = DynamicModelChoiceField(queryset=SoftwareVersion.objects.all(), required=True)
device_type = DynamicModelChoiceField(queryset=DeviceType.objects.all(), required=False)

class Meta:
"""Meta attributes for the HardwareLCMForm class."""

model = SoftwareNotice
fields = "__all__"

widgets = {
"end_of_sale": DatePicker(),
"end_of_support": DatePicker(),
"end_of_sw_releases": DatePicker(),
"end_of_security_patches": DatePicker(),
}


class SoftwareNoticeBulkEditForm(NautobotBulkEditForm):
"""SoftwareNotice Device Lifecycle bulk edit form."""

pk = forms.ModelMultipleChoiceField(queryset=SoftwareNotice.objects.all(), widget=forms.MultipleHiddenInput)
end_of_sale = forms.DateField(widget=DatePicker(), required=False)
end_of_support = forms.DateField(widget=DatePicker(), required=False)
end_of_sw_releases = forms.DateField(widget=DatePicker(), required=False)
end_of_security_patches = forms.DateField(widget=DatePicker(), required=False)
documentation_url = forms.URLField(required=False)
comments = forms.CharField(required=False)

class Meta:
"""Meta attributes for the SoftwareNoticeBulkEditForm class."""

nullable_fields = [
"end_of_sale",
"end_of_support",
"end_of_sw_releases",
"end_of_security_patches",
"documentation_url",
"comments",
]


class SoftwareNoticeFilterForm(NautobotFilterForm):
"""Filter form for filtering SoftwareNotice objects."""

model = SoftwareNotice
field_order = [
"q",
"expired",
"software_version",
"device_type",
"end_of_sale",
"end_of_support",
"end_of_sw_releases",
"end_of_security_patches",
"documentation_url",
]
q = forms.CharField(required=False, label="Search")
expired = forms.BooleanField(
required=False,
label="Support Expired",
widget=StaticSelect2(choices=BOOLEAN_WITH_BLANK_CHOICES),
)
software_version = DynamicModelMultipleChoiceField(required=False, queryset=SoftwareVersion.objects.all())
device_type = DynamicModelMultipleChoiceField(required=False, queryset=DeviceType.objects.all())
end_of_sale = NullableDateField(required=False, widget=DatePicker(), label="End of sale")
end_of_support = NullableDateField(required=False, widget=DatePicker(), label="End of support")
end_of_sw_releases = NullableDateField(required=False, widget=DatePicker(), label="End of software releases")
end_of_security_patches = NullableDateField(required=False, widget=DatePicker(), label="End of security patches")
documentation_url = forms.CharField(required=False, label="Documentation URL")


class ValidatedSoftwareLCMForm(NautobotModelForm):
"""ValidatedSoftwareLCM creation/edit form."""

Expand Down
66 changes: 66 additions & 0 deletions nautobot_device_lifecycle_mgmt/migrations/0030_softwarenotice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Generated by Django 4.2.16 on 2025-01-16 17:02

import uuid

import django.core.serializers.json
import django.db.models.deletion
import nautobot.core.models.fields
import nautobot.extras.models.mixins
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("dcim", "0062_module_data_migration"),
("extras", "0114_computedfield_grouping"),
("nautobot_device_lifecycle_mgmt", "0029_devicehardwarenoticeresult"),
]

operations = [
migrations.CreateModel(
name="SoftwareNotice",
fields=[
(
"id",
models.UUIDField(
default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True
),
),
("created", models.DateTimeField(auto_now_add=True, null=True)),
("last_updated", models.DateTimeField(auto_now=True, null=True)),
(
"_custom_field_data",
models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder),
),
("end_of_sale", models.DateField(blank=True, null=True)),
("end_of_support", models.DateField(blank=True, null=True)),
("end_of_sw_releases", models.DateField(blank=True, null=True)),
("end_of_security_patches", models.DateField(blank=True, null=True)),
("documentation_url", models.URLField(blank=True)),
("comments", models.TextField(blank=True, default="")),
(
"device_type",
models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="dcim.devicetype"
),
),
(
"software_version",
models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="dcim.softwareversion"
),
),
("tags", nautobot.core.models.fields.TagsField(through="extras.TaggedItem", to="extras.Tag")),
],
options={
"verbose_name": "Software Notice",
"ordering": ("end_of_sale",),
"unique_together": {("software_version", "device_type")},
},
bases=(
nautobot.extras.models.mixins.DynamicGroupMixin,
nautobot.extras.models.mixins.NotesMixin,
models.Model,
),
),
]
Loading

0 comments on commit 235ac13

Please sign in to comment.