diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index f8e195a98..ede7e340f 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -6,10 +6,8 @@ from timezone_field.rest_framework import TimeZoneSerializerField from dcim.choices import * from dcim.constants import * from dcim.models import * -from ipam.api.nested_serializers import ( - NestedASNSerializer, NestedFHRPGroupAssignmentSerializer, NestedIPAddressSerializer, NestedVLANSerializer, -) -from ipam.models import ASN, FHRPGroupAssignment, VLAN +from ipam.api.nested_serializers import NestedASNSerializer, NestedIPAddressSerializer, NestedVLANSerializer +from ipam.models import ASN, VLAN from netbox.api import ChoiceField, ContentTypeField, SerializedPKRelatedField from netbox.api.serializers import ( NestedGroupModelSerializer, PrimaryModelSerializer, ValidatedModelSerializer, WritableNestedSerializer, diff --git a/netbox/ipam/api/nested_serializers.py b/netbox/ipam/api/nested_serializers.py index bb0c02a47..1eb66743b 100644 --- a/netbox/ipam/api/nested_serializers.py +++ b/netbox/ipam/api/nested_serializers.py @@ -7,6 +7,7 @@ __all__ = [ 'NestedAggregateSerializer', 'NestedASNSerializer', 'NestedFHRPGroupSerializer', + 'NestedFHRPGroupAssignmentSerializer', 'NestedIPAddressSerializer', 'NestedIPRangeSerializer', 'NestedPrefixSerializer', @@ -94,6 +95,10 @@ class NestedFHRPGroupSerializer(WritableNestedSerializer): class NestedFHRPGroupAssignmentSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField(view_name='ipam-api:fhrpgroupassignment-detail') + class Meta: + model = models.FHRPGroupAssignment + fields = ['id', 'url', 'display', 'interface_type', 'interface_id', 'group_id', 'priority'] + # # VLANs diff --git a/netbox/ipam/api/serializers.py b/netbox/ipam/api/serializers.py index 28be07334..aa1d2834a 100644 --- a/netbox/ipam/api/serializers.py +++ b/netbox/ipam/api/serializers.py @@ -127,6 +127,7 @@ class FHRPGroupSerializer(PrimaryModelSerializer): class FHRPGroupAssignmentSerializer(PrimaryModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:contactassignment-detail') + group = NestedFHRPGroupSerializer() interface_type = ContentTypeField( queryset=ContentType.objects.all() ) @@ -135,7 +136,7 @@ class FHRPGroupAssignmentSerializer(PrimaryModelSerializer): class Meta: model = FHRPGroupAssignment fields = [ - 'id', 'url', 'display', 'interface_type', 'interface_id', 'interface', 'priority', 'created', + 'id', 'url', 'display', 'group', 'interface_type', 'interface_id', 'interface', 'priority', 'created', 'last_updated', ] diff --git a/netbox/ipam/graphql/types.py b/netbox/ipam/graphql/types.py index 72526b3bd..d9aec66b3 100644 --- a/netbox/ipam/graphql/types.py +++ b/netbox/ipam/graphql/types.py @@ -2,7 +2,7 @@ import graphene from ipam import filtersets, models from netbox.graphql.scalars import BigInt -from netbox.graphql.types import OrganizationalObjectType, PrimaryObjectType +from netbox.graphql.types import BaseObjectType, OrganizationalObjectType, PrimaryObjectType __all__ = ( 'ASNType', @@ -50,7 +50,7 @@ class FHRPGroupType(PrimaryObjectType): return self.auth_type or None -class FHRPGroupAssignmentType(PrimaryObjectType): +class FHRPGroupAssignmentType(BaseObjectType): class Meta: model = models.FHRPGroupAssignment diff --git a/netbox/ipam/tests/test_api.py b/netbox/ipam/tests/test_api.py index 5ec0a0177..50eb64060 100644 --- a/netbox/ipam/tests/test_api.py +++ b/netbox/ipam/tests/test_api.py @@ -4,11 +4,11 @@ from django.urls import reverse from netaddr import IPNetwork from rest_framework import status -from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site +from dcim.models import Device, DeviceRole, DeviceType, Interface, Manufacturer, Site from ipam.choices import * from ipam.models import * from tenancy.models import Tenant -from utilities.testing import APITestCase, APIViewTestCases, disable_warnings +from utilities.testing import APITestCase, APIViewTestCases, create_test_device, disable_warnings class AppTest(APITestCase): @@ -585,6 +585,85 @@ class FHRPGroupTest(APIViewTestCases.APIViewTestCase): ] +class FHRPGroupAssignmentTest(APIViewTestCases.APIViewTestCase): + model = FHRPGroupAssignment + brief_fields = ['display', 'group_id', 'id', 'interface_id', 'interface_type', 'priority', 'url'] + bulk_update_data = { + 'priority': 100, + } + + @classmethod + def setUpTestData(cls): + + device1 = create_test_device('device1') + device2 = create_test_device('device2') + device3 = create_test_device('device3') + + interfaces = ( + Interface(device=device1, name='eth0', type='other'), + Interface(device=device1, name='eth1', type='other'), + Interface(device=device1, name='eth2', type='other'), + Interface(device=device2, name='eth0', type='other'), + Interface(device=device2, name='eth1', type='other'), + Interface(device=device2, name='eth2', type='other'), + Interface(device=device3, name='eth0', type='other'), + Interface(device=device3, name='eth1', type='other'), + Interface(device=device3, name='eth2', type='other'), + ) + Interface.objects.bulk_create(interfaces) + + ip_addresses = ( + IPAddress(address=IPNetwork('192.168.0.2/24'), assigned_object=interfaces[0]), + IPAddress(address=IPNetwork('192.168.1.2/24'), assigned_object=interfaces[1]), + IPAddress(address=IPNetwork('192.168.2.2/24'), assigned_object=interfaces[2]), + IPAddress(address=IPNetwork('192.168.0.3/24'), assigned_object=interfaces[3]), + IPAddress(address=IPNetwork('192.168.1.3/24'), assigned_object=interfaces[4]), + IPAddress(address=IPNetwork('192.168.2.3/24'), assigned_object=interfaces[5]), + IPAddress(address=IPNetwork('192.168.0.4/24'), assigned_object=interfaces[6]), + IPAddress(address=IPNetwork('192.168.1.4/24'), assigned_object=interfaces[7]), + IPAddress(address=IPNetwork('192.168.2.4/24'), assigned_object=interfaces[8]), + ) + IPAddress.objects.bulk_create(ip_addresses) + + fhrp_groups = ( + FHRPGroup(protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP2, group_id=10), + FHRPGroup(protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP2, group_id=20), + FHRPGroup(protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP2, group_id=30), + ) + FHRPGroup.objects.bulk_create(fhrp_groups) + + fhrp_group_assignments = ( + FHRPGroupAssignment(group=fhrp_groups[0], interface=interfaces[0], priority=10), + FHRPGroupAssignment(group=fhrp_groups[1], interface=interfaces[1], priority=10), + FHRPGroupAssignment(group=fhrp_groups[2], interface=interfaces[2], priority=10), + FHRPGroupAssignment(group=fhrp_groups[0], interface=interfaces[3], priority=20), + FHRPGroupAssignment(group=fhrp_groups[1], interface=interfaces[4], priority=20), + FHRPGroupAssignment(group=fhrp_groups[2], interface=interfaces[5], priority=20), + ) + FHRPGroupAssignment.objects.bulk_create(fhrp_group_assignments) + + cls.create_data = [ + { + 'group': fhrp_groups[0].pk, + 'interface_type': 'dcim.interface', + 'interface_id': interfaces[6].pk, + 'priority': 30, + }, + { + 'group': fhrp_groups[1].pk, + 'interface_type': 'dcim.interface', + 'interface_id': interfaces[7].pk, + 'priority': 30, + }, + { + 'group': fhrp_groups[2].pk, + 'interface_type': 'dcim.interface', + 'interface_id': interfaces[8].pk, + 'priority': 30, + }, + ] + + class VLANGroupTest(APIViewTestCases.APIViewTestCase): model = VLANGroup brief_fields = ['display', 'id', 'name', 'slug', 'url', 'vlan_count']