Merge pull request #3069 from digitalocean/2647-caching

intial work on #2647 - caching
This commit is contained in:
Jeremy Stretch 2019-04-15 17:16:10 -04:00 committed by GitHub
commit 1877afc760
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 148 additions and 11 deletions

View File

@ -1,6 +1,7 @@
sudo: required sudo: required
services: services:
- postgresql - postgresql
- redis-server
addons: addons:
postgresql: "9.4" postgresql: "9.4"
language: python language: python

View File

@ -18,6 +18,10 @@ django-filter
# https://github.com/django-mptt/django-mptt # https://github.com/django-mptt/django-mptt
django-mptt django-mptt
# Django caching using Redis
# https://github.com/niwinz/django-redis
django-redis
# Abstraction models for rendering and paginating HTML tables # Abstraction models for rendering and paginating HTML tables
# https://github.com/jieter/django-tables2 # https://github.com/jieter/django-tables2
django-tables2 django-tables2

View File

@ -1,9 +1,12 @@
from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import permission_required from django.contrib.auth.decorators import permission_required
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db import transaction from django.db import transaction
from django.db.models import Count from django.db.models import Count
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.generic import View from django.views.generic import View
from extras.models import Graph, GRAPH_TYPE_PROVIDER from extras.models import Graph, GRAPH_TYPE_PROVIDER
@ -32,6 +35,7 @@ class ProviderListView(PermissionRequiredMixin, ObjectListView):
class ProviderView(PermissionRequiredMixin, View): class ProviderView(PermissionRequiredMixin, View):
permission_required = 'circuits.view_provider' permission_required = 'circuits.view_provider'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, slug): def get(self, request, slug):
provider = get_object_or_404(Provider, slug=slug) provider = get_object_or_404(Provider, slug=slug)
@ -147,6 +151,7 @@ class CircuitListView(PermissionRequiredMixin, ObjectListView):
class CircuitView(PermissionRequiredMixin, View): class CircuitView(PermissionRequiredMixin, View):
permission_required = 'circuits.view_circuit' permission_required = 'circuits.view_circuit'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
circuit = get_object_or_404(Circuit.objects.select_related('provider', 'type', 'tenant__group'), pk=pk) circuit = get_object_or_404(Circuit.objects.select_related('provider', 'type', 'tenant__group'), pk=pk)

View File

@ -13,6 +13,8 @@ from django.urls import reverse
from django.utils.html import escape from django.utils.html import escape
from django.utils.http import is_safe_url from django.utils.http import is_safe_url
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.generic import View from django.views.generic import View
from circuits.models import Circuit from circuits.models import Circuit
@ -195,6 +197,7 @@ class SiteListView(PermissionRequiredMixin, ObjectListView):
class SiteView(PermissionRequiredMixin, View): class SiteView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_site' permission_required = 'dcim.view_site'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, slug): def get(self, request, slug):
site = get_object_or_404(Site.objects.select_related('region', 'tenant__group'), slug=slug) site = get_object_or_404(Site.objects.select_related('region', 'tenant__group'), slug=slug)
@ -353,6 +356,7 @@ class RackElevationListView(PermissionRequiredMixin, View):
""" """
permission_required = 'dcim.view_rack' permission_required = 'dcim.view_rack'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request): def get(self, request):
racks = Rack.objects.select_related( racks = Rack.objects.select_related(
@ -392,6 +396,7 @@ class RackElevationListView(PermissionRequiredMixin, View):
class RackView(PermissionRequiredMixin, View): class RackView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_rack' permission_required = 'dcim.view_rack'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
rack = get_object_or_404(Rack.objects.select_related('site__region', 'tenant__group', 'group', 'role'), pk=pk) rack = get_object_or_404(Rack.objects.select_related('site__region', 'tenant__group', 'group', 'role'), pk=pk)
@ -570,6 +575,7 @@ class DeviceTypeListView(PermissionRequiredMixin, ObjectListView):
class DeviceTypeView(PermissionRequiredMixin, View): class DeviceTypeView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_devicetype' permission_required = 'dcim.view_devicetype'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
devicetype = get_object_or_404(DeviceType, pk=pk) devicetype = get_object_or_404(DeviceType, pk=pk)
@ -910,6 +916,7 @@ class DeviceListView(PermissionRequiredMixin, ObjectListView):
class DeviceView(PermissionRequiredMixin, View): class DeviceView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_device' permission_required = 'dcim.view_device'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
device = get_object_or_404(Device.objects.select_related( device = get_object_or_404(Device.objects.select_related(
@ -991,6 +998,7 @@ class DeviceView(PermissionRequiredMixin, View):
class DeviceInventoryView(PermissionRequiredMixin, View): class DeviceInventoryView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_device' permission_required = 'dcim.view_device'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
device = get_object_or_404(Device, pk=pk) device = get_object_or_404(Device, pk=pk)
@ -1012,6 +1020,7 @@ class DeviceInventoryView(PermissionRequiredMixin, View):
class DeviceStatusView(PermissionRequiredMixin, View): class DeviceStatusView(PermissionRequiredMixin, View):
permission_required = ('dcim.view_device', 'dcim.napalm_read') permission_required = ('dcim.view_device', 'dcim.napalm_read')
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
device = get_object_or_404(Device, pk=pk) device = get_object_or_404(Device, pk=pk)
@ -1025,6 +1034,7 @@ class DeviceStatusView(PermissionRequiredMixin, View):
class DeviceLLDPNeighborsView(PermissionRequiredMixin, View): class DeviceLLDPNeighborsView(PermissionRequiredMixin, View):
permission_required = ('dcim.view_device', 'dcim.napalm_read') permission_required = ('dcim.view_device', 'dcim.napalm_read')
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
device = get_object_or_404(Device, pk=pk) device = get_object_or_404(Device, pk=pk)
@ -1042,6 +1052,7 @@ class DeviceLLDPNeighborsView(PermissionRequiredMixin, View):
class DeviceConfigView(PermissionRequiredMixin, View): class DeviceConfigView(PermissionRequiredMixin, View):
permission_required = ('dcim.view_device', 'dcim.napalm_read') permission_required = ('dcim.view_device', 'dcim.napalm_read')
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
device = get_object_or_404(Device, pk=pk) device = get_object_or_404(Device, pk=pk)
@ -1279,6 +1290,7 @@ class PowerOutletBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
class InterfaceView(PermissionRequiredMixin, View): class InterfaceView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_interface' permission_required = 'dcim.view_interface'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
interface = get_object_or_404(Interface, pk=pk) interface = get_object_or_404(Interface, pk=pk)
@ -1499,6 +1511,7 @@ class DeviceBayDeleteView(PermissionRequiredMixin, ObjectDeleteView):
class DeviceBayPopulateView(PermissionRequiredMixin, View): class DeviceBayPopulateView(PermissionRequiredMixin, View):
permission_required = 'dcim.change_devicebay' permission_required = 'dcim.change_devicebay'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
device_bay = get_object_or_404(DeviceBay, pk=pk) device_bay = get_object_or_404(DeviceBay, pk=pk)
@ -1533,6 +1546,7 @@ class DeviceBayPopulateView(PermissionRequiredMixin, View):
class DeviceBayDepopulateView(PermissionRequiredMixin, View): class DeviceBayDepopulateView(PermissionRequiredMixin, View):
permission_required = 'dcim.change_devicebay' permission_required = 'dcim.change_devicebay'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
device_bay = get_object_or_404(DeviceBay, pk=pk) device_bay = get_object_or_404(DeviceBay, pk=pk)
@ -1672,6 +1686,7 @@ class CableListView(PermissionRequiredMixin, ObjectListView):
class CableView(PermissionRequiredMixin, View): class CableView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_cable' permission_required = 'dcim.view_cable'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
cable = get_object_or_404(Cable, pk=pk) cable = get_object_or_404(Cable, pk=pk)
@ -1687,6 +1702,7 @@ class CableTraceView(PermissionRequiredMixin, View):
""" """
permission_required = 'dcim.view_cable' permission_required = 'dcim.view_cable'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, model, pk): def get(self, request, model, pk):
obj = get_object_or_404(model, pk=pk) obj = get_object_or_404(model, pk=pk)
@ -1726,6 +1742,7 @@ class CableCreateView(PermissionRequiredMixin, GetReturnURLMixin, View):
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
# Parse initial data manually to avoid setting field values as lists # Parse initial data manually to avoid setting field values as lists
@ -2042,6 +2059,7 @@ class VirtualChassisCreateView(PermissionRequiredMixin, View):
class VirtualChassisEditView(PermissionRequiredMixin, GetReturnURLMixin, View): class VirtualChassisEditView(PermissionRequiredMixin, GetReturnURLMixin, View):
permission_required = 'dcim.change_virtualchassis' permission_required = 'dcim.change_virtualchassis'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
virtual_chassis = get_object_or_404(VirtualChassis, pk=pk) virtual_chassis = get_object_or_404(VirtualChassis, pk=pk)
@ -2110,6 +2128,7 @@ class VirtualChassisDeleteView(PermissionRequiredMixin, ObjectDeleteView):
class VirtualChassisAddMemberView(PermissionRequiredMixin, GetReturnURLMixin, View): class VirtualChassisAddMemberView(PermissionRequiredMixin, GetReturnURLMixin, View):
permission_required = 'dcim.change_virtualchassis' permission_required = 'dcim.change_virtualchassis'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
virtual_chassis = get_object_or_404(VirtualChassis, pk=pk) virtual_chassis = get_object_or_404(VirtualChassis, pk=pk)
@ -2164,6 +2183,7 @@ class VirtualChassisAddMemberView(PermissionRequiredMixin, GetReturnURLMixin, Vi
class VirtualChassisRemoveMemberView(PermissionRequiredMixin, GetReturnURLMixin, View): class VirtualChassisRemoveMemberView(PermissionRequiredMixin, GetReturnURLMixin, View):
permission_required = 'dcim.change_virtualchassis' permission_required = 'dcim.change_virtualchassis'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
device = get_object_or_404(Device, pk=pk, virtual_chassis__isnull=False) device = get_object_or_404(Device, pk=pk, virtual_chassis__isnull=False)
@ -2227,6 +2247,7 @@ class PowerPanelListView(PermissionRequiredMixin, ObjectListView):
class PowerPanelView(PermissionRequiredMixin, View): class PowerPanelView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_powerpanel' permission_required = 'dcim.view_powerpanel'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
powerpanel = get_object_or_404(PowerPanel.objects.select_related('site', 'rack_group'), pk=pk) powerpanel = get_object_or_404(PowerPanel.objects.select_related('site', 'rack_group'), pk=pk)
@ -2296,6 +2317,7 @@ class PowerFeedListView(PermissionRequiredMixin, ObjectListView):
class PowerFeedView(PermissionRequiredMixin, View): class PowerFeedView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_powerfeed' permission_required = 'dcim.view_powerfeed'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
powerfeed = get_object_or_404(PowerFeed.objects.select_related('power_panel', 'rack'), pk=pk) powerfeed = get_object_or_404(PowerFeed.objects.select_related('power_panel', 'rack'), pk=pk)

View File

@ -7,6 +7,8 @@ from django.db.models import Count, Q
from django.http import Http404 from django.http import Http404
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.generic import View from django.views.generic import View
from django_tables2 import RequestConfig from django_tables2 import RequestConfig
@ -41,6 +43,7 @@ class TagListView(ObjectListView):
class TagView(View): class TagView(View):
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, slug): def get(self, request, slug):
tag = get_object_or_404(Tag, slug=slug) tag = get_object_or_404(Tag, slug=slug)
@ -108,6 +111,7 @@ class ConfigContextListView(PermissionRequiredMixin, ObjectListView):
class ConfigContextView(PermissionRequiredMixin, View): class ConfigContextView(PermissionRequiredMixin, View):
permission_required = 'extras.view_configcontext' permission_required = 'extras.view_configcontext'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
configcontext = get_object_or_404(ConfigContext, pk=pk) configcontext = get_object_or_404(ConfigContext, pk=pk)
@ -155,6 +159,7 @@ class ObjectConfigContextView(View):
object_class = None object_class = None
base_template = None base_template = None
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
obj = get_object_or_404(self.object_class, pk=pk) obj = get_object_or_404(self.object_class, pk=pk)
@ -187,6 +192,7 @@ class ObjectChangeListView(PermissionRequiredMixin, ObjectListView):
class ObjectChangeView(PermissionRequiredMixin, View): class ObjectChangeView(PermissionRequiredMixin, View):
permission_required = 'extras.view_objectchange' permission_required = 'extras.view_objectchange'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
objectchange = get_object_or_404(ObjectChange, pk=pk) objectchange = get_object_or_404(ObjectChange, pk=pk)
@ -209,6 +215,7 @@ class ObjectChangeLogView(View):
Present a history of changes made to a particular object. Present a history of changes made to a particular object.
""" """
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, model, **kwargs): def get(self, request, model, **kwargs):
# Get object my model and kwargs (e.g. slug='foo') # Get object my model and kwargs (e.g. slug='foo')
@ -282,6 +289,7 @@ class ReportListView(PermissionRequiredMixin, View):
""" """
permission_required = 'extras.view_reportresult' permission_required = 'extras.view_reportresult'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request): def get(self, request):
reports = get_reports() reports = get_reports()
@ -306,6 +314,7 @@ class ReportView(PermissionRequiredMixin, View):
""" """
permission_required = 'extras.view_reportresult' permission_required = 'extras.view_reportresult'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, name): def get(self, request, name):
# Retrieve the Report by "<module>.<report>" # Retrieve the Report by "<module>.<report>"

View File

@ -3,6 +3,8 @@ from django.conf import settings
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db.models import Count, Q from django.db.models import Count, Q
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.generic import View from django.views.generic import View
from django_tables2 import RequestConfig from django_tables2 import RequestConfig
@ -125,6 +127,7 @@ class VRFListView(PermissionRequiredMixin, ObjectListView):
class VRFView(PermissionRequiredMixin, View): class VRFView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_vrf' permission_required = 'ipam.view_vrf'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
vrf = get_object_or_404(VRF.objects.all(), pk=pk) vrf = get_object_or_404(VRF.objects.all(), pk=pk)
@ -319,6 +322,7 @@ class AggregateListView(PermissionRequiredMixin, ObjectListView):
class AggregateView(PermissionRequiredMixin, View): class AggregateView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_aggregate' permission_required = 'ipam.view_aggregate'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
aggregate = get_object_or_404(Aggregate, pk=pk) aggregate = get_object_or_404(Aggregate, pk=pk)
@ -456,6 +460,7 @@ class PrefixListView(PermissionRequiredMixin, ObjectListView):
class PrefixView(PermissionRequiredMixin, View): class PrefixView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_prefix' permission_required = 'ipam.view_prefix'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
prefix = get_object_or_404(Prefix.objects.select_related( prefix = get_object_or_404(Prefix.objects.select_related(
@ -500,6 +505,7 @@ class PrefixView(PermissionRequiredMixin, View):
class PrefixPrefixesView(PermissionRequiredMixin, View): class PrefixPrefixesView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_prefix' permission_required = 'ipam.view_prefix'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
prefix = get_object_or_404(Prefix.objects.all(), pk=pk) prefix = get_object_or_404(Prefix.objects.all(), pk=pk)
@ -543,6 +549,7 @@ class PrefixPrefixesView(PermissionRequiredMixin, View):
class PrefixIPAddressesView(PermissionRequiredMixin, View): class PrefixIPAddressesView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_prefix' permission_required = 'ipam.view_prefix'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
prefix = get_object_or_404(Prefix.objects.all(), pk=pk) prefix = get_object_or_404(Prefix.objects.all(), pk=pk)
@ -643,6 +650,7 @@ class IPAddressListView(PermissionRequiredMixin, ObjectListView):
class IPAddressView(PermissionRequiredMixin, View): class IPAddressView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_ipaddress' permission_required = 'ipam.view_ipaddress'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
ipaddress = get_object_or_404(IPAddress.objects.select_related('vrf__tenant', 'tenant'), pk=pk) ipaddress = get_object_or_404(IPAddress.objects.select_related('vrf__tenant', 'tenant'), pk=pk)
@ -726,6 +734,7 @@ class IPAddressAssignView(PermissionRequiredMixin, View):
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request): def get(self, request):
form = forms.IPAddressAssignForm() form = forms.IPAddressAssignForm()
@ -838,6 +847,7 @@ class VLANGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
class VLANGroupVLANsView(PermissionRequiredMixin, View): class VLANGroupVLANsView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_vlangroup' permission_required = 'ipam.view_vlangroup'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
vlan_group = get_object_or_404(VLANGroup.objects.all(), pk=pk) vlan_group = get_object_or_404(VLANGroup.objects.all(), pk=pk)
@ -888,6 +898,7 @@ class VLANListView(PermissionRequiredMixin, ObjectListView):
class VLANView(PermissionRequiredMixin, View): class VLANView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_vlan' permission_required = 'ipam.view_vlan'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
vlan = get_object_or_404(VLAN.objects.select_related( vlan = get_object_or_404(VLAN.objects.select_related(
@ -906,6 +917,7 @@ class VLANView(PermissionRequiredMixin, View):
class VLANMembersView(PermissionRequiredMixin, View): class VLANMembersView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_vlan' permission_required = 'ipam.view_vlan'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
vlan = get_object_or_404(VLAN.objects.all(), pk=pk) vlan = get_object_or_404(VLAN.objects.all(), pk=pk)
@ -984,6 +996,7 @@ class ServiceListView(PermissionRequiredMixin, ObjectListView):
class ServiceView(PermissionRequiredMixin, View): class ServiceView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_service' permission_required = 'ipam.view_service'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
service = get_object_or_404(Service, pk=pk) service = get_object_or_404(Service, pk=pk)

View File

@ -25,6 +25,16 @@ DATABASE = {
# https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-SECRET_KEY # https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-SECRET_KEY
SECRET_KEY = '' SECRET_KEY = ''
# Redis database settings. The Redis database is used for caching and background processing such as webhooks
REDIS = {
'HOST': 'localhost',
'PORT': 6379,
'PASSWORD': '',
'DATABASE': 0,
'DEFAULT_TIMEOUT': 300,
'SSL': False,
}
######################### #########################
# # # #
@ -50,6 +60,18 @@ BANNER_LOGIN = ''
# BASE_PATH = 'netbox/' # BASE_PATH = 'netbox/'
BASE_PATH = '' BASE_PATH = ''
# The fraction of entries that are culled when CACHE_MAX_ENTRIES is reached. The actual ratio is 1 / CACHE_CULL_FREQUENCY,
# so set CACHE_CULL_FREQUENCY to 2 to cull half the entries when CACHE_MAX_ENTRIES is reached. This setting should be an
# integer and defaults to 3
CACHE_CULL_FREQUENCY = 3
# Max number of entries (unique pages) to store in the cache at a time.
CACHE_MAX_ENTRIES = 300
# Cache timeout in seconds. Set to `None` to enforce an infinate timeout. Set to 0 to dissable caching by immediatly
# expiring keys. Defaults to 900 (15 minutes)
CACHE_TIMEOUT = 900
# Maximum number of days to retain logged changes. Set to 0 to retain changes indefinitely. (Default: 90) # Maximum number of days to retain logged changes. Set to 0 to retain changes indefinitely. (Default: 90)
CHANGELOG_RETENTION = 90 CHANGELOG_RETENTION = 90
@ -133,16 +155,6 @@ PAGINATE_COUNT = 50
# prefer IPv4 instead. # prefer IPv4 instead.
PREFER_IPV4 = False PREFER_IPV4 = False
# Redis database settings (optional). A Redis database is required only if the webhooks backend is enabled.
REDIS = {
'HOST': 'localhost',
'PORT': 6379,
'PASSWORD': '',
'DATABASE': 0,
'DEFAULT_TIMEOUT': 300,
'SSL': False,
}
# The file path where custom reports will be stored. A trailing slash is not needed. Note that the default value of # The file path where custom reports will be stored. A trailing slash is not needed. Note that the default value of
# this setting is derived from the installed location. # this setting is derived from the installed location.
# REPORTS_ROOT = '/opt/netbox/netbox/reports' # REPORTS_ROOT = '/opt/netbox/netbox/reports'

View File

@ -28,7 +28,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Import required configuration parameters # Import required configuration parameters
ALLOWED_HOSTS = DATABASE = SECRET_KEY = None ALLOWED_HOSTS = DATABASE = SECRET_KEY = None
for setting in ['ALLOWED_HOSTS', 'DATABASE', 'SECRET_KEY']: for setting in ['ALLOWED_HOSTS', 'DATABASE', 'SECRET_KEY', 'REDIS']:
try: try:
globals()[setting] = getattr(configuration, setting) globals()[setting] = getattr(configuration, setting)
except AttributeError: except AttributeError:
@ -44,6 +44,9 @@ BANNER_TOP = getattr(configuration, 'BANNER_TOP', '')
BASE_PATH = getattr(configuration, 'BASE_PATH', '') BASE_PATH = getattr(configuration, 'BASE_PATH', '')
if BASE_PATH: if BASE_PATH:
BASE_PATH = BASE_PATH.strip('/') + '/' # Enforce trailing slash only BASE_PATH = BASE_PATH.strip('/') + '/' # Enforce trailing slash only
CACHE_TIMEOUT = getattr(configuration, 'CACHE_TIMEOUT', 900)
CACHE_MAX_ENTRIES = getattr(configuration, 'CACHE_MAX_ENTRIES', 300)
CACHE_CULL_FREQUENCY = getattr(configuration, 'CACHE_CULL_FREQUENCY', 3)
CHANGELOG_RETENTION = getattr(configuration, 'CHANGELOG_RETENTION', 90) CHANGELOG_RETENTION = getattr(configuration, 'CHANGELOG_RETENTION', 90)
CORS_ORIGIN_ALLOW_ALL = getattr(configuration, 'CORS_ORIGIN_ALLOW_ALL', False) CORS_ORIGIN_ALLOW_ALL = getattr(configuration, 'CORS_ORIGIN_ALLOW_ALL', False)
CORS_ORIGIN_REGEX_WHITELIST = getattr(configuration, 'CORS_ORIGIN_REGEX_WHITELIST', []) CORS_ORIGIN_REGEX_WHITELIST = getattr(configuration, 'CORS_ORIGIN_REGEX_WHITELIST', [])
@ -157,6 +160,7 @@ INSTALLED_APPS = [
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'django.contrib.humanize', 'django.contrib.humanize',
'corsheaders', 'corsheaders',
'django_redis',
'debug_toolbar', 'debug_toolbar',
'django_filters', 'django_filters',
'django_tables2', 'django_tables2',
@ -218,6 +222,31 @@ TEMPLATES = [
}, },
] ]
# Caching
if REDIS_SSL:
REDIS_CACHE_CON_STRING = 'rediss://'
else:
REDIS_CACHE_CON_STRING = 'redis://'
if REDIS_PASSWORD:
REDIS_CACHE_CON_STRING = '{}@{}'.format(REDIS_PASSWORD, REDIS_CACHE_CON_STRING)
REDIS_CACHE_CON_STRING = '{}{}:{}/{}'.format(REDIS_CACHE_CON_STRING, REDIS_HOST, REDIS_PORT, REDIS_DATABASE)
CACHE_BACKEND = 'django_redis.cache.RedisCache'
CACHES = {
"default": {
"BACKEND": CACHE_BACKEND,
"LOCATION": REDIS_CACHE_CON_STRING,
'TIMEOUT': CACHE_TIMEOUT,
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"MAX_ENTRIES": CACHE_MAX_ENTRIES,
"CULL_FREQUENCY": CACHE_CULL_FREQUENCY
}
}
}
# WSGI # WSGI
WSGI_APPLICATION = 'netbox.wsgi.application' WSGI_APPLICATION = 'netbox.wsgi.application'
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

View File

@ -1,7 +1,10 @@
from collections import OrderedDict from collections import OrderedDict
from django.conf import settings
from django.db.models import Count, F from django.db.models import Count, F
from django.shortcuts import render from django.shortcuts import render
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.generic import View from django.views.generic import View
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.reverse import reverse from rest_framework.reverse import reverse
@ -160,6 +163,7 @@ SEARCH_TYPES = OrderedDict((
class HomeView(View): class HomeView(View):
template_name = 'home.html' template_name = 'home.html'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request): def get(self, request):
connected_consoleports = ConsolePort.objects.filter( connected_consoleports = ConsolePort.objects.filter(
@ -219,6 +223,7 @@ class HomeView(View):
class SearchView(View): class SearchView(View):
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request): def get(self, request):
# No query # No query
@ -272,6 +277,7 @@ class APIRootView(APIView):
def get_view_name(self): def get_view_name(self):
return "API Root" return "API Root"
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, format=None): def get(self, request, format=None):
return Response(OrderedDict(( return Response(OrderedDict((

View File

@ -1,5 +1,6 @@
import base64 import base64
from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import permission_required, login_required from django.contrib.auth.decorators import permission_required, login_required
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
@ -7,6 +8,7 @@ from django.db.models import Count
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse from django.urls import reverse
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.generic import View from django.views.generic import View
from dcim.models import Device from dcim.models import Device
@ -80,6 +82,7 @@ class SecretListView(PermissionRequiredMixin, ObjectListView):
class SecretView(PermissionRequiredMixin, View): class SecretView(PermissionRequiredMixin, View):
permission_required = 'secrets.view_secret' permission_required = 'secrets.view_secret'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
secret = get_object_or_404(Secret, pk=pk) secret = get_object_or_404(Secret, pk=pk)

View File

@ -1,6 +1,9 @@
from django.conf import settings
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db.models import Count from django.db.models import Count
from django.shortcuts import get_object_or_404, render from django.shortcuts import get_object_or_404, render
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.generic import View from django.views.generic import View
from circuits.models import Circuit from circuits.models import Circuit
@ -66,6 +69,7 @@ class TenantListView(PermissionRequiredMixin, ObjectListView):
class TenantView(PermissionRequiredMixin, View): class TenantView(PermissionRequiredMixin, View):
permission_required = 'tenancy.view_tenant' permission_required = 'tenancy.view_tenant'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, slug): def get(self, request, slug):
tenant = get_object_or_404(Tenant, slug=slug) tenant = get_object_or_404(Tenant, slug=slug)

View File

@ -6,6 +6,8 @@ from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.db.models import ManyToManyField from django.db.models import ManyToManyField
from django.http import Http404 from django.http import Http404
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from rest_framework.exceptions import APIException from rest_framework.exceptions import APIException
from rest_framework.permissions import BasePermission from rest_framework.permissions import BasePermission
from rest_framework.relations import PrimaryKeyRelatedField, RelatedField from rest_framework.relations import PrimaryKeyRelatedField, RelatedField
@ -248,6 +250,20 @@ class ModelViewSet(_ModelViewSet):
# Fall back to the hard-coded serializer class # Fall back to the hard-coded serializer class
return self.serializer_class return self.serializer_class
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def list(self, *args, **kwargs):
"""
Call to super to allow for caching
"""
return super().list(*args, **kwargs)
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def retrieve(self, *args, **kwargs):
"""
Call to super to allow for caching
"""
return super().retrieve(*args, **kwargs)
class FieldChoicesViewSet(ViewSet): class FieldChoicesViewSet(ViewSet):
""" """
@ -284,9 +300,11 @@ class FieldChoicesViewSet(ViewSet):
}) })
self._fields[key] = choices self._fields[key] = choices
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def list(self, request): def list(self, request):
return Response(self._fields) return Response(self._fields)
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def retrieve(self, request, pk): def retrieve(self, request, pk):
if pk not in self._fields: if pk not in self._fields:
raise Http404 raise Http404

View File

@ -17,6 +17,8 @@ from django.urls import reverse
from django.utils.html import escape from django.utils.html import escape
from django.utils.http import is_safe_url from django.utils.http import is_safe_url
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.decorators.csrf import requires_csrf_token from django.views.decorators.csrf import requires_csrf_token
from django.views.defaults import ERROR_500_TEMPLATE_NAME from django.views.defaults import ERROR_500_TEMPLATE_NAME
from django.views.generic import View from django.views.generic import View
@ -106,6 +108,7 @@ class ObjectListView(View):
return csv_data return csv_data
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request): def get(self, request):
model = self.queryset.model model = self.queryset.model
@ -713,6 +716,7 @@ class ComponentCreateView(View):
model_form = None model_form = None
template_name = None template_name = None
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
parent = get_object_or_404(self.parent_model, pk=pk) parent = get_object_or_404(self.parent_model, pk=pk)

View File

@ -1,9 +1,12 @@
from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db import transaction from django.db import transaction
from django.db.models import Count from django.db.models import Count
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse from django.urls import reverse
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.generic import View from django.views.generic import View
from dcim.models import Device, Interface from dcim.models import Device, Interface
@ -106,6 +109,7 @@ class ClusterListView(PermissionRequiredMixin, ObjectListView):
class ClusterView(PermissionRequiredMixin, View): class ClusterView(PermissionRequiredMixin, View):
permission_required = 'virtualization.view_cluster' permission_required = 'virtualization.view_cluster'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
cluster = get_object_or_404(Cluster, pk=pk) cluster = get_object_or_404(Cluster, pk=pk)
@ -168,6 +172,7 @@ class ClusterAddDevicesView(PermissionRequiredMixin, View):
form = forms.ClusterAddDevicesForm form = forms.ClusterAddDevicesForm
template_name = 'virtualization/cluster_add_devices.html' template_name = 'virtualization/cluster_add_devices.html'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
cluster = get_object_or_404(Cluster, pk=pk) cluster = get_object_or_404(Cluster, pk=pk)
@ -263,6 +268,7 @@ class VirtualMachineListView(PermissionRequiredMixin, ObjectListView):
class VirtualMachineView(PermissionRequiredMixin, View): class VirtualMachineView(PermissionRequiredMixin, View):
permission_required = 'virtualization.view_virtualmachine' permission_required = 'virtualization.view_virtualmachine'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk): def get(self, request, pk):
virtualmachine = get_object_or_404(VirtualMachine.objects.select_related('tenant__group'), pk=pk) virtualmachine = get_object_or_404(VirtualMachine.objects.select_related('tenant__group'), pk=pk)

View File

@ -3,6 +3,7 @@ django-cors-headers==2.5.2
django-debug-toolbar==1.11 django-debug-toolbar==1.11
django-filter==2.1.0 django-filter==2.1.0
django-mptt==0.9.1 django-mptt==0.9.1
django-redis==4.5.0
django-tables2==2.0.6 django-tables2==2.0.6
django-taggit==1.1.0 django-taggit==1.1.0
django-taggit-serializer==0.1.7 django-taggit-serializer==0.1.7