mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-24 17:38:37 -06:00
#6732: Add asns relationship to SiteSerializer and extend tests
This commit is contained in:
parent
7a97d5d4eb
commit
7a55832a22
@ -1,4 +1,3 @@
|
|||||||
from django.conf import settings
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from drf_yasg.utils import swagger_serializer_method
|
from drf_yasg.utils import swagger_serializer_method
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
@ -7,8 +6,8 @@ from timezone_field.rest_framework import TimeZoneSerializerField
|
|||||||
from dcim.choices import *
|
from dcim.choices import *
|
||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
from dcim.models import *
|
from dcim.models import *
|
||||||
from ipam.api.nested_serializers import NestedIPAddressSerializer, NestedVLANSerializer, NestedASNSerializer
|
from ipam.api.nested_serializers import NestedASNSerializer, NestedIPAddressSerializer, NestedVLANSerializer
|
||||||
from ipam.models import VLAN
|
from ipam.models import ASN, VLAN
|
||||||
from netbox.api import ChoiceField, ContentTypeField, SerializedPKRelatedField
|
from netbox.api import ChoiceField, ContentTypeField, SerializedPKRelatedField
|
||||||
from netbox.api.serializers import (
|
from netbox.api.serializers import (
|
||||||
NestedGroupModelSerializer, PrimaryModelSerializer, ValidatedModelSerializer, WritableNestedSerializer,
|
NestedGroupModelSerializer, PrimaryModelSerializer, ValidatedModelSerializer, WritableNestedSerializer,
|
||||||
@ -113,13 +112,19 @@ class SiteSerializer(PrimaryModelSerializer):
|
|||||||
region = NestedRegionSerializer(required=False, allow_null=True)
|
region = NestedRegionSerializer(required=False, allow_null=True)
|
||||||
group = NestedSiteGroupSerializer(required=False, allow_null=True)
|
group = NestedSiteGroupSerializer(required=False, allow_null=True)
|
||||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||||
asns = NestedASNSerializer(many=True, required=False, allow_null=True)
|
|
||||||
time_zone = TimeZoneSerializerField(required=False)
|
time_zone = TimeZoneSerializerField(required=False)
|
||||||
|
asns = SerializedPKRelatedField(
|
||||||
|
queryset=ASN.objects.all(),
|
||||||
|
serializer=NestedASNSerializer,
|
||||||
|
required=False,
|
||||||
|
many=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Related object counts
|
||||||
circuit_count = serializers.IntegerField(read_only=True)
|
circuit_count = serializers.IntegerField(read_only=True)
|
||||||
device_count = serializers.IntegerField(read_only=True)
|
device_count = serializers.IntegerField(read_only=True)
|
||||||
prefix_count = serializers.IntegerField(read_only=True)
|
prefix_count = serializers.IntegerField(read_only=True)
|
||||||
rack_count = serializers.IntegerField(read_only=True)
|
rack_count = serializers.IntegerField(read_only=True)
|
||||||
asn_count = serializers.IntegerField(read_only=True)
|
|
||||||
virtualmachine_count = serializers.IntegerField(read_only=True)
|
virtualmachine_count = serializers.IntegerField(read_only=True)
|
||||||
vlan_count = serializers.IntegerField(read_only=True)
|
vlan_count = serializers.IntegerField(read_only=True)
|
||||||
|
|
||||||
@ -129,8 +134,7 @@ class SiteSerializer(PrimaryModelSerializer):
|
|||||||
'id', 'url', 'display', 'name', 'slug', 'status', 'region', 'group', 'tenant', 'facility', 'asn', 'asns',
|
'id', 'url', 'display', 'name', 'slug', 'status', 'region', 'group', 'tenant', 'facility', 'asn', 'asns',
|
||||||
'time_zone', 'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'contact_name',
|
'time_zone', 'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'contact_name',
|
||||||
'contact_phone', 'contact_email', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
|
'contact_phone', 'contact_email', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
|
||||||
'asn_count', 'circuit_count', 'device_count', 'prefix_count', 'rack_count', 'virtualmachine_count',
|
'circuit_count', 'device_count', 'prefix_count', 'rack_count', 'virtualmachine_count', 'vlan_count',
|
||||||
'vlan_count',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -137,9 +137,8 @@ class SiteGroupViewSet(CustomFieldModelViewSet):
|
|||||||
|
|
||||||
class SiteViewSet(CustomFieldModelViewSet):
|
class SiteViewSet(CustomFieldModelViewSet):
|
||||||
queryset = Site.objects.prefetch_related(
|
queryset = Site.objects.prefetch_related(
|
||||||
'region', 'tenant', 'tags'
|
'region', 'tenant', 'asns', 'tags'
|
||||||
).annotate(
|
).annotate(
|
||||||
asn_count=count_related(ASN, 'sites'),
|
|
||||||
device_count=count_related(Device, 'site'),
|
device_count=count_related(Device, 'site'),
|
||||||
rack_count=count_related(Rack, 'site'),
|
rack_count=count_related(Rack, 'site'),
|
||||||
prefix_count=count_related(Prefix, 'site'),
|
prefix_count=count_related(Prefix, 'site'),
|
||||||
|
@ -6,7 +6,7 @@ from rest_framework import status
|
|||||||
from dcim.choices import *
|
from dcim.choices import *
|
||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
from dcim.models import *
|
from dcim.models import *
|
||||||
from ipam.models import VLAN
|
from ipam.models import ASN, RIR, VLAN
|
||||||
from utilities.testing import APITestCase, APIViewTestCases
|
from utilities.testing import APITestCase, APIViewTestCases
|
||||||
from virtualization.models import Cluster, ClusterType
|
from virtualization.models import Cluster, ClusterType
|
||||||
|
|
||||||
@ -143,6 +143,13 @@ class SiteTest(APIViewTestCases.APIViewTestCase):
|
|||||||
)
|
)
|
||||||
Site.objects.bulk_create(sites)
|
Site.objects.bulk_create(sites)
|
||||||
|
|
||||||
|
rir = RIR.objects.create(name='RFC 6996', is_private=True)
|
||||||
|
|
||||||
|
asns = [
|
||||||
|
ASN(asn=65000 + i, rir=rir) for i in range(8)
|
||||||
|
]
|
||||||
|
ASN.objects.bulk_create(asns)
|
||||||
|
|
||||||
cls.create_data = [
|
cls.create_data = [
|
||||||
{
|
{
|
||||||
'name': 'Site 4',
|
'name': 'Site 4',
|
||||||
@ -150,6 +157,7 @@ class SiteTest(APIViewTestCases.APIViewTestCase):
|
|||||||
'region': regions[1].pk,
|
'region': regions[1].pk,
|
||||||
'group': groups[1].pk,
|
'group': groups[1].pk,
|
||||||
'status': SiteStatusChoices.STATUS_ACTIVE,
|
'status': SiteStatusChoices.STATUS_ACTIVE,
|
||||||
|
'asns': [asns[0].pk, asns[1].pk],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Site 5',
|
'name': 'Site 5',
|
||||||
@ -157,6 +165,7 @@ class SiteTest(APIViewTestCases.APIViewTestCase):
|
|||||||
'region': regions[1].pk,
|
'region': regions[1].pk,
|
||||||
'group': groups[1].pk,
|
'group': groups[1].pk,
|
||||||
'status': SiteStatusChoices.STATUS_ACTIVE,
|
'status': SiteStatusChoices.STATUS_ACTIVE,
|
||||||
|
'asns': [asns[2].pk, asns[3].pk],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Site 6',
|
'name': 'Site 6',
|
||||||
@ -164,6 +173,7 @@ class SiteTest(APIViewTestCases.APIViewTestCase):
|
|||||||
'region': regions[1].pk,
|
'region': regions[1].pk,
|
||||||
'group': groups[1].pk,
|
'group': groups[1].pk,
|
||||||
'status': SiteStatusChoices.STATUS_ACTIVE,
|
'status': SiteStatusChoices.STATUS_ACTIVE,
|
||||||
|
'asns': [asns[4].pk, asns[5].pk],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ from netaddr import EUI
|
|||||||
from dcim.choices import *
|
from dcim.choices import *
|
||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
from dcim.models import *
|
from dcim.models import *
|
||||||
from ipam.models import ASN, VLAN, RIR
|
from ipam.models import ASN, RIR, VLAN
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from utilities.testing import ViewTestCases, create_tags, create_test_device
|
from utilities.testing import ViewTestCases, create_tags, create_test_device
|
||||||
|
|
||||||
@ -110,41 +110,24 @@ class SiteTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||||||
for group in groups:
|
for group in groups:
|
||||||
group.save()
|
group.save()
|
||||||
|
|
||||||
|
rir = RIR.objects.create(name='RFC 6996', is_private=True)
|
||||||
|
|
||||||
|
asns = [
|
||||||
|
ASN(asn=65000 + i, rir=rir) for i in range(8)
|
||||||
|
]
|
||||||
|
ASN.objects.bulk_create(asns)
|
||||||
|
|
||||||
sites = Site.objects.bulk_create([
|
sites = Site.objects.bulk_create([
|
||||||
Site(name='Site 1', slug='site-1', region=regions[0], group=groups[1]),
|
Site(name='Site 1', slug='site-1', region=regions[0], group=groups[1]),
|
||||||
Site(name='Site 2', slug='site-2', region=regions[0], group=groups[1]),
|
Site(name='Site 2', slug='site-2', region=regions[0], group=groups[1]),
|
||||||
Site(name='Site 3', slug='site-3', region=regions[0], group=groups[1]),
|
Site(name='Site 3', slug='site-3', region=regions[0], group=groups[1]),
|
||||||
])
|
])
|
||||||
|
sites[0].asns.set([asns[0], asns[1]])
|
||||||
|
sites[1].asns.set([asns[2], asns[3]])
|
||||||
|
sites[2].asns.set([asns[4], asns[5]])
|
||||||
|
|
||||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||||
|
|
||||||
rir = RIR.objects.create(name='RFC 6996', is_private=True)
|
|
||||||
|
|
||||||
asns = [
|
|
||||||
ASN(asn=65000, rir=rir),
|
|
||||||
ASN(asn=65001, rir=rir),
|
|
||||||
ASN(asn=65002, rir=rir),
|
|
||||||
ASN(asn=65003, rir=rir),
|
|
||||||
ASN(asn=65004, rir=rir),
|
|
||||||
ASN(asn=65005, rir=rir),
|
|
||||||
ASN(asn=65006, rir=rir),
|
|
||||||
ASN(asn=65007, rir=rir),
|
|
||||||
ASN(asn=65008, rir=rir),
|
|
||||||
ASN(asn=65009, rir=rir),
|
|
||||||
ASN(asn=65010, rir=rir),
|
|
||||||
]
|
|
||||||
ASN.objects.bulk_create(asns)
|
|
||||||
|
|
||||||
asns[0].sites.set([sites[0]])
|
|
||||||
asns[2].sites.set([sites[0]])
|
|
||||||
asns[3].sites.set([sites[1]])
|
|
||||||
asns[4].sites.set([sites[2]])
|
|
||||||
asns[5].sites.set([sites[1]])
|
|
||||||
asns[6].sites.set([sites[2]])
|
|
||||||
asns[7].sites.set([sites[2]])
|
|
||||||
asns[8].sites.set([sites[2]])
|
|
||||||
asns[10].sites.set([sites[0]])
|
|
||||||
|
|
||||||
cls.form_data = {
|
cls.form_data = {
|
||||||
'name': 'Site X',
|
'name': 'Site X',
|
||||||
'slug': 'site-x',
|
'slug': 'site-x',
|
||||||
@ -153,6 +136,7 @@ class SiteTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||||||
'group': groups[1].pk,
|
'group': groups[1].pk,
|
||||||
'tenant': None,
|
'tenant': None,
|
||||||
'facility': 'Facility X',
|
'facility': 'Facility X',
|
||||||
|
'asns': [asns[6].pk, asns[7].pk],
|
||||||
'time_zone': pytz.UTC,
|
'time_zone': pytz.UTC,
|
||||||
'description': 'Site description',
|
'description': 'Site description',
|
||||||
'physical_address': '742 Evergreen Terrace, Springfield, USA',
|
'physical_address': '742 Evergreen Terrace, Springfield, USA',
|
||||||
|
@ -23,7 +23,6 @@ from .nested_serializers import *
|
|||||||
class ASNSerializer(PrimaryModelSerializer):
|
class ASNSerializer(PrimaryModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:asn-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:asn-detail')
|
||||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||||
|
|
||||||
site_count = serializers.IntegerField(read_only=True)
|
site_count = serializers.IntegerField(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -73,11 +73,12 @@ class RIR(OrganizationalModel):
|
|||||||
|
|
||||||
@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks')
|
@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks')
|
||||||
class ASN(PrimaryModel):
|
class ASN(PrimaryModel):
|
||||||
|
"""
|
||||||
|
An autonomous system (AS) number is typically used to represent an independent routing domain. A site can have
|
||||||
|
one or more ASNs assigned to it.
|
||||||
|
"""
|
||||||
asn = ASNField(
|
asn = ASNField(
|
||||||
unique=True,
|
unique=True,
|
||||||
blank=False,
|
|
||||||
null=False,
|
|
||||||
verbose_name='ASN',
|
verbose_name='ASN',
|
||||||
help_text='32-bit autonomous system number'
|
help_text='32-bit autonomous system number'
|
||||||
)
|
)
|
||||||
@ -89,8 +90,7 @@ class ASN(PrimaryModel):
|
|||||||
to='ipam.RIR',
|
to='ipam.RIR',
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='asns',
|
related_name='asns',
|
||||||
blank=False,
|
verbose_name='RIR'
|
||||||
null=False
|
|
||||||
)
|
)
|
||||||
tenant = models.ForeignKey(
|
tenant = models.ForeignKey(
|
||||||
to='tenancy.Tenant',
|
to='tenancy.Tenant',
|
||||||
|
Loading…
Reference in New Issue
Block a user