Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f84a6bde2a | ||
|
|
811490e429 | ||
|
|
7ce718ff9d | ||
|
|
9e87afcde7 | ||
|
|
e03195c9b1 | ||
|
|
59730a5184 | ||
|
|
69594bc33a | ||
|
|
766ecfc25e |
@@ -8,13 +8,16 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
types: [opened, synchronize, reopened]
|
types: [opened, synchronize, reopened]
|
||||||
|
|
||||||
|
permissions: {}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Build the Docker image
|
- name: Build the Docker image
|
||||||
run: docker compose build --no-cache
|
run: docker compose build --no-cache
|
||||||
@@ -35,7 +38,6 @@ jobs:
|
|||||||
|
|
||||||
- name: Sonar scan
|
- name: Sonar scan
|
||||||
if: env.SONAR_TOKEN != null
|
if: env.SONAR_TOKEN != null
|
||||||
uses: SonarSource/sonarqube-scan-action@v4
|
uses: SonarSource/sonarqube-scan-action@713881670b6b3676cda39549040e2d88c70d582e # v8.2.0
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||||
|
|||||||
@@ -8,13 +8,17 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
types: [opened, synchronize, reopened]
|
types: [opened, synchronize, reopened]
|
||||||
|
|
||||||
|
permissions: {}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
vale:
|
vale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- uses: errata-ai/vale-action@v2.1.1
|
- uses: errata-ai/vale-action@85f9f7f2c5f449ac0ae5b66662961bae3f77ca6a # v2.1.2
|
||||||
with:
|
with:
|
||||||
fail_on_error: true
|
fail_on_error: true
|
||||||
filter_mode: nofilter
|
filter_mode: nofilter
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
types: [opened, synchronize, reopened]
|
types: [opened, synchronize, reopened]
|
||||||
|
|
||||||
|
permissions: {}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -17,11 +19,14 @@ 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@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
name: GitHub Actions Security Analysis with zizmor
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened]
|
||||||
|
|
||||||
|
permissions: {}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
zizmor:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
security-events: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Run zizmor
|
||||||
|
uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6
|
||||||
+20
-1
@@ -2,11 +2,30 @@
|
|||||||
|
|
||||||
## [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
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Filters with default order by ID (#75)
|
||||||
|
|
||||||
|
## [1.8.2](https://github.com/ICTU/netbox_slm/releases/tag/1.8.2) - 2025-06-02
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Permission check for adding software product installations (#70)
|
||||||
|
|
||||||
## [1.8.1](https://github.com/ICTU/netbox_slm/releases/tag/1.8.1) - 2025-04-11
|
## [1.8.1](https://github.com/ICTU/netbox_slm/releases/tag/1.8.1) - 2025-04-11
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
* Fix filtering on foreign objects (#65)
|
* Filtering on foreign objects (#65)
|
||||||
|
|
||||||
## [1.8.0](https://github.com/ICTU/netbox_slm/releases/tag/1.8.0) - 2025-02-25
|
## [1.8.0](https://github.com/ICTU/netbox_slm/releases/tag/1.8.0) - 2025-02-25
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -7,3 +7,6 @@ Packages = proselint
|
|||||||
|
|
||||||
[*.md]
|
[*.md]
|
||||||
BasedOnStyles = Vale, proselint
|
BasedOnStyles = Vale, proselint
|
||||||
|
|
||||||
|
[ci/styles/**]
|
||||||
|
BasedOnStyles =
|
||||||
|
|||||||
+3
-3
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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 = {
|
||||||
|
'default': {
|
||||||
'NAME': environ.get('DB_NAME', 'netbox'),
|
'NAME': environ.get('DB_NAME', 'netbox'),
|
||||||
'USER': environ.get('DB_USER', ''),
|
'USER': environ.get('DB_USER', ''),
|
||||||
'PASSWORD': environ.get('DB_PASSWORD', ''),
|
'PASSWORD': environ.get('DB_PASSWORD', ''),
|
||||||
'HOST': environ.get('DB_HOST', 'localhost'),
|
'HOST': environ.get('DB_HOST', 'localhost'),
|
||||||
'PORT': environ.get('DB_PORT', ''),
|
'PORT': environ.get('DB_PORT', ''),
|
||||||
'CONN_MAX_AGE': int(environ.get('DB_CONN_MAX_AGE', '300')),
|
'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',
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -64,5 +64,3 @@ NetBox
|
|||||||
NetBox SLM
|
NetBox SLM
|
||||||
Mart
|
Mart
|
||||||
Visser
|
Visser
|
||||||
Suchow
|
|
||||||
Redistributions
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ limitations under the License.
|
|||||||
|
|
||||||
from netbox.plugins import PluginConfig
|
from netbox.plugins import PluginConfig
|
||||||
|
|
||||||
__version__ = "1.8.1"
|
__version__ = "1.9.0"
|
||||||
|
|
||||||
|
|
||||||
class SLMConfig(PluginConfig):
|
class SLMConfig(PluginConfig):
|
||||||
|
|||||||
@@ -27,24 +27,24 @@ class NetboxSLMRootView(APIRootView):
|
|||||||
|
|
||||||
|
|
||||||
class SoftwareProductViewSet(NetBoxModelViewSet):
|
class SoftwareProductViewSet(NetBoxModelViewSet):
|
||||||
queryset = SoftwareProduct.objects.all()
|
queryset = SoftwareProduct.objects.all().order_by("id")
|
||||||
serializer_class = SoftwareProductSerializer
|
serializer_class = SoftwareProductSerializer
|
||||||
filterset_class = SoftwareProductFilterSet
|
filterset_class = SoftwareProductFilterSet
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductVersionViewSet(NetBoxModelViewSet):
|
class SoftwareProductVersionViewSet(NetBoxModelViewSet):
|
||||||
queryset = SoftwareProductVersion.objects.all()
|
queryset = SoftwareProductVersion.objects.all().order_by("id")
|
||||||
serializer_class = SoftwareProductVersionSerializer
|
serializer_class = SoftwareProductVersionSerializer
|
||||||
filterset_class = SoftwareProductVersionFilterSet
|
filterset_class = SoftwareProductVersionFilterSet
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductInstallationViewSet(NetBoxModelViewSet):
|
class SoftwareProductInstallationViewSet(NetBoxModelViewSet):
|
||||||
queryset = SoftwareProductInstallation.objects.all()
|
queryset = SoftwareProductInstallation.objects.all().order_by("id")
|
||||||
serializer_class = SoftwareProductInstallationSerializer
|
serializer_class = SoftwareProductInstallationSerializer
|
||||||
filterset_class = SoftwareProductInstallationFilterSet
|
filterset_class = SoftwareProductInstallationFilterSet
|
||||||
|
|
||||||
|
|
||||||
class SoftwareLicenseViewSet(NetBoxModelViewSet):
|
class SoftwareLicenseViewSet(NetBoxModelViewSet):
|
||||||
queryset = SoftwareLicense.objects.all()
|
queryset = SoftwareLicense.objects.all().order_by("id")
|
||||||
serializer_class = SoftwareLicenseSerializer
|
serializer_class = SoftwareLicenseSerializer
|
||||||
filterset_class = SoftwareLicenseFilterSet
|
filterset_class = SoftwareLicenseFilterSet
|
||||||
|
|||||||
@@ -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
@@ -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)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<h2 class="card-header">
|
<h2 class="card-header">
|
||||||
Installations
|
Installations
|
||||||
{% if perms.netbox_slm.add_installation %}
|
{% if perms.netbox_slm.add_softwareproductinstallation %}
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<a href="{% url 'plugins:netbox_slm:softwareproductinstallation_add' %}?{{ search_obj }}={{ object.pk }}" class="btn btn-ghost-primary btn-sm">
|
<a href="{% url 'plugins:netbox_slm:softwareproductinstallation_add' %}?{{ search_obj }}={{ object.pk }}" class="btn btn-ghost-primary btn-sm">
|
||||||
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add an installation
|
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add an installation
|
||||||
|
|||||||
+7
-6
@@ -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,11 +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.1
|
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.python.version=3.11, 3.12, 3.13
|
sonar.exclusions=netbox_slm/tests/**
|
||||||
|
sonar.tests=netbox_slm/tests
|
||||||
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user