Просмотр исходного кода

Closes #10545: Standardize description & comment fields on primary models (#10834)

* Standardize description & comments fields on primary models

* Update REST API serializers

* Update forms

* Update tables

* Update templates
Jeremy Stretch 3 лет назад
Родитель
Сommit
bc6b5bc4be
100 измененных файлов с 969 добавлено и 513 удалено
  1. 51 0
      docs/release-notes/version-3.4.md
  2. 2 2
      netbox/circuits/api/serializers.py
  3. 5 1
      netbox/circuits/forms/bulk_edit.py
  4. 1 1
      netbox/circuits/forms/bulk_import.py
  5. 4 5
      netbox/circuits/forms/model_forms.py
  6. 18 0
      netbox/circuits/migrations/0041_standardize_description_comments.py
  7. 2 9
      netbox/circuits/models/circuits.py
  8. 3 14
      netbox/circuits/models/providers.py
  9. 2 2
      netbox/circuits/tables/providers.py
  10. 20 21
      netbox/dcim/api/serializers.py
  11. 102 29
      netbox/dcim/forms/bulk_edit.py
  12. 9 8
      netbox/dcim/forms/bulk_import.py
  13. 22 18
      netbox/dcim/forms/model_forms.py
  14. 2 2
      netbox/dcim/forms/object_import.py
  15. 78 0
      netbox/dcim/migrations/0165_standardize_description_comments.py
  16. 3 3
      netbox/dcim/models/cables.py
  17. 6 18
      netbox/dcim/models/devices.py
  18. 3 7
      netbox/dcim/models/power.py
  19. 3 6
      netbox/dcim/models/racks.py
  20. 2 9
      netbox/dcim/models/sites.py
  21. 2 1
      netbox/dcim/tables/cables.py
  22. 33 45
      netbox/dcim/tables/devices.py
  23. 14 30
      netbox/dcim/tables/devicetypes.py
  24. 3 3
      netbox/dcim/tables/modules.py
  25. 4 2
      netbox/dcim/tables/power.py
  26. 5 4
      netbox/dcim/tables/racks.py
  27. 21 18
      netbox/ipam/api/serializers.py
  28. 67 23
      netbox/ipam/forms/bulk_edit.py
  29. 12 12
      netbox/ipam/forms/bulk_import.py
  30. 33 17
      netbox/ipam/forms/model_forms.py
  31. 73 0
      netbox/ipam/migrations/0063_standardize_description_comments.py
  32. 2 6
      netbox/ipam/models/fhrp.py
  33. 6 26
      netbox/ipam/models/ip.py
  34. 2 6
      netbox/ipam/models/l2vpn.py
  35. 3 7
      netbox/ipam/models/services.py
  36. 2 12
      netbox/ipam/models/vlans.py
  37. 3 11
      netbox/ipam/models/vrfs.py
  38. 2 2
      netbox/ipam/tables/fhrp.py
  39. 16 10
      netbox/ipam/tables/ip.py
  40. 6 2
      netbox/ipam/tables/l2vpn.py
  41. 7 3
      netbox/ipam/tables/services.py
  42. 2 1
      netbox/ipam/tables/vlans.py
  43. 7 3
      netbox/ipam/tables/vrfs.py
  44. 19 2
      netbox/netbox/models/__init__.py
  45. 4 0
      netbox/templates/circuits/provider.html
  46. 5 0
      netbox/templates/dcim/cable.html
  47. 15 8
      netbox/templates/dcim/cable_edit.html
  48. 5 1
      netbox/templates/dcim/device.html
  49. 1 0
      netbox/templates/dcim/device_edit.html
  50. 4 0
      netbox/templates/dcim/devicetype.html
  51. 4 0
      netbox/templates/dcim/module.html
  52. 4 0
      netbox/templates/dcim/moduletype.html
  53. 4 0
      netbox/templates/dcim/powerfeed.html
  54. 22 19
      netbox/templates/dcim/powerpanel.html
  55. 4 0
      netbox/templates/dcim/rack.html
  56. 1 0
      netbox/templates/dcim/rack_edit.html
  57. 1 0
      netbox/templates/dcim/rackreservation.html
  58. 6 1
      netbox/templates/dcim/virtualchassis.html
  59. 7 1
      netbox/templates/dcim/virtualchassis_edit.html
  60. 1 0
      netbox/templates/ipam/aggregate.html
  61. 1 0
      netbox/templates/ipam/asn.html
  62. 1 0
      netbox/templates/ipam/fhrpgroup.html
  63. 9 2
      netbox/templates/ipam/fhrpgroup_edit.html
  64. 1 0
      netbox/templates/ipam/ipaddress.html
  65. 7 0
      netbox/templates/ipam/ipaddress_edit.html
  66. 4 3
      netbox/templates/ipam/iprange.html
  67. 1 0
      netbox/templates/ipam/l2vpn.html
  68. 1 0
      netbox/templates/ipam/prefix.html
  69. 1 0
      netbox/templates/ipam/routetarget.html
  70. 4 3
      netbox/templates/ipam/service.html
  71. 7 0
      netbox/templates/ipam/service_create.html
  72. 7 0
      netbox/templates/ipam/service_edit.html
  73. 7 6
      netbox/templates/ipam/servicetemplate.html
  74. 4 3
      netbox/templates/ipam/vlan.html
  75. 7 0
      netbox/templates/ipam/vlan_edit.html
  76. 1 0
      netbox/templates/ipam/vrf.html
  77. 4 0
      netbox/templates/tenancy/contact.html
  78. 4 0
      netbox/templates/virtualization/cluster.html
  79. 4 0
      netbox/templates/virtualization/virtualmachine.html
  80. 1 0
      netbox/templates/wireless/wirelesslan.html
  81. 1 0
      netbox/templates/wireless/wirelesslink.html
  82. 6 0
      netbox/templates/wireless/wirelesslink_edit.html
  83. 2 2
      netbox/tenancy/api/serializers.py
  84. 11 3
      netbox/tenancy/forms/bulk_edit.py
  85. 1 1
      netbox/tenancy/forms/bulk_import.py
  86. 2 2
      netbox/tenancy/forms/model_forms.py
  87. 18 0
      netbox/tenancy/migrations/0009_standardize_description_comments.py
  88. 2 6
      netbox/tenancy/models/contacts.py
  89. 2 10
      netbox/tenancy/models/tenants.py
  90. 2 2
      netbox/tenancy/tables/contacts.py
  91. 4 4
      netbox/virtualization/api/serializers.py
  92. 13 5
      netbox/virtualization/forms/bulk_edit.py
  93. 2 2
      netbox/virtualization/forms/bulk_import.py
  94. 6 4
      netbox/virtualization/forms/model_forms.py
  95. 23 0
      netbox/virtualization/migrations/0034_standardize_description_comments.py
  96. 3 9
      netbox/virtualization/models.py
  97. 2 2
      netbox/virtualization/tables/clusters.py
  98. 2 2
      netbox/virtualization/tables/virtualmachines.py
  99. 2 2
      netbox/wireless/api/serializers.py
  100. 19 9
      netbox/wireless/forms/bulk_edit.py

+ 51 - 0
docs/release-notes/version-3.4.md

@@ -69,18 +69,69 @@ A new `PluginMenu` class has been introduced, which enables a plugin to inject a
 
 
 * circuits.provider
 * circuits.provider
     * Removed the `asn`, `noc_contact`, `admin_contact`, and `portal_url` fields
     * Removed the `asn`, `noc_contact`, `admin_contact`, and `portal_url` fields
+    * Added a `description` field
+* dcim.Cable
+    * Added `description` and `comments` fields
+* dcim.Device
+    * Added a `description` field
 * dcim.DeviceType
 * dcim.DeviceType
+    * Added a `description` field
     * Added optional `weight` and `weight_unit` fields
     * Added optional `weight` and `weight_unit` fields
+* dcim.Module
+    * Added a `description` field
 * dcim.ModuleType
 * dcim.ModuleType
+    * Added a `description` field
     * Added optional `weight` and `weight_unit` fields
     * Added optional `weight` and `weight_unit` fields
+* dcim.PowerFeed
+    * Added a `description` field
+* dcim.PowerPanel
+    * Added `description` and `comments` fields
 * dcim.Rack
 * dcim.Rack
+    * Added a `description` field
     * Added optional `weight` and `weight_unit` fields
     * Added optional `weight` and `weight_unit` fields
+* dcim.RackReservation
+    * Added a `comments` field
+* dcim.VirtualChassis
+    * Added `description` and `comments` fields
 * extras.CustomLink
 * extras.CustomLink
     * Renamed `content_type` field to `content_types`
     * Renamed `content_type` field to `content_types`
 * extras.ExportTemplate
 * extras.ExportTemplate
     * Renamed `content_type` field to `content_types`
     * Renamed `content_type` field to `content_types`
+* ipam.Aggregate
+    * Added a `comments` field
+* ipam.ASN
+    * Added a `comments` field
 * ipam.FHRPGroup
 * ipam.FHRPGroup
+    * Added a `comments` field
     * Added optional `name` field
     * Added optional `name` field
+* ipam.IPAddress
+    * Added a `comments` field
+* ipam.IPRange
+    * Added a `comments` field
+* ipam.L2VPN
+    * Added a `comments` field
+* ipam.Prefix
+    * Added a `comments` field
+* ipam.RouteTarget
+    * Added a `comments` field
+* ipam.Service
+    * Added a `comments` field
+* ipam.ServiceTemplate
+    * Added a `comments` field
+* ipam.VLAN
+    * Added a `comments` field
+* ipam.VRF
+    * Added a `comments` field
+* tenancy.Contact
+    * Added a `description` field
+* virtualization.Cluster
+    * Added a `description` field
+* virtualization.VirtualMachine
+    * Added a `description` field
+* wireless.WirelessLAN
+    * Added a `comments` field
+* wireless.WirelessLink
+    * Added a `comments` field
 
 
 ### GraphQL API Changes
 ### GraphQL API Changes
 
 

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

@@ -31,8 +31,8 @@ class ProviderSerializer(NetBoxModelSerializer):
     class Meta:
     class Meta:
         model = Provider
         model = Provider
         fields = [
         fields = [
-            'id', 'url', 'display', 'name', 'slug', 'account',
-            'comments', 'asns', 'tags', 'custom_fields', 'created', 'last_updated', 'circuit_count',
+            'id', 'url', 'display', 'name', 'slug', 'account', 'description', 'comments', 'asns', 'tags',
+            'custom_fields', 'created', 'last_updated', 'circuit_count',
         ]
         ]
 
 
 
 

+ 5 - 1
netbox/circuits/forms/bulk_edit.py

@@ -30,6 +30,10 @@ class ProviderBulkEditForm(NetBoxModelBulkEditForm):
         required=False,
         required=False,
         label='Account number'
         label='Account number'
     )
     )
+    description = forms.CharField(
+        max_length=200,
+        required=False
+    )
     comments = CommentField(
     comments = CommentField(
         widget=SmallTextarea,
         widget=SmallTextarea,
         label='Comments'
         label='Comments'
@@ -40,7 +44,7 @@ class ProviderBulkEditForm(NetBoxModelBulkEditForm):
         (None, ('asns', 'account', )),
         (None, ('asns', 'account', )),
     )
     )
     nullable_fields = (
     nullable_fields = (
-        'asns', 'account', 'comments',
+        'asns', 'account', 'description', 'comments',
     )
     )
 
 
 
 

+ 1 - 1
netbox/circuits/forms/bulk_import.py

@@ -18,7 +18,7 @@ class ProviderCSVForm(NetBoxModelCSVForm):
     class Meta:
     class Meta:
         model = Provider
         model = Provider
         fields = (
         fields = (
-            'name', 'slug', 'account', 'comments',
+            'name', 'slug', 'account', 'description', 'comments',
         )
         )
 
 
 
 

+ 4 - 5
netbox/circuits/forms/model_forms.py

@@ -1,4 +1,3 @@
-from django import forms
 from django.utils.translation import gettext as _
 from django.utils.translation import gettext as _
 
 
 from circuits.models import *
 from circuits.models import *
@@ -7,8 +6,8 @@ from ipam.models import ASN
 from netbox.forms import NetBoxModelForm
 from netbox.forms import NetBoxModelForm
 from tenancy.forms import TenancyForm
 from tenancy.forms import TenancyForm
 from utilities.forms import (
 from utilities.forms import (
-    BootstrapMixin, CommentField, DatePicker, DynamicModelChoiceField, DynamicModelMultipleChoiceField,
-    SelectSpeedWidget, SmallTextarea, SlugField, StaticSelect,
+    CommentField, DatePicker, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SelectSpeedWidget, SlugField,
+    StaticSelect,
 )
 )
 
 
 __all__ = (
 __all__ = (
@@ -30,14 +29,14 @@ class ProviderForm(NetBoxModelForm):
     comments = CommentField()
     comments = CommentField()
 
 
     fieldsets = (
     fieldsets = (
-        ('Provider', ('name', 'slug', 'asns', 'tags')),
+        ('Provider', ('name', 'slug', 'asns', 'description', 'tags')),
         ('Support Info', ('account',)),
         ('Support Info', ('account',)),
     )
     )
 
 
     class Meta:
     class Meta:
         model = Provider
         model = Provider
         fields = [
         fields = [
-            'name', 'slug', 'account', 'asns', 'comments', 'tags',
+            'name', 'slug', 'account', 'asns', 'description', 'comments', 'tags',
         ]
         ]
         help_texts = {
         help_texts = {
             'name': "Full name of the provider",
             'name': "Full name of the provider",

+ 18 - 0
netbox/circuits/migrations/0041_standardize_description_comments.py

@@ -0,0 +1,18 @@
+# Generated by Django 4.1.2 on 2022-11-03 18:24
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('circuits', '0040_provider_remove_deprecated_fields'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='provider',
+            name='description',
+            field=models.CharField(blank=True, max_length=200),
+        ),
+    ]

+ 2 - 9
netbox/circuits/models/circuits.py

@@ -7,7 +7,7 @@ from django.urls import reverse
 from circuits.choices import *
 from circuits.choices import *
 from dcim.models import CabledObjectModel
 from dcim.models import CabledObjectModel
 from netbox.models import (
 from netbox.models import (
-    ChangeLoggedModel, CustomFieldsMixin, CustomLinksMixin, OrganizationalModel, NetBoxModel, TagsMixin,
+    ChangeLoggedModel, CustomFieldsMixin, CustomLinksMixin, OrganizationalModel, PrimaryModel, TagsMixin,
 )
 )
 from netbox.models.features import WebhooksMixin
 from netbox.models.features import WebhooksMixin
 
 
@@ -27,7 +27,7 @@ class CircuitType(OrganizationalModel):
         return reverse('circuits:circuittype', args=[self.pk])
         return reverse('circuits:circuittype', args=[self.pk])
 
 
 
 
-class Circuit(NetBoxModel):
+class Circuit(PrimaryModel):
     """
     """
     A communications circuit connects two points. Each Circuit belongs to a Provider; Providers may have multiple
     A communications circuit connects two points. Each Circuit belongs to a Provider; Providers may have multiple
     circuits. Each circuit is also assigned a CircuitType and a Site.  Circuit port speed and commit rate are measured
     circuits. Each circuit is also assigned a CircuitType and a Site.  Circuit port speed and commit rate are measured
@@ -73,13 +73,6 @@ class Circuit(NetBoxModel):
         blank=True,
         blank=True,
         null=True,
         null=True,
         verbose_name='Commit rate (Kbps)')
         verbose_name='Commit rate (Kbps)')
-    description = models.CharField(
-        max_length=200,
-        blank=True
-    )
-    comments = models.TextField(
-        blank=True
-    )
 
 
     # Generic relations
     # Generic relations
     contacts = GenericRelation(
     contacts = GenericRelation(

+ 3 - 14
netbox/circuits/models/providers.py

@@ -2,8 +2,7 @@ from django.contrib.contenttypes.fields import GenericRelation
 from django.db import models
 from django.db import models
 from django.urls import reverse
 from django.urls import reverse
 
 
-from dcim.fields import ASNField
-from netbox.models import NetBoxModel
+from netbox.models import PrimaryModel
 
 
 __all__ = (
 __all__ = (
     'ProviderNetwork',
     'ProviderNetwork',
@@ -11,7 +10,7 @@ __all__ = (
 )
 )
 
 
 
 
-class Provider(NetBoxModel):
+class Provider(PrimaryModel):
     """
     """
     Each Circuit belongs to a Provider. This is usually a telecommunications company or similar organization. This model
     Each Circuit belongs to a Provider. This is usually a telecommunications company or similar organization. This model
     stores information pertinent to the user's relationship with the Provider.
     stores information pertinent to the user's relationship with the Provider.
@@ -34,9 +33,6 @@ class Provider(NetBoxModel):
         blank=True,
         blank=True,
         verbose_name='Account number'
         verbose_name='Account number'
     )
     )
-    comments = models.TextField(
-        blank=True
-    )
 
 
     # Generic relations
     # Generic relations
     contacts = GenericRelation(
     contacts = GenericRelation(
@@ -57,7 +53,7 @@ class Provider(NetBoxModel):
         return reverse('circuits:provider', args=[self.pk])
         return reverse('circuits:provider', args=[self.pk])
 
 
 
 
-class ProviderNetwork(NetBoxModel):
+class ProviderNetwork(PrimaryModel):
     """
     """
     This represents a provider network which exists outside of NetBox, the details of which are unknown or
     This represents a provider network which exists outside of NetBox, the details of which are unknown or
     unimportant to the user.
     unimportant to the user.
@@ -75,13 +71,6 @@ class ProviderNetwork(NetBoxModel):
         blank=True,
         blank=True,
         verbose_name='Service ID'
         verbose_name='Service ID'
     )
     )
-    description = models.CharField(
-        max_length=200,
-        blank=True
-    )
-    comments = models.TextField(
-        blank=True
-    )
 
 
     class Meta:
     class Meta:
         ordering = ('provider', 'name')
         ordering = ('provider', 'name')

+ 2 - 2
netbox/circuits/tables/providers.py

@@ -39,8 +39,8 @@ class ProviderTable(ContactsColumnMixin, NetBoxTable):
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
         model = Provider
         model = Provider
         fields = (
         fields = (
-            'pk', 'id', 'name', 'asns', 'account', 'asn_count',
-            'circuit_count', 'comments', 'contacts', 'tags', 'created', 'last_updated',
+            'pk', 'id', 'name', 'asns', 'account', 'asn_count', 'circuit_count', 'description', 'comments', 'contacts',
+            'tags', 'created', 'last_updated',
         )
         )
         default_columns = ('pk', 'name', 'account', 'circuit_count')
         default_columns = ('pk', 'name', 'account', 'circuit_count')
 
 

+ 20 - 21
netbox/dcim/api/serializers.py

@@ -210,8 +210,8 @@ class RackSerializer(NetBoxModelSerializer):
         fields = [
         fields = [
             'id', 'url', 'display', 'name', 'facility_id', 'site', 'location', 'tenant', 'status', 'role', 'serial',
             'id', 'url', 'display', 'name', 'facility_id', 'site', 'location', 'tenant', 'status', 'role', 'serial',
             'asset_tag', 'type', 'width', 'u_height', 'weight', 'weight_unit', 'desc_units', 'outer_width',
             'asset_tag', 'type', 'width', 'u_height', 'weight', 'weight_unit', 'desc_units', 'outer_width',
-            'outer_depth', 'outer_unit', 'mounting_depth', 'comments', 'tags', 'custom_fields', 'created',
-            'last_updated', 'device_count', 'powerfeed_count',
+            'outer_depth', 'outer_unit', 'mounting_depth', 'description', 'comments', 'tags', 'custom_fields',
+            'created', 'last_updated', 'device_count', 'powerfeed_count',
         ]
         ]
 
 
 
 
@@ -243,8 +243,8 @@ class RackReservationSerializer(NetBoxModelSerializer):
     class Meta:
     class Meta:
         model = RackReservation
         model = RackReservation
         fields = [
         fields = [
-            'id', 'url', 'display', 'rack', 'units', 'created', 'last_updated', 'user', 'tenant', 'description', 'tags',
-            'custom_fields',
+            'id', 'url', 'display', 'rack', 'units', 'created', 'last_updated', 'user', 'tenant', 'description',
+            'comments', 'tags', 'custom_fields',
         ]
         ]
 
 
 
 
@@ -324,8 +324,8 @@ class DeviceTypeSerializer(NetBoxModelSerializer):
         model = DeviceType
         model = DeviceType
         fields = [
         fields = [
             'id', 'url', 'display', 'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth',
             'id', 'url', 'display', 'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth',
-            'subdevice_role', 'airflow', 'weight', 'weight_unit', 'front_image', 'rear_image', 'comments', 'tags',
-            'custom_fields', 'created', 'last_updated', 'device_count',
+            'subdevice_role', 'airflow', 'weight', 'weight_unit', 'front_image', 'rear_image', 'description',
+            'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count',
         ]
         ]
 
 
 
 
@@ -333,13 +333,12 @@ class ModuleTypeSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:moduletype-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:moduletype-detail')
     manufacturer = NestedManufacturerSerializer()
     manufacturer = NestedManufacturerSerializer()
     weight_unit = ChoiceField(choices=WeightUnitChoices, allow_blank=True, required=False)
     weight_unit = ChoiceField(choices=WeightUnitChoices, allow_blank=True, required=False)
-    # module_count = serializers.IntegerField(read_only=True)
 
 
     class Meta:
     class Meta:
         model = ModuleType
         model = ModuleType
         fields = [
         fields = [
-            'id', 'url', 'display', 'manufacturer', 'model', 'part_number', 'weight', 'weight_unit', 'comments', 'tags',
-            'custom_fields', 'created', 'last_updated',
+            'id', 'url', 'display', 'manufacturer', 'model', 'part_number', 'weight', 'weight_unit', 'description',
+            'comments', 'tags', 'custom_fields', 'created', 'last_updated',
         ]
         ]
 
 
 
 
@@ -656,8 +655,8 @@ class DeviceSerializer(NetBoxModelSerializer):
         fields = [
         fields = [
             'id', 'url', 'display', 'name', 'device_type', 'device_role', 'tenant', 'platform', 'serial', 'asset_tag',
             'id', 'url', 'display', 'name', 'device_type', 'device_role', 'tenant', 'platform', 'serial', 'asset_tag',
             'site', 'location', 'rack', 'position', 'face', 'parent_device', 'status', 'airflow', 'primary_ip',
             'site', 'location', 'rack', 'position', 'face', 'parent_device', 'status', 'airflow', 'primary_ip',
-            'primary_ip4', 'primary_ip6', 'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments',
-            'local_context_data', 'tags', 'custom_fields', 'created', 'last_updated',
+            'primary_ip4', 'primary_ip6', 'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'description',
+            'comments', 'local_context_data', 'tags', 'custom_fields', 'created', 'last_updated',
         ]
         ]
 
 
     @swagger_serializer_method(serializer_or_field=NestedDeviceSerializer)
     @swagger_serializer_method(serializer_or_field=NestedDeviceSerializer)
@@ -681,8 +680,8 @@ class ModuleSerializer(NetBoxModelSerializer):
     class Meta:
     class Meta:
         model = Module
         model = Module
         fields = [
         fields = [
-            'id', 'url', 'display', 'device', 'module_bay', 'module_type', 'serial', 'asset_tag', 'comments', 'tags',
-            'custom_fields', 'created', 'last_updated',
+            'id', 'url', 'display', 'device', 'module_bay', 'module_type', 'serial', 'asset_tag', 'description',
+            'comments', 'tags', 'custom_fields', 'created', 'last_updated',
         ]
         ]
 
 
 
 
@@ -1020,7 +1019,7 @@ class CableSerializer(NetBoxModelSerializer):
         model = Cable
         model = Cable
         fields = [
         fields = [
             'id', 'url', 'display', 'type', 'a_terminations', 'b_terminations', 'status', 'tenant', 'label', 'color',
             'id', 'url', 'display', 'type', 'a_terminations', 'b_terminations', 'status', 'tenant', 'label', 'color',
-            'length', 'length_unit', 'tags', 'custom_fields', 'created', 'last_updated',
+            'length', 'length_unit', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
         ]
         ]
 
 
 
 
@@ -1086,8 +1085,8 @@ class VirtualChassisSerializer(NetBoxModelSerializer):
     class Meta:
     class Meta:
         model = VirtualChassis
         model = VirtualChassis
         fields = [
         fields = [
-            'id', 'url', 'display', 'name', 'domain', 'master', 'tags', 'custom_fields', 'member_count',
-            'created', 'last_updated',
+            'id', 'url', 'display', 'name', 'domain', 'master', 'description', 'comments', 'tags', 'custom_fields',
+            'member_count', 'created', 'last_updated',
         ]
         ]
 
 
 
 
@@ -1108,8 +1107,8 @@ class PowerPanelSerializer(NetBoxModelSerializer):
     class Meta:
     class Meta:
         model = PowerPanel
         model = PowerPanel
         fields = [
         fields = [
-            'id', 'url', 'display', 'site', 'location', 'name', 'tags', 'custom_fields', 'powerfeed_count',
-            'created', 'last_updated',
+            'id', 'url', 'display', 'site', 'location', 'name', 'description', 'comments', 'tags', 'custom_fields',
+            'powerfeed_count', 'created', 'last_updated',
         ]
         ]
 
 
 
 
@@ -1142,7 +1141,7 @@ class PowerFeedSerializer(NetBoxModelSerializer, CabledObjectSerializer, Connect
         model = PowerFeed
         model = PowerFeed
         fields = [
         fields = [
             'id', 'url', 'display', 'power_panel', 'rack', 'name', 'status', 'type', 'supply', 'phase', 'voltage',
             'id', 'url', 'display', 'power_panel', 'rack', 'name', 'status', 'type', 'supply', 'phase', 'voltage',
-            'amperage', 'max_utilization', 'comments', 'mark_connected', 'cable', 'cable_end', 'link_peers',
-            'link_peers_type', 'connected_endpoints', 'connected_endpoints_type', 'connected_endpoints_reachable',
-            'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
+            'amperage', 'max_utilization', 'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type',
+            'connected_endpoints', 'connected_endpoints_type', 'connected_endpoints_reachable', 'description',
+            'comments', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
         ]
         ]

+ 102 - 29
netbox/dcim/forms/bulk_edit.py

@@ -127,22 +127,26 @@ class SiteBulkEditForm(NetBoxModelBulkEditForm):
         required=False,
         required=False,
         label='Contact E-mail'
         label='Contact E-mail'
     )
     )
-    description = forms.CharField(
-        max_length=100,
-        required=False
-    )
     time_zone = TimeZoneFormField(
     time_zone = TimeZoneFormField(
         choices=add_blank_choice(TimeZoneFormField().choices),
         choices=add_blank_choice(TimeZoneFormField().choices),
         required=False,
         required=False,
         widget=StaticSelect()
         widget=StaticSelect()
     )
     )
+    description = forms.CharField(
+        max_length=200,
+        required=False
+    )
+    comments = CommentField(
+        widget=SmallTextarea,
+        label='Comments'
+    )
 
 
     model = Site
     model = Site
     fieldsets = (
     fieldsets = (
         (None, ('status', 'region', 'group', 'tenant', 'asns', 'time_zone', 'description')),
         (None, ('status', 'region', 'group', 'tenant', 'asns', 'time_zone', 'description')),
     )
     )
     nullable_fields = (
     nullable_fields = (
-        'region', 'group', 'tenant', 'asns', 'description', 'time_zone',
+        'region', 'group', 'tenant', 'asns', 'time_zone', 'description', 'comments',
     )
     )
 
 
 
 
@@ -285,10 +289,6 @@ class RackBulkEditForm(NetBoxModelBulkEditForm):
         required=False,
         required=False,
         min_value=1
         min_value=1
     )
     )
-    comments = CommentField(
-        widget=SmallTextarea,
-        label='Comments'
-    )
     weight = forms.DecimalField(
     weight = forms.DecimalField(
         min_value=0,
         min_value=0,
         required=False
         required=False
@@ -299,10 +299,18 @@ class RackBulkEditForm(NetBoxModelBulkEditForm):
         initial='',
         initial='',
         widget=StaticSelect()
         widget=StaticSelect()
     )
     )
+    description = forms.CharField(
+        max_length=200,
+        required=False
+    )
+    comments = CommentField(
+        widget=SmallTextarea,
+        label='Comments'
+    )
 
 
     model = Rack
     model = Rack
     fieldsets = (
     fieldsets = (
-        ('Rack', ('status', 'role', 'tenant', 'serial', 'asset_tag')),
+        ('Rack', ('status', 'role', 'tenant', 'serial', 'asset_tag', 'description')),
         ('Location', ('region', 'site_group', 'site', 'location')),
         ('Location', ('region', 'site_group', 'site', 'location')),
         ('Hardware', (
         ('Hardware', (
             'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit', 'mounting_depth',
             'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit', 'mounting_depth',
@@ -310,8 +318,8 @@ class RackBulkEditForm(NetBoxModelBulkEditForm):
         ('Weight', ('weight', 'weight_unit')),
         ('Weight', ('weight', 'weight_unit')),
     )
     )
     nullable_fields = (
     nullable_fields = (
-        'location', 'tenant', 'role', 'serial', 'asset_tag', 'outer_width', 'outer_depth', 'outer_unit', 'comments',
-        'weight', 'weight_unit'
+        'location', 'tenant', 'role', 'serial', 'asset_tag', 'outer_width', 'outer_depth', 'outer_unit', 'weight',
+        'weight_unit', 'description', 'comments',
     )
     )
 
 
 
 
@@ -328,14 +336,19 @@ class RackReservationBulkEditForm(NetBoxModelBulkEditForm):
         required=False
         required=False
     )
     )
     description = forms.CharField(
     description = forms.CharField(
-        max_length=100,
+        max_length=200,
         required=False
         required=False
     )
     )
+    comments = CommentField(
+        widget=SmallTextarea,
+        label='Comments'
+    )
 
 
     model = RackReservation
     model = RackReservation
     fieldsets = (
     fieldsets = (
         (None, ('user', 'tenant', 'description')),
         (None, ('user', 'tenant', 'description')),
     )
     )
+    nullable_fields = ('comments',)
 
 
 
 
 class ManufacturerBulkEditForm(NetBoxModelBulkEditForm):
 class ManufacturerBulkEditForm(NetBoxModelBulkEditForm):
@@ -383,13 +396,21 @@ class DeviceTypeBulkEditForm(NetBoxModelBulkEditForm):
         initial='',
         initial='',
         widget=StaticSelect()
         widget=StaticSelect()
     )
     )
+    description = forms.CharField(
+        max_length=200,
+        required=False
+    )
+    comments = CommentField(
+        widget=SmallTextarea,
+        label='Comments'
+    )
 
 
     model = DeviceType
     model = DeviceType
     fieldsets = (
     fieldsets = (
-        ('Device Type', ('manufacturer', 'part_number', 'u_height', 'is_full_depth', 'airflow')),
+        ('Device Type', ('manufacturer', 'part_number', 'u_height', 'is_full_depth', 'airflow', 'description')),
         ('Weight', ('weight', 'weight_unit')),
         ('Weight', ('weight', 'weight_unit')),
     )
     )
-    nullable_fields = ('part_number', 'airflow', 'weight', 'weight_unit')
+    nullable_fields = ('part_number', 'airflow', 'weight', 'weight_unit', 'description', 'comments')
 
 
 
 
 class ModuleTypeBulkEditForm(NetBoxModelBulkEditForm):
 class ModuleTypeBulkEditForm(NetBoxModelBulkEditForm):
@@ -410,13 +431,21 @@ class ModuleTypeBulkEditForm(NetBoxModelBulkEditForm):
         initial='',
         initial='',
         widget=StaticSelect()
         widget=StaticSelect()
     )
     )
+    description = forms.CharField(
+        max_length=200,
+        required=False
+    )
+    comments = CommentField(
+        widget=SmallTextarea,
+        label='Comments'
+    )
 
 
     model = ModuleType
     model = ModuleType
     fieldsets = (
     fieldsets = (
-        ('Module Type', ('manufacturer', 'part_number')),
+        ('Module Type', ('manufacturer', 'part_number', 'description')),
         ('Weight', ('weight', 'weight_unit')),
         ('Weight', ('weight', 'weight_unit')),
     )
     )
-    nullable_fields = ('part_number', 'weight', 'weight_unit')
+    nullable_fields = ('part_number', 'weight', 'weight_unit', 'description', 'comments')
 
 
 
 
 class DeviceRoleBulkEditForm(NetBoxModelBulkEditForm):
 class DeviceRoleBulkEditForm(NetBoxModelBulkEditForm):
@@ -512,15 +541,23 @@ class DeviceBulkEditForm(NetBoxModelBulkEditForm):
         required=False,
         required=False,
         label='Serial Number'
         label='Serial Number'
     )
     )
+    description = forms.CharField(
+        max_length=200,
+        required=False
+    )
+    comments = CommentField(
+        widget=SmallTextarea,
+        label='Comments'
+    )
 
 
     model = Device
     model = Device
     fieldsets = (
     fieldsets = (
-        ('Device', ('device_role', 'status', 'tenant', 'platform')),
+        ('Device', ('device_role', 'status', 'tenant', 'platform', 'description')),
         ('Location', ('site', 'location')),
         ('Location', ('site', 'location')),
         ('Hardware', ('manufacturer', 'device_type', 'airflow', 'serial')),
         ('Hardware', ('manufacturer', 'device_type', 'airflow', 'serial')),
     )
     )
     nullable_fields = (
     nullable_fields = (
-        'location', 'tenant', 'platform', 'serial', 'airflow',
+        'location', 'tenant', 'platform', 'serial', 'airflow', 'description', 'comments',
     )
     )
 
 
 
 
@@ -541,12 +578,20 @@ class ModuleBulkEditForm(NetBoxModelBulkEditForm):
         required=False,
         required=False,
         label='Serial Number'
         label='Serial Number'
     )
     )
+    description = forms.CharField(
+        max_length=200,
+        required=False
+    )
+    comments = CommentField(
+        widget=SmallTextarea,
+        label='Comments'
+    )
 
 
     model = Module
     model = Module
     fieldsets = (
     fieldsets = (
-        (None, ('manufacturer', 'module_type', 'serial')),
+        (None, ('manufacturer', 'module_type', 'serial', 'description')),
     )
     )
-    nullable_fields = ('serial',)
+    nullable_fields = ('serial', 'description', 'comments')
 
 
 
 
 class CableBulkEditForm(NetBoxModelBulkEditForm):
 class CableBulkEditForm(NetBoxModelBulkEditForm):
@@ -583,14 +628,22 @@ class CableBulkEditForm(NetBoxModelBulkEditForm):
         initial='',
         initial='',
         widget=StaticSelect()
         widget=StaticSelect()
     )
     )
+    description = forms.CharField(
+        max_length=200,
+        required=False
+    )
+    comments = CommentField(
+        widget=SmallTextarea,
+        label='Comments'
+    )
 
 
     model = Cable
     model = Cable
     fieldsets = (
     fieldsets = (
-        (None, ('type', 'status', 'tenant', 'label')),
+        (None, ('type', 'status', 'tenant', 'label', 'description')),
         ('Attributes', ('color', 'length', 'length_unit')),
         ('Attributes', ('color', 'length', 'length_unit')),
     )
     )
     nullable_fields = (
     nullable_fields = (
-        'type', 'status', 'tenant', 'label', 'color', 'length',
+        'type', 'status', 'tenant', 'label', 'color', 'length', 'description', 'comments',
     )
     )
 
 
 
 
@@ -599,12 +652,20 @@ class VirtualChassisBulkEditForm(NetBoxModelBulkEditForm):
         max_length=30,
         max_length=30,
         required=False
         required=False
     )
     )
+    description = forms.CharField(
+        max_length=200,
+        required=False
+    )
+    comments = CommentField(
+        widget=SmallTextarea,
+        label='Comments'
+    )
 
 
     model = VirtualChassis
     model = VirtualChassis
     fieldsets = (
     fieldsets = (
-        (None, ('domain',)),
+        (None, ('domain', 'description')),
     )
     )
-    nullable_fields = ('domain',)
+    nullable_fields = ('domain', 'description', 'comments')
 
 
 
 
 class PowerPanelBulkEditForm(NetBoxModelBulkEditForm):
 class PowerPanelBulkEditForm(NetBoxModelBulkEditForm):
@@ -637,12 +698,20 @@ class PowerPanelBulkEditForm(NetBoxModelBulkEditForm):
             'site_id': '$site'
             'site_id': '$site'
         }
         }
     )
     )
+    description = forms.CharField(
+        max_length=200,
+        required=False
+    )
+    comments = CommentField(
+        widget=SmallTextarea,
+        label='Comments'
+    )
 
 
     model = PowerPanel
     model = PowerPanel
     fieldsets = (
     fieldsets = (
-        (None, ('region', 'site_group', 'site', 'location')),
+        (None, ('region', 'site_group', 'site', 'location', 'description')),
     )
     )
-    nullable_fields = ('location',)
+    nullable_fields = ('location', 'description', 'comments')
 
 
 
 
 class PowerFeedBulkEditForm(NetBoxModelBulkEditForm):
 class PowerFeedBulkEditForm(NetBoxModelBulkEditForm):
@@ -691,6 +760,10 @@ class PowerFeedBulkEditForm(NetBoxModelBulkEditForm):
         required=False,
         required=False,
         widget=BulkEditNullBooleanSelect
         widget=BulkEditNullBooleanSelect
     )
     )
+    description = forms.CharField(
+        max_length=200,
+        required=False
+    )
     comments = CommentField(
     comments = CommentField(
         widget=SmallTextarea,
         widget=SmallTextarea,
         label='Comments'
         label='Comments'
@@ -698,10 +771,10 @@ class PowerFeedBulkEditForm(NetBoxModelBulkEditForm):
 
 
     model = PowerFeed
     model = PowerFeed
     fieldsets = (
     fieldsets = (
-        (None, ('power_panel', 'rack', 'status', 'type', 'mark_connected')),
+        (None, ('power_panel', 'rack', 'status', 'type', 'mark_connected', 'description')),
         ('Power', ('supply', 'phase', 'voltage', 'amperage', 'max_utilization'))
         ('Power', ('supply', 'phase', 'voltage', 'amperage', 'max_utilization'))
     )
     )
-    nullable_fields = ('location', 'comments')
+    nullable_fields = ('location', 'description', 'comments')
 
 
 
 
 #
 #

+ 9 - 8
netbox/dcim/forms/bulk_import.py

@@ -196,7 +196,8 @@ class RackCSVForm(NetBoxModelCSVForm):
         model = Rack
         model = Rack
         fields = (
         fields = (
             'site', 'location', 'name', 'facility_id', 'tenant', 'status', 'role', 'type', 'serial', 'asset_tag',
             'site', 'location', 'name', 'facility_id', 'tenant', 'status', 'role', 'type', 'serial', 'asset_tag',
-            'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit', 'mounting_depth', 'comments',
+            'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit', 'mounting_depth',
+            'description', 'comments',
         )
         )
 
 
     def __init__(self, data=None, *args, **kwargs):
     def __init__(self, data=None, *args, **kwargs):
@@ -240,7 +241,7 @@ class RackReservationCSVForm(NetBoxModelCSVForm):
 
 
     class Meta:
     class Meta:
         model = RackReservation
         model = RackReservation
-        fields = ('site', 'location', 'rack', 'units', 'tenant', 'description')
+        fields = ('site', 'location', 'rack', 'units', 'tenant', 'description', 'comments')
 
 
     def __init__(self, data=None, *args, **kwargs):
     def __init__(self, data=None, *args, **kwargs):
         super().__init__(data, *args, **kwargs)
         super().__init__(data, *args, **kwargs)
@@ -387,7 +388,7 @@ class DeviceCSVForm(BaseDeviceCSVForm):
         fields = [
         fields = [
             'name', 'device_role', 'tenant', 'manufacturer', 'device_type', 'platform', 'serial', 'asset_tag', 'status',
             'name', 'device_role', 'tenant', 'manufacturer', 'device_type', 'platform', 'serial', 'asset_tag', 'status',
             'site', 'location', 'rack', 'position', 'face', 'airflow', 'virtual_chassis', 'vc_position', 'vc_priority',
             'site', 'location', 'rack', 'position', 'face', 'airflow', 'virtual_chassis', 'vc_position', 'vc_priority',
-            'cluster', 'comments',
+            'cluster', 'description', 'comments',
         ]
         ]
 
 
     def __init__(self, data=None, *args, **kwargs):
     def __init__(self, data=None, *args, **kwargs):
@@ -424,7 +425,7 @@ class ModuleCSVForm(NetBoxModelCSVForm):
     class Meta:
     class Meta:
         model = Module
         model = Module
         fields = (
         fields = (
-            'device', 'module_bay', 'module_type', 'serial', 'asset_tag', 'comments',
+            'device', 'module_bay', 'module_type', 'serial', 'asset_tag', 'description', 'comments',
         )
         )
 
 
     def __init__(self, data=None, *args, **kwargs):
     def __init__(self, data=None, *args, **kwargs):
@@ -927,7 +928,7 @@ class CableCSVForm(NetBoxModelCSVForm):
         model = Cable
         model = Cable
         fields = [
         fields = [
             'side_a_device', 'side_a_type', 'side_a_name', 'side_b_device', 'side_b_type', 'side_b_name', 'type',
             'side_a_device', 'side_a_type', 'side_a_name', 'side_b_device', 'side_b_type', 'side_b_name', 'type',
-            'status', 'tenant', 'label', 'color', 'length', 'length_unit',
+            'status', 'tenant', 'label', 'color', 'length', 'length_unit', 'description', 'comments',
         ]
         ]
         help_texts = {
         help_texts = {
             'color': mark_safe('RGB color in hexadecimal (e.g. <code>00ff00</code>)'),
             'color': mark_safe('RGB color in hexadecimal (e.g. <code>00ff00</code>)'),
@@ -984,7 +985,7 @@ class VirtualChassisCSVForm(NetBoxModelCSVForm):
 
 
     class Meta:
     class Meta:
         model = VirtualChassis
         model = VirtualChassis
-        fields = ('name', 'domain', 'master')
+        fields = ('name', 'domain', 'master', 'description')
 
 
 
 
 #
 #
@@ -1005,7 +1006,7 @@ class PowerPanelCSVForm(NetBoxModelCSVForm):
 
 
     class Meta:
     class Meta:
         model = PowerPanel
         model = PowerPanel
-        fields = ('site', 'location', 'name')
+        fields = ('site', 'location', 'name', 'description', 'comments')
 
 
     def __init__(self, data=None, *args, **kwargs):
     def __init__(self, data=None, *args, **kwargs):
         super().__init__(data, *args, **kwargs)
         super().__init__(data, *args, **kwargs)
@@ -1061,7 +1062,7 @@ class PowerFeedCSVForm(NetBoxModelCSVForm):
         model = PowerFeed
         model = PowerFeed
         fields = (
         fields = (
             'site', 'power_panel', 'location', 'rack', 'name', 'status', 'type', 'mark_connected', 'supply', 'phase',
             'site', 'power_panel', 'location', 'rack', 'name', 'status', 'type', 'mark_connected', 'supply', 'phase',
-            'voltage', 'amperage', 'max_utilization', 'comments',
+            'voltage', 'amperage', 'max_utilization', 'description', 'comments',
         )
         )
 
 
     def __init__(self, data=None, *args, **kwargs):
     def __init__(self, data=None, *args, **kwargs):

+ 22 - 18
netbox/dcim/forms/model_forms.py

@@ -278,7 +278,7 @@ class RackForm(TenancyForm, NetBoxModelForm):
         fields = [
         fields = [
             'region', 'site_group', 'site', 'location', 'name', 'facility_id', 'tenant_group', 'tenant', 'status',
             'region', 'site_group', 'site', 'location', 'name', 'facility_id', 'tenant_group', 'tenant', 'status',
             'role', 'serial', 'asset_tag', 'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth',
             'role', 'serial', 'asset_tag', 'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth',
-            'outer_unit', 'mounting_depth', 'weight', 'weight_unit', 'comments', 'tags',
+            'outer_unit', 'mounting_depth', 'weight', 'weight_unit', 'description', 'comments', 'tags',
         ]
         ]
         help_texts = {
         help_texts = {
             'site': "The site at which the rack exists",
             'site': "The site at which the rack exists",
@@ -342,6 +342,7 @@ class RackReservationForm(TenancyForm, NetBoxModelForm):
         ),
         ),
         widget=StaticSelect()
         widget=StaticSelect()
     )
     )
+    comments = CommentField()
 
 
     fieldsets = (
     fieldsets = (
         ('Reservation', ('region', 'site_group', 'site', 'location', 'rack', 'units', 'user', 'description', 'tags')),
         ('Reservation', ('region', 'site_group', 'site', 'location', 'rack', 'units', 'user', 'description', 'tags')),
@@ -352,7 +353,7 @@ class RackReservationForm(TenancyForm, NetBoxModelForm):
         model = RackReservation
         model = RackReservation
         fields = [
         fields = [
             'region', 'site_group', 'site', 'location', 'rack', 'units', 'user', 'tenant_group', 'tenant',
             'region', 'site_group', 'site', 'location', 'rack', 'units', 'user', 'tenant_group', 'tenant',
-            'description', 'tags',
+            'description', 'comments', 'tags',
         ]
         ]
 
 
 
 
@@ -383,10 +384,10 @@ class DeviceTypeForm(NetBoxModelForm):
 
 
     fieldsets = (
     fieldsets = (
         ('Device Type', (
         ('Device Type', (
-            'manufacturer', 'model', 'slug', 'part_number', 'tags',
+            'manufacturer', 'model', 'slug', 'description', 'tags',
         )),
         )),
         ('Chassis', (
         ('Chassis', (
-            'u_height', 'is_full_depth', 'subdevice_role', 'airflow',
+            'u_height', 'is_full_depth', 'part_number', 'subdevice_role', 'airflow',
         )),
         )),
         ('Attributes', ('weight', 'weight_unit')),
         ('Attributes', ('weight', 'weight_unit')),
         ('Images', ('front_image', 'rear_image')),
         ('Images', ('front_image', 'rear_image')),
@@ -396,7 +397,7 @@ class DeviceTypeForm(NetBoxModelForm):
         model = DeviceType
         model = DeviceType
         fields = [
         fields = [
             'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow',
             'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow',
-            'weight', 'weight_unit', 'front_image', 'rear_image', 'comments', 'tags',
+            'weight', 'weight_unit', 'front_image', 'rear_image', 'description', 'comments', 'tags',
         ]
         ]
         widgets = {
         widgets = {
             'airflow': StaticSelect(),
             'airflow': StaticSelect(),
@@ -418,15 +419,14 @@ class ModuleTypeForm(NetBoxModelForm):
     comments = CommentField()
     comments = CommentField()
 
 
     fieldsets = (
     fieldsets = (
-        ('Module Type', (
-            'manufacturer', 'model', 'part_number', 'tags', 'weight', 'weight_unit'
-        )),
+        ('Module Type', ('manufacturer', 'model', 'part_number', 'description', 'tags')),
+        ('Weight', ('weight', 'weight_unit'))
     )
     )
 
 
     class Meta:
     class Meta:
         model = ModuleType
         model = ModuleType
         fields = [
         fields = [
-            'manufacturer', 'model', 'part_number', 'weight', 'weight_unit', 'comments', 'tags',
+            'manufacturer', 'model', 'part_number', 'weight', 'weight_unit', 'description', 'comments', 'tags',
         ]
         ]
 
 
         widgets = {
         widgets = {
@@ -591,7 +591,7 @@ class DeviceForm(TenancyForm, NetBoxModelForm):
             'name', 'device_role', 'device_type', 'serial', 'asset_tag', 'region', 'site_group', 'site', 'rack',
             'name', 'device_role', 'device_type', 'serial', 'asset_tag', 'region', 'site_group', 'site', 'rack',
             'location', 'position', 'face', 'status', 'airflow', 'platform', 'primary_ip4', 'primary_ip6',
             'location', 'position', 'face', 'status', 'airflow', 'platform', 'primary_ip4', 'primary_ip6',
             'cluster_group', 'cluster', 'tenant_group', 'tenant', 'virtual_chassis', 'vc_position', 'vc_priority',
             'cluster_group', 'cluster', 'tenant_group', 'tenant', 'virtual_chassis', 'vc_position', 'vc_priority',
-            'comments', 'tags', 'local_context_data'
+            'description', 'comments', 'tags', 'local_context_data'
         ]
         ]
         help_texts = {
         help_texts = {
             'device_role': "The function this device serves",
             'device_role': "The function this device serves",
@@ -705,7 +705,7 @@ class ModuleForm(NetBoxModelForm):
 
 
     fieldsets = (
     fieldsets = (
         ('Module', (
         ('Module', (
-            'device', 'module_bay', 'manufacturer', 'module_type', 'tags',
+            'device', 'module_bay', 'manufacturer', 'module_type', 'description', 'tags',
         )),
         )),
         ('Hardware', (
         ('Hardware', (
             'serial', 'asset_tag', 'replicate_components', 'adopt_components',
             'serial', 'asset_tag', 'replicate_components', 'adopt_components',
@@ -716,7 +716,7 @@ class ModuleForm(NetBoxModelForm):
         model = Module
         model = Module
         fields = [
         fields = [
             'device', 'module_bay', 'manufacturer', 'module_type', 'serial', 'asset_tag', 'tags',
             'device', 'module_bay', 'manufacturer', 'module_type', 'serial', 'asset_tag', 'tags',
-            'replicate_components', 'adopt_components', 'comments',
+            'replicate_components', 'adopt_components', 'description', 'comments',
         ]
         ]
 
 
     def __init__(self, *args, **kwargs):
     def __init__(self, *args, **kwargs):
@@ -793,11 +793,13 @@ class ModuleForm(NetBoxModelForm):
 
 
 
 
 class CableForm(TenancyForm, NetBoxModelForm):
 class CableForm(TenancyForm, NetBoxModelForm):
+    comments = CommentField()
 
 
     class Meta:
     class Meta:
         model = Cable
         model = Cable
         fields = [
         fields = [
-            'type', 'status', 'tenant_group', 'tenant', 'label', 'color', 'length', 'length_unit', 'tags',
+            'type', 'status', 'tenant_group', 'tenant', 'label', 'color', 'length', 'length_unit', 'description',
+            'comments', 'tags',
         ]
         ]
         widgets = {
         widgets = {
             'status': StaticSelect,
             'status': StaticSelect,
@@ -840,15 +842,16 @@ class PowerPanelForm(NetBoxModelForm):
             'site_id': '$site'
             'site_id': '$site'
         }
         }
     )
     )
+    comments = CommentField()
 
 
     fieldsets = (
     fieldsets = (
-        ('Power Panel', ('region', 'site_group', 'site', 'location', 'name', 'tags')),
+        ('Power Panel', ('region', 'site_group', 'site', 'location', 'name', 'description', 'tags')),
     )
     )
 
 
     class Meta:
     class Meta:
         model = PowerPanel
         model = PowerPanel
         fields = [
         fields = [
-            'region', 'site_group', 'site', 'location', 'name', 'tags',
+            'region', 'site_group', 'site', 'location', 'name', 'description', 'comments', 'tags',
         ]
         ]
 
 
 
 
@@ -894,7 +897,7 @@ class PowerFeedForm(NetBoxModelForm):
     comments = CommentField()
     comments = CommentField()
 
 
     fieldsets = (
     fieldsets = (
-        ('Power Panel', ('region', 'site', 'power_panel')),
+        ('Power Panel', ('region', 'site', 'power_panel', 'description')),
         ('Power Feed', ('rack', 'name', 'status', 'type', 'mark_connected', 'tags')),
         ('Power Feed', ('rack', 'name', 'status', 'type', 'mark_connected', 'tags')),
         ('Characteristics', ('supply', 'voltage', 'amperage', 'phase', 'max_utilization')),
         ('Characteristics', ('supply', 'voltage', 'amperage', 'phase', 'max_utilization')),
     )
     )
@@ -903,7 +906,7 @@ class PowerFeedForm(NetBoxModelForm):
         model = PowerFeed
         model = PowerFeed
         fields = [
         fields = [
             'region', 'site_group', 'site', 'power_panel', 'rack', 'name', 'status', 'type', 'mark_connected', 'supply',
             'region', 'site_group', 'site', 'power_panel', 'rack', 'name', 'status', 'type', 'mark_connected', 'supply',
-            'phase', 'voltage', 'amperage', 'max_utilization', 'comments', 'tags',
+            'phase', 'voltage', 'amperage', 'max_utilization', 'description', 'comments', 'tags',
         ]
         ]
         widgets = {
         widgets = {
             'status': StaticSelect(),
             'status': StaticSelect(),
@@ -922,11 +925,12 @@ class VirtualChassisForm(NetBoxModelForm):
         queryset=Device.objects.all(),
         queryset=Device.objects.all(),
         required=False,
         required=False,
     )
     )
+    comments = CommentField()
 
 
     class Meta:
     class Meta:
         model = VirtualChassis
         model = VirtualChassis
         fields = [
         fields = [
-            'name', 'domain', 'master', 'tags',
+            'name', 'domain', 'master', 'description', 'comments', 'tags',
         ]
         ]
         widgets = {
         widgets = {
             'master': SelectWithPK(),
             'master': SelectWithPK(),

+ 2 - 2
netbox/dcim/forms/object_import.py

@@ -30,7 +30,7 @@ class DeviceTypeImportForm(BootstrapMixin, forms.ModelForm):
         model = DeviceType
         model = DeviceType
         fields = [
         fields = [
             'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow',
             'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow',
-            'comments',
+            'description', 'comments',
         ]
         ]
 
 
 
 
@@ -42,7 +42,7 @@ class ModuleTypeImportForm(BootstrapMixin, forms.ModelForm):
 
 
     class Meta:
     class Meta:
         model = ModuleType
         model = ModuleType
-        fields = ['manufacturer', 'model', 'part_number', 'comments']
+        fields = ['manufacturer', 'model', 'part_number', 'description', 'comments']
 
 
 
 
 #
 #

+ 78 - 0
netbox/dcim/migrations/0165_standardize_description_comments.py

@@ -0,0 +1,78 @@
+# Generated by Django 4.1.2 on 2022-11-03 18:24
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0164_rack_mounting_depth'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='cable',
+            name='comments',
+            field=models.TextField(blank=True),
+        ),
+        migrations.AddField(
+            model_name='cable',
+            name='description',
+            field=models.CharField(blank=True, max_length=200),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='description',
+            field=models.CharField(blank=True, max_length=200),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='description',
+            field=models.CharField(blank=True, max_length=200),
+        ),
+        migrations.AddField(
+            model_name='module',
+            name='description',
+            field=models.CharField(blank=True, max_length=200),
+        ),
+        migrations.AddField(
+            model_name='moduletype',
+            name='description',
+            field=models.CharField(blank=True, max_length=200),
+        ),
+        migrations.AddField(
+            model_name='powerfeed',
+            name='description',
+            field=models.CharField(blank=True, max_length=200),
+        ),
+        migrations.AddField(
+            model_name='powerpanel',
+            name='comments',
+            field=models.TextField(blank=True),
+        ),
+        migrations.AddField(
+            model_name='powerpanel',
+            name='description',
+            field=models.CharField(blank=True, max_length=200),
+        ),
+        migrations.AddField(
+            model_name='rack',
+            name='description',
+            field=models.CharField(blank=True, max_length=200),
+        ),
+        migrations.AddField(
+            model_name='rackreservation',
+            name='comments',
+            field=models.TextField(blank=True),
+        ),
+        migrations.AddField(
+            model_name='virtualchassis',
+            name='comments',
+            field=models.TextField(blank=True),
+        ),
+        migrations.AddField(
+            model_name='virtualchassis',
+            name='description',
+            field=models.CharField(blank=True, max_length=200),
+        ),
+    ]

+ 3 - 3
netbox/dcim/models/cables.py

@@ -12,8 +12,8 @@ from django.urls import reverse
 from dcim.choices import *
 from dcim.choices import *
 from dcim.constants import *
 from dcim.constants import *
 from dcim.fields import PathField
 from dcim.fields import PathField
-from dcim.utils import decompile_path_node, object_to_path_node, path_node_to_object
-from netbox.models import NetBoxModel
+from dcim.utils import decompile_path_node, object_to_path_node
+from netbox.models import PrimaryModel
 from utilities.fields import ColorField
 from utilities.fields import ColorField
 from utilities.querysets import RestrictedQuerySet
 from utilities.querysets import RestrictedQuerySet
 from utilities.utils import to_meters
 from utilities.utils import to_meters
@@ -34,7 +34,7 @@ trace_paths = Signal()
 # Cables
 # Cables
 #
 #
 
 
-class Cable(NetBoxModel):
+class Cable(PrimaryModel):
     """
     """
     A physical connection between two endpoints.
     A physical connection between two endpoints.
     """
     """

+ 6 - 18
netbox/dcim/models/devices.py

@@ -18,7 +18,7 @@ from dcim.constants import *
 from extras.models import ConfigContextModel
 from extras.models import ConfigContextModel
 from extras.querysets import ConfigContextModelQuerySet
 from extras.querysets import ConfigContextModelQuerySet
 from netbox.config import ConfigItem
 from netbox.config import ConfigItem
-from netbox.models import OrganizationalModel, NetBoxModel
+from netbox.models import OrganizationalModel, PrimaryModel
 from utilities.choices import ColorChoices
 from utilities.choices import ColorChoices
 from utilities.fields import ColorField, NaturalOrderingField
 from utilities.fields import ColorField, NaturalOrderingField
 from .device_components import *
 from .device_components import *
@@ -54,7 +54,7 @@ class Manufacturer(OrganizationalModel):
         return reverse('dcim:manufacturer', args=[self.pk])
         return reverse('dcim:manufacturer', args=[self.pk])
 
 
 
 
-class DeviceType(NetBoxModel, WeightMixin):
+class DeviceType(PrimaryModel, WeightMixin):
     """
     """
     A DeviceType represents a particular make (Manufacturer) and model of device. It specifies rack height and depth, as
     A DeviceType represents a particular make (Manufacturer) and model of device. It specifies rack height and depth, as
     well as high-level functional role(s).
     well as high-level functional role(s).
@@ -117,9 +117,6 @@ class DeviceType(NetBoxModel, WeightMixin):
         upload_to='devicetype-images',
         upload_to='devicetype-images',
         blank=True
         blank=True
     )
     )
-    comments = models.TextField(
-        blank=True
-    )
 
 
     clone_fields = (
     clone_fields = (
         'manufacturer', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow', 'weight', 'weight_unit',
         'manufacturer', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow', 'weight', 'weight_unit',
@@ -298,7 +295,7 @@ class DeviceType(NetBoxModel, WeightMixin):
         return self.subdevice_role == SubdeviceRoleChoices.ROLE_CHILD
         return self.subdevice_role == SubdeviceRoleChoices.ROLE_CHILD
 
 
 
 
-class ModuleType(NetBoxModel, WeightMixin):
+class ModuleType(PrimaryModel, WeightMixin):
     """
     """
     A ModuleType represents a hardware element that can be installed within a device and which houses additional
     A ModuleType represents a hardware element that can be installed within a device and which houses additional
     components; for example, a line card within a chassis-based switch such as the Cisco Catalyst 6500. Like a
     components; for example, a line card within a chassis-based switch such as the Cisco Catalyst 6500. Like a
@@ -318,9 +315,6 @@ class ModuleType(NetBoxModel, WeightMixin):
         blank=True,
         blank=True,
         help_text='Discrete part number (optional)'
         help_text='Discrete part number (optional)'
     )
     )
-    comments = models.TextField(
-        blank=True
-    )
 
 
     # Generic relations
     # Generic relations
     images = GenericRelation(
     images = GenericRelation(
@@ -443,7 +437,7 @@ class Platform(OrganizationalModel):
         return reverse('dcim:platform', args=[self.pk])
         return reverse('dcim:platform', args=[self.pk])
 
 
 
 
-class Device(NetBoxModel, ConfigContextModel):
+class Device(PrimaryModel, ConfigContextModel):
     """
     """
     A Device represents a piece of physical hardware mounted within a Rack. Each Device is assigned a DeviceType,
     A Device represents a piece of physical hardware mounted within a Rack. Each Device is assigned a DeviceType,
     DeviceRole, and (optionally) a Platform. Device names are not required, however if one is set it must be unique.
     DeviceRole, and (optionally) a Platform. Device names are not required, however if one is set it must be unique.
@@ -587,9 +581,6 @@ class Device(NetBoxModel, ConfigContextModel):
         null=True,
         null=True,
         validators=[MaxValueValidator(255)]
         validators=[MaxValueValidator(255)]
     )
     )
-    comments = models.TextField(
-        blank=True
-    )
 
 
     # Generic relations
     # Generic relations
     contacts = GenericRelation(
     contacts = GenericRelation(
@@ -906,7 +897,7 @@ class Device(NetBoxModel, ConfigContextModel):
         return round(total_weight / 1000, 2)
         return round(total_weight / 1000, 2)
 
 
 
 
-class Module(NetBoxModel, ConfigContextModel):
+class Module(PrimaryModel, ConfigContextModel):
     """
     """
     A Module represents a field-installable component within a Device which may itself hold multiple device components
     A Module represents a field-installable component within a Device which may itself hold multiple device components
     (for example, a line card within a chassis switch). Modules are instantiated from ModuleTypes.
     (for example, a line card within a chassis switch). Modules are instantiated from ModuleTypes.
@@ -939,9 +930,6 @@ class Module(NetBoxModel, ConfigContextModel):
         verbose_name='Asset tag',
         verbose_name='Asset tag',
         help_text='A unique tag used to identify this device'
         help_text='A unique tag used to identify this device'
     )
     )
-    comments = models.TextField(
-        blank=True
-    )
 
 
     clone_fields = ('device', 'module_type')
     clone_fields = ('device', 'module_type')
 
 
@@ -1019,7 +1007,7 @@ class Module(NetBoxModel, ConfigContextModel):
 # Virtual chassis
 # Virtual chassis
 #
 #
 
 
-class VirtualChassis(NetBoxModel):
+class VirtualChassis(PrimaryModel):
     """
     """
     A collection of Devices which operate with a shared control plane (e.g. a switch stack).
     A collection of Devices which operate with a shared control plane (e.g. a switch stack).
     """
     """

+ 3 - 7
netbox/dcim/models/power.py

@@ -6,9 +6,8 @@ from django.db import models
 from django.urls import reverse
 from django.urls import reverse
 
 
 from dcim.choices import *
 from dcim.choices import *
-from dcim.constants import *
 from netbox.config import ConfigItem
 from netbox.config import ConfigItem
-from netbox.models import NetBoxModel
+from netbox.models import PrimaryModel
 from utilities.validators import ExclusionValidator
 from utilities.validators import ExclusionValidator
 from .device_components import CabledObjectModel, PathEndpoint
 from .device_components import CabledObjectModel, PathEndpoint
 
 
@@ -22,7 +21,7 @@ __all__ = (
 # Power
 # Power
 #
 #
 
 
-class PowerPanel(NetBoxModel):
+class PowerPanel(PrimaryModel):
     """
     """
     A distribution point for electrical power; e.g. a data center RPP.
     A distribution point for electrical power; e.g. a data center RPP.
     """
     """
@@ -77,7 +76,7 @@ class PowerPanel(NetBoxModel):
             )
             )
 
 
 
 
-class PowerFeed(NetBoxModel, PathEndpoint, CabledObjectModel):
+class PowerFeed(PrimaryModel, PathEndpoint, CabledObjectModel):
     """
     """
     An electrical circuit delivered from a PowerPanel.
     An electrical circuit delivered from a PowerPanel.
     """
     """
@@ -132,9 +131,6 @@ class PowerFeed(NetBoxModel, PathEndpoint, CabledObjectModel):
         default=0,
         default=0,
         editable=False
         editable=False
     )
     )
-    comments = models.TextField(
-        blank=True
-    )
 
 
     clone_fields = (
     clone_fields = (
         'power_panel', 'rack', 'status', 'type', 'mark_connected', 'supply', 'phase', 'voltage', 'amperage',
         'power_panel', 'rack', 'status', 'type', 'mark_connected', 'supply', 'phase', 'voltage', 'amperage',

+ 3 - 6
netbox/dcim/models/racks.py

@@ -14,7 +14,7 @@ from django.urls import reverse
 from dcim.choices import *
 from dcim.choices import *
 from dcim.constants import *
 from dcim.constants import *
 from dcim.svg import RackElevationSVG
 from dcim.svg import RackElevationSVG
-from netbox.models import OrganizationalModel, NetBoxModel
+from netbox.models import OrganizationalModel, PrimaryModel
 from utilities.choices import ColorChoices
 from utilities.choices import ColorChoices
 from utilities.fields import ColorField, NaturalOrderingField
 from utilities.fields import ColorField, NaturalOrderingField
 from utilities.utils import array_to_string, drange
 from utilities.utils import array_to_string, drange
@@ -46,7 +46,7 @@ class RackRole(OrganizationalModel):
         return reverse('dcim:rackrole', args=[self.pk])
         return reverse('dcim:rackrole', args=[self.pk])
 
 
 
 
-class Rack(NetBoxModel, WeightMixin):
+class Rack(PrimaryModel, WeightMixin):
     """
     """
     Devices are housed within Racks. Each rack has a defined height measured in rack units, and a front and rear face.
     Devices are housed within Racks. Each rack has a defined height measured in rack units, and a front and rear face.
     Each Rack is assigned to a Site and (optionally) a Location.
     Each Rack is assigned to a Site and (optionally) a Location.
@@ -157,9 +157,6 @@ class Rack(NetBoxModel, WeightMixin):
             'distance between the front and rear rails.'
             'distance between the front and rear rails.'
         )
         )
     )
     )
-    comments = models.TextField(
-        blank=True
-    )
 
 
     # Generic relations
     # Generic relations
     vlan_groups = GenericRelation(
     vlan_groups = GenericRelation(
@@ -463,7 +460,7 @@ class Rack(NetBoxModel, WeightMixin):
         return round(total_weight / 1000, 2)
         return round(total_weight / 1000, 2)
 
 
 
 
-class RackReservation(NetBoxModel):
+class RackReservation(PrimaryModel):
     """
     """
     One or more reserved units within a Rack.
     One or more reserved units within a Rack.
     """
     """

+ 2 - 9
netbox/dcim/models/sites.py

@@ -6,7 +6,7 @@ from timezone_field import TimeZoneField
 
 
 from dcim.choices import *
 from dcim.choices import *
 from dcim.constants import *
 from dcim.constants import *
-from netbox.models import NestedGroupModel, NetBoxModel
+from netbox.models import NestedGroupModel, PrimaryModel
 from utilities.fields import NaturalOrderingField
 from utilities.fields import NaturalOrderingField
 
 
 __all__ = (
 __all__ = (
@@ -131,7 +131,7 @@ class SiteGroup(NestedGroupModel):
 # Sites
 # Sites
 #
 #
 
 
-class Site(NetBoxModel):
+class Site(PrimaryModel):
     """
     """
     A Site represents a geographic location within a network; typically a building or campus. The optional facility
     A Site represents a geographic location within a network; typically a building or campus. The optional facility
     field can be used to include an external designation, such as a data center name (e.g. Equinix SV6).
     field can be used to include an external designation, such as a data center name (e.g. Equinix SV6).
@@ -188,10 +188,6 @@ class Site(NetBoxModel):
     time_zone = TimeZoneField(
     time_zone = TimeZoneField(
         blank=True
         blank=True
     )
     )
-    description = models.CharField(
-        max_length=200,
-        blank=True
-    )
     physical_address = models.CharField(
     physical_address = models.CharField(
         max_length=200,
         max_length=200,
         blank=True
         blank=True
@@ -214,9 +210,6 @@ class Site(NetBoxModel):
         null=True,
         null=True,
         help_text='GPS coordinate (longitude)'
         help_text='GPS coordinate (longitude)'
     )
     )
-    comments = models.TextField(
-        blank=True
-    )
 
 
     # Generic relations
     # Generic relations
     vlan_groups = GenericRelation(
     vlan_groups = GenericRelation(

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

@@ -111,6 +111,7 @@ class CableTable(TenancyColumnsMixin, NetBoxTable):
         order_by=('_abs_length', 'length_unit')
         order_by=('_abs_length', 'length_unit')
     )
     )
     color = columns.ColorColumn()
     color = columns.ColorColumn()
+    comments = columns.MarkdownColumn()
     tags = columns.TagColumn(
     tags = columns.TagColumn(
         url_name='dcim:cable_list'
         url_name='dcim:cable_list'
     )
     )
@@ -120,7 +121,7 @@ class CableTable(TenancyColumnsMixin, NetBoxTable):
         fields = (
         fields = (
             'pk', 'id', 'label', 'a_terminations', 'b_terminations', 'device_a', 'device_b', 'rack_a', 'rack_b',
             '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', 'tenant_group', 'color',
             'location_a', 'location_b', 'site_a', 'site_b', 'status', 'type', 'tenant', 'tenant_group', 'color',
-            'length', 'tags', 'created', 'last_updated',
+            'length', 'description', 'comments', 'tags', 'created', 'last_updated',
         )
         )
         default_columns = (
         default_columns = (
             'pk', 'id', 'label', 'a_terminations', 'b_terminations', 'status', 'type',
             'pk', 'id', 'label', 'a_terminations', 'b_terminations', 'status', 'type',

+ 33 - 45
netbox/dcim/tables/devices.py

@@ -1,21 +1,5 @@
 import django_tables2 as tables
 import django_tables2 as tables
-from dcim.models import (
-    ConsolePort,
-    ConsoleServerPort,
-    Device,
-    DeviceBay,
-    DeviceRole,
-    FrontPort,
-    Interface,
-    InventoryItem,
-    InventoryItemRole,
-    ModuleBay,
-    Platform,
-    PowerOutlet,
-    PowerPort,
-    RearPort,
-    VirtualChassis,
-)
+from dcim import models
 from django_tables2.utils import Accessor
 from django_tables2.utils import Accessor
 from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin
 from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin
 
 
@@ -106,7 +90,7 @@ class DeviceRoleTable(NetBoxTable):
     )
     )
 
 
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
-        model = DeviceRole
+        model = models.DeviceRole
         fields = (
         fields = (
             'pk', 'id', 'name', 'device_count', 'vm_count', 'color', 'vm_role', 'description', 'slug', 'tags',
             'pk', 'id', 'name', 'device_count', 'vm_count', 'color', 'vm_role', 'description', 'slug', 'tags',
             'actions', 'created', 'last_updated',
             'actions', 'created', 'last_updated',
@@ -137,7 +121,7 @@ class PlatformTable(NetBoxTable):
     )
     )
 
 
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
-        model = Platform
+        model = models.Platform
         fields = (
         fields = (
             'pk', 'id', 'name', 'manufacturer', 'device_count', 'vm_count', 'slug', 'napalm_driver', 'napalm_args',
             'pk', 'id', 'name', 'manufacturer', 'device_count', 'vm_count', 'slug', 'napalm_driver', 'napalm_args',
             'description', 'tags', 'actions', 'created', 'last_updated',
             'description', 'tags', 'actions', 'created', 'last_updated',
@@ -220,12 +204,12 @@ class DeviceTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
     )
     )
 
 
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
-        model = Device
+        model = models.Device
         fields = (
         fields = (
             'pk', 'id', 'name', 'status', 'tenant', 'tenant_group', 'device_role', 'manufacturer', 'device_type',
             'pk', 'id', 'name', 'status', 'tenant', 'tenant_group', 'device_role', 'manufacturer', 'device_type',
             'platform', 'serial', 'asset_tag', 'region', 'site_group', 'site', 'location', 'rack', 'position', 'face',
             'platform', 'serial', 'asset_tag', 'region', 'site_group', 'site', 'location', 'rack', 'position', 'face',
             'airflow', 'primary_ip', 'primary_ip4', 'primary_ip6', 'cluster', 'virtual_chassis', 'vc_position',
             'airflow', 'primary_ip', 'primary_ip4', 'primary_ip6', 'cluster', 'virtual_chassis', 'vc_position',
-            'vc_priority', 'comments', 'contacts', 'tags', 'created', 'last_updated',
+            'vc_priority', 'description', 'comments', 'contacts', 'tags', 'created', 'last_updated',
         )
         )
         default_columns = (
         default_columns = (
             'pk', 'name', 'status', 'tenant', 'site', 'location', 'rack', 'device_role', 'manufacturer', 'device_type',
             'pk', 'name', 'status', 'tenant', 'site', 'location', 'rack', 'device_role', 'manufacturer', 'device_type',
@@ -252,7 +236,7 @@ class DeviceImportTable(TenancyColumnsMixin, NetBoxTable):
     )
     )
 
 
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
-        model = Device
+        model = models.Device
         fields = ('id', 'name', 'status', 'tenant', 'tenant_group', 'site', 'rack', 'position', 'device_role', 'device_type')
         fields = ('id', 'name', 'status', 'tenant', 'tenant_group', 'site', 'rack', 'position', 'device_role', 'device_type')
         empty_text = False
         empty_text = False
 
 
@@ -326,7 +310,7 @@ class ConsolePortTable(ModularDeviceComponentTable, PathEndpointTable):
     )
     )
 
 
     class Meta(DeviceComponentTable.Meta):
     class Meta(DeviceComponentTable.Meta):
-        model = ConsolePort
+        model = models.ConsolePort
         fields = (
         fields = (
             'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'speed', 'description',
             'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'speed', 'description',
             'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'tags', 'created', 'last_updated',
             'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'tags', 'created', 'last_updated',
@@ -345,7 +329,7 @@ class DeviceConsolePortTable(ConsolePortTable):
     )
     )
 
 
     class Meta(DeviceComponentTable.Meta):
     class Meta(DeviceComponentTable.Meta):
-        model = ConsolePort
+        model = models.ConsolePort
         fields = (
         fields = (
             'pk', 'id', 'name', 'module_bay', 'module', 'label', 'type', 'speed', 'description', 'mark_connected',
             'pk', 'id', 'name', 'module_bay', 'module', 'label', 'type', 'speed', 'description', 'mark_connected',
             'cable', 'cable_color', 'link_peer', 'connection', 'tags', 'actions'
             'cable', 'cable_color', 'link_peer', 'connection', 'tags', 'actions'
@@ -368,7 +352,7 @@ class ConsoleServerPortTable(ModularDeviceComponentTable, PathEndpointTable):
     )
     )
 
 
     class Meta(DeviceComponentTable.Meta):
     class Meta(DeviceComponentTable.Meta):
-        model = ConsoleServerPort
+        model = models.ConsoleServerPort
         fields = (
         fields = (
             'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'speed', 'description',
             'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'speed', 'description',
             'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'tags', 'created', 'last_updated',
             'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'tags', 'created', 'last_updated',
@@ -388,7 +372,7 @@ class DeviceConsoleServerPortTable(ConsoleServerPortTable):
     )
     )
 
 
     class Meta(DeviceComponentTable.Meta):
     class Meta(DeviceComponentTable.Meta):
-        model = ConsoleServerPort
+        model = models.ConsoleServerPort
         fields = (
         fields = (
             'pk', 'id', 'name', 'module_bay', 'module', 'label', 'type', 'speed', 'description', 'mark_connected',
             'pk', 'id', 'name', 'module_bay', 'module', 'label', 'type', 'speed', 'description', 'mark_connected',
             'cable', 'cable_color', 'link_peer', 'connection', 'tags', 'actions',
             'cable', 'cable_color', 'link_peer', 'connection', 'tags', 'actions',
@@ -411,7 +395,7 @@ class PowerPortTable(ModularDeviceComponentTable, PathEndpointTable):
     )
     )
 
 
     class Meta(DeviceComponentTable.Meta):
     class Meta(DeviceComponentTable.Meta):
-        model = PowerPort
+        model = models.PowerPort
         fields = (
         fields = (
             'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'description', 'mark_connected',
             'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'description', 'mark_connected',
             'maximum_draw', 'allocated_draw', 'cable', 'cable_color', 'link_peer', 'connection', 'tags', 'created',
             'maximum_draw', 'allocated_draw', 'cable', 'cable_color', 'link_peer', 'connection', 'tags', 'created',
@@ -432,7 +416,7 @@ class DevicePowerPortTable(PowerPortTable):
     )
     )
 
 
     class Meta(DeviceComponentTable.Meta):
     class Meta(DeviceComponentTable.Meta):
-        model = PowerPort
+        model = models.PowerPort
         fields = (
         fields = (
             'pk', 'id', 'name', 'module_bay', 'module', 'label', 'type', 'maximum_draw', 'allocated_draw',
             'pk', 'id', 'name', 'module_bay', 'module', 'label', 'type', 'maximum_draw', 'allocated_draw',
             'description', 'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'tags', 'actions',
             'description', 'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'tags', 'actions',
@@ -460,7 +444,7 @@ class PowerOutletTable(ModularDeviceComponentTable, PathEndpointTable):
     )
     )
 
 
     class Meta(DeviceComponentTable.Meta):
     class Meta(DeviceComponentTable.Meta):
-        model = PowerOutlet
+        model = models.PowerOutlet
         fields = (
         fields = (
             'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'description', 'power_port',
             'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'description', 'power_port',
             'feed_leg', 'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'tags', 'created',
             'feed_leg', 'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'tags', 'created',
@@ -480,7 +464,7 @@ class DevicePowerOutletTable(PowerOutletTable):
     )
     )
 
 
     class Meta(DeviceComponentTable.Meta):
     class Meta(DeviceComponentTable.Meta):
-        model = PowerOutlet
+        model = models.PowerOutlet
         fields = (
         fields = (
             'pk', 'id', 'name', 'module_bay', 'module', 'label', 'type', 'power_port', 'feed_leg', 'description',
             'pk', 'id', 'name', 'module_bay', 'module', 'label', 'type', 'power_port', 'feed_leg', 'description',
             'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'tags', 'actions',
             'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'tags', 'actions',
@@ -544,7 +528,7 @@ class InterfaceTable(ModularDeviceComponentTable, BaseInterfaceTable, PathEndpoi
     )
     )
 
 
     class Meta(DeviceComponentTable.Meta):
     class Meta(DeviceComponentTable.Meta):
-        model = Interface
+        model = models.Interface
         fields = (
         fields = (
             'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'enabled', 'type', 'mgmt_only', 'mtu',
             'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'enabled', 'type', 'mgmt_only', 'mtu',
             'speed', 'duplex', 'mode', 'mac_address', 'wwn', 'poe_mode', 'poe_type', 'rf_role', 'rf_channel',
             'speed', 'duplex', 'mode', 'mac_address', 'wwn', 'poe_mode', 'poe_type', 'rf_role', 'rf_channel',
@@ -578,7 +562,7 @@ class DeviceInterfaceTable(InterfaceTable):
     )
     )
 
 
     class Meta(DeviceComponentTable.Meta):
     class Meta(DeviceComponentTable.Meta):
-        model = Interface
+        model = models.Interface
         fields = (
         fields = (
             'pk', 'id', 'name', 'module_bay', 'module', 'label', 'enabled', 'type', 'parent', 'bridge', 'lag',
             'pk', 'id', 'name', 'module_bay', 'module', 'label', 'enabled', 'type', 'parent', 'bridge', 'lag',
             'mgmt_only', 'mtu', 'mode', 'mac_address', 'wwn', 'rf_role', 'rf_channel', 'rf_channel_frequency',
             'mgmt_only', 'mtu', 'mode', 'mac_address', 'wwn', 'rf_role', 'rf_channel', 'rf_channel_frequency',
@@ -617,7 +601,7 @@ class FrontPortTable(ModularDeviceComponentTable, CableTerminationTable):
     )
     )
 
 
     class Meta(DeviceComponentTable.Meta):
     class Meta(DeviceComponentTable.Meta):
-        model = FrontPort
+        model = models.FrontPort
         fields = (
         fields = (
             'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'color', 'rear_port',
             'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'color', 'rear_port',
             'rear_port_position', 'description', 'mark_connected', 'cable', 'cable_color', 'link_peer', 'tags',
             'rear_port_position', 'description', 'mark_connected', 'cable', 'cable_color', 'link_peer', 'tags',
@@ -640,7 +624,7 @@ class DeviceFrontPortTable(FrontPortTable):
     )
     )
 
 
     class Meta(DeviceComponentTable.Meta):
     class Meta(DeviceComponentTable.Meta):
-        model = FrontPort
+        model = models.FrontPort
         fields = (
         fields = (
             'pk', 'id', 'name', 'module_bay', 'module', 'label', 'type', 'rear_port', 'rear_port_position',
             'pk', 'id', 'name', 'module_bay', 'module', 'label', 'type', 'rear_port', 'rear_port_position',
             'description', 'mark_connected', 'cable', 'cable_color', 'link_peer', 'tags', 'actions',
             'description', 'mark_connected', 'cable', 'cable_color', 'link_peer', 'tags', 'actions',
@@ -666,7 +650,7 @@ class RearPortTable(ModularDeviceComponentTable, CableTerminationTable):
     )
     )
 
 
     class Meta(DeviceComponentTable.Meta):
     class Meta(DeviceComponentTable.Meta):
-        model = RearPort
+        model = models.RearPort
         fields = (
         fields = (
             'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'color', 'positions', 'description',
             'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'color', 'positions', 'description',
             'mark_connected', 'cable', 'cable_color', 'link_peer', 'tags', 'created', 'last_updated',
             'mark_connected', 'cable', 'cable_color', 'link_peer', 'tags', 'created', 'last_updated',
@@ -686,7 +670,7 @@ class DeviceRearPortTable(RearPortTable):
     )
     )
 
 
     class Meta(DeviceComponentTable.Meta):
     class Meta(DeviceComponentTable.Meta):
-        model = RearPort
+        model = models.RearPort
         fields = (
         fields = (
             'pk', 'id', 'name', 'module_bay', 'module', 'label', 'type', 'positions', 'description', 'mark_connected',
             'pk', 'id', 'name', 'module_bay', 'module', 'label', 'type', 'positions', 'description', 'mark_connected',
             'cable', 'cable_color', 'link_peer', 'tags', 'actions',
             'cable', 'cable_color', 'link_peer', 'tags', 'actions',
@@ -727,7 +711,7 @@ class DeviceBayTable(DeviceComponentTable):
     )
     )
 
 
     class Meta(DeviceComponentTable.Meta):
     class Meta(DeviceComponentTable.Meta):
-        model = DeviceBay
+        model = models.DeviceBay
         fields = (
         fields = (
             'pk', 'id', 'name', 'device', 'label', 'status', 'device_role', 'device_type', 'installed_device', 'description', 'tags',
             'pk', 'id', 'name', 'device', 'label', 'status', 'device_role', 'device_type', 'installed_device', 'description', 'tags',
             'created', 'last_updated',
             'created', 'last_updated',
@@ -748,7 +732,7 @@ class DeviceDeviceBayTable(DeviceBayTable):
     )
     )
 
 
     class Meta(DeviceComponentTable.Meta):
     class Meta(DeviceComponentTable.Meta):
-        model = DeviceBay
+        model = models.DeviceBay
         fields = (
         fields = (
             'pk', 'id', 'name', 'label', 'status', 'installed_device', 'description', 'tags', 'actions',
             'pk', 'id', 'name', 'label', 'status', 'installed_device', 'description', 'tags', 'actions',
         )
         )
@@ -777,7 +761,7 @@ class ModuleBayTable(DeviceComponentTable):
     )
     )
 
 
     class Meta(DeviceComponentTable.Meta):
     class Meta(DeviceComponentTable.Meta):
-        model = ModuleBay
+        model = models.ModuleBay
         fields = (
         fields = (
             'pk', 'id', 'name', 'device', 'label', 'position', 'installed_module', 'module_serial', 'module_asset_tag',
             'pk', 'id', 'name', 'device', 'label', 'position', 'installed_module', 'module_serial', 'module_asset_tag',
             'description', 'tags',
             'description', 'tags',
@@ -791,7 +775,7 @@ class DeviceModuleBayTable(ModuleBayTable):
     )
     )
 
 
     class Meta(DeviceComponentTable.Meta):
     class Meta(DeviceComponentTable.Meta):
-        model = ModuleBay
+        model = models.ModuleBay
         fields = (
         fields = (
             'pk', 'id', 'name', 'label', 'position', 'installed_module', 'module_serial', 'module_asset_tag',
             'pk', 'id', 'name', 'label', 'position', 'installed_module', 'module_serial', 'module_asset_tag',
             'description', 'tags', 'actions',
             'description', 'tags', 'actions',
@@ -821,7 +805,7 @@ class InventoryItemTable(DeviceComponentTable):
     cable = None  # Override DeviceComponentTable
     cable = None  # Override DeviceComponentTable
 
 
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
-        model = InventoryItem
+        model = models.InventoryItem
         fields = (
         fields = (
             'pk', 'id', 'name', 'device', 'component', 'label', 'role', 'manufacturer', 'part_id', 'serial',
             'pk', 'id', 'name', 'device', 'component', 'label', 'role', 'manufacturer', 'part_id', 'serial',
             'asset_tag', 'description', 'discovered', 'tags', 'created', 'last_updated',
             'asset_tag', 'description', 'discovered', 'tags', 'created', 'last_updated',
@@ -840,7 +824,7 @@ class DeviceInventoryItemTable(InventoryItemTable):
     )
     )
 
 
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
-        model = InventoryItem
+        model = models.InventoryItem
         fields = (
         fields = (
             'pk', 'id', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'component',
             'pk', 'id', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'component',
             'description', 'discovered', 'tags', 'actions',
             'description', 'discovered', 'tags', 'actions',
@@ -865,7 +849,7 @@ class InventoryItemRoleTable(NetBoxTable):
     )
     )
 
 
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
-        model = InventoryItemRole
+        model = models.InventoryItemRole
         fields = (
         fields = (
             'pk', 'id', 'name', 'inventoryitem_count', 'color', 'description', 'slug', 'tags', 'actions',
             'pk', 'id', 'name', 'inventoryitem_count', 'color', 'description', 'slug', 'tags', 'actions',
         )
         )
@@ -888,11 +872,15 @@ class VirtualChassisTable(NetBoxTable):
         url_params={'virtual_chassis_id': 'pk'},
         url_params={'virtual_chassis_id': 'pk'},
         verbose_name='Members'
         verbose_name='Members'
     )
     )
+    comments = columns.MarkdownColumn()
     tags = columns.TagColumn(
     tags = columns.TagColumn(
         url_name='dcim:virtualchassis_list'
         url_name='dcim:virtualchassis_list'
     )
     )
 
 
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
-        model = VirtualChassis
-        fields = ('pk', 'id', 'name', 'domain', 'master', 'member_count', 'tags', 'created', 'last_updated',)
+        model = models.VirtualChassis
+        fields = (
+            'pk', 'id', 'name', 'domain', 'master', 'member_count', 'description', 'comments', 'tags', 'created',
+            'last_updated',
+        )
         default_columns = ('pk', 'name', 'domain', 'master', 'member_count')
         default_columns = ('pk', 'name', 'domain', 'master', 'member_count')

+ 14 - 30
netbox/dcim/tables/devicetypes.py

@@ -1,19 +1,6 @@
 import django_tables2 as tables
 import django_tables2 as tables
 
 
-from dcim.models import (
-    ConsolePortTemplate,
-    ConsoleServerPortTemplate,
-    DeviceBayTemplate,
-    DeviceType,
-    FrontPortTemplate,
-    InterfaceTemplate,
-    InventoryItemTemplate,
-    Manufacturer,
-    ModuleBayTemplate,
-    PowerOutletTemplate,
-    PowerPortTemplate,
-    RearPortTemplate,
-)
+from dcim import models
 from netbox.tables import NetBoxTable, columns
 from netbox.tables import NetBoxTable, columns
 from tenancy.tables import ContactsColumnMixin
 from tenancy.tables import ContactsColumnMixin
 from .template_code import MODULAR_COMPONENT_TEMPLATE_BUTTONS, DEVICE_WEIGHT
 from .template_code import MODULAR_COMPONENT_TEMPLATE_BUTTONS, DEVICE_WEIGHT
@@ -59,7 +46,7 @@ class ManufacturerTable(ContactsColumnMixin, NetBoxTable):
     )
     )
 
 
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
-        model = Manufacturer
+        model = models.Manufacturer
         fields = (
         fields = (
             'pk', 'id', 'name', 'devicetype_count', 'inventoryitem_count', 'platform_count', 'description', 'slug',
             'pk', 'id', 'name', 'devicetype_count', 'inventoryitem_count', 'platform_count', 'description', 'slug',
             'contacts', 'actions', 'created', 'last_updated',
             'contacts', 'actions', 'created', 'last_updated',
@@ -100,15 +87,12 @@ class DeviceTypeTable(NetBoxTable):
         template_code=DEVICE_WEIGHT,
         template_code=DEVICE_WEIGHT,
         order_by=('_abs_weight', 'weight_unit')
         order_by=('_abs_weight', 'weight_unit')
     )
     )
-    u_height = columns.TemplateColumn(
-        template_code='{{ value|floatformat }}'
-    )
 
 
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
-        model = DeviceType
+        model = models.DeviceType
         fields = (
         fields = (
             'pk', 'id', 'model', 'manufacturer', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role',
             'pk', 'id', 'model', 'manufacturer', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role',
-            'airflow', 'weight', 'comments', 'instance_count', 'tags', 'created', 'last_updated',
+            'airflow', 'weight', 'description', 'comments', 'instance_count', 'tags', 'created', 'last_updated',
         )
         )
         default_columns = (
         default_columns = (
             'pk', 'model', 'manufacturer', 'part_number', 'u_height', 'is_full_depth', 'instance_count',
             'pk', 'model', 'manufacturer', 'part_number', 'u_height', 'is_full_depth', 'instance_count',
@@ -138,7 +122,7 @@ class ConsolePortTemplateTable(ComponentTemplateTable):
     )
     )
 
 
     class Meta(ComponentTemplateTable.Meta):
     class Meta(ComponentTemplateTable.Meta):
-        model = ConsolePortTemplate
+        model = models.ConsolePortTemplate
         fields = ('pk', 'name', 'label', 'type', 'description', 'actions')
         fields = ('pk', 'name', 'label', 'type', 'description', 'actions')
         empty_text = "None"
         empty_text = "None"
 
 
@@ -150,7 +134,7 @@ class ConsoleServerPortTemplateTable(ComponentTemplateTable):
     )
     )
 
 
     class Meta(ComponentTemplateTable.Meta):
     class Meta(ComponentTemplateTable.Meta):
-        model = ConsoleServerPortTemplate
+        model = models.ConsoleServerPortTemplate
         fields = ('pk', 'name', 'label', 'type', 'description', 'actions')
         fields = ('pk', 'name', 'label', 'type', 'description', 'actions')
         empty_text = "None"
         empty_text = "None"
 
 
@@ -162,7 +146,7 @@ class PowerPortTemplateTable(ComponentTemplateTable):
     )
     )
 
 
     class Meta(ComponentTemplateTable.Meta):
     class Meta(ComponentTemplateTable.Meta):
-        model = PowerPortTemplate
+        model = models.PowerPortTemplate
         fields = ('pk', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description', 'actions')
         fields = ('pk', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description', 'actions')
         empty_text = "None"
         empty_text = "None"
 
 
@@ -174,7 +158,7 @@ class PowerOutletTemplateTable(ComponentTemplateTable):
     )
     )
 
 
     class Meta(ComponentTemplateTable.Meta):
     class Meta(ComponentTemplateTable.Meta):
-        model = PowerOutletTemplate
+        model = models.PowerOutletTemplate
         fields = ('pk', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description', 'actions')
         fields = ('pk', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description', 'actions')
         empty_text = "None"
         empty_text = "None"
 
 
@@ -189,7 +173,7 @@ class InterfaceTemplateTable(ComponentTemplateTable):
     )
     )
 
 
     class Meta(ComponentTemplateTable.Meta):
     class Meta(ComponentTemplateTable.Meta):
-        model = InterfaceTemplate
+        model = models.InterfaceTemplate
         fields = ('pk', 'name', 'label', 'mgmt_only', 'type', 'description', 'poe_mode', 'poe_type', 'actions')
         fields = ('pk', 'name', 'label', 'mgmt_only', 'type', 'description', 'poe_mode', 'poe_type', 'actions')
         empty_text = "None"
         empty_text = "None"
 
 
@@ -205,7 +189,7 @@ class FrontPortTemplateTable(ComponentTemplateTable):
     )
     )
 
 
     class Meta(ComponentTemplateTable.Meta):
     class Meta(ComponentTemplateTable.Meta):
-        model = FrontPortTemplate
+        model = models.FrontPortTemplate
         fields = ('pk', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position', 'description', 'actions')
         fields = ('pk', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position', 'description', 'actions')
         empty_text = "None"
         empty_text = "None"
 
 
@@ -218,7 +202,7 @@ class RearPortTemplateTable(ComponentTemplateTable):
     )
     )
 
 
     class Meta(ComponentTemplateTable.Meta):
     class Meta(ComponentTemplateTable.Meta):
-        model = RearPortTemplate
+        model = models.RearPortTemplate
         fields = ('pk', 'name', 'label', 'type', 'color', 'positions', 'description', 'actions')
         fields = ('pk', 'name', 'label', 'type', 'color', 'positions', 'description', 'actions')
         empty_text = "None"
         empty_text = "None"
 
 
@@ -229,7 +213,7 @@ class ModuleBayTemplateTable(ComponentTemplateTable):
     )
     )
 
 
     class Meta(ComponentTemplateTable.Meta):
     class Meta(ComponentTemplateTable.Meta):
-        model = ModuleBayTemplate
+        model = models.ModuleBayTemplate
         fields = ('pk', 'name', 'label', 'position', 'description', 'actions')
         fields = ('pk', 'name', 'label', 'position', 'description', 'actions')
         empty_text = "None"
         empty_text = "None"
 
 
@@ -240,7 +224,7 @@ class DeviceBayTemplateTable(ComponentTemplateTable):
     )
     )
 
 
     class Meta(ComponentTemplateTable.Meta):
     class Meta(ComponentTemplateTable.Meta):
-        model = DeviceBayTemplate
+        model = models.DeviceBayTemplate
         fields = ('pk', 'name', 'label', 'description', 'actions')
         fields = ('pk', 'name', 'label', 'description', 'actions')
         empty_text = "None"
         empty_text = "None"
 
 
@@ -260,7 +244,7 @@ class InventoryItemTemplateTable(ComponentTemplateTable):
     )
     )
 
 
     class Meta(ComponentTemplateTable.Meta):
     class Meta(ComponentTemplateTable.Meta):
-        model = InventoryItemTemplate
+        model = models.InventoryItemTemplate
         fields = (
         fields = (
             'pk', 'name', 'label', 'parent', 'role', 'manufacturer', 'part_id', 'component', 'description', 'actions',
             'pk', 'name', 'label', 'parent', 'role', 'manufacturer', 'part_id', 'component', 'description', 'actions',
         )
         )

+ 3 - 3
netbox/dcim/tables/modules.py

@@ -35,7 +35,7 @@ class ModuleTypeTable(NetBoxTable):
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
         model = ModuleType
         model = ModuleType
         fields = (
         fields = (
-            'pk', 'id', 'model', 'manufacturer', 'part_number', 'weight', 'comments', 'tags',
+            'pk', 'id', 'model', 'manufacturer', 'part_number', 'weight', 'description', 'comments', 'tags',
         )
         )
         default_columns = (
         default_columns = (
             'pk', 'model', 'manufacturer', 'part_number',
             'pk', 'model', 'manufacturer', 'part_number',
@@ -64,8 +64,8 @@ class ModuleTable(NetBoxTable):
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
         model = Module
         model = Module
         fields = (
         fields = (
-            'pk', 'id', 'device', 'module_bay', 'manufacturer', 'module_type', 'serial', 'asset_tag', 'comments',
-            'tags',
+            'pk', 'id', 'device', 'module_bay', 'manufacturer', 'module_type', 'serial', 'asset_tag', 'description',
+            'comments', 'tags',
         )
         )
         default_columns = (
         default_columns = (
             'pk', 'id', 'device', 'module_bay', 'manufacturer', 'module_type', 'serial', 'asset_tag',
             'pk', 'id', 'device', 'module_bay', 'manufacturer', 'module_type', 'serial', 'asset_tag',

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

@@ -31,6 +31,7 @@ class PowerPanelTable(ContactsColumnMixin, NetBoxTable):
         url_params={'power_panel_id': 'pk'},
         url_params={'power_panel_id': 'pk'},
         verbose_name='Feeds'
         verbose_name='Feeds'
     )
     )
+    comments = columns.MarkdownColumn()
     tags = columns.TagColumn(
     tags = columns.TagColumn(
         url_name='dcim:powerpanel_list'
         url_name='dcim:powerpanel_list'
     )
     )
@@ -38,7 +39,8 @@ class PowerPanelTable(ContactsColumnMixin, NetBoxTable):
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
         model = PowerPanel
         model = PowerPanel
         fields = (
         fields = (
-            'pk', 'id', 'name', 'site', 'location', 'powerfeed_count', 'contacts', 'tags', 'created', 'last_updated',
+            'pk', 'id', 'name', 'site', 'location', 'powerfeed_count', 'contacts', 'description', 'comments', 'tags',
+            'created', 'last_updated',
         )
         )
         default_columns = ('pk', 'name', 'site', 'location', 'powerfeed_count')
         default_columns = ('pk', 'name', 'site', 'location', 'powerfeed_count')
 
 
@@ -77,7 +79,7 @@ class PowerFeedTable(CableTerminationTable):
         fields = (
         fields = (
             'pk', 'id', 'name', 'power_panel', 'rack', 'status', 'type', 'supply', 'voltage', 'amperage', 'phase',
             'pk', 'id', 'name', 'power_panel', 'rack', 'status', 'type', 'supply', 'voltage', 'amperage', 'phase',
             'max_utilization', 'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'available_power',
             'max_utilization', 'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'available_power',
-            'comments', 'tags', 'created', 'last_updated',
+            'description', 'comments', 'tags', 'created', 'last_updated',
         )
         )
         default_columns = (
         default_columns = (
             'pk', 'name', 'power_panel', 'rack', 'status', 'type', 'supply', 'voltage', 'amperage', 'phase', 'cable',
             'pk', 'name', 'power_panel', 'rack', 'status', 'type', 'supply', 'voltage', 'amperage', 'phase', 'cable',

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

@@ -90,8 +90,8 @@ class RackTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
         fields = (
         fields = (
             'pk', 'id', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'tenant_group', 'role', 'serial',
             'pk', 'id', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'tenant_group', 'role', 'serial',
             'asset_tag', 'type', 'u_height', 'width', 'outer_width', 'outer_depth', 'mounting_depth', 'weight',
             'asset_tag', 'type', 'u_height', 'width', 'outer_width', 'outer_depth', 'mounting_depth', 'weight',
-            'comments', 'device_count', 'get_utilization', 'get_power_utilization', 'contacts', 'tags', 'created',
-            'last_updated',
+            'comments', 'device_count', 'get_utilization', 'get_power_utilization', 'description', 'contacts', 'tags',
+            'created', 'last_updated',
         )
         )
         default_columns = (
         default_columns = (
             'pk', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'u_height', 'device_count',
             'pk', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'u_height', 'device_count',
@@ -123,6 +123,7 @@ class RackReservationTable(TenancyColumnsMixin, NetBoxTable):
         orderable=False,
         orderable=False,
         verbose_name='Units'
         verbose_name='Units'
     )
     )
+    comments = columns.MarkdownColumn()
     tags = columns.TagColumn(
     tags = columns.TagColumn(
         url_name='dcim:rackreservation_list'
         url_name='dcim:rackreservation_list'
     )
     )
@@ -130,7 +131,7 @@ class RackReservationTable(TenancyColumnsMixin, NetBoxTable):
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
         model = RackReservation
         model = RackReservation
         fields = (
         fields = (
-            'pk', 'id', 'reservation', 'site', 'location', 'rack', 'unit_list', 'user', 'created', 'tenant', 'tenant_group', 'description', 'tags',
-            'actions', 'created', 'last_updated',
+            'pk', 'id', 'reservation', 'site', 'location', 'rack', 'unit_list', 'user', 'created', 'tenant',
+            'tenant_group', 'description', 'comments', 'tags', 'actions', 'created', 'last_updated',
         )
         )
         default_columns = ('pk', 'reservation', 'site', 'rack', 'unit_list', 'user', 'description')
         default_columns = ('pk', 'reservation', 'site', 'rack', 'unit_list', 'user', 'description')

+ 21 - 18
netbox/ipam/api/serializers.py

@@ -31,8 +31,8 @@ class ASNSerializer(NetBoxModelSerializer):
     class Meta:
     class Meta:
         model = ASN
         model = ASN
         fields = [
         fields = [
-            'id', 'url', 'display', 'asn', 'rir', 'tenant', 'description', 'site_count', 'provider_count', 'tags',
-            'custom_fields', 'created', 'last_updated',
+            'id', 'url', 'display', 'asn', 'rir', 'tenant', 'description', 'comments', 'tags', 'custom_fields',
+            'created', 'last_updated', 'site_count', 'provider_count',
         ]
         ]
 
 
 
 
@@ -61,8 +61,9 @@ class VRFSerializer(NetBoxModelSerializer):
     class Meta:
     class Meta:
         model = VRF
         model = VRF
         fields = [
         fields = [
-            'id', 'url', 'display', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'import_targets',
-            'export_targets', 'tags', 'custom_fields', 'created', 'last_updated', 'ipaddress_count', 'prefix_count',
+            'id', 'url', 'display', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'comments',
+            'import_targets', 'export_targets', 'tags', 'custom_fields', 'created', 'last_updated', 'ipaddress_count',
+            'prefix_count',
         ]
         ]
 
 
 
 
@@ -77,7 +78,8 @@ class RouteTargetSerializer(NetBoxModelSerializer):
     class Meta:
     class Meta:
         model = RouteTarget
         model = RouteTarget
         fields = [
         fields = [
-            'id', 'url', 'display', 'name', 'tenant', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
+            'id', 'url', 'display', 'name', 'tenant', 'description', 'comments', 'tags', 'custom_fields', 'created',
+            'last_updated',
         ]
         ]
 
 
 
 
@@ -106,8 +108,8 @@ class AggregateSerializer(NetBoxModelSerializer):
     class Meta:
     class Meta:
         model = Aggregate
         model = Aggregate
         fields = [
         fields = [
-            'id', 'url', 'display', 'family', 'prefix', 'rir', 'tenant', 'date_added', 'description', 'tags',
-            'custom_fields', 'created', 'last_updated',
+            'id', 'url', 'display', 'family', 'prefix', 'rir', 'tenant', 'date_added', 'description', 'comments',
+            'tags', 'custom_fields', 'created', 'last_updated',
         ]
         ]
         read_only_fields = ['family']
         read_only_fields = ['family']
 
 
@@ -123,8 +125,8 @@ class FHRPGroupSerializer(NetBoxModelSerializer):
     class Meta:
     class Meta:
         model = FHRPGroup
         model = FHRPGroup
         fields = [
         fields = [
-            'id', 'name', 'url', 'display', 'protocol', 'group_id', 'auth_type', 'auth_key', 'description', 'ip_addresses',
-            'tags', 'custom_fields', 'created', 'last_updated',
+            'id', 'name', 'url', 'display', 'protocol', 'group_id', 'auth_type', 'auth_key', 'description', 'comments',
+            'tags', 'custom_fields', 'created', 'last_updated', 'ip_addresses',
         ]
         ]
 
 
 
 
@@ -215,7 +217,7 @@ class VLANSerializer(NetBoxModelSerializer):
         model = VLAN
         model = VLAN
         fields = [
         fields = [
             'id', 'url', 'display', 'site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description',
             'id', 'url', 'display', 'site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description',
-            'l2vpn_termination', 'tags', 'custom_fields', 'created', 'last_updated', 'prefix_count',
+            'comments', 'l2vpn_termination', 'tags', 'custom_fields', 'created', 'last_updated', 'prefix_count',
         ]
         ]
 
 
 
 
@@ -273,7 +275,8 @@ class PrefixSerializer(NetBoxModelSerializer):
         model = Prefix
         model = Prefix
         fields = [
         fields = [
             'id', 'url', 'display', 'family', 'prefix', 'site', 'vrf', 'tenant', 'vlan', 'status', 'role', 'is_pool',
             'id', 'url', 'display', 'family', 'prefix', 'site', 'vrf', 'tenant', 'vlan', 'status', 'role', 'is_pool',
-            'mark_utilized', 'description', 'tags', 'custom_fields', 'created', 'last_updated', 'children', '_depth',
+            'mark_utilized', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'children',
+            '_depth',
         ]
         ]
         read_only_fields = ['family']
         read_only_fields = ['family']
 
 
@@ -342,7 +345,7 @@ class IPRangeSerializer(NetBoxModelSerializer):
         model = IPRange
         model = IPRange
         fields = [
         fields = [
             'id', 'url', 'display', 'family', 'start_address', 'end_address', 'size', 'vrf', 'tenant', 'status', 'role',
             'id', 'url', 'display', 'family', 'start_address', 'end_address', 'size', 'vrf', 'tenant', 'status', 'role',
-            'description', 'tags', 'custom_fields', 'created', 'last_updated', 'children',
+            'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'children',
         ]
         ]
         read_only_fields = ['family']
         read_only_fields = ['family']
 
 
@@ -371,8 +374,8 @@ class IPAddressSerializer(NetBoxModelSerializer):
         model = IPAddress
         model = IPAddress
         fields = [
         fields = [
             'id', 'url', 'display', 'family', 'address', 'vrf', 'tenant', 'status', 'role', 'assigned_object_type',
             'id', 'url', 'display', 'family', 'address', 'vrf', 'tenant', 'status', 'role', 'assigned_object_type',
-            'assigned_object_id', 'assigned_object', 'nat_inside', 'nat_outside', 'dns_name', 'description', 'tags',
-            'custom_fields', 'created', 'last_updated',
+            'assigned_object_id', 'assigned_object', 'nat_inside', 'nat_outside', 'dns_name', 'description', 'comments',
+            'tags', 'custom_fields', 'created', 'last_updated',
         ]
         ]
 
 
     @swagger_serializer_method(serializer_or_field=serializers.JSONField)
     @swagger_serializer_method(serializer_or_field=serializers.JSONField)
@@ -415,8 +418,8 @@ class ServiceTemplateSerializer(NetBoxModelSerializer):
     class Meta:
     class Meta:
         model = ServiceTemplate
         model = ServiceTemplate
         fields = [
         fields = [
-            'id', 'url', 'display', 'name', 'ports', 'protocol', 'description', 'tags', 'custom_fields', 'created',
-            'last_updated',
+            'id', 'url', 'display', 'name', 'ports', 'protocol', 'description', 'comments', 'tags', 'custom_fields',
+            'created', 'last_updated',
         ]
         ]
 
 
 
 
@@ -436,7 +439,7 @@ class ServiceSerializer(NetBoxModelSerializer):
         model = Service
         model = Service
         fields = [
         fields = [
             'id', 'url', 'display', 'device', 'virtual_machine', 'name', 'ports', 'protocol', 'ipaddresses',
             'id', 'url', 'display', 'device', 'virtual_machine', 'name', 'ports', 'protocol', 'ipaddresses',
-            'description', 'tags', 'custom_fields', 'created', 'last_updated',
+            'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
         ]
         ]
 
 
 #
 #
@@ -465,7 +468,7 @@ class L2VPNSerializer(NetBoxModelSerializer):
         model = L2VPN
         model = L2VPN
         fields = [
         fields = [
             'id', 'url', 'display', 'identifier', 'name', 'slug', 'type', 'import_targets', 'export_targets',
             'id', 'url', 'display', 'identifier', 'name', 'slug', 'type', 'import_targets', 'export_targets',
-            'description', 'tenant', 'tags', 'custom_fields', 'created', 'last_updated'
+            'description', 'comments', 'tenant', 'tags', 'custom_fields', 'created', 'last_updated'
         ]
         ]
 
 
 
 

+ 67 - 23
netbox/ipam/forms/bulk_edit.py

@@ -8,8 +8,8 @@ from ipam.models import ASN
 from netbox.forms import NetBoxModelBulkEditForm
 from netbox.forms import NetBoxModelBulkEditForm
 from tenancy.models import Tenant
 from tenancy.models import Tenant
 from utilities.forms import (
 from utilities.forms import (
-    add_blank_choice, BulkEditNullBooleanSelect, DynamicModelChoiceField, NumericArrayField, StaticSelect,
-    DynamicModelMultipleChoiceField,
+    add_blank_choice, BulkEditNullBooleanSelect, CommentField, DynamicModelChoiceField, NumericArrayField,
+    SmallTextarea, StaticSelect, DynamicModelMultipleChoiceField,
 )
 )
 
 
 __all__ = (
 __all__ = (
@@ -43,15 +43,19 @@ class VRFBulkEditForm(NetBoxModelBulkEditForm):
         label='Enforce unique space'
         label='Enforce unique space'
     )
     )
     description = forms.CharField(
     description = forms.CharField(
-        max_length=100,
+        max_length=200,
         required=False
         required=False
     )
     )
+    comments = CommentField(
+        widget=SmallTextarea,
+        label='Comments'
+    )
 
 
     model = VRF
     model = VRF
     fieldsets = (
     fieldsets = (
         (None, ('tenant', 'enforce_unique', 'description')),
         (None, ('tenant', 'enforce_unique', 'description')),
     )
     )
-    nullable_fields = ('tenant', 'description')
+    nullable_fields = ('tenant', 'description', 'comments')
 
 
 
 
 class RouteTargetBulkEditForm(NetBoxModelBulkEditForm):
 class RouteTargetBulkEditForm(NetBoxModelBulkEditForm):
@@ -63,12 +67,16 @@ class RouteTargetBulkEditForm(NetBoxModelBulkEditForm):
         max_length=200,
         max_length=200,
         required=False
         required=False
     )
     )
+    comments = CommentField(
+        widget=SmallTextarea,
+        label='Comments'
+    )
 
 
     model = RouteTarget
     model = RouteTarget
     fieldsets = (
     fieldsets = (
         (None, ('tenant', 'description')),
         (None, ('tenant', 'description')),
     )
     )
-    nullable_fields = ('tenant', 'description')
+    nullable_fields = ('tenant', 'description', 'comments')
 
 
 
 
 class RIRBulkEditForm(NetBoxModelBulkEditForm):
 class RIRBulkEditForm(NetBoxModelBulkEditForm):
@@ -103,15 +111,19 @@ class ASNBulkEditForm(NetBoxModelBulkEditForm):
         required=False
         required=False
     )
     )
     description = forms.CharField(
     description = forms.CharField(
-        max_length=100,
+        max_length=200,
         required=False
         required=False
     )
     )
+    comments = CommentField(
+        widget=SmallTextarea,
+        label='Comments'
+    )
 
 
     model = ASN
     model = ASN
     fieldsets = (
     fieldsets = (
         (None, ('sites', 'rir', 'tenant', 'description')),
         (None, ('sites', 'rir', 'tenant', 'description')),
     )
     )
-    nullable_fields = ('date_added', 'description')
+    nullable_fields = ('date_added', 'description', 'comments')
 
 
 
 
 class AggregateBulkEditForm(NetBoxModelBulkEditForm):
 class AggregateBulkEditForm(NetBoxModelBulkEditForm):
@@ -128,15 +140,19 @@ class AggregateBulkEditForm(NetBoxModelBulkEditForm):
         required=False
         required=False
     )
     )
     description = forms.CharField(
     description = forms.CharField(
-        max_length=100,
+        max_length=200,
         required=False
         required=False
     )
     )
+    comments = CommentField(
+        widget=SmallTextarea,
+        label='Comments'
+    )
 
 
     model = Aggregate
     model = Aggregate
     fieldsets = (
     fieldsets = (
         (None, ('rir', 'tenant', 'date_added', 'description')),
         (None, ('rir', 'tenant', 'date_added', 'description')),
     )
     )
-    nullable_fields = ('date_added', 'description')
+    nullable_fields = ('date_added', 'description', 'comments')
 
 
 
 
 class RoleBulkEditForm(NetBoxModelBulkEditForm):
 class RoleBulkEditForm(NetBoxModelBulkEditForm):
@@ -206,9 +222,13 @@ class PrefixBulkEditForm(NetBoxModelBulkEditForm):
         label='Treat as 100% utilized'
         label='Treat as 100% utilized'
     )
     )
     description = forms.CharField(
     description = forms.CharField(
-        max_length=100,
+        max_length=200,
         required=False
         required=False
     )
     )
+    comments = CommentField(
+        widget=SmallTextarea,
+        label='Comments'
+    )
 
 
     model = Prefix
     model = Prefix
     fieldsets = (
     fieldsets = (
@@ -217,7 +237,7 @@ class PrefixBulkEditForm(NetBoxModelBulkEditForm):
         ('Addressing', ('vrf', 'prefix_length', 'is_pool', 'mark_utilized')),
         ('Addressing', ('vrf', 'prefix_length', 'is_pool', 'mark_utilized')),
     )
     )
     nullable_fields = (
     nullable_fields = (
-        'site', 'vrf', 'tenant', 'role', 'description',
+        'site', 'vrf', 'tenant', 'role', 'description', 'comments',
     )
     )
 
 
 
 
@@ -241,16 +261,20 @@ class IPRangeBulkEditForm(NetBoxModelBulkEditForm):
         required=False
         required=False
     )
     )
     description = forms.CharField(
     description = forms.CharField(
-        max_length=100,
+        max_length=200,
         required=False
         required=False
     )
     )
+    comments = CommentField(
+        widget=SmallTextarea,
+        label='Comments'
+    )
 
 
     model = IPRange
     model = IPRange
     fieldsets = (
     fieldsets = (
         (None, ('status', 'role', 'vrf', 'tenant', 'description')),
         (None, ('status', 'role', 'vrf', 'tenant', 'description')),
     )
     )
     nullable_fields = (
     nullable_fields = (
-        'vrf', 'tenant', 'role', 'description',
+        'vrf', 'tenant', 'role', 'description', 'comments',
     )
     )
 
 
 
 
@@ -285,9 +309,13 @@ class IPAddressBulkEditForm(NetBoxModelBulkEditForm):
         label='DNS name'
         label='DNS name'
     )
     )
     description = forms.CharField(
     description = forms.CharField(
-        max_length=100,
+        max_length=200,
         required=False
         required=False
     )
     )
+    comments = CommentField(
+        widget=SmallTextarea,
+        label='Comments'
+    )
 
 
     model = IPAddress
     model = IPAddress
     fieldsets = (
     fieldsets = (
@@ -295,7 +323,7 @@ class IPAddressBulkEditForm(NetBoxModelBulkEditForm):
         ('Addressing', ('vrf', 'mask_length', 'dns_name')),
         ('Addressing', ('vrf', 'mask_length', 'dns_name')),
     )
     )
     nullable_fields = (
     nullable_fields = (
-        'vrf', 'role', 'tenant', 'dns_name', 'description',
+        'vrf', 'role', 'tenant', 'dns_name', 'description', 'comments',
     )
     )
 
 
 
 
@@ -329,13 +357,17 @@ class FHRPGroupBulkEditForm(NetBoxModelBulkEditForm):
         max_length=200,
         max_length=200,
         required=False
         required=False
     )
     )
+    comments = CommentField(
+        widget=SmallTextarea,
+        label='Comments'
+    )
 
 
     model = FHRPGroup
     model = FHRPGroup
     fieldsets = (
     fieldsets = (
         (None, ('protocol', 'group_id', 'name', 'description')),
         (None, ('protocol', 'group_id', 'name', 'description')),
         ('Authentication', ('auth_type', 'auth_key')),
         ('Authentication', ('auth_type', 'auth_key')),
     )
     )
-    nullable_fields = ('auth_type', 'auth_key', 'name', 'description')
+    nullable_fields = ('auth_type', 'auth_key', 'name', 'description', 'comments')
 
 
 
 
 class VLANGroupBulkEditForm(NetBoxModelBulkEditForm):
 class VLANGroupBulkEditForm(NetBoxModelBulkEditForm):
@@ -405,9 +437,13 @@ class VLANBulkEditForm(NetBoxModelBulkEditForm):
         required=False
         required=False
     )
     )
     description = forms.CharField(
     description = forms.CharField(
-        max_length=100,
+        max_length=200,
         required=False
         required=False
     )
     )
+    comments = CommentField(
+        widget=SmallTextarea,
+        label='Comments'
+    )
 
 
     model = VLAN
     model = VLAN
     fieldsets = (
     fieldsets = (
@@ -415,7 +451,7 @@ class VLANBulkEditForm(NetBoxModelBulkEditForm):
         ('Site & Group', ('region', 'site_group', 'site', 'group')),
         ('Site & Group', ('region', 'site_group', 'site', 'group')),
     )
     )
     nullable_fields = (
     nullable_fields = (
-        'site', 'group', 'tenant', 'role', 'description',
+        'site', 'group', 'tenant', 'role', 'description', 'comments',
     )
     )
 
 
 
 
@@ -433,15 +469,19 @@ class ServiceTemplateBulkEditForm(NetBoxModelBulkEditForm):
         required=False
         required=False
     )
     )
     description = forms.CharField(
     description = forms.CharField(
-        max_length=100,
+        max_length=200,
         required=False
         required=False
     )
     )
+    comments = CommentField(
+        widget=SmallTextarea,
+        label='Comments'
+    )
 
 
     model = ServiceTemplate
     model = ServiceTemplate
     fieldsets = (
     fieldsets = (
         (None, ('protocol', 'ports', 'description')),
         (None, ('protocol', 'ports', 'description')),
     )
     )
-    nullable_fields = ('description',)
+    nullable_fields = ('description', 'comments')
 
 
 
 
 class ServiceBulkEditForm(ServiceTemplateBulkEditForm):
 class ServiceBulkEditForm(ServiceTemplateBulkEditForm):
@@ -459,15 +499,19 @@ class L2VPNBulkEditForm(NetBoxModelBulkEditForm):
         required=False
         required=False
     )
     )
     description = forms.CharField(
     description = forms.CharField(
-        max_length=100,
+        max_length=200,
         required=False
         required=False
     )
     )
+    comments = CommentField(
+        widget=SmallTextarea,
+        label='Comments'
+    )
 
 
     model = L2VPN
     model = L2VPN
     fieldsets = (
     fieldsets = (
-        (None, ('type', 'description', 'tenant')),
+        (None, ('type', 'tenant', 'description')),
     )
     )
-    nullable_fields = ('tenant', 'description',)
+    nullable_fields = ('tenant', 'description', 'comments')
 
 
 
 
 class L2VPNTerminationBulkEditForm(NetBoxModelBulkEditForm):
 class L2VPNTerminationBulkEditForm(NetBoxModelBulkEditForm):

+ 12 - 12
netbox/ipam/forms/bulk_import.py

@@ -41,7 +41,7 @@ class VRFCSVForm(NetBoxModelCSVForm):
 
 
     class Meta:
     class Meta:
         model = VRF
         model = VRF
-        fields = ('name', 'rd', 'tenant', 'enforce_unique', 'description')
+        fields = ('name', 'rd', 'tenant', 'enforce_unique', 'description', 'comments')
 
 
 
 
 class RouteTargetCSVForm(NetBoxModelCSVForm):
 class RouteTargetCSVForm(NetBoxModelCSVForm):
@@ -54,7 +54,7 @@ class RouteTargetCSVForm(NetBoxModelCSVForm):
 
 
     class Meta:
     class Meta:
         model = RouteTarget
         model = RouteTarget
-        fields = ('name', 'description', 'tenant')
+        fields = ('name', 'tenant', 'description', 'comments')
 
 
 
 
 class RIRCSVForm(NetBoxModelCSVForm):
 class RIRCSVForm(NetBoxModelCSVForm):
@@ -83,7 +83,7 @@ class AggregateCSVForm(NetBoxModelCSVForm):
 
 
     class Meta:
     class Meta:
         model = Aggregate
         model = Aggregate
-        fields = ('prefix', 'rir', 'tenant', 'date_added', 'description')
+        fields = ('prefix', 'rir', 'tenant', 'date_added', 'description', 'comments')
 
 
 
 
 class ASNCSVForm(NetBoxModelCSVForm):
 class ASNCSVForm(NetBoxModelCSVForm):
@@ -101,7 +101,7 @@ class ASNCSVForm(NetBoxModelCSVForm):
 
 
     class Meta:
     class Meta:
         model = ASN
         model = ASN
-        fields = ('asn', 'rir', 'tenant', 'description')
+        fields = ('asn', 'rir', 'tenant', 'description', 'comments')
         help_texts = {}
         help_texts = {}
 
 
 
 
@@ -159,7 +159,7 @@ class PrefixCSVForm(NetBoxModelCSVForm):
         model = Prefix
         model = Prefix
         fields = (
         fields = (
             'prefix', 'vrf', 'tenant', 'site', 'vlan_group', 'vlan', 'status', 'role', 'is_pool', 'mark_utilized',
             'prefix', 'vrf', 'tenant', 'site', 'vlan_group', 'vlan', 'status', 'role', 'is_pool', 'mark_utilized',
-            'description',
+            'description', 'comments',
         )
         )
 
 
     def __init__(self, data=None, *args, **kwargs):
     def __init__(self, data=None, *args, **kwargs):
@@ -204,7 +204,7 @@ class IPRangeCSVForm(NetBoxModelCSVForm):
     class Meta:
     class Meta:
         model = IPRange
         model = IPRange
         fields = (
         fields = (
-            'start_address', 'end_address', 'vrf', 'tenant', 'status', 'role', 'description',
+            'start_address', 'end_address', 'vrf', 'tenant', 'status', 'role', 'description', 'comments',
         )
         )
 
 
 
 
@@ -257,7 +257,7 @@ class IPAddressCSVForm(NetBoxModelCSVForm):
         model = IPAddress
         model = IPAddress
         fields = [
         fields = [
             'address', 'vrf', 'tenant', 'status', 'role', 'device', 'virtual_machine', 'interface', 'is_primary',
             'address', 'vrf', 'tenant', 'status', 'role', 'device', 'virtual_machine', 'interface', 'is_primary',
-            'dns_name', 'description',
+            'dns_name', 'description', 'comments',
         ]
         ]
 
 
     def __init__(self, data=None, *args, **kwargs):
     def __init__(self, data=None, *args, **kwargs):
@@ -326,7 +326,7 @@ class FHRPGroupCSVForm(NetBoxModelCSVForm):
 
 
     class Meta:
     class Meta:
         model = FHRPGroup
         model = FHRPGroup
-        fields = ('protocol', 'group_id', 'auth_type', 'auth_key', 'name', 'description')
+        fields = ('protocol', 'group_id', 'auth_type', 'auth_key', 'name', 'description', 'comments')
 
 
 
 
 class VLANGroupCSVForm(NetBoxModelCSVForm):
 class VLANGroupCSVForm(NetBoxModelCSVForm):
@@ -389,7 +389,7 @@ class VLANCSVForm(NetBoxModelCSVForm):
 
 
     class Meta:
     class Meta:
         model = VLAN
         model = VLAN
-        fields = ('site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description')
+        fields = ('site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description', 'comments')
         help_texts = {
         help_texts = {
             'vid': 'Numeric VLAN ID (1-4094)',
             'vid': 'Numeric VLAN ID (1-4094)',
             'name': 'VLAN name',
             'name': 'VLAN name',
@@ -404,7 +404,7 @@ class ServiceTemplateCSVForm(NetBoxModelCSVForm):
 
 
     class Meta:
     class Meta:
         model = ServiceTemplate
         model = ServiceTemplate
-        fields = ('name', 'protocol', 'ports', 'description')
+        fields = ('name', 'protocol', 'ports', 'description', 'comments')
 
 
 
 
 class ServiceCSVForm(NetBoxModelCSVForm):
 class ServiceCSVForm(NetBoxModelCSVForm):
@@ -427,7 +427,7 @@ class ServiceCSVForm(NetBoxModelCSVForm):
 
 
     class Meta:
     class Meta:
         model = Service
         model = Service
-        fields = ('device', 'virtual_machine', 'name', 'protocol', 'ports', 'description')
+        fields = ('device', 'virtual_machine', 'name', 'protocol', 'ports', 'description', 'comments')
 
 
 
 
 class L2VPNCSVForm(NetBoxModelCSVForm):
 class L2VPNCSVForm(NetBoxModelCSVForm):
@@ -443,7 +443,7 @@ class L2VPNCSVForm(NetBoxModelCSVForm):
 
 
     class Meta:
     class Meta:
         model = L2VPN
         model = L2VPN
-        fields = ('identifier', 'name', 'slug', 'type', 'description')
+        fields = ('identifier', 'name', 'slug', 'type', 'description', 'comments')
 
 
 
 
 class L2VPNTerminationCSVForm(NetBoxModelCSVForm):
 class L2VPNTerminationCSVForm(NetBoxModelCSVForm):

+ 33 - 17
netbox/ipam/forms/model_forms.py

@@ -11,7 +11,7 @@ from netbox.forms import NetBoxModelForm
 from tenancy.forms import TenancyForm
 from tenancy.forms import TenancyForm
 from utilities.exceptions import PermissionsViolation
 from utilities.exceptions import PermissionsViolation
 from utilities.forms import (
 from utilities.forms import (
-    add_blank_choice, BootstrapMixin, ContentTypeChoiceField, DatePicker, DynamicModelChoiceField,
+    add_blank_choice, BootstrapMixin, CommentField, ContentTypeChoiceField, DatePicker, DynamicModelChoiceField,
     DynamicModelMultipleChoiceField, NumericArrayField, SlugField, StaticSelect, StaticSelectMultiple,
     DynamicModelMultipleChoiceField, NumericArrayField, SlugField, StaticSelect, StaticSelectMultiple,
 )
 )
 from virtualization.models import Cluster, ClusterGroup, VirtualMachine, VMInterface
 from virtualization.models import Cluster, ClusterGroup, VirtualMachine, VMInterface
@@ -49,6 +49,7 @@ class VRFForm(TenancyForm, NetBoxModelForm):
         queryset=RouteTarget.objects.all(),
         queryset=RouteTarget.objects.all(),
         required=False
         required=False
     )
     )
+    comments = CommentField()
 
 
     fieldsets = (
     fieldsets = (
         ('VRF', ('name', 'rd', 'enforce_unique', 'description', 'tags')),
         ('VRF', ('name', 'rd', 'enforce_unique', 'description', 'tags')),
@@ -59,8 +60,8 @@ class VRFForm(TenancyForm, NetBoxModelForm):
     class Meta:
     class Meta:
         model = VRF
         model = VRF
         fields = [
         fields = [
-            'name', 'rd', 'enforce_unique', 'description', 'import_targets', 'export_targets', 'tenant_group', 'tenant',
-            'tags',
+            'name', 'rd', 'enforce_unique', 'import_targets', 'export_targets', 'tenant_group', 'tenant', 'description',
+            'comments', 'tags',
         ]
         ]
         labels = {
         labels = {
             'rd': "RD",
             'rd': "RD",
@@ -75,11 +76,12 @@ class RouteTargetForm(TenancyForm, NetBoxModelForm):
         ('Route Target', ('name', 'description', 'tags')),
         ('Route Target', ('name', 'description', 'tags')),
         ('Tenancy', ('tenant_group', 'tenant')),
         ('Tenancy', ('tenant_group', 'tenant')),
     )
     )
+    comments = CommentField()
 
 
     class Meta:
     class Meta:
         model = RouteTarget
         model = RouteTarget
         fields = [
         fields = [
-            'name', 'description', 'tenant_group', 'tenant', 'tags',
+            'name', 'tenant_group', 'tenant', 'description', 'comments', 'tags',
         ]
         ]
 
 
 
 
@@ -104,6 +106,7 @@ class AggregateForm(TenancyForm, NetBoxModelForm):
         queryset=RIR.objects.all(),
         queryset=RIR.objects.all(),
         label='RIR'
         label='RIR'
     )
     )
+    comments = CommentField()
 
 
     fieldsets = (
     fieldsets = (
         ('Aggregate', ('prefix', 'rir', 'date_added', 'description', 'tags')),
         ('Aggregate', ('prefix', 'rir', 'date_added', 'description', 'tags')),
@@ -113,7 +116,7 @@ class AggregateForm(TenancyForm, NetBoxModelForm):
     class Meta:
     class Meta:
         model = Aggregate
         model = Aggregate
         fields = [
         fields = [
-            'prefix', 'rir', 'date_added', 'description', 'tenant_group', 'tenant', 'tags',
+            'prefix', 'rir', 'date_added', 'tenant_group', 'tenant', 'description', 'comments', 'tags',
         ]
         ]
         help_texts = {
         help_texts = {
             'prefix': "IPv4 or IPv6 network",
             'prefix': "IPv4 or IPv6 network",
@@ -134,6 +137,7 @@ class ASNForm(TenancyForm, NetBoxModelForm):
         label='Sites',
         label='Sites',
         required=False
         required=False
     )
     )
+    comments = CommentField()
 
 
     fieldsets = (
     fieldsets = (
         ('ASN', ('asn', 'rir', 'sites', 'description', 'tags')),
         ('ASN', ('asn', 'rir', 'sites', 'description', 'tags')),
@@ -143,7 +147,7 @@ class ASNForm(TenancyForm, NetBoxModelForm):
     class Meta:
     class Meta:
         model = ASN
         model = ASN
         fields = [
         fields = [
-            'asn', 'rir', 'sites', 'tenant_group', 'tenant', 'description', 'tags'
+            'asn', 'rir', 'sites', 'tenant_group', 'tenant', 'description', 'comments', 'tags'
         ]
         ]
         help_texts = {
         help_texts = {
             'asn': "AS number",
             'asn': "AS number",
@@ -235,6 +239,7 @@ class PrefixForm(TenancyForm, NetBoxModelForm):
         queryset=Role.objects.all(),
         queryset=Role.objects.all(),
         required=False
         required=False
     )
     )
+    comments = CommentField()
 
 
     fieldsets = (
     fieldsets = (
         ('Prefix', ('prefix', 'status', 'vrf', 'role', 'is_pool', 'mark_utilized', 'description', 'tags')),
         ('Prefix', ('prefix', 'status', 'vrf', 'role', 'is_pool', 'mark_utilized', 'description', 'tags')),
@@ -245,8 +250,8 @@ class PrefixForm(TenancyForm, NetBoxModelForm):
     class Meta:
     class Meta:
         model = Prefix
         model = Prefix
         fields = [
         fields = [
-            'prefix', 'vrf', 'site', 'vlan', 'status', 'role', 'is_pool', 'mark_utilized', 'description',
-            'tenant_group', 'tenant', 'tags',
+            'prefix', 'vrf', 'site', 'vlan', 'status', 'role', 'is_pool', 'mark_utilized', 'tenant_group', 'tenant',
+            'description', 'comments', 'tags',
         ]
         ]
         widgets = {
         widgets = {
             'status': StaticSelect(),
             'status': StaticSelect(),
@@ -263,6 +268,7 @@ class IPRangeForm(TenancyForm, NetBoxModelForm):
         queryset=Role.objects.all(),
         queryset=Role.objects.all(),
         required=False
         required=False
     )
     )
+    comments = CommentField()
 
 
     fieldsets = (
     fieldsets = (
         ('IP Range', ('vrf', 'start_address', 'end_address', 'role', 'status', 'description', 'tags')),
         ('IP Range', ('vrf', 'start_address', 'end_address', 'role', 'status', 'description', 'tags')),
@@ -272,7 +278,8 @@ class IPRangeForm(TenancyForm, NetBoxModelForm):
     class Meta:
     class Meta:
         model = IPRange
         model = IPRange
         fields = [
         fields = [
-            'vrf', 'start_address', 'end_address', 'status', 'role', 'description', 'tenant_group', 'tenant', 'tags',
+            'vrf', 'start_address', 'end_address', 'status', 'role', 'tenant_group', 'tenant', 'description',
+            'comments', 'tags',
         ]
         ]
         widgets = {
         widgets = {
             'status': StaticSelect(),
             'status': StaticSelect(),
@@ -394,13 +401,14 @@ class IPAddressForm(TenancyForm, NetBoxModelForm):
         required=False,
         required=False,
         label='Make this the primary IP for the device/VM'
         label='Make this the primary IP for the device/VM'
     )
     )
+    comments = CommentField()
 
 
     class Meta:
     class Meta:
         model = IPAddress
         model = IPAddress
         fields = [
         fields = [
-            'address', 'vrf', 'status', 'role', 'dns_name', 'description', 'primary_for_parent', 'nat_site', 'nat_rack',
-            'nat_device', 'nat_cluster', 'nat_virtual_machine', 'nat_vrf', 'nat_inside', 'tenant_group', 'tenant',
-            'tags',
+            'address', 'vrf', 'status', 'role', 'dns_name', 'primary_for_parent', 'nat_site', 'nat_rack', 'nat_device',
+            'nat_cluster', 'nat_virtual_machine', 'nat_vrf', 'nat_inside', 'tenant_group', 'tenant', 'description',
+            'comments', 'tags',
         ]
         ]
         widgets = {
         widgets = {
             'status': StaticSelect(),
             'status': StaticSelect(),
@@ -535,6 +543,7 @@ class FHRPGroupForm(NetBoxModelForm):
         required=False,
         required=False,
         label='Status'
         label='Status'
     )
     )
+    comments = CommentField()
 
 
     fieldsets = (
     fieldsets = (
         ('FHRP Group', ('protocol', 'group_id', 'name', 'description', 'tags')),
         ('FHRP Group', ('protocol', 'group_id', 'name', 'description', 'tags')),
@@ -545,7 +554,8 @@ class FHRPGroupForm(NetBoxModelForm):
     class Meta:
     class Meta:
         model = FHRPGroup
         model = FHRPGroup
         fields = (
         fields = (
-            'protocol', 'group_id', 'auth_type', 'auth_key', 'name', 'description', 'ip_vrf', 'ip_address', 'ip_status', 'tags',
+            'protocol', 'group_id', 'auth_type', 'auth_key', 'name', 'ip_vrf', 'ip_address', 'ip_status', 'description',
+            'comments', 'tags',
         )
         )
 
 
     def save(self, *args, **kwargs):
     def save(self, *args, **kwargs):
@@ -767,11 +777,13 @@ class VLANForm(TenancyForm, NetBoxModelForm):
         queryset=Role.objects.all(),
         queryset=Role.objects.all(),
         required=False
         required=False
     )
     )
+    comments = CommentField()
 
 
     class Meta:
     class Meta:
         model = VLAN
         model = VLAN
         fields = [
         fields = [
-            'site', 'group', 'vid', 'name', 'status', 'role', 'description', 'tenant_group', 'tenant', 'tags',
+            'site', 'group', 'vid', 'name', 'status', 'role', 'tenant_group', 'tenant', 'description', 'comments',
+            'tags',
         ]
         ]
         help_texts = {
         help_texts = {
             'site': "Leave blank if this VLAN spans multiple sites",
             'site': "Leave blank if this VLAN spans multiple sites",
@@ -794,6 +806,7 @@ class ServiceTemplateForm(NetBoxModelForm):
         ),
         ),
         help_text="Comma-separated list of one or more port numbers. A range may be specified using a hyphen."
         help_text="Comma-separated list of one or more port numbers. A range may be specified using a hyphen."
     )
     )
+    comments = CommentField()
 
 
     fieldsets = (
     fieldsets = (
         ('Service Template', (
         ('Service Template', (
@@ -803,7 +816,7 @@ class ServiceTemplateForm(NetBoxModelForm):
 
 
     class Meta:
     class Meta:
         model = ServiceTemplate
         model = ServiceTemplate
-        fields = ('name', 'protocol', 'ports', 'description', 'tags')
+        fields = ('name', 'protocol', 'ports', 'description', 'comments', 'tags')
         widgets = {
         widgets = {
             'protocol': StaticSelect(),
             'protocol': StaticSelect(),
         }
         }
@@ -834,11 +847,12 @@ class ServiceForm(NetBoxModelForm):
             'virtual_machine_id': '$virtual_machine',
             'virtual_machine_id': '$virtual_machine',
         }
         }
     )
     )
+    comments = CommentField()
 
 
     class Meta:
     class Meta:
         model = Service
         model = Service
         fields = [
         fields = [
-            'device', 'virtual_machine', 'name', 'protocol', 'ports', 'ipaddresses', 'description', 'tags',
+            'device', 'virtual_machine', 'name', 'protocol', 'ports', 'ipaddresses', 'description', 'comments', 'tags',
         ]
         ]
         help_texts = {
         help_texts = {
             'ipaddresses': "IP address assignment is optional. If no IPs are selected, the service is assumed to be "
             'ipaddresses': "IP address assignment is optional. If no IPs are selected, the service is assumed to be "
@@ -899,6 +913,7 @@ class L2VPNForm(TenancyForm, NetBoxModelForm):
         queryset=RouteTarget.objects.all(),
         queryset=RouteTarget.objects.all(),
         required=False
         required=False
     )
     )
+    comments = CommentField()
 
 
     fieldsets = (
     fieldsets = (
         ('L2VPN', ('name', 'slug', 'type', 'identifier', 'description', 'tags')),
         ('L2VPN', ('name', 'slug', 'type', 'identifier', 'description', 'tags')),
@@ -909,7 +924,8 @@ class L2VPNForm(TenancyForm, NetBoxModelForm):
     class Meta:
     class Meta:
         model = L2VPN
         model = L2VPN
         fields = (
         fields = (
-            'name', 'slug', 'type', 'identifier', 'description', 'import_targets', 'export_targets', 'tenant', 'tags'
+            'name', 'slug', 'type', 'identifier', 'import_targets', 'export_targets', 'tenant', 'description',
+            'comments', 'tags'
         )
         )
         widgets = {
         widgets = {
             'type': StaticSelect(),
             'type': StaticSelect(),

+ 73 - 0
netbox/ipam/migrations/0063_standardize_description_comments.py

@@ -0,0 +1,73 @@
+# Generated by Django 4.1.2 on 2022-11-03 18:24
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ipam', '0062_unique_constraints'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='aggregate',
+            name='comments',
+            field=models.TextField(blank=True),
+        ),
+        migrations.AddField(
+            model_name='asn',
+            name='comments',
+            field=models.TextField(blank=True),
+        ),
+        migrations.AddField(
+            model_name='fhrpgroup',
+            name='comments',
+            field=models.TextField(blank=True),
+        ),
+        migrations.AddField(
+            model_name='ipaddress',
+            name='comments',
+            field=models.TextField(blank=True),
+        ),
+        migrations.AddField(
+            model_name='iprange',
+            name='comments',
+            field=models.TextField(blank=True),
+        ),
+        migrations.AddField(
+            model_name='l2vpn',
+            name='comments',
+            field=models.TextField(blank=True),
+        ),
+        migrations.AddField(
+            model_name='prefix',
+            name='comments',
+            field=models.TextField(blank=True),
+        ),
+        migrations.AddField(
+            model_name='routetarget',
+            name='comments',
+            field=models.TextField(blank=True),
+        ),
+        migrations.AddField(
+            model_name='service',
+            name='comments',
+            field=models.TextField(blank=True),
+        ),
+        migrations.AddField(
+            model_name='servicetemplate',
+            name='comments',
+            field=models.TextField(blank=True),
+        ),
+        migrations.AddField(
+            model_name='vlan',
+            name='comments',
+            field=models.TextField(blank=True),
+        ),
+        migrations.AddField(
+            model_name='vrf',
+            name='comments',
+            field=models.TextField(blank=True),
+        ),
+    ]

+ 2 - 6
netbox/ipam/models/fhrp.py

@@ -4,7 +4,7 @@ from django.core.validators import MaxValueValidator, MinValueValidator
 from django.db import models
 from django.db import models
 from django.urls import reverse
 from django.urls import reverse
 
 
-from netbox.models import ChangeLoggedModel, NetBoxModel
+from netbox.models import ChangeLoggedModel, PrimaryModel
 from netbox.models.features import WebhooksMixin
 from netbox.models.features import WebhooksMixin
 from ipam.choices import *
 from ipam.choices import *
 from ipam.constants import *
 from ipam.constants import *
@@ -15,7 +15,7 @@ __all__ = (
 )
 )
 
 
 
 
-class FHRPGroup(NetBoxModel):
+class FHRPGroup(PrimaryModel):
     """
     """
     A grouping of next hope resolution protocol (FHRP) peers. (For instance, VRRP or HSRP.)
     A grouping of next hope resolution protocol (FHRP) peers. (For instance, VRRP or HSRP.)
     """
     """
@@ -41,10 +41,6 @@ class FHRPGroup(NetBoxModel):
         blank=True,
         blank=True,
         verbose_name='Authentication key'
         verbose_name='Authentication key'
     )
     )
-    description = models.CharField(
-        max_length=200,
-        blank=True
-    )
     ip_addresses = GenericRelation(
     ip_addresses = GenericRelation(
         to='ipam.IPAddress',
         to='ipam.IPAddress',
         content_type_field='assigned_object_type',
         content_type_field='assigned_object_type',

+ 6 - 26
netbox/ipam/models/ip.py

@@ -9,7 +9,7 @@ from django.utils.functional import cached_property
 
 
 from dcim.fields import ASNField
 from dcim.fields import ASNField
 from dcim.models import Device
 from dcim.models import Device
-from netbox.models import OrganizationalModel, NetBoxModel
+from netbox.models import OrganizationalModel, PrimaryModel
 from ipam.choices import *
 from ipam.choices import *
 from ipam.constants import *
 from ipam.constants import *
 from ipam.fields import IPNetworkField, IPAddressField
 from ipam.fields import IPNetworkField, IPAddressField
@@ -76,7 +76,7 @@ class RIR(OrganizationalModel):
         return reverse('ipam:rir', args=[self.pk])
         return reverse('ipam:rir', args=[self.pk])
 
 
 
 
-class ASN(NetBoxModel):
+class ASN(PrimaryModel):
     """
     """
     An autonomous system (AS) number is typically used to represent an independent routing domain. A site can have
     An autonomous system (AS) number is typically used to represent an independent routing domain. A site can have
     one or more ASNs assigned to it.
     one or more ASNs assigned to it.
@@ -86,10 +86,6 @@ class ASN(NetBoxModel):
         verbose_name='ASN',
         verbose_name='ASN',
         help_text='32-bit autonomous system number'
         help_text='32-bit autonomous system number'
     )
     )
-    description = models.CharField(
-        max_length=200,
-        blank=True
-    )
     rir = models.ForeignKey(
     rir = models.ForeignKey(
         to='ipam.RIR',
         to='ipam.RIR',
         on_delete=models.PROTECT,
         on_delete=models.PROTECT,
@@ -139,7 +135,7 @@ class ASN(NetBoxModel):
             return self.asn
             return self.asn
 
 
 
 
-class Aggregate(GetAvailablePrefixesMixin, NetBoxModel):
+class Aggregate(GetAvailablePrefixesMixin, PrimaryModel):
     """
     """
     An aggregate exists at the root level of the IP address space hierarchy in NetBox. Aggregates are used to organize
     An aggregate exists at the root level of the IP address space hierarchy in NetBox. Aggregates are used to organize
     the hierarchy and track the overall utilization of available address space. Each Aggregate is assigned to a RIR.
     the hierarchy and track the overall utilization of available address space. Each Aggregate is assigned to a RIR.
@@ -162,10 +158,6 @@ class Aggregate(GetAvailablePrefixesMixin, NetBoxModel):
         blank=True,
         blank=True,
         null=True
         null=True
     )
     )
-    description = models.CharField(
-        max_length=200,
-        blank=True
-    )
 
 
     clone_fields = (
     clone_fields = (
         'rir', 'tenant', 'date_added', 'description',
         'rir', 'tenant', 'date_added', 'description',
@@ -264,7 +256,7 @@ class Role(OrganizationalModel):
         return reverse('ipam:role', args=[self.pk])
         return reverse('ipam:role', args=[self.pk])
 
 
 
 
-class Prefix(GetAvailablePrefixesMixin, NetBoxModel):
+class Prefix(GetAvailablePrefixesMixin, PrimaryModel):
     """
     """
     A Prefix represents an IPv4 or IPv6 network, including mask length. Prefixes can optionally be assigned to Sites and
     A Prefix represents an IPv4 or IPv6 network, including mask length. Prefixes can optionally be assigned to Sites and
     VRFs. A Prefix must be assigned a status and may optionally be assigned a used-define Role. A Prefix can also be
     VRFs. A Prefix must be assigned a status and may optionally be assigned a used-define Role. A Prefix can also be
@@ -327,10 +319,6 @@ class Prefix(GetAvailablePrefixesMixin, NetBoxModel):
         default=False,
         default=False,
         help_text="Treat as 100% utilized"
         help_text="Treat as 100% utilized"
     )
     )
-    description = models.CharField(
-        max_length=200,
-        blank=True
-    )
 
 
     # Cached depth & child counts
     # Cached depth & child counts
     _depth = models.PositiveSmallIntegerField(
     _depth = models.PositiveSmallIntegerField(
@@ -545,7 +533,7 @@ class Prefix(GetAvailablePrefixesMixin, NetBoxModel):
         return min(utilization, 100)
         return min(utilization, 100)
 
 
 
 
-class IPRange(NetBoxModel):
+class IPRange(PrimaryModel):
     """
     """
     A range of IP addresses, defined by start and end addresses.
     A range of IP addresses, defined by start and end addresses.
     """
     """
@@ -587,10 +575,6 @@ class IPRange(NetBoxModel):
         null=True,
         null=True,
         help_text='The primary function of this range'
         help_text='The primary function of this range'
     )
     )
-    description = models.CharField(
-        max_length=200,
-        blank=True
-    )
 
 
     clone_fields = (
     clone_fields = (
         'vrf', 'tenant', 'status', 'role', 'description',
         'vrf', 'tenant', 'status', 'role', 'description',
@@ -740,7 +724,7 @@ class IPRange(NetBoxModel):
         return int(float(child_count) / self.size * 100)
         return int(float(child_count) / self.size * 100)
 
 
 
 
-class IPAddress(NetBoxModel):
+class IPAddress(PrimaryModel):
     """
     """
     An IPAddress represents an individual IPv4 or IPv6 address and its mask. The mask length should match what is
     An IPAddress represents an individual IPv4 or IPv6 address and its mask. The mask length should match what is
     configured in the real world. (Typically, only loopback interfaces are configured with /32 or /128 masks.) Like
     configured in the real world. (Typically, only loopback interfaces are configured with /32 or /128 masks.) Like
@@ -813,10 +797,6 @@ class IPAddress(NetBoxModel):
         verbose_name='DNS Name',
         verbose_name='DNS Name',
         help_text='Hostname or FQDN (not case-sensitive)'
         help_text='Hostname or FQDN (not case-sensitive)'
     )
     )
-    description = models.CharField(
-        max_length=200,
-        blank=True
-    )
 
 
     objects = IPAddressManager()
     objects = IPAddressManager()
 
 

+ 2 - 6
netbox/ipam/models/l2vpn.py

@@ -8,7 +8,7 @@ from django.utils.functional import cached_property
 
 
 from ipam.choices import L2VPNTypeChoices
 from ipam.choices import L2VPNTypeChoices
 from ipam.constants import L2VPN_ASSIGNMENT_MODELS
 from ipam.constants import L2VPN_ASSIGNMENT_MODELS
-from netbox.models import NetBoxModel
+from netbox.models import NetBoxModel, PrimaryModel
 
 
 __all__ = (
 __all__ = (
     'L2VPN',
     'L2VPN',
@@ -16,7 +16,7 @@ __all__ = (
 )
 )
 
 
 
 
-class L2VPN(NetBoxModel):
+class L2VPN(PrimaryModel):
     name = models.CharField(
     name = models.CharField(
         max_length=100,
         max_length=100,
         unique=True
         unique=True
@@ -43,10 +43,6 @@ class L2VPN(NetBoxModel):
         related_name='exporting_l2vpns',
         related_name='exporting_l2vpns',
         blank=True
         blank=True
     )
     )
-    description = models.CharField(
-        max_length=200,
-        blank=True
-    )
     tenant = models.ForeignKey(
     tenant = models.ForeignKey(
         to='tenancy.Tenant',
         to='tenancy.Tenant',
         on_delete=models.PROTECT,
         on_delete=models.PROTECT,

+ 3 - 7
netbox/ipam/models/services.py

@@ -6,7 +6,7 @@ from django.urls import reverse
 
 
 from ipam.choices import *
 from ipam.choices import *
 from ipam.constants import *
 from ipam.constants import *
-from netbox.models import NetBoxModel
+from netbox.models import PrimaryModel
 from utilities.utils import array_to_string
 from utilities.utils import array_to_string
 
 
 
 
@@ -30,10 +30,6 @@ class ServiceBase(models.Model):
         ),
         ),
         verbose_name='Port numbers'
         verbose_name='Port numbers'
     )
     )
-    description = models.CharField(
-        max_length=200,
-        blank=True
-    )
 
 
     class Meta:
     class Meta:
         abstract = True
         abstract = True
@@ -46,7 +42,7 @@ class ServiceBase(models.Model):
         return array_to_string(self.ports)
         return array_to_string(self.ports)
 
 
 
 
-class ServiceTemplate(ServiceBase, NetBoxModel):
+class ServiceTemplate(ServiceBase, PrimaryModel):
     """
     """
     A template for a Service to be applied to a device or virtual machine.
     A template for a Service to be applied to a device or virtual machine.
     """
     """
@@ -62,7 +58,7 @@ class ServiceTemplate(ServiceBase, NetBoxModel):
         return reverse('ipam:servicetemplate', args=[self.pk])
         return reverse('ipam:servicetemplate', args=[self.pk])
 
 
 
 
-class Service(ServiceBase, NetBoxModel):
+class Service(ServiceBase, PrimaryModel):
     """
     """
     A Service represents a layer-four service (e.g. HTTP or SSH) running on a Device or VirtualMachine. A Service may
     A Service represents a layer-four service (e.g. HTTP or SSH) running on a Device or VirtualMachine. A Service may
     optionally be tied to one or more specific IPAddresses belonging to its parent.
     optionally be tied to one or more specific IPAddresses belonging to its parent.

+ 2 - 12
netbox/ipam/models/vlans.py

@@ -8,12 +8,10 @@ from django.urls import reverse
 from dcim.models import Interface
 from dcim.models import Interface
 from ipam.choices import *
 from ipam.choices import *
 from ipam.constants import *
 from ipam.constants import *
-from ipam.models import L2VPNTermination
 from ipam.querysets import VLANQuerySet
 from ipam.querysets import VLANQuerySet
-from netbox.models import OrganizationalModel, NetBoxModel
+from netbox.models import OrganizationalModel, PrimaryModel
 from virtualization.models import VMInterface
 from virtualization.models import VMInterface
 
 
-
 __all__ = (
 __all__ = (
     'VLAN',
     'VLAN',
     'VLANGroup',
     'VLANGroup',
@@ -63,10 +61,6 @@ class VLANGroup(OrganizationalModel):
         ),
         ),
         help_text='Highest permissible ID of a child VLAN'
         help_text='Highest permissible ID of a child VLAN'
     )
     )
-    description = models.CharField(
-        max_length=200,
-        blank=True
-    )
 
 
     class Meta:
     class Meta:
         ordering = ('name', 'pk')  # Name may be non-unique
         ordering = ('name', 'pk')  # Name may be non-unique
@@ -120,7 +114,7 @@ class VLANGroup(OrganizationalModel):
         return None
         return None
 
 
 
 
-class VLAN(NetBoxModel):
+class VLAN(PrimaryModel):
     """
     """
     A VLAN is a distinct layer two forwarding domain identified by a 12-bit integer (1-4094). Each VLAN must be assigned
     A VLAN is a distinct layer two forwarding domain identified by a 12-bit integer (1-4094). Each VLAN must be assigned
     to a Site, however VLAN IDs need not be unique within a Site. A VLAN may optionally be assigned to a VLANGroup,
     to a Site, however VLAN IDs need not be unique within a Site. A VLAN may optionally be assigned to a VLANGroup,
@@ -172,10 +166,6 @@ class VLAN(NetBoxModel):
         blank=True,
         blank=True,
         null=True
         null=True
     )
     )
-    description = models.CharField(
-        max_length=200,
-        blank=True
-    )
 
 
     l2vpn_terminations = GenericRelation(
     l2vpn_terminations = GenericRelation(
         to='ipam.L2VPNTermination',
         to='ipam.L2VPNTermination',

+ 3 - 11
netbox/ipam/models/vrfs.py

@@ -2,7 +2,7 @@ from django.db import models
 from django.urls import reverse
 from django.urls import reverse
 
 
 from ipam.constants import *
 from ipam.constants import *
-from netbox.models import NetBoxModel
+from netbox.models import PrimaryModel
 
 
 
 
 __all__ = (
 __all__ = (
@@ -11,7 +11,7 @@ __all__ = (
 )
 )
 
 
 
 
-class VRF(NetBoxModel):
+class VRF(PrimaryModel):
     """
     """
     A virtual routing and forwarding (VRF) table represents a discrete layer three forwarding domain (e.g. a routing
     A virtual routing and forwarding (VRF) table represents a discrete layer three forwarding domain (e.g. a routing
     table). Prefixes and IPAddresses can optionally be assigned to VRFs. (Prefixes and IPAddresses not assigned to a VRF
     table). Prefixes and IPAddresses can optionally be assigned to VRFs. (Prefixes and IPAddresses not assigned to a VRF
@@ -40,10 +40,6 @@ class VRF(NetBoxModel):
         verbose_name='Enforce unique space',
         verbose_name='Enforce unique space',
         help_text='Prevent duplicate prefixes/IP addresses within this VRF'
         help_text='Prevent duplicate prefixes/IP addresses within this VRF'
     )
     )
-    description = models.CharField(
-        max_length=200,
-        blank=True
-    )
     import_targets = models.ManyToManyField(
     import_targets = models.ManyToManyField(
         to='ipam.RouteTarget',
         to='ipam.RouteTarget',
         related_name='importing_vrfs',
         related_name='importing_vrfs',
@@ -73,7 +69,7 @@ class VRF(NetBoxModel):
         return reverse('ipam:vrf', args=[self.pk])
         return reverse('ipam:vrf', args=[self.pk])
 
 
 
 
-class RouteTarget(NetBoxModel):
+class RouteTarget(PrimaryModel):
     """
     """
     A BGP extended community used to control the redistribution of routes among VRFs, as defined in RFC 4364.
     A BGP extended community used to control the redistribution of routes among VRFs, as defined in RFC 4364.
     """
     """
@@ -82,10 +78,6 @@ class RouteTarget(NetBoxModel):
         unique=True,
         unique=True,
         help_text='Route target value (formatted in accordance with RFC 4360)'
         help_text='Route target value (formatted in accordance with RFC 4360)'
     )
     )
-    description = models.CharField(
-        max_length=200,
-        blank=True
-    )
     tenant = models.ForeignKey(
     tenant = models.ForeignKey(
         to='tenancy.Tenant',
         to='tenancy.Tenant',
         on_delete=models.PROTECT,
         on_delete=models.PROTECT,

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

@@ -20,7 +20,6 @@ class FHRPGroupTable(NetBoxTable):
     group_id = tables.Column(
     group_id = tables.Column(
         linkify=True
         linkify=True
     )
     )
-    comments = columns.MarkdownColumn()
     ip_addresses = tables.TemplateColumn(
     ip_addresses = tables.TemplateColumn(
         template_code=IPADDRESSES,
         template_code=IPADDRESSES,
         orderable=False,
         orderable=False,
@@ -29,6 +28,7 @@ class FHRPGroupTable(NetBoxTable):
     member_count = tables.Column(
     member_count = tables.Column(
         verbose_name='Members'
         verbose_name='Members'
     )
     )
+    comments = columns.MarkdownColumn()
     tags = columns.TagColumn(
     tags = columns.TagColumn(
         url_name='ipam:fhrpgroup_list'
         url_name='ipam:fhrpgroup_list'
     )
     )
@@ -36,7 +36,7 @@ class FHRPGroupTable(NetBoxTable):
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
         model = FHRPGroup
         model = FHRPGroup
         fields = (
         fields = (
-            'pk', 'group_id', 'protocol', 'name', 'auth_type', 'auth_key', 'description', 'ip_addresses',
+            'pk', 'group_id', 'protocol', 'name', 'auth_type', 'auth_key', 'description', 'comments', 'ip_addresses',
             'member_count', 'tags', 'created', 'last_updated',
             'member_count', 'tags', 'created', 'last_updated',
         )
         )
         default_columns = (
         default_columns = (

+ 16 - 10
netbox/ipam/tables/ip.py

@@ -120,6 +120,7 @@ class ASNTable(TenancyColumnsMixin, NetBoxTable):
         linkify_item=True,
         linkify_item=True,
         verbose_name='Sites'
         verbose_name='Sites'
     )
     )
+    comments = columns.MarkdownColumn()
     tags = columns.TagColumn(
     tags = columns.TagColumn(
         url_name='ipam:asn_list'
         url_name='ipam:asn_list'
     )
     )
@@ -127,8 +128,8 @@ class ASNTable(TenancyColumnsMixin, NetBoxTable):
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
         model = ASN
         model = ASN
         fields = (
         fields = (
-            'pk', 'asn', 'asn_asdot', 'rir', 'site_count', 'provider_count', 'tenant', 'tenant_group', 'description', 'sites', 'tags',
-            'created', 'last_updated', 'actions',
+            'pk', 'asn', 'asn_asdot', 'rir', 'site_count', 'provider_count', 'tenant', 'tenant_group', 'description',
+            'comments', 'sites', 'tags', 'created', 'last_updated', 'actions',
         )
         )
         default_columns = ('pk', 'asn', 'rir', 'site_count', 'provider_count', 'sites', 'description', 'tenant')
         default_columns = ('pk', 'asn', 'rir', 'site_count', 'provider_count', 'sites', 'description', 'tenant')
 
 
@@ -153,6 +154,7 @@ class AggregateTable(TenancyColumnsMixin, NetBoxTable):
         accessor='get_utilization',
         accessor='get_utilization',
         orderable=False
         orderable=False
     )
     )
+    comments = columns.MarkdownColumn()
     tags = columns.TagColumn(
     tags = columns.TagColumn(
         url_name='ipam:aggregate_list'
         url_name='ipam:aggregate_list'
     )
     )
@@ -160,8 +162,8 @@ class AggregateTable(TenancyColumnsMixin, NetBoxTable):
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
         model = Aggregate
         model = Aggregate
         fields = (
         fields = (
-            'pk', 'id', 'prefix', 'rir', 'tenant', 'tenant_group', 'child_count', 'utilization', 'date_added', 'description', 'tags',
-            'created', 'last_updated',
+            'pk', 'id', 'prefix', 'rir', 'tenant', 'tenant_group', 'child_count', 'utilization', 'date_added',
+            'description', 'comments', 'tags', 'created', 'last_updated',
         )
         )
         default_columns = ('pk', 'prefix', 'rir', 'tenant', 'child_count', 'utilization', 'date_added', 'description')
         default_columns = ('pk', 'prefix', 'rir', 'tenant', 'child_count', 'utilization', 'date_added', 'description')
 
 
@@ -278,6 +280,7 @@ class PrefixTable(TenancyColumnsMixin, NetBoxTable):
         accessor='get_utilization',
         accessor='get_utilization',
         orderable=False
         orderable=False
     )
     )
+    comments = columns.MarkdownColumn()
     tags = columns.TagColumn(
     tags = columns.TagColumn(
         url_name='ipam:prefix_list'
         url_name='ipam:prefix_list'
     )
     )
@@ -285,8 +288,9 @@ class PrefixTable(TenancyColumnsMixin, NetBoxTable):
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
         model = Prefix
         model = Prefix
         fields = (
         fields = (
-            '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',
+            'pk', 'id', 'prefix', 'prefix_flat', 'status', 'children', 'vrf', 'utilization', 'tenant', 'tenant_group',
+            'site', 'vlan_group', 'vlan', 'role', 'is_pool', 'mark_utilized', 'description', 'comments', 'tags',
+            'created', 'last_updated',
         )
         )
         default_columns = (
         default_columns = (
             'pk', 'prefix', 'status', 'children', 'vrf', 'utilization', 'tenant', 'site', 'vlan', 'role', 'description',
             'pk', 'prefix', 'status', 'children', 'vrf', 'utilization', 'tenant', 'site', 'vlan', 'role', 'description',
@@ -317,6 +321,7 @@ class IPRangeTable(TenancyColumnsMixin, NetBoxTable):
         accessor='utilization',
         accessor='utilization',
         orderable=False
         orderable=False
     )
     )
+    comments = columns.MarkdownColumn()
     tags = columns.TagColumn(
     tags = columns.TagColumn(
         url_name='ipam:iprange_list'
         url_name='ipam:iprange_list'
     )
     )
@@ -324,8 +329,8 @@ class IPRangeTable(TenancyColumnsMixin, NetBoxTable):
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
         model = IPRange
         model = IPRange
         fields = (
         fields = (
-            'pk', 'id', 'start_address', 'end_address', 'size', 'vrf', 'status', 'role', 'tenant', 'tenant_group', 'description',
-            'utilization', 'tags', 'created', 'last_updated',
+            'pk', 'id', 'start_address', 'end_address', 'size', 'vrf', 'status', 'role', 'tenant', 'tenant_group',
+            'utilization', 'description', 'comments', 'tags', 'created', 'last_updated',
         )
         )
         default_columns = (
         default_columns = (
             'pk', 'start_address', 'end_address', 'size', 'vrf', 'status', 'role', 'tenant', 'description',
             'pk', 'start_address', 'end_address', 'size', 'vrf', 'status', 'role', 'tenant', 'description',
@@ -378,6 +383,7 @@ class IPAddressTable(TenancyColumnsMixin, NetBoxTable):
         linkify=lambda record: record.assigned_object.get_absolute_url(),
         linkify=lambda record: record.assigned_object.get_absolute_url(),
         verbose_name='Assigned'
         verbose_name='Assigned'
     )
     )
+    comments = columns.MarkdownColumn()
     tags = columns.TagColumn(
     tags = columns.TagColumn(
         url_name='ipam:ipaddress_list'
         url_name='ipam:ipaddress_list'
     )
     )
@@ -385,8 +391,8 @@ class IPAddressTable(TenancyColumnsMixin, NetBoxTable):
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
         model = IPAddress
         model = IPAddress
         fields = (
         fields = (
-            'pk', 'id', 'address', 'vrf', 'status', 'role', 'tenant', 'tenant_group', 'nat_inside', 'nat_outside', 'assigned', 'dns_name', 'description',
-            'tags', 'created', 'last_updated',
+            'pk', 'id', 'address', 'vrf', 'status', 'role', 'tenant', 'tenant_group', 'nat_inside', 'nat_outside',
+            'assigned', 'dns_name', 'description', 'comments', 'tags', 'created', 'last_updated',
         )
         )
         default_columns = (
         default_columns = (
             'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'assigned', 'dns_name', 'description',
             'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'assigned', 'dns_name', 'description',

+ 6 - 2
netbox/ipam/tables/l2vpn.py

@@ -29,12 +29,16 @@ class L2VPNTable(TenancyColumnsMixin, NetBoxTable):
         template_code=L2VPN_TARGETS,
         template_code=L2VPN_TARGETS,
         orderable=False
         orderable=False
     )
     )
+    comments = columns.MarkdownColumn()
+    tags = columns.TagColumn(
+        url_name='ipam:prefix_list'
+    )
 
 
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
         model = L2VPN
         model = L2VPN
         fields = (
         fields = (
-            'pk', 'name', 'slug', 'identifier', 'type', 'description', 'import_targets', 'export_targets', 'tenant', 'tenant_group',
-            'actions',
+            'pk', 'name', 'slug', 'identifier', 'type', 'import_targets', 'export_targets', 'tenant', 'tenant_group',
+            'description', 'comments', 'tags', 'created', 'last_updated', 'actions',
         )
         )
         default_columns = ('pk', 'name', 'identifier', 'type', 'description', 'actions')
         default_columns = ('pk', 'name', 'identifier', 'type', 'description', 'actions')
 
 

+ 7 - 3
netbox/ipam/tables/services.py

@@ -17,13 +17,16 @@ class ServiceTemplateTable(NetBoxTable):
         accessor=tables.A('port_list'),
         accessor=tables.A('port_list'),
         order_by=tables.A('ports'),
         order_by=tables.A('ports'),
     )
     )
+    comments = columns.MarkdownColumn()
     tags = columns.TagColumn(
     tags = columns.TagColumn(
         url_name='ipam:servicetemplate_list'
         url_name='ipam:servicetemplate_list'
     )
     )
 
 
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
         model = ServiceTemplate
         model = ServiceTemplate
-        fields = ('pk', 'id', 'name', 'protocol', 'ports', 'description', 'tags')
+        fields = (
+            'pk', 'id', 'name', 'protocol', 'ports', 'description', 'comments', 'tags', 'created', 'last_updated',
+        )
         default_columns = ('pk', 'name', 'protocol', 'ports', 'description')
         default_columns = ('pk', 'name', 'protocol', 'ports', 'description')
 
 
 
 
@@ -39,6 +42,7 @@ class ServiceTable(NetBoxTable):
         accessor=tables.A('port_list'),
         accessor=tables.A('port_list'),
         order_by=tables.A('ports'),
         order_by=tables.A('ports'),
     )
     )
+    comments = columns.MarkdownColumn()
     tags = columns.TagColumn(
     tags = columns.TagColumn(
         url_name='ipam:service_list'
         url_name='ipam:service_list'
     )
     )
@@ -46,7 +50,7 @@ class ServiceTable(NetBoxTable):
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
         model = Service
         model = Service
         fields = (
         fields = (
-            'pk', 'id', 'name', 'parent', 'protocol', 'ports', 'ipaddresses', 'description', 'tags', 'created',
-            'last_updated',
+            'pk', 'id', 'name', 'parent', 'protocol', 'ports', 'ipaddresses', 'description', 'comments', 'tags',
+            'created', 'last_updated',
         )
         )
         default_columns = ('pk', 'name', 'parent', 'protocol', 'ports', 'description')
         default_columns = ('pk', 'name', 'parent', 'protocol', 'ports', 'description')

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

@@ -121,6 +121,7 @@ class VLANTable(TenancyColumnsMixin, NetBoxTable):
         orderable=False,
         orderable=False,
         verbose_name='Prefixes'
         verbose_name='Prefixes'
     )
     )
+    comments = columns.MarkdownColumn()
     tags = columns.TagColumn(
     tags = columns.TagColumn(
         url_name='ipam:vlan_list'
         url_name='ipam:vlan_list'
     )
     )
@@ -129,7 +130,7 @@ class VLANTable(TenancyColumnsMixin, NetBoxTable):
         model = VLAN
         model = VLAN
         fields = (
         fields = (
             'pk', 'id', 'vid', 'name', 'site', 'group', 'prefixes', 'tenant', 'tenant_group', 'status', 'role',
             'pk', 'id', 'vid', 'name', 'site', 'group', 'prefixes', 'tenant', 'tenant_group', 'status', 'role',
-            'description', 'tags', 'l2vpn', 'created', 'last_updated',
+            'description', 'comments', 'tags', 'l2vpn', 'created', 'last_updated',
         )
         )
         default_columns = ('pk', 'vid', 'name', 'site', 'group', 'prefixes', 'tenant', 'status', 'role', 'description')
         default_columns = ('pk', 'vid', 'name', 'site', 'group', 'prefixes', 'tenant', 'status', 'role', 'description')
         row_attrs = {
         row_attrs = {

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

@@ -38,6 +38,7 @@ class VRFTable(TenancyColumnsMixin, NetBoxTable):
         template_code=VRF_TARGETS,
         template_code=VRF_TARGETS,
         orderable=False
         orderable=False
     )
     )
+    comments = columns.MarkdownColumn()
     tags = columns.TagColumn(
     tags = columns.TagColumn(
         url_name='ipam:vrf_list'
         url_name='ipam:vrf_list'
     )
     )
@@ -45,8 +46,8 @@ class VRFTable(TenancyColumnsMixin, NetBoxTable):
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
         model = VRF
         model = VRF
         fields = (
         fields = (
-            'pk', 'id', 'name', 'rd', 'tenant', 'tenant_group', 'enforce_unique', 'description', 'import_targets', 'export_targets',
-            'tags', 'created', 'last_updated',
+            'pk', 'id', 'name', 'rd', 'tenant', 'tenant_group', 'enforce_unique', 'import_targets', 'export_targets',
+            'description', 'comments', 'tags', 'created', 'last_updated',
         )
         )
         default_columns = ('pk', 'name', 'rd', 'tenant', 'description')
         default_columns = ('pk', 'name', 'rd', 'tenant', 'description')
 
 
@@ -59,11 +60,14 @@ class RouteTargetTable(TenancyColumnsMixin, NetBoxTable):
     name = tables.Column(
     name = tables.Column(
         linkify=True
         linkify=True
     )
     )
+    comments = columns.MarkdownColumn()
     tags = columns.TagColumn(
     tags = columns.TagColumn(
         url_name='ipam:vrf_list'
         url_name='ipam:vrf_list'
     )
     )
 
 
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
         model = RouteTarget
         model = RouteTarget
-        fields = ('pk', 'id', 'name', 'tenant', 'tenant_group', 'description', 'tags', 'created', 'last_updated',)
+        fields = (
+            'pk', 'id', 'name', 'tenant', 'tenant_group', 'description', 'comments', 'tags', 'created', 'last_updated',
+        )
         default_columns = ('pk', 'name', 'tenant', 'description')
         default_columns = ('pk', 'name', 'tenant', 'description')

+ 19 - 2
netbox/netbox/models/__init__.py

@@ -10,8 +10,9 @@ from netbox.models.features import *
 __all__ = (
 __all__ = (
     'ChangeLoggedModel',
     'ChangeLoggedModel',
     'NestedGroupModel',
     'NestedGroupModel',
-    'OrganizationalModel',
     'NetBoxModel',
     'NetBoxModel',
+    'OrganizationalModel',
+    'PrimaryModel',
 )
 )
 
 
 
 
@@ -58,7 +59,7 @@ class ChangeLoggedModel(ChangeLoggingMixin, CustomValidationMixin, models.Model)
 
 
 class NetBoxModel(CloningMixin, NetBoxFeatureSet, models.Model):
 class NetBoxModel(CloningMixin, NetBoxFeatureSet, models.Model):
     """
     """
-    Primary models represent real objects within the infrastructure being modeled.
+    Base model for most object types. Suitable for use by plugins.
     """
     """
     objects = RestrictedQuerySet.as_manager()
     objects = RestrictedQuerySet.as_manager()
 
 
@@ -66,6 +67,22 @@ class NetBoxModel(CloningMixin, NetBoxFeatureSet, models.Model):
         abstract = True
         abstract = True
 
 
 
 
+class PrimaryModel(NetBoxModel):
+    """
+    Primary models represent real objects within the infrastructure being modeled.
+    """
+    description = models.CharField(
+        max_length=200,
+        blank=True
+    )
+    comments = models.TextField(
+        blank=True
+    )
+
+    class Meta:
+        abstract = True
+
+
 class NestedGroupModel(NetBoxFeatureSet, MPTTModel):
 class NestedGroupModel(NetBoxFeatureSet, MPTTModel):
     """
     """
     Base model for objects which are used to form a hierarchy (regions, locations, etc.). These models nest
     Base model for objects which are used to form a hierarchy (regions, locations, etc.). These models nest

+ 4 - 0
netbox/templates/circuits/provider.html

@@ -33,6 +33,10 @@
                         <th scope="row">Account</th>
                         <th scope="row">Account</th>
                         <td>{{ object.account|placeholder }}</td>
                         <td>{{ object.account|placeholder }}</td>
                     </tr>
                     </tr>
+                    <tr>
+                        <th scope="row">Description</th>
+                        <td>{{ object.description|placeholder }}</td>
+                    </tr>
                     <tr>
                     <tr>
                         <th scope="row">Circuits</th>
                         <th scope="row">Circuits</th>
                         <td>
                         <td>

+ 5 - 0
netbox/templates/dcim/cable.html

@@ -32,6 +32,10 @@
               <th scope="row">Label</th>
               <th scope="row">Label</th>
               <td>{{ object.label|placeholder }}</td>
               <td>{{ object.label|placeholder }}</td>
             </tr>
             </tr>
+            <tr>
+              <th scope="row">Description</th>
+              <td>{{ object.description|placeholder }}</td>
+            </tr>
             <tr>
             <tr>
               <th scope="row">Color</th>
               <th scope="row">Color</th>
               <td>
               <td>
@@ -57,6 +61,7 @@
       </div>
       </div>
       {% include 'inc/panels/custom_fields.html' %}
       {% include 'inc/panels/custom_fields.html' %}
       {% include 'inc/panels/tags.html' %}
       {% include 'inc/panels/tags.html' %}
+      {% include 'inc/panels/comments.html' %}
       {% plugin_left_page object %}
       {% plugin_left_page object %}
     </div>
     </div>
     <div class="col col-md-6">
     <div class="col col-md-6">

+ 15 - 8
netbox/templates/dcim/cable_edit.html

@@ -80,6 +80,7 @@
               {% render_field form.tenant_group %}
               {% render_field form.tenant_group %}
               {% render_field form.tenant %}
               {% render_field form.tenant %}
               {% render_field form.label %}
               {% render_field form.label %}
+              {% render_field form.description %}
               {% render_field form.color %}
               {% render_field form.color %}
               <div class="row mb-3">
               <div class="row mb-3">
                 <label class="col-sm-3 col-form-label text-lg-end">{{ form.length.label }}</label>
                 <label class="col-sm-3 col-form-label text-lg-end">{{ form.length.label }}</label>
@@ -92,16 +93,22 @@
                 <div class="invalid-feedback"></div>
                 <div class="invalid-feedback"></div>
               </div>
               </div>
               {% render_field form.tags %}
               {% render_field form.tags %}
-              {% if form.custom_fields %}
-                <div class="field-group">
-                  <div class="row mb-3">
-                    <h5 class="offset-sm-3">Custom Fields</h5>
-                  </div>
-                  {% render_custom_fields form %}
-                </div>
-              {% endif %}
             </div>
             </div>
           </div>
           </div>
+            <div class="card">
+              <h5 class="card-header text-center">Comments</h5>
+              <div class="card-body">
+              {% render_field form.comments %}
+              </div>
+            </div>
+          {% if form.custom_fields %}
+            <div class="card">
+              <h5 class="card-header offset-sm-3">Custom Fields</h5>
+              <div class="card-body">
+                {% render_custom_fields form %}
+              </div>
+            </div>
+          {% endif %}
         </div>
         </div>
       </div>
       </div>
       <div class="row my-3">
       <div class="row my-3">

+ 5 - 1
netbox/templates/dcim/device.html

@@ -94,7 +94,11 @@
                             </td>
                             </td>
                         </tr>
                         </tr>
                         <tr>
                         <tr>
-                            <td>Airflow</td>
+                            <th scope="row">Description</th>
+                            <td>{{ object.description|placeholder }}</td>
+                        </tr>
+                        <tr>
+                            <th scope="row">Airflow</th>
                             <td>
                             <td>
                                 {{ object.get_airflow_display|placeholder }}
                                 {{ object.get_airflow_display|placeholder }}
                             </td>
                             </td>

+ 1 - 0
netbox/templates/dcim/device_edit.html

@@ -10,6 +10,7 @@
       </div>
       </div>
       {% render_field form.name %}
       {% render_field form.name %}
       {% render_field form.device_role %}
       {% render_field form.device_role %}
+      {% render_field form.description %}
       {% render_field form.tags %}
       {% render_field form.tags %}
     </div>
     </div>
     
     

+ 4 - 0
netbox/templates/dcim/devicetype.html

@@ -27,6 +27,10 @@
                             <td>Part Number</td>
                             <td>Part Number</td>
                             <td>{{ object.part_number|placeholder }}</td>
                             <td>{{ object.part_number|placeholder }}</td>
                         </tr>
                         </tr>
+                        <tr>
+                            <td>Description</td>
+                            <td>{{ object.description|placeholder }}</td>
+                        </tr>
                         <tr>
                         <tr>
                             <td>Height (U)</td>
                             <td>Height (U)</td>
                             <td>{{ object.u_height|floatformat }}</td>
                             <td>{{ object.u_height|floatformat }}</td>

+ 4 - 0
netbox/templates/dcim/module.html

@@ -62,6 +62,10 @@
             <th scope="row">Module Type</th>
             <th scope="row">Module Type</th>
             <td>{{ object.module_type|linkify }}</td>
             <td>{{ object.module_type|linkify }}</td>
           </tr>
           </tr>
+          <tr>
+            <th scope="row">Description</th>
+            <td>{{ object.description|placeholder }}</td>
+          </tr>
           <tr>
           <tr>
             <th scope="row">Serial Number</th>
             <th scope="row">Serial Number</th>
             <td class="font-monospace">{{ object.serial|placeholder }}</td>
             <td class="font-monospace">{{ object.serial|placeholder }}</td>

+ 4 - 0
netbox/templates/dcim/moduletype.html

@@ -22,6 +22,10 @@
               <td>Part Number</td>
               <td>Part Number</td>
               <td>{{ object.part_number|placeholder }}</td>
               <td>{{ object.part_number|placeholder }}</td>
             </tr>
             </tr>
+            <tr>
+              <th scope="row">Description</th>
+              <td>{{ object.description|placeholder }}</td>
+            </tr>
             <tr>
             <tr>
                 <td>Weight</td>
                 <td>Weight</td>
                 <td>
                 <td>

+ 4 - 0
netbox/templates/dcim/powerfeed.html

@@ -38,6 +38,10 @@
                         <th scope="row">Status</th>
                         <th scope="row">Status</th>
                         <td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
                         <td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
                     </tr>
                     </tr>
+                    <tr>
+                        <th scope="row">Description</th>
+                        <td>{{ object.description|placeholder }}</td>
+                    </tr>
                     <tr>
                     <tr>
                         <th scope="row">Connected Device</th>
                         <th scope="row">Connected Device</th>
                         <td>
                         <td>

+ 22 - 19
netbox/templates/dcim/powerpanel.html

@@ -14,26 +14,29 @@
 {% block content %}
 {% block content %}
 <div class="row">
 <div class="row">
 	<div class="col col-md-6">
 	<div class="col col-md-6">
-        <div class="card">
-            <h5 class="card-header">
-                Power Panel
-            </h5>
-            <div class="card-body">
-                <table class="table table-hover attr-table">
-                    <tr>
-                        <th scope="row">Site</th>
-                        <td>{{ object.site|linkify }}</td>
-                    </tr>
-                    <tr>
-                        <th scope="row">Location</th>
-                        <td>{{ object.location|linkify|placeholder }}</td>
-                    </tr>
-                </table>
-            </div>
-        </div>
-        {% include 'inc/panels/tags.html' %}
-        {% plugin_left_page object %}
+    <div class="card">
+      <h5 class="card-header">Power Panel</h5>
+      <div class="card-body">
+        <table class="table table-hover attr-table">
+          <tr>
+            <th scope="row">Site</th>
+            <td>{{ object.site|linkify }}</td>
+          </tr>
+          <tr>
+            <th scope="row">Location</th>
+            <td>{{ object.location|linkify|placeholder }}</td>
+          </tr>
+          <tr>
+            <th scope="row">Description</th>
+            <td>{{ object.description|placeholder }}</td>
+          </tr>
+        </table>
+      </div>
     </div>
     </div>
+    {% include 'inc/panels/tags.html' %}
+    {% include 'inc/panels/comments.html' %}
+    {% plugin_left_page object %}
+  </div>
 	<div class="col col-md-6">
 	<div class="col col-md-6">
         {% include 'inc/panels/custom_fields.html' %}
         {% include 'inc/panels/custom_fields.html' %}
         {% include 'inc/panels/contacts.html' %}
         {% include 'inc/panels/contacts.html' %}

+ 4 - 0
netbox/templates/dcim/rack.html

@@ -78,6 +78,10 @@
                         <th scope="row">Role</th>
                         <th scope="row">Role</th>
                         <td>{{ object.role|linkify|placeholder }}</td>
                         <td>{{ object.role|linkify|placeholder }}</td>
                     </tr>
                     </tr>
+                    <tr>
+                        <th scope="row">Description</th>
+                        <td>{{ object.description|placeholder }}</td>
+                    </tr>
                     <tr>
                     <tr>
                         <th scope="row">Serial Number</th>
                         <th scope="row">Serial Number</th>
                         <td class="font-monospace">{{ object.serial|placeholder }}</td>
                         <td class="font-monospace">{{ object.serial|placeholder }}</td>

+ 1 - 0
netbox/templates/dcim/rack_edit.html

@@ -13,6 +13,7 @@
         {% render_field form.name %}
         {% render_field form.name %}
         {% render_field form.status %}
         {% render_field form.status %}
         {% render_field form.role %}
         {% render_field form.role %}
+        {% render_field form.description %}
         {% render_field form.tags %}
         {% render_field form.tags %}
     </div>
     </div>
 
 

+ 1 - 0
netbox/templates/dcim/rackreservation.html

@@ -73,6 +73,7 @@
         </div>
         </div>
         {% include 'inc/panels/custom_fields.html' %}
         {% include 'inc/panels/custom_fields.html' %}
         {% include 'inc/panels/tags.html' %}
         {% include 'inc/panels/tags.html' %}
+        {% include 'inc/panels/comments.html' %}
         {% plugin_left_page object %}
         {% plugin_left_page object %}
 	</div>
 	</div>
     <div class="col col-12 col-xl-7">
     <div class="col col-12 col-xl-7">

+ 6 - 1
netbox/templates/dcim/virtualchassis.html

@@ -27,11 +27,15 @@
             <th scope="row">Master</th>
             <th scope="row">Master</th>
             <td>{{ object.master|linkify }}</td>
             <td>{{ object.master|linkify }}</td>
           </tr>
           </tr>
+          <tr>
+            <th scope="row">Description</th>
+            <td>{{ object.description|placeholder }}</td>
+          </tr>
         </table>
         </table>
       </div>
       </div>
     </div>
     </div>
-    {% include 'inc/panels/custom_fields.html' %}
     {% include 'inc/panels/tags.html' %}
     {% include 'inc/panels/tags.html' %}
+    {% include 'inc/panels/custom_fields.html' %}
     {% plugin_left_page object %}
     {% plugin_left_page object %}
     </div>
     </div>
     <div class="col col-md-8">
     <div class="col col-md-8">
@@ -73,6 +77,7 @@
           </div>
           </div>
         {% endif %}
         {% endif %}
       </div>
       </div>
+      {% include 'inc/panels/comments.html' %}
       {% plugin_right_page object %}
       {% plugin_right_page object %}
 	</div>
 	</div>
 </div>
 </div>

+ 7 - 1
netbox/templates/dcim/virtualchassis_edit.html

@@ -17,12 +17,18 @@
           </div>
           </div>
           {% render_field vc_form.name %}
           {% render_field vc_form.name %}
           {% render_field vc_form.domain %}
           {% render_field vc_form.domain %}
+          {% render_field vc_form.description %}
           {% render_field vc_form.master %}
           {% render_field vc_form.master %}
           {% render_field vc_form.tags %}
           {% render_field vc_form.tags %}
         </div>
         </div>
 
 
+        <div class="field-group my-5">
+          <h5 class="text-center">Comments</h5>
+          {% render_field vc_form.comments %}
+        </div>
+
         {% if vc_form.custom_fields %}
         {% if vc_form.custom_fields %}
-          <div class="field-group my-5">
+          <div class="field-group mb-5">
             <div class="row mb-2">
             <div class="row mb-2">
               <h5 class="offset-sm-3">Custom Fields</h5>
               <h5 class="offset-sm-3">Custom Fields</h5>
             </div>
             </div>

+ 1 - 0
netbox/templates/ipam/aggregate.html

@@ -51,6 +51,7 @@
     <div class="col col-md-6">
     <div class="col col-md-6">
       {% include 'inc/panels/custom_fields.html' %}
       {% include 'inc/panels/custom_fields.html' %}
       {% include 'inc/panels/tags.html' %}
       {% include 'inc/panels/tags.html' %}
+      {% include 'inc/panels/comments.html' %}
       {% plugin_right_page object %}
       {% plugin_right_page object %}
     </div>
     </div>
   </div>
   </div>

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

@@ -67,6 +67,7 @@
     <div class="col col-md-6">
     <div class="col col-md-6">
       {% include 'inc/panels/custom_fields.html' %}
       {% include 'inc/panels/custom_fields.html' %}
       {% include 'inc/panels/tags.html' with tags=object.tags.all url='ipam:asn_list' %}
       {% include 'inc/panels/tags.html' with tags=object.tags.all url='ipam:asn_list' %}
+      {% include 'inc/panels/comments.html' %}
       {% plugin_right_page object %}
       {% plugin_right_page object %}
     </div>
     </div>
   </div>
   </div>

+ 1 - 0
netbox/templates/ipam/fhrpgroup.html

@@ -42,6 +42,7 @@
         </div>
         </div>
       </div>
       </div>
       {% include 'inc/panels/tags.html' %}
       {% include 'inc/panels/tags.html' %}
+      {% include 'inc/panels/comments.html' %}
       {% plugin_left_page object %}
       {% plugin_left_page object %}
     </div>
     </div>
     <div class="col col-md-6">
     <div class="col col-md-6">

+ 9 - 2
netbox/templates/ipam/fhrpgroup_edit.html

@@ -13,7 +13,7 @@
     {% render_field form.tags %}
     {% render_field form.tags %}
   </div>
   </div>
 
 
-  <div class="field-group my-5">
+  <div class="field-group mb-5">
     <div class="row mb-2">
     <div class="row mb-2">
       <h5 class="offset-sm-3">Authentication</h5>
       <h5 class="offset-sm-3">Authentication</h5>
     </div>
     </div>
@@ -22,7 +22,7 @@
   </div>
   </div>
 
 
   {% if not form.instance.pk %}
   {% if not form.instance.pk %}
-    <div class="field-group my-5">
+    <div class="field-group mb-5">
       <div class="row mb-2">
       <div class="row mb-2">
         <h5 class="offset-sm-3">Virtual IP Address</h5>
         <h5 class="offset-sm-3">Virtual IP Address</h5>
       </div>
       </div>
@@ -32,6 +32,13 @@
     </div>
     </div>
   {% endif %}
   {% endif %}
 
 
+  <div class="field-group mb-5">
+    <div class="row mb-2">
+      <h5 class="offset-sm-3">Comments</h5>
+    </div>
+    {% render_field form.comments %}
+  </div>
+
   {% if form.custom_fields %}
   {% if form.custom_fields %}
     <div class="row mb-2">
     <div class="row mb-2">
       <h5 class="offset-sm-3">Custom Fields</h5>
       <h5 class="offset-sm-3">Custom Fields</h5>

+ 1 - 0
netbox/templates/ipam/ipaddress.html

@@ -108,6 +108,7 @@
       </div>
       </div>
       {% include 'inc/panels/tags.html' %}
       {% include 'inc/panels/tags.html' %}
       {% include 'inc/panels/custom_fields.html' %}
       {% include 'inc/panels/custom_fields.html' %}
+      {% include 'inc/panels/comments.html' %}
       {% plugin_left_page object %}
       {% plugin_left_page object %}
 	</div>
 	</div>
 	<div class="col col-md-8">
 	<div class="col col-md-8">

+ 7 - 0
netbox/templates/ipam/ipaddress_edit.html

@@ -138,6 +138,13 @@
       </div>
       </div>
     </div>
     </div>
 
 
+    <div class="field-group my-5">
+      <div class="row mb-2">
+        <h5 class="text-center">Comments</h5>
+      </div>
+      {% render_field form.comments %}
+    </div>
+
     {% if form.custom_fields %}
     {% if form.custom_fields %}
       <div class="field-group my-5">
       <div class="field-group my-5">
         <div class="row mb-2">
         <div class="row mb-2">

+ 4 - 3
netbox/templates/ipam/iprange.html

@@ -70,9 +70,10 @@
         {% plugin_left_page object %}
         {% plugin_left_page object %}
     </div>
     </div>
     <div class="col col-md-6">
     <div class="col col-md-6">
-        {% include 'inc/panels/tags.html' %}
-        {% include 'inc/panels/custom_fields.html' %}
-        {% plugin_right_page object %}
+      {% include 'inc/panels/tags.html' %}
+      {% include 'inc/panels/custom_fields.html' %}
+      {% include 'inc/panels/comments.html' %}
+      {% plugin_right_page object %}
     </div>
     </div>
 </div>
 </div>
 <div class="row">
 <div class="row">

+ 1 - 0
netbox/templates/ipam/l2vpn.html

@@ -39,6 +39,7 @@
 	<div class="col col-md-6">
 	<div class="col col-md-6">
       {% include 'inc/panels/contacts.html' %}
       {% include 'inc/panels/contacts.html' %}
       {% include 'inc/panels/custom_fields.html' %}
       {% include 'inc/panels/custom_fields.html' %}
+      {% include 'inc/panels/comments.html' %}
       {% plugin_right_page object %}
       {% plugin_right_page object %}
     </div>
     </div>
 </div>
 </div>

+ 1 - 0
netbox/templates/ipam/prefix.html

@@ -155,6 +155,7 @@
     </div>
     </div>
     {% include 'inc/panels/custom_fields.html' %}
     {% include 'inc/panels/custom_fields.html' %}
     {% include 'inc/panels/tags.html' %}
     {% include 'inc/panels/tags.html' %}
+    {% include 'inc/panels/comments.html' %}
     {% plugin_right_page object %}
     {% plugin_right_page object %}
   </div>
   </div>
 </div>
 </div>

+ 1 - 0
netbox/templates/ipam/routetarget.html

@@ -26,6 +26,7 @@
       </div>
       </div>
       {% include 'inc/panels/tags.html' %}
       {% include 'inc/panels/tags.html' %}
       {% include 'inc/panels/custom_fields.html' %}
       {% include 'inc/panels/custom_fields.html' %}
+      {% include 'inc/panels/comments.html' %}
       {% plugin_left_page object %}
       {% plugin_left_page object %}
     </div>
     </div>
     <div class="col col-md-6">
     <div class="col col-md-6">

+ 4 - 3
netbox/templates/ipam/service.html

@@ -58,9 +58,10 @@
         {% plugin_left_page object %}
         {% plugin_left_page object %}
     </div>
     </div>
     <div class="col col-md-6">
     <div class="col col-md-6">
-        {% include 'inc/panels/custom_fields.html' %}
-        {% include 'inc/panels/tags.html' %}
-        {% plugin_right_page object %}
+      {% include 'inc/panels/custom_fields.html' %}
+      {% include 'inc/panels/tags.html' %}
+      {% include 'inc/panels/comments.html' %}
+      {% plugin_right_page object %}
     </div>
     </div>
 </div>
 </div>
 <div class="row mb-3">
 <div class="row mb-3">

+ 7 - 0
netbox/templates/ipam/service_create.html

@@ -65,6 +65,13 @@
     {% render_field form.tags %}
     {% render_field form.tags %}
   </div>
   </div>
 
 
+  <div class="field-group my-5">
+    <div class="row mb-2">
+      <h5 class="text-center">Comments</h5>
+    </div>
+    {% render_field form.comments %}
+  </div>
+
   {% if form.custom_fields %}
   {% if form.custom_fields %}
     <div class="row mb-2">
     <div class="row mb-2">
       <h5 class="offset-sm-3">Custom Fields</h5>
       <h5 class="offset-sm-3">Custom Fields</h5>

+ 7 - 0
netbox/templates/ipam/service_edit.html

@@ -52,6 +52,13 @@
     {% render_field form.tags %}
     {% render_field form.tags %}
   </div>
   </div>
 
 
+  <div class="field-group my-5">
+    <div class="row mb-2">
+      <h5 class="text-center">Comments</h5>
+    </div>
+    {% render_field form.comments %}
+  </div>
+
   {% if form.custom_fields %}
   {% if form.custom_fields %}
     <div class="row mb-2">
     <div class="row mb-2">
       <h5 class="offset-sm-3">Custom Fields</h5>
       <h5 class="offset-sm-3">Custom Fields</h5>

+ 7 - 6
netbox/templates/ipam/servicetemplate.html

@@ -31,12 +31,13 @@
         </div>
         </div>
       </div>
       </div>
       {% plugin_left_page object %}
       {% plugin_left_page object %}
-      </div>
-      <div class="col col-md-6">
-        {% include 'inc/panels/custom_fields.html' %}
-        {% include 'inc/panels/tags.html' %}
-        {% plugin_right_page object %}
-      </div>
+    </div>
+    <div class="col col-md-6">
+      {% include 'inc/panels/custom_fields.html' %}
+      {% include 'inc/panels/tags.html' %}
+      {% include 'inc/panels/comments.html' %}
+      {% plugin_right_page object %}
+    </div>
   </div>
   </div>
   <div class="row mb-3">
   <div class="row mb-3">
     <div class="col col-md-12">
     <div class="col col-md-12">

+ 4 - 3
netbox/templates/ipam/vlan.html

@@ -74,9 +74,10 @@
             {% plugin_left_page object %}
             {% plugin_left_page object %}
         </div>
         </div>
         <div class="col col-md-6">
         <div class="col col-md-6">
-            {% include 'inc/panels/custom_fields.html' %}
-            {% include 'inc/panels/tags.html' %}
-            {% plugin_right_page object %}
+          {% include 'inc/panels/custom_fields.html' %}
+          {% include 'inc/panels/tags.html' %}
+          {% include 'inc/panels/comments.html' %}
+          {% plugin_right_page object %}
         </div>
         </div>
     </div>
     </div>
     <div class="row">
     <div class="row">

+ 7 - 0
netbox/templates/ipam/vlan_edit.html

@@ -55,6 +55,13 @@
     {% endwith %}
     {% endwith %}
   </div>
   </div>
 
 
+  <div class="field-group my-5">
+    <div class="row mb-2">
+      <h5 class="text-center">Comments</h5>
+    </div>
+    {% render_field form.comments %}
+  </div>
+
   {% if form.custom_fields %}
   {% if form.custom_fields %}
     <div class="field-group my-5">
     <div class="field-group my-5">
       <div class="row mb-2">
       <div class="row mb-2">

+ 1 - 0
netbox/templates/ipam/vrf.html

@@ -55,6 +55,7 @@
   <div class="col col-md-6">
   <div class="col col-md-6">
     {% include 'inc/panels/tags.html' %}
     {% include 'inc/panels/tags.html' %}
     {% include 'inc/panels/custom_fields.html' %}
     {% include 'inc/panels/custom_fields.html' %}
+    {% include 'inc/panels/comments.html' %}
     {% plugin_right_page object %}
     {% plugin_right_page object %}
 	</div>
 	</div>
 </div>
 </div>

+ 4 - 0
netbox/templates/tenancy/contact.html

@@ -63,6 +63,10 @@
                 {% endif %}
                 {% endif %}
               </td>
               </td>
             </tr>
             </tr>
+            <tr>
+              <th scope="row">Description</th>
+              <td>{{ object.description|placeholder }}</td>
+            </tr>
             <tr>
             <tr>
               <th scope="row">Assignments</th>
               <th scope="row">Assignments</th>
               <td>{{ assignment_count }}</td>
               <td>{{ assignment_count }}</td>

+ 4 - 0
netbox/templates/virtualization/cluster.html

@@ -23,6 +23,10 @@
                   <th scope="row">Status</th>
                   <th scope="row">Status</th>
                   <td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
                   <td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
                 </tr>
                 </tr>
+                <tr>
+                  <th scope="row">Description</th>
+                  <td>{{ object.description|placeholder }}</td>
+                </tr>
                 <tr>
                 <tr>
                     <th scope="row">Group</th>
                     <th scope="row">Group</th>
                     <td>{{ object.group|linkify|placeholder }}</td>
                     <td>{{ object.group|linkify|placeholder }}</td>

+ 4 - 0
netbox/templates/virtualization/virtualmachine.html

@@ -29,6 +29,10 @@
                         <th scope="row">Platform</th>
                         <th scope="row">Platform</th>
                         <td>{{ object.platform|linkify|placeholder }}</td>
                         <td>{{ object.platform|linkify|placeholder }}</td>
                     </tr>
                     </tr>
+                    <tr>
+                      <th scope="row">Description</th>
+                      <td>{{ object.description|placeholder }}</td>
+                    </tr>
                     <tr>
                     <tr>
                         <th scope="row">Tenant</th>
                         <th scope="row">Tenant</th>
                         <td>
                         <td>

+ 1 - 0
netbox/templates/wireless/wirelesslan.html

@@ -39,6 +39,7 @@
       </div>
       </div>
     </div>
     </div>
     {% include 'inc/panels/tags.html' %}
     {% include 'inc/panels/tags.html' %}
+    {% include 'inc/panels/comments.html' %}
     {% plugin_left_page object %}
     {% plugin_left_page object %}
   </div>
   </div>
   <div class="col col-md-6">
   <div class="col col-md-6">

+ 1 - 0
netbox/templates/wireless/wirelesslink.html

@@ -40,6 +40,7 @@
         </div>
         </div>
       </div>
       </div>
       {% include 'inc/panels/tags.html' %}
       {% include 'inc/panels/tags.html' %}
+      {% include 'inc/panels/comments.html' %}
       {% plugin_left_page object %}
       {% plugin_left_page object %}
     </div>
     </div>
     <div class="col col-md-6">
     <div class="col col-md-6">

+ 6 - 0
netbox/templates/wireless/wirelesslink_edit.html

@@ -22,6 +22,12 @@
       </div>
       </div>
     </div>
     </div>
   </div>
   </div>
+  <div class="field-group my-5">
+    <div class="row mb-2">
+      <h5 class="offset-sm-3">Comments</h5>
+    </div>
+    {% render_field form.comments %}
+  </div>
   {% if form.custom_fields %}
   {% if form.custom_fields %}
     <div class="field-group my-5">
     <div class="field-group my-5">
       <div class="row mb-2">
       <div class="row mb-2">

+ 2 - 2
netbox/tenancy/api/serializers.py

@@ -85,8 +85,8 @@ class ContactSerializer(NetBoxModelSerializer):
     class Meta:
     class Meta:
         model = Contact
         model = Contact
         fields = [
         fields = [
-            'id', 'url', 'display', 'group', 'name', 'title', 'phone', 'email', 'address', 'link', 'comments', 'tags',
-            'custom_fields', 'created', 'last_updated',
+            'id', 'url', 'display', 'group', 'name', 'title', 'phone', 'email', 'address', 'link', 'description',
+            'comments', 'tags', 'custom_fields', 'created', 'last_updated',
         ]
         ]
 
 
 
 

+ 11 - 3
netbox/tenancy/forms/bulk_edit.py

@@ -2,7 +2,7 @@ from django import forms
 
 
 from netbox.forms import NetBoxModelBulkEditForm
 from netbox.forms import NetBoxModelBulkEditForm
 from tenancy.models import *
 from tenancy.models import *
-from utilities.forms import DynamicModelChoiceField
+from utilities.forms import CommentField, DynamicModelChoiceField, SmallTextarea
 
 
 __all__ = (
 __all__ = (
     'ContactBulkEditForm',
     'ContactBulkEditForm',
@@ -101,9 +101,17 @@ class ContactBulkEditForm(NetBoxModelBulkEditForm):
     link = forms.URLField(
     link = forms.URLField(
         required=False
         required=False
     )
     )
+    description = forms.CharField(
+        max_length=200,
+        required=False
+    )
+    comments = CommentField(
+        widget=SmallTextarea,
+        label='Comments'
+    )
 
 
     model = Contact
     model = Contact
     fieldsets = (
     fieldsets = (
-        (None, ('group', 'title', 'phone', 'email', 'address', 'link')),
+        (None, ('group', 'title', 'phone', 'email', 'address', 'link', 'description')),
     )
     )
-    nullable_fields = ('group', 'title', 'phone', 'email', 'address', 'link', 'comments')
+    nullable_fields = ('group', 'title', 'phone', 'email', 'address', 'link', 'description', 'comments')

+ 1 - 1
netbox/tenancy/forms/bulk_import.py

@@ -79,4 +79,4 @@ class ContactCSVForm(NetBoxModelCSVForm):
 
 
     class Meta:
     class Meta:
         model = Contact
         model = Contact
-        fields = ('name', 'title', 'phone', 'email', 'address', 'link', 'group', 'comments')
+        fields = ('name', 'title', 'phone', 'email', 'address', 'link', 'group', 'description', 'comments')

+ 2 - 2
netbox/tenancy/forms/model_forms.py

@@ -103,13 +103,13 @@ class ContactForm(NetBoxModelForm):
     comments = CommentField()
     comments = CommentField()
 
 
     fieldsets = (
     fieldsets = (
-        ('Contact', ('group', 'name', 'title', 'phone', 'email', 'address', 'link', 'tags')),
+        ('Contact', ('group', 'name', 'title', 'phone', 'email', 'address', 'link', 'description', 'tags')),
     )
     )
 
 
     class Meta:
     class Meta:
         model = Contact
         model = Contact
         fields = (
         fields = (
-            'group', 'name', 'title', 'phone', 'email', 'address', 'link', 'comments', 'tags',
+            'group', 'name', 'title', 'phone', 'email', 'address', 'link', 'description', 'comments', 'tags',
         )
         )
         widgets = {
         widgets = {
             'address': SmallTextarea(attrs={'rows': 3}),
             'address': SmallTextarea(attrs={'rows': 3}),

+ 18 - 0
netbox/tenancy/migrations/0009_standardize_description_comments.py

@@ -0,0 +1,18 @@
+# Generated by Django 4.1.2 on 2022-11-03 18:24
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('tenancy', '0008_unique_constraints'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='contact',
+            name='description',
+            field=models.CharField(blank=True, max_length=200),
+        ),
+    ]

+ 2 - 6
netbox/tenancy/models/contacts.py

@@ -2,9 +2,8 @@ from django.contrib.contenttypes.fields import GenericForeignKey
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.models import ContentType
 from django.db import models
 from django.db import models
 from django.urls import reverse
 from django.urls import reverse
-from mptt.models import TreeForeignKey
 
 
-from netbox.models import ChangeLoggedModel, NestedGroupModel, OrganizationalModel, NetBoxModel
+from netbox.models import ChangeLoggedModel, NestedGroupModel, OrganizationalModel, PrimaryModel
 from netbox.models.features import WebhooksMixin
 from netbox.models.features import WebhooksMixin
 from tenancy.choices import *
 from tenancy.choices import *
 
 
@@ -41,7 +40,7 @@ class ContactRole(OrganizationalModel):
         return reverse('tenancy:contactrole', args=[self.pk])
         return reverse('tenancy:contactrole', args=[self.pk])
 
 
 
 
-class Contact(NetBoxModel):
+class Contact(PrimaryModel):
     """
     """
     Contact information for a particular object(s) in NetBox.
     Contact information for a particular object(s) in NetBox.
     """
     """
@@ -73,9 +72,6 @@ class Contact(NetBoxModel):
     link = models.URLField(
     link = models.URLField(
         blank=True
         blank=True
     )
     )
-    comments = models.TextField(
-        blank=True
-    )
 
 
     clone_fields = (
     clone_fields = (
         'group', 'name', 'title', 'phone', 'email', 'address', 'link',
         'group', 'name', 'title', 'phone', 'email', 'address', 'link',

+ 2 - 10
netbox/tenancy/models/tenants.py

@@ -1,9 +1,8 @@
 from django.contrib.contenttypes.fields import GenericRelation
 from django.contrib.contenttypes.fields import GenericRelation
 from django.db import models
 from django.db import models
 from django.urls import reverse
 from django.urls import reverse
-from mptt.models import TreeForeignKey
 
 
-from netbox.models import NestedGroupModel, NetBoxModel
+from netbox.models import NestedGroupModel, PrimaryModel
 
 
 __all__ = (
 __all__ = (
     'Tenant',
     'Tenant',
@@ -31,7 +30,7 @@ class TenantGroup(NestedGroupModel):
         return reverse('tenancy:tenantgroup', args=[self.pk])
         return reverse('tenancy:tenantgroup', args=[self.pk])
 
 
 
 
-class Tenant(NetBoxModel):
+class Tenant(PrimaryModel):
     """
     """
     A Tenant represents an organization served by the NetBox owner. This is typically a customer or an internal
     A Tenant represents an organization served by the NetBox owner. This is typically a customer or an internal
     department.
     department.
@@ -51,13 +50,6 @@ class Tenant(NetBoxModel):
         blank=True,
         blank=True,
         null=True
         null=True
     )
     )
-    description = models.CharField(
-        max_length=200,
-        blank=True
-    )
-    comments = models.TextField(
-        blank=True
-    )
 
 
     # Generic relations
     # Generic relations
     contacts = GenericRelation(
     contacts = GenericRelation(

+ 2 - 2
netbox/tenancy/tables/contacts.py

@@ -65,8 +65,8 @@ class ContactTable(NetBoxTable):
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
         model = Contact
         model = Contact
         fields = (
         fields = (
-            'pk', 'name', 'group', 'title', 'phone', 'email', 'address', 'link', 'comments', 'assignment_count', 'tags',
-            'created', 'last_updated',
+            'pk', 'name', 'group', 'title', 'phone', 'email', 'address', 'link', 'description', 'comments',
+            'assignment_count', 'tags', 'created', 'last_updated',
         )
         )
         default_columns = ('pk', 'name', 'group', 'assignment_count', 'title', 'phone', 'email')
         default_columns = ('pk', 'name', 'group', 'assignment_count', 'title', 'phone', 'email')
 
 

+ 4 - 4
netbox/virtualization/api/serializers.py

@@ -58,8 +58,8 @@ class ClusterSerializer(NetBoxModelSerializer):
     class Meta:
     class Meta:
         model = Cluster
         model = Cluster
         fields = [
         fields = [
-            'id', 'url', 'display', 'name', 'type', 'group', 'status', 'tenant', 'site', 'comments', 'tags',
-            'custom_fields', 'created', 'last_updated', 'device_count', 'virtualmachine_count',
+            'id', 'url', 'display', 'name', 'type', 'group', 'status', 'tenant', 'site', 'description', 'comments',
+            'tags', 'custom_fields', 'created', 'last_updated', 'device_count', 'virtualmachine_count',
         ]
         ]
 
 
 
 
@@ -84,8 +84,8 @@ class VirtualMachineSerializer(NetBoxModelSerializer):
         model = VirtualMachine
         model = VirtualMachine
         fields = [
         fields = [
             'id', 'url', 'display', 'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant', 'platform',
             'id', 'url', 'display', 'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant', 'platform',
-            'primary_ip', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'comments', 'local_context_data',
-            'tags', 'custom_fields', 'created', 'last_updated',
+            'primary_ip', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'description', 'comments',
+            'local_context_data', 'tags', 'custom_fields', 'created', 'last_updated',
         ]
         ]
         validators = []
         validators = []
 
 

+ 13 - 5
netbox/virtualization/forms/bulk_edit.py

@@ -84,6 +84,10 @@ class ClusterBulkEditForm(NetBoxModelBulkEditForm):
             'group_id': '$site_group',
             'group_id': '$site_group',
         }
         }
     )
     )
+    description = forms.CharField(
+        max_length=200,
+        required=False
+    )
     comments = CommentField(
     comments = CommentField(
         widget=SmallTextarea,
         widget=SmallTextarea,
         label='Comments'
         label='Comments'
@@ -91,11 +95,11 @@ class ClusterBulkEditForm(NetBoxModelBulkEditForm):
 
 
     model = Cluster
     model = Cluster
     fieldsets = (
     fieldsets = (
-        (None, ('type', 'group', 'status', 'tenant',)),
-        ('Site', ('region', 'site_group', 'site',)),
+        (None, ('type', 'group', 'status', 'tenant', 'description')),
+        ('Site', ('region', 'site_group', 'site')),
     )
     )
     nullable_fields = (
     nullable_fields = (
-        'group', 'site', 'comments', 'tenant',
+        'group', 'site', 'tenant', 'description', 'comments',
     )
     )
 
 
 
 
@@ -153,6 +157,10 @@ class VirtualMachineBulkEditForm(NetBoxModelBulkEditForm):
         required=False,
         required=False,
         label='Disk (GB)'
         label='Disk (GB)'
     )
     )
+    description = forms.CharField(
+        max_length=200,
+        required=False
+    )
     comments = CommentField(
     comments = CommentField(
         widget=SmallTextarea,
         widget=SmallTextarea,
         label='Comments'
         label='Comments'
@@ -160,11 +168,11 @@ class VirtualMachineBulkEditForm(NetBoxModelBulkEditForm):
 
 
     model = VirtualMachine
     model = VirtualMachine
     fieldsets = (
     fieldsets = (
-        (None, ('site', 'cluster', 'device', 'status', 'role', 'tenant', 'platform')),
+        (None, ('site', 'cluster', 'device', 'status', 'role', 'tenant', 'platform', 'description')),
         ('Resources', ('vcpus', 'memory', 'disk'))
         ('Resources', ('vcpus', 'memory', 'disk'))
     )
     )
     nullable_fields = (
     nullable_fields = (
-        'site', 'cluster', 'device', 'role', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments',
+        'site', 'cluster', 'device', 'role', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'description', 'comments',
     )
     )
 
 
 
 

+ 2 - 2
netbox/virtualization/forms/bulk_import.py

@@ -63,7 +63,7 @@ class ClusterCSVForm(NetBoxModelCSVForm):
 
 
     class Meta:
     class Meta:
         model = Cluster
         model = Cluster
-        fields = ('name', 'type', 'group', 'status', 'site', 'comments')
+        fields = ('name', 'type', 'group', 'status', 'site', 'description', 'comments')
 
 
 
 
 class VirtualMachineCSVForm(NetBoxModelCSVForm):
 class VirtualMachineCSVForm(NetBoxModelCSVForm):
@@ -114,7 +114,7 @@ class VirtualMachineCSVForm(NetBoxModelCSVForm):
         model = VirtualMachine
         model = VirtualMachine
         fields = (
         fields = (
             'name', 'status', 'role', 'site', 'cluster', 'device', 'tenant', 'platform', 'vcpus', 'memory', 'disk',
             'name', 'status', 'role', 'site', 'cluster', 'device', 'tenant', 'platform', 'vcpus', 'memory', 'disk',
-            'comments',
+            'description', 'comments',
         )
         )
 
 
 
 

+ 6 - 4
netbox/virtualization/forms/model_forms.py

@@ -90,7 +90,7 @@ class ClusterForm(TenancyForm, NetBoxModelForm):
     comments = CommentField()
     comments = CommentField()
 
 
     fieldsets = (
     fieldsets = (
-        ('Cluster', ('name', 'type', 'group', 'status', 'tags')),
+        ('Cluster', ('name', 'type', 'group', 'status', 'description', 'tags')),
         ('Site', ('region', 'site_group', 'site')),
         ('Site', ('region', 'site_group', 'site')),
         ('Tenancy', ('tenant_group', 'tenant')),
         ('Tenancy', ('tenant_group', 'tenant')),
     )
     )
@@ -98,7 +98,8 @@ class ClusterForm(TenancyForm, NetBoxModelForm):
     class Meta:
     class Meta:
         model = Cluster
         model = Cluster
         fields = (
         fields = (
-            'name', 'type', 'group', 'status', 'tenant', 'region', 'site_group', 'site', 'comments', 'tags',
+            'name', 'type', 'group', 'status', 'tenant', 'region', 'site_group', 'site', 'description', 'comments',
+            'tags',
         )
         )
         widgets = {
         widgets = {
             'status': StaticSelect(),
             'status': StaticSelect(),
@@ -220,9 +221,10 @@ class VirtualMachineForm(TenancyForm, NetBoxModelForm):
         required=False,
         required=False,
         label=''
         label=''
     )
     )
+    comments = CommentField()
 
 
     fieldsets = (
     fieldsets = (
-        ('Virtual Machine', ('name', 'role', 'status', 'tags')),
+        ('Virtual Machine', ('name', 'role', 'status', 'description', 'tags')),
         ('Site/Cluster', ('site', 'cluster_group', 'cluster', 'device')),
         ('Site/Cluster', ('site', 'cluster_group', 'cluster', 'device')),
         ('Tenancy', ('tenant_group', 'tenant')),
         ('Tenancy', ('tenant_group', 'tenant')),
         ('Management', ('platform', 'primary_ip4', 'primary_ip6')),
         ('Management', ('platform', 'primary_ip4', 'primary_ip6')),
@@ -234,7 +236,7 @@ class VirtualMachineForm(TenancyForm, NetBoxModelForm):
         model = VirtualMachine
         model = VirtualMachine
         fields = [
         fields = [
             'name', 'status', 'site', 'cluster_group', 'cluster', 'device', 'role', 'tenant_group', 'tenant',
             'name', 'status', 'site', 'cluster_group', 'cluster', 'device', 'role', 'tenant_group', 'tenant',
-            'platform', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'comments', 'tags',
+            'platform', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'description', 'comments', 'tags',
             'local_context_data',
             'local_context_data',
         ]
         ]
         help_texts = {
         help_texts = {

+ 23 - 0
netbox/virtualization/migrations/0034_standardize_description_comments.py

@@ -0,0 +1,23 @@
+# Generated by Django 4.1.2 on 2022-11-03 18:24
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('virtualization', '0033_unique_constraints'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='cluster',
+            name='description',
+            field=models.CharField(blank=True, max_length=200),
+        ),
+        migrations.AddField(
+            model_name='virtualmachine',
+            name='description',
+            field=models.CharField(blank=True, max_length=200),
+        ),
+    ]

+ 3 - 9
netbox/virtualization/models.py

@@ -10,7 +10,7 @@ from dcim.models import BaseInterface, Device
 from extras.models import ConfigContextModel
 from extras.models import ConfigContextModel
 from extras.querysets import ConfigContextModelQuerySet
 from extras.querysets import ConfigContextModelQuerySet
 from netbox.config import get_config
 from netbox.config import get_config
-from netbox.models import OrganizationalModel, NetBoxModel
+from netbox.models import NetBoxModel, OrganizationalModel, PrimaryModel
 from utilities.fields import NaturalOrderingField
 from utilities.fields import NaturalOrderingField
 from utilities.ordering import naturalize_interface
 from utilities.ordering import naturalize_interface
 from utilities.query_functions import CollateAsChar
 from utilities.query_functions import CollateAsChar
@@ -64,7 +64,7 @@ class ClusterGroup(OrganizationalModel):
 # Clusters
 # Clusters
 #
 #
 
 
-class Cluster(NetBoxModel):
+class Cluster(PrimaryModel):
     """
     """
     A cluster of VirtualMachines. Each Cluster may optionally be associated with one or more Devices.
     A cluster of VirtualMachines. Each Cluster may optionally be associated with one or more Devices.
     """
     """
@@ -102,9 +102,6 @@ class Cluster(NetBoxModel):
         blank=True,
         blank=True,
         null=True
         null=True
     )
     )
-    comments = models.TextField(
-        blank=True
-    )
 
 
     # Generic relations
     # Generic relations
     vlan_groups = GenericRelation(
     vlan_groups = GenericRelation(
@@ -165,7 +162,7 @@ class Cluster(NetBoxModel):
 # Virtual machines
 # Virtual machines
 #
 #
 
 
-class VirtualMachine(NetBoxModel, ConfigContextModel):
+class VirtualMachine(PrimaryModel, ConfigContextModel):
     """
     """
     A virtual machine which runs inside a Cluster.
     A virtual machine which runs inside a Cluster.
     """
     """
@@ -262,9 +259,6 @@ class VirtualMachine(NetBoxModel, ConfigContextModel):
         null=True,
         null=True,
         verbose_name='Disk (GB)'
         verbose_name='Disk (GB)'
     )
     )
-    comments = models.TextField(
-        blank=True
-    )
 
 
     # Generic relation
     # Generic relation
     contacts = GenericRelation(
     contacts = GenericRelation(

+ 2 - 2
netbox/virtualization/tables/clusters.py

@@ -86,7 +86,7 @@ class ClusterTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
         model = Cluster
         model = Cluster
         fields = (
         fields = (
-            'pk', 'id', 'name', 'type', 'group', 'status', 'tenant', 'tenant_group', 'site', 'comments', 'device_count',
-            'vm_count', 'contacts', 'tags', 'created', 'last_updated',
+            'pk', 'id', 'name', 'type', 'group', 'status', 'tenant', 'tenant_group', 'site', 'description', 'comments',
+            'device_count', 'vm_count', 'contacts', 'tags', 'created', 'last_updated',
         )
         )
         default_columns = ('pk', 'name', 'type', 'group', 'status', 'tenant', 'site', 'device_count', 'vm_count')
         default_columns = ('pk', 'name', 'type', 'group', 'status', 'tenant', 'site', 'device_count', 'vm_count')

+ 2 - 2
netbox/virtualization/tables/virtualmachines.py

@@ -75,8 +75,8 @@ class VirtualMachineTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable)
         model = VirtualMachine
         model = VirtualMachine
         fields = (
         fields = (
             'pk', 'id', 'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant', 'tenant_group', 'platform',
             'pk', 'id', 'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant', 'tenant_group', 'platform',
-            'vcpus', 'memory', 'disk', 'primary_ip4', 'primary_ip6', 'primary_ip', 'comments', 'contacts', 'tags',
-            'created', 'last_updated',
+            'vcpus', 'memory', 'disk', 'primary_ip4', 'primary_ip6', 'primary_ip', 'description', 'comments',
+            'contacts', 'tags', 'created', 'last_updated',
         )
         )
         default_columns = (
         default_columns = (
             'pk', 'name', 'status', 'site', 'cluster', 'role', 'tenant', 'vcpus', 'memory', 'disk', 'primary_ip',
             'pk', 'name', 'status', 'site', 'cluster', 'role', 'tenant', 'vcpus', 'memory', 'disk', 'primary_ip',

+ 2 - 2
netbox/wireless/api/serializers.py

@@ -42,7 +42,7 @@ class WirelessLANSerializer(NetBoxModelSerializer):
         model = WirelessLAN
         model = WirelessLAN
         fields = [
         fields = [
             'id', 'url', 'display', 'ssid', 'description', 'group', 'vlan', 'tenant', 'auth_type', 'auth_cipher',
             'id', 'url', 'display', 'ssid', 'description', 'group', 'vlan', 'tenant', 'auth_type', 'auth_cipher',
-            'auth_psk', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
+            'auth_psk', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
         ]
         ]
 
 
 
 
@@ -59,5 +59,5 @@ class WirelessLinkSerializer(NetBoxModelSerializer):
         model = WirelessLink
         model = WirelessLink
         fields = [
         fields = [
             'id', 'url', 'display', 'interface_a', 'interface_b', 'ssid', 'status', 'tenant', 'auth_type',
             'id', 'url', 'display', 'interface_a', 'interface_b', 'ssid', 'status', 'tenant', 'auth_type',
-            'auth_cipher', 'auth_psk', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
+            'auth_cipher', 'auth_psk', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
         ]
         ]

+ 19 - 9
netbox/wireless/forms/bulk_edit.py

@@ -4,7 +4,7 @@ from dcim.choices import LinkStatusChoices
 from ipam.models import VLAN
 from ipam.models import VLAN
 from netbox.forms import NetBoxModelBulkEditForm
 from netbox.forms import NetBoxModelBulkEditForm
 from tenancy.models import Tenant
 from tenancy.models import Tenant
-from utilities.forms import add_blank_choice, DynamicModelChoiceField
+from utilities.forms import add_blank_choice, CommentField, DynamicModelChoiceField, SmallTextarea
 from wireless.choices import *
 from wireless.choices import *
 from wireless.constants import SSID_MAX_LENGTH
 from wireless.constants import SSID_MAX_LENGTH
 from wireless.models import *
 from wireless.models import *
@@ -52,9 +52,6 @@ class WirelessLANBulkEditForm(NetBoxModelBulkEditForm):
         queryset=Tenant.objects.all(),
         queryset=Tenant.objects.all(),
         required=False
         required=False
     )
     )
-    description = forms.CharField(
-        required=False
-    )
     auth_type = forms.ChoiceField(
     auth_type = forms.ChoiceField(
         choices=add_blank_choice(WirelessAuthTypeChoices),
         choices=add_blank_choice(WirelessAuthTypeChoices),
         required=False
         required=False
@@ -67,6 +64,14 @@ class WirelessLANBulkEditForm(NetBoxModelBulkEditForm):
         required=False,
         required=False,
         label='Pre-shared key'
         label='Pre-shared key'
     )
     )
+    description = forms.CharField(
+        max_length=200,
+        required=False
+    )
+    comments = CommentField(
+        widget=SmallTextarea,
+        label='Comments'
+    )
 
 
     model = WirelessLAN
     model = WirelessLAN
     fieldsets = (
     fieldsets = (
@@ -74,7 +79,7 @@ class WirelessLANBulkEditForm(NetBoxModelBulkEditForm):
         ('Authentication', ('auth_type', 'auth_cipher', 'auth_psk')),
         ('Authentication', ('auth_type', 'auth_cipher', 'auth_psk')),
     )
     )
     nullable_fields = (
     nullable_fields = (
-        'ssid', 'group', 'vlan', 'tenant', 'description', 'auth_type', 'auth_cipher', 'auth_psk',
+        'ssid', 'group', 'vlan', 'tenant', 'description', 'auth_type', 'auth_cipher', 'auth_psk', 'comments',
     )
     )
 
 
 
 
@@ -92,9 +97,6 @@ class WirelessLinkBulkEditForm(NetBoxModelBulkEditForm):
         queryset=Tenant.objects.all(),
         queryset=Tenant.objects.all(),
         required=False
         required=False
     )
     )
-    description = forms.CharField(
-        required=False
-    )
     auth_type = forms.ChoiceField(
     auth_type = forms.ChoiceField(
         choices=add_blank_choice(WirelessAuthTypeChoices),
         choices=add_blank_choice(WirelessAuthTypeChoices),
         required=False
         required=False
@@ -107,6 +109,14 @@ class WirelessLinkBulkEditForm(NetBoxModelBulkEditForm):
         required=False,
         required=False,
         label='Pre-shared key'
         label='Pre-shared key'
     )
     )
+    description = forms.CharField(
+        max_length=200,
+        required=False
+    )
+    comments = CommentField(
+        widget=SmallTextarea,
+        label='Comments'
+    )
 
 
     model = WirelessLink
     model = WirelessLink
     fieldsets = (
     fieldsets = (
@@ -114,5 +124,5 @@ class WirelessLinkBulkEditForm(NetBoxModelBulkEditForm):
         ('Authentication', ('auth_type', 'auth_cipher', 'auth_psk'))
         ('Authentication', ('auth_type', 'auth_cipher', 'auth_psk'))
     )
     )
     nullable_fields = (
     nullable_fields = (
-        'ssid', 'tenant', 'description', 'auth_type', 'auth_cipher', 'auth_psk',
+        'ssid', 'tenant', 'description', 'auth_type', 'auth_cipher', 'auth_psk', 'comments',
     )
     )

Некоторые файлы не были показаны из-за большого количества измененных файлов