Przeglądaj źródła

Introduce ConfigItem; add rack elevation parameters

jeremystretch 4 lat temu
rodzic
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 (
 from netbox.api.serializers import (
     NestedGroupModelSerializer, PrimaryModelSerializer, ValidatedModelSerializer, WritableNestedSerializer,
     NestedGroupModelSerializer, PrimaryModelSerializer, ValidatedModelSerializer, WritableNestedSerializer,
 )
 )
+from netbox.config import ConfigItem
 from tenancy.api.nested_serializers import NestedTenantSerializer
 from tenancy.api.nested_serializers import NestedTenantSerializer
 from users.api.nested_serializers import NestedUserSerializer
 from users.api.nested_serializers import NestedUserSerializer
 from utilities.api import get_serializer_for_model
 from utilities.api import get_serializer_for_model
@@ -229,10 +230,10 @@ class RackElevationDetailFilterSerializer(serializers.Serializer):
         default=RackElevationDetailRenderChoices.RENDER_JSON
         default=RackElevationDetailRenderChoices.RENDER_JSON
     )
     )
     unit_width = serializers.IntegerField(
     unit_width = serializers.IntegerField(
-        default=settings.RACK_ELEVATION_DEFAULT_UNIT_WIDTH
+        default=ConfigItem('RACK_ELEVATION_DEFAULT_UNIT_WIDTH')
     )
     )
     unit_height = serializers.IntegerField(
     unit_height = serializers.IntegerField(
-        default=settings.RACK_ELEVATION_DEFAULT_UNIT_HEIGHT
+        default=ConfigItem('RACK_ELEVATION_DEFAULT_UNIT_HEIGHT')
     )
     )
     legend_width = serializers.IntegerField(
     legend_width = serializers.IntegerField(
         default=RACK_ELEVATION_LEGEND_WIDTH_DEFAULT
         default=RACK_ELEVATION_LEGEND_WIDTH_DEFAULT

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

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

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

@@ -1,6 +1,5 @@
 from collections import OrderedDict
 from collections import OrderedDict
 
 
-from django.conf import settings
 from django.contrib.auth.models import User
 from django.contrib.auth.models import User
 from django.contrib.contenttypes.fields import GenericRelation
 from django.contrib.contenttypes.fields import GenericRelation
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.models import ContentType
@@ -15,6 +14,7 @@ from dcim.choices import *
 from dcim.constants import *
 from dcim.constants import *
 from dcim.svg import RackElevationSVG
 from dcim.svg import RackElevationSVG
 from extras.utils import extras_features
 from extras.utils import extras_features
+from netbox.config import Config
 from netbox.models import OrganizationalModel, PrimaryModel
 from netbox.models import OrganizationalModel, PrimaryModel
 from utilities.choices import ColorChoices
 from utilities.choices import ColorChoices
 from utilities.fields import ColorField, NaturalOrderingField
 from utilities.fields import ColorField, NaturalOrderingField
@@ -373,8 +373,8 @@ class Rack(PrimaryModel):
             self,
             self,
             face=DeviceFaceChoices.FACE_FRONT,
             face=DeviceFaceChoices.FACE_FRONT,
             user=None,
             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,
             legend_width=RACK_ELEVATION_LEGEND_WIDTH_DEFAULT,
             include_images=True,
             include_images=True,
             base_url=None
             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.
         :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)
         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)
         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', {
         # ('Authentication', {
         #     'fields': ('LOGIN_REQUIRED', 'LOGIN_PERSISTENCE', 'LOGIN_TIMEOUT'),
         #     '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', {
         ('IPAM', {
             'fields': ('ENFORCE_GLOBAL_UNIQUE', 'PREFER_IPV4'),
             'fields': ('ENFORCE_GLOBAL_UNIQUE', 'PREFER_IPV4'),
         }),
         }),

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

@@ -1,10 +1,9 @@
 import netaddr
 import netaddr
-from django.conf import settings
 from django.contrib.contenttypes.fields import GenericForeignKey
 from django.contrib.contenttypes.fields import GenericForeignKey
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.models import ContentType
 from django.core.exceptions import ValidationError
 from django.core.exceptions import ValidationError
 from django.db import models
 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.urls import reverse
 from django.utils.functional import cached_property
 from django.utils.functional import cached_property
 
 
@@ -17,7 +16,7 @@ from ipam.fields import IPNetworkField, IPAddressField
 from ipam.managers import IPAddressManager
 from ipam.managers import IPAddressManager
 from ipam.querysets import PrefixQuerySet
 from ipam.querysets import PrefixQuerySet
 from ipam.validators import DNSValidator
 from ipam.validators import DNSValidator
-from netbox.config import ConfigResolver
+from netbox.config import Config
 from utilities.querysets import RestrictedQuerySet
 from utilities.querysets import RestrictedQuerySet
 from virtualization.models import VirtualMachine
 from virtualization.models import VirtualMachine
 
 
@@ -317,8 +316,7 @@ class Prefix(PrimaryModel):
                 })
                 })
 
 
             # Enforce unique IP space (if applicable)
             # 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()
                 duplicate_prefixes = self.get_duplicates()
                 if duplicate_prefixes:
                 if duplicate_prefixes:
                     raise ValidationError({
                     raise ValidationError({
@@ -813,8 +811,7 @@ class IPAddress(PrimaryModel):
                 })
                 })
 
 
             # Enforce unique IP space (if applicable)
             # 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()
                 duplicate_ips = self.get_duplicates()
                 if duplicate_ips and (
                 if duplicate_ips and (
                         self.role not in IPADDRESS_ROLES_NONUNIQUE or
                         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
 from .parameters import PARAMS
 
 
 __all__ = (
 __all__ = (
-    'ConfigResolver',
+    'Config',
+    'ConfigItem',
     'PARAMS',
     '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):
     def __init__(self):
         self.config = cache.get('config')
         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}
         self.defaults = {param.name: param.default for param in PARAMS}
 
 
     def __getattr__(self, item):
     def __getattr__(self, item):
@@ -33,3 +35,16 @@ class ConfigResolver:
             return self.defaults[item]
             return self.defaults[item]
 
 
         raise AttributeError(f"Invalid configuration parameter: {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
         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',
     '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 URL path if accessing NetBox within a directory. For example, if installed at https://example.com/netbox/, set:
 # BASE_PATH = 'netbox/'
 # BASE_PATH = 'netbox/'
 BASE_PATH = ''
 BASE_PATH = ''
@@ -134,10 +126,6 @@ EMAIL = {
     'FROM_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
 # 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.
 # by anonymous users. List models in the form `<app>.<model>`. Add '*' to this list to exempt all models.
 EXEMPT_VIEW_PERMISSIONS = [
 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 authentication support
 REMOTE_AUTH_ENABLED = False
 REMOTE_AUTH_ENABLED = False
 REMOTE_AUTH_BACKEND = 'netbox.authentication.RemoteUserBackend'
 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 django.conf import settings as django_settings
 
 
 from extras.registry import registry
 from extras.registry import registry
-from netbox.config import ConfigResolver
+from netbox.config import Config
 
 
 
 
 def settings_and_registry(request):
 def settings_and_registry(request):
@@ -10,7 +10,7 @@ def settings_and_registry(request):
     """
     """
     return {
     return {
         'settings': django_settings,
         'settings': django_settings,
-        'config': ConfigResolver(),
+        'config': Config(),
         'registry': registry,
         'registry': registry,
         'preferences': request.user.config if request.user.is_authenticated else {},
         '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_TIMEOUT = getattr(configuration, 'NAPALM_TIMEOUT', 30)
 NAPALM_USERNAME = getattr(configuration, 'NAPALM_USERNAME', '')
 NAPALM_USERNAME = getattr(configuration, 'NAPALM_USERNAME', '')
 PAGINATE_COUNT = getattr(configuration, 'PAGINATE_COUNT', 50)
 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)
 RELEASE_CHECK_URL = getattr(configuration, 'RELEASE_CHECK_URL', None)
 
 
 # Validate update repo URL and timeout
 # 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.models import ConfigContextModel
 from extras.querysets import ConfigContextModelQuerySet
 from extras.querysets import ConfigContextModelQuerySet
 from extras.utils import extras_features
 from extras.utils import extras_features
-from netbox.config import ConfigResolver
+from netbox.config import Config
 from netbox.models import OrganizationalModel, PrimaryModel
 from netbox.models import OrganizationalModel, PrimaryModel
 from utilities.fields import NaturalOrderingField
 from utilities.fields import NaturalOrderingField
 from utilities.ordering import naturalize_interface
 from utilities.ordering import naturalize_interface
@@ -340,8 +340,7 @@ class VirtualMachine(PrimaryModel, ConfigContextModel):
 
 
     @property
     @property
     def primary_ip(self):
     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
             return self.primary_ip4
         elif self.primary_ip6:
         elif self.primary_ip6:
             return self.primary_ip6
             return self.primary_ip6