diff --git a/netbox/dcim/api/nested_serializers.py b/netbox/dcim/api/nested_serializers.py index 6ed7c63c6..9440e5d4b 100644 --- a/netbox/dcim/api/nested_serializers.py +++ b/netbox/dcim/api/nested_serializers.py @@ -4,6 +4,7 @@ from dcim import models from netbox.api.serializers import BaseModelSerializer, WritableNestedSerializer __all__ = [ + 'ComponentNestedModuleSerializer', 'NestedCableSerializer', 'NestedConsolePortSerializer', 'NestedConsolePortTemplateSerializer', @@ -261,11 +262,30 @@ class NestedDeviceSerializer(WritableNestedSerializer): fields = ['id', 'url', 'display', 'name'] +class ModuleNestedModuleBaySerializer(WritableNestedSerializer): + url = serializers.HyperlinkedIdentityField(view_name='dcim-api:modulebay-detail') + + class Meta: + model = models.ModuleBay + fields = ['id', 'url', 'display', 'name'] + + +class ComponentNestedModuleSerializer(WritableNestedSerializer): + """ + Used by device component serializers. + """ + url = serializers.HyperlinkedIdentityField(view_name='dcim-api:module-detail') + module_bay = ModuleNestedModuleBaySerializer(read_only=True) + + class Meta: + model = models.Module + fields = ['id', 'url', 'display', 'device', 'module_bay'] + + class NestedModuleSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:module-detail') device = NestedDeviceSerializer(read_only=True) - # TODO: Solve circular dependency - # module_bay = NestedModuleBaySerializer(read_only=True) + module_bay = ModuleNestedModuleBaySerializer(read_only=True) module_type = NestedModuleTypeSerializer(read_only=True) class Meta: diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index b58355f32..c81b26d1f 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -558,6 +558,10 @@ class DeviceNAPALMSerializer(serializers.Serializer): class ConsoleServerPortSerializer(PrimaryModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleserverport-detail') device = NestedDeviceSerializer() + module = ComponentNestedModuleSerializer( + required=False, + allow_null=True + ) type = ChoiceField( choices=ConsolePortTypeChoices, allow_blank=True, @@ -573,8 +577,8 @@ class ConsoleServerPortSerializer(PrimaryModelSerializer, LinkTerminationSeriali class Meta: model = ConsoleServerPort fields = [ - 'id', 'url', 'display', 'device', 'name', 'label', 'type', 'speed', 'description', 'mark_connected', - 'cable', 'link_peer', 'link_peer_type', 'connected_endpoint', 'connected_endpoint_type', + 'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'speed', 'description', + 'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'connected_endpoint', 'connected_endpoint_type', 'connected_endpoint_reachable', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied', ] @@ -582,6 +586,10 @@ class ConsoleServerPortSerializer(PrimaryModelSerializer, LinkTerminationSeriali class ConsolePortSerializer(PrimaryModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleport-detail') device = NestedDeviceSerializer() + module = ComponentNestedModuleSerializer( + required=False, + allow_null=True + ) type = ChoiceField( choices=ConsolePortTypeChoices, allow_blank=True, @@ -597,8 +605,8 @@ class ConsolePortSerializer(PrimaryModelSerializer, LinkTerminationSerializer, C class Meta: model = ConsolePort fields = [ - 'id', 'url', 'display', 'device', 'name', 'label', 'type', 'speed', 'description', 'mark_connected', - 'cable', 'link_peer', 'link_peer_type', 'connected_endpoint', 'connected_endpoint_type', + 'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'speed', 'description', + 'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'connected_endpoint', 'connected_endpoint_type', 'connected_endpoint_reachable', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied', ] @@ -606,6 +614,10 @@ class ConsolePortSerializer(PrimaryModelSerializer, LinkTerminationSerializer, C class PowerOutletSerializer(PrimaryModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:poweroutlet-detail') device = NestedDeviceSerializer() + module = ComponentNestedModuleSerializer( + required=False, + allow_null=True + ) type = ChoiceField( choices=PowerOutletTypeChoices, allow_blank=True, @@ -627,15 +639,20 @@ class PowerOutletSerializer(PrimaryModelSerializer, LinkTerminationSerializer, C class Meta: model = PowerOutlet fields = [ - 'id', 'url', 'display', 'device', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description', - 'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'connected_endpoint', 'connected_endpoint_type', - 'connected_endpoint_reachable', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied', + 'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'power_port', 'feed_leg', + 'description', 'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'connected_endpoint', + 'connected_endpoint_type', 'connected_endpoint_reachable', 'tags', 'custom_fields', 'created', + 'last_updated', '_occupied', ] class PowerPortSerializer(PrimaryModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerport-detail') device = NestedDeviceSerializer() + module = ComponentNestedModuleSerializer( + required=False, + allow_null=True + ) type = ChoiceField( choices=PowerPortTypeChoices, allow_blank=True, @@ -646,15 +663,20 @@ class PowerPortSerializer(PrimaryModelSerializer, LinkTerminationSerializer, Con class Meta: model = PowerPort fields = [ - 'id', 'url', 'display', 'device', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description', - 'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'connected_endpoint', 'connected_endpoint_type', - 'connected_endpoint_reachable', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied', + 'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', + 'description', 'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'connected_endpoint', + 'connected_endpoint_type', 'connected_endpoint_reachable', 'tags', 'custom_fields', 'created', + 'last_updated', '_occupied', ] class InterfaceSerializer(PrimaryModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail') device = NestedDeviceSerializer() + module = ComponentNestedModuleSerializer( + required=False, + allow_null=True + ) type = ChoiceField(choices=InterfaceTypeChoices) parent = NestedInterfaceSerializer(required=False, allow_null=True) bridge = NestedInterfaceSerializer(required=False, allow_null=True) @@ -683,12 +705,12 @@ class InterfaceSerializer(PrimaryModelSerializer, LinkTerminationSerializer, Con class Meta: model = Interface fields = [ - 'id', 'url', 'display', 'device', 'name', 'label', 'type', 'enabled', 'parent', 'bridge', 'lag', 'mtu', - 'mac_address', 'wwn', 'mgmt_only', 'description', 'mode', 'rf_role', 'rf_channel', 'rf_channel_frequency', - 'rf_channel_width', 'tx_power', 'untagged_vlan', 'tagged_vlans', 'mark_connected', 'cable', 'wireless_link', - 'link_peer', 'link_peer_type', 'wireless_lans', 'connected_endpoint', 'connected_endpoint_type', - 'connected_endpoint_reachable', 'tags', 'custom_fields', 'created', 'last_updated', 'count_ipaddresses', - 'count_fhrp_groups', '_occupied', + 'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'enabled', 'parent', 'bridge', 'lag', + 'mtu', 'mac_address', 'wwn', 'mgmt_only', 'description', 'mode', 'rf_role', 'rf_channel', + 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'untagged_vlan', 'tagged_vlans', 'mark_connected', + 'cable', 'wireless_link', 'link_peer', 'link_peer_type', 'wireless_lans', 'connected_endpoint', + 'connected_endpoint_type', 'connected_endpoint_reachable', 'tags', 'custom_fields', 'created', + 'last_updated', 'count_ipaddresses', 'count_fhrp_groups', '_occupied', ] def validate(self, data): @@ -708,13 +730,17 @@ class InterfaceSerializer(PrimaryModelSerializer, LinkTerminationSerializer, Con class RearPortSerializer(PrimaryModelSerializer, LinkTerminationSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearport-detail') device = NestedDeviceSerializer() + module = ComponentNestedModuleSerializer( + required=False, + allow_null=True + ) type = ChoiceField(choices=PortTypeChoices) cable = NestedCableSerializer(read_only=True) class Meta: model = RearPort fields = [ - 'id', 'url', 'display', 'device', 'name', 'label', 'type', 'color', 'positions', 'description', + 'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'color', 'positions', 'description', 'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied', ] @@ -734,6 +760,10 @@ class FrontPortRearPortSerializer(WritableNestedSerializer): class FrontPortSerializer(PrimaryModelSerializer, LinkTerminationSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:frontport-detail') device = NestedDeviceSerializer() + module = ComponentNestedModuleSerializer( + required=False, + allow_null=True + ) type = ChoiceField(choices=PortTypeChoices) rear_port = FrontPortRearPortSerializer() cable = NestedCableSerializer(read_only=True) @@ -741,9 +771,9 @@ class FrontPortSerializer(PrimaryModelSerializer, LinkTerminationSerializer): class Meta: model = FrontPort fields = [ - 'id', 'url', 'display', 'device', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position', - 'description', 'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'tags', 'custom_fields', - 'created', 'last_updated', '_occupied', + 'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'color', 'rear_port', + 'rear_port_position', 'description', 'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'tags', + 'custom_fields', 'created', 'last_updated', '_occupied', ] diff --git a/netbox/dcim/api/views.py b/netbox/dcim/api/views.py index 378e697c8..9d1be93d5 100644 --- a/netbox/dcim/api/views.py +++ b/netbox/dcim/api/views.py @@ -539,7 +539,9 @@ class ModuleViewSet(CustomFieldModelViewSet): # class ConsolePortViewSet(PathEndpointMixin, ModelViewSet): - queryset = ConsolePort.objects.prefetch_related('device', '_path__destination', 'cable', '_link_peer', 'tags') + queryset = ConsolePort.objects.prefetch_related( + 'device', 'module__module_bay', '_path__destination', 'cable', '_link_peer', 'tags' + ) serializer_class = serializers.ConsolePortSerializer filterset_class = filtersets.ConsolePortFilterSet brief_prefetch_fields = ['device'] @@ -547,7 +549,7 @@ class ConsolePortViewSet(PathEndpointMixin, ModelViewSet): class ConsoleServerPortViewSet(PathEndpointMixin, ModelViewSet): queryset = ConsoleServerPort.objects.prefetch_related( - 'device', '_path__destination', 'cable', '_link_peer', 'tags' + 'device', 'module__module_bay', '_path__destination', 'cable', '_link_peer', 'tags' ) serializer_class = serializers.ConsoleServerPortSerializer filterset_class = filtersets.ConsoleServerPortFilterSet @@ -555,14 +557,18 @@ class ConsoleServerPortViewSet(PathEndpointMixin, ModelViewSet): class PowerPortViewSet(PathEndpointMixin, ModelViewSet): - queryset = PowerPort.objects.prefetch_related('device', '_path__destination', 'cable', '_link_peer', 'tags') + queryset = PowerPort.objects.prefetch_related( + 'device', 'module__module_bay', '_path__destination', 'cable', '_link_peer', 'tags' + ) serializer_class = serializers.PowerPortSerializer filterset_class = filtersets.PowerPortFilterSet brief_prefetch_fields = ['device'] class PowerOutletViewSet(PathEndpointMixin, ModelViewSet): - queryset = PowerOutlet.objects.prefetch_related('device', '_path__destination', 'cable', '_link_peer', 'tags') + queryset = PowerOutlet.objects.prefetch_related( + 'device', 'module__module_bay', '_path__destination', 'cable', '_link_peer', 'tags' + ) serializer_class = serializers.PowerOutletSerializer filterset_class = filtersets.PowerOutletFilterSet brief_prefetch_fields = ['device'] @@ -570,8 +576,8 @@ class PowerOutletViewSet(PathEndpointMixin, ModelViewSet): class InterfaceViewSet(PathEndpointMixin, ModelViewSet): queryset = Interface.objects.prefetch_related( - 'device', 'parent', 'bridge', 'lag', '_path__destination', 'cable', '_link_peer', 'wireless_lans', - 'untagged_vlan', 'tagged_vlans', 'ip_addresses', 'fhrp_group_assignments', 'tags' + 'device', 'module__module_bay', 'parent', 'bridge', 'lag', '_path__destination', 'cable', '_link_peer', + 'wireless_lans', 'untagged_vlan', 'tagged_vlans', 'ip_addresses', 'fhrp_group_assignments', 'tags' ) serializer_class = serializers.InterfaceSerializer filterset_class = filtersets.InterfaceFilterSet @@ -579,14 +585,18 @@ class InterfaceViewSet(PathEndpointMixin, ModelViewSet): class FrontPortViewSet(PassThroughPortMixin, ModelViewSet): - queryset = FrontPort.objects.prefetch_related('device__device_type__manufacturer', 'rear_port', 'cable', 'tags') + queryset = FrontPort.objects.prefetch_related( + 'device__device_type__manufacturer', 'module__module_bay', 'rear_port', 'cable', 'tags' + ) serializer_class = serializers.FrontPortSerializer filterset_class = filtersets.FrontPortFilterSet brief_prefetch_fields = ['device'] class RearPortViewSet(PassThroughPortMixin, ModelViewSet): - queryset = RearPort.objects.prefetch_related('device__device_type__manufacturer', 'cable', 'tags') + queryset = RearPort.objects.prefetch_related( + 'device__device_type__manufacturer', 'module__module_bay', 'cable', 'tags' + ) serializer_class = serializers.RearPortSerializer filterset_class = filtersets.RearPortFilterSet brief_prefetch_fields = ['device']