diff --git a/netbox/netbox/views/__init__.py b/netbox/netbox/views/__init__.py index 5406e7206..ccd349678 100644 --- a/netbox/netbox/views/__init__.py +++ b/netbox/netbox/views/__init__.py @@ -5,7 +5,7 @@ from django.conf import settings from django.contrib.contenttypes.models import ContentType from django.db.models import F from django.http import HttpResponseServerError -from django.shortcuts import render +from django.shortcuts import redirect, render from django.template import loader from django.template.exceptions import TemplateDoesNotExist from django.urls import reverse @@ -30,9 +30,11 @@ from virtualization.models import Cluster, VirtualMachine class HomeView(View): - template_name = 'home.html' + template_name = 'pages/home.html' def get(self, request): + if settings.LOGIN_REQUIRED and not request.user.is_authenticated: + return redirect("login") connected_consoleports = ConsolePort.objects.restrict(request.user, 'view').prefetch_related('_path').filter( _path__destination_id__isnull=False @@ -52,42 +54,72 @@ class HomeView(View): status__in=JobResultStatusChoices.TERMINAL_STATE_CHOICES ).defer('data')[:10] - stats = { - # Organization - 'site_count': Site.objects.restrict(request.user, 'view').count(), - 'tenant_count': Tenant.objects.restrict(request.user, 'view').count(), - - # DCIM - 'rack_count': Rack.objects.restrict(request.user, 'view').count(), - 'devicetype_count': DeviceType.objects.restrict(request.user, 'view').count(), - 'device_count': Device.objects.restrict(request.user, 'view').count(), - 'interface_connections_count': connected_interfaces.count(), - 'cable_count': Cable.objects.restrict(request.user, 'view').count(), - 'console_connections_count': connected_consoleports.count(), - 'power_connections_count': connected_powerports.count(), - 'powerpanel_count': PowerPanel.objects.restrict(request.user, 'view').count(), - 'powerfeed_count': PowerFeed.objects.restrict(request.user, 'view').count(), - - # IPAM - 'vrf_count': VRF.objects.restrict(request.user, 'view').count(), - 'aggregate_count': Aggregate.objects.restrict(request.user, 'view').count(), - 'prefix_count': Prefix.objects.restrict(request.user, 'view').count(), - 'ipaddress_count': IPAddress.objects.restrict(request.user, 'view').count(), - 'vlan_count': VLAN.objects.restrict(request.user, 'view').count(), - - # Circuits - 'provider_count': Provider.objects.restrict(request.user, 'view').count(), - 'circuit_count': Circuit.objects.restrict(request.user, 'view').count(), - - # Secrets - 'secret_count': Secret.objects.restrict(request.user, 'view').count(), - - # Virtualization - 'cluster_count': Cluster.objects.restrict(request.user, 'view').count(), - 'virtualmachine_count': VirtualMachine.objects.restrict(request.user, 'view').count(), - - } + def build_stats(): + perms = request.user.get_all_permissions() + stats = [] + org = ( + ("dcim.view_site", "Sites", "Geographic locations", Site.objects.restrict(request.user, 'view').count), + ("tenancy.view_tenant", "Tenants", "Customers or departments", Tenant.objects.restrict(request.user, 'view').count), + ) + dcim = ( + ("dcim.view_rack", "Racks", "Equipment racks, optionally organized by group", Rack.objects.restrict(request.user, 'view').count), + ("dcim.view_devicetype", "Device Types", "Physical hardware models by manufacturer", DeviceType.objects.restrict(request.user, 'view').count), + ("dcim.view_device", "Devices", "Rack-mounted infrastructure", Device.objects.restrict(request.user, 'view').count), + ) + ipam = ( + ("ipam.view_vrf", "VRFs", "Virtual routing & forwarding tables", VRF.objects.restrict(request.user, 'view').count), + ("ipam.view_aggregate", "Aggregates", "Top-level IP allocations", Aggregate.objects.restrict(request.user, 'view').count), + ("ipam.view_prefix", "Prefixes", "IPv4 & IPv6 network assignments", Prefix.objects.restrict(request.user, 'view').count), + ("ipam.view_ipaddress", "IP Addresses", "Individual IPv4 & IPv6 addresses", IPAddress.objects.restrict(request.user, 'view').count), + ("ipam.view_vlan", "VLANs", "Layer 2 domains, by VLAN ID", VLAN.objects.restrict(request.user, 'view').count) + + ) + circuits = ( + ("circuits.view_provider", "Providers", "Organizations that provide circuits", Provider.objects.restrict(request.user, 'view').count), + ("circuits.view_circuit", "Circuits", "Communication links for transit, transport, & other services", Circuit.objects.restrict(request.user, 'view').count), + ) + secrets = ( + ("secrets.view_secret", "Secrets", "Cryptographically secured data", Secret.objects.restrict(request.user, 'view').count), + ) + virtualization = ( + ("virtualization.view_cluster", "Clusters", "Clusters of physical virtual machine hosts", Cluster.objects.restrict(request.user, 'view').count), + ("virtualization.view_virtualmachine", "Virtual Machines", "Virtual compute instances running inside clusters", VirtualMachine.objects.restrict(request.user, 'view').count), + + ) + connections = ( + ("dcim.view_cable", "Cables", None, Cable.objects.restrict(request.user, 'view').count), + ("dcim.view_consoleport", "Console", None, connected_consoleports.count), + ("dcim.view_interface", "Interfaces", None, connected_interfaces.count), + ("dcim.view_powerport", "Power Connections",None, connected_powerports.count), + ) + power = ( + ("dcim.view_powerpanel", "Power Panels", "Electrical panels receiving utility power", PowerPanel.objects.restrict(request.user, 'view').count ), + ("dcim.view_powerfeed", "Power Feeds", "Electrical circuits delivering power from panels", PowerFeed.objects.restrict(request.user, 'view').count), + ) + sections = ( + ("Organization", org), + ("IPAM", ipam), + ("Virtualization", virtualization), + ("Inventory", dcim), + ("Connections", connections), + ("Circuits", circuits), + ("Secrets", secrets), + ("Power", power), + ) + for section_label, section_items in sections: + stat = {"label": section_label, "items": []} + for perm, item_label, description, get_count in section_items: + if perm in perms: + app, scope = perm.split(".") + url = ":".join((app, scope.replace("view_", "") + "_list")) + stat["items"].append( + {"label": item_label, "description": description, "count": get_count(), "url": url} + ) + stats.append(stat) + return stats + + changelog = ObjectChange.objects.restrict(request.user, 'view').prefetch_related('user', 'changed_object_type') @@ -105,7 +137,7 @@ class HomeView(View): return render(request, self.template_name, { 'search_form': SearchForm(), - 'stats': stats, + 'stats': build_stats(), 'report_results': report_results, 'changelog': changelog[:15], 'new_release': new_release,