Skip to content

Commit 8af6b64

Browse files
authored
Remove NB scripts, introduce distributed RunTests (#102)
* all steps and launcher are ready * introduce dimi, refactor dependencies * wip introducing dimi * introduce dimi * everything except template is ready * gui for running tests * remove two-phase and rollback * run tests button * almost working * fix work splitup * fully working runtests * report table and filterset * remove old scripts * delete scripts migration * api for running tests * fix log debug * netbox version compatibility * final tests * disable fail fast * add system levels * backports to support 3.7 * stop running tests for 3.6 * page-header -> header * remove unneeded template parts * adjust runtests template
1 parent ef15f08 commit 8af6b64

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+2223
-739
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ jobs:
2121
test:
2222
runs-on: ubuntu-latest
2323
strategy:
24+
fail-fast: false
2425
matrix:
25-
netbox_version: [v3.6.9, v3.7.8, v4.0.7]
26+
netbox_version: [v3.7.8, v4.0.11]
2627
steps:
2728
- name: Checkout
2829
uses: actions/checkout@v3
@@ -58,8 +59,9 @@ jobs:
5859
test_migrations:
5960
runs-on: ubuntu-latest
6061
strategy:
62+
fail-fast: false
6163
matrix:
62-
netbox_version: [v3.6.9, v3.7.8, v4.0.2]
64+
netbox_version: [v3.7.8, v4.0.11]
6365
steps:
6466
- name: Checkout
6567
uses: actions/checkout@v3

development/.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ REDIS_PASSWORD=redis
66
SECRET_KEY=SOME_ARBITRARY_LONG_ENOUGH_DJANGO_SECRET_KEY_STRING
77
COMPOSE_PROJECT_NAME=validity
88
DEBUGWEB=0
9+
DEBUGWORKER=0

development/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ RUN mkdir -p /opt/netbox \
1616

1717
# Install Validity
1818
COPY . /plugin/validity
19+
COPY ./development/start.sh /opt/netbox/netbox/
1920
RUN pip install --editable /plugin/validity[dev]
2021

2122
WORKDIR /opt/netbox/netbox/

development/docker-compose.yaml

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ services:
55
dockerfile: ./development/Dockerfile
66
args:
77
NETBOX_VERSION: ${NETBOX_VERSION}
8-
command: sh -c "python manage.py rqworker"
8+
command: ./start.sh $DEBUGWORKER manage.py rqworker
9+
ports:
10+
- "5679:5678"
911
depends_on:
1012
- postgres
1113
- redis
@@ -24,13 +26,7 @@ services:
2426

2527
netbox:
2628
<<: *worker
27-
command: >
28-
bash -c "
29-
if [[ $DEBUGWEB == 1 ]]; then
30-
python -m debugpy --listen 0.0.0.0:5678 manage.py runserver 0.0.0.0:8000;
31-
else
32-
python manage.py runserver 0.0.0.0:8000;
33-
fi"
29+
command: ./start.sh $DEBUGWEB manage.py runserver 0.0.0.0:8000
3430
ports:
3531
- "8000:8000"
3632
- "5678:5678"

development/start.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/bash
2+
3+
DEBUG=$1
4+
shift
5+
6+
if [[ $DEBUG == 1 ]]; then
7+
echo "!!! DEBUGGING IS ENABLED !!!"
8+
python -m debugpy --listen 0.0.0.0:5678 $@
9+
else
10+
python $@
11+
fi

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ branch = true
6060
omit = [
6161
"validity/tests/*",
6262
"validity/migrations/*",
63+
"validity/dependencies.py",
6364
]
6465
source = ["validity"]
6566

requirements/base.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
deepdiff>=6.2.0,<7
2+
dimi < 2
23
django-bootstrap5 >=24.2,<25
34
dulwich # Core NetBox "optional" requirement
45
jq>=1.4.0,<2

validity/__init__.py

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import logging
22

3-
from django.conf import settings as django_settings
3+
from dimi import Container
44
from netbox.settings import VERSION
5-
from pydantic import BaseModel, Field
65

76
from validity.utils.version import NetboxVersion
87

@@ -31,20 +30,12 @@ class NetBoxValidityConfig(PluginConfig):
3130
netbox_version = NetboxVersion(VERSION)
3231

3332
def ready(self):
34-
import validity.data_backends
33+
from validity import data_backends, dependencies, signals
3534

3635
return super().ready()
3736

3837

3938
config = NetBoxValidityConfig
4039

4140

42-
class ValiditySettings(BaseModel):
43-
store_last_results: int = Field(default=5, gt=0, lt=1001)
44-
store_reports: int = Field(default=5, gt=0, lt=1001)
45-
sleep_between_tests: float = 0
46-
result_batch_size: int = Field(default=500, ge=1)
47-
polling_threads: int = Field(default=500, ge=1)
48-
49-
50-
settings = ValiditySettings.model_validate(django_settings.PLUGINS_CONFIG.get("validity", {}))
41+
di = Container()

validity/api/helpers.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from django.core.exceptions import ValidationError
66
from django.db.models import ManyToManyField
77
from netbox.api.serializers import WritableNestedSerializer
8+
from rest_framework.relations import PrimaryKeyRelatedField
89
from rest_framework.serializers import JSONField, ModelSerializer
910

1011
from validity import NetboxVersion
@@ -94,3 +95,13 @@ def validate(self, attrs):
9495
]
9596
raise ValidationError({instance.subform_json_field: errors})
9697
return attrs
98+
99+
100+
class PrimaryKeyField(PrimaryKeyRelatedField):
101+
"""
102+
Returns primary key only instead of the whole model instance
103+
"""
104+
105+
def to_internal_value(self, data):
106+
obj = super().to_internal_value(data)
107+
return obj.pk

validity/api/serializers.py

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
from core.api.nested_serializers import NestedDataFileSerializer, NestedDataSourceSerializer
2+
from core.api.serializers import JobSerializer
3+
from core.models import DataSource
24
from dcim.api.nested_serializers import (
35
NestedDeviceSerializer,
46
NestedDeviceTypeSerializer,
@@ -7,7 +9,9 @@
79
NestedPlatformSerializer,
810
NestedSiteSerializer,
911
)
10-
from dcim.models import DeviceType, Location, Manufacturer, Platform, Site
12+
from dcim.models import Device, DeviceType, Location, Manufacturer, Platform, Site
13+
from django.utils import timezone
14+
from django.utils.translation import gettext_lazy as _
1115
from extras.api.nested_serializers import NestedTagSerializer
1216
from extras.models import Tag
1317
from netbox.api.fields import SerializedPKRelatedField
@@ -18,7 +22,15 @@
1822
from tenancy.models import Tenant
1923

2024
from validity import config, models
21-
from .helpers import EncryptedDictField, FieldsMixin, ListQPMixin, SubformValidationMixin, nested_factory
25+
from validity.choices import ExplanationVerbosityChoices
26+
from .helpers import (
27+
EncryptedDictField,
28+
FieldsMixin,
29+
ListQPMixin,
30+
PrimaryKeyField,
31+
SubformValidationMixin,
32+
nested_factory,
33+
)
2234

2335

2436
class ComplianceSelectorSerializer(NetBoxModelSerializer):
@@ -370,3 +382,30 @@ def to_representation(self, instance):
370382
if name_filter := self.get_list_param("name"):
371383
instance = [item for item in instance if item.name in set(name_filter)]
372384
return super().to_representation(instance)
385+
386+
387+
class RunTestsSerializer(serializers.Serializer):
388+
sync_datasources = serializers.BooleanField(required=False)
389+
selectors = PrimaryKeyField(
390+
many=True,
391+
required=False,
392+
queryset=models.ComplianceSelector.objects.all(),
393+
)
394+
devices = PrimaryKeyField(many=True, required=False, queryset=Device.objects.all())
395+
test_tags = PrimaryKeyField(many=True, required=False, queryset=Tag.objects.all())
396+
explanation_verbosity = serializers.ChoiceField(
397+
choices=ExplanationVerbosityChoices.choices, required=False, default=ExplanationVerbosityChoices.maximum
398+
)
399+
overriding_datasource = PrimaryKeyField(required=False, queryset=DataSource.objects.all())
400+
workers_num = serializers.IntegerField(min_value=1, default=1)
401+
schedule_at = serializers.DateTimeField(required=False, allow_null=True)
402+
schedule_interval = serializers.IntegerField(required=False, allow_null=True)
403+
404+
def validate_schedule_at(self, value):
405+
if value and value < timezone.now():
406+
raise serializers.ValidationError(_("Scheduled time must be in the future."))
407+
return value
408+
409+
410+
class ScriptResultSerializer(serializers.Serializer):
411+
result = JobSerializer(read_only=True)

0 commit comments

Comments
 (0)