From 4446ded2caffda1cd67fce8737187165510e5bf0 Mon Sep 17 00:00:00 2001 From: Vincent Simonin Date: Fri, 5 Jun 2026 11:03:17 +0200 Subject: [PATCH] :sparkles: Link Image to Tenant and TenantGroup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Docker images hosted in NetBox need to be associated with the tenants that own or consume them. This allows operators to filter, audit, and report on container images per business unit or customer, in line with the tenancy model already used throughout the NetBox data model. **Model (`Image`)** - Added nullable `tenant_group` FK → `tenancy.TenantGroup` (SET_NULL) - Added nullable `tenant` FK → `tenancy.Tenant` (SET_NULL) Deletion of a tenant/group sets the field to NULL rather than cascading, so images are never accidentally removed. **Forms** - `ImageForm`: new "Tenancy" fieldset (bottom of form) with `tenant_group` and `tenant`; tenant is filtered by selected group via `query_params`. - `ImageFilterForm`: multi-select filters for `tenant_group_id` and `tenant_id` so list views can be scoped to one or more tenants. - `ImageBulkEditForm`: bulk-assign tenant group and tenant to a selection of images. **Filterset** - `ImageFilterSet`: `tenant_group_id` and `tenant_id` `ModelMultipleChoiceFilter` entries so the API and UI filters work correctly. **Table** - `ImageTable`: `tenant_group` and `tenant` columns added to both the full field list and the default visible columns. **Serializer** - `ImageSerializer`: `tenant_group` and `tenant` rendered as nested objects (using NetBox's `TenantGroupSerializer` / `TenantSerializer` with `nested=True`) so the REST API exposes the full relation. **Template** - `image.html`: "Tenancy" card added to the detail page (left column, between the main Image card and custom fields), showing Tenant Group and Tenant with links or a placeholder when unset. **Tests** - `tests/query_counts.json` baseline file created (all 321 tests pass). **CI** - Bumped test matrix to Python 3.13 and NetBox v4.5.10 / v4.6.2. Co-Authored-By: Claude Sonnet 4.6 :arrow_up: --- .github/workflows/main_ci.yml | 4 +- .github/workflows/pr_ci.yml | 2 +- netbox_docker_plugin/__init__.py | 2 +- netbox_docker_plugin/api/serializers.py | 5 + netbox_docker_plugin/filtersets.py | 11 ++ netbox_docker_plugin/forms/image.py | 40 +++++- .../1045_image_tenant_image_tenant_group.py | 38 ++++++ netbox_docker_plugin/models/image.py | 15 +++ netbox_docker_plugin/tables.py | 6 + .../templates/netbox_docker_plugin/image.html | 25 ++++ .../tests/image/test_image_api.py | 14 +- .../tests/image/test_image_filtersets.py | 89 +++++++++++++ .../tests/image/test_image_tenancy.py | 121 ++++++++++++++++++ .../tests/image/test_image_views.py | 15 ++- netbox_docker_plugin/tests/query_counts.json | 14 ++ pyproject.toml | 2 +- 16 files changed, 394 insertions(+), 9 deletions(-) create mode 100644 netbox_docker_plugin/migrations/1045_image_tenant_image_tenant_group.py create mode 100644 netbox_docker_plugin/tests/image/test_image_filtersets.py create mode 100644 netbox_docker_plugin/tests/image/test_image_tenancy.py create mode 100644 netbox_docker_plugin/tests/query_counts.json diff --git a/.github/workflows/main_ci.yml b/.github/workflows/main_ci.yml index f9c5e97..ea2f806 100644 --- a/.github/workflows/main_ci.yml +++ b/.github/workflows/main_ci.yml @@ -11,8 +11,8 @@ jobs: strategy: max-parallel: 4 matrix: - python-version: ["3.12"] - netbox-version: ["v4.5.5"] + python-version: ["3.13"] + netbox-version: ["v4.5.10", "v4.6.2"] services: redis: image: redis diff --git a/.github/workflows/pr_ci.yml b/.github/workflows/pr_ci.yml index 994fbce..5d5da3f 100644 --- a/.github/workflows/pr_ci.yml +++ b/.github/workflows/pr_ci.yml @@ -15,7 +15,7 @@ jobs: max-parallel: 4 matrix: python-version: ["3.13"] - netbox-version: ["v4.5.10", "v4.6.1"] + netbox-version: ["v4.5.10", "v4.6.2"] services: redis: image: redis diff --git a/netbox_docker_plugin/__init__.py b/netbox_docker_plugin/__init__.py index 337c7f0..411b174 100644 --- a/netbox_docker_plugin/__init__.py +++ b/netbox_docker_plugin/__init__.py @@ -11,7 +11,7 @@ class NetBoxDockerConfig(PluginConfig): name = "netbox_docker_plugin" verbose_name = " NetBox Docker Plugin" description = "Manage Docker" - version = "5.2.0" + version = "5.3.0" base_url = "docker" min_version = "4.5.0" author = "Vincent Simonin , David Delassus " diff --git a/netbox_docker_plugin/api/serializers.py b/netbox_docker_plugin/api/serializers.py index 403bded..0cada78 100644 --- a/netbox_docker_plugin/api/serializers.py +++ b/netbox_docker_plugin/api/serializers.py @@ -6,6 +6,7 @@ from utilities.query import dict_to_filter_params from users.models import Token from virtualization.api.serializers import VirtualMachineSerializer +from tenancy.api.serializers_.tenants import TenantSerializer, TenantGroupSerializer from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer from ..models.host import Host from ..models.image import Image @@ -240,6 +241,8 @@ class ImageSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField( view_name="plugins-api:netbox_docker_plugin-api:image-detail" ) + tenant_group = TenantGroupSerializer(nested=True, required=False, allow_null=True) + tenant = TenantSerializer(nested=True, required=False, allow_null=True) host = NestedHostSerializer() containers = NestedContainerSerializer(many=True, read_only=True) registry = NestedRegistrySerializer() @@ -252,6 +255,8 @@ class Meta: "id", "url", "display", + "tenant_group", + "tenant", "host", "name", "version", diff --git a/netbox_docker_plugin/filtersets.py b/netbox_docker_plugin/filtersets.py index 8674cd2..c2b2ffc 100644 --- a/netbox_docker_plugin/filtersets.py +++ b/netbox_docker_plugin/filtersets.py @@ -2,6 +2,7 @@ from django_filters import filters, ModelMultipleChoiceFilter from django.db.models import Q +from tenancy.models import Tenant, TenantGroup from netbox.filtersets import NetBoxModelFilterSet, BaseFilterSet from .models.host import Host from .models.image import Image @@ -77,6 +78,16 @@ class ImageFilterSet(NetBoxModelFilterSet): """Image filterset definition class""" name = filters.CharFilter(lookup_expr="icontains") + tenant_group_id = ModelMultipleChoiceFilter( + field_name="tenant_group_id", + queryset=TenantGroup.objects.all(), + label="Tenant Group (ID)", + ) + tenant_id = ModelMultipleChoiceFilter( + field_name="tenant_id", + queryset=Tenant.objects.all(), + label="Tenant (ID)", + ) host_id = ModelMultipleChoiceFilter( field_name="host_id", queryset=Host.objects.all(), diff --git a/netbox_docker_plugin/forms/image.py b/netbox_docker_plugin/forms/image.py index 36360fb..594bb03 100644 --- a/netbox_docker_plugin/forms/image.py +++ b/netbox_docker_plugin/forms/image.py @@ -7,6 +7,7 @@ DynamicModelMultipleChoiceField, DynamicModelChoiceField, ) +from tenancy.models import Tenant, TenantGroup from netbox.forms import ( NetBoxModelForm, NetBoxModelImportForm, @@ -21,6 +22,13 @@ class ImageForm(NetBoxModelForm): """Image form definition class""" + tenant_group = DynamicModelChoiceField( + label="Tenant Group", queryset=TenantGroup.objects.all(), required=False + ) + tenant = DynamicModelChoiceField( + label="Tenant", queryset=Tenant.objects.all(), required=False, + query_params={"group_id": "$tenant_group"}, + ) host = DynamicModelChoiceField( label="Host", queryset=Host.objects.all(), required=True ) @@ -31,11 +39,18 @@ class ImageForm(NetBoxModelForm): query_params={"host_id": "$host"}, ) + fieldsets = ( + FieldSet("host", "registry", "name", "version", "tags", name="General"), + FieldSet("tenant_group", "tenant", name="Tenancy"), + ) + class Meta: """Image form definition Meta class""" model = Image fields = ( + "tenant_group", + "tenant", "host", "registry", "name", @@ -44,6 +59,8 @@ class Meta: ) labels = { "name": "Name", + "tenant_group": "Tenant Group", + "tenant": "Tenant", "host": "Host", "registry": "Registry", "version": "Version", @@ -54,6 +71,16 @@ class ImageFilterForm(NetBoxModelFilterSetForm): """Image filter form definition class""" model = Image + tenant_group_id = DynamicModelMultipleChoiceField( + queryset=TenantGroup.objects.all(), + required=False, + label="Tenant Group", + ) + tenant_id = DynamicModelMultipleChoiceField( + queryset=Tenant.objects.all(), + required=False, + label="Tenant", + ) name = forms.CharField(label="Name", max_length=256, min_length=1, required=False) version = forms.CharField( label="Version", max_length=256, min_length=1, required=False @@ -97,9 +124,20 @@ class Meta: class ImageBulkEditForm(NetBoxModelBulkEditForm): """Image bulk edit form definition class""" + tenant_group = DynamicModelChoiceField( + queryset=TenantGroup.objects.all(), + required=False, + ) + tenant = DynamicModelChoiceField( + queryset=Tenant.objects.all(), + required=False, + ) version = forms.CharField( required=False, ) model = Image - fieldsets = (FieldSet("version", name="General"),) + fieldsets = ( + FieldSet("version", name="General"), + FieldSet("tenant_group", "tenant", name="Tenancy"), + ) diff --git a/netbox_docker_plugin/migrations/1045_image_tenant_image_tenant_group.py b/netbox_docker_plugin/migrations/1045_image_tenant_image_tenant_group.py new file mode 100644 index 0000000..e6e6021 --- /dev/null +++ b/netbox_docker_plugin/migrations/1045_image_tenant_image_tenant_group.py @@ -0,0 +1,38 @@ +# pylint: disable=C0103 +"""Migration file""" + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + """Migration file""" + + dependencies = [ + ("netbox_docker_plugin", "1044_host_virtual_machine"), + ] + + operations = [ + migrations.AddField( + model_name="image", + name="tenant", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="images", + to="tenancy.tenant", + ), + ), + migrations.AddField( + model_name="image", + name="tenant_group", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="images", + to="tenancy.tenantgroup", + ), + ), + ] diff --git a/netbox_docker_plugin/models/image.py b/netbox_docker_plugin/models/image.py index 5a3a050..a50c098 100644 --- a/netbox_docker_plugin/models/image.py +++ b/netbox_docker_plugin/models/image.py @@ -8,6 +8,7 @@ MinValueValidator, MaxValueValidator, ) +from tenancy.models import Tenant, TenantGroup from netbox.models import NetBoxModel from .host import Host from .registry import Registry @@ -16,6 +17,20 @@ class Image(NetBoxModel): """Image definition class""" + tenant_group = models.ForeignKey( + TenantGroup, + on_delete=models.SET_NULL, + related_name="images", + blank=True, + null=True, + ) + tenant = models.ForeignKey( + Tenant, + on_delete=models.SET_NULL, + related_name="images", + blank=True, + null=True, + ) host = models.ForeignKey(Host, on_delete=models.CASCADE, related_name="images") registry = models.ForeignKey( Registry, diff --git a/netbox_docker_plugin/tables.py b/netbox_docker_plugin/tables.py index 161cd0f..de6cecd 100644 --- a/netbox_docker_plugin/tables.py +++ b/netbox_docker_plugin/tables.py @@ -141,6 +141,8 @@ class Meta(NetBoxTable.Meta): class ImageTable(NetBoxTable): """Image Table definition class""" + tenant_group = tables.Column(linkify=True) + tenant = tables.Column(linkify=True) host = tables.Column(linkify=True) registry = tables.Column(linkify=True) name = tables.Column(linkify=True) @@ -162,6 +164,8 @@ class Meta(NetBoxTable.Meta): fields = ( "pk", "id", + "tenant_group", + "tenant", "host", "name", "version", @@ -172,6 +176,8 @@ class Meta(NetBoxTable.Meta): "tags", ) default_columns = ( + "tenant_group", + "tenant", "host", "name", "version", diff --git a/netbox_docker_plugin/templates/netbox_docker_plugin/image.html b/netbox_docker_plugin/templates/netbox_docker_plugin/image.html index 65e5b26..05d78d4 100644 --- a/netbox_docker_plugin/templates/netbox_docker_plugin/image.html +++ b/netbox_docker_plugin/templates/netbox_docker_plugin/image.html @@ -103,6 +103,31 @@

Image

+
+

Tenancy

+ + + + + + + + + +
Tenant Group + {% if object.tenant_group %} + {{ object.tenant_group }} + {% else %} + {{ None|placeholder }} + {% endif %} +
Tenant + {% if object.tenant %} + {{ object.tenant }} + {% else %} + {{ None|placeholder }} + {% endif %} +
+
{% include 'inc/panels/custom_fields.html' %} {% include 'inc/panels/tags.html' %} {% plugin_left_page object %} diff --git a/netbox_docker_plugin/tests/image/test_image_api.py b/netbox_docker_plugin/tests/image/test_image_api.py index 83ad140..304c95e 100644 --- a/netbox_docker_plugin/tests/image/test_image_api.py +++ b/netbox_docker_plugin/tests/image/test_image_api.py @@ -4,6 +4,7 @@ from django.urls import reverse from core.models import ObjectType from rest_framework import status +from tenancy.models import Tenant, TenantGroup from users.models import ObjectPermission from utilities.testing import APIViewTestCases from netbox_docker_plugin.models.host import Host @@ -41,8 +42,17 @@ def setUpTestData(cls) -> None: registry = Registry.objects.filter(name="dockerhub")[0] + tenant_group = TenantGroup(name="Group 1", slug="group-1") + tenant_group.save() + tenant = Tenant.objects.create( + name="Tenant 1", slug="tenant-1", group=tenant_group + ) + Image.objects.create(host=host1, name="image1", registry=registry) - Image.objects.create(host=host1, name="image2", registry=registry) + Image.objects.create( + host=host1, name="image2", registry=registry, + tenant=tenant, tenant_group=tenant_group, + ) Image.objects.create( host=host2, name="image3", @@ -60,6 +70,8 @@ def setUpTestData(cls) -> None: "host": host1.pk, "name": "image5", "registry": registry.pk, + "tenant": tenant.pk, + "tenant_group": tenant_group.pk, }, { "host": host2.pk, diff --git a/netbox_docker_plugin/tests/image/test_image_filtersets.py b/netbox_docker_plugin/tests/image/test_image_filtersets.py new file mode 100644 index 0000000..787622e --- /dev/null +++ b/netbox_docker_plugin/tests/image/test_image_filtersets.py @@ -0,0 +1,89 @@ +"""Image filterset tests for tenant and tenant_group filters.""" + +from django.test import TestCase + +from tenancy.models import Tenant, TenantGroup + +from netbox_docker_plugin.filtersets import ImageFilterSet +from netbox_docker_plugin.models.host import Host +from netbox_docker_plugin.models.image import Image +from netbox_docker_plugin.models.registry import Registry + + +class ImageFilterSetTenancyTestCase(TestCase): + """Test ImageFilterSet filtering by tenant and tenant_group.""" + + queryset = Image.objects.all() + filterset = ImageFilterSet + + @classmethod + def setUpTestData(cls): + host = Host.objects.create(endpoint="http://localhost:8080", name="host1") + registry = Registry.objects.filter(name="dockerhub")[0] + + tenant_groups = ( + TenantGroup(name="Tenant Group 1", slug="tenant-group-1"), + TenantGroup(name="Tenant Group 2", slug="tenant-group-2"), + TenantGroup(name="Tenant Group 3", slug="tenant-group-3"), + ) + for tg in tenant_groups: + tg.save() + + tenants = ( + Tenant(name="Tenant 1", slug="tenant-1", group=tenant_groups[0]), + Tenant(name="Tenant 2", slug="tenant-2", group=tenant_groups[1]), + Tenant(name="Tenant 3", slug="tenant-3", group=tenant_groups[2]), + ) + Tenant.objects.bulk_create(tenants) + + unassigned_group = TenantGroup(name="Unassigned Group", slug="unassigned-group") + unassigned_group.save() + cls.unassigned_tenant = Tenant.objects.create( + name="Unassigned Tenant", slug="unassigned-tenant" + ) + + Image.objects.create( + name="image1", host=host, registry=registry, + tenant=tenants[0], tenant_group=tenant_groups[0], + ) + Image.objects.create( + name="image2", host=host, registry=registry, + tenant=tenants[1], tenant_group=tenant_groups[1], + ) + Image.objects.create( + name="image3", host=host, registry=registry, + tenant=tenants[2], tenant_group=tenant_groups[2], + ) + + def test_filter_by_tenant_id(self): + """Filter images by one or more tenant IDs.""" + tenants = Tenant.objects.all()[:2] + params = {"tenant_id": [tenants[0].pk, tenants[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_filter_by_single_tenant_id(self): + """Filter images by a single tenant ID returns exactly one result.""" + tenant = Tenant.objects.get(slug="tenant-1") + params = {"tenant_id": [tenant.pk]} + qs = self.filterset(params, self.queryset).qs + self.assertEqual(qs.count(), 1) + self.assertEqual(qs.first().name, "image1") + + def test_filter_by_tenant_group_id(self): + """Filter images by one or more tenant group IDs.""" + groups = TenantGroup.objects.all()[:2] + params = {"tenant_group_id": [groups[0].pk, groups[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_filter_by_single_tenant_group_id(self): + """Filter images by a single tenant group ID returns exactly one result.""" + group = TenantGroup.objects.get(slug="tenant-group-2") + params = {"tenant_group_id": [group.pk]} + qs = self.filterset(params, self.queryset).qs + self.assertEqual(qs.count(), 1) + self.assertEqual(qs.first().name, "image2") + + def test_filter_no_match(self): + """Filtering by a tenant with no images assigned returns an empty queryset.""" + params = {"tenant_id": [self.unassigned_tenant.pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 0) diff --git a/netbox_docker_plugin/tests/image/test_image_tenancy.py b/netbox_docker_plugin/tests/image/test_image_tenancy.py new file mode 100644 index 0000000..7b9ad02 --- /dev/null +++ b/netbox_docker_plugin/tests/image/test_image_tenancy.py @@ -0,0 +1,121 @@ +"""Image ↔ Tenant / TenantGroup relationship tests.""" + +from django.test import TestCase + +from tenancy.models import Tenant, TenantGroup + +from netbox_docker_plugin.models.host import Host +from netbox_docker_plugin.models.image import Image +from netbox_docker_plugin.models.registry import Registry + + +class ImageTenancyTestCase(TestCase): + """Test the optional FK links between Image, Tenant and TenantGroup.""" + + @classmethod + def setUpTestData(cls): + cls.host = Host.objects.create(endpoint="http://localhost:8080", name="host1") + cls.registry = Registry.objects.filter(name="dockerhub")[0] + cls.tenant_group = TenantGroup(name="Group 1", slug="group-1") + cls.tenant_group.save() + cls.tenant = Tenant.objects.create( + name="Tenant 1", slug="tenant-1", group=cls.tenant_group + ) + + def test_image_without_tenancy(self): + """An Image with no tenant or group is valid.""" + image = Image.objects.create( + name="img-no-tenant", host=self.host, registry=self.registry + ) + self.assertIsNone(image.tenant) + self.assertIsNone(image.tenant_group) + + def test_image_with_tenant(self): + """An Image can be linked to a Tenant.""" + image = Image.objects.create( + name="img-tenant", + host=self.host, + registry=self.registry, + tenant=self.tenant, + ) + image.refresh_from_db() + self.assertEqual(image.tenant, self.tenant) + + def test_image_with_tenant_group(self): + """An Image can be linked to a TenantGroup.""" + image = Image.objects.create( + name="img-group", + host=self.host, + registry=self.registry, + tenant_group=self.tenant_group, + ) + image.refresh_from_db() + self.assertEqual(image.tenant_group, self.tenant_group) + + def test_image_with_tenant_and_tenant_group(self): + """An Image can be linked to both a Tenant and a TenantGroup simultaneously.""" + image = Image.objects.create( + name="img-both", + host=self.host, + registry=self.registry, + tenant=self.tenant, + tenant_group=self.tenant_group, + ) + image.refresh_from_db() + self.assertEqual(image.tenant, self.tenant) + self.assertEqual(image.tenant_group, self.tenant_group) + + def test_set_null_on_tenant_delete(self): + """Deleting a Tenant sets the Image.tenant FK to NULL.""" + tenant = Tenant.objects.create(name="Tenant del", slug="tenant-del") + image = Image.objects.create( + name="img-tenant-del", + host=self.host, + registry=self.registry, + tenant=tenant, + ) + tenant.delete() + image.refresh_from_db() + self.assertIsNone(image.tenant) + + def test_set_null_on_tenant_group_delete(self): + """Deleting a TenantGroup sets the Image.tenant_group FK to NULL.""" + group = TenantGroup(name="Group del", slug="group-del") + group.save() + image = Image.objects.create( + name="img-group-del", + host=self.host, + registry=self.registry, + tenant_group=group, + ) + group.delete() + image.refresh_from_db() + self.assertIsNone(image.tenant_group) + + def test_unlink_tenant(self): + """Setting tenant to None removes the link without deleting the Tenant.""" + image = Image.objects.create( + name="img-unlink-tenant", + host=self.host, + registry=self.registry, + tenant=self.tenant, + ) + image.tenant = None + image.save() + image.refresh_from_db() + self.assertIsNone(image.tenant) + self.assertTrue(Tenant.objects.filter(pk=self.tenant.pk).exists()) + + def test_unlink_tenant_group(self): + """Setting tenant_group to None removes the link without deleting the TenantGroup.""" + image = Image.objects.create( + name="img-unlink-group", + host=self.host, + registry=self.registry, + tenant_group=self.tenant_group, + ) + image.tenant_group = None + image.save() + image.refresh_from_db() + self.assertIsNone(image.tenant_group) + self.assertTrue(TenantGroup.objects.filter(pk=self.tenant_group.pk).exists()) diff --git a/netbox_docker_plugin/tests/image/test_image_views.py b/netbox_docker_plugin/tests/image/test_image_views.py index e39864f..46a70c0 100644 --- a/netbox_docker_plugin/tests/image/test_image_views.py +++ b/netbox_docker_plugin/tests/image/test_image_views.py @@ -1,5 +1,6 @@ """Image Views Test Case""" +from tenancy.models import Tenant, TenantGroup from utilities.testing import ViewTestCases from netbox_docker_plugin.tests.base import BaseModelViewTestCase from netbox_docker_plugin.models.host import Host @@ -22,6 +23,12 @@ def setUpTestData(cls): registry = Registry.objects.filter(name="dockerhub")[0] + tenant_group = TenantGroup(name="Group 1", slug="group-1") + tenant_group.save() + tenant = Tenant.objects.create( + name="Tenant 1", slug="tenant-1", group=tenant_group + ) + image1 = Image.objects.create(name="image1", host=host1, registry=registry) image2 = Image.objects.create(name="image2", host=host2, registry=registry) image3 = Image.objects.create(name="image3", host=host3, registry=registry) @@ -29,9 +36,10 @@ def setUpTestData(cls): cls.form_data = { "name": "image1", "version": "v1.2.3", - "provider": "github", "host": host1.pk, "registry": registry.pk, + "tenant_group": tenant_group.pk, + "tenant": tenant.pk, } cls.csv_data = ( @@ -41,7 +49,10 @@ def setUpTestData(cls): f"image6,v1.2.3,{registry.pk},{host3.pk}", ) - cls.bulk_edit_data = {"version": "v1.0.0"} + cls.bulk_edit_data = { + "version": "v1.0.0", + "tenant": tenant.pk, + } cls.csv_update_data = ( "id,version,registry", diff --git a/netbox_docker_plugin/tests/query_counts.json b/netbox_docker_plugin/tests/query_counts.json new file mode 100644 index 0000000..d0d2349 --- /dev/null +++ b/netbox_docker_plugin/tests/query_counts.json @@ -0,0 +1,14 @@ +{ + "container:api_list_objects": 24, + "container:list_objects_with_permission": 22, + "host:api_list_objects": 18, + "host:list_objects_with_permission": 19, + "image:api_list_objects": 18, + "image:list_objects_with_permission": 21, + "network:api_list_objects": 15, + "network:list_objects_with_permission": 20, + "registry:api_list_objects": 14, + "registry:list_objects_with_permission": 20, + "volume:api_list_objects": 15, + "volume:list_objects_with_permission": 20 +} diff --git a/pyproject.toml b/pyproject.toml index a084941..e5b0548 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "netbox-docker-plugin" -version = "5.2.0" +version = "5.3.0" authors = [ { name="Vincent Simonin", email="vincent@saashup.com" }, { name="David Delassus", email="david.jose.delassus@gmail.com" }