Unify api serializers (#16)

This commit is contained in:
wkoot
2023-04-19 10:14:32 +02:00
committed by GitHub
parent 7f18a3ad6f
commit 5c717e168f
29 changed files with 127 additions and 192 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
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
+20 -3
View File
@@ -1,6 +1,11 @@
name: Docker Image CI
on: [push, pull_request]
on:
push:
branches:
- master
pull_request:
types: [opened, synchronize, reopened]
jobs:
build:
@@ -15,5 +20,17 @@ jobs:
# 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"
- name: Run Django tests for netbox_slm
run: docker compose run netbox sh -c "/opt/netbox/venv/bin/python manage.py test netbox_slm"
- name: Run Django tests with coverage for 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 }}
+2
View File
@@ -6,4 +6,6 @@
**/.vscode
**/dist
**/netbox_slm.egg-info
ci/docker-compose.override.yml
ci/reports
out/production
+2 -1
View File
@@ -1,3 +1,4 @@
include ../LICENSE
include ../README.rst
include ../README.md
include ../SECURITY.md
recursive-include netbox_slm/templates *
+4
View File
@@ -1,3 +1,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
+7
View File
@@ -2,3 +2,10 @@ PLUGINS = ['netbox_slm']
DEBUG = True
SECRET_KEY = 'dummy'
DEVELOPER = True
PLUGINS_CONFIG = {
'netbox_slm': dict(
TEST_RUNNER='xmlrunner.extra.djangotestrunner.XMLTestRunner',
TEST_OUTPUT_DIR='/ci/reports/',
TEST_OUTPUT_FILE_NAME='junit.xml',
)
}
-28
View File
@@ -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"]
-90
View File
@@ -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 users 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: ..
dockerfile: ci/Dockerfile-CI
image: netbox:slm
environment:
- COVERAGE_FILE=/ci/.coverage
volumes:
- ./reports:/ci/reports
netbox-worker:
image: netbox:slm
netbox-housekeeping:
-2
View File
@@ -11,8 +11,6 @@ services:
user: 'unit:root'
volumes:
- ./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-worker:
<<: *netbox
View File
+2
View File
@@ -0,0 +1,2 @@
coverage==7.2.2
unittest-xml-reporting==3.2.0
+5 -4
View File
@@ -4,14 +4,15 @@ from extras.plugins import PluginConfig
class SLMConfig(PluginConfig):
name = 'netbox_slm'
verbose_name = 'Software Lifecycle Management'
description = 'Software Lifecycle Management'
version = '1.2'
author = 'Hedde van der Heide'
author_email = 'hedde.vanderheide@ictu.nl'
description = 'Software Lifecycle Management Netbox Plugin.'
version = '1.3'
author = 'ICTU'
author_email = 'open-source-projects@ictu.nl'
base_url = 'slm'
required_settings = []
default_settings = {
'version_info': False
}
config = SLMConfig
+4 -6
View File
@@ -1,9 +1,7 @@
from rest_framework import serializers
from netbox.api.serializers import NetBoxModelSerializer
from netbox_slm.models import (
SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation,
)
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
class SoftwareProductSerializer(NetBoxModelSerializer):
@@ -19,7 +17,7 @@ class SoftwareProductSerializer(NetBoxModelSerializer):
]
def get_display(self, obj):
return f"{obj.manufacturer.name} - {obj.name}"
return f"{obj.manufacturer} - {obj}"
class SoftwareProductVersionSerializer(NetBoxModelSerializer):
@@ -35,7 +33,7 @@ class SoftwareProductVersionSerializer(NetBoxModelSerializer):
]
def get_display(self, obj):
return obj.name
return f"{obj}"
class SoftwareProductInstallationSerializer(NetBoxModelSerializer):
@@ -52,4 +50,4 @@ class SoftwareProductInstallationSerializer(NetBoxModelSerializer):
]
def get_display(self, obj):
return obj
return f"{obj}"
+2 -4
View File
@@ -1,15 +1,13 @@
from netbox.api.viewsets import NetBoxModelViewSet
from rest_framework.routers import APIRootView
from netbox.api.viewsets import NetBoxModelViewSet
from netbox_slm.api.serializers import (
SoftwareProductSerializer, SoftwareProductVersionSerializer, SoftwareProductInstallationSerializer,
)
from netbox_slm.filtersets import (
SoftwareProductFilterSet, SoftwareProductVersionFilterSet, SoftwareProductInstallationFilterSet,
)
from netbox_slm.models import (
SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation,
)
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
class NetboxSLMRootView(APIRootView):
+5 -3
View File
@@ -1,11 +1,12 @@
from django.db.models import Q
from netbox.filtersets import NetBoxModelFilterSet
from netbox_slm.models import *
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
class SoftwareProductFilterSet(NetBoxModelFilterSet):
"""Filter capabilities for SoftwareProduct instances."""
class Meta:
model = SoftwareProduct
fields = tuple()
@@ -14,13 +15,13 @@ class SoftwareProductFilterSet(NetBoxModelFilterSet):
"""Perform the filtered search."""
if not value.strip():
return queryset
qs_filter = Q(name__icontains=value) | \
Q(manufacturer__name__icontains=value)
qs_filter = Q(name__icontains=value) | Q(manufacturer__name__icontains=value)
return queryset.filter(qs_filter)
class SoftwareProductVersionFilterSet(NetBoxModelFilterSet):
"""Filter capabilities for SoftwareProductVersion instances."""
class Meta:
model = SoftwareProductVersion
fields = (
@@ -39,6 +40,7 @@ class SoftwareProductVersionFilterSet(NetBoxModelFilterSet):
class SoftwareProductInstallationFilterSet(NetBoxModelFilterSet):
"""Filter capabilities for SoftwareProductInstallation instances."""
class Meta:
model = SoftwareProductInstallation
fields = tuple()
+2 -9
View File
@@ -3,16 +3,9 @@ from django.urls import reverse_lazy
from django.utils.translation import gettext as _
from dcim.models import Manufacturer, Device
from netbox.forms import (
NetBoxModelForm,
NetBoxModelCSVForm,
NetBoxModelBulkEditForm,
NetBoxModelFilterSetForm,
)
from netbox.forms import NetBoxModelForm, NetBoxModelCSVForm, NetBoxModelBulkEditForm, NetBoxModelFilterSetForm
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
from utilities.forms import (
DynamicModelChoiceField, APISelect, TagFilterField, ChoiceField
)
from utilities.forms import DynamicModelChoiceField, APISelect, TagFilterField
from virtualization.models import VirtualMachine
+1 -1
View File
@@ -89,4 +89,4 @@ class SoftwareProductInstallation(NetBoxModel):
return self.device or self.virtualmachine
def render_type(self):
return f"{'device' if self.device else 'virtualmachine'}"
return 'device' if self.device else 'virtualmachine'
-1
View File
@@ -1,7 +1,6 @@
from extras.plugins import PluginMenuButton, PluginMenuItem
from utilities.choices import ButtonColorChoices
menu_items = (
PluginMenuItem(
link='plugins:netbox_slm:softwareproduct_list',
+2 -2
View File
@@ -1,9 +1,9 @@
import django_tables2 as tables
from django.db.models import Count
from django_tables2.utils import Accessor
from netbox.tables import NetBoxTable, ToggleColumn, columns
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
from netbox.tables import NetBoxTable, ChoiceFieldColumn, ToggleColumn, columns
class SoftwareProductTable(NetBoxTable):
+36
View File
@@ -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())
-16
View File
@@ -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
View File
@@ -1,6 +1,6 @@
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.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
+2 -6
View File
@@ -1,10 +1,6 @@
from netbox.views import generic
from netbox_slm import filtersets
from netbox_slm import forms
from netbox_slm import tables
from netbox_slm.models import (
SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
)
from netbox_slm import filtersets, forms, tables
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation
class SoftwareProductListView(generic.ObjectListView):
+6 -8
View File
@@ -1,11 +1,9 @@
[metadata]
name = netbox-slm
version = 1.2
version = 1.3
description = Software Lifecycle Management Netbox Plugin.
# long_description = file: README.rst
# long_description_content_type='text/x-rst'
author = Hedde van der Heide
author_email = hedde.vanderheide@ictu.nl
author = ICTU
author_email = open-source-projects@ictu.nl
license = Apache 2.0
classifiers =
Environment :: Web Environment
@@ -16,13 +14,13 @@ classifiers =
Programming Language :: Python
Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
Topic :: Internet :: WWW/HTTP
Topic :: Internet :: WWW/HTTP :: Dynamic Content
[options]
include_package_data = true
packages = find:
python_requires = >=3.7
python_requires = >=3.9
+1 -1
View File
@@ -1,3 +1,3 @@
from setuptools import setup, Extension
from setuptools import setup
setup()
+13
View File
@@ -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
View File
@@ -1,7 +1,7 @@
git clone -b release https://github.com/netbox-community/netbox-docker.git
cd netbox-docker
tee docker-compose.override.yml <<EOF
version: '3.4'
version: '3.7'
services:
netbox:
ports: