Skip to content

Commit

Permalink
Updated Filters for various models, including adding an experimental …
Browse files Browse the repository at this point in the history
…`_isnull` on DateTime objects (#558)
  • Loading branch information
itdependsnetworks authored Sep 1, 2023
1 parent 59d050e commit 470ee0e
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 42 deletions.
2 changes: 2 additions & 0 deletions docs/admin/release_notes/version_1.5.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- Made custom compliance a boolean to support both JSON or CLI custom compliance types.
- Add metrics for Golden Config plugin.
- Add jinja settings support.
- Updated Filters for various models, including adding an experimental `_isnull` on DateTime objects.
- Supports Nautobot >=1.6.1,<2.0.0.

## v1.5.0 - 2023-08
Expand All @@ -14,6 +15,7 @@
- [485](https://github.com/nautobot/nautobot-plugin-golden-config/pull/485) - Custom compliance for CLI and JSON rules.
- [487](https://github.com/nautobot/nautobot-plugin-golden-config/pull/487) - Implement native JSON support.
- [527](https://github.com/nautobot/nautobot-plugin-golden-config/pull/527) - Add the ability to update Jinja environment setting from nautobot_config.
- [558](https://github.com/nautobot/nautobot-plugin-golden-config/pull/558) - Updated Filters for various models, including adding an experimental `_isnull` on DateTime objects.

### Changed

Expand Down
6 changes: 5 additions & 1 deletion docs/user/app_faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,14 @@ ERROR: 1
If you receive this error, the issue is the secret key has been changed, and **does not** have anything to do with the Golden Config plugin. You can either delete the entries from your data source and the reference to those in the Golden Config settings or revert the secret key back so it matches the original deployment. Any issues opened will be closed and this faq referred to. If you still need help, feel free to join the Slack community.
_I got a `preemptively failed` error, but I know my system is setup correctly?_
## _I got a `preemptively failed` error, but I know my system is setup correctly?_
These errors have been accurate so far, that is not to say that there is no way they could be a bug, but most commonly they have worked as expected thus far. Common issues include.
* Incorrectly configured Secrets
* Filtering to nothing when presumption is the filter works a certain way
* Referencing an OS that is not recognized
## _Why is the `_isnull` on DateTime filters considered experimental?_
There are various ways we can create a programmatic interface, which may change the behavior or name, for now it should be considered experimental as we may update this strategy.
141 changes: 110 additions & 31 deletions nautobot_golden_config/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,57 @@
import django_filters
from django.db.models import Q
from nautobot.dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Platform, Rack, RackGroup, Region, Site
from nautobot.extras.filters import CustomFieldModelFilterSetMixin, StatusFilter
from nautobot.dcim.filters import DeviceFilterSet
from nautobot.extras.filters import StatusFilter
from nautobot.extras.filters import NautobotFilterSet
from nautobot.extras.models import Status
from nautobot.tenancy.models import Tenant, TenantGroup
from nautobot.utilities.filters import BaseFilterSet, NameSlugSearchFilterSet, TreeNodeMultipleChoiceFilter
from nautobot.utilities.filters import TreeNodeMultipleChoiceFilter
from nautobot.utilities.filters import MultiValueDateTimeFilter

from nautobot_golden_config import models


class GenericPlatformFilterSet(CustomFieldModelFilterSetMixin):
"""Generic method to reuse common FilterSet."""
class GoldenConfigDeviceFilterSet(DeviceFilterSet): # pylint: disable=too-many-ancestors
"""Filter capabilities that extend the standard DeviceFilterSet."""

platform_id = django_filters.ModelMultipleChoiceFilter(
queryset=Platform.objects.all(),
label="Platform (ID)",
)
platform = django_filters.ModelMultipleChoiceFilter(
field_name="platform__slug",
queryset=Platform.objects.all(),
to_field_name="slug",
label="Platform (slug)",
)
@staticmethod
def _get_filter_lookup_dict(existing_filter):
"""Extend method to account for isnull on datetime types."""
# Choose the lookup expression map based on the filter type
lookup_map = DeviceFilterSet._get_filter_lookup_dict(existing_filter)
if isinstance(existing_filter, MultiValueDateTimeFilter):
lookup_map.update({"isnull": "isnull"})
return lookup_map

class Meta(DeviceFilterSet.Meta):
"""Update the Meta class, but only for fields."""

fields = DeviceFilterSet.Meta.fields + [
"goldenconfig__backup_config",
"goldenconfig__backup_last_attempt_date",
"goldenconfig__backup_last_success_date",
"goldenconfig__intended_config",
"goldenconfig__intended_last_attempt_date",
"goldenconfig__intended_last_success_date",
"goldenconfig__compliance_config",
"goldenconfig__compliance_last_attempt_date",
"goldenconfig__compliance_last_success_date",
]


class GoldenConfigFilterSet(CustomFieldModelFilterSetMixin):
class GoldenConfigFilterSet(NautobotFilterSet):
"""Filter capabilities for GoldenConfig instances."""

@staticmethod
def _get_filter_lookup_dict(existing_filter):
"""Extend method to account for isnull on datetime types."""
# Choose the lookup expression map based on the filter type
lookup_map = NautobotFilterSet._get_filter_lookup_dict(existing_filter)
if isinstance(existing_filter, MultiValueDateTimeFilter):
lookup_map.update({"isnull": "isnull"})
return lookup_map

q = django_filters.CharFilter(
method="search",
label="Search",
Expand Down Expand Up @@ -175,21 +201,44 @@ class Meta:

model = models.GoldenConfig
distinct = True
fields = ["id"]


class ConfigComplianceFilterSet(GoldenConfigFilterSet):
fields = [
"id",
"backup_config",
"backup_last_attempt_date",
"backup_last_success_date",
"intended_config",
"intended_last_attempt_date",
"intended_last_success_date",
"compliance_config",
"compliance_last_attempt_date",
"compliance_last_success_date",
]


class ConfigComplianceFilterSet(GoldenConfigFilterSet): # pylint: disable=too-many-ancestors
"""Filter capabilities for ConfigCompliance instances."""

feature_id = django_filters.ModelMultipleChoiceFilter(
field_name="rule__feature",
queryset=models.ComplianceFeature.objects.all(),
label="ComplianceFeature (ID)",
)
feature = django_filters.ModelMultipleChoiceFilter(
field_name="rule__feature__slug",
queryset=models.ComplianceFeature.objects.all(),
to_field_name="slug",
label="ComplianceFeature (slug)",
)

class Meta:
"""Meta class attributes for ConfigComplianceFilter."""

model = models.ConfigCompliance
fields = ["id"]
fields = ["id", "compliance", "actual", "intended", "missing", "extra", "ordered", "compliance_int", "rule"]


class ComplianceFeatureFilterSet(CustomFieldModelFilterSetMixin):
"""Inherits Base Class CustomFieldModelFilterSetMixin."""
class ComplianceFeatureFilterSet(NautobotFilterSet):
"""Inherits Base Class NautobotFilterSet."""

q = django_filters.CharFilter(
method="search",
Expand All @@ -207,16 +256,26 @@ class Meta:
"""Boilerplate filter Meta data for compliance feature."""

model = models.ComplianceFeature
fields = ["id", "name", "slug"]
fields = ["id", "name", "slug", "description"]


class ComplianceRuleFilterSet(GenericPlatformFilterSet):
"""Inherits Base Class CustomFieldModelFilterSetMixin."""
class ComplianceRuleFilterSet(NautobotFilterSet):
"""Inherits Base Class NautobotFilterSet."""

q = django_filters.CharFilter(
method="search",
label="Search",
)
platform_id = django_filters.ModelMultipleChoiceFilter(
queryset=Platform.objects.all(),
label="Platform (ID)",
)
platform = django_filters.ModelMultipleChoiceFilter(
field_name="platform__slug",
queryset=Platform.objects.all(),
to_field_name="slug",
label="Platform (slug)",
)

def search(self, queryset, name, value): # pylint: disable=unused-argument
"""Perform the filtered search."""
Expand All @@ -232,13 +291,23 @@ class Meta:
fields = ["feature", "id"]


class ConfigRemoveFilterSet(GenericPlatformFilterSet):
"""Inherits Base Class CustomFieldModelFilterSetMixin."""
class ConfigRemoveFilterSet(NautobotFilterSet):
"""Inherits Base Class NautobotFilterSet."""

q = django_filters.CharFilter(
method="search",
label="Search",
)
platform_id = django_filters.ModelMultipleChoiceFilter(
queryset=Platform.objects.all(),
label="Platform (ID)",
)
platform = django_filters.ModelMultipleChoiceFilter(
field_name="platform__slug",
queryset=Platform.objects.all(),
to_field_name="slug",
label="Platform (slug)",
)

def search(self, queryset, name, value): # pylint: disable=unused-argument
"""Perform the filtered search."""
Expand All @@ -254,13 +323,23 @@ class Meta:
fields = ["id", "name"]


class ConfigReplaceFilterSet(GenericPlatformFilterSet):
"""Inherits Base Class CustomFieldModelFilterSetMixin."""
class ConfigReplaceFilterSet(NautobotFilterSet):
"""Inherits Base Class NautobotFilterSet."""

q = django_filters.CharFilter(
method="search",
label="Search",
)
platform_id = django_filters.ModelMultipleChoiceFilter(
queryset=Platform.objects.all(),
label="Platform (ID)",
)
platform = django_filters.ModelMultipleChoiceFilter(
field_name="platform__slug",
queryset=Platform.objects.all(),
to_field_name="slug",
label="Platform (slug)",
)

def search(self, queryset, name, value): # pylint: disable=unused-argument
"""Perform the filtered search."""
Expand All @@ -276,8 +355,8 @@ class Meta:
fields = ["id", "name"]


class GoldenConfigSettingFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
"""Inherits Base Class BaseFilterSet."""
class GoldenConfigSettingFilterSet(NautobotFilterSet):
"""Inherits Base Class NautobotFilterSet."""

class Meta:
"""Boilerplate filter Meta data for Config Remove."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
<table class="table table-hover panel-body attr-table">
<tr>
<td>Platform</td>
<td>{{ object.platform.name }}</td>
<td><a href="{{ object.platform.get_absolute_url }}">{{ object.platform.name }}</a></td>
</tr>
<tr>
<td>Feature</td>
<td>{{ object.feature.name }}</td>
<td><a href="{{ object.feature.get_absolute_url }}">{{ object.feature.name }}</a></td>
</tr>
<tr>
<td>Description</td>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</tr>
<tr>
<td>Platform</td>
<td>{{ object.platform.name }}</td>
<td><a href="{{ object.platform.get_absolute_url }}">{{ object.platform.name }}</a></td>
</tr>
<tr>
<td>Description</td>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</tr>
<tr>
<td>Platform</td>
<td>{{ object.platform.name }}</td>
<td><a href="{{ object.platform.get_absolute_url }}">{{ object.platform.name }}</a></td>
</tr>
<tr>
<td>Description</td>
Expand Down
6 changes: 3 additions & 3 deletions nautobot_golden_config/tests/test_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def setUp(self):

def test_id(self):
"""Test filtering by ID (primary key)."""
params = {"id": self.queryset.values_list("pk", flat=True)[0]}
params = {"id": str(self.queryset.values_list("pk", flat=True)[0])}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)

def test_full(self):
Expand Down Expand Up @@ -184,7 +184,7 @@ def setUp(self):

def test_id(self):
"""Test filtering by ID (primary key)."""
params = {"id": self.queryset.values_list("pk", flat=True)[0]}
params = {"id": str(self.queryset.values_list("pk", flat=True)[0])}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)

def test_full(self):
Expand Down Expand Up @@ -287,7 +287,7 @@ def test_full(self):

def test_id(self):
"""Test filtering by ID (primary key)."""
params = {"id": self.queryset.values_list("pk", flat=True)[0]}
params = {"id": str(self.queryset.values_list("pk", flat=True)[0])}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)

def test_name(self):
Expand Down
5 changes: 2 additions & 3 deletions nautobot_golden_config/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
from django_pivot.pivot import pivot
from nautobot.core.views import generic
from nautobot.core.views.viewsets import NautobotUIViewSet
from nautobot.dcim.filters import DeviceFilterSet
from nautobot.dcim.forms import DeviceFilterForm
from nautobot.dcim.models import Device
from nautobot.utilities.error_handlers import handle_protectederror
Expand Down Expand Up @@ -48,7 +47,7 @@ class GoldenConfigListView(generic.ObjectListView):
"""View for displaying the configuration management status for backup, intended, diff, and SoT Agg."""

table = tables.GoldenConfigTable
filterset = DeviceFilterSet
filterset = filters.GoldenConfigDeviceFilterSet
filterset_form = DeviceFilterForm
queryset = Device.objects.all()
template_name = "nautobot_golden_config/goldenconfig_list.html"
Expand Down Expand Up @@ -110,7 +109,7 @@ class GoldenConfigBulkDeleteView(generic.BulkDeleteView):

queryset = Device.objects.all()
table = tables.GoldenConfigTable
filterset = DeviceFilterSet
filterset = filters.GoldenConfigDeviceFilterSet

def post(self, request, **kwargs):
"""Delete instances based on post request data."""
Expand Down

0 comments on commit 470ee0e

Please sign in to comment.