mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-25 01:48:38 -06:00
Introduce ConfigItem; add rack elevation parameters
This commit is contained in:
parent
82243732a1
commit
7c0f32e8ee
@ -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
|
||||||
|
@ -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 ConfigItem('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
|
||||||
|
@ -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_width=None,
|
||||||
unit_height=settings.RACK_ELEVATION_DEFAULT_UNIT_HEIGHT,
|
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)
|
||||||
|
|
||||||
|
@ -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', {
|
('Rack Elevations', {
|
||||||
# 'fields': ('RACK_ELEVATION_DEFAULT_UNIT_HEIGHT', 'RACK_ELEVATION_DEFAULT_UNIT_WIDTH'),
|
'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'),
|
||||||
}),
|
}),
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
),
|
||||||
|
|
||||||
)
|
)
|
||||||
|
@ -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'
|
||||||
|
@ -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 {},
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user