1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Fixes #5146: Add custom fields support for cables, power panels, rack reservations, and virtual chassis

This commit is contained in:
Jeremy Stretch
2020-09-17 14:22:14 -04:00
parent 91eca8cac9
commit 0030fe1779
14 changed files with 80 additions and 21 deletions

View File

@ -4,6 +4,10 @@
**NOTE:** This release completely removes support for embedded graphs.
### New Features
* [#5146](https://github.com/netbox-community/netbox/issues/5146) - Add custom fields support for cables, power panels, rack reservations, and virtual chassis
### Other Changes
* [#4349](https://github.com/netbox-community/netbox/issues/4349) - Dropped support for embedded graphs

View File

@ -168,7 +168,7 @@ class RackUnitSerializer(serializers.Serializer):
occupied = serializers.BooleanField(read_only=True)
class RackReservationSerializer(TaggedObjectSerializer, ValidatedModelSerializer):
class RackReservationSerializer(TaggedObjectSerializer, CustomFieldModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackreservation-detail')
rack = NestedRackSerializer()
user = NestedUserSerializer()
@ -176,7 +176,7 @@ class RackReservationSerializer(TaggedObjectSerializer, ValidatedModelSerializer
class Meta:
model = RackReservation
fields = ['id', 'url', 'rack', 'units', 'created', 'user', 'tenant', 'description', 'tags']
fields = ['id', 'url', 'rack', 'units', 'created', 'user', 'tenant', 'description', 'tags', 'custom_fields']
class RackElevationDetailFilterSerializer(serializers.Serializer):
@ -649,7 +649,7 @@ class InventoryItemSerializer(TaggedObjectSerializer, ValidatedModelSerializer):
# Cables
#
class CableSerializer(TaggedObjectSerializer, ValidatedModelSerializer):
class CableSerializer(TaggedObjectSerializer, CustomFieldModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:cable-detail')
termination_a_type = ContentTypeField(
queryset=ContentType.objects.filter(CABLE_TERMINATION_MODELS)
@ -667,6 +667,7 @@ class CableSerializer(TaggedObjectSerializer, ValidatedModelSerializer):
fields = [
'id', 'url', 'termination_a_type', 'termination_a_id', 'termination_a', 'termination_b_type',
'termination_b_id', 'termination_b', 'type', 'status', 'label', 'color', 'length', 'length_unit', 'tags',
'custom_fields',
]
def _get_termination(self, obj, side):
@ -729,21 +730,21 @@ class InterfaceConnectionSerializer(ValidatedModelSerializer):
# Virtual chassis
#
class VirtualChassisSerializer(TaggedObjectSerializer, ValidatedModelSerializer):
class VirtualChassisSerializer(TaggedObjectSerializer, CustomFieldModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:virtualchassis-detail')
master = NestedDeviceSerializer(required=False)
member_count = serializers.IntegerField(read_only=True)
class Meta:
model = VirtualChassis
fields = ['id', 'url', 'name', 'domain', 'master', 'tags', 'member_count']
fields = ['id', 'url', 'name', 'domain', 'master', 'tags', 'custom_fields', 'member_count']
#
# Power panels
#
class PowerPanelSerializer(TaggedObjectSerializer, ValidatedModelSerializer):
class PowerPanelSerializer(TaggedObjectSerializer, CustomFieldModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerpanel-detail')
site = NestedSiteSerializer()
rack_group = NestedRackGroupSerializer(
@ -755,7 +756,7 @@ class PowerPanelSerializer(TaggedObjectSerializer, ValidatedModelSerializer):
class Meta:
model = PowerPanel
fields = ['id', 'url', 'site', 'rack_group', 'name', 'tags', 'powerfeed_count']
fields = ['id', 'url', 'site', 'rack_group', 'name', 'tags', 'custom_fields', 'powerfeed_count']
class PowerFeedSerializer(TaggedObjectSerializer, CustomFieldModelSerializer):

View File

@ -690,7 +690,7 @@ class RackElevationFilterForm(RackFilterForm):
# Rack reservations
#
class RackReservationForm(BootstrapMixin, TenancyForm, forms.ModelForm):
class RackReservationForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
site = DynamicModelChoiceField(
queryset=Site.objects.all(),
required=False
@ -3608,7 +3608,7 @@ class ConnectCableToPowerFeedForm(BootstrapMixin, forms.ModelForm):
return getattr(self.cleaned_data['termination_b_id'], 'pk', None)
class CableForm(BootstrapMixin, forms.ModelForm):
class CableForm(BootstrapMixin, CustomFieldModelForm):
tags = DynamicModelMultipleChoiceField(
queryset=Tag.objects.all(),
required=False
@ -3919,7 +3919,7 @@ class DeviceSelectionForm(forms.Form):
)
class VirtualChassisCreateForm(BootstrapMixin, forms.ModelForm):
class VirtualChassisCreateForm(BootstrapMixin, CustomFieldModelForm):
site = DynamicModelChoiceField(
queryset=Site.objects.all(),
required=False
@ -3972,7 +3972,7 @@ class VirtualChassisCreateForm(BootstrapMixin, forms.ModelForm):
return instance
class VirtualChassisForm(BootstrapMixin, forms.ModelForm):
class VirtualChassisForm(BootstrapMixin, CustomFieldModelForm):
master = forms.ModelChoiceField(
queryset=Device.objects.all(),
required=False,
@ -4152,7 +4152,7 @@ class VirtualChassisFilterForm(BootstrapMixin, CustomFieldFilterForm):
# Power panels
#
class PowerPanelForm(BootstrapMixin, forms.ModelForm):
class PowerPanelForm(BootstrapMixin, CustomFieldModelForm):
site = DynamicModelChoiceField(
queryset=Site.objects.all()
)

View File

@ -9,6 +9,7 @@ class Migration(migrations.Migration):
]
operations = [
# Original CustomFieldModels
migrations.AddField(
model_name='device',
name='custom_field_data',
@ -34,4 +35,26 @@ class Migration(migrations.Migration):
name='custom_field_data',
field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder),
),
# Added under #5146
migrations.AddField(
model_name='cable',
name='custom_field_data',
field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder),
),
migrations.AddField(
model_name='powerpanel',
name='custom_field_data',
field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder),
),
migrations.AddField(
model_name='rackreservation',
name='custom_field_data',
field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder),
),
migrations.AddField(
model_name='virtualchassis',
name='custom_field_data',
field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder),
),
]

View File

@ -882,8 +882,8 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
# Cables
#
@extras_features('custom_links', 'export_templates', 'webhooks')
class Cable(ChangeLoggedModel):
@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks')
class Cable(ChangeLoggedModel, CustomFieldModel):
"""
A physical connection between two endpoints.
"""
@ -1168,8 +1168,8 @@ class Cable(ChangeLoggedModel):
# Virtual chassis
#
@extras_features('custom_links', 'export_templates', 'webhooks')
class VirtualChassis(ChangeLoggedModel):
@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks')
class VirtualChassis(ChangeLoggedModel, CustomFieldModel):
"""
A collection of Devices which operate with a shared control plane (e.g. a switch stack).
"""

View File

@ -22,8 +22,8 @@ __all__ = (
# Power
#
@extras_features('custom_links', 'export_templates', 'webhooks')
class PowerPanel(ChangeLoggedModel):
@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks')
class PowerPanel(ChangeLoggedModel, CustomFieldModel):
"""
A distribution point for electrical power; e.g. a data center RPP.
"""

View File

@ -561,8 +561,8 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
return 0
@extras_features('custom_links', 'export_templates', 'webhooks')
class RackReservation(ChangeLoggedModel):
@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks')
class RackReservation(ChangeLoggedModel, CustomFieldModel):
"""
One or more reserved units within a Rack.
"""

View File

@ -83,6 +83,7 @@
</tr>
</table>
</div>
{% include 'inc/custom_fields_panel.html' with obj=cable %}
{% include 'extras/inc/tags_panel.html' with tags=cable.tags.all url='dcim:cable_list' %}
{% plugin_left_page cable %}
</div>

View File

@ -32,3 +32,11 @@
{% render_field form.tags %}
</div>
</div>
{% if form.custom_fields %}
<div class="panel panel-default">
<div class="panel-heading"><strong>Custom Fields</strong></div>
<div class="panel-body">
{% render_custom_fields form %}
</div>
</div>
{% endif %}

View File

@ -82,6 +82,7 @@
</tr>
</table>
</div>
{% include 'inc/custom_fields_panel.html' with obj=powerpanel %}
{% include 'extras/inc/tags_panel.html' with tags=powerpanel.tags.all url='dcim:powerpanel_list' %}
{% plugin_left_page powerpanel %}
</div>

View File

@ -124,6 +124,7 @@
</tr>
</table>
</div>
{% include 'inc/custom_fields_panel.html' with obj=rackreservation %}
{% include 'extras/inc/tags_panel.html' with tags=rackreservation.tags.all url='dcim:rackreservation_list' %}
{% plugin_left_page rackreservation %}
</div>

View File

@ -21,4 +21,12 @@
{% render_field form.tenant %}
</div>
</div>
{% if form.custom_fields %}
<div class="panel panel-default">
<div class="panel-heading"><strong>Custom Fields</strong></div>
<div class="panel-body">
{% render_custom_fields form %}
</div>
</div>
{% endif %}
{% endblock %}

View File

@ -78,6 +78,7 @@
</tr>
</table>
</div>
{% include 'inc/custom_fields_panel.html' with obj=virtualchassis %}
{% include 'extras/inc/tags_panel.html' with tags=virtualchassis.tags.all url='dcim:virtualchassis_list' %}
{% plugin_left_page virtualchassis %}
</div>

View File

@ -21,9 +21,20 @@
<div class="panel panel-default">
<div class="panel-heading"><strong>Virtual Chassis</strong></div>
<div class="table panel-body">
{% render_form vc_form %}
{% render_field vc_form.name %}
{% render_field vc_form.domain %}
{% render_field vc_form.master %}
{% render_field vc_form.tags %}
</div>
</div>
{% if vc_form.custom_fields %}
<div class="panel panel-default">
<div class="panel-heading"><strong>Custom Fields</strong></div>
<div class="panel-body">
{% render_custom_fields vc_form %}
</div>
</div>
{% endif %}
<div class="panel panel-default">
<div class="panel-heading"><strong>Members</strong></div>
<table class="table panel-body">