jeremystretch 4 سال پیش
والد
کامیت
3e3880823b

+ 1 - 1
.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.1.5
+      placeholder: v3.1.6
     validations:
       required: true
   - type: dropdown

+ 1 - 1
.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.1.5
+      placeholder: v3.1.6
     validations:
       required: true
   - type: dropdown

+ 0 - 4
base_requirements.txt

@@ -98,10 +98,6 @@ psycopg2-binary
 # https://github.com/yaml/pyyaml
 PyYAML
 
-# In-memory key/value store used for caching and queuing
-# https://github.com/andymccurdy/redis-py
-redis
-
 # Social authentication framework
 # https://github.com/python-social-auth/social-core
 social-auth-core[all]

+ 3 - 0
docs/plugins/development.md

@@ -1,5 +1,8 @@
 # Plugin Development
 
+!!! info "Help Improve the NetBox Plugins Framework!"
+    We're looking for volunteers to help improve NetBox's plugins framework. If you have experience developing plugins, we'd love to hear from you! You can find more information about this initiative [here](https://github.com/netbox-community/netbox/discussions/8338).
+
 This documentation covers the development of custom plugins for NetBox. Plugins are essentially self-contained [Django apps](https://docs.djangoproject.com/en/stable/) which integrate with NetBox to provide custom functionality. Since the development of Django apps is already very well-documented, we'll only be covering the aspects that are specific to NetBox.
 
 Plugins can do a lot, including:

+ 11 - 1
docs/release-notes/version-3.1.md

@@ -1,16 +1,23 @@
 # NetBox v3.1
 
-## v3.1.6 (FUTURE)
+## v3.1.7 (FUTURE)
+
+---
+
+## v3.1.6 (2022-01-17)
 
 ### Enhancements
 
 * [#8246](https://github.com/netbox-community/netbox/issues/8246) - Show human-friendly values for commit rates in circuits table
 * [#8262](https://github.com/netbox-community/netbox/issues/8262) - Add cable count to tenant stats
 * [#8265](https://github.com/netbox-community/netbox/issues/8265) - Add Stackwise-n interface types
+* [#8293](https://github.com/netbox-community/netbox/issues/8293) - Show 4-byte ASNs in ASDOT notation
 * [#8302](https://github.com/netbox-community/netbox/issues/8302) - Linkify role column in device & VM tables
+* [#8337](https://github.com/netbox-community/netbox/issues/8337) - Enable sorting object tables by created & updated times
 
 ### Bug Fixes
 
+* [#8279](https://github.com/netbox-community/netbox/issues/8279) - Fix display of virtual chassis members in rack elevations
 * [#8285](https://github.com/netbox-community/netbox/issues/8285) - Fix `cluster_count` under tenant REST API serializer
 * [#8287](https://github.com/netbox-community/netbox/issues/8287) - Correct label in export template form
 * [#8301](https://github.com/netbox-community/netbox/issues/8301) - Fix delete button for various object children views
@@ -19,6 +26,9 @@
 * [#8314](https://github.com/netbox-community/netbox/issues/8314) - Prevent custom fields with default values from appearing as applied filters erroneously
 * [#8317](https://github.com/netbox-community/netbox/issues/8317) - Fix CSV import of multi-select custom field values
 * [#8319](https://github.com/netbox-community/netbox/issues/8319) - Custom URL fields should honor `ALLOWED_URL_SCHEMES` config parameter
+* [#8342](https://github.com/netbox-community/netbox/issues/8342) - Restore `created` & `last_updated` fields missing from several REST API serializers
+* [#8357](https://github.com/netbox-community/netbox/issues/8357) - Add missing tags field to location filter form
+* [#8358](https://github.com/netbox-community/netbox/issues/8358) - Fix inconsistent styling of custom fields on filter & bulk edit forms
 
 ---
 

+ 1 - 1
netbox/circuits/api/serializers.py

@@ -100,5 +100,5 @@ class CircuitTerminationSerializer(ValidatedModelSerializer, LinkTerminationSeri
         fields = [
             'id', 'url', 'display', 'circuit', 'term_side', 'site', 'provider_network', 'port_speed', 'upstream_speed',
             'xconnect_id', 'pp_info', 'description', 'mark_connected', 'cable', 'link_peer', 'link_peer_type',
-            '_occupied',
+            '_occupied', 'created', 'last_updated',
         ]

+ 8 - 4
netbox/circuits/tables.py

@@ -66,7 +66,7 @@ class ProviderTable(BaseTable):
         model = Provider
         fields = (
             'pk', 'id', 'name', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'circuit_count',
-            'comments', 'tags',
+            'comments', 'tags', 'created', 'last_updated',
         )
         default_columns = ('pk', 'name', 'asn', 'account', 'circuit_count')
 
@@ -90,7 +90,9 @@ class ProviderNetworkTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = ProviderNetwork
-        fields = ('pk', 'id', 'name', 'provider', 'service_id', 'description', 'comments', 'tags')
+        fields = (
+            'pk', 'id', 'name', 'provider', 'service_id', 'description', 'comments', 'created', 'last_updated', 'tags',
+        )
         default_columns = ('pk', 'name', 'provider', 'service_id', 'description')
 
 
@@ -112,7 +114,9 @@ class CircuitTypeTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = CircuitType
-        fields = ('pk', 'id', 'name', 'circuit_count', 'description', 'slug', 'tags', 'actions')
+        fields = (
+            'pk', 'id', 'name', 'circuit_count', 'description', 'slug', 'tags', 'created', 'last_updated', 'actions',
+        )
         default_columns = ('pk', 'name', 'circuit_count', 'description', 'slug')
 
 
@@ -149,7 +153,7 @@ class CircuitTable(BaseTable):
         model = Circuit
         fields = (
             'pk', 'id', 'cid', 'provider', 'type', 'status', 'tenant', 'termination_a', 'termination_z', 'install_date',
-            'commit_rate', 'description', 'comments', 'tags',
+            'commit_rate', 'description', 'comments', 'tags', 'created', 'last_updated',
         )
         default_columns = (
             'pk', 'cid', 'provider', 'type', 'status', 'tenant', 'termination_a', 'termination_z', 'description',

+ 10 - 4
netbox/dcim/api/serializers.py

@@ -221,7 +221,7 @@ class RackReservationSerializer(PrimaryModelSerializer):
     class Meta:
         model = RackReservation
         fields = [
-            'id', 'url', 'display', 'rack', 'units', 'created', 'user', 'tenant', 'description', 'tags',
+            'id', 'url', 'display', 'rack', 'units', 'created', 'last_updated', 'user', 'tenant', 'description', 'tags',
             'custom_fields',
         ]
 
@@ -913,7 +913,7 @@ class CableSerializer(PrimaryModelSerializer):
         fields = [
             'id', 'url', 'display', 'termination_a_type', 'termination_a_id', 'termination_a', 'termination_b_type',
             'termination_b_id', 'termination_b', 'type', 'status', 'tenant', 'label', 'color', 'length', 'length_unit',
-            'tags', 'custom_fields',
+            'tags', 'custom_fields', 'created', 'last_updated',
         ]
 
     def _get_termination(self, obj, side):
@@ -1007,7 +1007,10 @@ class VirtualChassisSerializer(PrimaryModelSerializer):
 
     class Meta:
         model = VirtualChassis
-        fields = ['id', 'url', 'display', 'name', 'domain', 'master', 'tags', 'custom_fields', 'member_count']
+        fields = [
+            'id', 'url', 'display', 'name', 'domain', 'master', 'tags', 'custom_fields', 'member_count',
+            'created', 'last_updated',
+        ]
 
 
 #
@@ -1026,7 +1029,10 @@ class PowerPanelSerializer(PrimaryModelSerializer):
 
     class Meta:
         model = PowerPanel
-        fields = ['id', 'url', 'display', 'site', 'location', 'name', 'tags', 'custom_fields', 'powerfeed_count']
+        fields = [
+            'id', 'url', 'display', 'site', 'location', 'name', 'tags', 'custom_fields', 'powerfeed_count',
+            'created', 'last_updated',
+        ]
 
 
 class PowerFeedSerializer(PrimaryModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer):

+ 1 - 1
netbox/dcim/forms/filtersets.py

@@ -157,7 +157,7 @@ class SiteFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
 class LocationFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
     model = Location
     field_groups = [
-        ['q'],
+        ['q', 'tag'],
         ['region_id', 'site_group_id', 'site_id', 'parent_id'],
         ['tenant_group_id', 'tenant_id'],
     ]

+ 6 - 1
netbox/dcim/svg.py

@@ -19,7 +19,12 @@ __all__ = (
 
 
 def get_device_name(device):
-    return device.name or str(device.device_type)
+    if device.virtual_chassis:
+        return f'{device.virtual_chassis.name}:{device.vc_position}'
+    elif device.name:
+        return device.name
+    else:
+        return str(device.device_type)
 
 
 class RackElevationSVG:

+ 1 - 1
netbox/dcim/tables/cables.py

@@ -56,7 +56,7 @@ class CableTable(BaseTable):
         model = Cable
         fields = (
             'pk', 'id', 'label', 'termination_a_parent', 'termination_a', 'termination_b_parent', 'termination_b',
-            'status', 'type', 'tenant', 'color', 'length', 'tags',
+            'status', 'type', 'tenant', 'color', 'length', 'tags', 'created', 'last_updated',
         )
         default_columns = (
             'pk', 'id', 'label', 'termination_a_parent', 'termination_a', 'termination_b_parent', 'termination_b',

+ 20 - 11
netbox/dcim/tables/devices.py

@@ -99,7 +99,7 @@ class DeviceRoleTable(BaseTable):
         model = DeviceRole
         fields = (
             'pk', 'id', 'name', 'device_count', 'vm_count', 'color', 'vm_role', 'description', 'slug', 'tags',
-            'actions',
+            'actions', 'created', 'last_updated',
         )
         default_columns = ('pk', 'name', 'device_count', 'vm_count', 'color', 'vm_role', 'description')
 
@@ -131,7 +131,7 @@ class PlatformTable(BaseTable):
         model = Platform
         fields = (
             'pk', 'id', 'name', 'manufacturer', 'device_count', 'vm_count', 'slug', 'napalm_driver', 'napalm_args',
-            'description', 'tags', 'actions',
+            'description', 'tags', 'actions', 'created', 'last_updated',
         )
         default_columns = (
             'pk', 'name', 'manufacturer', 'device_count', 'vm_count', 'napalm_driver', 'description',
@@ -205,7 +205,8 @@ class DeviceTable(BaseTable):
         fields = (
             'pk', 'id', 'name', 'status', 'tenant', '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', 'tags',
+            'primary_ip6', 'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments', 'tags', 'created',
+            'last_updated',
         )
         default_columns = (
             'pk', 'name', 'status', 'tenant', 'site', 'location', 'rack', 'device_role', 'manufacturer', 'device_type',
@@ -311,7 +312,7 @@ class ConsolePortTable(ModularDeviceComponentTable, PathEndpointTable):
         model = ConsolePort
         fields = (
             'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'speed', 'description',
-            'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'tags',
+            'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'tags', 'created', 'last_updated',
         )
         default_columns = ('pk', 'name', 'device', 'label', 'type', 'speed', 'description')
 
@@ -353,7 +354,7 @@ class ConsoleServerPortTable(ModularDeviceComponentTable, PathEndpointTable):
         model = ConsoleServerPort
         fields = (
             'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'speed', 'description',
-            'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'tags',
+            'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'tags', 'created', 'last_updated',
         )
         default_columns = ('pk', 'name', 'device', 'label', 'type', 'speed', 'description')
 
@@ -396,7 +397,8 @@ class PowerPortTable(ModularDeviceComponentTable, PathEndpointTable):
         model = PowerPort
         fields = (
             'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'description', 'mark_connected',
-            'maximum_draw', 'allocated_draw', 'cable', 'cable_color', 'link_peer', 'connection', 'tags',
+            'maximum_draw', 'allocated_draw', 'cable', 'cable_color', 'link_peer', 'connection', 'tags', 'created',
+            'last_updated',
         )
         default_columns = ('pk', 'name', 'device', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description')
 
@@ -444,7 +446,8 @@ class PowerOutletTable(ModularDeviceComponentTable, PathEndpointTable):
         model = PowerOutlet
         fields = (
             'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'description', 'power_port',
-            'feed_leg', 'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'tags',
+            'feed_leg', 'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'tags', 'created',
+            'last_updated',
         )
         default_columns = ('pk', 'name', 'device', 'label', 'type', 'power_port', 'feed_leg', 'description')
 
@@ -524,6 +527,7 @@ class InterfaceTable(ModularDeviceComponentTable, BaseInterfaceTable, PathEndpoi
             'mode', 'mac_address', 'wwn', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width',
             'tx_power', 'description', 'mark_connected', 'cable', 'cable_color', 'wireless_link', 'wireless_lans',
             'link_peer', 'connection', 'tags', 'vrf', 'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans',
+            'created', 'last_updated',
         )
         default_columns = ('pk', 'name', 'device', 'label', 'enabled', 'type', 'description')
 
@@ -594,6 +598,7 @@ class FrontPortTable(ModularDeviceComponentTable, CableTerminationTable):
         fields = (
             'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'color', 'rear_port',
             'rear_port_position', 'description', 'mark_connected', 'cable', 'cable_color', 'link_peer', 'tags',
+            'created', 'last_updated',
         )
         default_columns = (
             'pk', 'name', 'device', 'label', 'type', 'color', 'rear_port', 'rear_port_position', 'description',
@@ -641,7 +646,7 @@ class RearPortTable(ModularDeviceComponentTable, CableTerminationTable):
         model = RearPort
         fields = (
             'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'color', 'positions', 'description',
-            'mark_connected', 'cable', 'cable_color', 'link_peer', 'tags',
+            'mark_connected', 'cable', 'cable_color', 'link_peer', 'tags', 'created', 'last_updated',
         )
         default_columns = ('pk', 'name', 'device', 'label', 'type', 'color', 'description')
 
@@ -691,7 +696,11 @@ class DeviceBayTable(DeviceComponentTable):
 
     class Meta(DeviceComponentTable.Meta):
         model = DeviceBay
-        fields = ('pk', 'id', 'name', 'device', 'label', 'status', 'installed_device', 'description', 'tags')
+        fields = (
+            'pk', 'id', 'name', 'device', 'label', 'status', 'installed_device', 'description', 'tags',
+            'created', 'last_updated',
+        )
+
         default_columns = ('pk', 'name', 'device', 'label', 'status', 'installed_device', 'description')
 
 
@@ -774,7 +783,7 @@ class InventoryItemTable(DeviceComponentTable):
         model = InventoryItem
         fields = (
             'pk', 'id', 'name', 'device', 'component', 'label', 'role', 'manufacturer', 'part_id', 'serial',
-            'asset_tag', 'description', 'discovered', 'tags',
+            'asset_tag', 'description', 'discovered', 'tags', 'created', 'last_updated',
         )
         default_columns = (
             'pk', 'name', 'device', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag',
@@ -847,5 +856,5 @@ class VirtualChassisTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = VirtualChassis
-        fields = ('pk', 'id', 'name', 'domain', 'master', 'member_count', 'tags')
+        fields = ('pk', 'id', 'name', 'domain', 'master', 'member_count', 'tags', 'created', 'last_updated',)
         default_columns = ('pk', 'name', 'domain', 'master', 'member_count')

+ 2 - 2
netbox/dcim/tables/devicetypes.py

@@ -53,7 +53,7 @@ class ManufacturerTable(BaseTable):
         model = Manufacturer
         fields = (
             'pk', 'id', 'name', 'devicetype_count', 'inventoryitem_count', 'platform_count', 'description', 'slug',
-            'actions',
+            'actions', 'created', 'last_updated',
         )
         default_columns = (
             'pk', 'name', 'devicetype_count', 'inventoryitem_count', 'platform_count', 'description', 'slug',
@@ -87,7 +87,7 @@ class DeviceTypeTable(BaseTable):
         model = DeviceType
         fields = (
             'pk', 'id', 'model', 'manufacturer', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role',
-            'airflow', 'comments', 'instance_count', 'tags',
+            'airflow', 'comments', 'instance_count', 'tags', 'created', 'last_updated',
         )
         default_columns = (
             'pk', 'model', 'manufacturer', 'part_number', 'u_height', 'is_full_depth', 'instance_count',

+ 2 - 2
netbox/dcim/tables/power.py

@@ -33,7 +33,7 @@ class PowerPanelTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = PowerPanel
-        fields = ('pk', 'id', 'name', 'site', 'location', 'powerfeed_count', 'tags')
+        fields = ('pk', 'id', 'name', 'site', 'location', 'powerfeed_count', 'tags', 'created', 'last_updated',)
         default_columns = ('pk', 'name', 'site', 'location', 'powerfeed_count')
 
 
@@ -72,7 +72,7 @@ class PowerFeedTable(CableTerminationTable):
         fields = (
             'pk', 'id', 'name', 'power_panel', 'rack', 'status', 'type', 'supply', 'voltage', 'amperage', 'phase',
             'max_utilization', 'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'available_power',
-            'comments', 'tags',
+            'comments', 'tags', 'created', 'last_updated',
         )
         default_columns = (
             'pk', 'name', 'power_panel', 'rack', 'status', 'type', 'supply', 'voltage', 'amperage', 'phase', 'cable',

+ 8 - 4
netbox/dcim/tables/racks.py

@@ -30,7 +30,10 @@ class RackRoleTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = RackRole
-        fields = ('pk', 'id', 'name', 'rack_count', 'color', 'description', 'slug', 'tags', 'actions')
+        fields = (
+            'pk', 'id', 'name', 'rack_count', 'color', 'description', 'slug', 'tags', 'actions', 'created',
+            'last_updated',
+        )
         default_columns = ('pk', 'name', 'rack_count', 'color', 'description')
 
 
@@ -86,8 +89,9 @@ class RackTable(BaseTable):
     class Meta(BaseTable.Meta):
         model = Rack
         fields = (
-            'pk', 'id', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'serial', 'asset_tag', 'type',
-            'width', 'outer_width', 'outer_depth', 'u_height', 'comments', 'device_count', 'get_utilization', 'get_power_utilization', 'tags',
+            'pk', 'id', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'serial', 'asset_tag',
+            'type', 'width', 'outer_width', 'outer_depth', 'u_height', 'comments', 'device_count', 'get_utilization',
+            'get_power_utilization', 'tags', 'created', 'last_updated',
         )
         default_columns = (
             'pk', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'u_height', 'device_count',
@@ -125,6 +129,6 @@ class RackReservationTable(BaseTable):
         model = RackReservation
         fields = (
             'pk', 'id', 'reservation', 'site', 'rack', 'unit_list', 'user', 'created', 'tenant', 'description', 'tags',
-            'actions',
+            'actions', 'created', 'last_updated',
         )
         default_columns = ('pk', 'reservation', 'site', 'rack', 'unit_list', 'user', 'description')

+ 8 - 4
netbox/dcim/tables/sites.py

@@ -35,7 +35,9 @@ class RegionTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = Region
-        fields = ('pk', 'id', 'name', 'slug', 'site_count', 'description', 'tags', 'actions')
+        fields = (
+            'pk', 'id', 'name', 'slug', 'site_count', 'description', 'tags', 'created', 'last_updated', 'actions',
+        )
         default_columns = ('pk', 'name', 'site_count', 'description')
 
 
@@ -59,7 +61,9 @@ class SiteGroupTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = SiteGroup
-        fields = ('pk', 'id', 'name', 'slug', 'site_count', 'description', 'tags', 'actions')
+        fields = (
+            'pk', 'id', 'name', 'slug', 'site_count', 'description', 'tags', 'created', 'last_updated', 'actions',
+        )
         default_columns = ('pk', 'name', 'site_count', 'description')
 
 
@@ -96,7 +100,7 @@ class SiteTable(BaseTable):
         fields = (
             'pk', 'id', 'name', 'slug', 'status', 'facility', 'region', 'group', 'tenant', 'asn_count', 'time_zone',
             'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'comments', 'tags',
-            'actions',
+            'created', 'last_updated', 'actions',
         )
         default_columns = ('pk', 'name', 'status', 'facility', 'region', 'group', 'tenant', 'description')
 
@@ -135,6 +139,6 @@ class LocationTable(BaseTable):
         model = Location
         fields = (
             'pk', 'id', 'name', 'site', 'tenant', 'rack_count', 'device_count', 'description', 'slug', 'tags',
-            'actions',
+            'actions', 'created', 'last_updated',
         )
         default_columns = ('pk', 'name', 'site', 'tenant', 'rack_count', 'device_count', 'description')

+ 8 - 5
netbox/extras/api/serializers.py

@@ -63,7 +63,7 @@ class WebhookSerializer(ValidatedModelSerializer):
         fields = [
             'id', 'url', 'display', 'content_types', 'name', 'type_create', 'type_update', 'type_delete', 'payload_url',
             'enabled', 'http_method', 'http_content_type', 'additional_headers', 'body_template', 'secret',
-            'conditions', 'ssl_verification', 'ca_file_path',
+            'conditions', 'ssl_verification', 'ca_file_path', 'created', 'last_updated',
         ]
 
 
@@ -84,7 +84,8 @@ class CustomFieldSerializer(ValidatedModelSerializer):
         model = CustomField
         fields = [
             'id', 'url', 'display', 'content_types', 'type', 'name', 'label', 'description', 'required', 'filter_logic',
-            'default', 'weight', 'validation_minimum', 'validation_maximum', 'validation_regex', 'choices',
+            'default', 'weight', 'validation_minimum', 'validation_maximum', 'validation_regex', 'choices', 'created',
+            'last_updated',
         ]
 
 
@@ -102,7 +103,7 @@ class CustomLinkSerializer(ValidatedModelSerializer):
         model = CustomLink
         fields = [
             'id', 'url', 'display', 'content_type', 'name', 'enabled', 'link_text', 'link_url', 'weight', 'group_name',
-            'button_class', 'new_window',
+            'button_class', 'new_window', 'created', 'last_updated',
         ]
 
 
@@ -120,7 +121,7 @@ class ExportTemplateSerializer(ValidatedModelSerializer):
         model = ExportTemplate
         fields = [
             'id', 'url', 'display', 'content_type', 'name', 'description', 'template_code', 'mime_type',
-            'file_extension', 'as_attachment',
+            'file_extension', 'as_attachment', 'created', 'last_updated',
         ]
 
 
@@ -134,7 +135,9 @@ class TagSerializer(ValidatedModelSerializer):
 
     class Meta:
         model = Tag
-        fields = ['id', 'url', 'display', 'name', 'slug', 'color', 'description', 'tagged_items']
+        fields = [
+            'id', 'url', 'display', 'name', 'slug', 'color', 'description', 'tagged_items', 'created', 'last_updated',
+        ]
 
 
 #

+ 28 - 31
netbox/extras/forms/customfields.py

@@ -4,7 +4,7 @@ from django.db.models import Q
 
 from extras.choices import *
 from extras.models import *
-from utilities.forms import BootstrapMixin, BulkEditForm, CSVModelForm, FilterForm
+from utilities.forms import BootstrapMixin, BulkEditBaseForm, CSVModelForm
 
 __all__ = (
     'CustomFieldModelCSVForm',
@@ -34,6 +34,9 @@ class CustomFieldsMixin:
             raise NotImplementedError(f"{self.__class__.__name__} must specify a model class.")
         return ContentType.objects.get_for_model(self.model)
 
+    def _get_custom_fields(self, content_type):
+        return CustomField.objects.filter(content_types=content_type)
+
     def _get_form_field(self, customfield):
         return customfield.to_form_field()
 
@@ -41,10 +44,7 @@ class CustomFieldsMixin:
         """
         Append form fields for all CustomFields assigned to this object type.
         """
-        content_type = self._get_content_type()
-
-        # Append form fields; assign initial values if modifying and existing object
-        for customfield in CustomField.objects.filter(content_types=content_type):
+        for customfield in self._get_custom_fields(self._get_content_type()):
             field_name = f'cf_{customfield.name}'
             self.fields[field_name] = self._get_form_field(customfield)
 
@@ -89,40 +89,37 @@ class CustomFieldModelCSVForm(CSVModelForm, CustomFieldModelForm):
         return customfield.to_form_field(for_csv_import=True)
 
 
-class CustomFieldModelBulkEditForm(BulkEditForm):
-
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
+class CustomFieldModelBulkEditForm(BootstrapMixin, CustomFieldsMixin, BulkEditBaseForm):
 
-        self.custom_fields = []
-        self.obj_type = ContentType.objects.get_for_model(self.model)
+    def _get_form_field(self, customfield):
+        return customfield.to_form_field(set_initial=False, enforce_required=False)
 
-        # Add all applicable CustomFields to the form
-        custom_fields = CustomField.objects.filter(content_types=self.obj_type)
-        for cf in custom_fields:
+    def _append_customfield_fields(self):
+        """
+        Append form fields for all CustomFields assigned to this object type.
+        """
+        for customfield in self._get_custom_fields(self._get_content_type()):
             # Annotate non-required custom fields as nullable
-            if not cf.required:
-                self.nullable_fields.append(cf.name)
-            self.fields[cf.name] = cf.to_form_field(set_initial=False, enforce_required=False)
-            # Annotate this as a custom field
-            self.custom_fields.append(cf.name)
+            if not customfield.required:
+                self.nullable_fields.append(customfield.name)
 
+            self.fields[customfield.name] = self._get_form_field(customfield)
 
-class CustomFieldModelFilterForm(FilterForm):
-
-    def __init__(self, *args, **kwargs):
+            # Annotate the field in the list of CustomField form fields
+            self.custom_fields[customfield.name] = customfield
 
-        self.obj_type = ContentType.objects.get_for_model(self.model)
 
-        super().__init__(*args, **kwargs)
+class CustomFieldModelFilterForm(BootstrapMixin, CustomFieldsMixin, forms.Form):
+    q = forms.CharField(
+        required=False,
+        label='Search'
+    )
 
-        # Add all applicable CustomFields to the form
-        self.custom_field_filters = []
-        custom_fields = CustomField.objects.filter(content_types=self.obj_type).exclude(
+    def _get_custom_fields(self, content_type):
+        return CustomField.objects.filter(content_types=content_type).exclude(
             Q(filter_logic=CustomFieldFilterLogicChoices.FILTER_DISABLED) |
             Q(type=CustomFieldTypeChoices.TYPE_JSON)
         )
-        for cf in custom_fields:
-            field_name = f'cf_{cf.name}'
-            self.fields[field_name] = cf.to_form_field(set_initial=False, enforce_required=False)
-            self.custom_field_filters.append(field_name)
+
+    def _get_form_field(self, customfield):
+        return customfield.to_form_field(set_initial=False, enforce_required=False)

+ 7 - 5
netbox/extras/tables.py

@@ -58,7 +58,7 @@ class CustomFieldTable(BaseTable):
         model = CustomField
         fields = (
             'pk', 'id', 'name', 'content_types', 'label', 'type', 'required', 'weight', 'default',
-            'description', 'filter_logic', 'choices',
+            'description', 'filter_logic', 'choices', 'created', 'last_updated',
         )
         default_columns = ('pk', 'name', 'content_types', 'label', 'type', 'required', 'description')
 
@@ -80,7 +80,7 @@ class CustomLinkTable(BaseTable):
         model = CustomLink
         fields = (
             'pk', 'id', 'name', 'content_type', 'enabled', 'link_text', 'link_url', 'weight', 'group_name',
-            'button_class', 'new_window',
+            'button_class', 'new_window', 'created', 'last_updated',
         )
         default_columns = ('pk', 'name', 'content_type', 'enabled', 'group_name', 'button_class', 'new_window')
 
@@ -101,6 +101,7 @@ class ExportTemplateTable(BaseTable):
         model = ExportTemplate
         fields = (
             'pk', 'id', 'name', 'content_type', 'description', 'mime_type', 'file_extension', 'as_attachment',
+            'created', 'last_updated',
         )
         default_columns = (
             'pk', 'name', 'content_type', 'description', 'mime_type', 'file_extension', 'as_attachment',
@@ -135,7 +136,7 @@ class WebhookTable(BaseTable):
         model = Webhook
         fields = (
             'pk', 'id', 'name', 'content_types', 'enabled', 'type_create', 'type_update', 'type_delete', 'http_method',
-            'payload_url', 'secret', 'ssl_validation', 'ca_file_path',
+            'payload_url', 'secret', 'ssl_validation', 'ca_file_path', 'created', 'last_updated',
         )
         default_columns = (
             'pk', 'name', 'content_types', 'enabled', 'type_create', 'type_update', 'type_delete', 'http_method',
@@ -156,7 +157,7 @@ class TagTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = Tag
-        fields = ('pk', 'id', 'name', 'items', 'slug', 'color', 'description', 'actions')
+        fields = ('pk', 'id', 'name', 'items', 'slug', 'color', 'description', 'created', 'last_updated', 'actions')
         default_columns = ('pk', 'name', 'items', 'slug', 'color', 'description')
 
 
@@ -193,7 +194,8 @@ class ConfigContextTable(BaseTable):
         model = ConfigContext
         fields = (
             'pk', 'id', 'name', 'weight', 'is_active', 'description', 'regions', 'sites', 'roles',
-            'platforms', 'cluster_types', 'cluster_groups', 'clusters', 'tenant_groups', 'tenants',
+            'platforms', 'cluster_types', 'cluster_groups', 'clusters', 'tenant_groups', 'tenants', 'created',
+            'last_updated',
         )
         default_columns = ('pk', 'name', 'weight', 'is_active', 'description')
 

+ 20 - 1
netbox/ipam/models/ip.py

@@ -125,11 +125,30 @@ class ASN(PrimaryModel):
         verbose_name_plural = 'ASNs'
 
     def __str__(self):
-        return f'AS{self.asn}'
+        return f'AS{self.asn_with_asdot}'
 
     def get_absolute_url(self):
         return reverse('ipam:asn', args=[self.pk])
 
+    @property
+    def asn_asdot(self):
+        """
+        Return ASDOT notation for AS numbers greater than 16 bits.
+        """
+        if self.asn > 65535:
+            return f'{self.asn // 65536}.{self.asn % 65536}'
+        return self.asn
+
+    @property
+    def asn_with_asdot(self):
+        """
+        Return both plain and ASDOT notation, where applicable.
+        """
+        if self.asn > 65535:
+            return f'{self.asn} ({self.asn // 65536}.{self.asn % 65536})'
+        else:
+            return self.asn
+
 
 @extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks')
 class Aggregate(GetAvailablePrefixesMixin, PrimaryModel):

+ 1 - 1
netbox/ipam/tables/fhrp.py

@@ -38,7 +38,7 @@ class FHRPGroupTable(BaseTable):
         model = FHRPGroup
         fields = (
             'pk', 'group_id', 'protocol', 'auth_type', 'auth_key', 'description', 'ip_addresses', 'interface_count',
-            'tags',
+            'tags', 'created', 'last_updated',
         )
         default_columns = ('pk', 'group_id', 'protocol', 'auth_type', 'description', 'ip_addresses', 'interface_count')
 

+ 19 - 8
netbox/ipam/tables/ip.py

@@ -91,7 +91,10 @@ class RIRTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = RIR
-        fields = ('pk', 'id', 'name', 'slug', 'is_private', 'aggregate_count', 'description', 'tags', 'actions')
+        fields = (
+            'pk', 'id', 'name', 'slug', 'is_private', 'aggregate_count', 'description', 'tags', 'created',
+            'last_updated', 'actions',
+        )
         default_columns = ('pk', 'name', 'is_private', 'aggregate_count', 'description')
 
 
@@ -102,8 +105,10 @@ class RIRTable(BaseTable):
 class ASNTable(BaseTable):
     pk = ToggleColumn()
     asn = tables.Column(
+        accessor=tables.A('asn_asdot'),
         linkify=True
     )
+
     site_count = LinkedCountColumn(
         viewname='dcim:site_list',
         url_params={'asn_id': 'pk'},
@@ -112,7 +117,7 @@ class ASNTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = ASN
-        fields = ('pk', 'asn', 'rir', 'site_count', 'tenant', 'description', 'actions')
+        fields = ('pk', 'asn', 'rir', 'site_count', 'tenant', 'description', 'created', 'last_updated', 'actions')
         default_columns = ('pk', 'asn', 'rir', 'site_count', 'sites', 'tenant')
 
 
@@ -144,7 +149,10 @@ class AggregateTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = Aggregate
-        fields = ('pk', 'id', 'prefix', 'rir', 'tenant', 'child_count', 'utilization', 'date_added', 'description', 'tags')
+        fields = (
+            'pk', 'id', 'prefix', 'rir', 'tenant', 'child_count', 'utilization', 'date_added', 'description', 'tags',
+            'created', 'last_updated',
+        )
         default_columns = ('pk', 'prefix', 'rir', 'tenant', 'child_count', 'utilization', 'date_added', 'description')
 
 
@@ -173,7 +181,10 @@ class RoleTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = Role
-        fields = ('pk', 'id', 'name', 'slug', 'prefix_count', 'vlan_count', 'description', 'weight', 'tags', 'actions')
+        fields = (
+            'pk', 'id', 'name', 'slug', 'prefix_count', 'vlan_count', 'description', 'weight', 'tags', 'created',
+            'last_updated', 'actions',
+        )
         default_columns = ('pk', 'name', 'prefix_count', 'vlan_count', 'description')
 
 
@@ -260,8 +271,8 @@ class PrefixTable(BaseTable):
     class Meta(BaseTable.Meta):
         model = Prefix
         fields = (
-            'pk', 'id', 'prefix', 'prefix_flat', 'status', 'children', 'vrf', 'utilization', 'tenant', 'site', 'vlan_group',
-            'vlan', 'role', 'is_pool', 'mark_utilized', 'description', 'tags',
+            'pk', 'id', 'prefix', 'prefix_flat', 'status', 'children', 'vrf', 'utilization', 'tenant', 'site',
+            'vlan_group', 'vlan', 'role', 'is_pool', 'mark_utilized', 'description', 'tags', 'created', 'last_updated',
         )
         default_columns = (
             'pk', 'prefix', 'status', 'children', 'vrf', 'utilization', 'tenant', 'site', 'vlan', 'role', 'description',
@@ -302,7 +313,7 @@ class IPRangeTable(BaseTable):
         model = IPRange
         fields = (
             'pk', 'id', 'start_address', 'end_address', 'size', 'vrf', 'status', 'role', 'tenant', 'description',
-            'utilization', 'tags',
+            'utilization', 'tags', 'created', 'last_updated',
         )
         default_columns = (
             'pk', 'start_address', 'end_address', 'size', 'vrf', 'status', 'role', 'tenant', 'description',
@@ -360,7 +371,7 @@ class IPAddressTable(BaseTable):
         model = IPAddress
         fields = (
             'pk', 'id', 'address', 'vrf', 'status', 'role', 'tenant', 'nat_inside', 'assigned', 'dns_name', 'description',
-            'tags',
+            'tags', 'created', 'last_updated',
         )
         default_columns = (
             'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'assigned', 'dns_name', 'description',

+ 4 - 1
netbox/ipam/tables/services.py

@@ -45,5 +45,8 @@ class ServiceTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = Service
-        fields = ('pk', 'id', 'name', 'parent', 'protocol', 'ports', 'ipaddresses', 'description', 'tags')
+        fields = (
+            'pk', 'id', 'name', 'parent', 'protocol', 'ports', 'ipaddresses', 'description', 'tags', 'created',
+            'last_updated',
+        )
         default_columns = ('pk', 'name', 'parent', 'protocol', 'ports', 'description')

+ 5 - 2
netbox/ipam/tables/vlans.py

@@ -85,7 +85,7 @@ class VLANGroupTable(BaseTable):
         model = VLANGroup
         fields = (
             'pk', 'id', 'name', 'scope_type', 'scope', 'min_vid', 'max_vid', 'vlan_count', 'slug', 'description',
-            'tags', 'actions',
+            'tags', 'created', 'last_updated', 'actions',
         )
         default_columns = ('pk', 'name', 'scope_type', 'scope', 'vlan_count', 'description')
 
@@ -127,7 +127,10 @@ class VLANTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = VLAN
-        fields = ('pk', 'id', 'vid', 'name', 'site', 'group', 'prefixes', 'tenant', 'status', 'role', 'description', 'tags')
+        fields = (
+            'pk', 'id', 'vid', 'name', 'site', 'group', 'prefixes', 'tenant', 'status', 'role', 'description', 'tags',
+            'created', 'last_updated',
+        )
         default_columns = ('pk', 'vid', 'name', 'site', 'group', 'prefixes', 'tenant', 'status', 'role', 'description')
         row_attrs = {
             'class': lambda record: 'success' if not isinstance(record, VLAN) else '',

+ 3 - 2
netbox/ipam/tables/vrfs.py

@@ -47,7 +47,8 @@ class VRFTable(BaseTable):
     class Meta(BaseTable.Meta):
         model = VRF
         fields = (
-            'pk', 'id', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'import_targets', 'export_targets', 'tags',
+            'pk', 'id', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'import_targets', 'export_targets',
+            'tags', 'created', 'last_updated',
         )
         default_columns = ('pk', 'name', 'rd', 'tenant', 'description')
 
@@ -68,5 +69,5 @@ class RouteTargetTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = RouteTarget
-        fields = ('pk', 'id', 'name', 'tenant', 'description', 'tags')
+        fields = ('pk', 'id', 'name', 'tenant', 'description', 'tags', 'created', 'last_updated',)
         default_columns = ('pk', 'name', 'tenant', 'description')

+ 1 - 1
netbox/netbox/views/generic/bulk_views.py

@@ -287,7 +287,7 @@ class BulkEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
     def _update_objects(self, form, request):
         custom_fields = getattr(form, 'custom_fields', [])
         standard_fields = [
-            field for field in form.fields if field not in custom_fields + ['pk']
+            field for field in form.fields if field not in list(custom_fields) + ['pk']
         ]
         nullified_fields = request.POST.getlist('_nullify')
         updated_objects = []

+ 3 - 3
netbox/templates/inc/filter_list.html

@@ -24,17 +24,17 @@
       {% else %}
         {# List all non-customfield filters as declared in the form class #}
         {% for field in filter_form.visible_fields %}
-          {% if not filter_form.custom_field_filters or field.name not in filter_form.custom_field_filters %}
+          {% if not filter_form.custom_fields or field.name not in filter_form.custom_fields %}
             <div class="col col-12">
               {% render_field field %}
             </div>
           {% endif %}
         {% endfor %}
       {% endif %}
-      {% if filter_form.custom_field_filters %}
+      {% if filter_form.custom_fields %}
         {# List all custom field filters #}
         <hr class="card-divider mt-0" />
-        {% for name in filter_form.custom_field_filters %}
+        {% for name in filter_form.custom_fields %}
           <div class="col col-12">
             {% with field=filter_form|get_item:name %}
               {% render_field field %}

+ 1 - 1
netbox/templates/ipam/asn.html

@@ -18,7 +18,7 @@
           <table class="table table-hover attr-table">
             <tr>
               <td>AS Number</td>
-              <td>{{ object.asn }}</td>
+              <td>{{ object.asn_with_asdot }}</td>
             </tr>
             <tr>
               <td>RIR</td>

+ 12 - 5
netbox/tenancy/tables.py

@@ -62,7 +62,9 @@ class TenantGroupTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = TenantGroup
-        fields = ('pk', 'id', 'name', 'tenant_count', 'description', 'slug', 'tags', 'actions')
+        fields = (
+            'pk', 'id', 'name', 'tenant_count', 'description', 'slug', 'tags', 'created', 'last_updated', 'actions',
+        )
         default_columns = ('pk', 'name', 'tenant_count', 'description')
 
 
@@ -81,7 +83,7 @@ class TenantTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = Tenant
-        fields = ('pk', 'id', 'name', 'slug', 'group', 'description', 'comments', 'tags')
+        fields = ('pk', 'id', 'name', 'slug', 'group', 'description', 'comments', 'tags', 'created', 'last_updated',)
         default_columns = ('pk', 'name', 'group', 'description')
 
 
@@ -105,7 +107,9 @@ class ContactGroupTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = ContactGroup
-        fields = ('pk', 'name', 'contact_count', 'description', 'slug', 'tags', 'actions')
+        fields = (
+            'pk', 'name', 'contact_count', 'description', 'slug', 'tags', 'created', 'last_updated', 'actions',
+        )
         default_columns = ('pk', 'name', 'contact_count', 'description')
 
 
@@ -117,7 +121,7 @@ class ContactRoleTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = ContactRole
-        fields = ('pk', 'name', 'description', 'slug', 'actions')
+        fields = ('pk', 'name', 'description', 'slug', 'created', 'last_updated', 'actions')
         default_columns = ('pk', 'name', 'description')
 
 
@@ -142,7 +146,10 @@ class ContactTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = Contact
-        fields = ('pk', 'name', 'group', 'title', 'phone', 'email', 'address', 'comments', 'assignment_count', 'tags')
+        fields = (
+            'pk', 'name', 'group', 'title', 'phone', 'email', 'address', 'comments', 'assignment_count', 'tags',
+            'created', 'last_updated',
+        )
         default_columns = ('pk', 'name', 'group', 'assignment_count', 'title', 'phone', 'email')
 
 

+ 7 - 7
netbox/utilities/forms/forms.py

@@ -3,7 +3,6 @@ import re
 
 import yaml
 from django import forms
-from django.utils.translation import gettext as _
 
 from .widgets import APISelect, APISelectMultiple, ClearableFileInput, StaticSelect
 
@@ -11,6 +10,7 @@ from .widgets import APISelect, APISelectMultiple, ClearableFileInput, StaticSel
 __all__ = (
     'BootstrapMixin',
     'BulkEditForm',
+    'BulkEditBaseForm',
     'BulkRenameForm',
     'ConfirmationForm',
     'CSVModelForm',
@@ -75,11 +75,10 @@ class ConfirmationForm(BootstrapMixin, ReturnURLForm):
     confirm = forms.BooleanField(required=True, widget=forms.HiddenInput(), initial=True)
 
 
-class BulkEditForm(BootstrapMixin, forms.Form):
+class BulkEditBaseForm(forms.Form):
     """
     Base form for editing multiple objects in bulk
     """
-
     def __init__(self, model, *args, **kwargs):
         super().__init__(*args, **kwargs)
         self.model = model
@@ -90,6 +89,10 @@ class BulkEditForm(BootstrapMixin, forms.Form):
             self.nullable_fields = self.Meta.nullable_fields
 
 
+class BulkEditForm(BootstrapMixin, BulkEditBaseForm):
+    pass
+
+
 class BulkRenameForm(BootstrapMixin, forms.Form):
     """
     An extendable form to be used for renaming objects in bulk.
@@ -185,10 +188,7 @@ class FilterForm(BootstrapMixin, forms.Form):
     """
     q = forms.CharField(
         required=False,
-        widget=forms.TextInput(
-            attrs={'placeholder': _('All fields')}
-        ),
-        label=_('Search')
+        label='Search'
     )
 
 

+ 13 - 6
netbox/virtualization/tables.py

@@ -44,7 +44,9 @@ class ClusterTypeTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = ClusterType
-        fields = ('pk', 'id', 'name', 'slug', 'cluster_count', 'description', 'tags', 'actions')
+        fields = (
+            'pk', 'id', 'name', 'slug', 'cluster_count', 'description', 'created', 'last_updated', 'tags', 'actions',
+        )
         default_columns = ('pk', 'name', 'cluster_count', 'description')
 
 
@@ -66,7 +68,9 @@ class ClusterGroupTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = ClusterGroup
-        fields = ('pk', 'id', 'name', 'slug', 'cluster_count', 'description', 'tags', 'actions')
+        fields = (
+            'pk', 'id', 'name', 'slug', 'cluster_count', 'description', 'tags', 'created', 'last_updated', 'actions',
+        )
         default_columns = ('pk', 'name', 'cluster_count', 'description')
 
 
@@ -108,7 +112,10 @@ class ClusterTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = Cluster
-        fields = ('pk', 'id', 'name', 'type', 'group', 'tenant', 'site', 'comments', 'device_count', 'vm_count', 'tags')
+        fields = (
+            'pk', 'id', 'name', 'type', 'group', 'tenant', 'site', 'comments', 'device_count', 'vm_count', 'tags',
+            'created', 'last_updated',
+        )
         default_columns = ('pk', 'name', 'type', 'group', 'tenant', 'site', 'device_count', 'vm_count')
 
 
@@ -149,8 +156,8 @@ class VirtualMachineTable(BaseTable):
     class Meta(BaseTable.Meta):
         model = VirtualMachine
         fields = (
-            'pk', 'id', 'name', 'status', 'cluster', 'role', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'primary_ip4',
-            'primary_ip6', 'primary_ip', 'comments', 'tags',
+            'pk', 'id', 'name', 'status', 'cluster', 'role', 'tenant', 'platform', 'vcpus', 'memory', 'disk',
+            'primary_ip4', 'primary_ip6', 'primary_ip', 'comments', 'tags', 'created', 'last_updated',
         )
         default_columns = (
             'pk', 'name', 'status', 'cluster', 'role', 'tenant', 'vcpus', 'memory', 'disk', 'primary_ip',
@@ -177,7 +184,7 @@ class VMInterfaceTable(BaseInterfaceTable):
         model = VMInterface
         fields = (
             'pk', 'id', 'name', 'virtual_machine', 'enabled', 'mac_address', 'mtu', 'mode', 'description', 'tags',
-            'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans',
+            'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'created', 'last_updated',
         )
         default_columns = ('pk', 'name', 'virtual_machine', 'enabled', 'description')
 

+ 5 - 3
netbox/wireless/tables.py

@@ -27,7 +27,9 @@ class WirelessLANGroupTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         model = WirelessLANGroup
-        fields = ('pk', 'name', 'wirelesslan_count', 'description', 'slug', 'tags', 'actions')
+        fields = (
+            'pk', 'name', 'wirelesslan_count', 'description', 'slug', 'tags', 'created', 'last_updated', 'actions',
+        )
         default_columns = ('pk', 'name', 'wirelesslan_count', 'description')
 
 
@@ -50,7 +52,7 @@ class WirelessLANTable(BaseTable):
         model = WirelessLAN
         fields = (
             'pk', 'ssid', 'group', 'description', 'vlan', 'interface_count', 'auth_type', 'auth_cipher', 'auth_psk',
-            'tags',
+            'tags', 'created', 'last_updated',
         )
         default_columns = ('pk', 'ssid', 'group', 'description', 'vlan', 'auth_type', 'interface_count')
 
@@ -99,7 +101,7 @@ class WirelessLinkTable(BaseTable):
         model = WirelessLink
         fields = (
             'pk', 'id', 'status', 'device_a', 'interface_a', 'device_b', 'interface_b', 'ssid', 'description',
-            'auth_type', 'auth_cipher', 'auth_psk', 'tags',
+            'auth_type', 'auth_cipher', 'auth_psk', 'tags', 'created', 'last_updated',
         )
         default_columns = (
             'pk', 'id', 'status', 'device_a', 'interface_a', 'device_b', 'interface_b', 'ssid', 'auth_type',

+ 3 - 3
requirements.txt

@@ -1,5 +1,5 @@
 Django==3.2.11
-django-cors-headers==3.10.1
+django-cors-headers==3.11.0
 django-debug-toolbar==3.2.4
 django-filter==21.1
 django-graphiql-debug-toolbar==0.2.0
@@ -10,7 +10,7 @@ django-redis==5.2.0
 django-rq==2.5.1
 django-tables2==2.4.1
 django-taggit==2.0.0
-django-timezone-field==4.2.1
+django-timezone-field==4.2.3
 djangorestframework==3.12.4
 drf-yasg[validation]==1.20.0
 graphene_django==2.15.0
@@ -18,7 +18,7 @@ gunicorn==20.1.0
 Jinja2==3.0.3
 Markdown==3.3.6
 markdown-include==0.6.0
-mkdocs-material==8.1.4
+mkdocs-material==8.1.7
 netaddr==0.8.0
 Pillow==8.4.0
 psycopg2-binary==2.9.3