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

View File

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

View File

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

View File

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

View File

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