From 508915e4fc069b1f75e22602ad2b8fe70cd1147b Mon Sep 17 00:00:00 2001 From: Michal Fiedorowicz Date: Thu, 1 Aug 2024 21:11:33 +0100 Subject: [PATCH] feat: configure plugin with migrations (#17) --------- Signed-off-by: Michal Fiedorowicz --- Makefile | 4 +- README.md | 4 +- docker/Dockerfile-diode-netbox-plugin | 2 - docker/docker-compose.test.yaml | 5 - docker/netbox/configuration-test/plugins.py | 13 --- docker/netbox/configuration/plugins.py | 2 +- docker/netbox/enable-diode-plugin.sh | 9 -- docker/netbox/launch-netbox.sh | 2 - netbox_diode_plugin/management/__init__.py | 3 - .../management/commands/__init__.py | 3 - .../commands/configurediodeplugin.py | 75 ------------ .../migrations/0001_initial.py | 110 ++++++++++++++++++ .../tests/test_apply_change_set.py | 4 - 13 files changed, 115 insertions(+), 121 deletions(-) delete mode 100644 docker/docker-compose.test.yaml delete mode 100644 docker/netbox/configuration-test/plugins.py delete mode 100644 docker/netbox/enable-diode-plugin.sh delete mode 100644 netbox_diode_plugin/management/__init__.py delete mode 100644 netbox_diode_plugin/management/commands/__init__.py delete mode 100644 netbox_diode_plugin/management/commands/configurediodeplugin.py create mode 100644 netbox_diode_plugin/migrations/0001_initial.py diff --git a/Makefile b/Makefile index 1c16ab2..3052b28 100644 --- a/Makefile +++ b/Makefile @@ -14,10 +14,10 @@ docker-compose-netbox-plugin-down: .PHONY: docker-compose-netbox-plugin-test docker-compose-netbox-plugin-test: - -@$(DOCKER_COMPOSE) -f docker/docker-compose.yaml -f docker/docker-compose.test.yaml run -u root --rm netbox ./manage.py test --keepdb netbox_diode_plugin + -@$(DOCKER_COMPOSE) -f docker/docker-compose.yaml run -u root --rm netbox ./manage.py test --keepdb netbox_diode_plugin @$(MAKE) docker-compose-netbox-plugin-down .PHONY: docker-compose-netbox-plugin-test-cover docker-compose-netbox-plugin-test-cover: - -@$(DOCKER_COMPOSE) -f docker/docker-compose.yaml -f docker/docker-compose.test.yaml run --rm -u root -e COVERAGE_FILE=/opt/netbox/netbox/coverage/.coverage netbox sh -c "coverage run --source=netbox_diode_plugin ./manage.py test --keepdb netbox_diode_plugin && coverage xml -o /opt/netbox/netbox/coverage/report.xml && coverage report -m | tee /opt/netbox/netbox/coverage/report.txt" + -@$(DOCKER_COMPOSE) -f docker/docker-compose.yaml run --rm -u root -e COVERAGE_FILE=/opt/netbox/netbox/coverage/.coverage netbox sh -c "coverage run --source=netbox_diode_plugin ./manage.py test --keepdb netbox_diode_plugin && coverage xml -o /opt/netbox/netbox/coverage/report.xml && coverage report -m | tee /opt/netbox/netbox/coverage/report.txt" @$(MAKE) docker-compose-netbox-plugin-down diff --git a/README.md b/README.md index 82f5d9b..7793a0a 100644 --- a/README.md +++ b/README.md @@ -68,11 +68,11 @@ export DIODE_API_KEY=$(head -c20 /etc/netbox/config/plugins.py - -./manage.py configurediodeplugin || echo "❌ enabling diode-netbox-plugin failed" && exit 1 - -echo "✅ diode-netbox-plugin enabled successfully!" diff --git a/docker/netbox/launch-netbox.sh b/docker/netbox/launch-netbox.sh index df0b462..c3dfcb2 100755 --- a/docker/netbox/launch-netbox.sh +++ b/docker/netbox/launch-netbox.sh @@ -62,8 +62,6 @@ reload_netbox() { load_configuration & -sh /opt/netbox/enable-diode-plugin.sh & - reload_netbox & exec unitd \ diff --git a/netbox_diode_plugin/management/__init__.py b/netbox_diode_plugin/management/__init__.py deleted file mode 100644 index 6ac73c6..0000000 --- a/netbox_diode_plugin/management/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env python -# Copyright 2024 NetBox Labs Inc -"""Diode NetBox Plugin - Management.""" diff --git a/netbox_diode_plugin/management/commands/__init__.py b/netbox_diode_plugin/management/commands/__init__.py deleted file mode 100644 index 75483df..0000000 --- a/netbox_diode_plugin/management/commands/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env python -# Copyright 2024 NetBox Labs Inc -"""Diode NetBox Plugin - Management commands.""" diff --git a/netbox_diode_plugin/management/commands/configurediodeplugin.py b/netbox_diode_plugin/management/commands/configurediodeplugin.py deleted file mode 100644 index 0e26503..0000000 --- a/netbox_diode_plugin/management/commands/configurediodeplugin.py +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env python -# Copyright 2024 NetBox Labs Inc -"""Diode NetBox Plugin - Configure Diode plugin command.""" - -import os - -from django.conf import settings -from django.contrib.auth import get_user_model -from django.core.management.base import BaseCommand -from packaging import version -from users.models import Group, ObjectPermission, Token - -if version.parse(settings.VERSION).major >= 4: - from core.models import ObjectType as NetBoxType -else: - from django.contrib.contenttypes.models import ContentType as NetBoxType - -User = get_user_model() - - -def _create_user_with_token( - username: str, group: Group, is_superuser: bool = False -) -> User: - """Create a user with the given username and API key if it does not exist.""" - try: - user = User.objects.get(username=username) - except User.DoesNotExist: - if is_superuser: - user = User.objects.create_superuser(username=username, is_active=True) - else: - user = User.objects.create(username=username, is_active=True) - - user.groups.add(*[group.id]) - - if not Token.objects.filter(user=user).exists(): - Token.objects.create(user=user, key=os.getenv(f"{username}_API_KEY")) - - return user - - -class Command(BaseCommand): - """Configure NetBox Diode plugin.""" - - help = "Configure NetBox Diode plugin" - - diode_to_netbox_username = "DIODE_TO_NETBOX" - netbox_to_diode_username = "NETBOX_TO_DIODE" - diode_username = "DIODE" - - def handle(self, *args, **options): - """Handle command execution.""" - self.stdout.write("Configuring NetBox Diode plugin...") - - group, _ = Group.objects.get_or_create(name="diode") - - diode_to_netbox_user = _create_user_with_token( - self.diode_to_netbox_username, group - ) - _ = _create_user_with_token(self.netbox_to_diode_username, group, True) - _ = _create_user_with_token(self.diode_username, group) - - diode_plugin_object_type = NetBoxType.objects.get( - app_label="netbox_diode_plugin", model="diode" - ) - - permission, _ = ObjectPermission.objects.get_or_create( - name="Diode", - actions=["add", "view"], - ) - - permission.groups.set([group]) - permission.users.set([diode_to_netbox_user]) - permission.object_types.set([diode_plugin_object_type]) - - self.stdout.write("Finished.") diff --git a/netbox_diode_plugin/migrations/0001_initial.py b/netbox_diode_plugin/migrations/0001_initial.py new file mode 100644 index 0000000..3441166 --- /dev/null +++ b/netbox_diode_plugin/migrations/0001_initial.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python +# Copyright 2024 NetBox Labs Inc +"""Diode Netbox Plugin - Database migrations.""" + +import os + +from django.apps import apps as django_apps +from django.conf import settings +from django.contrib.contenttypes.management import create_contenttypes +from django.db import migrations, models +from users.models import Token as NetBoxToken + + +def _create_user_with_token(apps, username, group, is_superuser: bool = False): + User = apps.get_model(settings.AUTH_USER_MODEL) + """Create a user with the given username and API key if it does not exist.""" + try: + user = User.objects.get(username=username) + except User.DoesNotExist: + if is_superuser: + user = User.objects.create_superuser(username=username, is_active=True) + else: + user = User.objects.create(username=username, is_active=True) + + user.groups.add(*[group.id]) + + Token = apps.get_model("users", "Token") + + if not Token.objects.filter(user=user).exists(): + api_key = os.getenv(f"{username}_API_KEY") + if api_key is None: + api_key = NetBoxToken.generate_key() + Token.objects.create(user=user, key=api_key) + + return user + + +def configure_plugin(apps, schema_editor): + """Configure the plugin.""" + diode_to_netbox_username = "DIODE_TO_NETBOX" + netbox_to_diode_username = "NETBOX_TO_DIODE" + diode_username = "DIODE" + + Group = apps.get_model("users", "Group") + group, _ = Group.objects.get_or_create(name="diode") + + diode_to_netbox_user = _create_user_with_token( + apps, diode_to_netbox_username, group + ) + _ = _create_user_with_token(apps, netbox_to_diode_username, group, True) + _ = _create_user_with_token(apps, diode_username, group) + + app_config = django_apps.get_app_config("netbox_diode_plugin") + + create_contenttypes(app_config, verbosity=0) + + ContentType = apps.get_model("contenttypes", "ContentType") + + diode_plugin_object_type = ContentType.objects.get( + app_label="netbox_diode_plugin", model="diode" + ) + + ObjectPermission = apps.get_model("users", "ObjectPermission") + permission, _ = ObjectPermission.objects.get_or_create( + name="Diode", + actions=["add", "view"], + ) + + permission.groups.set([group.id]) + permission.users.set([diode_to_netbox_user.id]) + permission.object_types.set([diode_plugin_object_type.id]) + + +class Migration(migrations.Migration): + """Initial migration.""" + + initial = True + + dependencies = [ + ("contenttypes", "0001_initial"), + ("users", "0006_custom_group_model"), + ] + + operations = [ + migrations.CreateModel( + # Does not create any table / fields in the database + # Registers the Diode model as migrated + # This model is used to generate permissions for the Diode NetBox Plugin + name="Diode", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False + ), + ), + ], + options={ + "permissions": ( + ("view_diode", "Can view Diode"), + ("add_diode", "Can apply change sets from Diode"), + ), + "managed": False, + "default_permissions": (), + }, + ), + migrations.RunPython( + code=configure_plugin, reverse_code=migrations.RunPython.noop + ), + ] diff --git a/netbox_diode_plugin/tests/test_apply_change_set.py b/netbox_diode_plugin/tests/test_apply_change_set.py index 75804be..4940e54 100644 --- a/netbox_diode_plugin/tests/test_apply_change_set.py +++ b/netbox_diode_plugin/tests/test_apply_change_set.py @@ -29,10 +29,6 @@ class BaseApplyChangeSet(APITestCase): def setUp(self): """Set up test.""" - # Necessary to use with signals. - self.user_netbox_to_diode = User.objects.create_user(username="NETBOX_TO_DIODE") - Token.objects.create(user=self.user_netbox_to_diode) - self.user = User.objects.create_user(username="testcommonuser") self.add_permissions("netbox_diode_plugin.add_diode") self.user_token = Token.objects.create(user=self.user)