From 163338758df764d80d522f7424bff0e783ca82a0 Mon Sep 17 00:00:00 2001 From: Anton Date: Wed, 22 Jan 2025 22:36:57 +0100 Subject: [PATCH] add support for netbox 4.2 (#141) --- .github/workflows/ci.yml | 4 +- validity/__init__.py | 2 +- validity/models/data.py | 7 +++ validity/models/device.py | 4 ++ validity/netbox_changes/__init__.py | 4 +- validity/netbox_changes/current.py | 9 +--- validity/netbox_changes/old.py | 8 +++- validity/netbox_changes/oldest.py | 2 +- validity/template_content.py | 3 ++ validity/tests/conftest.py | 48 +++++++++++++++++++ .../tests/migrations/apply/0001_initial.py | 11 +++++ validity/tests/migrations/apply/__init__.py | 0 .../tests/migrations/dont_apply/__init__.py | 0 validity/views/report.py | 1 - 14 files changed, 87 insertions(+), 16 deletions(-) create mode 100644 validity/tests/migrations/apply/0001_initial.py create mode 100644 validity/tests/migrations/apply/__init__.py create mode 100644 validity/tests/migrations/dont_apply/__init__.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d8ada7..70f7558 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: strategy: fail-fast: false matrix: - netbox_version: [v4.0.11, v4.1.11] + netbox_version: [v4.0.11, v4.1.11, v4.2.2] steps: - name: Checkout uses: actions/checkout@v3 @@ -61,7 +61,7 @@ jobs: strategy: fail-fast: false matrix: - netbox_version: [v4.0.11, v4.1.3] + netbox_version: [v4.0.11, v4.1.11, v4.2.2] steps: - name: Checkout uses: actions/checkout@v3 diff --git a/validity/__init__.py b/validity/__init__.py index ae3cff8..82facc2 100644 --- a/validity/__init__.py +++ b/validity/__init__.py @@ -19,7 +19,7 @@ class NetBoxValidityConfig(PluginConfig): version = "3.0.5" base_url = "validity" django_apps = ["django_bootstrap5"] - min_version = "3.7.0" + min_version = "4.0.0" # custom field netbox_version = NetboxVersion(VERSION) diff --git a/validity/models/data.py b/validity/models/data.py index c14c679..1f43292 100644 --- a/validity/models/data.py +++ b/validity/models/data.py @@ -7,6 +7,7 @@ from core.models import DataFile, DataSource from core.signals import post_sync, pre_sync from django.db.models import Q +from django.urls import reverse from django.utils import timezone from validity.j2_env import Environment @@ -23,6 +24,9 @@ class VDataFile(DataFile): class Meta: proxy = True + def get_absolute_url(self): + return reverse("core:datafile", args=[self.pk]) + class VDataSource(DataSource): objects = VDataSourceQS.as_manager() @@ -132,3 +136,6 @@ def sync_in_migration(self, datafile_model: type): with self._backup_allowed(False): new_paths = self.partial_sync(Q()) datafile_model.objects.exclude(path__in=new_paths).delete() + + def get_absolute_url(self): + return reverse("core:datasource", args=[self.pk]) diff --git a/validity/models/device.py b/validity/models/device.py index 40351aa..5250b3b 100644 --- a/validity/models/device.py +++ b/validity/models/device.py @@ -2,6 +2,7 @@ from typing import TYPE_CHECKING, Optional from dcim.models import Device +from django.urls import reverse from validity.compliance.serialization import Serializable from validity.compliance.state import State @@ -82,3 +83,6 @@ def primary_ip(self): return self.primary_ip4 else: return None + + def get_absolute_url(self): + return reverse("dcim:device", args=[self.pk]) diff --git a/validity/netbox_changes/__init__.py b/validity/netbox_changes/__init__.py index 0243f5b..3965651 100644 --- a/validity/netbox_changes/__init__.py +++ b/validity/netbox_changes/__init__.py @@ -6,9 +6,9 @@ from validity import config -if config.netbox_version >= "4.1.0": +if config.netbox_version >= "4.2.0": from .current import * -elif config.netbox_version >= "4.0.0": +elif config.netbox_version >= "4.1.0": from .old import * else: from .oldest import * diff --git a/validity/netbox_changes/current.py b/validity/netbox_changes/current.py index 0d51a30..2d21ae8 100644 --- a/validity/netbox_changes/current.py +++ b/validity/netbox_changes/current.py @@ -1,9 +1,2 @@ -# NetBox 4.1 -from pydoc import locate as __locate - +# NetBox 4.2 from .old import * - - -enqueue_event = __locate("extras.events.enqueue_event") - -QUEUE_CREATE_ACTION = "object_created" diff --git a/validity/netbox_changes/old.py b/validity/netbox_changes/old.py index 73fa891..5aecfe0 100644 --- a/validity/netbox_changes/old.py +++ b/validity/netbox_changes/old.py @@ -1,3 +1,9 @@ -# NetBox 4.0 +# NetBox 4.1 +from pydoc import locate as __locate from .oldest import * + + +enqueue_event = __locate("extras.events.enqueue_event") + +QUEUE_CREATE_ACTION = "object_created" diff --git a/validity/netbox_changes/oldest.py b/validity/netbox_changes/oldest.py index 0d8f7f9..a7cf124 100644 --- a/validity/netbox_changes/oldest.py +++ b/validity/netbox_changes/oldest.py @@ -1,4 +1,4 @@ -# NetBox 3.7 +# NetBox 4.0 from pydoc import locate as __locate diff --git a/validity/template_content.py b/validity/template_content.py index 99a6acc..ef04a69 100644 --- a/validity/template_content.py +++ b/validity/template_content.py @@ -8,6 +8,7 @@ class PollingInfoExtension(PluginTemplateExtension): + models = ["core.datasource"] model = "core.datasource" def get_polling_info(self, data_file) -> str: @@ -32,6 +33,7 @@ def right_page(self): class DataSourceTenantExtension(PluginTemplateExtension): + models = ["core.datasource"] model = "core.datasource" def right_page(self): @@ -47,6 +49,7 @@ def right_page(self): class ComplianceTestExtension(PluginTemplateExtension): + models = ["validity.compliancetest"] model = "validity.compliancetest" def list_buttons(self): diff --git a/validity/tests/conftest.py b/validity/tests/conftest.py index c26d681..fe7b7aa 100644 --- a/validity/tests/conftest.py +++ b/validity/tests/conftest.py @@ -1,9 +1,14 @@ +from contextlib import contextmanager from pathlib import Path import pytest from core.models import DataSource from dcim.models import Device, DeviceType, Manufacturer +from django.conf import settings from django.contrib.contenttypes.models import ContentType +from django.core.management import call_command +from django.db import connection +from django.test.utils import setup_databases, teardown_databases from django.utils import timezone from extras.models import CustomField from graphene_django.utils.testing import graphql_query @@ -103,3 +108,46 @@ def _now(tz): @pytest.fixture def launcher_factory(di): return di[dependencies.launcher_factory] + + +@contextmanager +def _setup_migrations(use_test_migrations): + class UseTestMigrations: + def __init__(self, getitem_fn): + self.getitem_fn = getitem_fn + + def __contains__(self, item): + return True + + def __getitem__(self, item): + return self.getitem_fn(item) + + if use_test_migrations: + settings.MIGRATION_MODULES = UseTestMigrations( + lambda app: "migrations.apply" if app == "validity" else "migrations.dont_apply" + ) + yield + if use_test_migrations: + settings.MIGRATION_MODULES = UseTestMigrations( + lambda app: None + ) # force creating the tables according to the models + call_command("migrate", interactive=False, database=connection.alias, run_syncdb=True) + settings.MIGRATION_MODULES = {} + + +@pytest.fixture(scope="session") +def django_db_setup(request, django_test_environment, django_db_blocker, django_db_use_migrations): + """ + This is the override of pytest-django native fixture. + It allows create the collation first and only then create the tables (many of netbox models require that collation) + """ + + with django_db_blocker.unblock(), _setup_migrations(use_test_migrations=not django_db_use_migrations): + db_cfg = setup_databases(verbosity=request.config.option.verbose, interactive=False, serialized_aliases=[]) + yield + + with django_db_blocker.unblock(): + try: + teardown_databases(db_cfg, verbosity=request.config.option.verbose) + except Exception as exc: + request.node.warn(pytest.PytestWarning(f"Error when trying to teardown test databases: {exc!r}")) diff --git a/validity/tests/migrations/apply/0001_initial.py b/validity/tests/migrations/apply/0001_initial.py new file mode 100644 index 0000000..4ee1beb --- /dev/null +++ b/validity/tests/migrations/apply/0001_initial.py @@ -0,0 +1,11 @@ +from django.contrib.postgres.operations import CreateCollation +from django.db import migrations +import sys + + +class Migration(migrations.Migration): + dependencies = [] + + operations = [ + CreateCollation('natural_sort', provider='icu', locale='und-u-kn-true'), + ] diff --git a/validity/tests/migrations/apply/__init__.py b/validity/tests/migrations/apply/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/validity/tests/migrations/dont_apply/__init__.py b/validity/tests/migrations/dont_apply/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/validity/views/report.py b/validity/views/report.py index 54fdedb..bcb306f 100644 --- a/validity/views/report.py +++ b/validity/views/report.py @@ -91,7 +91,6 @@ def get_queryset(self) -> QuerySet[models.VDevice]: self.queryset.filter(results__report=self.object) .annotate_result_stats(self.object.pk, severity_ge) .prefetch_results(self.object.pk, severity_ge) - .order_by("_name") ) def get_filterform_initial(self):