diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 3b87a49e4..78231890b 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -14,7 +14,7 @@ body: attributes: label: NetBox version description: What version of NetBox are you currently running? - placeholder: v3.2.5 + placeholder: v3.2.6 validations: required: true - type: dropdown diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index 1fc0268ab..71d45092c 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -14,7 +14,7 @@ body: attributes: label: NetBox version description: What version of NetBox are you currently running? - placeholder: v3.2.5 + placeholder: v3.2.6 validations: required: true - type: dropdown diff --git a/base_requirements.txt b/base_requirements.txt index 9bbee6115..efeede487 100644 --- a/base_requirements.txt +++ b/base_requirements.txt @@ -1,3 +1,7 @@ +# HTML sanitizer +# https://github.com/mozilla/bleach +bleach + # The Python web framework on which NetBox is built # https://github.com/django/django Django @@ -130,7 +134,3 @@ tablib # Timezone data (required by django-timezone-field on Python 3.9+) # https://github.com/python/tzdata tzdata - -# HTML sanitizer -# https://github.com/mozilla/bleach -bleach \ No newline at end of file diff --git a/docs/models/extras/customfield.md b/docs/models/extras/customfield.md index da73816b6..e0c01688d 100644 --- a/docs/models/extras/customfield.md +++ b/docs/models/extras/customfield.md @@ -10,7 +10,7 @@ Within the database, custom fields are stored as JSON data directly alongside ea Custom fields may be created by navigating to Customization > Custom Fields. NetBox supports six types of custom field: -* Text: Free-form text (up to 255 characters) +* Text: Free-form text (intended for single-line use) * Long text: Free-form of any length; supports Markdown rendering * Integer: A whole number (positive or negative) * Boolean: True or false diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index 9b4dc800d..1ba6235b8 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -1,6 +1,10 @@ # NetBox v3.2 -## v3.2.6 (FUTURE) +## v3.2.7 (FUTURE) + +--- + +## v3.2.6 (2022-07-11) ### Enhancements @@ -8,14 +12,18 @@ * [#9396](https://github.com/netbox-community/netbox/issues/9396) - Allow filtering modules by bay ID * [#9403](https://github.com/netbox-community/netbox/issues/9403) - Enable modifying virtual chassis properties when creating/editing a device * [#9540](https://github.com/netbox-community/netbox/issues/9540) - Add filters for assigned device & VM to IP addresses list +* [#9686](https://github.com/netbox-community/netbox/issues/9686) - Add tenant group column for all object tables with tenant assignments ### Bug Fixes * [#8854](https://github.com/netbox-community/netbox/issues/8854) - Fix `REMOTE_AUTH_DEFAULT_GROUPS` for social-auth backends * [#9575](https://github.com/netbox-community/netbox/issues/9575) - Fix AttributeError exception for FHRP group with an IP address assigned * [#9597](https://github.com/netbox-community/netbox/issues/9597) - Include `installed_module` in module bay REST API serializer +* [#9632](https://github.com/netbox-community/netbox/issues/9632) - Automatically focus on search box when expanding dropdowns * [#9657](https://github.com/netbox-community/netbox/issues/9657) - Fix filtering for custom fields and webhooks in the UI * [#9682](https://github.com/netbox-community/netbox/issues/9682) - Fix bulk assignment of ASNs to sites +* [#9687](https://github.com/netbox-community/netbox/issues/9687) - Don't restrict custom text field lengths when entering via UI form +* [#9704](https://github.com/netbox-community/netbox/issues/9704) - Include `last_updated` field on JournalEntry REST API serializer --- diff --git a/netbox/circuits/tables/circuits.py b/netbox/circuits/tables/circuits.py index 8b59700ee..f9ab7e190 100644 --- a/netbox/circuits/tables/circuits.py +++ b/netbox/circuits/tables/circuits.py @@ -2,7 +2,7 @@ import django_tables2 as tables from circuits.models import * from netbox.tables import NetBoxTable, columns -from tenancy.tables import TenantColumn +from tenancy.tables import TenancyColumnsMixin from .columns import CommitRateColumn __all__ = ( @@ -39,7 +39,7 @@ class CircuitTypeTable(NetBoxTable): default_columns = ('pk', 'name', 'circuit_count', 'description', 'slug') -class CircuitTable(NetBoxTable): +class CircuitTable(TenancyColumnsMixin, NetBoxTable): cid = tables.Column( linkify=True, verbose_name='Circuit ID' @@ -48,7 +48,6 @@ class CircuitTable(NetBoxTable): linkify=True ) status = columns.ChoiceFieldColumn() - tenant = TenantColumn() termination_a = tables.TemplateColumn( template_code=CIRCUITTERMINATION_LINK, verbose_name='Side A' @@ -69,8 +68,9 @@ class CircuitTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = Circuit fields = ( - 'pk', 'id', 'cid', 'provider', 'type', 'status', 'tenant', 'termination_a', 'termination_z', 'install_date', - 'termination_date', 'commit_rate', 'description', 'comments', 'contacts', 'tags', 'created', 'last_updated', + 'pk', 'id', 'cid', 'provider', 'type', 'status', 'tenant', 'tenant_group', 'termination_a', 'termination_z', + 'install_date', 'termination_date', 'commit_rate', 'description', 'comments', 'contacts', 'tags', 'created', + 'last_updated', ) default_columns = ( 'pk', 'cid', 'provider', 'type', 'status', 'tenant', 'termination_a', 'termination_z', 'description', diff --git a/netbox/circuits/views.py b/netbox/circuits/views.py index f3b1269f9..11f211b27 100644 --- a/netbox/circuits/views.py +++ b/netbox/circuits/views.py @@ -30,7 +30,7 @@ class ProviderView(generic.ObjectView): circuits = Circuit.objects.restrict(request.user, 'view').filter( provider=instance ).prefetch_related( - 'type', 'tenant', 'terminations__site' + 'type', 'tenant', 'tenant__group', 'terminations__site' ) circuits_table = tables.CircuitTable(circuits, user=request.user, exclude=('provider',)) circuits_table.configure(request) @@ -91,7 +91,7 @@ class ProviderNetworkView(generic.ObjectView): Q(termination_a__provider_network=instance.pk) | Q(termination_z__provider_network=instance.pk) ).prefetch_related( - 'type', 'tenant', 'terminations__site' + 'type', 'tenant', 'tenant__group', 'terminations__site' ) circuits_table = tables.CircuitTable(circuits, user=request.user) circuits_table.configure(request) @@ -192,7 +192,7 @@ class CircuitTypeBulkDeleteView(generic.BulkDeleteView): class CircuitListView(generic.ObjectListView): queryset = Circuit.objects.prefetch_related( - 'provider', 'type', 'tenant', 'termination_a', 'termination_z' + 'provider', 'type', 'tenant', 'tenant__group', 'termination_a', 'termination_z' ) filterset = filtersets.CircuitFilterSet filterset_form = forms.CircuitFilterForm diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index 82dacbff6..9645efdbf 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -96,8 +96,7 @@ class ModularComponentModel(ComponentModel): inventory_items = GenericRelation( to='dcim.InventoryItem', content_type_field='component_type', - object_id_field='component_id', - related_name='%(class)ss', + object_id_field='component_id' ) class Meta: diff --git a/netbox/dcim/tables/cables.py b/netbox/dcim/tables/cables.py index ff2a672ca..e5410e42a 100644 --- a/netbox/dcim/tables/cables.py +++ b/netbox/dcim/tables/cables.py @@ -4,7 +4,7 @@ from django.utils.safestring import mark_safe from dcim.models import Cable from netbox.tables import NetBoxTable, columns -from tenancy.tables import TenantColumn +from tenancy.tables import TenancyColumnsMixin from .template_code import CABLE_LENGTH __all__ = ( @@ -46,7 +46,7 @@ class CableTerminationsColumn(tables.Column): # Cables # -class CableTable(NetBoxTable): +class CableTable(TenancyColumnsMixin, NetBoxTable): a_terminations = CableTerminationsColumn( cable_end='A', orderable=False, @@ -106,7 +106,6 @@ class CableTable(NetBoxTable): verbose_name='Site B' ) status = columns.ChoiceFieldColumn() - tenant = TenantColumn() length = columns.TemplateColumn( template_code=CABLE_LENGTH, order_by=('_abs_length', 'length_unit') @@ -120,8 +119,8 @@ class CableTable(NetBoxTable): model = Cable fields = ( 'pk', 'id', 'label', 'a_terminations', 'b_terminations', 'device_a', 'device_b', 'rack_a', 'rack_b', - 'location_a', 'location_b', 'site_a', 'site_b', 'status', 'type', 'tenant', 'color', 'length', 'tags', - 'created', 'last_updated', + 'location_a', 'location_b', 'site_a', 'site_b', 'status', 'type', 'tenant', 'tenant_group', 'color', + 'length', 'tags', 'created', 'last_updated', ) default_columns = ( 'pk', 'id', 'label', 'a_terminations', 'b_terminations', 'status', 'type', diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py index b3dd700cb..a6c0c0ecc 100644 --- a/netbox/dcim/tables/devices.py +++ b/netbox/dcim/tables/devices.py @@ -6,7 +6,7 @@ from dcim.models import ( InventoryItemRole, ModuleBay, Platform, PowerOutlet, PowerPort, RearPort, VirtualChassis, ) from netbox.tables import NetBoxTable, columns -from tenancy.tables import TenantColumn +from tenancy.tables import TenancyColumnsMixin from .template_code import * __all__ = ( @@ -137,13 +137,12 @@ class PlatformTable(NetBoxTable): # Devices # -class DeviceTable(NetBoxTable): +class DeviceTable(TenancyColumnsMixin, NetBoxTable): name = tables.TemplateColumn( order_by=('_name',), template_code=DEVICE_LINK ) status = columns.ChoiceFieldColumn() - tenant = TenantColumn() site = tables.Column( linkify=True ) @@ -200,7 +199,7 @@ class DeviceTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = Device fields = ( - 'pk', 'id', 'name', 'status', 'tenant', 'device_role', 'manufacturer', 'device_type', 'platform', 'serial', + 'pk', 'id', 'name', 'status', 'tenant', 'tenant_group', 'device_role', 'manufacturer', 'device_type', 'platform', 'serial', 'asset_tag', 'site', 'location', 'rack', 'position', 'face', 'primary_ip', 'airflow', 'primary_ip4', 'primary_ip6', 'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments', 'contacts', 'tags', 'created', 'last_updated', @@ -211,12 +210,11 @@ class DeviceTable(NetBoxTable): ) -class DeviceImportTable(NetBoxTable): +class DeviceImportTable(TenancyColumnsMixin, NetBoxTable): name = tables.TemplateColumn( template_code=DEVICE_LINK ) status = columns.ChoiceFieldColumn() - tenant = TenantColumn() site = tables.Column( linkify=True ) @@ -232,7 +230,7 @@ class DeviceImportTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = Device - fields = ('id', 'name', 'status', 'tenant', 'site', 'rack', 'position', 'device_role', 'device_type') + fields = ('id', 'name', 'status', 'tenant', 'tenant_group', 'site', 'rack', 'position', 'device_role', 'device_type') empty_text = False diff --git a/netbox/dcim/tables/racks.py b/netbox/dcim/tables/racks.py index e6368cb74..5412e2297 100644 --- a/netbox/dcim/tables/racks.py +++ b/netbox/dcim/tables/racks.py @@ -3,7 +3,7 @@ from django_tables2.utils import Accessor from dcim.models import Rack, RackReservation, RackRole from netbox.tables import NetBoxTable, columns -from tenancy.tables import TenantColumn +from tenancy.tables import TenancyColumnsMixin __all__ = ( 'RackTable', @@ -37,7 +37,7 @@ class RackRoleTable(NetBoxTable): # Racks # -class RackTable(NetBoxTable): +class RackTable(TenancyColumnsMixin, NetBoxTable): name = tables.Column( order_by=('_name',), linkify=True @@ -48,7 +48,6 @@ class RackTable(NetBoxTable): site = tables.Column( linkify=True ) - tenant = TenantColumn() status = columns.ChoiceFieldColumn() role = columns.ColoredLabelColumn() u_height = tables.TemplateColumn( @@ -87,7 +86,7 @@ class RackTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = Rack fields = ( - 'pk', 'id', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'serial', 'asset_tag', + 'pk', 'id', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'tenant_group', 'role', 'serial', 'asset_tag', 'type', 'width', 'outer_width', 'outer_depth', 'u_height', 'comments', 'device_count', 'get_utilization', 'get_power_utilization', 'contacts', 'tags', 'created', 'last_updated', ) @@ -101,7 +100,7 @@ class RackTable(NetBoxTable): # Rack reservations # -class RackReservationTable(NetBoxTable): +class RackReservationTable(TenancyColumnsMixin, NetBoxTable): reservation = tables.Column( accessor='pk', linkify=True @@ -110,7 +109,6 @@ class RackReservationTable(NetBoxTable): accessor=Accessor('rack__site'), linkify=True ) - tenant = TenantColumn() rack = tables.Column( linkify=True ) @@ -125,7 +123,7 @@ class RackReservationTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = RackReservation fields = ( - 'pk', 'id', 'reservation', 'site', 'rack', 'unit_list', 'user', 'created', 'tenant', 'description', 'tags', + 'pk', 'id', 'reservation', 'site', 'rack', 'unit_list', 'user', 'created', 'tenant', 'tenant_group', 'description', 'tags', 'actions', 'created', 'last_updated', ) default_columns = ('pk', 'reservation', 'site', 'rack', 'unit_list', 'user', 'description') diff --git a/netbox/dcim/tables/sites.py b/netbox/dcim/tables/sites.py index 83db99aec..e452badea 100644 --- a/netbox/dcim/tables/sites.py +++ b/netbox/dcim/tables/sites.py @@ -2,7 +2,7 @@ import django_tables2 as tables from dcim.models import Location, Region, Site, SiteGroup from netbox.tables import NetBoxTable, columns -from tenancy.tables import TenantColumn +from tenancy.tables import TenancyColumnsMixin from .template_code import LOCATION_BUTTONS __all__ = ( @@ -75,7 +75,7 @@ class SiteGroupTable(NetBoxTable): # Sites # -class SiteTable(NetBoxTable): +class SiteTable(TenancyColumnsMixin, NetBoxTable): name = tables.Column( linkify=True ) @@ -96,7 +96,6 @@ class SiteTable(NetBoxTable): url_params={'site_id': 'pk'}, verbose_name='ASN Count' ) - tenant = TenantColumn() comments = columns.MarkdownColumn() contacts = columns.ManyToManyColumn( linkify_item=True @@ -108,7 +107,7 @@ class SiteTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = Site fields = ( - 'pk', 'id', 'name', 'slug', 'status', 'facility', 'region', 'group', 'tenant', 'asns', 'asn_count', + 'pk', 'id', 'name', 'slug', 'status', 'facility', 'region', 'group', 'tenant', 'tenant_group', 'asns', 'asn_count', 'time_zone', 'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'comments', 'contacts', 'tags', 'created', 'last_updated', 'actions', ) @@ -119,15 +118,13 @@ class SiteTable(NetBoxTable): # Locations # -class LocationTable(NetBoxTable): +class LocationTable(TenancyColumnsMixin, NetBoxTable): name = columns.MPTTColumn( linkify=True ) site = tables.Column( linkify=True ) - status = columns.ChoiceFieldColumn() - tenant = TenantColumn() rack_count = columns.LinkedCountColumn( viewname='dcim:rack_list', url_params={'location_id': 'pk'}, @@ -151,7 +148,7 @@ class LocationTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = Location fields = ( - 'pk', 'id', 'name', 'site', 'status', 'tenant', 'rack_count', 'device_count', 'description', 'slug', - 'contacts', 'tags', 'actions', 'created', 'last_updated', + 'pk', 'id', 'name', 'site', 'status', 'tenant', 'tenant_group', 'rack_count', 'device_count', 'description', + 'slug', 'contacts', 'tags', 'actions', 'created', 'last_updated', ) default_columns = ('pk', 'name', 'site', 'status', 'tenant', 'rack_count', 'device_count', 'description') diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 1f2f04d7a..b247c3a6d 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -572,9 +572,7 @@ class RackRoleBulkDeleteView(generic.BulkDeleteView): # class RackListView(generic.ObjectListView): - queryset = Rack.objects.prefetch_related( - 'site', 'location', 'tenant', 'role', 'devices__device_type' - ).annotate( + queryset = Rack.objects.prefetch_related('devices__device_type').annotate( device_count=count_related(Device, 'rack') ) filterset = filtersets.RackFilterSet diff --git a/netbox/extras/api/serializers.py b/netbox/extras/api/serializers.py index 2060e3e86..988c3bf7b 100644 --- a/netbox/extras/api/serializers.py +++ b/netbox/extras/api/serializers.py @@ -222,7 +222,7 @@ class JournalEntrySerializer(NetBoxModelSerializer): model = JournalEntry fields = [ 'id', 'url', 'display', 'assigned_object_type', 'assigned_object_id', 'assigned_object', 'created', - 'created_by', 'kind', 'comments', 'tags', 'custom_fields', + 'created_by', 'kind', 'comments', 'tags', 'custom_fields', 'last_updated', ] def validate(self, data): diff --git a/netbox/extras/models/customfields.py b/netbox/extras/models/customfields.py index c91f96c15..bbc66f279 100644 --- a/netbox/extras/models/customfields.py +++ b/netbox/extras/models/customfields.py @@ -377,13 +377,8 @@ class CustomField(ExportTemplatesMixin, WebhooksMixin, ChangeLoggedModel): # Text else: - if self.type == CustomFieldTypeChoices.TYPE_LONGTEXT: - max_length = None - widget = forms.Textarea - else: - max_length = 255 - widget = None - field = forms.CharField(max_length=max_length, required=required, initial=initial, widget=widget) + widget = forms.Textarea if self.type == CustomFieldTypeChoices.TYPE_LONGTEXT else None + field = forms.CharField(required=required, initial=initial, widget=widget) if self.validation_regex: field.validators = [ RegexValidator( diff --git a/netbox/ipam/tables/ip.py b/netbox/ipam/tables/ip.py index 558631585..bec05eeff 100644 --- a/netbox/ipam/tables/ip.py +++ b/netbox/ipam/tables/ip.py @@ -4,7 +4,7 @@ from django_tables2.utils import Accessor from ipam.models import * from netbox.tables import NetBoxTable, columns -from tenancy.tables import TenantColumn +from tenancy.tables import TenancyColumnsMixin, TenantColumn __all__ = ( 'AggregateTable', @@ -99,7 +99,7 @@ class RIRTable(NetBoxTable): # ASNs # -class ASNTable(NetBoxTable): +class ASNTable(TenancyColumnsMixin, NetBoxTable): asn = tables.Column( linkify=True ) @@ -122,7 +122,6 @@ class ASNTable(NetBoxTable): linkify_item=True, verbose_name='Sites' ) - tenant = TenantColumn() tags = columns.TagColumn( url_name='ipam:asn_list' ) @@ -130,7 +129,7 @@ class ASNTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = ASN fields = ( - 'pk', 'asn', 'asn_asdot', 'rir', 'site_count', 'provider_count', 'tenant', 'description', 'sites', 'tags', + 'pk', 'asn', 'asn_asdot', 'rir', 'site_count', 'provider_count', 'tenant', 'tenant_group', 'description', 'sites', 'tags', 'created', 'last_updated', 'actions', ) default_columns = ('pk', 'asn', 'rir', 'site_count', 'provider_count', 'sites', 'description', 'tenant') @@ -140,12 +139,11 @@ class ASNTable(NetBoxTable): # Aggregates # -class AggregateTable(NetBoxTable): +class AggregateTable(TenancyColumnsMixin, NetBoxTable): prefix = tables.Column( linkify=True, verbose_name='Aggregate' ) - tenant = TenantColumn() date_added = tables.DateColumn( format="Y-m-d", verbose_name='Added' @@ -164,7 +162,7 @@ class AggregateTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = Aggregate fields = ( - 'pk', 'id', 'prefix', 'rir', 'tenant', 'child_count', 'utilization', 'date_added', 'description', 'tags', + 'pk', 'id', 'prefix', 'rir', 'tenant', 'tenant_group', 'child_count', 'utilization', 'date_added', 'description', 'tags', 'created', 'last_updated', ) default_columns = ('pk', 'prefix', 'rir', 'tenant', 'child_count', 'utilization', 'date_added', 'description') @@ -225,7 +223,7 @@ class PrefixUtilizationColumn(columns.UtilizationColumn): """ -class PrefixTable(NetBoxTable): +class PrefixTable(TenancyColumnsMixin, NetBoxTable): prefix = columns.TemplateColumn( template_code=PREFIX_LINK, export_raw=True, @@ -256,7 +254,6 @@ class PrefixTable(NetBoxTable): template_code=VRF_LINK, verbose_name='VRF' ) - tenant = TenantColumn() site = tables.Column( linkify=True ) @@ -289,7 +286,7 @@ class PrefixTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = Prefix fields = ( - 'pk', 'id', 'prefix', 'prefix_flat', 'status', 'children', 'vrf', 'utilization', 'tenant', 'site', + 'pk', 'id', 'prefix', 'prefix_flat', 'status', 'children', 'vrf', 'utilization', 'tenant', 'tenant_group', 'site', 'vlan_group', 'vlan', 'role', 'is_pool', 'mark_utilized', 'description', 'tags', 'created', 'last_updated', ) default_columns = ( @@ -303,7 +300,7 @@ class PrefixTable(NetBoxTable): # # IP ranges # -class IPRangeTable(NetBoxTable): +class IPRangeTable(TenancyColumnsMixin, NetBoxTable): start_address = tables.Column( linkify=True ) @@ -317,7 +314,6 @@ class IPRangeTable(NetBoxTable): role = tables.Column( linkify=True ) - tenant = TenantColumn() utilization = columns.UtilizationColumn( accessor='utilization', orderable=False @@ -329,7 +325,7 @@ class IPRangeTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = IPRange fields = ( - 'pk', 'id', 'start_address', 'end_address', 'size', 'vrf', 'status', 'role', 'tenant', 'description', + 'pk', 'id', 'start_address', 'end_address', 'size', 'vrf', 'status', 'role', 'tenant', 'tenant_group', 'description', 'utilization', 'tags', 'created', 'last_updated', ) default_columns = ( @@ -344,7 +340,7 @@ class IPRangeTable(NetBoxTable): # IPAddresses # -class IPAddressTable(NetBoxTable): +class IPAddressTable(TenancyColumnsMixin, NetBoxTable): address = tables.TemplateColumn( template_code=IPADDRESS_LINK, verbose_name='IP Address' @@ -357,7 +353,6 @@ class IPAddressTable(NetBoxTable): default=AVAILABLE_LABEL ) role = columns.ChoiceFieldColumn() - tenant = TenantColumn() assigned_object = tables.Column( linkify=True, orderable=False, @@ -386,7 +381,7 @@ class IPAddressTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = IPAddress fields = ( - 'pk', 'id', 'address', 'vrf', 'status', 'role', 'tenant', 'nat_inside', 'assigned', 'dns_name', 'description', + 'pk', 'id', 'address', 'vrf', 'status', 'role', 'tenant', 'tenant_group', 'nat_inside', 'assigned', 'dns_name', 'description', 'tags', 'created', 'last_updated', ) default_columns = ( diff --git a/netbox/ipam/tables/vlans.py b/netbox/ipam/tables/vlans.py index 4551a1c3d..7878de507 100644 --- a/netbox/ipam/tables/vlans.py +++ b/netbox/ipam/tables/vlans.py @@ -5,7 +5,7 @@ from django_tables2.utils import Accessor from dcim.models import Interface from ipam.models import * from netbox.tables import NetBoxTable, columns -from tenancy.tables import TenantColumn +from tenancy.tables import TenancyColumnsMixin, TenantColumn from virtualization.models import VMInterface __all__ = ( @@ -90,7 +90,7 @@ class VLANGroupTable(NetBoxTable): # VLANs # -class VLANTable(NetBoxTable): +class VLANTable(TenancyColumnsMixin, NetBoxTable): vid = tables.TemplateColumn( template_code=VLAN_LINK, verbose_name='VID' @@ -104,7 +104,6 @@ class VLANTable(NetBoxTable): group = tables.Column( linkify=True ) - tenant = TenantColumn() status = columns.ChoiceFieldColumn( default=AVAILABLE_LABEL ) @@ -123,7 +122,7 @@ class VLANTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = VLAN fields = ( - 'pk', 'id', 'vid', 'name', 'site', 'group', 'prefixes', 'tenant', 'status', 'role', 'description', 'tags', + 'pk', 'id', 'vid', 'name', 'site', 'group', 'prefixes', 'tenant', 'tenant_group', 'status', 'role', 'description', 'tags', 'created', 'last_updated', ) default_columns = ('pk', 'vid', 'name', 'site', 'group', 'prefixes', 'tenant', 'status', 'role', 'description') diff --git a/netbox/ipam/tables/vrfs.py b/netbox/ipam/tables/vrfs.py index 727f402ff..69807410b 100644 --- a/netbox/ipam/tables/vrfs.py +++ b/netbox/ipam/tables/vrfs.py @@ -2,7 +2,7 @@ import django_tables2 as tables from ipam.models import * from netbox.tables import NetBoxTable, columns -from tenancy.tables import TenantColumn +from tenancy.tables import TenancyColumnsMixin __all__ = ( 'RouteTargetTable', @@ -20,14 +20,13 @@ VRF_TARGETS = """ # VRFs # -class VRFTable(NetBoxTable): +class VRFTable(TenancyColumnsMixin, NetBoxTable): name = tables.Column( linkify=True ) rd = tables.Column( verbose_name='RD' ) - tenant = TenantColumn() enforce_unique = columns.BooleanColumn( verbose_name='Unique' ) @@ -46,7 +45,7 @@ class VRFTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = VRF fields = ( - 'pk', 'id', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'import_targets', 'export_targets', + 'pk', 'id', 'name', 'rd', 'tenant', 'tenant_group', 'enforce_unique', 'description', 'import_targets', 'export_targets', 'tags', 'created', 'last_updated', ) default_columns = ('pk', 'name', 'rd', 'tenant', 'description') @@ -56,16 +55,15 @@ class VRFTable(NetBoxTable): # Route targets # -class RouteTargetTable(NetBoxTable): +class RouteTargetTable(TenancyColumnsMixin, NetBoxTable): name = tables.Column( linkify=True ) - tenant = TenantColumn() tags = columns.TagColumn( url_name='ipam:vrf_list' ) class Meta(NetBoxTable.Meta): model = RouteTarget - fields = ('pk', 'id', 'name', 'tenant', 'description', 'tags', 'created', 'last_updated',) + fields = ('pk', 'id', 'name', 'tenant', 'tenant_group', 'description', 'tags', 'created', 'last_updated',) default_columns = ('pk', 'name', 'tenant', 'description') diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 5545bc344..72b223b55 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -299,7 +299,7 @@ class AggregatePrefixesView(generic.ObjectChildrenView): def get_children(self, request, parent): return Prefix.objects.restrict(request.user, 'view').filter( prefix__net_contained_or_equal=str(parent.prefix) - ).prefetch_related('site', 'role', 'tenant', 'vlan') + ).prefetch_related('site', 'role', 'tenant', 'tenant__group', 'vlan') def prep_table_data(self, request, queryset, parent): # Determine whether to show assigned prefixes, available prefixes, or both @@ -471,7 +471,7 @@ class PrefixPrefixesView(generic.ObjectChildrenView): def get_children(self, request, parent): return parent.get_child_prefixes().restrict(request.user, 'view').prefetch_related( - 'site', 'vrf', 'vlan', 'role', 'tenant', + 'site', 'vrf', 'vlan', 'role', 'tenant', 'tenant__group' ) def prep_table_data(self, request, queryset, parent): @@ -500,7 +500,7 @@ class PrefixIPRangesView(generic.ObjectChildrenView): def get_children(self, request, parent): return parent.get_child_ranges().restrict(request.user, 'view').prefetch_related( - 'vrf', 'role', 'tenant', + 'vrf', 'role', 'tenant', 'tenant__group', ) def get_extra_context(self, request, instance): @@ -587,9 +587,7 @@ class IPRangeIPAddressesView(generic.ObjectChildrenView): template_name = 'ipam/iprange/ip_addresses.html' def get_children(self, request, parent): - return parent.get_child_ips().restrict(request.user, 'view').prefetch_related( - 'vrf', 'tenant', - ) + return parent.get_child_ips().restrict(request.user, 'view') def get_extra_context(self, request, instance): return { diff --git a/netbox/netbox/constants.py b/netbox/netbox/constants.py index 8ca0d98c1..cc04e9aa8 100644 --- a/netbox/netbox/constants.py +++ b/netbox/netbox/constants.py @@ -34,7 +34,7 @@ CIRCUIT_TYPES = OrderedDict( }), ('circuit', { 'queryset': Circuit.objects.prefetch_related( - 'type', 'provider', 'tenant', 'terminations__site' + 'type', 'provider', 'tenant', 'tenant__group', 'terminations__site' ), 'filterset': circuits.filtersets.CircuitFilterSet, 'table': circuits.tables.CircuitTable, @@ -53,13 +53,13 @@ CIRCUIT_TYPES = OrderedDict( DCIM_TYPES = OrderedDict( ( ('site', { - 'queryset': Site.objects.prefetch_related('region', 'tenant'), + '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', 'role').annotate( + 'queryset': Rack.objects.prefetch_related('site', 'location', 'tenant', 'tenant__group', 'role').annotate( device_count=count_related(Device, 'rack') ), 'filterset': dcim.filtersets.RackFilterSet, @@ -100,7 +100,7 @@ DCIM_TYPES = OrderedDict( }), ('device', { 'queryset': Device.objects.prefetch_related( - 'device_type__manufacturer', 'device_role', 'tenant', 'site', 'rack', 'primary_ip4', 'primary_ip6', + 'device_type__manufacturer', 'device_role', 'tenant', 'tenant__group', 'site', 'rack', 'primary_ip4', 'primary_ip6', ), 'filterset': dcim.filtersets.DeviceFilterSet, 'table': dcim.tables.DeviceTable, @@ -148,7 +148,7 @@ DCIM_TYPES = OrderedDict( IPAM_TYPES = OrderedDict( ( ('vrf', { - 'queryset': VRF.objects.prefetch_related('tenant'), + 'queryset': VRF.objects.prefetch_related('tenant', 'tenant__group'), 'filterset': ipam.filtersets.VRFFilterSet, 'table': ipam.tables.VRFTable, 'url': 'ipam:vrf_list', @@ -160,25 +160,25 @@ IPAM_TYPES = OrderedDict( 'url': 'ipam:aggregate_list', }), ('prefix', { - 'queryset': Prefix.objects.prefetch_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role'), + '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'), + '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', 'role'), + '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'), + 'queryset': ASN.objects.prefetch_related('rir', 'tenant', 'tenant__group'), 'filterset': ipam.filtersets.ASNFilterSet, 'table': ipam.tables.ASNTable, 'url': 'ipam:asn_list', @@ -223,7 +223,7 @@ VIRTUALIZATION_TYPES = OrderedDict( }), ('virtualmachine', { 'queryset': VirtualMachine.objects.prefetch_related( - 'cluster', 'tenant', 'platform', 'primary_ip4', 'primary_ip6', + 'cluster', 'tenant', 'tenant__group', 'platform', 'primary_ip4', 'primary_ip6', ), 'filterset': virtualization.filtersets.VirtualMachineFilterSet, 'table': virtualization.tables.VirtualMachineTable, diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js index bc0cabef0..b611079e1 100644 Binary files a/netbox/project-static/dist/netbox.js and b/netbox/project-static/dist/netbox.js differ diff --git a/netbox/project-static/dist/netbox.js.map b/netbox/project-static/dist/netbox.js.map index 26bb1c514..494ba0b56 100644 Binary files a/netbox/project-static/dist/netbox.js.map and b/netbox/project-static/dist/netbox.js.map differ diff --git a/netbox/project-static/src/select/api/apiSelect.ts b/netbox/project-static/src/select/api/apiSelect.ts index f5b605d58..798694f0e 100644 --- a/netbox/project-static/src/select/api/apiSelect.ts +++ b/netbox/project-static/src/select/api/apiSelect.ts @@ -411,6 +411,7 @@ export class APISelect { } finally { this.setOptionStyles(); this.enable(); + this.slim.slim.search.input.focus(); this.base.dispatchEvent(this.loadEvent); } } diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html index 2ee1e1154..8286f2c61 100644 --- a/netbox/templates/dcim/device.html +++ b/netbox/templates/dcim/device.html @@ -48,9 +48,9 @@