Skip to content
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
4 changes: 2 additions & 2 deletions .github/workflows/commit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
strategy:
max-parallel: 10
matrix:
python: ["3.10", "3.11", "3.12"]
python: ["3.12"]
steps:
- name: Checkout
uses: actions/checkout@v3
Expand All @@ -31,7 +31,7 @@ jobs:
strategy:
max-parallel: 10
matrix:
netbox_version: ["v4.2.9", "v4.3.7", "v4.2.2"]
netbox_version: ["v4.4.10", "v4.5.2"]
steps:
- name: Checkout
uses: actions/checkout@v3
Expand Down
23 changes: 12 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[![NetBox version](https://img.shields.io/badge/NetBox-4.1|4.2|4.3|4.4-blue.svg)](https://github.com/netbox-community/netbox)
[![NetBox version](https://img.shields.io/badge/NetBox-4.4|4.5-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)
Expand Down Expand Up @@ -44,16 +44,17 @@ This is possible thanks to the scrapli_cfg. Read [Scrapli](https://github.com/sc

## Compatibility

| NetBox Version | Plugin Version |
|----------------|------------------|
| 3.5 | =>0.1.0, <=2.5.0 |
| 3.6 | =>0.1.0, <=2.6.0 |
| 3.7 | =>0.1.0, <=2.8.0 |
| 4.0 | =>2.6.0, <=2.9.0 |
| 4.1 | =>2.7.0 |
| 4.2 | =>2.9.0 |
| 4.3 | =>2.10.0 |
| 4.4 | =>2.11.0 |
| NetBox Version | Plugin Version |
|----------------|--------------------|
| 3.5 | =>0.1.0, <=2.5.0 |
| 3.6 | =>0.1.0, <=2.6.0 |
| 3.7 | =>0.1.0, <=2.8.0 |
| 4.0 | =>2.6.0, <=2.9.0 |
| 4.1 | =>2.7.0, <=2.12.0 |
| 4.2 | =>2.9.0, <=2.12.0 |
| 4.3 | =>2.10.0, <=2.12.0 |
| 4.4 | =>2.11.0 |
| 4.5 | =>2.13.0 |

<!--install-start-->
## Installing
Expand Down
1 change: 0 additions & 1 deletion development/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ services:
- "8000:8000"
depends_on:
- postgres
- worker
- redis

postgres:
Expand Down
5 changes: 3 additions & 2 deletions netbox_config_diff/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

__author__ = "Artem Kotik"
__email__ = "miaow2@yandex.ru"
__version__ = "2.12.0"
__version__ = "2.13.0"


class ConfigDiffConfig(PluginConfig):
Expand All @@ -14,7 +14,8 @@ class ConfigDiffConfig(PluginConfig):
version = __version__
base_url = "config-diff"
required_settings = ["USERNAME", "PASSWORD"]
min_version = "4.1.0"
min_version = "4.4.0"
max_version = "4.5.99"
default_settings = {
"USER_SECRET_ROLE": "Username",
"PASSWORD_SECRET_ROLE": "Password",
Expand Down
24 changes: 24 additions & 0 deletions netbox_config_diff/graphql/base_filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from netbox.settings import VERSION

if VERSION.startswith("4.4"):
from core.graphql.filter_mixins import ChangeLogFilterMixin as ChangeLoggedModelFilter
from netbox.graphql.filter_mixins import NetBoxModelFilterMixin as NetBoxModelFilter
from netbox.graphql.filter_mixins import PrimaryModelFilterMixin as PrimaryModelFilter
elif VERSION.startswith("4.5"):
from netbox.graphql.filters import (
ChangeLoggedModelFilter,
NetBoxModelFilter,
PrimaryModelFilter,
)


class ChangeLoggedGraphQLFilter(ChangeLoggedModelFilter):
pass


class NetBoxGraphQLFilter(NetBoxModelFilter):
pass


class PrimaryNetBoxGraphQLFilter(PrimaryModelFilter):
pass
16 changes: 8 additions & 8 deletions netbox_config_diff/graphql/enums.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# import strawberry
import strawberry

# from netbox_config_diff.choices import ConfigComplianceStatusChoices, ConfigurationRequestStatusChoices
from netbox_config_diff.choices import ConfigComplianceStatusChoices, ConfigurationRequestStatusChoices

# __all__ = (
# "ConfigComplianceStatusEnum",
# "ConfigurationRequestStatusEnum",
# )
__all__ = (
"ConfigComplianceStatusEnum",
"ConfigurationRequestStatusEnum",
)

# ConfigComplianceStatusEnum = strawberry.enum(ConfigComplianceStatusChoices.as_enum())
# ConfigurationRequestStatusEnum = strawberry.enum(ConfigurationRequestStatusChoices.as_enum())
ConfigComplianceStatusEnum = strawberry.enum(ConfigComplianceStatusChoices.as_enum())
ConfigurationRequestStatusEnum = strawberry.enum(ConfigurationRequestStatusChoices.as_enum())
129 changes: 82 additions & 47 deletions netbox_config_diff/graphql/filters.py
Original file line number Diff line number Diff line change
@@ -1,47 +1,82 @@
# TODO: After drop 4.2 support write all graphql filters
# import strawberry_django
# import strawberry
# from strawberry.scalars import ID
# from core.graphql.filter_mixins import BaseFilterMixin, BaseObjectTypeFilterMixin, ChangeLogFilterMixin
# from typing import Annotated, TYPE_CHECKING


# from netbox_config_diff.models import ConfigCompliance, ConfigurationRequest, PlatformSetting, Substitute
# if TYPE_CHECKING:
# from .enums import ConfigComplianceStatusEnum, ConfigurationRequestStatusEnum
# from dcim.graphql.filters import DeviceFilter

# @strawberry_django.filter_type(ConfigCompliance, lookups=True)
# class ConfigComplianceFilter(BaseFilterMixin):
# device = Annotated["DeviceFilter", strawberry.lazy("dcim.graphql.filters")] | None = (
# strawberry_django.filter_field()
# )
# device_id: ID | None = strawberry_django.filter_field()
# status: Annotated["ConfigComplianceStatusEnum", strawberry.lazy("dcim.graphql.enums")] | None = (
# strawberry_django.filter_field()
# )
# diff: strawberry_django.FilterLookup[str] | None = strawberry_django.filter_field()
# error: strawberry_django.FilterLookup[str] | None = strawberry_django.filter_field()
# actual_config: strawberry_django.FilterLookup[str] | None = strawberry_django.filter_field()
# rendered_config: strawberry_django.FilterLookup[str] | None = strawberry_django.filter_field()
# missing: strawberry_django.FilterLookup[str] | None = strawberry_django.filter_field()
# extra: strawberry_django.FilterLookup[str] | None = strawberry_django.filter_field()
# patch: strawberry_django.FilterLookup[str] | None = strawberry_django.filter_field()


# @strawberry_django.filter(ConfigurationRequest, lookups=True)
# @autotype_decorator(ConfigurationRequestFilterSet)
# class ConfigurationRequestFilter(BaseFilterMixin):
# pass


# @strawberry_django.filter(PlatformSetting, lookups=True)
# @autotype_decorator(PlatformSettingFilterSet)
# class PlatformSettingFilter(BaseFilterMixin):
# pass


# @strawberry_django.filter(Substitute, lookups=True)
# @autotype_decorator(SubstituteFilterSet)
# class SubstituteFilter(BaseFilterMixin):
# pass
from datetime import datetime
from typing import TYPE_CHECKING, Annotated

import strawberry
import strawberry_django
from strawberry.scalars import ID
from strawberry_django import DatetimeFilterLookup

from netbox_config_diff.models import ConfigCompliance, ConfigurationRequest, PlatformSetting, Substitute

from .base_filters import ChangeLoggedGraphQLFilter, NetBoxGraphQLFilter, PrimaryNetBoxGraphQLFilter

if TYPE_CHECKING:
from dcim.graphql.filters import DeviceFilter, PlatformFilter
from users.graphql.filters import UserFilter

from .enums import ConfigComplianceStatusEnum, ConfigurationRequestStatusEnum


@strawberry_django.filter_type(ConfigCompliance, lookups=True)
class ConfigComplianceFilter(ChangeLoggedGraphQLFilter):
device: Annotated["DeviceFilter", strawberry.lazy("dcim.graphql.filters")] | None = strawberry_django.filter_field()
device_id: ID | None = strawberry_django.filter_field()
status: Annotated["ConfigComplianceStatusEnum", strawberry.lazy("netbox_config_diff.graphql.enums")] | None = (
strawberry_django.filter_field()
)
diff: strawberry_django.FilterLookup[str] | None = strawberry_django.filter_field()
error: strawberry_django.FilterLookup[str] | None = strawberry_django.filter_field()
actual_config: strawberry_django.FilterLookup[str] | None = strawberry_django.filter_field()
rendered_config: strawberry_django.FilterLookup[str] | None = strawberry_django.filter_field()
missing: strawberry_django.FilterLookup[str] | None = strawberry_django.filter_field()
extra: strawberry_django.FilterLookup[str] | None = strawberry_django.filter_field()
patch: strawberry_django.FilterLookup[str] | None = strawberry_django.filter_field()


@strawberry_django.filter(ConfigurationRequest, lookups=True)
class ConfigurationRequestFilter(PrimaryNetBoxGraphQLFilter):
description: strawberry_django.FilterLookup[str] | None = strawberry_django.filter_field()
status: Annotated["ConfigurationRequestStatusEnum", strawberry.lazy("netbox_config_diff.graphql.enums")] | None = (
strawberry_django.filter_field()
)
devices: Annotated["DeviceFilter", strawberry.lazy("dcim.graphql.filters")] | None = (
strawberry_django.filter_field()
)
created_by: Annotated["UserFilter", strawberry.lazy("users.graphql.filters")] | None = (
strawberry_django.filter_field()
)
created_by_id: ID | None = strawberry_django.filter_field()
approved_by: Annotated["UserFilter", strawberry.lazy("users.graphql.filters")] | None = (
strawberry_django.filter_field()
)
approved_by_id: ID | None = strawberry_django.filter_field()
scheduled_by: Annotated["UserFilter", strawberry.lazy("users.graphql.filters")] | None = (
strawberry_django.filter_field()
)
scheduled_by_id: ID | None = strawberry_django.filter_field()
scheduled: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field()
started: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field()
completed: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field()


@strawberry_django.filter(PlatformSetting, lookups=True)
class PlatformSettingFilter(NetBoxGraphQLFilter):
platform: Annotated["PlatformFilter", strawberry.lazy("dcim.graphql.filters")] | None = (
strawberry_django.filter_field()
)
platform_id: ID | None = strawberry_django.filter_field()
driver: strawberry_django.FilterLookup[str] | None = strawberry_django.filter_field()
description: strawberry_django.FilterLookup[str] | None = strawberry_django.filter_field()
command: strawberry_django.FilterLookup[str] | None = strawberry_django.filter_field()
exclude_regex: strawberry_django.FilterLookup[str] | None = strawberry_django.filter_field()


@strawberry_django.filter(Substitute, lookups=True)
class SubstituteFilter(NetBoxGraphQLFilter):
platform_setting: (
Annotated["PlatformSettingFilter", strawberry.lazy("netbox_config_diff.graphql.filters")] | None
) = strawberry_django.filter_field()
platform_setting_id: ID | None = strawberry_django.filter_field()
name: strawberry_django.FilterLookup[str] | None = strawberry_django.filter_field()
description: strawberry_django.FilterLookup[str] | None = strawberry_django.filter_field()
regexp: strawberry_django.FilterLookup[str] | None = strawberry_django.filter_field()
10 changes: 6 additions & 4 deletions netbox_config_diff/graphql/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@

from netbox_config_diff.models import ConfigCompliance, ConfigurationRequest, PlatformSetting, Substitute

from .filters import ConfigComplianceFilter, ConfigurationRequestFilter, PlatformSettingFilter, SubstituteFilter

@strawberry_django.type(ConfigCompliance, fields="__all__")

@strawberry_django.type(ConfigCompliance, fields="__all__", filters=ConfigComplianceFilter, pagination=True)
class ConfigComplianceType(ObjectType):
device: Annotated["DeviceType", strawberry.lazy("dcim.graphql.types")]
status: str
Expand All @@ -22,7 +24,7 @@ class ConfigComplianceType(ObjectType):
patch: str


@strawberry_django.type(ConfigurationRequest, fields="__all__")
@strawberry_django.type(ConfigurationRequest, fields="__all__", filters=ConfigurationRequestFilter, pagination=True)
class ConfigurationRequestType(NetBoxObjectType):
created_by: Annotated["UserType", strawberry.lazy("users.graphql.types")] | None
approved_by: Annotated["UserType", strawberry.lazy("users.graphql.types")] | None
Expand All @@ -36,7 +38,7 @@ class ConfigurationRequestType(NetBoxObjectType):
completed: str


@strawberry_django.type(PlatformSetting, fields="__all__")
@strawberry_django.type(PlatformSetting, fields="__all__", filters=PlatformSettingFilter, pagination=True)
class PlatformSettingType(NetBoxObjectType):
platform: Annotated["PlatformType", strawberry.lazy("dcim.graphql.types")]
description: str
Expand All @@ -45,7 +47,7 @@ class PlatformSettingType(NetBoxObjectType):
exclude_regex: str


@strawberry_django.type(Substitute, fields="__all__")
@strawberry_django.type(Substitute, fields="__all__", filters=SubstituteFilter, pagination=True)
class SubstituteType(NetBoxObjectType):
platform_setting: Annotated["PlatformSettingType", strawberry.lazy("netbox_config_diff.graphql.types")]
name: str
Expand Down
22 changes: 6 additions & 16 deletions netbox_config_diff/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,19 @@

urlpatterns = (
# Config Compliances
path("config-compliances/", views.ConfigComplianceListView.as_view(), name="configcompliance_list"),
path(
"config-compliances/delete/",
views.ConfigComplianceBulkDeleteView.as_view(),
name="configcompliance_bulk_delete",
),
path("config-compliances/", include(get_model_urls("netbox_config_diff", "configcompliance", detail=False))),
path("config-compliances/<int:pk>/", include(get_model_urls("netbox_config_diff", "configcompliance"))),
# Platform Settings
path("platform-settings/", views.PlatformSettingListView.as_view(), name="platformsetting_list"),
path("platform-settings/add/", views.PlatformSettingEditView.as_view(), name="platformsetting_add"),
path("platform-settings/edit/", views.PlatformSettingBulkEditView.as_view(), name="platformsetting_bulk_edit"),
path(
"platform-settings/delete/", views.PlatformSettingBulkDeleteView.as_view(), name="platformsetting_bulk_delete"
),
path("platform-settings/", include(get_model_urls("netbox_config_diff", "platformsetting", detail=False))),
path("platform-settings/<int:pk>/", include(get_model_urls("netbox_config_diff", "platformsetting"))),
# Configuration Requests
path("configuration-requests/", views.ConfigurationRequestListView.as_view(), name="configurationrequest_list"),
path("configuration-requests/add/", views.ConfigurationRequestEditView.as_view(), name="configurationrequest_add"),
path(
"configuration-requests/", include(get_model_urls("netbox_config_diff", "configurationrequest", detail=False))
),
path("configuration-requests/<int:pk>/", include(get_model_urls("netbox_config_diff", "configurationrequest"))),
# Jobs
path("jobs/", views.JobListView.as_view(), name="configurationrequest_job_list"),
# Configuration Requests
path("substitutes/", views.SubstituteListView.as_view(), name="substitute_list"),
path("substitutes/add/", views.SubstituteEditView.as_view(), name="substitute_add"),
path("substitutes/", include(get_model_urls("netbox_config_diff", "substitute", detail=False))),
path("substitutes/<int:pk>/", include(get_model_urls("netbox_config_diff", "substitute"))),
)
9 changes: 9 additions & 0 deletions netbox_config_diff/views/compliance.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from dcim.models import Device
from django.shortcuts import redirect, render
from django.utils.translation import gettext as _
from netbox.object_actions import AddObject, BulkDelete, BulkEdit, BulkExport
from netbox.views import generic
from utilities.views import ViewTab, register_model_view

Expand Down Expand Up @@ -114,18 +115,21 @@ def get(self, request, **kwargs):
)


@register_model_view(ConfigCompliance, "list", path="", detail=False)
class ConfigComplianceListView(generic.ObjectListView):
queryset = ConfigCompliance.objects.prefetch_related("device")
filterset = ConfigComplianceFilterSet
filterset_form = ConfigComplianceFilterForm
table = ConfigComplianceTable
actions = (BulkDelete, BulkExport)


@register_model_view(ConfigCompliance, "delete")
class ConfigComplianceDeleteView(BaseObjectDeleteView):
queryset = ConfigCompliance.objects.all()


@register_model_view(ConfigCompliance, "bulk_delete", path="delete", detail=False)
class ConfigComplianceBulkDeleteView(generic.BulkDeleteView):
queryset = ConfigCompliance.objects.all()
filterset = ConfigComplianceFilterSet
Expand All @@ -137,13 +141,16 @@ class PlatformSettingView(generic.ObjectView):
queryset = PlatformSetting.objects.all()


@register_model_view(PlatformSetting, "list", path="", detail=False)
class PlatformSettingListView(generic.ObjectListView):
queryset = PlatformSetting.objects.prefetch_related("platform", "tags")
filterset = PlatformSettingFilterSet
filterset_form = PlatformSettingFilterForm
table = PlatformSettingTable
actions = (AddObject, BulkDelete, BulkEdit, BulkExport)


@register_model_view(PlatformSetting, "add", detail=False)
@register_model_view(PlatformSetting, "edit")
class PlatformSettingEditView(BaseObjectEditView):
queryset = PlatformSetting.objects.all()
Expand All @@ -155,13 +162,15 @@ class PlatformSettingDeleteView(BaseObjectDeleteView):
queryset = PlatformSetting.objects.all()


@register_model_view(PlatformSetting, "bulk_edit", path="edit", detail=False)
class PlatformSettingBulkEditView(generic.BulkEditView):
queryset = PlatformSetting.objects.all()
filterset = PlatformSettingFilterSet
table = PlatformSettingTable
form = PlatformSettingBulkEditForm


@register_model_view(PlatformSetting, "bulk_delete", path="delete", detail=False)
class PlatformSettingBulkDeleteView(generic.BulkDeleteView):
queryset = PlatformSetting.objects.all()
filterset = PlatformSettingFilterSet
Expand Down
Loading