diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index b241a0bbd..edcd93ef2 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -476,6 +476,16 @@ class NestedClusterSerializer(serializers.ModelSerializer): fields = ['id', 'url', 'name'] +# Cannot import NestedVirtualChassisSerializer due to circular dependency +class DeviceVirtualChassisSerializer(serializers.ModelSerializer): + url = serializers.HyperlinkedIdentityField(view_name='dcim-api:virtualchassis-detail') + master = NestedDeviceSerializer() + + class Meta: + model = VirtualChassis + fields = ['id', 'url', 'master'] + + class DeviceSerializer(CustomFieldModelSerializer): device_type = NestedDeviceTypeSerializer() device_role = NestedDeviceRoleSerializer() @@ -490,13 +500,14 @@ class DeviceSerializer(CustomFieldModelSerializer): primary_ip6 = DeviceIPAddressSerializer() parent_device = serializers.SerializerMethodField() cluster = NestedClusterSerializer() + virtual_chassis = DeviceVirtualChassisSerializer() class Meta: model = Device fields = [ 'id', 'name', 'display_name', 'device_type', 'device_role', 'tenant', 'platform', 'serial', 'asset_tag', - 'site', 'rack', 'position', 'face', 'parent_device', 'virtual_chassis', 'status', 'primary_ip', - 'primary_ip4', 'primary_ip6', 'cluster', 'virtual_chassis', 'comments', 'custom_fields', 'created', + 'site', 'rack', 'position', 'face', 'parent_device', 'status', 'primary_ip', 'primary_ip4', 'primary_ip6', + 'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments', 'custom_fields', 'created', 'last_updated', ] @@ -517,8 +528,8 @@ class WritableDeviceSerializer(CustomFieldModelSerializer): model = Device fields = [ 'id', 'name', 'device_type', 'device_role', 'tenant', 'platform', 'serial', 'asset_tag', 'site', 'rack', - 'position', 'face', 'status', 'primary_ip4', 'primary_ip6', 'cluster', 'comments', 'custom_fields', - 'created', 'last_updated', + 'position', 'face', 'status', 'primary_ip4', 'primary_ip6', 'cluster', 'virtual_chassis', 'vc_position', + 'vc_priority', 'comments', 'custom_fields', 'created', 'last_updated', ] validators = [] diff --git a/netbox/dcim/api/views.py b/netbox/dcim/api/views.py index 7ea51b870..12e657e79 100644 --- a/netbox/dcim/api/views.py +++ b/netbox/dcim/api/views.py @@ -235,7 +235,8 @@ class PlatformViewSet(ModelViewSet): class DeviceViewSet(CustomFieldModelViewSet): queryset = Device.objects.select_related( - 'device_type__manufacturer', 'device_role', 'tenant', 'platform', 'site', 'rack', 'parent_bay', 'vc_membership', + 'device_type__manufacturer', 'device_role', 'tenant', 'platform', 'site', 'rack', 'parent_bay', + 'virtual_chassis__master', ).prefetch_related( 'primary_ip4__nat_outside', 'primary_ip6__nat_outside', ) diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py index b1b33be85..25fb057fc 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -487,6 +487,11 @@ class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet): method='_has_primary_ip', label='Has a primary IP', ) + virtual_chassis_id = django_filters.ModelMultipleChoiceFilter( + name='virtual_chassis', + queryset=VirtualChassis.objects.all(), + label='Virtual chassis (ID)', + ) class Meta: model = Device diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index 33fa173cb..6ee666cde 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -1078,8 +1078,8 @@ class Device(CreatedUpdatedModel, CustomFieldModel): def display_name(self): if self.name: return self.name - elif hasattr(self, 'vc_membership') and self.vc_membership.virtual_chassis.master.name: - return "{}:{}".format(self.vc_membership.virtual_chassis.master, self.vc_membership.position) + elif hasattr(self, 'virtual_chassis') and self.virtual_chassis.master.name: + return "{}:{}".format(self.virtual_chassis.master, self.vc_position) elif hasattr(self, 'device_type'): return "{}".format(self.device_type) return "" @@ -1108,8 +1108,8 @@ class Device(CreatedUpdatedModel, CustomFieldModel): """ If this Device is a VirtualChassis member, return the VC master. Otherwise, return None. """ - if hasattr(self, 'vc_membership'): - return self.vc_membership.virtual_chassis.master + if hasattr(self, 'virtual_chassis'): + return self.virtual_chassis.master else: return None @@ -1117,11 +1117,11 @@ class Device(CreatedUpdatedModel, CustomFieldModel): def vc_interfaces(self): """ Return a QuerySet matching all Interfaces assigned to this Device or, if this Device is a VC master, to another - Device belonging to the same virtual chassis. + Device belonging to the same VirtualChassis. """ filter = Q(device=self) - if hasattr(self, 'vc_membership') and self.vc_membership.is_master: - filter |= Q(device__vc_membership__virtual_chassis=self.vc_membership.virtual_chassis, mgmt_only=False) + if hasattr(self, 'virtual_chassis') and self.virtual_chassis.master == self: + filter |= Q(device__virtual_chassis=self.virtual_chassis, mgmt_only=False) return Interface.objects.filter(filter) def get_children(self):