From 9d6bc9d1b419f5eff3a100309b0b18f685d3b1c7 Mon Sep 17 00:00:00 2001 From: Artem Kotik Date: Sun, 29 Sep 2024 17:28:10 +0200 Subject: [PATCH] Version 2.7.0 (#82) --- .github/workflows/commit.yaml | 2 +- README.md | 13 +++++-- netbox_config_diff/__init__.py | 6 +-- netbox_config_diff/api/serializers.py | 8 +++- netbox_config_diff/configurator/base.py | 4 +- netbox_config_diff/forms/general.py | 25 ++++++++++-- .../migrations/0010_create_script.py | 39 +++++++++++++++++++ .../configurationrequest/diffs.html | 4 +- netbox_config_diff/views/configuration.py | 1 + requirements/base.txt | 6 +-- 10 files changed, 87 insertions(+), 21 deletions(-) create mode 100644 netbox_config_diff/migrations/0010_create_script.py diff --git a/.github/workflows/commit.yaml b/.github/workflows/commit.yaml index 02a5a42..8cedc7d 100644 --- a/.github/workflows/commit.yaml +++ b/.github/workflows/commit.yaml @@ -31,7 +31,7 @@ jobs: strategy: max-parallel: 10 matrix: - netbox_version: ["v3.6.9", "v3.7.8", "v4.0.7"] + netbox_version: ["v3.7.8", "v4.0.7", "v4.1.1"] steps: - name: Checkout uses: actions/checkout@v3 diff --git a/README.md b/README.md index d025e33..ed92f41 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![NetBox version](https://img.shields.io/badge/NetBox-3.6|3.7|4.0-blue.svg)](https://github.com/netbox-community/netbox) +[![NetBox version](https://img.shields.io/badge/NetBox-3.7|4.0|4.1-blue.svg)](https://github.com/netbox-community/netbox) [![Supported Versions](https://img.shields.io/pypi/pyversions/netbox-config-diff.svg)](https://pypi.org/project/netbox-config-diff/) [![PyPI version](https://badge.fury.io/py/netbox-config-diff.svg)](https://badge.fury.io/py/netbox-config-diff) [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) @@ -9,8 +9,10 @@ [NetBox](https://github.com/netbox-community/netbox) plugin for Config Diff. -* Free software: Apache-2.0 -* Documentation: https://miaow2.github.io/netbox-config-diff/ +* Find diff between the rendered configuration for a device to its actual configuration, retrieved from the device itself, or stored in DataSource. +* Push the rendered configuration from NetBox to a device and apply it. + +Documentation: https://miaow2.github.io/netbox-config-diff/ ## About @@ -45,7 +47,10 @@ This is possible thanks to the scrapli_cfg. Read [Scrapli](https://github.com/sc | NetBox Version | Plugin Version | |----------------|------------------| | 3.5 | =>0.1.0, <=2.5.0 | -| 3.6, 3.7, 4.0 | =>0.1.0 | +| 3.6 | =>0.1.0, <=2.6.0 | +| 3.7 | =>0.1.0 | +| 4.0 | =>2.6.0 | +| 4.1 | =>2.7.0 | ## Installing diff --git a/netbox_config_diff/__init__.py b/netbox_config_diff/__init__.py index 698e976..2600791 100644 --- a/netbox_config_diff/__init__.py +++ b/netbox_config_diff/__init__.py @@ -8,19 +8,19 @@ __author__ = "Artem Kotik" __email__ = "miaow2@yandex.ru" -__version__ = "2.6.0" +__version__ = "2.7.0" class ConfigDiffConfig(PluginConfig): name = "netbox_config_diff" verbose_name = "NetBox Config Diff Plugin" - description = "Find diff between the intended device configuration and actual." + description = "Find diff and push rendered device configurations from NetBox to devices and apply them." author = __author__ author_email = __email__ version = __version__ base_url = "config-diff" required_settings = ["USERNAME", "PASSWORD"] - min_version = "3.6.0" + min_version = "3.7.0" default_settings = { "USER_SECRET_ROLE": "Username", "PASSWORD_SECRET_ROLE": "Password", diff --git a/netbox_config_diff/api/serializers.py b/netbox_config_diff/api/serializers.py index 43cd638..b1bb802 100644 --- a/netbox_config_diff/api/serializers.py +++ b/netbox_config_diff/api/serializers.py @@ -1,19 +1,23 @@ -from dcim.api.serializers import NestedDeviceSerializer, NestedPlatformSerializer +from dcim.api.nested_serializers import NestedDeviceSerializer, NestedPlatformSerializer from dcim.models import Device from netbox.api.fields import ChoiceField, SerializedPKRelatedField from netbox.api.serializers import NetBoxModelSerializer from netbox.settings import VERSION from rest_framework import serializers from rest_framework.serializers import ValidationError -from users.api.nested_serializers import NestedUserSerializer from netbox_config_diff.choices import ConfigComplianceStatusChoices, ConfigurationRequestStatusChoices from netbox_config_diff.constants import ACCEPTABLE_DRIVERS from netbox_config_diff.models import ConfigCompliance, ConfigurationRequest, PlatformSetting, Substitute if VERSION.startswith("3."): + from users.api.nested_serializers import NestedUserSerializer from utilities.utils import local_now +elif VERSION.startswith("4.0"): + from users.api.nested_serializers import NestedUserSerializer + from utilities.datetime import local_now else: + from users.api.serializers_.nested import NestedUserSerializer from utilities.datetime import local_now diff --git a/netbox_config_diff/configurator/base.py b/netbox_config_diff/configurator/base.py index 1fa70f7..0106ffe 100644 --- a/netbox_config_diff/configurator/base.py +++ b/netbox_config_diff/configurator/base.py @@ -177,11 +177,11 @@ async def _push_one_config(self, device: ConfiguratorDeviceDataClass) -> None: conn = self.connections[device.name] response = await conn.load_config(config=device.rendered_config, replace=True) if response.failed: - await self.abort_config("load", conn, response, device.name) + await self.abort_config("load", conn, response, device) return response = await conn.commit_config() if response.failed: - await self.abort_config("commit", conn, response, device.name) + await self.abort_config("commit", conn, response, device) return self.unprocessed_devices.remove(device) self.processed_devices.add(device) diff --git a/netbox_config_diff/forms/general.py b/netbox_config_diff/forms/general.py index 4aae6d0..ac7cf8e 100644 --- a/netbox_config_diff/forms/general.py +++ b/netbox_config_diff/forms/general.py @@ -21,11 +21,15 @@ from utilities.utils import local_now else: from utilities.datetime import local_now + from utilities.forms.rendering import FieldSet class ConfigComplianceFilterForm(NetBoxModelFilterSetForm): model = ConfigCompliance - fieldsets = ((None, ("q", "device_id", "status")),) + if VERSION.startswith("3."): + fieldsets = ((None, ("q", "device_id", "status")),) + else: + fieldsets = (FieldSet("q", "device_id", "status"),) device_id = DynamicModelMultipleChoiceField( queryset=Device.objects.all(), required=False, @@ -56,7 +60,10 @@ class Meta: class PlatformSettingFilterForm(NetBoxModelFilterSetForm): model = PlatformSetting - fieldsets = ((None, ("q", "platform_id", "tag")),) + if VERSION.startswith("3."): + fieldsets = ((None, ("q", "platform_id", "tag")),) + else: + fieldsets = (FieldSet("q", "filter_id", "tag"),) platform_id = DynamicModelMultipleChoiceField( queryset=Platform.objects.all(), required=False, @@ -84,7 +91,10 @@ class PlatformSettingBulkEditForm(NetBoxModelBulkEditForm): ) model = PlatformSetting - fieldsets = ((None, ("driver", "command", "description", "exclude_regex")),) + if VERSION.startswith("3."): + fieldsets = ((None, ("driver", "command", "description", "exclude_regex")),) + else: + fieldsets = (FieldSet("driver", "command", "description", "exclude_regex"),) nullable_fields = ("description", "exclude_regex") @@ -135,7 +145,10 @@ def clean(self): class ConfigurationRequestFilterForm(NetBoxModelFilterSetForm): model = ConfigurationRequest - fieldsets = ((None, ("q", "created_by_id", "approved_by_id", "scheduled_by_id", "device_id", "status", "tag")),) + if VERSION.startswith("3."): + fieldsets = ((None, ("q", "created_by_id", "approved_by_id", "scheduled_by_id", "device_id", "status", "tag")),) + else: + fieldsets = (FieldSet("q", "created_by_id", "approved_by_id", "scheduled_by_id", "device_id", "status", "tag"),) created_by_id = DynamicModelMultipleChoiceField( queryset=get_user_model().objects.all(), required=False, @@ -201,6 +214,10 @@ class Meta: class SubstituteFilterForm(NetBoxModelFilterSetForm): model = Substitute fieldsets = ((None, ("q", "platform_setting_id", "tag")),) + if VERSION.startswith("3."): + fieldsets = ((None, ("q", "platform_setting_id", "tag")),) + else: + fieldsets = (FieldSet("q", "platform_setting_id", "tag"),) platform_setting_id = DynamicModelMultipleChoiceField( queryset=PlatformSetting.objects.all(), required=False, diff --git a/netbox_config_diff/migrations/0010_create_script.py b/netbox_config_diff/migrations/0010_create_script.py new file mode 100644 index 0000000..fa867c9 --- /dev/null +++ b/netbox_config_diff/migrations/0010_create_script.py @@ -0,0 +1,39 @@ +from django.db import migrations + +from extras.models import Script, ScriptModule +from netbox.settings import VERSION + + +def forward_func(apps, schema_editor): + if VERSION.startswith("3."): + return + + if Script.objects.filter(name="ConfigDiffScript"): + return + + obj = None + for module in ScriptModule.objects.all(): + if module.python_name == "config_diff": + obj = module + break + + if obj: + Script.objects.create(module=obj, name="ConfigDiffScript") + + +def reverse_func(apps, schema_editor): + if VERSION.startswith("3."): + return + + if qs := Script.objects.filter(name="ConfigDiffScript"): + qs.delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("netbox_config_diff", "0009_configcompliance_patch"), + ] + + operations = [ + migrations.RunPython(forward_func, reverse_func), + ] diff --git a/netbox_config_diff/templates/netbox_config_diff/configurationrequest/diffs.html b/netbox_config_diff/templates/netbox_config_diff/configurationrequest/diffs.html index db63d30..a978405 100644 --- a/netbox_config_diff/templates/netbox_config_diff/configurationrequest/diffs.html +++ b/netbox_config_diff/templates/netbox_config_diff/configurationrequest/diffs.html @@ -77,8 +77,8 @@
{{ diff.name }} - No diff
{% endif %} {% else %} -