From b78eebed312355730ef1ac24b98da2ac71600c45 Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Fri, 28 Oct 2022 14:52:05 -0500 Subject: [PATCH] Fix missing NestedSerializer. --- netbox/dcim/api/nested_serializers.py | 10 ++++++++ netbox/dcim/api/serializers.py | 24 ++++++++++++------- netbox/dcim/forms/model_forms.py | 12 +++++----- ..._vdc_type_virtualdevicecontext_and_more.py | 2 +- netbox/dcim/models/device_components.py | 2 +- netbox/dcim/models/devices.py | 14 +++++++---- netbox/dcim/signals.py | 6 ++--- netbox/templates/dcim/devicetype.html | 6 +++++ .../templates/dcim/virtualdevicecontext.html | 5 ++++ 9 files changed, 56 insertions(+), 25 deletions(-) diff --git a/netbox/dcim/api/nested_serializers.py b/netbox/dcim/api/nested_serializers.py index f5e06e155..327d26703 100644 --- a/netbox/dcim/api/nested_serializers.py +++ b/netbox/dcim/api/nested_serializers.py @@ -45,6 +45,7 @@ __all__ = [ 'NestedSiteSerializer', 'NestedSiteGroupSerializer', 'NestedVirtualChassisSerializer', + 'NestedVirtualDeviceContextSerializer', ] @@ -466,3 +467,12 @@ class NestedPowerFeedSerializer(WritableNestedSerializer): class Meta: model = models.PowerFeed fields = ['id', 'url', 'display', 'name', 'cable', '_occupied'] + + +class NestedVirtualDeviceContextSerializer(WritableNestedSerializer): + url = serializers.HyperlinkedIdentityField(view_name='dcim-api:virtualdevicecontext-detail') + device = NestedDeviceSerializer() + + class Meta: + model = models.VirtualDeviceContext + fields = ['id', 'url', 'display', 'name', 'identifier', 'device', 'vdc_type'] diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index 71f170bee..4576bf158 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -317,6 +317,7 @@ class DeviceTypeSerializer(NetBoxModelSerializer): ) subdevice_role = ChoiceField(choices=SubdeviceRoleChoices, allow_blank=True, required=False) airflow = ChoiceField(choices=DeviceAirflowChoices, allow_blank=True, required=False) + vdc_type = ChoiceField(choices=VirtualDeviceContextTypeChoices, allow_blank=True, required=False) weight_unit = ChoiceField(choices=WeightUnitChoices, allow_blank=True, required=False) device_count = serializers.IntegerField(read_only=True) @@ -324,8 +325,8 @@ class DeviceTypeSerializer(NetBoxModelSerializer): model = DeviceType fields = [ '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', 'vdc_type', 'airflow', 'weight', 'weight_unit', 'front_image', 'rear_image', 'comments', + 'tags', 'custom_fields', 'created', 'last_updated', 'device_count', ] @@ -840,6 +841,11 @@ class PowerPortSerializer(NetBoxModelSerializer, CabledObjectSerializer, Connect class InterfaceSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail') device = NestedDeviceSerializer() + vdcs = NestedVirtualDeviceContextSerializer( + required=False, + allow_null=True, + many=True + ) module = ComponentNestedModuleSerializer( required=False, allow_null=True @@ -876,13 +882,13 @@ class InterfaceSerializer(NetBoxModelSerializer, CabledObjectSerializer, Connect class Meta: model = Interface fields = [ - 'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'enabled', 'parent', 'bridge', 'lag', - 'mtu', 'mac_address', 'speed', 'duplex', 'wwn', 'mgmt_only', 'description', 'mode', 'rf_role', 'rf_channel', - 'poe_mode', 'poe_type', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'untagged_vlan', - 'tagged_vlans', 'mark_connected', 'cable', 'cable_end', 'wireless_link', 'link_peers', 'link_peers_type', - 'wireless_lans', 'vrf', 'l2vpn_termination', 'connected_endpoints', 'connected_endpoints_type', - 'connected_endpoints_reachable', 'tags', 'custom_fields', 'created', 'last_updated', 'count_ipaddresses', - 'count_fhrp_groups', '_occupied', + 'id', 'url', 'display', 'device', 'vdcs', 'module', 'name', 'label', 'type', 'enabled', 'parent', 'bridge', + 'lag', 'mtu', 'mac_address', 'speed', 'duplex', 'wwn', 'mgmt_only', 'description', 'mode', 'rf_role', + 'rf_channel', 'poe_mode', 'poe_type', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', + 'untagged_vlan', 'tagged_vlans', 'mark_connected', 'cable', 'cable_end', 'wireless_link', 'link_peers', + 'link_peers_type', 'wireless_lans', 'vrf', 'l2vpn_termination', 'connected_endpoints', + 'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields', 'created', + 'last_updated', 'count_ipaddresses', 'count_fhrp_groups', '_occupied', ] def validate(self, data): diff --git a/netbox/dcim/forms/model_forms.py b/netbox/dcim/forms/model_forms.py index 6b12ff2d6..87eb078a9 100644 --- a/netbox/dcim/forms/model_forms.py +++ b/netbox/dcim/forms/model_forms.py @@ -397,7 +397,7 @@ class DeviceTypeForm(NetBoxModelForm): model = DeviceType fields = [ 'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow', - 'vdc_type', 'vdc_type', 'weight', 'weight_unit', 'front_image', 'rear_image', 'comments', 'tags', + 'vdc_type', 'weight', 'weight_unit', 'front_image', 'rear_image', 'comments', 'tags', ] widgets = { 'airflow': StaticSelect(), @@ -1375,7 +1375,7 @@ class PowerOutletForm(ModularDeviceComponentForm): class InterfaceForm(InterfaceCommonForm, ModularDeviceComponentForm): - vdc = DynamicModelMultipleChoiceField( + vdcs = DynamicModelMultipleChoiceField( queryset=VirtualDeviceContext.objects.all(), required=False, label='Virtual Device Contexts', @@ -1457,7 +1457,7 @@ class InterfaceForm(InterfaceCommonForm, ModularDeviceComponentForm): ) fieldsets = ( - ('Interface', ('device', 'module', 'vdc', 'name', 'label', 'type', 'speed', 'duplex', 'description', 'tags')), + ('Interface', ('device', 'module', 'vdcs', 'name', 'label', 'type', 'speed', 'duplex', 'description', 'tags')), ('Addressing', ('vrf', 'mac_address', 'wwn')), ('Operation', ('mtu', 'tx_power', 'enabled', 'mgmt_only', 'mark_connected')), ('Related Interfaces', ('parent', 'bridge', 'lag')), @@ -1471,7 +1471,7 @@ class InterfaceForm(InterfaceCommonForm, ModularDeviceComponentForm): class Meta: model = Interface fields = [ - 'device', 'module', 'vdc', 'name', 'label', 'type', 'speed', 'duplex', 'enabled', 'parent', 'bridge', 'lag', + 'device', 'module', 'vdcs', 'name', 'label', 'type', 'speed', 'duplex', 'enabled', 'parent', 'bridge', 'lag', 'mac_address', 'wwn', 'mtu', 'mgmt_only', 'mark_connected', 'description', 'poe_mode', 'poe_type', 'mode', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'wireless_lans', 'untagged_vlan', 'tagged_vlans', 'vrf', 'tags', @@ -1498,9 +1498,9 @@ class InterfaceForm(InterfaceCommonForm, ModularDeviceComponentForm): def clean_vdc(self): device = self.cleaned_data.get('device') if device.device_type.vdc_type not in [VirtualDeviceContextTypeChoices.CISCO_ASA_CONTEXT, VirtualDeviceContextTypeChoices.CISCO_FTD_INSTANCE]\ - and len(self.cleaned_data.get('vdc')) > 1: + and len(self.cleaned_data.get('vdcs')) > 1: raise forms.ValidationError(f"You cannot assign more then 1 VDC for {device.device_type}") - return self.cleaned_data.get('vdc') + return self.cleaned_data.get('vdcs') class FrontPortForm(ModularDeviceComponentForm): diff --git a/netbox/dcim/migrations/0164_devicetype_vdc_type_virtualdevicecontext_and_more.py b/netbox/dcim/migrations/0164_devicetype_vdc_type_virtualdevicecontext_and_more.py index cd4762e43..37a8795e1 100644 --- a/netbox/dcim/migrations/0164_devicetype_vdc_type_virtualdevicecontext_and_more.py +++ b/netbox/dcim/migrations/0164_devicetype_vdc_type_virtualdevicecontext_and_more.py @@ -45,7 +45,7 @@ class Migration(migrations.Migration): ), migrations.AddField( model_name='interface', - name='vdc', + name='vdcs', field=models.ManyToManyField(related_name='interfaces', to='dcim.virtualdevicecontext'), ), migrations.AddConstraint( diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index 6e46d7707..45ce4a23f 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -531,7 +531,7 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd max_length=100, blank=True ) - vdc = models.ManyToManyField( + vdcs = models.ManyToManyField( to='dcim.VirtualDeviceContext', related_name='interfaces' ) diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py index 176b72273..05e9465e4 100644 --- a/netbox/dcim/models/devices.py +++ b/netbox/dcim/models/devices.py @@ -124,17 +124,17 @@ class DeviceType(NetBoxModel, WeightMixin): help_text='Parent devices house child devices in device bays. Leave blank ' 'if this device type is neither a parent nor a child.' ) + airflow = models.CharField( + max_length=50, + choices=DeviceAirflowChoices, + blank=True + ) vdc_type = models.CharField( max_length=50, blank=True, choices=VirtualDeviceContextTypeChoices, verbose_name='VDC Type' ) - airflow = models.CharField( - max_length=50, - choices=DeviceAirflowChoices, - blank=True - ) front_image = models.ImageField( upload_to='devicetype-images', blank=True @@ -1217,3 +1217,7 @@ class VirtualDeviceContext(NetBoxModel): return self.primary_ip4 else: return None + + @property + def vdc_type(self): + return self.device.device_type.vdc_type diff --git a/netbox/dcim/signals.py b/netbox/dcim/signals.py index 9c5ad89a7..5ccaceddb 100644 --- a/netbox/dcim/signals.py +++ b/netbox/dcim/signals.py @@ -127,13 +127,13 @@ def nullify_connected_endpoints(instance, **kwargs): cablepath.retrace() -@receiver(m2m_changed, sender=Interface.vdc.through) +@receiver(m2m_changed, sender=Interface.vdcs.through) def enforce_vdc_type_restrictions(instance, **kwargs): if 'action' == 'post_add': device = instance.device if device.device_type.vdc_type not in [VirtualDeviceContextTypeChoices.CISCO_ASA_CONTEXT, VirtualDeviceContextTypeChoices.CISCO_FTD_INSTANCE] \ - and len(instance.vdc) > 1: + and len(instance.vdcs) > 1: print('Error') raise forms.ValidationError({ - 'vdc': f"You cannot assign more then 1 VDC for {device.device_type}" + 'vdcs': f"You cannot assign more then 1 VDC for {device.device_type}" }) diff --git a/netbox/templates/dcim/devicetype.html b/netbox/templates/dcim/devicetype.html index 458c74ac1..62b5c171f 100644 --- a/netbox/templates/dcim/devicetype.html +++ b/netbox/templates/dcim/devicetype.html @@ -57,6 +57,12 @@ {{ object.get_airflow_display|placeholder }} + + VDC Type + + {{ object.get_vdc_type_display|placeholder }} + + Front Image diff --git a/netbox/templates/dcim/virtualdevicecontext.html b/netbox/templates/dcim/virtualdevicecontext.html index 91ecd063b..d7f73e293 100644 --- a/netbox/templates/dcim/virtualdevicecontext.html +++ b/netbox/templates/dcim/virtualdevicecontext.html @@ -36,6 +36,11 @@ Identifier {{ object.identifier|placeholder }} + + + VDC Type + {{ object.device.device_type.get_vdc_type_display |placeholder }} + Primary IPv4