Browse Source

Introduce ConfigItem; add rack elevation parameters

jeremystretch 4 years ago
parent
commit
7c0f32e8ee

+ 3 - 2
netbox/dcim/api/serializers.py

@@ -13,6 +13,7 @@ from netbox.api import ChoiceField, ContentTypeField, SerializedPKRelatedField
 from netbox.api.serializers import (
     NestedGroupModelSerializer, PrimaryModelSerializer, ValidatedModelSerializer, WritableNestedSerializer,
 )
+from netbox.config import ConfigItem
 from tenancy.api.nested_serializers import NestedTenantSerializer
 from users.api.nested_serializers import NestedUserSerializer
 from utilities.api import get_serializer_for_model
@@ -229,10 +230,10 @@ class RackElevationDetailFilterSerializer(serializers.Serializer):
         default=RackElevationDetailRenderChoices.RENDER_JSON
     )
     unit_width = serializers.IntegerField(
-        default=settings.RACK_ELEVATION_DEFAULT_UNIT_WIDTH
+        default=ConfigItem('RACK_ELEVATION_DEFAULT_UNIT_WIDTH')
     )
     unit_height = serializers.IntegerField(
-        default=settings.RACK_ELEVATION_DEFAULT_UNIT_HEIGHT
+        default=ConfigItem('RACK_ELEVATION_DEFAULT_UNIT_HEIGHT')
     )
     legend_width = serializers.IntegerField(
         default=RACK_ELEVATION_LEGEND_WIDTH_DEFAULT

+ 2 - 4
netbox/dcim/models/devices.py

@@ -1,7 +1,6 @@
 from collections import OrderedDict
 
 import yaml
-from django.conf import settings
 from django.contrib.contenttypes.fields import GenericRelation
 from django.core.exceptions import ValidationError
 from django.core.validators import MaxValueValidator, MinValueValidator
@@ -15,7 +14,7 @@ from dcim.constants import *
 from extras.models import ConfigContextModel
 from extras.querysets import ConfigContextModelQuerySet
 from extras.utils import extras_features
-from netbox.config import ConfigResolver
+from netbox.config import ConfigItem
 from netbox.models import OrganizationalModel, PrimaryModel
 from utilities.choices import ColorChoices
 from utilities.fields import ColorField, NaturalOrderingField
@@ -816,8 +815,7 @@ class Device(PrimaryModel, ConfigContextModel):
 
     @property
     def primary_ip(self):
-        config = ConfigResolver()
-        if config.PREFER_IPV4 and self.primary_ip4:
+        if ConfigItem('PREFER_IPV4')() and self.primary_ip4:
             return self.primary_ip4
         elif self.primary_ip6:
             return self.primary_ip6

+ 7 - 3
netbox/dcim/models/racks.py

@@ -1,6 +1,5 @@
 from collections import OrderedDict
 
-from django.conf import settings
 from django.contrib.auth.models import User
 from django.contrib.contenttypes.fields import GenericRelation
 from django.contrib.contenttypes.models import ContentType
@@ -15,6 +14,7 @@ from dcim.choices import *
 from dcim.constants import *
 from dcim.svg import RackElevationSVG
 from extras.utils import extras_features
+from netbox.config import Config
 from netbox.models import OrganizationalModel, PrimaryModel
 from utilities.choices import ColorChoices
 from utilities.fields import ColorField, NaturalOrderingField
@@ -373,8 +373,8 @@ class Rack(PrimaryModel):
             self,
             face=DeviceFaceChoices.FACE_FRONT,
             user=None,
-            unit_width=settings.RACK_ELEVATION_DEFAULT_UNIT_WIDTH,
-            unit_height=settings.RACK_ELEVATION_DEFAULT_UNIT_HEIGHT,
+            unit_width=None,
+            unit_height=None,
             legend_width=RACK_ELEVATION_LEGEND_WIDTH_DEFAULT,
             include_images=True,
             base_url=None
@@ -393,6 +393,10 @@ class Rack(PrimaryModel):
         :param base_url: Base URL for links and images. If none, URLs will be relative.
         """
         elevation = RackElevationSVG(self, user=user, include_images=include_images, base_url=base_url)
+        if unit_width is None or unit_height is None:
+            config = Config()
+            unit_width = unit_width or config.RACK_ELEVATION_DEFAULT_UNIT_WIDTH
+            unit_height = unit_height or config.RACK_ELEVATION_DEFAULT_UNIT_HEIGHT
 
         return elevation.render(face, unit_width, unit_height, legend_width)
 

+ 3 - 3
netbox/extras/admin.py

@@ -10,9 +10,9 @@ class ConfigRevisionAdmin(admin.ModelAdmin):
         # ('Authentication', {
         #     'fields': ('LOGIN_REQUIRED', 'LOGIN_PERSISTENCE', 'LOGIN_TIMEOUT'),
         # }),
-        # ('Rack Elevations', {
-        #     'fields': ('RACK_ELEVATION_DEFAULT_UNIT_HEIGHT', 'RACK_ELEVATION_DEFAULT_UNIT_WIDTH'),
-        # }),
+        ('Rack Elevations', {
+            'fields': ('RACK_ELEVATION_DEFAULT_UNIT_HEIGHT', 'RACK_ELEVATION_DEFAULT_UNIT_WIDTH'),
+        }),
         ('IPAM', {
             'fields': ('ENFORCE_GLOBAL_UNIQUE', 'PREFER_IPV4'),
         }),

+ 4 - 7
netbox/ipam/models/ip.py

@@ -1,10 +1,9 @@
 import netaddr
-from django.conf import settings
 from django.contrib.contenttypes.fields import GenericForeignKey
 from django.contrib.contenttypes.models import ContentType
 from django.core.exceptions import ValidationError
 from django.db import models
-from django.db.models import F, Q
+from django.db.models import F
 from django.urls import reverse
 from django.utils.functional import cached_property
 
@@ -17,7 +16,7 @@ from ipam.fields import IPNetworkField, IPAddressField
 from ipam.managers import IPAddressManager
 from ipam.querysets import PrefixQuerySet
 from ipam.validators import DNSValidator
-from netbox.config import ConfigResolver
+from netbox.config import Config
 from utilities.querysets import RestrictedQuerySet
 from virtualization.models import VirtualMachine
 
@@ -317,8 +316,7 @@ class Prefix(PrimaryModel):
                 })
 
             # Enforce unique IP space (if applicable)
-            config = ConfigResolver()
-            if (self.vrf is None and config.ENFORCE_GLOBAL_UNIQUE) or (self.vrf and self.vrf.enforce_unique):
+            if (self.vrf is None and Config().ENFORCE_GLOBAL_UNIQUE) or (self.vrf and self.vrf.enforce_unique):
                 duplicate_prefixes = self.get_duplicates()
                 if duplicate_prefixes:
                     raise ValidationError({
@@ -813,8 +811,7 @@ class IPAddress(PrimaryModel):
                 })
 
             # Enforce unique IP space (if applicable)
-            config = ConfigResolver()
-            if (self.vrf is None and config.ENFORCE_GLOBAL_UNIQUE) or (self.vrf and self.vrf.enforce_unique):
+            if (self.vrf is None and Config().ENFORCE_GLOBAL_UNIQUE) or (self.vrf and self.vrf.enforce_unique):
                 duplicate_ips = self.get_duplicates()
                 if duplicate_ips and (
                         self.role not in IPADDRESS_ROLES_NONUNIQUE or

+ 19 - 4
netbox/netbox/config/__init__.py

@@ -4,18 +4,20 @@ from django.core.cache import cache
 from .parameters import PARAMS
 
 __all__ = (
-    'ConfigResolver',
+    'Config',
+    'ConfigItem',
     'PARAMS',
 )
 
 
-class ConfigResolver:
+class Config:
     """
-    Active NetBox configuration.
+    Fetch and store in memory the current NetBox configuration. This class must be instantiated prior to access, and
+    must be re-instantiated each time it's necessary to check for updates to the cached config.
     """
     def __init__(self):
         self.config = cache.get('config')
-        self.version = self.config.get('config_version')
+        self.version = cache.get('config_version')
         self.defaults = {param.name: param.default for param in PARAMS}
 
     def __getattr__(self, item):
@@ -33,3 +35,16 @@ class ConfigResolver:
             return self.defaults[item]
 
         raise AttributeError(f"Invalid configuration parameter: {item}")
+
+
+class ConfigItem:
+    """
+    A callable to retrieve a configuration parameter from the cache. This can serve as a placeholder to defer
+    referencing a configuration parameter.
+    """
+    def __init__(self, item):
+        self.item = item
+
+    def __call__(self):
+        config = Config()
+        return getattr(config, self.item)

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

@@ -52,4 +52,20 @@ PARAMS = (
         field=OptionalBooleanField
     ),
 
+    # Racks
+    ConfigParam(
+        name='RACK_ELEVATION_DEFAULT_UNIT_HEIGHT',
+        label='Rack Unit Height',
+        default=22,
+        description="Default unit height for rendered rack elevations",
+        field=forms.IntegerField
+    ),
+    ConfigParam(
+        name='RACK_ELEVATION_DEFAULT_UNIT_WIDTH',
+        label='Rack Unit Width',
+        default=220,
+        description="Default unit width for rendered rack elevations",
+        field=forms.IntegerField
+    ),
+
 )

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

@@ -77,14 +77,6 @@ ALLOWED_URL_SCHEMES = (
     'file', 'ftp', 'ftps', 'http', 'https', 'irc', 'mailto', 'sftp', 'ssh', 'tel', 'telnet', 'tftp', 'vnc', 'xmpp',
 )
 
-# Optionally display a persistent banner at the top and/or bottom of every page. HTML is allowed. To display the same
-# content in both banners, define BANNER_TOP and set BANNER_BOTTOM = BANNER_TOP.
-BANNER_TOP = ''
-BANNER_BOTTOM = ''
-
-# Text to include on the login page above the login form. HTML is allowed.
-BANNER_LOGIN = ''
-
 # Base URL path if accessing NetBox within a directory. For example, if installed at https://example.com/netbox/, set:
 # BASE_PATH = 'netbox/'
 BASE_PATH = ''
@@ -134,10 +126,6 @@ EMAIL = {
     'FROM_EMAIL': '',
 }
 
-# Enforcement of unique IP space can be toggled on a per-VRF basis. To enforce unique IP space within the global table
-# (all prefixes and IP addresses not assigned to a VRF), set ENFORCE_GLOBAL_UNIQUE to True.
-ENFORCE_GLOBAL_UNIQUE = False
-
 # Exempt certain models from the enforcement of view permissions. Models listed here will be viewable by all users and
 # by anonymous users. List models in the form `<app>.<model>`. Add '*' to this list to exempt all models.
 EXEMPT_VIEW_PERMISSIONS = [
@@ -229,14 +217,6 @@ PLUGINS = []
 #     }
 # }
 
-# When determining the primary IP address for a device, IPv6 is preferred over IPv4 by default. Set this to True to
-# prefer IPv4 instead.
-PREFER_IPV4 = False
-
-# Rack elevation size defaults, in pixels. For best results, the ratio of width to height should be roughly 10:1.
-RACK_ELEVATION_DEFAULT_UNIT_HEIGHT = 22
-RACK_ELEVATION_DEFAULT_UNIT_WIDTH = 220
-
 # Remote authentication support
 REMOTE_AUTH_ENABLED = False
 REMOTE_AUTH_BACKEND = 'netbox.authentication.RemoteUserBackend'

+ 2 - 2
netbox/netbox/context_processors.py

@@ -1,7 +1,7 @@
 from django.conf import settings as django_settings
 
 from extras.registry import registry
-from netbox.config import ConfigResolver
+from netbox.config import Config
 
 
 def settings_and_registry(request):
@@ -10,7 +10,7 @@ def settings_and_registry(request):
     """
     return {
         'settings': django_settings,
-        'config': ConfigResolver(),
+        'config': Config(),
         'registry': registry,
         'preferences': request.user.config if request.user.is_authenticated else {},
     }

+ 0 - 2
netbox/netbox/settings.py

@@ -140,8 +140,6 @@ NAPALM_PASSWORD = getattr(configuration, 'NAPALM_PASSWORD', '')
 NAPALM_TIMEOUT = getattr(configuration, 'NAPALM_TIMEOUT', 30)
 NAPALM_USERNAME = getattr(configuration, 'NAPALM_USERNAME', '')
 PAGINATE_COUNT = getattr(configuration, 'PAGINATE_COUNT', 50)
-RACK_ELEVATION_DEFAULT_UNIT_HEIGHT = getattr(configuration, 'RACK_ELEVATION_DEFAULT_UNIT_HEIGHT', 22)
-RACK_ELEVATION_DEFAULT_UNIT_WIDTH = getattr(configuration, 'RACK_ELEVATION_DEFAULT_UNIT_WIDTH', 220)
 RELEASE_CHECK_URL = getattr(configuration, 'RELEASE_CHECK_URL', None)
 
 # Validate update repo URL and timeout

+ 2 - 3
netbox/virtualization/models.py

@@ -8,7 +8,7 @@ from dcim.models import BaseInterface, Device
 from extras.models import ConfigContextModel
 from extras.querysets import ConfigContextModelQuerySet
 from extras.utils import extras_features
-from netbox.config import ConfigResolver
+from netbox.config import Config
 from netbox.models import OrganizationalModel, PrimaryModel
 from utilities.fields import NaturalOrderingField
 from utilities.ordering import naturalize_interface
@@ -340,8 +340,7 @@ class VirtualMachine(PrimaryModel, ConfigContextModel):
 
     @property
     def primary_ip(self):
-        config = ConfigResolver()
-        if config.PREFER_IPV4 and self.primary_ip4:
+        if Config().PREFER_IPV4 and self.primary_ip4:
             return self.primary_ip4
         elif self.primary_ip6:
             return self.primary_ip6