Version bump to NetBox v4.6.3 (#82)

* Add support for NetBox 4.6
* Test NetBox v4.6.3 and Python 3.14

Signed-off-by: wkoot <3715211+wkoot@users.noreply.github.com>
Co-authored-by: Artem Kotik <artem.i.kotik@ringcentral.com>
This commit is contained in:
wkoot
2026-06-25 18:06:27 +02:00
committed by GitHub
co-authored by Artem Kotik
parent 811490e429
commit f84a6bde2a
16 changed files with 69 additions and 70 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
fetch-depth: 0 fetch-depth: 0
persist-credentials: false persist-credentials: false
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
vale: vale:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
persist-credentials: false persist-credentials: false
+2 -1
View File
@@ -19,8 +19,9 @@ jobs:
- python-version: 3.11 - python-version: 3.11
- python-version: 3.12 - python-version: 3.12
- python-version: 3.13 - python-version: 3.13
- python-version: 3.14
steps: steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
persist-credentials: false persist-credentials: false
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
security-events: write security-events: write
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
persist-credentials: false persist-credentials: false
+7
View File
@@ -2,6 +2,13 @@
## [Unreleased] ## [Unreleased]
## [1.9.0](https://github.com/ICTU/netbox_slm/releases/tag/1.9.0) - 2026-06-25
### Changed
* Support NetBox v4.6.3 (#76)
* Support Python 3.14 (#76)
## [1.8.3](https://github.com/ICTU/netbox_slm/releases/tag/1.8.3) - 2025-12-18 ## [1.8.3](https://github.com/ICTU/netbox_slm/releases/tag/1.8.3) - 2025-12-18
### Fixed ### Fixed
+2
View File
@@ -4,6 +4,8 @@
## Version upgrade workflow ## Version upgrade workflow
1. Update version spec in `netbox_slm/__init__.py` and `sonar-project.properties` 1. Update version spec in `netbox_slm/__init__.py` and `sonar-project.properties`
1. Update NetBox base image versions in `ci/Dockerfile`, `ci/Dockerfile-CI` and `ci/docker-compose.yml`
1. Ensure that the `PYTHON_VERSION` in the `Dockerfile`s is the same as the NetBox community docker base image
1. Check for any runtime errors and warnings in the `netbox-*` container logs 1. Check for any runtime errors and warnings in the `netbox-*` container logs
1. Create new version tag on GitHub, following semantic versioning as: `MAJOR.MINOR.PATCH` 1. Create new version tag on GitHub, following semantic versioning as: `MAJOR.MINOR.PATCH`
1. Update the `CHANGELOG.md` with new version information and move `[Unreleased]` items to new version section 1. Update the `CHANGELOG.md` with new version information and move `[Unreleased]` items to new version section
+3 -3
View File
@@ -1,4 +1,4 @@
ARG PYTHON_VERSION=3.12 ARG PYTHON_VERSION=3.14
FROM python:${PYTHON_VERSION}-alpine AS compile-image FROM python:${PYTHON_VERSION}-alpine AS compile-image
# Python version build arg is not actually supported by NetBox container image, only used for maintenance convenience # Python version build arg is not actually supported by NetBox container image, only used for maintenance convenience
@@ -12,7 +12,7 @@ RUN pip install -U .[build] &&\
python -m build &&\ python -m build &&\
pip install --no-index /build pip install --no-index /build
FROM netboxcommunity/netbox:v4.2.3 FROM netboxcommunity/netbox:v4.6.3
ARG PYTHON_VERSION=3.12 ARG PYTHON_VERSION=3.14
COPY --from=compile-image /opt/netbox/venv/lib/python${PYTHON_VERSION}/site-packages/netbox_slm /opt/netbox/venv/lib/python${PYTHON_VERSION}/site-packages/netbox_slm COPY --from=compile-image /opt/netbox/venv/lib/python${PYTHON_VERSION}/site-packages/netbox_slm /opt/netbox/venv/lib/python${PYTHON_VERSION}/site-packages/netbox_slm
+2 -2
View File
@@ -1,5 +1,5 @@
FROM netboxcommunity/netbox:v4.2.3 FROM netboxcommunity/netbox:v4.6.3
ARG PYTHON_VERSION=3.12 ARG PYTHON_VERSION=3.14
COPY ../pyproject.toml /tmp/ COPY ../pyproject.toml /tmp/
RUN uv pip install -r /tmp/pyproject.toml --extra ci RUN uv pip install -r /tmp/pyproject.toml --extra ci
+16 -8
View File
@@ -1,18 +1,20 @@
""" """
Minimal NetBox config for NetBox SLM plugin testing, docs: https://netbox.readthedocs.io/en/stable/configuration/ Minimal NetBox config for NetBox SLM plugin testing, docs: https://netbox.readthedocs.io/en/stable/configuration/
Based on https://github.com/netbox-community/netbox/blob/v4.2.3/netbox/netbox/configuration_testing.py Based on https://github.com/netbox-community/netbox/blob/v4.6.3/netbox/netbox/configuration_testing.py
""" """
from os import environ from os import environ
ALLOWED_HOSTS = ['*'] ALLOWED_HOSTS = ['*']
DATABASE = { DATABASES = {
'NAME': environ.get('DB_NAME', 'netbox'), 'default': {
'USER': environ.get('DB_USER', ''), 'NAME': environ.get('DB_NAME', 'netbox'),
'PASSWORD': environ.get('DB_PASSWORD', ''), 'USER': environ.get('DB_USER', ''),
'HOST': environ.get('DB_HOST', 'localhost'), 'PASSWORD': environ.get('DB_PASSWORD', ''),
'PORT': environ.get('DB_PORT', ''), 'HOST': environ.get('DB_HOST', 'localhost'),
'CONN_MAX_AGE': int(environ.get('DB_CONN_MAX_AGE', '300')), 'PORT': environ.get('DB_PORT', ''),
'CONN_MAX_AGE': int(environ.get('DB_CONN_MAX_AGE', '300')),
}
} }
DEBUG = environ.get('DEBUG', 'False').lower() == 'true' DEBUG = environ.get('DEBUG', 'False').lower() == 'true'
@@ -38,3 +40,9 @@ REDIS = {
} }
SECRET_KEY = 'dummydummydummydummydummydummydummydummydummydummy' SECRET_KEY = 'dummydummydummydummydummydummydummydummydummydummy'
DEFAULT_PERMISSIONS = {}
API_TOKEN_PEPPERS = {
1: 'TEST-VALUE-DO-NOT-USE-TEST-VALUE-DO-NOT-USE-TEST-VALUE-DO-NOT-USE',
}
+2 -8
View File
@@ -12,7 +12,7 @@ services:
healthcheck: healthcheck:
disable: true disable: true
volumes: volumes:
- ./config:/etc/netbox/config:ro - ./config:/etc/netbox/config:z,ro
- ./reports:/ci/reports:rw - ./reports:/ci/reports:rw
- ../netbox_slm:/opt/netbox/netbox/netbox_slm:ro - ../netbox_slm:/opt/netbox/netbox/netbox_slm:ro
netbox-worker: netbox-worker:
@@ -22,14 +22,8 @@ services:
healthcheck: healthcheck:
disable: true disable: true
volumes: volumes:
- ./config:/etc/netbox/config:ro - ./config:/etc/netbox/config:z,ro
- ../netbox_slm:/opt/netbox/netbox/netbox_slm:ro - ../netbox_slm:/opt/netbox/netbox/netbox_slm:ro
netbox-housekeeping:
image: netbox:slm
depends_on:
- netbox
healthcheck:
disable: true
postgres: postgres:
ports: ports:
- "5432:5432" - "5432:5432"
+13 -25
View File
@@ -1,21 +1,21 @@
--- ---
# based on https://github.com/netbox-community/netbox-docker/blob/3.2.0/docker-compose.yml # based on https://github.com/netbox-community/netbox-docker/blob/5.0.1/docker-compose.yml
services: services:
netbox: &netbox netbox: &netbox
image: netboxcommunity/netbox:${VERSION-v4.2.3} image: docker.io/netboxcommunity/netbox:${VERSION-v4.6.3}
depends_on: depends_on:
- postgres - postgres
- redis - redis
- redis-cache - redis-cache
env_file: env/netbox.env env_file: env/netbox.env
user: "unit:root" user: "netbox:root"
healthcheck: healthcheck:
test: curl -f http://localhost:8080/login/ || exit 1 test: curl -f http://localhost:8080/login/ || exit 1
start_period: 90s start_period: 90s
timeout: 3s timeout: 3s
interval: 15s interval: 15s
volumes: volumes:
- ./config:/etc/netbox/config:ro - ./config:/etc/netbox/config:z,ro
- netbox-media-files:/opt/netbox/netbox/media:rw - netbox-media-files:/opt/netbox/netbox/media:rw
- netbox-reports-files:/opt/netbox/netbox/reports:rw - netbox-reports-files:/opt/netbox/netbox/reports:rw
- netbox-scripts-files:/opt/netbox/netbox/scripts:rw - netbox-scripts-files:/opt/netbox/netbox/scripts:rw
@@ -33,22 +33,10 @@ services:
start_period: 20s start_period: 20s
timeout: 3s timeout: 3s
interval: 15s interval: 15s
netbox-housekeeping:
<<: *netbox
depends_on:
netbox:
condition: service_healthy
command:
- /opt/netbox/housekeeping.sh
healthcheck:
test: ps -aux | grep -v grep | grep -q housekeeping || exit 1
start_period: 20s
timeout: 3s
interval: 15s
# postgres # postgres
postgres: postgres:
image: postgres:17-alpine image: docker.io/postgres:18-alpine
healthcheck: healthcheck:
test: pg_isready -q -t 2 -d $$POSTGRES_DB -U $$POSTGRES_USER test: pg_isready -q -t 2 -d $$POSTGRES_DB -U $$POSTGRES_USER
start_period: 20s start_period: 20s
@@ -57,15 +45,15 @@ services:
retries: 5 retries: 5
env_file: env/postgres.env env_file: env/postgres.env
volumes: volumes:
- netbox-postgres-data:/var/lib/postgresql/data - netbox-postgres:/var/lib/postgresql
# redis # redis
redis: redis:
image: valkey/valkey:8.0-alpine image: docker.io/valkey/valkey:9.0-alpine
command: command:
- sh - sh
- -c - -c # this is to evaluate the $REDIS_PASSWORD from the env
- valkey-server --appendonly yes --requirepass $$REDIS_PASSWORD - valkey-server --appendonly yes --requirepass $$REDIS_PASSWORD ## $$ because of docker-compose
healthcheck: &redis-healthcheck healthcheck: &redis-healthcheck
test: '[ $$(valkey-cli --pass "$${REDIS_PASSWORD}" ping) = ''PONG'' ]' test: '[ $$(valkey-cli --pass "$${REDIS_PASSWORD}" ping) = ''PONG'' ]'
start_period: 5s start_period: 5s
@@ -76,11 +64,11 @@ services:
volumes: volumes:
- netbox-redis-data:/data - netbox-redis-data:/data
redis-cache: redis-cache:
image: valkey/valkey:8.0-alpine image: docker.io/valkey/valkey:9.0-alpine
command: command:
- sh - sh
- -c - -c # this is to evaluate the $REDIS_PASSWORD from the env
- valkey-server --requirepass $$REDIS_PASSWORD - valkey-server --requirepass $$REDIS_PASSWORD ## $$ because of docker-compose
healthcheck: *redis-healthcheck healthcheck: *redis-healthcheck
env_file: env/redis-cache.env env_file: env/redis-cache.env
volumes: volumes:
@@ -89,7 +77,7 @@ services:
volumes: volumes:
netbox-media-files: netbox-media-files:
driver: local driver: local
netbox-postgres-data: netbox-postgres:
driver: local driver: local
netbox-redis-cache-data: netbox-redis-cache-data:
driver: local driver: local
+1 -1
View File
@@ -16,7 +16,7 @@ limitations under the License.
from netbox.plugins import PluginConfig from netbox.plugins import PluginConfig
__version__ = "1.8.3" __version__ = "1.9.0"
class SLMConfig(PluginConfig): class SLMConfig(PluginConfig):
@@ -24,6 +24,6 @@ class Migration(migrations.Migration):
), ),
migrations.AddConstraint( migrations.AddConstraint(
model_name='softwareproductinstallation', model_name='softwareproductinstallation',
constraint=models.CheckConstraint(check=models.Q(models.Q(('cluster__isnull', True), ('device__isnull', False), ('virtualmachine__isnull', True)), models.Q(('cluster__isnull', True), ('device__isnull', True), ('virtualmachine__isnull', False)), models.Q(('cluster__isnull', False), ('device__isnull', True), ('virtualmachine__isnull', True)), _connector='OR'), name='netbox_slm_softwareproductinstallation_platform', violation_error_message='Installation requires exactly one platform destination.'), constraint=models.CheckConstraint(condition=models.Q(models.Q(('cluster__isnull', True), ('device__isnull', False), ('virtualmachine__isnull', True)), models.Q(('cluster__isnull', True), ('device__isnull', True), ('virtualmachine__isnull', False)), models.Q(('cluster__isnull', False), ('device__isnull', True), ('virtualmachine__isnull', True)), _connector='OR'), name='netbox_slm_softwareproductinstallation_platform', violation_error_message='Installation requires exactly one platform destination.'),
), ),
] ]
+8 -10
View File
@@ -1,7 +1,7 @@
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.urls import reverse from django.urls import reverse
from django.utils.html import format_html, urlencode from django.utils.html import format_html
from license_expression import Licensing, get_spdx_licensing from license_expression import Licensing, get_spdx_licensing
from netbox.models import NetBoxModel from netbox.models import NetBoxModel
@@ -37,10 +37,10 @@ class SoftwareProduct(NetBoxModel):
def get_installation_count(self): def get_installation_count(self):
count = SoftwareProductInstallation.objects.filter(software_product_id=self.pk).count() count = SoftwareProductInstallation.objects.filter(software_product_id=self.pk).count()
query_string = urlencode(dict(software_product_id=self.pk)) search_target = reverse(
search_target = reverse("plugins:netbox_slm:softwareproductinstallation_list") "plugins:netbox_slm:softwareproductinstallation_list", query={"software_product_id": self.pk}
# Can be composed directly with reverse(query=) in Django 5.2, see https://code.djangoproject.com/ticket/25582 )
return format_html(f"<a href='{search_target}?{query_string}'>{count}</a>") if count else "0" return format_html("<a href='{}'>{}</a>", search_target, count) if count else "0"
class SoftwareReleaseTypes(models.TextChoices): class SoftwareReleaseTypes(models.TextChoices):
@@ -83,10 +83,8 @@ class SoftwareProductVersion(NetBoxModel):
def get_installation_count(self): def get_installation_count(self):
count = SoftwareProductInstallation.objects.filter(version_id=self.pk).count() count = SoftwareProductInstallation.objects.filter(version_id=self.pk).count()
query_string = urlencode(dict(version_id=self.pk)) search_target = reverse("plugins:netbox_slm:softwareproductinstallation_list", query={"version_id": self.pk})
search_target = reverse("plugins:netbox_slm:softwareproductinstallation_list") return format_html("<a href='{}'>{}</a>", search_target, count) if count else "0"
# Can be composed directly with reverse(query=) in Django 5.2, see https://code.djangoproject.com/ticket/25582
return format_html(f"<a href='{search_target}?{query_string}'>{count}</a>") if count else "0"
class SoftwareProductInstallation(NetBoxModel): class SoftwareProductInstallation(NetBoxModel):
@@ -109,7 +107,7 @@ class SoftwareProductInstallation(NetBoxModel):
constraints = [ constraints = [
models.CheckConstraint( models.CheckConstraint(
name="%(app_label)s_%(class)s_platform", name="%(app_label)s_%(class)s_platform",
check=( condition=(
models.Q(device__isnull=False, virtualmachine__isnull=True, cluster__isnull=True) models.Q(device__isnull=False, virtualmachine__isnull=True, cluster__isnull=True)
| models.Q(device__isnull=True, virtualmachine__isnull=False, cluster__isnull=True) | models.Q(device__isnull=True, virtualmachine__isnull=False, cluster__isnull=True)
| models.Q(device__isnull=True, virtualmachine__isnull=True, cluster__isnull=False) | models.Q(device__isnull=True, virtualmachine__isnull=True, cluster__isnull=False)
+7 -6
View File
@@ -26,23 +26,24 @@ classifiers = [
"Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python", "Programming Language :: Python",
"Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP",
"Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Internet :: WWW/HTTP :: Dynamic Content",
] ]
dependencies = [ dependencies = [
"license-expression == 30.4.1", "license-expression == 30.4.4",
] ]
[project.optional-dependencies] [project.optional-dependencies]
build = [ build = [
"build == 1.2.2.post1", "build == 1.5.0",
"setuptools == 75.8.0", "setuptools == 82.0.1",
"twine == 6.1.0", "twine == 6.2.0",
] ]
ci = [ ci = [
"coverage == 7.6.11", "coverage == 7.14.3",
"ruff == 0.9.6", "ruff == 0.15.19",
] ]
[project.urls] [project.urls]
+2 -2
View File
@@ -2,13 +2,13 @@
sonar.organization=ictu sonar.organization=ictu
sonar.projectKey=ICTU_netbox_slm sonar.projectKey=ICTU_netbox_slm
sonar.projectName=netbox-slm sonar.projectName=netbox-slm
sonar.projectVersion=1.8.3 sonar.projectVersion=1.9.0
# Path is relative to the sonar-project.properties file # Path is relative to the sonar-project.properties file
sonar.sources=netbox_slm sonar.sources=netbox_slm
sonar.exclusions=netbox_slm/tests/** sonar.exclusions=netbox_slm/tests/**
sonar.tests=netbox_slm/tests sonar.tests=netbox_slm/tests
sonar.python.version=3.11, 3.12, 3.13 sonar.python.version=3.11, 3.12, 3.13, 3.14
# Python test reports # Python test reports
sonar.python.coverage.reportPaths=/ci/reports/coverage.xml sonar.python.coverage.reportPaths=/ci/reports/coverage.xml