mirror of
https://github.com/netbox-community/netbox.git
synced 2026-01-23 12:08:43 -06:00
* Work on #7854 * Move to new URL scheme. * Fix PEP8 errors * Fix PEP8 errors * Add GraphQL and fix primary_ip missing * Fix PEP8 on GQL Type * Fix missing NestedSerializer. * Fix missing NestedSerializer & rename VDC to VDCs * Fix migration * Change Validation for identifier * Fix missing migration * Rebase to feature * Post-review changes * Remove VDC Type * Remove M2M Enforcement logic * Interface related changes * Add filter fields to filterset for Interface filter * Add form field to filterset form for Interface filter * Add VDC display to interface detail template * Remove VirtualDeviceContextTypeChoices * Accommodate recent changes in feature branch * Add tests Add missing search() * Update tests, and fix model form * Update test_api * Update test_api.InterfaceTest create_data * Fix issue with tests * Update interface serializer * Update serializer and tests * Update status to be required * Remove error message for constraint * Remove extraneous import * Re-ordered devices menu to place VDC below virtual chassis * Add helptext for `identifier` field * Fix breadcrumb link * Remove add interface link * Add missing tenant and status fields * Changes to tests as per Jeremy * Change for #9623 Co-authored-by: Jeremy Stretch <jstretch@ns1.com> * Update filterset form for status field * Remove Rename View * Change tabs to spaces * Update netbox/dcim/tables/devices.py Co-authored-by: Jeremy Stretch <jstretch@ns1.com> * Update netbox/dcim/tables/devices.py Co-authored-by: Jeremy Stretch <jstretch@ns1.com> * Fix tenant in bulk_edit * Apply suggestions from code review Co-authored-by: Jeremy Stretch <jstretch@ns1.com> * Add status field to table. * Re-order table fields. Co-authored-by: Jeremy Stretch <jstretch@ns1.com>
This commit is contained in:
@@ -1485,6 +1485,12 @@ class InterfaceTest(Mixins.ComponentTraceMixin, APIViewTestCases.APIViewTestCase
|
||||
)
|
||||
Interface.objects.bulk_create(interfaces)
|
||||
|
||||
vdcs = (
|
||||
VirtualDeviceContext(name='VDC 1', identifier=1, device=device),
|
||||
VirtualDeviceContext(name='VDC 2', identifier=2, device=device)
|
||||
)
|
||||
VirtualDeviceContext.objects.bulk_create(vdcs)
|
||||
|
||||
vlans = (
|
||||
VLAN(name='VLAN 1', vid=1),
|
||||
VLAN(name='VLAN 2', vid=2),
|
||||
@@ -1533,6 +1539,7 @@ class InterfaceTest(Mixins.ComponentTraceMixin, APIViewTestCases.APIViewTestCase
|
||||
},
|
||||
{
|
||||
'device': device.pk,
|
||||
'vdcs': [vdcs[0].pk],
|
||||
'name': 'Interface 6',
|
||||
'type': 'virtual',
|
||||
'mode': InterfaceModeChoices.MODE_TAGGED,
|
||||
@@ -1543,6 +1550,7 @@ class InterfaceTest(Mixins.ComponentTraceMixin, APIViewTestCases.APIViewTestCase
|
||||
},
|
||||
{
|
||||
'device': device.pk,
|
||||
'vdcs': [vdcs[1].pk],
|
||||
'name': 'Interface 7',
|
||||
'type': InterfaceTypeChoices.TYPE_80211A,
|
||||
'tx_power': 10,
|
||||
@@ -1551,6 +1559,7 @@ class InterfaceTest(Mixins.ComponentTraceMixin, APIViewTestCases.APIViewTestCase
|
||||
},
|
||||
{
|
||||
'device': device.pk,
|
||||
'vdcs': [vdcs[1].pk],
|
||||
'name': 'Interface 8',
|
||||
'type': InterfaceTypeChoices.TYPE_80211A,
|
||||
'tx_power': 10,
|
||||
@@ -2163,3 +2172,57 @@ class PowerFeedTest(APIViewTestCases.APIViewTestCase):
|
||||
'type': REDUNDANT,
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class VirtualDeviceContextTest(APIViewTestCases.APIViewTestCase):
|
||||
model = VirtualDeviceContext
|
||||
brief_fields = ['device', 'display', 'id', 'identifier', 'name', 'url']
|
||||
bulk_update_data = {
|
||||
'status': 'planned',
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
site = Site.objects.create(name='Test Site', slug='test-site')
|
||||
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||
devicetype = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type', slug='device-type')
|
||||
devicerole = DeviceRole.objects.create(name='Device Role', slug='device-role', color='ff0000')
|
||||
|
||||
devices = (
|
||||
Device(name='Device 1', device_type=devicetype, device_role=devicerole, site=site),
|
||||
Device(name='Device 2', device_type=devicetype, device_role=devicerole, site=site),
|
||||
Device(name='Device 3', device_type=devicetype, device_role=devicerole, site=site),
|
||||
)
|
||||
Device.objects.bulk_create(devices)
|
||||
|
||||
vdcs = (
|
||||
VirtualDeviceContext(device=devices[1], name='VDC 1', identifier=1, status='active'),
|
||||
VirtualDeviceContext(device=devices[1], name='VDC 2', identifier=2, status='active'),
|
||||
VirtualDeviceContext(device=devices[2], name='VDC 1', identifier=1, status='active'),
|
||||
VirtualDeviceContext(device=devices[2], name='VDC 2', identifier=2, status='active'),
|
||||
VirtualDeviceContext(device=devices[2], name='VDC 3', identifier=3, status='active'),
|
||||
VirtualDeviceContext(device=devices[2], name='VDC 4', identifier=4, status='active'),
|
||||
VirtualDeviceContext(device=devices[2], name='VDC 5', identifier=5, status='active'),
|
||||
)
|
||||
VirtualDeviceContext.objects.bulk_create(vdcs)
|
||||
|
||||
cls.create_data = [
|
||||
{
|
||||
'device': devices[0].pk,
|
||||
'status': 'active',
|
||||
'name': 'VDC 1',
|
||||
'identifier': 1,
|
||||
},
|
||||
{
|
||||
'device': devices[0].pk,
|
||||
'status': 'active',
|
||||
'name': 'VDC 2',
|
||||
'identifier': 2,
|
||||
},
|
||||
{
|
||||
'device': devices[1].pk,
|
||||
'status': 'active',
|
||||
'name': 'VDC 3',
|
||||
'identifier': 3,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -2681,6 +2681,13 @@ class InterfaceTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
)
|
||||
VRF.objects.bulk_create(vrfs)
|
||||
|
||||
# Virtual Device Context Creation
|
||||
vdcs = (
|
||||
VirtualDeviceContext(device=devices[3], name='VDC 1', identifier=1, status=VirtualDeviceContextStatusChoices.STATUS_ACTIVE),
|
||||
VirtualDeviceContext(device=devices[3], name='VDC 2', identifier=2, status=VirtualDeviceContextStatusChoices.STATUS_PLANNED),
|
||||
)
|
||||
VirtualDeviceContext.objects.bulk_create(vdcs)
|
||||
|
||||
# VirtualChassis assignment for filtering
|
||||
virtual_chassis = VirtualChassis.objects.create(master=devices[0])
|
||||
Device.objects.filter(pk=devices[0].pk).update(virtual_chassis=virtual_chassis, vc_position=1, vc_priority=1)
|
||||
@@ -2793,6 +2800,12 @@ class InterfaceTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
)
|
||||
Interface.objects.bulk_create(interfaces)
|
||||
|
||||
interfaces[3].vdcs.set([vdcs[0], vdcs[1]])
|
||||
interfaces[4].vdcs.set([vdcs[0], vdcs[1]])
|
||||
interfaces[5].vdcs.set([vdcs[0]])
|
||||
interfaces[6].vdcs.set([vdcs[0]])
|
||||
interfaces[7].vdcs.set([vdcs[1]])
|
||||
|
||||
# Cables
|
||||
Cable(a_terminations=[interfaces[0]], b_terminations=[interfaces[3]]).save()
|
||||
Cable(a_terminations=[interfaces[1]], b_terminations=[interfaces[4]]).save()
|
||||
@@ -2997,6 +3010,21 @@ class InterfaceTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
params = {'vrf': [vrfs[0].rd, vrfs[1].rd]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_vdc(self):
|
||||
params = {'vdc': ['VDC 1']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||
|
||||
devices = Device.objects.last()
|
||||
vdc = VirtualDeviceContext.objects.filter(device=devices, name='VDC 2')
|
||||
params = {'vdc_id': vdc.values_list('pk', flat=True)}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||
|
||||
def test_vdc_identifier(self):
|
||||
devices = Device.objects.last()
|
||||
vdc = VirtualDeviceContext.objects.filter(device=devices, name='VDC 2')
|
||||
params = {'vdc_identifier': vdc.values_list('identifier', flat=True)}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||
|
||||
|
||||
class FrontPortTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
queryset = FrontPort.objects.all()
|
||||
@@ -4254,4 +4282,83 @@ class PowerFeedTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
||||
|
||||
|
||||
# TODO: Connection filters
|
||||
class VirtualDeviceContextTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
queryset = VirtualDeviceContext.objects.all()
|
||||
filterset = VirtualDeviceContextFilterSet
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
sites = (
|
||||
Site(name='Site 1', slug='site-1'),
|
||||
Site(name='Site 2', slug='site-2'),
|
||||
Site(name='Site 3', slug='site-3'),
|
||||
)
|
||||
Site.objects.bulk_create(sites)
|
||||
|
||||
tenants = (
|
||||
Tenant(name='Tenant 1', slug='tenant-1'),
|
||||
Tenant(name='Tenant 2', slug='tenant-2'),
|
||||
Tenant(name='Tenant 3', slug='tenant-3'),
|
||||
)
|
||||
Tenant.objects.bulk_create(tenants)
|
||||
|
||||
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||
device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Model 1', slug='model-1')
|
||||
device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
|
||||
|
||||
devices = (
|
||||
Device(name='Device 1', device_type=device_type, device_role=device_role, site=sites[0]),
|
||||
Device(name='Device 2', device_type=device_type, device_role=device_role, site=sites[1]),
|
||||
Device(name='Device 3', device_type=device_type, device_role=device_role, site=sites[2]),
|
||||
)
|
||||
Device.objects.bulk_create(devices)
|
||||
|
||||
vdcs = (
|
||||
VirtualDeviceContext(device=devices[0], name='VDC 1', identifier=1, status=VirtualDeviceContextStatusChoices.STATUS_ACTIVE),
|
||||
VirtualDeviceContext(device=devices[0], name='VDC 2', identifier=2, status=VirtualDeviceContextStatusChoices.STATUS_PLANNED),
|
||||
VirtualDeviceContext(device=devices[1], name='VDC 1', status=VirtualDeviceContextStatusChoices.STATUS_OFFLINE),
|
||||
VirtualDeviceContext(device=devices[1], name='VDC 2', status=VirtualDeviceContextStatusChoices.STATUS_PLANNED),
|
||||
VirtualDeviceContext(device=devices[2], name='VDC 1', status=VirtualDeviceContextStatusChoices.STATUS_ACTIVE),
|
||||
VirtualDeviceContext(device=devices[2], name='VDC 2', status=VirtualDeviceContextStatusChoices.STATUS_ACTIVE),
|
||||
)
|
||||
VirtualDeviceContext.objects.bulk_create(vdcs)
|
||||
|
||||
interfaces = (
|
||||
Interface(device=devices[0], name='Interface 1', type='virtual'),
|
||||
Interface(device=devices[0], name='Interface 2', type='virtual'),
|
||||
)
|
||||
Interface.objects.bulk_create(interfaces)
|
||||
|
||||
interfaces[0].vdcs.set([vdcs[0]])
|
||||
interfaces[1].vdcs.set([vdcs[1]])
|
||||
|
||||
addresses = (
|
||||
IPAddress(assigned_object=interfaces[0], address='10.1.1.1/24'),
|
||||
IPAddress(assigned_object=interfaces[1], address='10.1.1.2/24'),
|
||||
)
|
||||
IPAddress.objects.bulk_create(addresses)
|
||||
|
||||
vdcs[0].primary_ip4 = addresses[0]
|
||||
vdcs[0].save()
|
||||
vdcs[1].primary_ip4 = addresses[1]
|
||||
vdcs[1].save()
|
||||
|
||||
def test_device(self):
|
||||
params = {'device': ['Device 1', 'Device 2']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 6)
|
||||
|
||||
def test_status(self):
|
||||
params = {'status': ['active']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||
|
||||
def test_device_id(self):
|
||||
devices = Device.objects.filter(name__in=['Device 1', 'Device 2'])
|
||||
params = {'device_id': [devices[0].pk, devices[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||
|
||||
def test_has_primary_ip(self):
|
||||
params = {'has_primary_ip': True}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'has_primary_ip': False}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||
|
||||
@@ -588,3 +588,50 @@ class CableTestCase(TestCase):
|
||||
cable = Cable(a_terminations=[self.interface2], b_terminations=[wireless_interface])
|
||||
with self.assertRaises(ValidationError):
|
||||
cable.clean()
|
||||
|
||||
|
||||
class VirtualDeviceContextTestCase(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
||||
site = Site.objects.create(name='Test Site 1', slug='test-site-1')
|
||||
manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
|
||||
devicetype = DeviceType.objects.create(
|
||||
manufacturer=manufacturer, model='Test Device Type 1', slug='test-device-type-1'
|
||||
)
|
||||
devicerole = DeviceRole.objects.create(
|
||||
name='Test Device Role 1', slug='test-device-role-1', color='ff0000'
|
||||
)
|
||||
self.device = Device.objects.create(
|
||||
device_type=devicetype, device_role=devicerole, name='TestDevice1', site=site
|
||||
)
|
||||
|
||||
def test_vdc_and_interface_creation(self):
|
||||
|
||||
vdc = VirtualDeviceContext(device=self.device, name="VDC 1", identifier=1, status='active')
|
||||
vdc.full_clean()
|
||||
vdc.save()
|
||||
|
||||
interface = Interface(device=self.device, name='Eth1/1', type='10gbase-t')
|
||||
interface.full_clean()
|
||||
interface.save()
|
||||
|
||||
interface.vdcs.set([vdc])
|
||||
|
||||
def test_vdc_duplicate_name(self):
|
||||
vdc1 = VirtualDeviceContext(device=self.device, name="VDC 1", identifier=1, status='active')
|
||||
vdc1.full_clean()
|
||||
vdc1.save()
|
||||
|
||||
vdc2 = VirtualDeviceContext(device=self.device, name="VDC 1", identifier=2, status='active')
|
||||
with self.assertRaises(ValidationError):
|
||||
vdc2.full_clean()
|
||||
|
||||
def test_vdc_duplicate_identifier(self):
|
||||
vdc1 = VirtualDeviceContext(device=self.device, name="VDC 1", identifier=1, status='active')
|
||||
vdc1.full_clean()
|
||||
vdc1.save()
|
||||
|
||||
vdc2 = VirtualDeviceContext(device=self.device, name="VDC 2", identifier=1, status='active')
|
||||
with self.assertRaises(ValidationError):
|
||||
vdc2.full_clean()
|
||||
|
||||
@@ -3076,3 +3076,48 @@ class PowerFeedTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
|
||||
response = self.client.get(reverse('dcim:powerfeed_trace', kwargs={'pk': powerfeed.pk}))
|
||||
self.assertHttpStatus(response, 200)
|
||||
|
||||
|
||||
class VirtualDeviceContextTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
model = VirtualDeviceContext
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
devices = [create_test_device(name='Device 1')]
|
||||
|
||||
vdcs = (
|
||||
VirtualDeviceContext(name='VDC 1', identifier=1, device=devices[0], status='active'),
|
||||
VirtualDeviceContext(name='VDC 2', identifier=2, device=devices[0], status='active'),
|
||||
VirtualDeviceContext(name='VDC 3', identifier=3, device=devices[0], status='active'),
|
||||
)
|
||||
VirtualDeviceContext.objects.bulk_create(vdcs)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
cls.form_data = {
|
||||
'device': devices[0].pk,
|
||||
'status': 'active',
|
||||
'name': 'VDC 4',
|
||||
'identifier': 4,
|
||||
'primary_ip4': None,
|
||||
'primary_ip6': None,
|
||||
'tags': [t.pk for t in tags],
|
||||
}
|
||||
|
||||
cls.csv_data = (
|
||||
"device,status,name,identifier",
|
||||
"Device 1,active,VDC 5,5",
|
||||
"Device 1,active,VDC 6,6",
|
||||
"Device 1,active,VDC 7,7",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,status",
|
||||
f"{vdcs[0].pk},{VirtualDeviceContextStatusChoices.STATUS_PLANNED}",
|
||||
f"{vdcs[1].pk},{VirtualDeviceContextStatusChoices.STATUS_PLANNED}",
|
||||
f"{vdcs[2].pk},{VirtualDeviceContextStatusChoices.STATUS_PLANNED}",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'status': VirtualDeviceContextStatusChoices.STATUS_OFFLINE,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user