Skip to content

Closes #62: Add support for NetBox 4.0 #74

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

Merged
merged 8 commits into from
Jul 14, 2024
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 @@ -24,14 +24,14 @@ jobs:
- name: Run ruff format
run: ruff format .
- name: Run ruff
run: ruff .
run: ruff check .

test:
runs-on: ubuntu-latest
strategy:
max-parallel: 10
matrix:
netbox_version: ["v3.5.9", "v3.6.9", "v3.7.8"]
netbox_version: ["v3.6.9", "v3.7.8", "v4.0.7"]
steps:
- name: Checkout
uses: actions/checkout@v3
Expand Down
2 changes: 1 addition & 1 deletion development/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ ENV PYTHONDONTWRITEBYTECODE 1
ARG NETBOX_VERSION

RUN apt-get update \
&& apt-get install -y --no-install-recommends git \
&& apt-get install -y --no-install-recommends git postgresql-client libpq-dev gcc build-essential \
&& pip install --upgrade pip

# Install NetBox
Expand Down
12 changes: 9 additions & 3 deletions netbox_config_diff/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
from extras.plugins import PluginConfig
from netbox.settings import VERSION

if VERSION.startswith("3."):
from extras.plugins import PluginConfig
else:
from netbox.plugins import PluginConfig


__author__ = "Artem Kotik"
__email__ = "miaow2@yandex.ru"
__version__ = "2.5.0"
__version__ = "2.6.0"


class ConfigDiffConfig(PluginConfig):
Expand All @@ -14,7 +20,7 @@ class ConfigDiffConfig(PluginConfig):
version = __version__
base_url = "config-diff"
required_settings = ["USERNAME", "PASSWORD"]
min_version = "3.5.0"
min_version = "3.6.0"
default_settings = {
"USER_SECRET_ROLE": "Username",
"PASSWORD_SECRET_ROLE": "Password",
Expand Down
8 changes: 7 additions & 1 deletion netbox_config_diff/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,22 @@
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 utilities.utils import local_now

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 utilities.utils import local_now
else:
from utilities.datetime import local_now


# TODO: after droping support for NetBox 3.x, delete nested serializers and add brief_fields
class ConfigComplianceSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name="plugins-api:netbox_config_diff-api:configcompliance-detail")
device = NestedDeviceSerializer()
Expand Down
7 changes: 6 additions & 1 deletion netbox_config_diff/compliance/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,20 @@
from django.db.models import Q
from extras.scripts import MultiObjectVar, ObjectVar, TextVar
from jinja2.exceptions import TemplateError
from netbox.settings import VERSION
from netutils.config.compliance import diff_network_config
from utilities.exceptions import AbortScript
from utilities.utils import render_jinja2

from netbox_config_diff.models import ConplianceDeviceDataClass

from .secrets import SecretsMixin
from .utils import PLATFORM_MAPPING, CustomChoiceVar, exclude_lines, get_remediation_commands, get_unified_diff

if VERSION.startswith("3."):
from utilities.utils import render_jinja2
else:
from utilities.jinja2 import render_jinja2


class ConfigDiffBase(SecretsMixin):
site = ObjectVar(
Expand Down
7 changes: 6 additions & 1 deletion netbox_config_diff/compliance/secrets.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@
from typing import TYPE_CHECKING

from dcim.models import Device
from extras.plugins import get_installed_plugins, get_plugin_config
from netbox.settings import VERSION

if TYPE_CHECKING:
from netbox_secrets.models import Secret

if VERSION.startswith("3."):
from extras.plugins import get_installed_plugins, get_plugin_config
else:
from netbox.plugins import get_installed_plugins, get_plugin_config


class SecretsMixin:
username: str
Expand Down
7 changes: 6 additions & 1 deletion netbox_config_diff/configurator/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
from asgiref.sync import sync_to_async
from dcim.models import Device
from jinja2.exceptions import TemplateError
from netbox.settings import VERSION
from netutils.config.compliance import diff_network_config
from scrapli import AsyncScrapli
from scrapli_cfg.platform.base.async_platform import AsyncScrapliCfgPlatform
from scrapli_cfg.response import ScrapliCfgResponse
from utilities.utils import NetBoxFakeRequest

from netbox_config_diff.compliance.secrets import SecretsMixin
from netbox_config_diff.compliance.utils import PLATFORM_MAPPING, get_remediation_commands, get_unified_diff
Expand All @@ -22,6 +22,11 @@

from .factory import AsyncScrapliCfg

if VERSION.startswith("3."):
from utilities.utils import NetBoxFakeRequest
else:
from utilities.request import NetBoxFakeRequest


class Configurator(SecretsMixin):
def __init__(self, devices: Iterable[Device], request: NetBoxFakeRequest) -> None:
Expand Down
23 changes: 23 additions & 0 deletions netbox_config_diff/forms/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from .general import (
ConfigComplianceFilterForm,
ConfigurationRequestFilterForm,
ConfigurationRequestForm,
ConfigurationRequestScheduleForm,
PlatformSettingBulkEditForm,
PlatformSettingFilterForm,
PlatformSettingForm,
SubstituteFilterForm,
SubstituteForm,
)

__all__ = [
"ConfigComplianceFilterForm",
"ConfigurationRequestFilterForm",
"ConfigurationRequestForm",
"ConfigurationRequestScheduleForm",
"PlatformSettingBulkEditForm",
"PlatformSettingFilterForm",
"PlatformSettingForm",
"SubstituteFilterForm",
"SubstituteForm",
]
12 changes: 12 additions & 0 deletions netbox_config_diff/forms/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from django.forms import ModelForm
from netbox.settings import VERSION

if VERSION.startswith("3."):
from utilities.forms.mixins import BootstrapMixin

class CustomForm(BootstrapMixin, ModelForm):
pass
else:

class CustomForm(ModelForm):
pass
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,25 @@
from django import forms
from django.contrib.auth import get_user_model
from netbox.forms import NetBoxModelBulkEditForm, NetBoxModelFilterSetForm, NetBoxModelForm
from netbox.settings import VERSION
from utilities.forms.fields import (
DynamicModelChoiceField,
DynamicModelMultipleChoiceField,
TagFilterField,
)
from utilities.forms.mixins import BootstrapMixin
from utilities.forms.widgets import DateTimePicker
from utilities.utils import local_now

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

from .base import CustomForm

if VERSION.startswith("3."):
from utilities.utils import local_now
else:
from utilities.datetime import local_now


class ConfigComplianceFilterForm(NetBoxModelFilterSetForm):
model = ConfigCompliance
Expand Down Expand Up @@ -157,7 +163,7 @@ class ConfigurationRequestFilterForm(NetBoxModelFilterSetForm):
tag = TagFilterField(model)


class ConfigurationRequestScheduleForm(BootstrapMixin, forms.ModelForm):
class ConfigurationRequestScheduleForm(CustomForm):
scheduled = forms.DateTimeField(
widget=DateTimePicker(),
label="Schedule at",
Expand Down
6 changes: 6 additions & 0 deletions netbox_config_diff/graphql/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from netbox.settings import VERSION

if VERSION.startswith("3."):
from .old.schema import schema # noqa
else:
from .new.schema import schema # noqa
Empty file.
34 changes: 34 additions & 0 deletions netbox_config_diff/graphql/new/filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import strawberry_django
from netbox.graphql.filter_mixins import BaseFilterMixin, autotype_decorator

from netbox_config_diff.filtersets import (
ConfigComplianceFilterSet,
ConfigurationRequestFilterSet,
PlatformSettingFilterSet,
SubstituteFilterSet,
)
from netbox_config_diff.models import ConfigCompliance, ConfigurationRequest, PlatformSetting, Substitute


@strawberry_django.filter(ConfigCompliance, lookups=True)
@autotype_decorator(ConfigComplianceFilterSet)
class ConfigComplianceFilter(BaseFilterMixin):
pass


@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
36 changes: 36 additions & 0 deletions netbox_config_diff/graphql/new/schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import strawberry
import strawberry_django

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

from .types import ConfigComplianceType, ConfigurationRequestType, PlatformSettingType, SubstituteType


@strawberry.type
class NetBoxConfigDiffQuery:
@strawberry.field
def config_compliance(self, id: int) -> ConfigComplianceType:
return ConfigCompliance.objects.get(pk=id)

config_compliance_list: list[ConfigComplianceType] = strawberry_django.field()

@strawberry.field
def configuration_request(self, id: int) -> ConfigurationRequestType:
return ConfigurationRequest.objects.get(pk=id)

configuration_request_list: list[ConfigurationRequestType] = strawberry_django.field()

@strawberry.field
def platform_setting(self, id: int) -> PlatformSettingType:
return PlatformSetting.objects.get(pk=id)

platform_setting_list: list[PlatformSettingType] = strawberry_django.field()

@strawberry.field
def substitute(self, id: int) -> SubstituteType:
return Substitute.objects.get(pk=id)

substitute_list: list[SubstituteType] = strawberry_django.field()


schema = [NetBoxConfigDiffQuery]
55 changes: 55 additions & 0 deletions netbox_config_diff/graphql/new/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from typing import Annotated

import strawberry
import strawberry_django
from dcim.graphql.types import DeviceType, PlatformType
from netbox.graphql.types import NetBoxObjectType, ObjectType
from users.graphql.types import UserType

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

from .filters import ConfigComplianceFilter, ConfigurationRequestFilter, PlatformSettingFilter, SubstituteFilter


@strawberry_django.type(ConfigCompliance, fields="__all__", filters=ConfigComplianceFilter)
class ConfigComplianceType(ObjectType):
device: Annotated["DeviceType", strawberry.lazy("dcim.graphql.types")]
status: str
diff: str
error: str
actual_config: str
rendered_config: str
missing: str
extra: str
patch: str


@strawberry_django.type(ConfigurationRequest, fields="__all__", filters=ConfigurationRequestFilter)
class ConfigurationRequestType(NetBoxObjectType):
created_by: Annotated["UserType", strawberry.lazy("users.graphql.types")] | None
approved_by: Annotated["UserType", strawberry.lazy("users.graphql.types")] | None
scheduled_by: Annotated["UserType", strawberry.lazy("users.graphql.types")] | None
status: str
devices: list[Annotated["DeviceType", strawberry.lazy("dcim.graphql.types")]]
description: str
comments: str
scheduled: str
started: str
completed: str


@strawberry_django.type(PlatformSetting, fields="__all__", filters=PlatformSettingFilter)
class PlatformSettingType(NetBoxObjectType):
platform: Annotated["PlatformType", strawberry.lazy("dcim.graphql.types")]
description: str
driver: str
command: str
exclude_regex: str


@strawberry_django.type(Substitute, fields="__all__", filters=SubstituteFilter)
class SubstituteType(NetBoxObjectType):
platform_setting: Annotated["PlatformSettingType", strawberry.lazy("netbox_config_diff.graphql.new.types")]
name: str
description: str
regexp: str
Empty file.
File renamed without changes.
8 changes: 7 additions & 1 deletion netbox_config_diff/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@

from core.choices import JobStatusChoices
from core.models import Job
from utilities.utils import NetBoxFakeRequest
from netbox.settings import VERSION

from netbox_config_diff.choices import ConfigurationRequestStatusChoices
from netbox_config_diff.configurator.base import Configurator
from netbox_config_diff.models import ConfigurationRequest

if VERSION.startswith("3."):
from utilities.utils import NetBoxFakeRequest
else:
from utilities.request import NetBoxFakeRequest


logger = logging.getLogger(__name__)


Expand Down
7 changes: 6 additions & 1 deletion netbox_config_diff/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,19 @@
from netbox.constants import RQ_QUEUE_DEFAULT
from netbox.models import NetBoxModel, PrimaryModel
from netbox.models.features import ChangeLoggingMixin, JobsMixin
from netbox.settings import VERSION
from rq.exceptions import InvalidJobOperation
from utilities.querysets import RestrictedQuerySet
from utilities.utils import copy_safe_request

from netbox_config_diff.choices import ConfigComplianceStatusChoices, ConfigurationRequestStatusChoices

from .base import AbsoluteURLMixin

if VERSION.startswith("3."):
from utilities.utils import copy_safe_request
else:
from utilities.request import copy_safe_request


class ConfigCompliance(AbsoluteURLMixin, ChangeLoggingMixin, models.Model):
device = models.OneToOneField(
Expand Down
Loading
Loading