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

Closes #39: Add status field to ConfigDiffScript #40

Merged
merged 3 commits into from
Oct 23, 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 docs/colliecting-diffs.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ In the script, you can define a site, on which devices run compliance, or device
If you define both fields, script will run only on devices from `Devices` field

!!! warning
Script runs only on devices with status `Active`, assigned Primary IP, Config Template, Platform and PlatformSetting
Script runs only on devices with assigned Primary IP, Config Template, Platform and PlatformSetting

If you have configs in NetBox DataSource, you can define it, the script instead of connecting to devices will find configs in DataSource by device's names.

Expand Down
16 changes: 9 additions & 7 deletions netbox_config_diff/compliance/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,29 @@

from .models import DeviceDataClass
from .secrets import SecretsMixin
from .utils import PLATFORM_MAPPING, exclude_lines, get_unified_diff
from .utils import PLATFORM_MAPPING, CustomChoiceVar, exclude_lines, get_unified_diff


class ConfigDiffBase(SecretsMixin):
site = ObjectVar(
model=Site,
required=False,
description="Run compliance for devices (with status Active, "
"primary IP, platform and config template) in this site",
description="Run compliance for devices (with primary IP, platform and config template) in this site",
)
devices = MultiObjectVar(
model=Device,
required=False,
query_params={
"status": DeviceStatusChoices.STATUS_ACTIVE,
"has_primary_ip": True,
"platform_id__n": "null",
"config_template_id__n": "null",
},
description="If you define devices in this field, the Site field will be ignored",
)
status = CustomChoiceVar(
choices=DeviceStatusChoices,
default=DeviceStatusChoices.STATUS_ACTIVE,
)
data_source = ObjectVar(
model=DataSource,
required=False,
Expand Down Expand Up @@ -66,7 +68,7 @@ def validate_data(self, data: dict) -> Iterable[Device]:
devices = (
data["devices"]
.filter(
status=DeviceStatusChoices.STATUS_ACTIVE,
status=data["status"],
platform__platform_setting__isnull=False,
config_template__isnull=False,
)
Expand All @@ -77,7 +79,7 @@ def validate_data(self, data: dict) -> Iterable[Device]:
else:
devices = Device.objects.filter(
site=data["site"],
status=DeviceStatusChoices.STATUS_ACTIVE,
status=data["status"],
platform__platform_setting__isnull=False,
config_template__isnull=False,
).exclude(
Expand All @@ -90,7 +92,7 @@ def validate_data(self, data: dict) -> Iterable[Device]:

if not devices:
self.log_warning(
"No matching devices found, devices must have status `Active`, primary IP, platform and platformsetting"
"No matching devices found, devices must have status primary IP, platform and platformsetting"
)
else:
self.log_info(f"Working with device(s): {', '.join(d.name for d in devices)}")
Expand Down
11 changes: 11 additions & 0 deletions netbox_config_diff/compliance/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import re
from difflib import unified_diff

from django.forms import ChoiceField
from extras.scripts import ScriptVariable

PLATFORM_MAPPING = {
"arista_eos": "arista_eos",
"cisco_aireos": "cisco_aireos",
Expand All @@ -17,6 +20,14 @@
}


class CustomChoiceVar(ScriptVariable):
form_field = ChoiceField

def __init__(self, choices, *args, **kwargs):
super().__init__(*args, **kwargs)
self.field_attrs["choices"] = choices


def get_unified_diff(rendered_config: str, actual_config: str, device: str) -> str:
diff = unified_diff(
rendered_config.strip().splitlines(),
Expand Down
7 changes: 4 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ def factory(**fields: Unpack["ScriptData"]) -> "ScriptData":
"site": None,
"devices": None,
"data_source": None,
"status": "active",
}
if fields.get("status"):
data["data_source"] = DataSourceFactory.create(status=fields["status"])
fields.pop("status")
if fields.get("data_source_status"):
data["data_source"] = DataSourceFactory.create(status=fields["data_source_status"])
fields.pop("data_source_status")
return data | fields

return factory
Expand Down
4 changes: 3 additions & 1 deletion tests/test_compliance.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ def test_validate_data_no_sync_datasource(
mock_config_diff: "ConfigDiffBase", script_data_factory: "ScriptDataFactory"
) -> None:
with pytest.raises(AbortScript) as e:
mock_config_diff.validate_data(data=script_data_factory(**{"site": "test", "devices": "test", "status": "new"}))
mock_config_diff.validate_data(
data=script_data_factory(**{"site": "test", "devices": "test", "data_source_status": "new"})
)
assert str(e.value) == "Define synced DataSource"


Expand Down