diff --git a/.github/workflows/commit.yaml b/.github/workflows/commit.yaml index 02a5a42..2c1815a 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", "4.1.1"] steps: - name: Checkout uses: actions/checkout@v3 diff --git a/README.md b/README.md index d025e33..47ad1cc 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 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..761c93a 100644 --- a/netbox_config_diff/api/serializers.py +++ b/netbox_config_diff/api/serializers.py @@ -1,4 +1,4 @@ -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 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 %} -