Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

additional pr review updates for remediation feature #564

Merged
merged 4 commits into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion development/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ RUN pip show nautobot | grep "^Version: " | sed -e 's/Version: /nautobot==/' > c
# We can't use the entire freeze as it takes forever to resolve with rigidly fixed non-direct dependencies,
# especially those that are only direct to Nautobot but the container included versions slightly mismatch
RUN poetry export -f requirements.txt --without-hashes --output poetry_freeze_base.txt
RUN poetry export -f requirements.txt --dev --without-hashes --output poetry_freeze_all.txt
RUN poetry export -f requirements.txt --with dev --without-hashes --output poetry_freeze_all.txt
RUN sort poetry_freeze_base.txt poetry_freeze_all.txt | uniq -u > poetry_freeze_dev.txt

# Install all local project as editable, constrained on Nautobot version, to get any additional
Expand Down
5 changes: 3 additions & 2 deletions development/docker-compose.base.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ services:
condition: "service_started"
db:
condition: "service_healthy"
<<: *nautobot-build
<<: *nautobot-base
<<:
- *nautobot-build
- *nautobot-base
worker:
entrypoint:
- "sh"
Expand Down
2 changes: 1 addition & 1 deletion nautobot_golden_config/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ class ConfigToPushViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):


class RemediationSettingViewSet(NautobotModelViewSet): # pylint:disable=too-many-ancestors
"""API viewset for interacting with ConfigRemove objects."""
"""API viewset for interacting with RemediationSetting objects."""

queryset = models.RemediationSetting.objects.all()
serializer_class = serializers.RemediationSettingSerializer
Expand Down
14 changes: 7 additions & 7 deletions nautobot_golden_config/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from nautobot.extras.filters import CustomFieldModelFilterSetMixin, StatusFilter
from nautobot.extras.models import JobResult, Status
from nautobot.tenancy.models import Tenant, TenantGroup
from nautobot.utilities.filters import BaseFilterSet, NameSlugSearchFilterSet, TreeNodeMultipleChoiceFilter, TagFilter
from nautobot.utilities.filters import BaseFilterSet, NameSlugSearchFilterSet, TagFilter, TreeNodeMultipleChoiceFilter
from nautobot_golden_config import models


Expand Down Expand Up @@ -293,18 +293,18 @@ class RemediationSettingFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
method="search",
label="Search",
)
remediationsetting_id = django_filters.ModelMultipleChoiceFilter(
queryset=models.RemediationSetting.objects.all(),
label="RemediationSetting ID",
)
platform = django_filters.ModelMultipleChoiceFilter(
field_name="platform__name",
queryset=Platform.objects.all(),
to_field_name="name",
label="Platform Name",
)
platform_id = django_filters.ModelMultipleChoiceFilter(
queryset=Platform.objects.all(),
label="Platform ID",
)
remediation_type = django_filters.ModelMultipleChoiceFilter(
field_name="remediationsetting__remediation_type",
field_name="remediation_type",
queryset=models.RemediationSetting.objects.all(),
to_field_name="remediation_type",
label="Remediation Type",
Expand All @@ -314,7 +314,7 @@ def search(self, queryset, name, value): # pylint: disable=unused-argument
"""Perform the filtered search."""
if not value.strip():
return queryset
qs_filter = Q(platform__icontains=value)
qs_filter = Q(platform__name__icontains=value) | Q(remediation_type__icontains=value)
return queryset.filter(qs_filter)

class Meta:
Expand Down
28 changes: 13 additions & 15 deletions nautobot_golden_config/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,13 @@
import nautobot.extras.forms as extras_forms
import nautobot.utilities.forms as utilities_forms
from django import forms
from nautobot.dcim.models import (
Device,
DeviceRole,
DeviceType,
Manufacturer,
Platform,
Rack,
RackGroup,
Region,
Site,
)
from nautobot.dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Platform, Rack, RackGroup, Region, Site
from nautobot.extras.forms import NautobotBulkEditForm, NautobotFilterForm, NautobotModelForm
from nautobot.extras.models import DynamicGroup, GitRepository, JobResult, Status, Tag
from nautobot.tenancy.models import Tenant, TenantGroup
from nautobot.utilities.forms import add_blank_choice, DatePicker, SlugField, TagFilterField
from nautobot_golden_config import models
from nautobot_golden_config.choices import ComplianceRuleConfigTypeChoice, ConfigPlanTypeChoice
from nautobot_golden_config.choices import ComplianceRuleConfigTypeChoice, ConfigPlanTypeChoice, RemediationTypeChoice

# ConfigCompliance

Expand Down Expand Up @@ -416,10 +406,10 @@ class Meta:

# Remediation Setting
class RemediationSettingForm(NautobotModelForm):
"""Filter Form for Line Removal instances."""
"""Filter Form for Remediation Settings instances."""

class Meta:
"""Boilerplate form Meta data for removal feature."""
"""Boilerplate form Meta data for Remediation Settings."""

model = models.RemediationSetting
fields = (
Expand All @@ -430,9 +420,16 @@ class Meta:


class RemediationSettingFilterForm(NautobotFilterForm):
"""Filter Form for Line Replacement."""
"""Filter Form for Remediation Settings."""

model = models.RemediationSetting
q = forms.CharField(required=False, label="Search")
platform = utilities_forms.DynamicModelMultipleChoiceField(
queryset=Platform.objects.all(), required=False, display_field="name", to_field_name="name"
)
remediation_type = forms.ChoiceField(
choices=add_blank_choice(RemediationTypeChoice), required=False, label="Remediation Type"
)


class RemediationSettingCSVForm(extras_forms.CustomFieldModelCSVForm):
Expand All @@ -451,6 +448,7 @@ class RemediationSettingBulkEditForm(NautobotBulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=models.RemediationSetting.objects.all(), widget=forms.MultipleHiddenInput
)
remediation_type = forms.ChoiceField(choices=RemediationTypeChoice, label="Remediation Type")

class Meta:
"""Boilerplate form Meta data for RemediationSetting."""
Expand Down
28 changes: 12 additions & 16 deletions nautobot_golden_config/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,10 @@
from nautobot.utilities.utils import serialize_object, serialize_object_v2
from netutils.config.compliance import feature_compliance
from netutils.lib_mapper import HIERCONFIG_LIB_MAPPER_REVERSE
from nautobot_golden_config.choices import (
ComplianceRuleConfigTypeChoice,
ConfigPlanTypeChoice,
RemediationTypeChoice,
)
from nautobot_golden_config.choices import ComplianceRuleConfigTypeChoice, ConfigPlanTypeChoice, RemediationTypeChoice
from nautobot_golden_config.utilities.constant import ENABLE_SOTAGG, PLUGIN_CFG
from nautobot_golden_config.utilities.utils import get_platform


LOGGER = logging.getLogger(__name__)
GRAPHQL_STR_START = "query ($device_id: ID!)"

Expand Down Expand Up @@ -298,11 +293,7 @@ class ComplianceRule(PrimaryModel): # pylint: disable=too-many-ancestors
@property
def remediation_setting(self):
"""Returns remediation settings for a particular platform."""
try:
remediation_setting = RemediationSetting.objects.get(platform=self.platform)
except RemediationSetting.DoesNotExist as err:
raise ValidationError(f"Platform {self.platform.slug} has no Remediation Settings defined.") from err
return remediation_setting
return RemediationSetting.objects.filter(platform=self.platform).first()

def to_csv(self):
"""Indicates model fields to return as csv."""
Expand Down Expand Up @@ -428,10 +419,10 @@ def remediation_on_save(self):
self.remediation = None
return

if not FUNC_MAPPER.get(self.rule.remediation_setting.remediation_type):
raise ValidationError(
f"Remediation {self.rule.remediation_setting.remediation_type} has no associated function set."
)
if not self.rule.remediation_setting:
self.remediation = None
return

remediation_config = FUNC_MAPPER[self.rule.remediation_setting.remediation_type](obj=self)
self.remediation = remediation_config

Expand Down Expand Up @@ -826,6 +817,11 @@ class RemediationSetting(PrimaryModel): # pylint: disable=too-many-ancestors
"remediation_type",
]

class Meta:
"""Meta information for RemediationSettings model."""

ordering = ("platform", "remediation_type")

def to_csv(self):
"""Indicates model fields to return as csv."""
return (
Expand All @@ -835,7 +831,7 @@ def to_csv(self):

def __str__(self):
"""Return a sane string representation of the instance."""
return self.platform.slug
return str(self.platform.slug)

def get_absolute_url(self):
"""Absolute url for the RemediationRule instance."""
Expand Down
31 changes: 22 additions & 9 deletions nautobot_golden_config/tests/test_api.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,26 @@
"""Unit tests for nautobot_golden_config."""
from copy import deepcopy

from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType

from django.urls import reverse
from rest_framework import status

from nautobot.dcim.models import Device, Platform
from nautobot.extras.models import DynamicGroup, GitRepository, GraphQLQuery, Status
from nautobot.utilities.testing import APITestCase, APIViewTestCases
from nautobot.extras.models import GitRepository, GraphQLQuery, DynamicGroup, Status
from rest_framework import status
from nautobot_golden_config.choices import RemediationTypeChoice
from nautobot_golden_config.models import ConfigPlan, GoldenConfigSetting, RemediationSetting

from .conftest import (
create_config_compliance,
create_device,
create_device_data,
create_feature_rule_json,
create_config_compliance,
create_git_repos,
create_saved_queries,
create_device_data,
create_job_result,
create_saved_queries,
)


User = get_user_model()


Expand Down Expand Up @@ -312,6 +310,22 @@ def setUpTestData(cls):
remediation_type=type_custom,
)

platforms = (
Platform.objects.create(name="Platform 4", slug="platform-4"),
Platform.objects.create(name="Platform 5", slug="platform-5"),
Platform.objects.create(name="Platform 6", slug="platform-6"),
)

cls.create_data = [
{"platform": platforms[0].pk, "remediation_type": type_cli},
{
"platform": platforms[1].pk,
"remediation_type": type_cli,
"remediation_options": {"some_option": "some_value"},
},
{"platform": platforms[2].pk, "remediation_type": type_custom},
]

cls.update_data = {
"remediation_type": type_custom,
}
Expand All @@ -324,7 +338,6 @@ def test_list_objects_brief(self):
"""Skipping test due to brief_fields not implemented."""



# pylint: disable=too-many-ancestors,too-many-locals
class ConfigPlanTest(APIViewTestCases.APIViewTestCase):
"""Test API for ConfigPlan."""
Expand Down
Loading