Skip to content

Commit

Permalink
Merge pull request #569 from nautobot/jmcgill/2.x-remove-csv-import-e…
Browse files Browse the repository at this point in the history
…xport

Remove code around csv import/export per 2.x changes
  • Loading branch information
jmcgill298 authored Sep 6, 2023
2 parents bc4d9f7 + 0d12d61 commit ab7cb45
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 281 deletions.
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

0 comments on commit ab7cb45

Please sign in to comment.