diff --git a/docs/release-notes/version-2.9.md b/docs/release-notes/version-2.9.md index 3fefd8569..1d20201e9 100644 --- a/docs/release-notes/version-2.9.md +++ b/docs/release-notes/version-2.9.md @@ -16,6 +16,7 @@ NetBox v2.9 replaces Django's built-in permissions framework with one that suppo * [#4742](https://github.com/netbox-community/netbox/issues/4742) - Add tagging for cables, power panels, and rack reservations * [#4788](https://github.com/netbox-community/netbox/issues/4788) - Add dedicated views for all device components * [#4792](https://github.com/netbox-community/netbox/issues/4792) - Add bulk rename capability for console and power ports +* [#4793](https://github.com/netbox-community/netbox/issues/4793) - Add `description` field to device component templates * [#4795](https://github.com/netbox-community/netbox/issues/4795) - Add bulk disconnect capability for console and power ports ### Configuration Changes @@ -40,6 +41,7 @@ NetBox v2.9 replaces Django's built-in permissions framework with one that suppo * The IP address model now uses a generic foreign key to refer to the assigned interface. The `interface` field on the serializer has been replaced with `assigned_object_type` and `assigned_object_id` for write operations. If one exists, the assigned interface is available as `assigned_object`. * The serialized representation of a virtual machine interface now includes only relevant fields: `type`, `lag`, `mgmt_only`, `connected_endpoint_type`, `connected_endpoint`, and `cable` are no longer included. * dcim.VirtualChassis: Added a mandatory `name` field +* An optional `description` field has been added to all device component templates ### Other Changes diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index 45a908685..18e35aabe 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -245,7 +245,7 @@ class ConsolePortTemplateSerializer(ValidatedModelSerializer): class Meta: model = ConsolePortTemplate - fields = ['id', 'device_type', 'name', 'label', 'type'] + fields = ['id', 'device_type', 'name', 'label', 'type', 'description'] class ConsoleServerPortTemplateSerializer(ValidatedModelSerializer): @@ -258,7 +258,7 @@ class ConsoleServerPortTemplateSerializer(ValidatedModelSerializer): class Meta: model = ConsoleServerPortTemplate - fields = ['id', 'device_type', 'name', 'label', 'type'] + fields = ['id', 'device_type', 'name', 'label', 'type', 'description'] class PowerPortTemplateSerializer(ValidatedModelSerializer): @@ -271,7 +271,7 @@ class PowerPortTemplateSerializer(ValidatedModelSerializer): class Meta: model = PowerPortTemplate - fields = ['id', 'device_type', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw'] + fields = ['id', 'device_type', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description'] class PowerOutletTemplateSerializer(ValidatedModelSerializer): @@ -292,7 +292,7 @@ class PowerOutletTemplateSerializer(ValidatedModelSerializer): class Meta: model = PowerOutletTemplate - fields = ['id', 'device_type', 'name', 'label', 'type', 'power_port', 'feed_leg'] + fields = ['id', 'device_type', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description'] class InterfaceTemplateSerializer(ValidatedModelSerializer): @@ -301,7 +301,7 @@ class InterfaceTemplateSerializer(ValidatedModelSerializer): class Meta: model = InterfaceTemplate - fields = ['id', 'device_type', 'name', 'label', 'type', 'mgmt_only'] + fields = ['id', 'device_type', 'name', 'label', 'type', 'mgmt_only', 'description'] class RearPortTemplateSerializer(ValidatedModelSerializer): @@ -310,7 +310,7 @@ class RearPortTemplateSerializer(ValidatedModelSerializer): class Meta: model = RearPortTemplate - fields = ['id', 'device_type', 'name', 'type', 'positions'] + fields = ['id', 'device_type', 'name', 'type', 'positions', 'description'] class FrontPortTemplateSerializer(ValidatedModelSerializer): @@ -320,7 +320,7 @@ class FrontPortTemplateSerializer(ValidatedModelSerializer): class Meta: model = FrontPortTemplate - fields = ['id', 'device_type', 'name', 'type', 'rear_port', 'rear_port_position'] + fields = ['id', 'device_type', 'name', 'type', 'rear_port', 'rear_port_position', 'description'] class DeviceBayTemplateSerializer(ValidatedModelSerializer): @@ -328,7 +328,7 @@ class DeviceBayTemplateSerializer(ValidatedModelSerializer): class Meta: model = DeviceBayTemplate - fields = ['id', 'device_type', 'name', 'label'] + fields = ['id', 'device_type', 'name', 'label', 'description'] # diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index deb61729f..2244ef443 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -1053,6 +1053,9 @@ class ComponentTemplateCreateForm(LabeledComponentForm): display_field='model' ) ) + description = forms.CharField( + required=False + ) class ConsolePortTemplateForm(BootstrapMixin, forms.ModelForm): @@ -1060,7 +1063,7 @@ class ConsolePortTemplateForm(BootstrapMixin, forms.ModelForm): class Meta: model = ConsolePortTemplate fields = [ - 'device_type', 'name', 'label', 'type', + 'device_type', 'name', 'label', 'type', 'description', ] widgets = { 'device_type': forms.HiddenInput(), @@ -1086,7 +1089,7 @@ class ConsolePortTemplateBulkEditForm(BootstrapMixin, BulkEditForm): ) class Meta: - nullable_fields = ('type',) + nullable_fields = ('type', 'description') class ConsoleServerPortTemplateForm(BootstrapMixin, forms.ModelForm): @@ -1094,7 +1097,7 @@ class ConsoleServerPortTemplateForm(BootstrapMixin, forms.ModelForm): class Meta: model = ConsoleServerPortTemplate fields = [ - 'device_type', 'name', 'label', 'type', + 'device_type', 'name', 'label', 'type', 'description', ] widgets = { 'device_type': forms.HiddenInput(), @@ -1118,9 +1121,12 @@ class ConsoleServerPortTemplateBulkEditForm(BootstrapMixin, BulkEditForm): required=False, widget=StaticSelect2() ) + description = forms.CharField( + required=False + ) class Meta: - nullable_fields = ('type',) + nullable_fields = ('type', 'description') class PowerPortTemplateForm(BootstrapMixin, forms.ModelForm): @@ -1128,7 +1134,7 @@ class PowerPortTemplateForm(BootstrapMixin, forms.ModelForm): class Meta: model = PowerPortTemplate fields = [ - 'device_type', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', + 'device_type', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description', ] widgets = { 'device_type': forms.HiddenInput(), @@ -1172,9 +1178,12 @@ class PowerPortTemplateBulkEditForm(BootstrapMixin, BulkEditForm): required=False, help_text="Allocated power draw (watts)" ) + description = forms.CharField( + required=False + ) class Meta: - nullable_fields = ('type', 'maximum_draw', 'allocated_draw') + nullable_fields = ('type', 'maximum_draw', 'allocated_draw', 'description') class PowerOutletTemplateForm(BootstrapMixin, forms.ModelForm): @@ -1182,7 +1191,7 @@ class PowerOutletTemplateForm(BootstrapMixin, forms.ModelForm): class Meta: model = PowerOutletTemplate fields = [ - 'device_type', 'name', 'label', 'type', 'power_port', 'feed_leg', + 'device_type', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description', ] widgets = { 'device_type': forms.HiddenInput(), @@ -1251,9 +1260,12 @@ class PowerOutletTemplateBulkEditForm(BootstrapMixin, BulkEditForm): required=False, widget=StaticSelect2() ) + description = forms.CharField( + required=False + ) class Meta: - nullable_fields = ('type', 'power_port', 'feed_leg') + nullable_fields = ('type', 'power_port', 'feed_leg', 'description') def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -1272,7 +1284,7 @@ class InterfaceTemplateForm(BootstrapMixin, forms.ModelForm): class Meta: model = InterfaceTemplate fields = [ - 'device_type', 'name', 'label', 'type', 'mgmt_only', + 'device_type', 'name', 'label', 'type', 'mgmt_only', 'description', ] widgets = { 'device_type': forms.HiddenInput(), @@ -1306,9 +1318,12 @@ class InterfaceTemplateBulkEditForm(BootstrapMixin, BulkEditForm): widget=BulkEditNullBooleanSelect, label='Management only' ) + description = forms.CharField( + required=False + ) class Meta: - nullable_fields = [] + nullable_fields = ('description',) class FrontPortTemplateForm(BootstrapMixin, forms.ModelForm): @@ -1316,7 +1331,7 @@ class FrontPortTemplateForm(BootstrapMixin, forms.ModelForm): class Meta: model = FrontPortTemplate fields = [ - 'device_type', 'name', 'type', 'rear_port', 'rear_port_position', + 'device_type', 'name', 'type', 'rear_port', 'rear_port_position', 'description', ] widgets = { 'device_type': forms.HiddenInput(), @@ -1401,9 +1416,12 @@ class FrontPortTemplateBulkEditForm(BootstrapMixin, BulkEditForm): required=False, widget=StaticSelect2() ) + description = forms.CharField( + required=False + ) class Meta: - nullable_fields = () + nullable_fields = ('description',) class RearPortTemplateForm(BootstrapMixin, forms.ModelForm): @@ -1411,7 +1429,7 @@ class RearPortTemplateForm(BootstrapMixin, forms.ModelForm): class Meta: model = RearPortTemplate fields = [ - 'device_type', 'name', 'type', 'positions', + 'device_type', 'name', 'type', 'positions', 'description', ] widgets = { 'device_type': forms.HiddenInput(), @@ -1442,9 +1460,12 @@ class RearPortTemplateBulkEditForm(BootstrapMixin, BulkEditForm): required=False, widget=StaticSelect2() ) + description = forms.CharField( + required=False + ) class Meta: - nullable_fields = () + nullable_fields = ('description',) class DeviceBayTemplateForm(BootstrapMixin, forms.ModelForm): @@ -1452,7 +1473,7 @@ class DeviceBayTemplateForm(BootstrapMixin, forms.ModelForm): class Meta: model = DeviceBayTemplate fields = [ - 'device_type', 'name', 'label', + 'device_type', 'name', 'label', 'description', ] widgets = { 'device_type': forms.HiddenInput(), @@ -1460,7 +1481,9 @@ class DeviceBayTemplateForm(BootstrapMixin, forms.ModelForm): class DeviceBayTemplateCreateForm(ComponentTemplateCreateForm): - pass + description = forms.CharField( + required=False + ) # TODO: DeviceBayTemplate has no fields suitable for bulk-editing yet diff --git a/netbox/dcim/migrations/0111_component_template_description.py b/netbox/dcim/migrations/0111_component_template_description.py new file mode 100644 index 000000000..3040f586c --- /dev/null +++ b/netbox/dcim/migrations/0111_component_template_description.py @@ -0,0 +1,53 @@ +# Generated by Django 3.0.6 on 2020-06-30 18:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0110_virtualchassis_name'), + ] + + operations = [ + migrations.AddField( + model_name='consoleporttemplate', + name='description', + field=models.CharField(blank=True, max_length=200), + ), + migrations.AddField( + model_name='consoleserverporttemplate', + name='description', + field=models.CharField(blank=True, max_length=200), + ), + migrations.AddField( + model_name='devicebaytemplate', + name='description', + field=models.CharField(blank=True, max_length=200), + ), + migrations.AddField( + model_name='frontporttemplate', + name='description', + field=models.CharField(blank=True, max_length=200), + ), + migrations.AddField( + model_name='interfacetemplate', + name='description', + field=models.CharField(blank=True, max_length=200), + ), + migrations.AddField( + model_name='poweroutlettemplate', + name='description', + field=models.CharField(blank=True, max_length=200), + ), + migrations.AddField( + model_name='powerporttemplate', + name='description', + field=models.CharField(blank=True, max_length=200), + ), + migrations.AddField( + model_name='rearporttemplate', + name='description', + field=models.CharField(blank=True, max_length=200), + ), + ] diff --git a/netbox/dcim/models/device_component_templates.py b/netbox/dcim/models/device_component_templates.py index 904352196..1c2be0e5d 100644 --- a/netbox/dcim/models/device_component_templates.py +++ b/netbox/dcim/models/device_component_templates.py @@ -27,6 +27,11 @@ __all__ = ( class ComponentTemplateModel(models.Model): + description = models.CharField( + max_length=200, + blank=True + ) + objects = RestrictedQuerySet.as_manager() class Meta: diff --git a/netbox/dcim/tables.py b/netbox/dcim/tables.py index 9979bea1a..dd6b96406 100644 --- a/netbox/dcim/tables.py +++ b/netbox/dcim/tables.py @@ -486,7 +486,7 @@ class ConsolePortTemplateTable(ComponentTemplateTable): class Meta(BaseTable.Meta): model = ConsolePortTemplate - fields = ('pk', 'name', 'label', 'type', 'actions') + fields = ('pk', 'name', 'label', 'type', 'description', 'actions') empty_text = "None" @@ -499,7 +499,7 @@ class ConsoleServerPortTemplateTable(ComponentTemplateTable): class Meta(BaseTable.Meta): model = ConsoleServerPortTemplate - fields = ('pk', 'name', 'label', 'type', 'actions') + fields = ('pk', 'name', 'label', 'type', 'description', 'actions') empty_text = "None" @@ -512,7 +512,7 @@ class PowerPortTemplateTable(ComponentTemplateTable): class Meta(BaseTable.Meta): model = PowerPortTemplate - fields = ('pk', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'actions') + fields = ('pk', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description', 'actions') empty_text = "None" @@ -525,7 +525,7 @@ class PowerOutletTemplateTable(ComponentTemplateTable): class Meta(BaseTable.Meta): model = PowerOutletTemplate - fields = ('pk', 'name', 'label', 'type', 'power_port', 'feed_leg', 'actions') + fields = ('pk', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description', 'actions') empty_text = "None" @@ -541,7 +541,7 @@ class InterfaceTemplateTable(ComponentTemplateTable): class Meta(BaseTable.Meta): model = InterfaceTemplate - fields = ('pk', 'name', 'label', 'mgmt_only', 'type', 'actions') + fields = ('pk', 'name', 'label', 'mgmt_only', 'type', 'description', 'actions') empty_text = "None" @@ -557,7 +557,7 @@ class FrontPortTemplateTable(ComponentTemplateTable): class Meta(BaseTable.Meta): model = FrontPortTemplate - fields = ('pk', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'actions') + fields = ('pk', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description', 'actions') empty_text = "None" @@ -570,7 +570,7 @@ class RearPortTemplateTable(ComponentTemplateTable): class Meta(BaseTable.Meta): model = RearPortTemplate - fields = ('pk', 'name', 'label', 'type', 'positions', 'actions') + fields = ('pk', 'name', 'label', 'type', 'positions', 'description', 'actions') empty_text = "None" @@ -583,7 +583,7 @@ class DeviceBayTemplateTable(ComponentTemplateTable): class Meta(BaseTable.Meta): model = DeviceBayTemplate - fields = ('pk', 'name', 'label', 'actions') + fields = ('pk', 'name', 'label', 'description', 'actions') empty_text = "None"