Fix tests; misc cleanup

This commit is contained in:
Jeremy Stretch 2024-10-09 14:25:27 -04:00
parent 09b4686440
commit d0b23f90fc
6 changed files with 72 additions and 77 deletions

View File

@ -61,7 +61,7 @@ class SiteSerializer(NetBoxModelSerializer):
# Related object counts
circuit_count = RelatedObjectCountField('circuit_terminations')
device_count = RelatedObjectCountField('devices')
prefix_count = RelatedObjectCountField('prefixes')
# prefix_count = RelatedObjectCountField('prefixes')
rack_count = RelatedObjectCountField('racks')
vlan_count = RelatedObjectCountField('vlans')
virtualmachine_count = RelatedObjectCountField('virtual_machines')
@ -72,7 +72,7 @@ class SiteSerializer(NetBoxModelSerializer):
'id', 'url', 'display_url', 'display', 'name', 'slug', 'status', 'region', 'group', 'tenant', 'facility',
'time_zone', 'description', 'physical_address', 'shipping_address', 'latitude', 'longitude',
'comments', 'asns', 'tags', 'custom_fields', 'created', 'last_updated', 'circuit_count', 'device_count',
'prefix_count', 'rack_count', 'virtualmachine_count', 'vlan_count',
'rack_count', 'virtualmachine_count', 'vlan_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'description', 'slug')

View File

@ -167,13 +167,11 @@ class PrefixImportForm(NetBoxModelImportForm):
to_field_name='name',
help_text=_('Assigned tenant')
)
# site = CSVModelChoiceField(
# label=_('Site'),
# queryset=Site.objects.all(),
# required=False,
# to_field_name='name',
# help_text=_('Assigned site')
# )
scope_type = CSVContentTypeField(
queryset=ContentType.objects.filter(model__in=PREFIX_SCOPE_TYPES),
required=False,
label=_('Scope type (app & model)')
)
vlan_group = CSVModelChoiceField(
label=_('VLAN group'),
queryset=VLANGroup.objects.all(),
@ -204,9 +202,12 @@ class PrefixImportForm(NetBoxModelImportForm):
class Meta:
model = Prefix
fields = (
'prefix', 'vrf', 'tenant', 'vlan_group', 'vlan', 'status', 'role', 'is_pool', 'mark_utilized',
'description', 'comments', 'tags',
'prefix', 'vrf', 'tenant', 'vlan_group', 'vlan', 'status', 'role', 'scope_type', 'scope_id', 'is_pool',
'mark_utilized', 'description', 'comments', 'tags',
)
labels = {
'scope_id': 'Scope ID',
}
def __init__(self, data=None, *args, **kwargs):
super().__init__(data, *args, **kwargs)

View File

@ -207,7 +207,7 @@ class PrefixForm(TenancyForm, NetBoxModelForm):
required=False,
label=_('Scope type')
)
scope = DynamicModelChoiceField(
scope_id = DynamicModelChoiceField(
label=_('Scope'),
queryset=Site.objects.none(), # Initial queryset
required=False,
@ -242,8 +242,8 @@ class PrefixForm(TenancyForm, NetBoxModelForm):
class Meta:
model = Prefix
fields = [
'prefix', 'vrf', 'vlan', 'scope_type', 'scope', 'status', 'role', 'is_pool', 'mark_utilized',
'tenant_group', 'tenant', 'description', 'comments', 'tags',
'prefix', 'vrf', 'vlan', 'status', 'role', 'is_pool', 'mark_utilized', 'tenant_group', 'tenant',
'description', 'comments', 'tags',
]
def __init__(self, *args, **kwargs):
@ -260,21 +260,21 @@ class PrefixForm(TenancyForm, NetBoxModelForm):
try:
scope_type = ContentType.objects.get(pk=scope_type_id)
model = scope_type.model_class()
self.fields['scope'].queryset = model.objects.all()
self.fields['scope'].widget.attrs['selector'] = model._meta.label_lower
self.fields['scope'].disabled = False
self.fields['scope'].label = _(bettertitle(model._meta.verbose_name))
self.fields['scope_id'].queryset = model.objects.all()
self.fields['scope_id'].widget.attrs['selector'] = model._meta.label_lower
self.fields['scope_id'].disabled = False
self.fields['scope_id'].label = _(bettertitle(model._meta.verbose_name))
except ObjectDoesNotExist:
pass
if self.instance and scope_type_id != self.instance.scope_type_id:
self.initial['scope'] = None
self.initial['scope_id'] = None
def clean(self):
super().clean()
# Assign the selected scope (if any)
self.instance.scope = self.cleaned_data.get('scope')
self.instance.scope = self.cleaned_data.get('scope_id')
class IPRangeForm(TenancyForm, NetBoxModelForm):

View File

@ -1,5 +1,6 @@
import datetime
from django.contrib.contenttypes.models import ContentType
from django.test import override_settings
from django.urls import reverse
from netaddr import IPNetwork
@ -409,9 +410,9 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase):
Role.objects.bulk_create(roles)
prefixes = (
Prefix(prefix=IPNetwork('10.1.0.0/16'), vrf=vrfs[0], site=sites[0], role=roles[0]),
Prefix(prefix=IPNetwork('10.2.0.0/16'), vrf=vrfs[0], site=sites[0], role=roles[0]),
Prefix(prefix=IPNetwork('10.3.0.0/16'), vrf=vrfs[0], site=sites[0], role=roles[0]),
Prefix(prefix=IPNetwork('10.1.0.0/16'), vrf=vrfs[0], scope=sites[0], role=roles[0]),
Prefix(prefix=IPNetwork('10.2.0.0/16'), vrf=vrfs[0], scope=sites[0], role=roles[0]),
Prefix(prefix=IPNetwork('10.3.0.0/16'), vrf=vrfs[0], scope=sites[0], role=roles[0]),
)
Prefix.objects.bulk_create(prefixes)
@ -419,7 +420,8 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase):
cls.form_data = {
'prefix': IPNetwork('192.0.2.0/24'),
'site': sites[1].pk,
'scope_type': ContentType.objects.get_for_model(Site).pk,
'scope_id': sites[1].pk,
'vrf': vrfs[1].pk,
'tenant': None,
'vlan': None,
@ -445,7 +447,6 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase):
)
cls.bulk_edit_data = {
'site': sites[1].pk,
'vrf': vrfs[1].pk,
'tenant': None,
'status': PrefixStatusChoices.STATUS_RESERVED,
@ -501,11 +502,13 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase):
"""
Custom import test for YAML-based imports (versus CSV)
"""
IMPORT_DATA = """
site = Site.objects.get(name='Site 1')
IMPORT_DATA = f"""
prefix: 10.1.1.0/24
status: active
vlan: 101
site: Site 1
scope_type: dcim.site
scope_id: {site.pk}
"""
# Note, a site is not tied to the VLAN to verify the fix for #12622
VLAN.objects.create(vid=101, name='VLAN101')
@ -523,19 +526,21 @@ site: Site 1
prefix = Prefix.objects.get(prefix='10.1.1.0/24')
self.assertEqual(prefix.status, PrefixStatusChoices.STATUS_ACTIVE)
self.assertEqual(prefix.vlan.vid, 101)
self.assertEqual(prefix.site.name, "Site 1")
self.assertEqual(prefix.scope, site)
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
def test_prefix_import_with_vlan_group(self):
"""
This test covers a unique import edge case where VLAN group is specified during the import.
"""
IMPORT_DATA = """
site = Site.objects.get(name='Site 1')
IMPORT_DATA = f"""
prefix: 10.1.2.0/24
status: active
vlan: 102
site: Site 1
scope_type: dcim.site
scope_id: {site.pk}
vlan_group: Group 1
vlan: 102
"""
vlan_group = VLANGroup.objects.create(name='Group 1', slug='group-1', scope=Site.objects.get(name="Site 1"))
VLAN.objects.create(vid=102, name='VLAN102', group=vlan_group)
@ -553,7 +558,7 @@ vlan_group: Group 1
prefix = Prefix.objects.get(prefix='10.1.2.0/24')
self.assertEqual(prefix.status, PrefixStatusChoices.STATUS_ACTIVE)
self.assertEqual(prefix.vlan.vid, 102)
self.assertEqual(prefix.site.name, "Site 1")
self.assertEqual(prefix.scope, site)
class IPRangeTestCase(ViewTestCases.PrimaryObjectViewTestCase):

View File

@ -352,7 +352,7 @@ class AggregatePrefixesView(generic.ObjectChildrenView):
def get_children(self, request, parent):
return Prefix.objects.restrict(request.user, 'view').filter(
prefix__net_contained_or_equal=str(parent.prefix)
).prefetch_related('site', 'role', 'tenant', 'tenant__group', 'vlan')
).prefetch_related('scope', 'role', 'tenant', 'tenant__group', 'vlan')
def prep_table_data(self, request, queryset, parent):
# Determine whether to show assigned prefixes, available prefixes, or both

View File

@ -8,8 +8,7 @@ from netaddr import IPNetwork
from rest_framework.test import APIClient
from core.models import ObjectType
from dcim.models import Site
from ipam.models import Prefix
from dcim.models import Rack, Site
from users.models import Group, ObjectPermission, Token, User
from utilities.testing import TestCase
from utilities.testing.api import APITestCase
@ -410,18 +409,18 @@ class ObjectPermissionAPIViewTestCase(TestCase):
)
Site.objects.bulk_create(cls.sites)
cls.prefixes = (
Prefix(prefix=IPNetwork('10.0.0.0/24'), site=cls.sites[0]),
Prefix(prefix=IPNetwork('10.0.1.0/24'), site=cls.sites[0]),
Prefix(prefix=IPNetwork('10.0.2.0/24'), site=cls.sites[0]),
Prefix(prefix=IPNetwork('10.0.3.0/24'), site=cls.sites[1]),
Prefix(prefix=IPNetwork('10.0.4.0/24'), site=cls.sites[1]),
Prefix(prefix=IPNetwork('10.0.5.0/24'), site=cls.sites[1]),
Prefix(prefix=IPNetwork('10.0.6.0/24'), site=cls.sites[2]),
Prefix(prefix=IPNetwork('10.0.7.0/24'), site=cls.sites[2]),
Prefix(prefix=IPNetwork('10.0.8.0/24'), site=cls.sites[2]),
cls.racks = (
Rack(name='Rack 1', site=cls.sites[0]),
Rack(name='Rack 2', site=cls.sites[0]),
Rack(name='Rack 3', site=cls.sites[0]),
Rack(name='Rack 4', site=cls.sites[1]),
Rack(name='Rack 5', site=cls.sites[1]),
Rack(name='Rack 6', site=cls.sites[1]),
Rack(name='Rack 7', site=cls.sites[2]),
Rack(name='Rack 8', site=cls.sites[2]),
Rack(name='Rack 9', site=cls.sites[2]),
)
Prefix.objects.bulk_create(cls.prefixes)
Rack.objects.bulk_create(cls.racks)
def setUp(self):
"""
@ -435,8 +434,7 @@ class ObjectPermissionAPIViewTestCase(TestCase):
def test_get_object(self):
# Attempt to retrieve object without permission
url = reverse('ipam-api:prefix-detail',
kwargs={'pk': self.prefixes[0].pk})
url = reverse('dcim-api:rack-detail', kwargs={'pk': self.racks[0].pk})
response = self.client.get(url, **self.header)
self.assertEqual(response.status_code, 403)
@ -448,23 +446,21 @@ class ObjectPermissionAPIViewTestCase(TestCase):
)
obj_perm.save()
obj_perm.users.add(self.user)
obj_perm.object_types.add(ObjectType.objects.get_for_model(Prefix))
obj_perm.object_types.add(ObjectType.objects.get_for_model(Rack))
# Retrieve permitted object
url = reverse('ipam-api:prefix-detail',
kwargs={'pk': self.prefixes[0].pk})
url = reverse('dcim-api:rack-detail', kwargs={'pk': self.racks[0].pk})
response = self.client.get(url, **self.header)
self.assertEqual(response.status_code, 200)
# Attempt to retrieve non-permitted object
url = reverse('ipam-api:prefix-detail',
kwargs={'pk': self.prefixes[3].pk})
url = reverse('dcim-api:rack-detail', kwargs={'pk': self.racks[3].pk})
response = self.client.get(url, **self.header)
self.assertEqual(response.status_code, 404)
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
def test_list_objects(self):
url = reverse('ipam-api:prefix-list')
url = reverse('dcim-api:rack-list')
# Attempt to list objects without permission
response = self.client.get(url, **self.header)
@ -478,7 +474,7 @@ class ObjectPermissionAPIViewTestCase(TestCase):
)
obj_perm.save()
obj_perm.users.add(self.user)
obj_perm.object_types.add(ObjectType.objects.get_for_model(Prefix))
obj_perm.object_types.add(ObjectType.objects.get_for_model(Rack))
# Retrieve all objects. Only permitted objects should be returned.
response = self.client.get(url, **self.header)
@ -487,12 +483,12 @@ class ObjectPermissionAPIViewTestCase(TestCase):
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
def test_create_object(self):
url = reverse('ipam-api:prefix-list')
url = reverse('dcim-api:rack-list')
data = {
'prefix': '10.0.9.0/24',
'name': 'Rack 10',
'site': self.sites[1].pk,
}
initial_count = Prefix.objects.count()
initial_count = Rack.objects.count()
# Attempt to create an object without permission
response = self.client.post(url, data, format='json', **self.header)
@ -506,26 +502,25 @@ class ObjectPermissionAPIViewTestCase(TestCase):
)
obj_perm.save()
obj_perm.users.add(self.user)
obj_perm.object_types.add(ObjectType.objects.get_for_model(Prefix))
obj_perm.object_types.add(ObjectType.objects.get_for_model(Rack))
# Attempt to create a non-permitted object
response = self.client.post(url, data, format='json', **self.header)
self.assertEqual(response.status_code, 403)
self.assertEqual(Prefix.objects.count(), initial_count)
self.assertEqual(Rack.objects.count(), initial_count)
# Create a permitted object
data['site'] = self.sites[0].pk
response = self.client.post(url, data, format='json', **self.header)
self.assertEqual(response.status_code, 201)
self.assertEqual(Prefix.objects.count(), initial_count + 1)
self.assertEqual(Rack.objects.count(), initial_count + 1)
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
def test_edit_object(self):
# Attempt to edit an object without permission
data = {'site': self.sites[0].pk}
url = reverse('ipam-api:prefix-detail',
kwargs={'pk': self.prefixes[0].pk})
url = reverse('dcim-api:rack-detail', kwargs={'pk': self.racks[0].pk})
response = self.client.patch(url, data, format='json', **self.header)
self.assertEqual(response.status_code, 403)
@ -537,26 +532,23 @@ class ObjectPermissionAPIViewTestCase(TestCase):
)
obj_perm.save()
obj_perm.users.add(self.user)
obj_perm.object_types.add(ObjectType.objects.get_for_model(Prefix))
obj_perm.object_types.add(ObjectType.objects.get_for_model(Rack))
# Attempt to edit a non-permitted object
data = {'site': self.sites[0].pk}
url = reverse('ipam-api:prefix-detail',
kwargs={'pk': self.prefixes[3].pk})
url = reverse('dcim-api:rack-detail', kwargs={'pk': self.racks[3].pk})
response = self.client.patch(url, data, format='json', **self.header)
self.assertEqual(response.status_code, 404)
# Edit a permitted object
data['status'] = 'reserved'
url = reverse('ipam-api:prefix-detail',
kwargs={'pk': self.prefixes[0].pk})
url = reverse('dcim-api:rack-detail', kwargs={'pk': self.racks[0].pk})
response = self.client.patch(url, data, format='json', **self.header)
self.assertEqual(response.status_code, 200)
# Attempt to modify a permitted object to a non-permitted object
data['site'] = self.sites[1].pk
url = reverse('ipam-api:prefix-detail',
kwargs={'pk': self.prefixes[0].pk})
url = reverse('dcim-api:rack-detail', kwargs={'pk': self.racks[0].pk})
response = self.client.patch(url, data, format='json', **self.header)
self.assertEqual(response.status_code, 403)
@ -564,8 +556,7 @@ class ObjectPermissionAPIViewTestCase(TestCase):
def test_delete_object(self):
# Attempt to delete an object without permission
url = reverse('ipam-api:prefix-detail',
kwargs={'pk': self.prefixes[0].pk})
url = reverse('dcim-api:rack-detail', kwargs={'pk': self.racks[0].pk})
response = self.client.delete(url, format='json', **self.header)
self.assertEqual(response.status_code, 403)
@ -577,16 +568,14 @@ class ObjectPermissionAPIViewTestCase(TestCase):
)
obj_perm.save()
obj_perm.users.add(self.user)
obj_perm.object_types.add(ObjectType.objects.get_for_model(Prefix))
obj_perm.object_types.add(ObjectType.objects.get_for_model(Rack))
# Attempt to delete a non-permitted object
url = reverse('ipam-api:prefix-detail',
kwargs={'pk': self.prefixes[3].pk})
url = reverse('dcim-api:rack-detail', kwargs={'pk': self.racks[3].pk})
response = self.client.delete(url, format='json', **self.header)
self.assertEqual(response.status_code, 404)
# Delete a permitted object
url = reverse('ipam-api:prefix-detail',
kwargs={'pk': self.prefixes[0].pk})
url = reverse('dcim-api:rack-detail', kwargs={'pk': self.racks[0].pk})
response = self.client.delete(url, format='json', **self.header)
self.assertEqual(response.status_code, 204)