Unify api serializers (#16)
This commit is contained in:
@@ -1,3 +1,3 @@
|
|||||||
COMPOSE_PATH_SEPARATOR=:
|
COMPOSE_PATH_SEPARATOR=:
|
||||||
COMPOSE_FILE=ci/docker-compose.yml:ci/docker-compose.override.yml
|
COMPOSE_FILE=ci/docker-compose.yml:ci/docker-compose.ci.yml
|
||||||
COMPOSE_PROJECT_NAME=netbox-docker
|
COMPOSE_PROJECT_NAME=netbox-docker
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
name: Docker Image CI
|
name: Docker Image CI
|
||||||
|
|
||||||
on: [push, pull_request]
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -15,5 +20,17 @@ jobs:
|
|||||||
# clearcache is provided by Django extras, which is not loaded if netbox-slm cannot be loaded
|
# clearcache is provided by Django extras, which is not loaded if netbox-slm cannot be loaded
|
||||||
run: docker compose run netbox sh -c "/opt/netbox/venv/bin/python manage.py clearcache"
|
run: docker compose run netbox sh -c "/opt/netbox/venv/bin/python manage.py clearcache"
|
||||||
|
|
||||||
- name: Run Django tests for netbox_slm
|
- name: Run Django tests with coverage for netbox_slm
|
||||||
run: docker compose run netbox sh -c "/opt/netbox/venv/bin/python manage.py test netbox_slm"
|
run: |
|
||||||
|
chmod go+w ci/reports
|
||||||
|
docker compose run netbox sh -c " \
|
||||||
|
/opt/netbox/venv/bin/coverage run --source='netbox_slm' manage.py test netbox_slm && \
|
||||||
|
/opt/netbox/venv/bin/coverage report --fail-under=0 && \
|
||||||
|
/opt/netbox/venv/bin/coverage xml -o /ci/reports/coverage.xml"
|
||||||
|
|
||||||
|
- name: Sonarcloud scan
|
||||||
|
if: env.SONAR_TOKEN != null
|
||||||
|
uses: sonarsource/sonarcloud-github-action@master
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||||
|
|||||||
@@ -6,4 +6,6 @@
|
|||||||
**/.vscode
|
**/.vscode
|
||||||
**/dist
|
**/dist
|
||||||
**/netbox_slm.egg-info
|
**/netbox_slm.egg-info
|
||||||
|
ci/docker-compose.override.yml
|
||||||
|
ci/reports
|
||||||
out/production
|
out/production
|
||||||
|
|||||||
+3
-2
@@ -1,3 +1,4 @@
|
|||||||
include ../LICENSE
|
include ../LICENSE
|
||||||
include ../README.rst
|
include ../README.md
|
||||||
recursive-include netbox_slm/templates *
|
include ../SECURITY.md
|
||||||
|
recursive-include netbox_slm/templates *
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
FROM netboxcommunity/netbox:v3.4.7
|
FROM netboxcommunity/netbox:v3.4.7
|
||||||
|
|
||||||
|
RUN mkdir /ci && chmod go+w /ci
|
||||||
|
COPY ../ci/requirements_ci.txt /ci/
|
||||||
|
RUN pip install -r /ci/requirements_ci.txt
|
||||||
|
|
||||||
COPY ../netbox_slm /opt/netbox/netbox/netbox_slm
|
COPY ../netbox_slm /opt/netbox/netbox/netbox_slm
|
||||||
|
|||||||
@@ -2,3 +2,10 @@ PLUGINS = ['netbox_slm']
|
|||||||
DEBUG = True
|
DEBUG = True
|
||||||
SECRET_KEY = 'dummy'
|
SECRET_KEY = 'dummy'
|
||||||
DEVELOPER = True
|
DEVELOPER = True
|
||||||
|
PLUGINS_CONFIG = {
|
||||||
|
'netbox_slm': dict(
|
||||||
|
TEST_RUNNER='xmlrunner.extra.djangotestrunner.XMLTestRunner',
|
||||||
|
TEST_OUTPUT_DIR='/ci/reports/',
|
||||||
|
TEST_OUTPUT_FILE_NAME='junit.xml',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
####
|
|
||||||
## This file contains extra configuration options that can't be configured
|
|
||||||
## directly through environment variables.
|
|
||||||
## All vairables set here overwrite any existing found in ldap_config.py
|
|
||||||
####
|
|
||||||
|
|
||||||
# # This Python script inherits all the imports from ldap_config.py
|
|
||||||
# from django_auth_ldap.config import LDAPGroupQuery # Imported since not in ldap_config.py
|
|
||||||
|
|
||||||
# # Sets a base requirement of membetship to netbox-user-ro, netbox-user-rw, or netbox-user-admin.
|
|
||||||
# AUTH_LDAP_REQUIRE_GROUP = (
|
|
||||||
# LDAPGroupQuery("cn=netbox-user-ro,ou=groups,dc=example,dc=com")
|
|
||||||
# | LDAPGroupQuery("cn=netbox-user-rw,ou=groups,dc=example,dc=com")
|
|
||||||
# | LDAPGroupQuery("cn=netbox-user-admin,ou=groups,dc=example,dc=com")
|
|
||||||
# )
|
|
||||||
|
|
||||||
# # Sets LDAP Flag groups variables with example.
|
|
||||||
# AUTH_LDAP_USER_FLAGS_BY_GROUP = {
|
|
||||||
# "is_staff": (
|
|
||||||
# LDAPGroupQuery("cn=netbox-user-ro,ou=groups,dc=example,dc=com")
|
|
||||||
# | LDAPGroupQuery("cn=netbox-user-rw,ou=groups,dc=example,dc=com")
|
|
||||||
# | LDAPGroupQuery("cn=netbox-user-admin,ou=groups,dc=example,dc=com")
|
|
||||||
# ),
|
|
||||||
# "is_superuser": "cn=netbox-user-admin,ou=groups,dc=example,dc=com",
|
|
||||||
# }
|
|
||||||
|
|
||||||
# # Sets LDAP Mirror groups variables with example groups
|
|
||||||
# AUTH_LDAP_MIRROR_GROUPS = ["netbox-user-ro", "netbox-user-rw", "netbox-user-admin"]
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
from importlib import import_module
|
|
||||||
from os import environ
|
|
||||||
|
|
||||||
import ldap
|
|
||||||
from django_auth_ldap.config import LDAPSearch
|
|
||||||
|
|
||||||
|
|
||||||
# Read secret from file
|
|
||||||
def _read_secret(secret_name, default=None):
|
|
||||||
try:
|
|
||||||
f = open('/run/secrets/' + secret_name, 'r', encoding='utf-8')
|
|
||||||
except EnvironmentError:
|
|
||||||
return default
|
|
||||||
else:
|
|
||||||
with f:
|
|
||||||
return f.readline().strip()
|
|
||||||
|
|
||||||
# Import and return the group type based on string name
|
|
||||||
def _import_group_type(group_type_name):
|
|
||||||
mod = import_module('django_auth_ldap.config')
|
|
||||||
try:
|
|
||||||
return getattr(mod, group_type_name)()
|
|
||||||
except:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Server URI
|
|
||||||
AUTH_LDAP_SERVER_URI = environ.get('AUTH_LDAP_SERVER_URI', '')
|
|
||||||
|
|
||||||
# The following may be needed if you are binding to Active Directory.
|
|
||||||
AUTH_LDAP_CONNECTION_OPTIONS = {
|
|
||||||
ldap.OPT_REFERRALS: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
# Set the DN and password for the NetBox service account.
|
|
||||||
AUTH_LDAP_BIND_DN = environ.get('AUTH_LDAP_BIND_DN', '')
|
|
||||||
AUTH_LDAP_BIND_PASSWORD = _read_secret('auth_ldap_bind_password', environ.get('AUTH_LDAP_BIND_PASSWORD', ''))
|
|
||||||
|
|
||||||
# Set a string template that describes any user’s distinguished name based on the username.
|
|
||||||
AUTH_LDAP_USER_DN_TEMPLATE = environ.get('AUTH_LDAP_USER_DN_TEMPLATE', None)
|
|
||||||
|
|
||||||
# Enable STARTTLS for ldap authentication.
|
|
||||||
AUTH_LDAP_START_TLS = environ.get('AUTH_LDAP_START_TLS', 'False').lower() == 'true'
|
|
||||||
|
|
||||||
# Include this setting if you want to ignore certificate errors. This might be needed to accept a self-signed cert.
|
|
||||||
# Note that this is a NetBox-specific setting which sets:
|
|
||||||
# ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
|
|
||||||
LDAP_IGNORE_CERT_ERRORS = environ.get('LDAP_IGNORE_CERT_ERRORS', 'False').lower() == 'true'
|
|
||||||
|
|
||||||
AUTH_LDAP_USER_SEARCH_BASEDN = environ.get('AUTH_LDAP_USER_SEARCH_BASEDN', '')
|
|
||||||
AUTH_LDAP_USER_SEARCH_ATTR = environ.get('AUTH_LDAP_USER_SEARCH_ATTR', 'sAMAccountName')
|
|
||||||
AUTH_LDAP_USER_SEARCH = LDAPSearch(
|
|
||||||
AUTH_LDAP_USER_SEARCH_BASEDN,
|
|
||||||
ldap.SCOPE_SUBTREE,
|
|
||||||
"(" + AUTH_LDAP_USER_SEARCH_ATTR + "=%(user)s)"
|
|
||||||
)
|
|
||||||
|
|
||||||
# This search ought to return all groups to which the user belongs. django_auth_ldap uses this to determine group
|
|
||||||
# heirarchy.
|
|
||||||
AUTH_LDAP_GROUP_SEARCH_BASEDN = environ.get('AUTH_LDAP_GROUP_SEARCH_BASEDN', '')
|
|
||||||
AUTH_LDAP_GROUP_SEARCH_CLASS = environ.get('AUTH_LDAP_GROUP_SEARCH_CLASS', 'group')
|
|
||||||
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(AUTH_LDAP_GROUP_SEARCH_BASEDN, ldap.SCOPE_SUBTREE,
|
|
||||||
"(objectClass=" + AUTH_LDAP_GROUP_SEARCH_CLASS + ")")
|
|
||||||
AUTH_LDAP_GROUP_TYPE = _import_group_type(environ.get('AUTH_LDAP_GROUP_TYPE', 'GroupOfNamesType'))
|
|
||||||
|
|
||||||
# Define a group required to login.
|
|
||||||
AUTH_LDAP_REQUIRE_GROUP = environ.get('AUTH_LDAP_REQUIRE_GROUP_DN')
|
|
||||||
|
|
||||||
# Define special user types using groups. Exercise great caution when assigning superuser status.
|
|
||||||
AUTH_LDAP_USER_FLAGS_BY_GROUP = {}
|
|
||||||
|
|
||||||
if AUTH_LDAP_REQUIRE_GROUP is not None:
|
|
||||||
AUTH_LDAP_USER_FLAGS_BY_GROUP = {
|
|
||||||
"is_active": environ.get('AUTH_LDAP_REQUIRE_GROUP_DN', ''),
|
|
||||||
"is_staff": environ.get('AUTH_LDAP_IS_ADMIN_DN', ''),
|
|
||||||
"is_superuser": environ.get('AUTH_LDAP_IS_SUPERUSER_DN', '')
|
|
||||||
}
|
|
||||||
|
|
||||||
# For more granular permissions, we can map LDAP groups to Django groups.
|
|
||||||
AUTH_LDAP_FIND_GROUP_PERMS = environ.get('AUTH_LDAP_FIND_GROUP_PERMS', 'True').lower() == 'true'
|
|
||||||
AUTH_LDAP_MIRROR_GROUPS = environ.get('AUTH_LDAP_MIRROR_GROUPS', '').lower() == 'true'
|
|
||||||
|
|
||||||
# Cache groups for one hour to reduce LDAP traffic
|
|
||||||
AUTH_LDAP_CACHE_TIMEOUT = int(environ.get('AUTH_LDAP_CACHE_TIMEOUT', 3600))
|
|
||||||
|
|
||||||
# Populate the Django user from the LDAP directory.
|
|
||||||
AUTH_LDAP_USER_ATTR_MAP = {
|
|
||||||
"first_name": environ.get('AUTH_LDAP_ATTR_FIRSTNAME', 'givenName'),
|
|
||||||
"last_name": environ.get('AUTH_LDAP_ATTR_LASTNAME', 'sn'),
|
|
||||||
"email": environ.get('AUTH_LDAP_ATTR_MAIL', 'mail')
|
|
||||||
}
|
|
||||||
@@ -7,6 +7,10 @@ services:
|
|||||||
context: ..
|
context: ..
|
||||||
dockerfile: ci/Dockerfile-CI
|
dockerfile: ci/Dockerfile-CI
|
||||||
image: netbox:slm
|
image: netbox:slm
|
||||||
|
environment:
|
||||||
|
- COVERAGE_FILE=/ci/.coverage
|
||||||
|
volumes:
|
||||||
|
- ./reports:/ci/reports
|
||||||
netbox-worker:
|
netbox-worker:
|
||||||
image: netbox:slm
|
image: netbox:slm
|
||||||
netbox-housekeeping:
|
netbox-housekeeping:
|
||||||
@@ -11,8 +11,6 @@ services:
|
|||||||
user: 'unit:root'
|
user: 'unit:root'
|
||||||
volumes:
|
volumes:
|
||||||
- ./configuration:/etc/netbox/config:z,ro
|
- ./configuration:/etc/netbox/config:z,ro
|
||||||
# - ./reports:/etc/netbox/reports:z,ro
|
|
||||||
# - ./scripts:/etc/netbox/scripts:z,ro
|
|
||||||
- netbox-media-files:/opt/netbox/netbox/media:z
|
- netbox-media-files:/opt/netbox/netbox/media:z
|
||||||
netbox-worker:
|
netbox-worker:
|
||||||
<<: *netbox
|
<<: *netbox
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
coverage==7.2.2
|
||||||
|
unittest-xml-reporting==3.2.0
|
||||||
@@ -4,14 +4,15 @@ from extras.plugins import PluginConfig
|
|||||||
class SLMConfig(PluginConfig):
|
class SLMConfig(PluginConfig):
|
||||||
name = 'netbox_slm'
|
name = 'netbox_slm'
|
||||||
verbose_name = 'Software Lifecycle Management'
|
verbose_name = 'Software Lifecycle Management'
|
||||||
description = 'Software Lifecycle Management'
|
description = 'Software Lifecycle Management Netbox Plugin.'
|
||||||
version = '1.2'
|
version = '1.3'
|
||||||
author = 'Hedde van der Heide'
|
author = 'ICTU'
|
||||||
author_email = 'hedde.vanderheide@ictu.nl'
|
author_email = 'open-source-projects@ictu.nl'
|
||||||
base_url = 'slm'
|
base_url = 'slm'
|
||||||
required_settings = []
|
required_settings = []
|
||||||
default_settings = {
|
default_settings = {
|
||||||
'version_info': False
|
'version_info': False
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
config = SLMConfig
|
config = SLMConfig
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from netbox.api.serializers import NetBoxModelSerializer
|
from netbox.api.serializers import NetBoxModelSerializer
|
||||||
from netbox_slm.models import (
|
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
|
||||||
SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductSerializer(NetBoxModelSerializer):
|
class SoftwareProductSerializer(NetBoxModelSerializer):
|
||||||
@@ -19,7 +17,7 @@ class SoftwareProductSerializer(NetBoxModelSerializer):
|
|||||||
]
|
]
|
||||||
|
|
||||||
def get_display(self, obj):
|
def get_display(self, obj):
|
||||||
return f"{obj.manufacturer.name} - {obj.name}"
|
return f"{obj.manufacturer} - {obj}"
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductVersionSerializer(NetBoxModelSerializer):
|
class SoftwareProductVersionSerializer(NetBoxModelSerializer):
|
||||||
@@ -35,7 +33,7 @@ class SoftwareProductVersionSerializer(NetBoxModelSerializer):
|
|||||||
]
|
]
|
||||||
|
|
||||||
def get_display(self, obj):
|
def get_display(self, obj):
|
||||||
return obj.name
|
return f"{obj}"
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductInstallationSerializer(NetBoxModelSerializer):
|
class SoftwareProductInstallationSerializer(NetBoxModelSerializer):
|
||||||
@@ -52,4 +50,4 @@ class SoftwareProductInstallationSerializer(NetBoxModelSerializer):
|
|||||||
]
|
]
|
||||||
|
|
||||||
def get_display(self, obj):
|
def get_display(self, obj):
|
||||||
return obj
|
return f"{obj}"
|
||||||
|
|||||||
@@ -12,4 +12,4 @@ router.APIRootView = NetboxSLMRootView
|
|||||||
router.register("softwareproducts", SoftwareProductViewSet)
|
router.register("softwareproducts", SoftwareProductViewSet)
|
||||||
router.register("softwareproductversions", SoftwareProductVersionViewSet)
|
router.register("softwareproductversions", SoftwareProductVersionViewSet)
|
||||||
router.register("softwareproductinstallations", SoftwareProductInstallationViewSet)
|
router.register("softwareproductinstallations", SoftwareProductInstallationViewSet)
|
||||||
urlpatterns = router.urls
|
urlpatterns = router.urls
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
from netbox.api.viewsets import NetBoxModelViewSet
|
|
||||||
from rest_framework.routers import APIRootView
|
from rest_framework.routers import APIRootView
|
||||||
|
|
||||||
|
from netbox.api.viewsets import NetBoxModelViewSet
|
||||||
from netbox_slm.api.serializers import (
|
from netbox_slm.api.serializers import (
|
||||||
SoftwareProductSerializer, SoftwareProductVersionSerializer, SoftwareProductInstallationSerializer,
|
SoftwareProductSerializer, SoftwareProductVersionSerializer, SoftwareProductInstallationSerializer,
|
||||||
)
|
)
|
||||||
from netbox_slm.filtersets import (
|
from netbox_slm.filtersets import (
|
||||||
SoftwareProductFilterSet, SoftwareProductVersionFilterSet, SoftwareProductInstallationFilterSet,
|
SoftwareProductFilterSet, SoftwareProductVersionFilterSet, SoftwareProductInstallationFilterSet,
|
||||||
)
|
)
|
||||||
from netbox_slm.models import (
|
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
|
||||||
SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class NetboxSLMRootView(APIRootView):
|
class NetboxSLMRootView(APIRootView):
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
|
||||||
from netbox.filtersets import NetBoxModelFilterSet
|
from netbox.filtersets import NetBoxModelFilterSet
|
||||||
from netbox_slm.models import *
|
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductFilterSet(NetBoxModelFilterSet):
|
class SoftwareProductFilterSet(NetBoxModelFilterSet):
|
||||||
"""Filter capabilities for SoftwareProduct instances."""
|
"""Filter capabilities for SoftwareProduct instances."""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = SoftwareProduct
|
model = SoftwareProduct
|
||||||
fields = tuple()
|
fields = tuple()
|
||||||
@@ -14,13 +15,13 @@ class SoftwareProductFilterSet(NetBoxModelFilterSet):
|
|||||||
"""Perform the filtered search."""
|
"""Perform the filtered search."""
|
||||||
if not value.strip():
|
if not value.strip():
|
||||||
return queryset
|
return queryset
|
||||||
qs_filter = Q(name__icontains=value) | \
|
qs_filter = Q(name__icontains=value) | Q(manufacturer__name__icontains=value)
|
||||||
Q(manufacturer__name__icontains=value)
|
|
||||||
return queryset.filter(qs_filter)
|
return queryset.filter(qs_filter)
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductVersionFilterSet(NetBoxModelFilterSet):
|
class SoftwareProductVersionFilterSet(NetBoxModelFilterSet):
|
||||||
"""Filter capabilities for SoftwareProductVersion instances."""
|
"""Filter capabilities for SoftwareProductVersion instances."""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = SoftwareProductVersion
|
model = SoftwareProductVersion
|
||||||
fields = (
|
fields = (
|
||||||
@@ -39,6 +40,7 @@ class SoftwareProductVersionFilterSet(NetBoxModelFilterSet):
|
|||||||
|
|
||||||
class SoftwareProductInstallationFilterSet(NetBoxModelFilterSet):
|
class SoftwareProductInstallationFilterSet(NetBoxModelFilterSet):
|
||||||
"""Filter capabilities for SoftwareProductInstallation instances."""
|
"""Filter capabilities for SoftwareProductInstallation instances."""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = SoftwareProductInstallation
|
model = SoftwareProductInstallation
|
||||||
fields = tuple()
|
fields = tuple()
|
||||||
|
|||||||
+2
-9
@@ -3,16 +3,9 @@ from django.urls import reverse_lazy
|
|||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from dcim.models import Manufacturer, Device
|
from dcim.models import Manufacturer, Device
|
||||||
from netbox.forms import (
|
from netbox.forms import NetBoxModelForm, NetBoxModelCSVForm, NetBoxModelBulkEditForm, NetBoxModelFilterSetForm
|
||||||
NetBoxModelForm,
|
|
||||||
NetBoxModelCSVForm,
|
|
||||||
NetBoxModelBulkEditForm,
|
|
||||||
NetBoxModelFilterSetForm,
|
|
||||||
)
|
|
||||||
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
|
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
|
||||||
from utilities.forms import (
|
from utilities.forms import DynamicModelChoiceField, APISelect, TagFilterField
|
||||||
DynamicModelChoiceField, APISelect, TagFilterField, ChoiceField
|
|
||||||
)
|
|
||||||
from virtualization.models import VirtualMachine
|
from virtualization.models import VirtualMachine
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from utilities.querysets import RestrictedQuerySet
|
|||||||
class SoftwareProduct(NetBoxModel):
|
class SoftwareProduct(NetBoxModel):
|
||||||
name = models.CharField(max_length=128)
|
name = models.CharField(max_length=128)
|
||||||
description = models.CharField(max_length=255, null=True, blank=True)
|
description = models.CharField(max_length=255, null=True, blank=True)
|
||||||
|
|
||||||
manufacturer = models.ForeignKey(
|
manufacturer = models.ForeignKey(
|
||||||
to='dcim.Manufacturer',
|
to='dcim.Manufacturer',
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
@@ -89,4 +89,4 @@ class SoftwareProductInstallation(NetBoxModel):
|
|||||||
return self.device or self.virtualmachine
|
return self.device or self.virtualmachine
|
||||||
|
|
||||||
def render_type(self):
|
def render_type(self):
|
||||||
return f"{'device' if self.device else 'virtualmachine'}"
|
return 'device' if self.device else 'virtualmachine'
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
from extras.plugins import PluginMenuButton, PluginMenuItem
|
from extras.plugins import PluginMenuButton, PluginMenuItem
|
||||||
from utilities.choices import ButtonColorChoices
|
from utilities.choices import ButtonColorChoices
|
||||||
|
|
||||||
|
|
||||||
menu_items = (
|
menu_items = (
|
||||||
PluginMenuItem(
|
PluginMenuItem(
|
||||||
link='plugins:netbox_slm:softwareproduct_list',
|
link='plugins:netbox_slm:softwareproduct_list',
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
|
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
from django_tables2.utils import Accessor
|
from django_tables2.utils import Accessor
|
||||||
|
|
||||||
|
from netbox.tables import NetBoxTable, ToggleColumn, columns
|
||||||
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
|
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
|
||||||
from netbox.tables import NetBoxTable, ChoiceFieldColumn, ToggleColumn, columns
|
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductTable(NetBoxTable):
|
class SoftwareProductTable(NetBoxTable):
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
|
||||||
|
|
||||||
|
|
||||||
|
class ModelTestCase(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.p_name = "test product"
|
||||||
|
self.v_name = "test version"
|
||||||
|
|
||||||
|
self.software_product = SoftwareProduct.objects.create(name=self.p_name)
|
||||||
|
self.software_product_version = SoftwareProductVersion.objects.create(
|
||||||
|
name=self.v_name, software_product=self.software_product
|
||||||
|
)
|
||||||
|
self.software_product_installation = SoftwareProductInstallation.objects.create(
|
||||||
|
software_product=self.software_product, version=self.software_product_version
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_model_name(self):
|
||||||
|
self.assertEqual(self.p_name, str(self.software_product))
|
||||||
|
self.assertEqual(self.v_name, str(self.software_product_version))
|
||||||
|
self.assertTrue(str(self.software_product_installation).isnumeric()) # should be PK
|
||||||
|
|
||||||
|
def test_absolute_url(self):
|
||||||
|
self.assertEqual("/plugins/slm/software-products/1/", self.software_product.get_absolute_url())
|
||||||
|
self.assertEqual("/plugins/slm/versions/1/", self.software_product_version.get_absolute_url())
|
||||||
|
self.assertEqual("/plugins/slm/installations/1/", self.software_product_installation.get_absolute_url())
|
||||||
|
|
||||||
|
def test_get_installation_count(self):
|
||||||
|
installation_ss = '<a href="/plugins/slm/installations/?q={}">1</a>'
|
||||||
|
self.assertEqual(installation_ss.format(self.p_name), self.software_product.get_installation_count())
|
||||||
|
self.assertEqual(installation_ss.format(self.v_name), self.software_product_version.get_installation_count())
|
||||||
|
|
||||||
|
def test_product_installation_methods(self):
|
||||||
|
self.assertEqual("virtualmachine", self.software_product_installation.render_type())
|
||||||
|
self.assertIsNone(self.software_product_installation.get_platform())
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
|
|
||||||
|
|
||||||
|
|
||||||
class ModelTestCase(TestCase):
|
|
||||||
def test_smoke(self):
|
|
||||||
software_product = SoftwareProduct.objects.create(name="test product")
|
|
||||||
software_product_version = SoftwareProductVersion.objects.create(
|
|
||||||
name="test version", software_product=software_product
|
|
||||||
)
|
|
||||||
software_product_installation = SoftwareProductInstallation.objects.create(
|
|
||||||
software_product=software_product, version=software_product_version
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual('virtualmachine', software_product_installation.render_type())
|
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
from netbox.views.generic import ObjectChangeLogView
|
|
||||||
|
|
||||||
|
from netbox.views.generic import ObjectChangeLogView
|
||||||
from netbox_slm import views
|
from netbox_slm import views
|
||||||
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
|
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
|
||||||
|
|
||||||
|
|||||||
+2
-6
@@ -1,10 +1,6 @@
|
|||||||
from netbox.views import generic
|
from netbox.views import generic
|
||||||
from netbox_slm import filtersets
|
from netbox_slm import filtersets, forms, tables
|
||||||
from netbox_slm import forms
|
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
|
||||||
from netbox_slm import tables
|
|
||||||
from netbox_slm.models import (
|
|
||||||
SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class SoftwareProductListView(generic.ObjectListView):
|
class SoftwareProductListView(generic.ObjectListView):
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
name = netbox-slm
|
name = netbox-slm
|
||||||
version = 1.2
|
version = 1.3
|
||||||
description = Software Lifecycle Management Netbox Plugin.
|
description = Software Lifecycle Management Netbox Plugin.
|
||||||
# long_description = file: README.rst
|
author = ICTU
|
||||||
# long_description_content_type='text/x-rst'
|
author_email = open-source-projects@ictu.nl
|
||||||
author = Hedde van der Heide
|
|
||||||
author_email = hedde.vanderheide@ictu.nl
|
|
||||||
license = Apache 2.0
|
license = Apache 2.0
|
||||||
classifiers =
|
classifiers =
|
||||||
Environment :: Web Environment
|
Environment :: Web Environment
|
||||||
@@ -16,13 +14,13 @@ classifiers =
|
|||||||
Programming Language :: Python
|
Programming Language :: Python
|
||||||
Programming Language :: Python :: 3
|
Programming Language :: Python :: 3
|
||||||
Programming Language :: Python :: 3 :: Only
|
Programming Language :: Python :: 3 :: Only
|
||||||
Programming Language :: Python :: 3.7
|
|
||||||
Programming Language :: Python :: 3.8
|
|
||||||
Programming Language :: Python :: 3.9
|
Programming Language :: Python :: 3.9
|
||||||
|
Programming Language :: Python :: 3.10
|
||||||
|
Programming Language :: Python :: 3.11
|
||||||
Topic :: Internet :: WWW/HTTP
|
Topic :: Internet :: WWW/HTTP
|
||||||
Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
||||||
|
|
||||||
[options]
|
[options]
|
||||||
include_package_data = true
|
include_package_data = true
|
||||||
packages = find:
|
packages = find:
|
||||||
python_requires = >=3.7
|
python_requires = >=3.9
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
from setuptools import setup, Extension
|
from setuptools import setup
|
||||||
|
|
||||||
setup()
|
setup()
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Project metadata
|
||||||
|
sonar.organization=ictu
|
||||||
|
sonar.projectKey=ICTU_netbox_slm
|
||||||
|
sonar.projectName=netbox-slm
|
||||||
|
sonar.projectVersion=1.3
|
||||||
|
|
||||||
|
# Path is relative to the sonar-project.properties file
|
||||||
|
sonar.sources=netbox_slm
|
||||||
|
sonar.python.version=3.9
|
||||||
|
|
||||||
|
# Unit tests
|
||||||
|
sonar.python.xunit.reportPath=ci/reports/junit.xml
|
||||||
|
sonar.python.coverage.reportPaths=ci/reports/coverage.xml
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
git clone -b release https://github.com/netbox-community/netbox-docker.git
|
git clone -b release https://github.com/netbox-community/netbox-docker.git
|
||||||
cd netbox-docker
|
cd netbox-docker
|
||||||
tee docker-compose.override.yml <<EOF
|
tee docker-compose.override.yml <<EOF
|
||||||
version: '3.4'
|
version: '3.7'
|
||||||
services:
|
services:
|
||||||
netbox:
|
netbox:
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
Reference in New Issue
Block a user