Explorar o código

Merge pull request #7778 from netbox-community/7775-dynamic-config

7775 dynamic config
Jeremy Stretch %!s(int64=4) %!d(string=hai) anos
pai
achega
c0653da736

+ 43 - 0
docs/configuration/dynamic-settings.md

@@ -31,6 +31,41 @@ This defines custom content to be displayed on the login page above the login fo
 
 ---
 
+## CHANGELOG_RETENTION
+
+Default: 90
+
+The number of days to retain logged changes (object creations, updates, and deletions). Set this to `0` to retain
+changes in the database indefinitely.
+
+!!! warning
+    If enabling indefinite changelog retention, it is recommended to periodically delete old entries. Otherwise, the database may eventually exceed capacity.
+
+---
+
+## CUSTOM_VALIDATORS
+
+This is a mapping of models to [custom validators](../customization/custom-validation.md) that have been defined locally to enforce custom validation logic. An example is provided below:
+
+```python
+CUSTOM_VALIDATORS = {
+    "dcim.site": [
+        {
+            "name": {
+                "min_length": 5,
+                "max_length": 30
+            }
+        },
+        "my_plugin.validators.Validator1"
+    ],
+    "dim.device": [
+        "my_plugin.validators.Validator1"
+    ]
+}
+```
+
+---
+
 ## ENFORCE_GLOBAL_UNIQUE
 
 Default: False
@@ -39,6 +74,14 @@ By default, NetBox will permit users to create duplicate prefixes and IP address
 
 ---
 
+## GRAPHQL_ENABLED
+
+Default: True
+
+Setting this to False will disable the GraphQL API.
+
+---
+
 ## MAINTENANCE_MODE
 
 Default: False

+ 0 - 36
docs/configuration/optional-settings.md

@@ -25,18 +25,6 @@ BASE_PATH = 'netbox/'
 
 ---
 
-## CHANGELOG_RETENTION
-
-Default: 90
-
-The number of days to retain logged changes (object creations, updates, and deletions). Set this to `0` to retain
-changes in the database indefinitely.
-
-!!! warning
-    If enabling indefinite changelog retention, it is recommended to periodically delete old entries. Otherwise, the database may eventually exceed capacity.
-
----
-
 ## CORS_ORIGIN_ALLOW_ALL
 
 Default: False
@@ -61,22 +49,6 @@ CORS_ORIGIN_WHITELIST = [
 
 ---
 
-## CUSTOM_VALIDATORS
-
-This is a mapping of models to [custom validators](../customization/custom-validation.md) that have been defined locally to enforce custom validation logic. An example is provided below:
-
-```python
-CUSTOM_VALIDATORS = {
-    'dcim.site': (
-        Validator1,
-        Validator2,
-        Validator3
-    )
-}
-```
-
----
-
 ## DEBUG
 
 Default: False
@@ -168,14 +140,6 @@ EXEMPT_VIEW_PERMISSIONS = ['*']
 
 ---
 
-## GRAPHQL_ENABLED
-
-Default: True
-
-Setting this to False will disable the GraphQL API.
-
----
-
 ## HTTP_PROXIES
 
 Default: None

+ 1 - 0
docs/release-notes/version-3.1.md

@@ -3,6 +3,7 @@
 ### Enhancements
 
 * [#7619](https://github.com/netbox-community/netbox/issues/7619) - Permit custom validation rules to be defined as plain data or dotted path to class
+* [#7775](https://github.com/netbox-community/netbox/issues/7775) - Enable dynamic config for `CHANGELOG_RETENTION`, `CUSTOM_VALIDATORS`, and `GRAPHQL_ENABLED`
 
 ### Bug Fixes
 

+ 4 - 1
netbox/extras/admin.py

@@ -27,11 +27,14 @@ class ConfigRevisionAdmin(admin.ModelAdmin):
         ('Pagination', {
             'fields': ('PAGINATE_COUNT', 'MAX_PAGE_SIZE'),
         }),
+        ('Validation', {
+            'fields': ('CUSTOM_VALIDATORS',),
+        }),
         ('NAPALM', {
             'fields': ('NAPALM_USERNAME', 'NAPALM_PASSWORD', 'NAPALM_TIMEOUT', 'NAPALM_ARGS'),
         }),
         ('Miscellaneous', {
-            'fields': ('MAINTENANCE_MODE', 'MAPS_URL'),
+            'fields': ('MAINTENANCE_MODE', 'GRAPHQL_ENABLED', 'CHANGELOG_RETENTION', 'MAPS_URL'),
         }),
         ('Config Revision', {
             'fields': ('comment',),

+ 6 - 4
netbox/extras/management/commands/housekeeping.py

@@ -10,12 +10,14 @@ from django.utils import timezone
 from packaging import version
 
 from extras.models import ObjectChange
+from netbox.config import Config
 
 
 class Command(BaseCommand):
     help = "Perform nightly housekeeping tasks. (This command can be run at any time.)"
 
     def handle(self, *args, **options):
+        config = Config()
 
         # Clear expired authentication sessions (essentially replicating the `clearsessions` command)
         if options['verbosity']:
@@ -37,10 +39,10 @@ class Command(BaseCommand):
         # Delete expired ObjectRecords
         if options['verbosity']:
             self.stdout.write("[*] Checking for expired changelog records")
-        if settings.CHANGELOG_RETENTION:
-            cutoff = timezone.now() - timedelta(days=settings.CHANGELOG_RETENTION)
+        if config.CHANGELOG_RETENTION:
+            cutoff = timezone.now() - timedelta(days=config.CHANGELOG_RETENTION)
             if options['verbosity'] >= 2:
-                self.stdout.write(f"\tRetention period: {settings.CHANGELOG_RETENTION} days")
+                self.stdout.write(f"\tRetention period: {config.CHANGELOG_RETENTION} days")
                 self.stdout.write(f"\tCut-off time: {cutoff}")
             expired_records = ObjectChange.objects.filter(time__lt=cutoff).count()
             if expired_records:
@@ -58,7 +60,7 @@ class Command(BaseCommand):
                 self.stdout.write("\tNo expired records found.", self.style.SUCCESS)
         elif options['verbosity']:
             self.stdout.write(
-                f"\tSkipping: No retention period specified (CHANGELOG_RETENTION = {settings.CHANGELOG_RETENTION})"
+                f"\tSkipping: No retention period specified (CHANGELOG_RETENTION = {config.CHANGELOG_RETENTION})"
             )
 
         # Check for new releases (if enabled)

+ 3 - 2
netbox/extras/signals.py

@@ -1,13 +1,13 @@
 import importlib
 import logging
 
-from django.conf import settings
 from django.contrib.contenttypes.models import ContentType
 from django.db.models.signals import m2m_changed, post_save, pre_delete
 from django.dispatch import receiver, Signal
 from django_prometheus.models import model_deletes, model_inserts, model_updates
 
 from extras.validators import CustomValidator
+from netbox.config import get_config
 from netbox.signals import post_clean
 from .choices import ObjectChangeActionChoices
 from .models import ConfigRevision, CustomField, ObjectChange
@@ -159,8 +159,9 @@ m2m_changed.connect(handle_cf_removed_obj_types, sender=CustomField.content_type
 
 @receiver(post_clean)
 def run_custom_validators(sender, instance, **kwargs):
+    config = get_config()
     model_name = f'{sender._meta.app_label}.{sender._meta.model_name}'
-    validators = settings.CUSTOM_VALIDATORS.get(model_name, [])
+    validators = config.CUSTOM_VALIDATORS.get(model_name, [])
 
     for validator in validators:
 

+ 23 - 0
netbox/netbox/config/parameters.py

@@ -94,6 +94,15 @@ PARAMS = (
         field=forms.IntegerField
     ),
 
+    # Validation
+    ConfigParam(
+        name='CUSTOM_VALIDATORS',
+        label='Custom validators',
+        default={},
+        description="Custom validation rules (JSON)",
+        field=forms.JSONField
+    ),
+
     # NAPALM
     ConfigParam(
         name='NAPALM_USERNAME',
@@ -130,6 +139,20 @@ PARAMS = (
         description="Enable maintenance mode",
         field=forms.BooleanField
     ),
+    ConfigParam(
+        name='GRAPHQL_ENABLED',
+        label='GraphQL enabled',
+        default=True,
+        description="Enable the GraphQL API",
+        field=forms.BooleanField
+    ),
+    ConfigParam(
+        name='CHANGELOG_RETENTION',
+        label='Changelog retention',
+        default=90,
+        description="Days to retain changelog history (set to zero for unlimited)",
+        field=forms.IntegerField
+    ),
     ConfigParam(
         name='MAPS_URL',
         label='Maps URL',

+ 0 - 20
netbox/netbox/configuration.example.py

@@ -76,9 +76,6 @@ ADMINS = [
 # BASE_PATH = 'netbox/'
 BASE_PATH = ''
 
-# Maximum number of days to retain logged changes. Set to 0 to retain changes indefinitely. (Default: 90)
-CHANGELOG_RETENTION = 90
-
 # API Cross-Origin Resource Sharing (CORS) settings. If CORS_ORIGIN_ALLOW_ALL is set to True, all origins will be
 # allowed. Otherwise, define a list of allowed origins using either CORS_ORIGIN_WHITELIST or
 # CORS_ORIGIN_REGEX_WHITELIST. For more information, see https://github.com/ottoyiu/django-cors-headers
@@ -90,20 +87,6 @@ CORS_ORIGIN_REGEX_WHITELIST = [
     # r'^(https?://)?(\w+\.)?example\.com$',
 ]
 
-# Specify any custom validators here, as a mapping of model to a list of validators classes. Validators should be
-# instances of or inherit from CustomValidator.
-# from extras.validators import CustomValidator
-CUSTOM_VALIDATORS = {
-    # 'dcim.site': [
-    #     CustomValidator({
-    #         'name': {
-    #             'min_length': 10,
-    #             'regex': r'\d{3}$',
-    #         }
-    #     })
-    # ],
-}
-
 # Set to True to enable server debugging. WARNING: Debugging introduces a substantial performance penalty and may reveal
 # sensitive information about your installation. Only enable debugging while performing testing. Never enable debugging
 # on a production system.
@@ -129,9 +112,6 @@ EXEMPT_VIEW_PERMISSIONS = [
     # 'ipam.prefix',
 ]
 
-# Enable the GraphQL API
-GRAPHQL_ENABLED = True
-
 # HTTP proxies NetBox should use when sending outbound HTTP requests (e.g. for webhooks).
 # HTTP_PROXIES = {
 #     'http': 'http://10.10.1.10:3128',

+ 3 - 1
netbox/netbox/graphql/views.py

@@ -6,6 +6,7 @@ from graphene_django.views import GraphQLView as GraphQLView_
 from rest_framework.exceptions import AuthenticationFailed
 
 from netbox.api.authentication import TokenAuthentication
+from netbox.config import get_config
 
 
 class GraphQLView(GraphQLView_):
@@ -15,9 +16,10 @@ class GraphQLView(GraphQLView_):
     graphiql_template = 'graphiql.html'
 
     def dispatch(self, request, *args, **kwargs):
+        config = get_config()
 
         # Enforce GRAPHQL_ENABLED
-        if not settings.GRAPHQL_ENABLED:
+        if not config.GRAPHQL_ENABLED:
             return HttpResponseNotFound("The GraphQL API is not enabled.")
 
         # Attempt to authenticate the user using a DRF token, if provided

+ 0 - 3
netbox/netbox/settings.py

@@ -80,11 +80,9 @@ ADMINS = getattr(configuration, 'ADMINS', [])
 BASE_PATH = getattr(configuration, 'BASE_PATH', '')
 if BASE_PATH:
     BASE_PATH = BASE_PATH.strip('/') + '/'  # Enforce trailing slash only
-CHANGELOG_RETENTION = getattr(configuration, 'CHANGELOG_RETENTION', 90)
 CORS_ORIGIN_ALLOW_ALL = getattr(configuration, 'CORS_ORIGIN_ALLOW_ALL', False)
 CORS_ORIGIN_REGEX_WHITELIST = getattr(configuration, 'CORS_ORIGIN_REGEX_WHITELIST', [])
 CORS_ORIGIN_WHITELIST = getattr(configuration, 'CORS_ORIGIN_WHITELIST', [])
-CUSTOM_VALIDATORS = getattr(configuration, 'CUSTOM_VALIDATORS', {})
 DATE_FORMAT = getattr(configuration, 'DATE_FORMAT', 'N j, Y')
 DATETIME_FORMAT = getattr(configuration, 'DATETIME_FORMAT', 'N j, Y g:i a')
 DEBUG = getattr(configuration, 'DEBUG', False)
@@ -92,7 +90,6 @@ DEVELOPER = getattr(configuration, 'DEVELOPER', False)
 DOCS_ROOT = getattr(configuration, 'DOCS_ROOT', os.path.join(os.path.dirname(BASE_DIR), 'docs'))
 EMAIL = getattr(configuration, 'EMAIL', {})
 EXEMPT_VIEW_PERMISSIONS = getattr(configuration, 'EXEMPT_VIEW_PERMISSIONS', [])
-GRAPHQL_ENABLED = getattr(configuration, 'GRAPHQL_ENABLED', True)
 HTTP_PROXIES = getattr(configuration, 'HTTP_PROXIES', None)
 INTERNAL_IPS = getattr(configuration, 'INTERNAL_IPS', ('127.0.0.1', '::1'))
 LOGGING = getattr(configuration, 'LOGGING', {})

+ 1 - 1
netbox/templates/base/layout.html

@@ -127,7 +127,7 @@
                 </a>
 
                 {# GraphQL API #}
-                {% if settings.GRAPHQL_ENABLED %}
+                {% if config.GRAPHQL_ENABLED %}
                   <a type="button" class="nav-link" href="{% url 'graphql' %}" target="_blank">
                     <i title="GraphQL API" class="mdi mdi-graphql text-primary" data-bs-placement="top" data-bs-toggle="tooltip"></i>
                   </a>