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

Remove code around csv import/export per 2.x changes #569

Merged
merged 1 commit 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
51 changes: 0 additions & 51 deletions nautobot_golden_config/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

from django import forms

import nautobot.extras.forms as extras_forms
import nautobot.core.forms as core_forms
from nautobot.dcim.models import Device, Platform, Location, DeviceType, Manufacturer, Rack, RackGroup
from nautobot.extras.forms import NautobotFilterForm, NautobotBulkEditForm, NautobotModelForm
Expand Down Expand Up @@ -176,16 +175,6 @@ class Meta:
nullable_fields = []


class ComplianceRuleCSVForm(extras_forms.CustomFieldModelCSVForm):
"""CSV Form for ComplianceRule instances."""

class Meta:
"""Boilerplate form Meta data for ComplianceRule."""

model = models.ComplianceRule
fields = models.ComplianceRule.csv_headers


# ComplianceFeature


Expand Down Expand Up @@ -222,16 +211,6 @@ class Meta:
nullable_fields = []


class ComplianceFeatureCSVForm(extras_forms.CustomFieldModelCSVForm):
"""CSV Form for ComplianceFeature instances."""

class Meta:
"""Boilerplate form Meta data for ComplianceFeature."""

model = models.ComplianceFeature
fields = models.ComplianceFeature.csv_headers


# ConfigRemove


Expand Down Expand Up @@ -275,16 +254,6 @@ class Meta:
nullable_fields = []


class ConfigRemoveCSVForm(extras_forms.CustomFieldModelCSVForm):
"""CSV Form for ConfigRemove instances."""

class Meta:
"""Boilerplate form Meta data for ConfigRemove."""

model = models.ConfigRemove
fields = models.ConfigRemove.csv_headers


# ConfigReplace


Expand Down Expand Up @@ -319,16 +288,6 @@ class ConfigReplaceFilterForm(NautobotFilterForm):
)


class ConfigReplaceCSVForm(extras_forms.CustomFieldModelCSVForm):
"""CSV Form for ConfigReplace instances."""

class Meta:
"""Boilerplate form Meta data for ConfigReplace."""

model = models.ConfigReplace
fields = models.ConfigReplace.csv_headers


class ConfigReplaceBulkEditForm(NautobotBulkEditForm):
"""BulkEdit form for ConfigReplace instances."""

Expand Down Expand Up @@ -393,16 +352,6 @@ class GoldenConfigSettingFilterForm(NautobotFilterForm):
)


class GoldenConfigSettingCSVForm(extras_forms.CustomFieldModelCSVForm):
"""CSV Form for GoldenConfigSetting instances."""

class Meta:
"""Boilerplate form Meta data for GoldenConfigSetting."""

model = models.GoldenConfigSetting
fields = models.GoldenConfigSetting.csv_headers


class GoldenConfigSettingBulkEditForm(NautobotBulkEditForm):
"""BulkEdit form for GoldenConfigSetting instances."""

Expand Down
93 changes: 0 additions & 93 deletions nautobot_golden_config/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,6 @@ class ComplianceFeature(PrimaryModel): # pylint: disable=too-many-ancestors
slug = models.SlugField(max_length=100, unique=True)
description = models.CharField(max_length=200, blank=True)

csv_headers = ["name", "slug", "description"]

def to_csv(self):
"""Indicates model fields to return as csv."""
return (self.name, self.slug, self.description)

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

Expand Down Expand Up @@ -223,33 +217,10 @@ class ComplianceRule(PrimaryModel): # pylint: disable=too-many-ancestors
choices=ComplianceRuleConfigTypeChoice,
help_text="Whether the configuration is in CLI or JSON/structured format.",
)

custom_compliance = models.BooleanField(
default=False, help_text="Whether this Compliance Rule is proceeded as custom."
)

csv_headers = [
"platform",
"feature",
"description",
"config_ordered",
"match_config",
"config_type",
"custom_compliance",
]

def to_csv(self):
"""Indicates model fields to return as csv."""
return (
self.platform.name, # TODO: verify that changing slug -> name works (write a test case)
self.feature.name,
self.description,
self.config_ordered,
self.match_config,
self.config_type,
self.custom_compliance,
)

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

Expand Down Expand Up @@ -297,17 +268,11 @@ class ConfigCompliance(PrimaryModel): # pylint: disable=too-many-ancestors
# Used for django-pivot, both compliance and compliance_int should be set.
compliance_int = models.IntegerField(null=True, blank=True)

csv_headers = ["Device Name", "Feature", "Compliance"]

# TODO: Fix pylint arguments-differ for 2.x migration
def get_absolute_url(self): # pylint: disable=arguments-differ
"""Return absolute URL for instance."""
return reverse("plugins:nautobot_golden_config:configcompliance", args=[self.pk])

def to_csv(self):
"""Indicates model fields to return as csv."""
return (self.device.name, self.rule.feature.name, self.compliance)

def to_objectchange(
self, action, *, related_object=None, object_data_extra=None, object_data_exclude=None
): # pylint: disable=arguments-differ
Expand Down Expand Up @@ -384,28 +349,6 @@ class GoldenConfig(PrimaryModel): # pylint: disable=too-many-ancestors
compliance_last_attempt_date = models.DateTimeField(null=True)
compliance_last_success_date = models.DateTimeField(null=True)

csv_headers = [
"Device Name",
"backup attempt",
"backup successful",
"intended attempt",
"intended successful",
"compliance attempt",
"compliance successful",
]

def to_csv(self):
"""Indicates model fields to return as csv."""
return (
self.device,
self.backup_last_attempt_date,
self.backup_last_success_date,
self.intended_last_attempt_date,
self.intended_last_success_date,
self.compliance_last_attempt_date,
self.compliance_last_success_date,
)

def to_objectchange(
self, action, *, related_object=None, object_data_extra=None, object_data_exclude=None
): # pylint: disable=arguments-differ
Expand Down Expand Up @@ -508,22 +451,6 @@ class GoldenConfigSetting(PrimaryModel): # pylint: disable=too-many-ancestors
related_name="golden_config_setting",
)

csv_headers = [
"name",
"slug",
"weight",
"description",
]

def to_csv(self):
"""Indicates model fields to return as csv."""
return (
self.name,
self.slug,
self.weight,
self.description,
)

def __str__(self):
"""Return a simple string if model is called."""
return f"Golden Config Setting - {self.name}"
Expand Down Expand Up @@ -595,15 +522,6 @@ class ConfigRemove(PrimaryModel): # pylint: disable=too-many-ancestors
)

clone_fields = ["platform", "description", "regex"]
csv_headers = ["name", "platform", "description", "regex"]

def to_csv(self):
"""Indicates model fields to return as csv."""
return (
self.name,
self.platform.name,
self.regex,
) # TODO: verify that changing platform.slug -> name works (write a test case)

class Meta:
"""Meta information for ConfigRemove model."""
Expand Down Expand Up @@ -652,17 +570,6 @@ class ConfigReplace(PrimaryModel): # pylint: disable=too-many-ancestors
)

clone_fields = ["platform", "description", "regex", "replace"]
csv_headers = ["name", "platform", "description", "regex", "replace"]

def to_csv(self):
"""Indicates model fields to return as csv."""
return (
self.name,
self.platform.name,
self.description,
self.regex,
self.replace,
) # TODO: verify that changing platform.slug -> name works (write a test case)

class Meta:
"""Meta information for ConfigReplace model."""
Expand Down
61 changes: 61 additions & 0 deletions nautobot_golden_config/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,64 @@ def test_settings_api_clean_up(self):
# Put back a general GoldenConfigSetting object.
global_settings = GoldenConfigSetting.objects.create(dynamic_group=self.dynamic_group)
global_settings.save()


# pylint: disable=too-many-ancestors


class GoldenConfigSerializerCSVTest(APITestCase):
"""Test CSV Export returns 200/OK."""

url = reverse("plugins-api:nautobot_golden_config-api:goldenconfig-list")

def setUp(self):
super().setUp()
self._add_permissions()

def _add_permissions(self):
model = self.url.split("/")[-2]
permission_name = model.replace("-", "")
self.add_permissions(f"nautobot_golden_config.view_{permission_name}")

def test_csv_export(self):
response = self.client.get(f"{self.url}?format=csv", **self.header)
self.assertEqual(response.status_code, status.HTTP_200_OK)


class GoldenConfigSettingSerializerCSVTest(GoldenConfigSerializerCSVTest):
"""Test CSV Export returns 200/OK."""

url = reverse("plugins-api:nautobot_golden_config-api:goldenconfigsetting-list")

def _add_permissions(self):
self.add_permissions("nautobot_golden_config.view_goldenconfigsetting")


class ComplianceFeatureSerializerCSVTest(GoldenConfigSerializerCSVTest):
"""Test CSV Export returns 200/OK."""

url = reverse("plugins-api:nautobot_golden_config-api:compliancefeature-list")


class ComplianceRuleCSVTest(GoldenConfigSerializerCSVTest):
"""Test CSV Export returns 200/OK."""

url = reverse("plugins-api:nautobot_golden_config-api:compliancerule-list")


class ConfigComplianceCSVTest(GoldenConfigSerializerCSVTest):
"""Test CSV Export returns 200/OK."""

url = reverse("plugins-api:nautobot_golden_config-api:configcompliance-list")


class ConfigRemoveCSVTest(GoldenConfigSerializerCSVTest):
"""Test CSV Export returns 200/OK."""

url = reverse("plugins-api:nautobot_golden_config-api:configremove-list")


class ConfigReplaceCSVTest(GoldenConfigSerializerCSVTest):
"""Test CSV Export returns 200/OK."""

url = reverse("plugins-api:nautobot_golden_config-api:configreplace-list")
56 changes: 1 addition & 55 deletions nautobot_golden_config/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""Unit tests for nautobot_golden_config views."""

import datetime
from unittest import mock, skip
from unittest import mock

from lxml import html

Expand Down Expand Up @@ -145,19 +144,6 @@ def _entry_regex(self):
def _entry_replace(self):
return "<dontlookatme>"

@skip("TODO: Look into export changes")
def test_configreplace_export(self):
response = self.client.get(f"{self._url}?export")
self.assertEqual(response.status_code, 200)
self.assertEqual(response.headers["Content-Type"], "text/csv")
last_entry = models.ConfigReplace.objects.last()
csv_data = response.content.decode().splitlines()
expected_last_entry = f"{last_entry.name},{last_entry.platform.name},{last_entry.description},{last_entry.regex},{last_entry.replace}"
self.assertEqual(csv_data[0], self._csv_headers)
self.assertEqual(csv_data[-1], expected_last_entry)
self.assertEqual(len(csv_data) - 1, models.ConfigReplace.objects.count())

@skip("TODO: Look into import changes")
def test_configreplace_import(self):
self._delete_test_entry()
platform = Device.objects.first().platform
Expand Down Expand Up @@ -256,43 +242,3 @@ def test_scope_change_affects_table_entries(self):
_, table_body = self._get_golden_config_table()
devices_in_table = [device_column.text for device_column in table_body.xpath("tr/td[2]/a")]
self.assertNotIn(last_device.name, devices_in_table)

@skip("TODO: Look into export changes")
def test_csv_export(self):
# verify GoldenConfig table is empty
self.assertEqual(models.GoldenConfig.objects.count(), 0)
intended_datetime = datetime.datetime.now()
first_device = self.gc_dynamic_group.members.first()
models.GoldenConfig.objects.create(
device=first_device,
intended_last_attempt_date=intended_datetime,
intended_last_success_date=intended_datetime,
)
response = self.client.get(f"{self._url}?export")
self.assertEqual(response.status_code, 200)
self.assertEqual(response.headers["Content-Type"], "text/csv")
csv_data = response.content.decode().splitlines()
csv_headers = "Device Name,backup attempt,backup successful,intended attempt,intended successful,compliance attempt,compliance successful"
self.assertEqual(csv_headers, csv_data[0])
intended_datetime_formated = intended_datetime.strftime("%Y-%m-%dT%H:%M:%S.%f+00:00")
# Test single entry in GoldenConfig table has data
expected_first_row = f"{first_device.name},,,{intended_datetime_formated},{intended_datetime_formated},,"
self.assertEqual(expected_first_row, csv_data[1])
# Test Devices in scope but without entries in GoldenConfig have empty entries
empty_csv_rows = [
f"{device.name},,,,,," for device in self.gc_dynamic_group.members.exclude(pk=first_device.pk)
]
self.assertEqual(empty_csv_rows, csv_data[2:])

@skip("TODO: Look into export changes")
def test_csv_export_with_filter(self):
devices_in_site_1 = Device.objects.filter(location__name="Site 1")
golden_config_devices = self.gc_dynamic_group.members.all()
# Test that there are Devices in GC that are not related to Site 1
self.assertNotEqual(devices_in_site_1, golden_config_devices)
response = self.client.get(f"{self._url}?location={Device.objects.first().location.name}&export")
self.assertEqual(response.status_code, 200)
csv_data = response.content.decode().splitlines()
device_names_in_export = [entry.split(",")[0] for entry in csv_data[1:]]
device_names_in_site_1 = [device.name for device in devices_in_site_1]
self.assertEqual(device_names_in_export, device_names_in_site_1)
Loading