1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Merge v2.5 work

This commit is contained in:
Jeremy Stretch
2018-12-07 10:51:28 -05:00
396 changed files with 8663 additions and 6511 deletions

View File

@@ -1,7 +1,7 @@
from django.conf import settings
from django.contrib.admin import AdminSite
from django.contrib.auth.models import Group, User
from django.contrib.auth.admin import GroupAdmin, UserAdmin
from django.contrib.auth.models import Group, User
from taggit.admin import TagAdmin
from taggit.models import Tag

View File

@@ -1,5 +1,3 @@
from __future__ import unicode_literals
from django.conf import settings
from rest_framework import authentication, exceptions
from rest_framework.pagination import LimitOffsetPagination
@@ -59,16 +57,15 @@ class TokenPermissions(DjangoModelPermissions):
"""
def __init__(self):
# LOGIN_REQUIRED determines whether read-only access is provided to anonymous users.
from django.conf import settings
self.authenticated_users_only = settings.LOGIN_REQUIRED
super(TokenPermissions, self).__init__()
super().__init__()
def has_permission(self, request, view):
# If token authentication is in use, verify that the token allows write operations (for unsafe methods).
if request.method not in SAFE_METHODS and isinstance(request.auth, Token):
if not request.auth.write_enabled:
return False
return super(TokenPermissions, self).has_permission(request, view)
return super().has_permission(request, view)
#
@@ -84,10 +81,17 @@ class OptionalLimitOffsetPagination(LimitOffsetPagination):
def paginate_queryset(self, queryset, request, view=None):
try:
self.count = queryset.count()
except (AttributeError, TypeError):
if hasattr(queryset, 'all'):
# TODO: This breaks filtering by annotated values
# Make a clone of the queryset with any annotations stripped (performance hack)
qs = queryset.all()
qs.query.annotations.clear()
self.count = qs.count()
else:
# We're dealing with an iterable, not a QuerySet
self.count = len(queryset)
self.limit = self.get_limit(request)
self.offset = self.get_offset(request)
self.request = request
@@ -128,7 +132,7 @@ class OptionalLimitOffsetPagination(LimitOffsetPagination):
if not self.limit:
return None
return super(OptionalLimitOffsetPagination, self).get_next_link()
return super().get_next_link()
def get_previous_link(self):
@@ -136,7 +140,7 @@ class OptionalLimitOffsetPagination(LimitOffsetPagination):
if not self.limit:
return None
return super(OptionalLimitOffsetPagination, self).get_previous_link()
return super().get_previous_link()
#

View File

@@ -91,6 +91,10 @@ LOGGING = {}
# are permitted to access most data in NetBox (excluding secrets) but not make any changes.
LOGIN_REQUIRED = False
# The length of time (in seconds) for which a user will remain logged into the web UI before being prompted to
# re-authenticate. (Default: 1209600 [14 days])
LOGIN_TIMEOUT = None
# Setting this to True will display a "maintenance mode" banner at the top of every page.
MAINTENANCE_MODE = False
@@ -121,10 +125,6 @@ PAGINATE_COUNT = 50
# prefer IPv4 instead.
PREFER_IPV4 = False
# The Webhook event backend is disabled by default. Set this to True to enable it. Note that this requires a Redis
# database be configured and accessible by NetBox (see `REDIS` below).
WEBHOOKS_ENABLED = False
# Redis database settings (optional). A Redis database is required only if the webhooks backend is enabled.
REDIS = {
'HOST': 'localhost',
@@ -138,9 +138,18 @@ REDIS = {
# this setting is derived from the installed location.
# REPORTS_ROOT = '/opt/netbox/netbox/reports'
# By default, NetBox will store session data in the database. Alternatively, a file path can be specified here to use
# local file storage instead. (This can be useful for enabling authentication on a standby instance with read-only
# database access.) Note that the user as which NetBox runs must have read and write permissions to this path.
SESSION_FILE_PATH = None
# Time zone (default: UTC)
TIME_ZONE = 'UTC'
# The webhooks backend is disabled by default. Set this to True to enable it. Note that this requires a Redis
# database be configured and accessible by NetBox.
WEBHOOKS_ENABLED = False
# Date/time formatting. See the following link for supported formats:
# https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
DATE_FORMAT = 'N j, Y'

View File

@@ -1,5 +1,3 @@
from __future__ import unicode_literals
from django import forms
from utilities.forms import BootstrapMixin

View File

@@ -7,6 +7,13 @@ import warnings
from django.contrib.messages import constants as messages
from django.core.exceptions import ImproperlyConfigured
# Check for Python 3.5+
if sys.version_info < (3, 5):
raise RuntimeError(
"NetBox requires Python 3.5 or higher (current: Python {})".format(sys.version.split()[0])
)
# Check for configuration file
try:
from netbox import configuration
except ImportError:
@@ -14,15 +21,8 @@ except ImportError:
"Configuration file is not present. Please define netbox/netbox/configuration.py per the documentation."
)
# Raise a deprecation warning for Python 2.x
if sys.version_info[0] < 3:
warnings.warn(
"Support for Python 2 will be removed in NetBox v2.5. Please consider migration to Python 3 at your earliest "
"opportunity. Guidance is available in the documentation at http://netbox.readthedocs.io/.",
DeprecationWarning
)
VERSION = '2.4.9'
VERSION = '2.5-beta2'
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@@ -55,6 +55,7 @@ ENFORCE_GLOBAL_UNIQUE = getattr(configuration, 'ENFORCE_GLOBAL_UNIQUE', False)
EMAIL = getattr(configuration, 'EMAIL', {})
LOGGING = getattr(configuration, 'LOGGING', {})
LOGIN_REQUIRED = getattr(configuration, 'LOGIN_REQUIRED', False)
LOGIN_TIMEOUT = getattr(configuration, 'LOGIN_TIMEOUT', None)
MAINTENANCE_MODE = getattr(configuration, 'MAINTENANCE_MODE', False)
MAX_PAGE_SIZE = getattr(configuration, 'MAX_PAGE_SIZE', 1000)
MEDIA_ROOT = getattr(configuration, 'MEDIA_ROOT', os.path.join(BASE_DIR, 'media')).rstrip('/')
@@ -66,6 +67,7 @@ PAGINATE_COUNT = getattr(configuration, 'PAGINATE_COUNT', 50)
PREFER_IPV4 = getattr(configuration, 'PREFER_IPV4', False)
REPORTS_ROOT = getattr(configuration, 'REPORTS_ROOT', os.path.join(BASE_DIR, 'reports')).rstrip('/')
REDIS = getattr(configuration, 'REDIS', {})
SESSION_FILE_PATH = getattr(configuration, 'SESSION_FILE_PATH', None)
SHORT_DATE_FORMAT = getattr(configuration, 'SHORT_DATE_FORMAT', 'Y-m-d')
SHORT_DATETIME_FORMAT = getattr(configuration, 'SHORT_DATETIME_FORMAT', 'Y-m-d H:i')
SHORT_TIME_FORMAT = getattr(configuration, 'SHORT_TIME_FORMAT', 'H:i:s')
@@ -112,6 +114,17 @@ DATABASES = {
'default': configuration.DATABASE,
}
# Sessions
if LOGIN_TIMEOUT is not None:
if type(LOGIN_TIMEOUT) is not int or LOGIN_TIMEOUT < 0:
raise ImproperlyConfigured(
"LOGIN_TIMEOUT must be a positive integer (value: {})".format(LOGIN_TIMEOUT)
)
# Django default is 1209600 seconds (14 days)
SESSION_COOKIE_AGE = LOGIN_TIMEOUT
if SESSION_FILE_PATH is not None:
SESSION_ENGINE = 'django.contrib.sessions.backends.file'
# Redis
REDIS_HOST = REDIS.get('HOST', 'localhost')
REDIS_PORT = REDIS.get('PORT', 6379)
@@ -235,7 +248,7 @@ SECRETS_MIN_PUBKEY_SIZE = 2048
# Django filters
FILTERS_NULL_CHOICE_LABEL = 'None'
FILTERS_NULL_CHOICE_VALUE = '0' # Must be a string
FILTERS_NULL_CHOICE_VALUE = 'null'
# Django REST framework (API)
REST_FRAMEWORK_VERSION = VERSION[0:3] # Use major.minor as API version

View File

@@ -1,10 +1,8 @@
from __future__ import unicode_literals
from django.conf import settings
from django.conf.urls import include, url
from django.views.static import serve
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
from drf_yasg.views import get_schema_view
from netbox.views import APIRootView, HomeView, SearchView
from users.views import LoginView, LogoutView

View File

@@ -1,8 +1,6 @@
from __future__ import unicode_literals
from collections import OrderedDict
from django.db.models import Count
from django.db.models import Count, F
from django.shortcuts import render
from django.views.generic import View
from rest_framework.response import Response
@@ -16,8 +14,7 @@ from dcim.filters import (
DeviceFilter, DeviceTypeFilter, RackFilter, RackGroupFilter, SiteFilter, VirtualChassisFilter
)
from dcim.models import (
ConsolePort, Device, DeviceType, InterfaceConnection, PowerPort, Rack, RackGroup, Site,
VirtualChassis
Cable, ConsolePort, Device, DeviceType, Interface, PowerPort, Rack, RackGroup, Site, VirtualChassis
)
from dcim.tables import (
DeviceDetailTable, DeviceTypeTable, RackTable, RackGroupTable, SiteTable, VirtualChassisTable
@@ -159,6 +156,18 @@ class HomeView(View):
def get(self, request):
connected_consoleports = ConsolePort.objects.filter(
connected_endpoint__isnull=False
)
connected_powerports = PowerPort.objects.filter(
connected_endpoint__isnull=False
)
connected_interfaces = Interface.objects.filter(
_connected_interface__isnull=False,
pk__lt=F('_connected_interface')
)
cables = Cable.objects.all()
stats = {
# Organization
@@ -168,9 +177,10 @@ class HomeView(View):
# DCIM
'rack_count': Rack.objects.count(),
'device_count': Device.objects.count(),
'interface_connections_count': InterfaceConnection.objects.count(),
'console_connections_count': ConsolePort.objects.filter(cs_port__isnull=False).count(),
'power_connections_count': PowerPort.objects.filter(power_outlet__isnull=False).count(),
'interface_connections_count': connected_interfaces.count(),
'cable_count': cables.count(),
'console_connections_count': connected_consoleports.count(),
'power_connections_count': connected_powerports.count(),
# IPAM
'vrf_count': VRF.objects.count(),

View File

@@ -2,7 +2,6 @@ import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "netbox.settings")
application = get_wsgi_application()