From 68f53aaa87137d3b22e4b6275b565a363e36b253 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Fri, 15 Jul 2022 15:24:47 -0400 Subject: [PATCH] Closes #9745: Add wireless LANs and links to global search --- docs/release-notes/version-3.2.md | 1 + netbox/netbox/constants.py | 256 +---------------------------- netbox/netbox/forms/__init__.py | 2 +- netbox/netbox/search.py | 261 ++++++++++++++++++++++++++++++ netbox/netbox/views/__init__.py | 3 +- 5 files changed, 266 insertions(+), 257 deletions(-) create mode 100644 netbox/netbox/search.py diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index 6ec077b8d..3eb6a7a15 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -5,6 +5,7 @@ ### Enhancements * [#9741](https://github.com/netbox-community/netbox/issues/9741) - Check for UserConfig instance during user login +* [#9745](https://github.com/netbox-community/netbox/issues/9745) - Add wireless LANs and links to global search ### Bug Fixes diff --git a/netbox/netbox/constants.py b/netbox/netbox/constants.py index cc04e9aa8..61588d331 100644 --- a/netbox/netbox/constants.py +++ b/netbox/netbox/constants.py @@ -1,256 +1,2 @@ -from collections import OrderedDict -from typing import Dict - -import circuits.filtersets -import circuits.tables -import dcim.filtersets -import dcim.tables -import ipam.filtersets -import ipam.tables -import tenancy.filtersets -import tenancy.tables -import virtualization.filtersets -import virtualization.tables -from circuits.models import Circuit, ProviderNetwork, Provider -from dcim.models import ( - Cable, Device, DeviceType, Location, Module, ModuleType, PowerFeed, Rack, RackReservation, Site, VirtualChassis, -) -from ipam.models import Aggregate, ASN, IPAddress, Prefix, Service, VLAN, VRF -from tenancy.models import Contact, Tenant, ContactAssignment -from utilities.utils import count_related -from virtualization.models import Cluster, VirtualMachine - +# Max results per object type SEARCH_MAX_RESULTS = 15 - -CIRCUIT_TYPES = OrderedDict( - ( - ('provider', { - 'queryset': Provider.objects.annotate( - count_circuits=count_related(Circuit, 'provider') - ), - 'filterset': circuits.filtersets.ProviderFilterSet, - 'table': circuits.tables.ProviderTable, - 'url': 'circuits:provider_list', - }), - ('circuit', { - 'queryset': Circuit.objects.prefetch_related( - 'type', 'provider', 'tenant', 'tenant__group', 'terminations__site' - ), - 'filterset': circuits.filtersets.CircuitFilterSet, - 'table': circuits.tables.CircuitTable, - 'url': 'circuits:circuit_list', - }), - ('providernetwork', { - 'queryset': ProviderNetwork.objects.prefetch_related('provider'), - 'filterset': circuits.filtersets.ProviderNetworkFilterSet, - 'table': circuits.tables.ProviderNetworkTable, - 'url': 'circuits:providernetwork_list', - }), - ) -) - - -DCIM_TYPES = OrderedDict( - ( - ('site', { - 'queryset': Site.objects.prefetch_related('region', 'tenant', 'tenant__group'), - 'filterset': dcim.filtersets.SiteFilterSet, - 'table': dcim.tables.SiteTable, - 'url': 'dcim:site_list', - }), - ('rack', { - 'queryset': Rack.objects.prefetch_related('site', 'location', 'tenant', 'tenant__group', 'role').annotate( - device_count=count_related(Device, 'rack') - ), - 'filterset': dcim.filtersets.RackFilterSet, - 'table': dcim.tables.RackTable, - 'url': 'dcim:rack_list', - }), - ('rackreservation', { - 'queryset': RackReservation.objects.prefetch_related('site', 'rack', 'user'), - 'filterset': dcim.filtersets.RackReservationFilterSet, - 'table': dcim.tables.RackReservationTable, - 'url': 'dcim:rackreservation_list', - }), - ('location', { - 'queryset': Location.objects.add_related_count( - Location.objects.add_related_count( - Location.objects.all(), - Device, - 'location', - 'device_count', - cumulative=True - ), - Rack, - 'location', - 'rack_count', - cumulative=True - ).prefetch_related('site'), - 'filterset': dcim.filtersets.LocationFilterSet, - 'table': dcim.tables.LocationTable, - 'url': 'dcim:location_list', - }), - ('devicetype', { - 'queryset': DeviceType.objects.prefetch_related('manufacturer').annotate( - instance_count=count_related(Device, 'device_type') - ), - 'filterset': dcim.filtersets.DeviceTypeFilterSet, - 'table': dcim.tables.DeviceTypeTable, - 'url': 'dcim:devicetype_list', - }), - ('device', { - 'queryset': Device.objects.prefetch_related( - 'device_type__manufacturer', 'device_role', 'tenant', 'tenant__group', 'site', 'rack', 'primary_ip4', 'primary_ip6', - ), - 'filterset': dcim.filtersets.DeviceFilterSet, - 'table': dcim.tables.DeviceTable, - 'url': 'dcim:device_list', - }), - ('moduletype', { - 'queryset': ModuleType.objects.prefetch_related('manufacturer').annotate( - instance_count=count_related(Module, 'module_type') - ), - 'filterset': dcim.filtersets.ModuleTypeFilterSet, - 'table': dcim.tables.ModuleTypeTable, - 'url': 'dcim:moduletype_list', - }), - ('module', { - 'queryset': Module.objects.prefetch_related( - 'module_type__manufacturer', 'device', 'module_bay', - ), - 'filterset': dcim.filtersets.ModuleFilterSet, - 'table': dcim.tables.ModuleTable, - 'url': 'dcim:module_list', - }), - ('virtualchassis', { - 'queryset': VirtualChassis.objects.prefetch_related('master').annotate( - member_count=count_related(Device, 'virtual_chassis') - ), - 'filterset': dcim.filtersets.VirtualChassisFilterSet, - 'table': dcim.tables.VirtualChassisTable, - 'url': 'dcim:virtualchassis_list', - }), - ('cable', { - 'queryset': Cable.objects.all(), - 'filterset': dcim.filtersets.CableFilterSet, - 'table': dcim.tables.CableTable, - 'url': 'dcim:cable_list', - }), - ('powerfeed', { - 'queryset': PowerFeed.objects.all(), - 'filterset': dcim.filtersets.PowerFeedFilterSet, - 'table': dcim.tables.PowerFeedTable, - 'url': 'dcim:powerfeed_list', - }), - ) -) - -IPAM_TYPES = OrderedDict( - ( - ('vrf', { - 'queryset': VRF.objects.prefetch_related('tenant', 'tenant__group'), - 'filterset': ipam.filtersets.VRFFilterSet, - 'table': ipam.tables.VRFTable, - 'url': 'ipam:vrf_list', - }), - ('aggregate', { - 'queryset': Aggregate.objects.prefetch_related('rir'), - 'filterset': ipam.filtersets.AggregateFilterSet, - 'table': ipam.tables.AggregateTable, - 'url': 'ipam:aggregate_list', - }), - ('prefix', { - 'queryset': Prefix.objects.prefetch_related('site', 'vrf__tenant', 'tenant', 'tenant__group', 'vlan', 'role'), - 'filterset': ipam.filtersets.PrefixFilterSet, - 'table': ipam.tables.PrefixTable, - 'url': 'ipam:prefix_list', - }), - ('ipaddress', { - 'queryset': IPAddress.objects.prefetch_related('vrf__tenant', 'tenant', 'tenant__group'), - 'filterset': ipam.filtersets.IPAddressFilterSet, - 'table': ipam.tables.IPAddressTable, - 'url': 'ipam:ipaddress_list', - }), - ('vlan', { - 'queryset': VLAN.objects.prefetch_related('site', 'group', 'tenant', 'tenant__group', 'role'), - 'filterset': ipam.filtersets.VLANFilterSet, - 'table': ipam.tables.VLANTable, - 'url': 'ipam:vlan_list', - }), - ('asn', { - 'queryset': ASN.objects.prefetch_related('rir', 'tenant', 'tenant__group'), - 'filterset': ipam.filtersets.ASNFilterSet, - 'table': ipam.tables.ASNTable, - 'url': 'ipam:asn_list', - }), - ('service', { - 'queryset': Service.objects.prefetch_related('device', 'virtual_machine'), - 'filterset': ipam.filtersets.ServiceFilterSet, - 'table': ipam.tables.ServiceTable, - 'url': 'ipam:service_list', - }), - ) -) - -TENANCY_TYPES = OrderedDict( - ( - ('tenant', { - 'queryset': Tenant.objects.prefetch_related('group'), - 'filterset': tenancy.filtersets.TenantFilterSet, - 'table': tenancy.tables.TenantTable, - 'url': 'tenancy:tenant_list', - }), - ('contact', { - 'queryset': Contact.objects.prefetch_related('group', 'assignments').annotate( - assignment_count=count_related(ContactAssignment, 'contact')), - 'filterset': tenancy.filtersets.ContactFilterSet, - 'table': tenancy.tables.ContactTable, - 'url': 'tenancy:contact_list', - }), - ) -) - -VIRTUALIZATION_TYPES = OrderedDict( - ( - ('cluster', { - 'queryset': Cluster.objects.prefetch_related('type', 'group').annotate( - device_count=count_related(Device, 'cluster'), - vm_count=count_related(VirtualMachine, 'cluster') - ), - 'filterset': virtualization.filtersets.ClusterFilterSet, - 'table': virtualization.tables.ClusterTable, - 'url': 'virtualization:cluster_list', - }), - ('virtualmachine', { - 'queryset': VirtualMachine.objects.prefetch_related( - 'cluster', 'tenant', 'tenant__group', 'platform', 'primary_ip4', 'primary_ip6', - ), - 'filterset': virtualization.filtersets.VirtualMachineFilterSet, - 'table': virtualization.tables.VirtualMachineTable, - 'url': 'virtualization:virtualmachine_list', - }), - ) -) - -SEARCH_TYPE_HIERARCHY = OrderedDict( - ( - ("Circuits", CIRCUIT_TYPES), - ("DCIM", DCIM_TYPES), - ("IPAM", IPAM_TYPES), - ("Tenancy", TENANCY_TYPES), - ("Virtualization", VIRTUALIZATION_TYPES), - ) -) - - -def build_search_types() -> Dict[str, Dict]: - result = dict() - - for app_types in SEARCH_TYPE_HIERARCHY.values(): - for name, items in app_types.items(): - result[name] = items - - return result - - -SEARCH_TYPES = build_search_types() diff --git a/netbox/netbox/forms/__init__.py b/netbox/netbox/forms/__init__.py index 23848724d..d1451e003 100644 --- a/netbox/netbox/forms/__init__.py +++ b/netbox/netbox/forms/__init__.py @@ -1,6 +1,6 @@ from django import forms -from netbox.constants import SEARCH_TYPE_HIERARCHY +from netbox.search import SEARCH_TYPE_HIERARCHY from utilities.forms import BootstrapMixin from .base import * diff --git a/netbox/netbox/search.py b/netbox/netbox/search.py new file mode 100644 index 000000000..ef0c4fd87 --- /dev/null +++ b/netbox/netbox/search.py @@ -0,0 +1,261 @@ +import circuits.filtersets +import circuits.tables +import dcim.filtersets +import dcim.tables +import ipam.filtersets +import ipam.tables +import tenancy.filtersets +import tenancy.tables +import virtualization.filtersets +import wireless.tables +import wireless.filtersets +import virtualization.tables +from circuits.models import Circuit, ProviderNetwork, Provider +from dcim.models import ( + Cable, Device, DeviceType, Interface, Location, Module, ModuleType, PowerFeed, Rack, RackReservation, Site, + VirtualChassis, +) +from ipam.models import Aggregate, ASN, IPAddress, Prefix, Service, VLAN, VRF +from tenancy.models import Contact, Tenant, ContactAssignment +from utilities.utils import count_related +from wireless.models import WirelessLAN, WirelessLink +from virtualization.models import Cluster, VirtualMachine + +CIRCUIT_TYPES = { + 'provider': { + 'queryset': Provider.objects.annotate( + count_circuits=count_related(Circuit, 'provider') + ), + 'filterset': circuits.filtersets.ProviderFilterSet, + 'table': circuits.tables.ProviderTable, + 'url': 'circuits:provider_list', + }, + 'circuit': { + 'queryset': Circuit.objects.prefetch_related( + 'type', 'provider', 'tenant', 'tenant__group', 'terminations__site' + ), + 'filterset': circuits.filtersets.CircuitFilterSet, + 'table': circuits.tables.CircuitTable, + 'url': 'circuits:circuit_list', + }, + 'providernetwork': { + 'queryset': ProviderNetwork.objects.prefetch_related('provider'), + 'filterset': circuits.filtersets.ProviderNetworkFilterSet, + 'table': circuits.tables.ProviderNetworkTable, + 'url': 'circuits:providernetwork_list', + }, +} + +DCIM_TYPES = { + 'site': { + 'queryset': Site.objects.prefetch_related('region', 'tenant', 'tenant__group'), + 'filterset': dcim.filtersets.SiteFilterSet, + 'table': dcim.tables.SiteTable, + 'url': 'dcim:site_list', + }, + 'rack': { + 'queryset': Rack.objects.prefetch_related('site', 'location', 'tenant', 'tenant__group', 'role').annotate( + device_count=count_related(Device, 'rack') + ), + 'filterset': dcim.filtersets.RackFilterSet, + 'table': dcim.tables.RackTable, + 'url': 'dcim:rack_list', + }, + 'rackreservation': { + 'queryset': RackReservation.objects.prefetch_related('site', 'rack', 'user'), + 'filterset': dcim.filtersets.RackReservationFilterSet, + 'table': dcim.tables.RackReservationTable, + 'url': 'dcim:rackreservation_list', + }, + 'location': { + 'queryset': Location.objects.add_related_count( + Location.objects.add_related_count( + Location.objects.all(), + Device, + 'location', + 'device_count', + cumulative=True + ), + Rack, + 'location', + 'rack_count', + cumulative=True + ).prefetch_related('site'), + 'filterset': dcim.filtersets.LocationFilterSet, + 'table': dcim.tables.LocationTable, + 'url': 'dcim:location_list', + }, + 'devicetype': { + 'queryset': DeviceType.objects.prefetch_related('manufacturer').annotate( + instance_count=count_related(Device, 'device_type') + ), + 'filterset': dcim.filtersets.DeviceTypeFilterSet, + 'table': dcim.tables.DeviceTypeTable, + 'url': 'dcim:devicetype_list', + }, + 'device': { + 'queryset': Device.objects.prefetch_related( + 'device_type__manufacturer', 'device_role', 'tenant', 'tenant__group', 'site', 'rack', 'primary_ip4', + 'primary_ip6', + ), + 'filterset': dcim.filtersets.DeviceFilterSet, + 'table': dcim.tables.DeviceTable, + 'url': 'dcim:device_list', + }, + 'moduletype': { + 'queryset': ModuleType.objects.prefetch_related('manufacturer').annotate( + instance_count=count_related(Module, 'module_type') + ), + 'filterset': dcim.filtersets.ModuleTypeFilterSet, + 'table': dcim.tables.ModuleTypeTable, + 'url': 'dcim:moduletype_list', + }, + 'module': { + 'queryset': Module.objects.prefetch_related( + 'module_type__manufacturer', 'device', 'module_bay', + ), + 'filterset': dcim.filtersets.ModuleFilterSet, + 'table': dcim.tables.ModuleTable, + 'url': 'dcim:module_list', + }, + 'virtualchassis': { + 'queryset': VirtualChassis.objects.prefetch_related('master').annotate( + member_count=count_related(Device, 'virtual_chassis') + ), + 'filterset': dcim.filtersets.VirtualChassisFilterSet, + 'table': dcim.tables.VirtualChassisTable, + 'url': 'dcim:virtualchassis_list', + }, + 'cable': { + 'queryset': Cable.objects.all(), + 'filterset': dcim.filtersets.CableFilterSet, + 'table': dcim.tables.CableTable, + 'url': 'dcim:cable_list', + }, + 'powerfeed': { + 'queryset': PowerFeed.objects.all(), + 'filterset': dcim.filtersets.PowerFeedFilterSet, + 'table': dcim.tables.PowerFeedTable, + 'url': 'dcim:powerfeed_list', + }, +} + +IPAM_TYPES = { + 'vrf': { + 'queryset': VRF.objects.prefetch_related('tenant', 'tenant__group'), + 'filterset': ipam.filtersets.VRFFilterSet, + 'table': ipam.tables.VRFTable, + 'url': 'ipam:vrf_list', + }, + 'aggregate': { + 'queryset': Aggregate.objects.prefetch_related('rir'), + 'filterset': ipam.filtersets.AggregateFilterSet, + 'table': ipam.tables.AggregateTable, + 'url': 'ipam:aggregate_list', + }, + 'prefix': { + 'queryset': Prefix.objects.prefetch_related('site', 'vrf__tenant', 'tenant', 'tenant__group', 'vlan', 'role'), + 'filterset': ipam.filtersets.PrefixFilterSet, + 'table': ipam.tables.PrefixTable, + 'url': 'ipam:prefix_list', + }, + 'ipaddress': { + 'queryset': IPAddress.objects.prefetch_related('vrf__tenant', 'tenant', 'tenant__group'), + 'filterset': ipam.filtersets.IPAddressFilterSet, + 'table': ipam.tables.IPAddressTable, + 'url': 'ipam:ipaddress_list', + }, + 'vlan': { + 'queryset': VLAN.objects.prefetch_related('site', 'group', 'tenant', 'tenant__group', 'role'), + 'filterset': ipam.filtersets.VLANFilterSet, + 'table': ipam.tables.VLANTable, + 'url': 'ipam:vlan_list', + }, + 'asn': { + 'queryset': ASN.objects.prefetch_related('rir', 'tenant', 'tenant__group'), + 'filterset': ipam.filtersets.ASNFilterSet, + 'table': ipam.tables.ASNTable, + 'url': 'ipam:asn_list', + }, + 'service': { + 'queryset': Service.objects.prefetch_related('device', 'virtual_machine'), + 'filterset': ipam.filtersets.ServiceFilterSet, + 'table': ipam.tables.ServiceTable, + 'url': 'ipam:service_list', + }, +} + +TENANCY_TYPES = { + 'tenant': { + 'queryset': Tenant.objects.prefetch_related('group'), + 'filterset': tenancy.filtersets.TenantFilterSet, + 'table': tenancy.tables.TenantTable, + 'url': 'tenancy:tenant_list', + }, + 'contact': { + 'queryset': Contact.objects.prefetch_related('group', 'assignments').annotate( + assignment_count=count_related(ContactAssignment, 'contact')), + 'filterset': tenancy.filtersets.ContactFilterSet, + 'table': tenancy.tables.ContactTable, + 'url': 'tenancy:contact_list', + }, +} + +VIRTUALIZATION_TYPES = { + 'cluster': { + 'queryset': Cluster.objects.prefetch_related('type', 'group').annotate( + device_count=count_related(Device, 'cluster'), + vm_count=count_related(VirtualMachine, 'cluster') + ), + 'filterset': virtualization.filtersets.ClusterFilterSet, + 'table': virtualization.tables.ClusterTable, + 'url': 'virtualization:cluster_list', + }, + 'virtualmachine': { + 'queryset': VirtualMachine.objects.prefetch_related( + 'cluster', 'tenant', 'tenant__group', 'platform', 'primary_ip4', 'primary_ip6', + ), + 'filterset': virtualization.filtersets.VirtualMachineFilterSet, + 'table': virtualization.tables.VirtualMachineTable, + 'url': 'virtualization:virtualmachine_list', + }, +} + +WIRELESS_TYPES = { + 'wirelesslan': { + 'queryset': WirelessLAN.objects.prefetch_related('group', 'vlan').annotate( + interface_count=count_related(Interface, 'wireless_lans') + ), + 'filterset': wireless.filtersets.WirelessLANFilterSet, + 'table': wireless.tables.WirelessLANTable, + 'url': 'wireless:wirelesslan_list', + }, + 'wirelesslink': { + 'queryset': WirelessLink.objects.prefetch_related('interface_a__device', 'interface_b__device'), + 'filterset': wireless.filtersets.WirelessLinkFilterSet, + 'table': wireless.tables.WirelessLinkTable, + 'url': 'wireless:wirelesslink_list', + }, +} + +SEARCH_TYPE_HIERARCHY = { + 'Circuits': CIRCUIT_TYPES, + 'DCIM': DCIM_TYPES, + 'IPAM': IPAM_TYPES, + 'Tenancy': TENANCY_TYPES, + 'Virtualization': VIRTUALIZATION_TYPES, + 'Wireless': WIRELESS_TYPES, +} + + +def build_search_types(): + result = dict() + + for app_types in SEARCH_TYPE_HIERARCHY.values(): + for name, items in app_types.items(): + result[name] = items + + return result + + +SEARCH_TYPES = build_search_types() diff --git a/netbox/netbox/views/__init__.py b/netbox/netbox/views/__init__.py index f159ee637..204fce469 100644 --- a/netbox/netbox/views/__init__.py +++ b/netbox/netbox/views/__init__.py @@ -22,8 +22,9 @@ from dcim.models import ( from extras.models import ObjectChange from extras.tables import ObjectChangeTable from ipam.models import Aggregate, IPAddress, IPRange, Prefix, VLAN, VRF -from netbox.constants import SEARCH_MAX_RESULTS, SEARCH_TYPES +from netbox.constants import SEARCH_MAX_RESULTS from netbox.forms import SearchForm +from netbox.search import SEARCH_TYPES from tenancy.models import Tenant from virtualization.models import Cluster, VirtualMachine from wireless.models import WirelessLAN, WirelessLink