mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Closes #7852: Enable assigning interfaces to VRFs
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
## Interfaces
|
||||
|
||||
Interfaces in NetBox represent network interfaces used to exchange data with connected devices. On modern networks, these are most commonly Ethernet, but other types are supported as well. Each interface must be assigned a type, and may optionally be assigned a MAC address, MTU, and IEEE 802.1Q mode (tagged or access). Each interface can also be enabled or disabled, and optionally designated as management-only (for out-of-band management).
|
||||
Interfaces in NetBox represent network interfaces used to exchange data with connected devices. On modern networks, these are most commonly Ethernet, but other types are supported as well. Each interface must be assigned a type, and may optionally be assigned a MAC address, MTU, and IEEE 802.1Q mode (tagged or access). Each interface can also be enabled or disabled, and optionally designated as management-only (for out-of-band management). Additionally, each interface may optionally be assigned to a VRF.
|
||||
|
||||
!!! note
|
||||
Although devices and virtual machines both can have interfaces, a separate model is used for each. Thus, device interfaces have some properties that are not present on virtual machine interfaces and vice versa.
|
||||
|
@ -61,6 +61,7 @@ Inventory item templates can be arranged hierarchically within a device type, an
|
||||
* [#7759](https://github.com/netbox-community/netbox/issues/7759) - Improved the user preferences form
|
||||
* [#7784](https://github.com/netbox-community/netbox/issues/7784) - Support cluster type assignment for config contexts
|
||||
* [#7846](https://github.com/netbox-community/netbox/issues/7846) - Enable associating inventory items with device components
|
||||
* [#7852](https://github.com/netbox-community/netbox/issues/7852) - Enable assigning interfaces to VRFs
|
||||
* [#8168](https://github.com/netbox-community/netbox/issues/8168) - Add `min_vid` and `max_vid` fields to VLAN group
|
||||
|
||||
### Other Changes
|
||||
@ -88,7 +89,7 @@ Inventory item templates can be arranged hierarchically within a device type, an
|
||||
* dcim.FrontPort
|
||||
* Added `module` field
|
||||
* dcim.Interface
|
||||
* Added `module` field
|
||||
* Added `module` and `vrf` fields
|
||||
* dcim.InventoryItem
|
||||
* Added `component_type`, `component_id`, and `role` fields
|
||||
* Added read-only `component` field
|
||||
|
@ -6,7 +6,9 @@ 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, NestedIPAddressSerializer, NestedVLANSerializer
|
||||
from ipam.api.nested_serializers import (
|
||||
NestedASNSerializer, NestedIPAddressSerializer, NestedVLANSerializer, NestedVRFSerializer,
|
||||
)
|
||||
from ipam.models import ASN, VLAN
|
||||
from netbox.api import ChoiceField, ContentTypeField, SerializedPKRelatedField
|
||||
from netbox.api.serializers import (
|
||||
@ -728,6 +730,7 @@ class InterfaceSerializer(PrimaryModelSerializer, LinkTerminationSerializer, Con
|
||||
required=False,
|
||||
many=True
|
||||
)
|
||||
vrf = NestedVRFSerializer(required=False, allow_null=True)
|
||||
cable = NestedCableSerializer(read_only=True)
|
||||
wireless_link = NestedWirelessLinkSerializer(read_only=True)
|
||||
wireless_lans = SerializedPKRelatedField(
|
||||
@ -745,7 +748,7 @@ class InterfaceSerializer(PrimaryModelSerializer, LinkTerminationSerializer, Con
|
||||
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'enabled', 'parent', 'bridge', 'lag',
|
||||
'mtu', 'mac_address', 'wwn', 'mgmt_only', 'description', 'mode', 'rf_role', 'rf_channel',
|
||||
'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'untagged_vlan', 'tagged_vlans', 'mark_connected',
|
||||
'cable', 'wireless_link', 'link_peer', 'link_peer_type', 'wireless_lans', 'connected_endpoint',
|
||||
'cable', 'wireless_link', 'link_peer', 'link_peer_type', 'wireless_lans', 'vrf', 'connected_endpoint',
|
||||
'connected_endpoint_type', 'connected_endpoint_reachable', 'tags', 'custom_fields', 'created',
|
||||
'last_updated', 'count_ipaddresses', 'count_fhrp_groups', '_occupied',
|
||||
]
|
||||
|
@ -583,7 +583,7 @@ class PowerOutletViewSet(PathEndpointMixin, ModelViewSet):
|
||||
class InterfaceViewSet(PathEndpointMixin, ModelViewSet):
|
||||
queryset = Interface.objects.prefetch_related(
|
||||
'device', 'module__module_bay', 'parent', 'bridge', 'lag', '_path__destination', 'cable', '_link_peer',
|
||||
'wireless_lans', 'untagged_vlan', 'tagged_vlans', 'ip_addresses', 'fhrp_group_assignments', 'tags'
|
||||
'wireless_lans', 'untagged_vlan', 'tagged_vlans', 'vrf', 'ip_addresses', 'fhrp_group_assignments', 'tags'
|
||||
)
|
||||
serializer_class = serializers.InterfaceSerializer
|
||||
filterset_class = filtersets.InterfaceFilterSet
|
||||
|
@ -3,7 +3,7 @@ from django.contrib.auth.models import User
|
||||
|
||||
from extras.filters import TagFilter
|
||||
from extras.filtersets import LocalConfigContextFilterSet
|
||||
from ipam.models import ASN
|
||||
from ipam.models import ASN, VRF
|
||||
from netbox.filtersets import (
|
||||
BaseFilterSet, ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, PrimaryModelFilterSet,
|
||||
)
|
||||
@ -1217,6 +1217,17 @@ class InterfaceFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet, CableT
|
||||
rf_channel = django_filters.MultipleChoiceFilter(
|
||||
choices=WirelessChannelChoices
|
||||
)
|
||||
vrf_id = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='vrf',
|
||||
queryset=VRF.objects.all(),
|
||||
label='VRF',
|
||||
)
|
||||
vrf = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='vrf__rd',
|
||||
queryset=VRF.objects.all(),
|
||||
to_field_name='rd',
|
||||
label='VRF (RD)',
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Interface
|
||||
|
@ -7,7 +7,7 @@ from dcim.choices import *
|
||||
from dcim.constants import *
|
||||
from dcim.models import *
|
||||
from extras.forms import AddRemoveTagsForm, CustomFieldModelBulkEditForm
|
||||
from ipam.models import VLAN, ASN
|
||||
from ipam.models import ASN, VLAN, VRF
|
||||
from tenancy.models import Tenant
|
||||
from utilities.forms import (
|
||||
add_blank_choice, BulkEditForm, BulkEditNullBooleanSelect, ColorField, CommentField, DynamicModelChoiceField,
|
||||
@ -1061,7 +1061,8 @@ class InterfaceBulkEditForm(
|
||||
required=False,
|
||||
query_params={
|
||||
'type': 'lag',
|
||||
}
|
||||
},
|
||||
label='LAG'
|
||||
)
|
||||
mgmt_only = forms.NullBooleanField(
|
||||
required=False,
|
||||
@ -1080,11 +1081,16 @@ class InterfaceBulkEditForm(
|
||||
queryset=VLAN.objects.all(),
|
||||
required=False
|
||||
)
|
||||
vrf = DynamicModelChoiceField(
|
||||
queryset=VRF.objects.all(),
|
||||
required=False,
|
||||
label='VRF'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = [
|
||||
'label', 'parent', 'bridge', 'lag', 'mac_address', 'wwn', 'mtu', 'description', 'mode', 'rf_channel',
|
||||
'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'untagged_vlan', 'tagged_vlans',
|
||||
'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'untagged_vlan', 'tagged_vlans', 'vrf',
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
@ -8,6 +8,7 @@ from dcim.choices import *
|
||||
from dcim.constants import *
|
||||
from dcim.models import *
|
||||
from extras.forms import CustomFieldModelCSVForm
|
||||
from ipam.models import VRF
|
||||
from tenancy.models import Tenant
|
||||
from utilities.forms import CSVChoiceField, CSVContentTypeField, CSVModelChoiceField, CSVTypedChoiceField, SlugField
|
||||
from virtualization.models import Cluster
|
||||
@ -622,6 +623,12 @@ class InterfaceCSVForm(CustomFieldModelCSVForm):
|
||||
required=False,
|
||||
help_text='IEEE 802.1Q operational mode (for L2 interfaces)'
|
||||
)
|
||||
vrf = CSVModelChoiceField(
|
||||
queryset=VRF.objects.all(),
|
||||
required=False,
|
||||
to_field_name='rd',
|
||||
help_text='Assigned VRF'
|
||||
)
|
||||
rf_role = CSVChoiceField(
|
||||
choices=WirelessRoleChoices,
|
||||
required=False,
|
||||
@ -632,7 +639,7 @@ class InterfaceCSVForm(CustomFieldModelCSVForm):
|
||||
model = Interface
|
||||
fields = (
|
||||
'device', 'name', 'label', 'parent', 'bridge', 'lag', 'type', 'enabled', 'mark_connected', 'mac_address',
|
||||
'wwn', 'mtu', 'mgmt_only', 'description', 'mode', 'rf_role', 'rf_channel', 'rf_channel_frequency',
|
||||
'wwn', 'mtu', 'mgmt_only', 'description', 'mode', 'vrf', 'rf_role', 'rf_channel', 'rf_channel_frequency',
|
||||
'rf_channel_width', 'tx_power',
|
||||
)
|
||||
|
||||
|
@ -6,7 +6,7 @@ from dcim.choices import *
|
||||
from dcim.constants import *
|
||||
from dcim.models import *
|
||||
from extras.forms import CustomFieldModelFilterForm, LocalConfigContextFilterForm
|
||||
from ipam.models import ASN
|
||||
from ipam.models import ASN, VRF
|
||||
from tenancy.forms import TenancyFilterForm
|
||||
from utilities.forms import (
|
||||
APISelectMultiple, add_blank_choice, ColorField, DynamicModelMultipleChoiceField, FilterForm, StaticSelect,
|
||||
@ -920,7 +920,8 @@ class InterfaceFilterForm(DeviceComponentFilterForm):
|
||||
model = Interface
|
||||
field_groups = [
|
||||
['q', 'tag'],
|
||||
['name', 'label', 'kind', 'type', 'enabled', 'mgmt_only', 'mac_address', 'wwn'],
|
||||
['name', 'label', 'kind', 'type', 'enabled', 'mgmt_only'],
|
||||
['vrf_id', 'mac_address', 'wwn'],
|
||||
['rf_role', 'rf_channel', 'rf_channel_width', 'tx_power'],
|
||||
['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'],
|
||||
]
|
||||
@ -980,6 +981,11 @@ class InterfaceFilterForm(DeviceComponentFilterForm):
|
||||
min_value=0,
|
||||
max_value=127
|
||||
)
|
||||
vrf_id = DynamicModelMultipleChoiceField(
|
||||
queryset=VRF.objects.all(),
|
||||
required=False,
|
||||
label='VRF'
|
||||
)
|
||||
tag = TagFilterField(model)
|
||||
|
||||
|
||||
|
@ -9,7 +9,7 @@ from dcim.constants import *
|
||||
from dcim.models import *
|
||||
from extras.forms import CustomFieldModelForm
|
||||
from extras.models import Tag
|
||||
from ipam.models import IPAddress, VLAN, VLANGroup, ASN
|
||||
from ipam.models import ASN, IPAddress, VLAN, VLANGroup, VRF
|
||||
from tenancy.forms import TenancyForm
|
||||
from utilities.forms import (
|
||||
APISelect, add_blank_choice, BootstrapMixin, ClearableFileInput, CommentField, ContentTypeChoiceField,
|
||||
@ -1261,6 +1261,11 @@ class InterfaceForm(InterfaceCommonForm, CustomFieldModelForm):
|
||||
'available_on_device': '$device',
|
||||
}
|
||||
)
|
||||
vrf = DynamicModelChoiceField(
|
||||
queryset=VRF.objects.all(),
|
||||
required=False,
|
||||
label='VRF'
|
||||
)
|
||||
tags = DynamicModelMultipleChoiceField(
|
||||
queryset=Tag.objects.all(),
|
||||
required=False
|
||||
@ -1271,11 +1276,11 @@ class InterfaceForm(InterfaceCommonForm, CustomFieldModelForm):
|
||||
fields = [
|
||||
'device', 'name', 'label', 'type', 'enabled', 'parent', 'bridge', 'lag', 'mac_address', 'wwn', 'mtu',
|
||||
'mgmt_only', 'mark_connected', 'description', 'mode', 'rf_role', 'rf_channel', 'rf_channel_frequency',
|
||||
'rf_channel_width', 'tx_power', 'wireless_lans', 'untagged_vlan', 'tagged_vlans', 'tags',
|
||||
'rf_channel_width', 'tx_power', 'wireless_lans', 'untagged_vlan', 'tagged_vlans', 'vrf', 'tags',
|
||||
]
|
||||
fieldsets = (
|
||||
('Interface', ('device', 'name', 'type', 'label', 'description', 'tags')),
|
||||
('Addressing', ('mac_address', 'wwn')),
|
||||
('Addressing', ('vrf', 'mac_address', 'wwn')),
|
||||
('Operation', ('mtu', 'tx_power', 'enabled', 'mgmt_only', 'mark_connected')),
|
||||
('Related Interfaces', ('parent', 'bridge', 'lag')),
|
||||
('802.1Q Switching', ('mode', 'vlan_group', 'untagged_vlan', 'tagged_vlans')),
|
||||
|
20
netbox/dcim/migrations/0149_interface_vrf.py
Normal file
20
netbox/dcim/migrations/0149_interface_vrf.py
Normal file
@ -0,0 +1,20 @@
|
||||
# Generated by Django 3.2.11 on 2022-01-07 18:34
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ipam', '0054_vlangroup_min_max_vids'),
|
||||
('dcim', '0148_inventoryitem_templates'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='interface',
|
||||
name='vrf',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='interfaces', to='ipam.vrf'),
|
||||
),
|
||||
]
|
@ -616,6 +616,14 @@ class Interface(ModularComponentModel, BaseInterface, LinkTermination, PathEndpo
|
||||
blank=True,
|
||||
verbose_name='Tagged VLANs'
|
||||
)
|
||||
vrf = models.ForeignKey(
|
||||
to='ipam.VRF',
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='interfaces',
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name='VRF'
|
||||
)
|
||||
ip_addresses = GenericRelation(
|
||||
to='ipam.IPAddress',
|
||||
content_type_field='assigned_object_type',
|
||||
|
@ -521,6 +521,9 @@ class InterfaceTable(ModularDeviceComponentTable, BaseInterfaceTable, PathEndpoi
|
||||
orderable=False,
|
||||
verbose_name='Wireless LANs'
|
||||
)
|
||||
vrf = tables.Column(
|
||||
linkify=True
|
||||
)
|
||||
tags = TagColumn(
|
||||
url_name='dcim:interface_list'
|
||||
)
|
||||
@ -531,7 +534,7 @@ class InterfaceTable(ModularDeviceComponentTable, BaseInterfaceTable, PathEndpoi
|
||||
'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'enabled', 'type', 'mgmt_only', 'mtu',
|
||||
'mode', 'mac_address', 'wwn', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width',
|
||||
'tx_power', 'description', 'mark_connected', 'cable', 'cable_color', 'wireless_link', 'wireless_lans',
|
||||
'link_peer', 'connection', 'tags', 'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans',
|
||||
'link_peer', 'connection', 'tags', 'vrf', 'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans',
|
||||
)
|
||||
default_columns = ('pk', 'name', 'device', 'label', 'enabled', 'type', 'description')
|
||||
|
||||
|
@ -6,7 +6,7 @@ from rest_framework import status
|
||||
from dcim.choices import *
|
||||
from dcim.constants import *
|
||||
from dcim.models import *
|
||||
from ipam.models import ASN, RIR, VLAN
|
||||
from ipam.models import ASN, RIR, VLAN, VRF
|
||||
from utilities.testing import APITestCase, APIViewTestCases, create_test_device
|
||||
from virtualization.models import Cluster, ClusterType
|
||||
from wireless.models import WirelessLAN
|
||||
@ -1424,6 +1424,13 @@ class InterfaceTest(Mixins.ComponentTraceMixin, APIViewTestCases.APIViewTestCase
|
||||
)
|
||||
WirelessLAN.objects.bulk_create(wireless_lans)
|
||||
|
||||
vrfs = (
|
||||
VRF(name='VRF 1'),
|
||||
VRF(name='VRF 2'),
|
||||
VRF(name='VRF 3'),
|
||||
)
|
||||
VRF.objects.bulk_create(vrfs)
|
||||
|
||||
cls.create_data = [
|
||||
{
|
||||
'device': device.pk,
|
||||
@ -1431,6 +1438,7 @@ class InterfaceTest(Mixins.ComponentTraceMixin, APIViewTestCases.APIViewTestCase
|
||||
'type': '1000base-t',
|
||||
'mode': InterfaceModeChoices.MODE_TAGGED,
|
||||
'tx_power': 10,
|
||||
'vrf': vrfs[0].pk,
|
||||
'tagged_vlans': [vlans[0].pk, vlans[1].pk],
|
||||
'untagged_vlan': vlans[2].pk,
|
||||
'wireless_lans': [wireless_lans[0].pk, wireless_lans[1].pk],
|
||||
@ -1442,6 +1450,7 @@ class InterfaceTest(Mixins.ComponentTraceMixin, APIViewTestCases.APIViewTestCase
|
||||
'mode': InterfaceModeChoices.MODE_TAGGED,
|
||||
'bridge': interfaces[0].pk,
|
||||
'tx_power': 10,
|
||||
'vrf': vrfs[1].pk,
|
||||
'tagged_vlans': [vlans[0].pk, vlans[1].pk],
|
||||
'untagged_vlan': vlans[2].pk,
|
||||
'wireless_lans': [wireless_lans[0].pk, wireless_lans[1].pk],
|
||||
@ -1453,6 +1462,7 @@ class InterfaceTest(Mixins.ComponentTraceMixin, APIViewTestCases.APIViewTestCase
|
||||
'mode': InterfaceModeChoices.MODE_TAGGED,
|
||||
'parent': interfaces[1].pk,
|
||||
'tx_power': 10,
|
||||
'vrf': vrfs[2].pk,
|
||||
'tagged_vlans': [vlans[0].pk, vlans[1].pk],
|
||||
'untagged_vlan': vlans[2].pk,
|
||||
'wireless_lans': [wireless_lans[0].pk, wireless_lans[1].pk],
|
||||
|
@ -4,7 +4,7 @@ from django.test import TestCase
|
||||
from dcim.choices import *
|
||||
from dcim.filtersets import *
|
||||
from dcim.models import *
|
||||
from ipam.models import ASN, IPAddress, RIR
|
||||
from ipam.models import ASN, IPAddress, RIR, VRF
|
||||
from tenancy.models import Tenant, TenantGroup
|
||||
from utilities.choices import ColorChoices
|
||||
from utilities.testing import ChangeLoggedFilterSetTests, create_test_device
|
||||
@ -2370,15 +2370,22 @@ class InterfaceTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
)
|
||||
Device.objects.bulk_create(devices)
|
||||
|
||||
vrfs = (
|
||||
VRF(name='VRF 1', rd='65000:1'),
|
||||
VRF(name='VRF 2', rd='65000:2'),
|
||||
VRF(name='VRF 3', rd='65000:3'),
|
||||
)
|
||||
VRF.objects.bulk_create(vrfs)
|
||||
|
||||
# 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)
|
||||
Device.objects.filter(pk=devices[1].pk).update(virtual_chassis=virtual_chassis, vc_position=2, vc_priority=2)
|
||||
|
||||
interfaces = (
|
||||
Interface(device=devices[0], name='Interface 1', label='A', type=InterfaceTypeChoices.TYPE_1GE_SFP, enabled=True, mgmt_only=True, mtu=100, mode=InterfaceModeChoices.MODE_ACCESS, mac_address='00-00-00-00-00-01', description='First'),
|
||||
Interface(device=devices[1], name='Interface 2', label='B', type=InterfaceTypeChoices.TYPE_1GE_GBIC, enabled=True, mgmt_only=True, mtu=200, mode=InterfaceModeChoices.MODE_TAGGED, mac_address='00-00-00-00-00-02', description='Second'),
|
||||
Interface(device=devices[2], name='Interface 3', label='C', type=InterfaceTypeChoices.TYPE_1GE_FIXED, enabled=False, mgmt_only=False, mtu=300, mode=InterfaceModeChoices.MODE_TAGGED_ALL, mac_address='00-00-00-00-00-03', description='Third'),
|
||||
Interface(device=devices[0], name='Interface 1', label='A', type=InterfaceTypeChoices.TYPE_1GE_SFP, enabled=True, mgmt_only=True, mtu=100, mode=InterfaceModeChoices.MODE_ACCESS, mac_address='00-00-00-00-00-01', description='First', vrf=vrfs[0]),
|
||||
Interface(device=devices[1], name='Interface 2', label='B', type=InterfaceTypeChoices.TYPE_1GE_GBIC, enabled=True, mgmt_only=True, mtu=200, mode=InterfaceModeChoices.MODE_TAGGED, mac_address='00-00-00-00-00-02', description='Second', vrf=vrfs[1]),
|
||||
Interface(device=devices[2], name='Interface 3', label='C', type=InterfaceTypeChoices.TYPE_1GE_FIXED, enabled=False, mgmt_only=False, mtu=300, mode=InterfaceModeChoices.MODE_TAGGED_ALL, mac_address='00-00-00-00-00-03', description='Third', vrf=vrfs[2]),
|
||||
Interface(device=devices[3], name='Interface 4', label='D', type=InterfaceTypeChoices.TYPE_OTHER, enabled=True, mgmt_only=True, tx_power=40),
|
||||
Interface(device=devices[3], name='Interface 5', label='E', type=InterfaceTypeChoices.TYPE_OTHER, enabled=True, mgmt_only=True, tx_power=40),
|
||||
Interface(device=devices[3], name='Interface 6', label='F', type=InterfaceTypeChoices.TYPE_OTHER, enabled=False, mgmt_only=False, tx_power=40),
|
||||
@ -2550,6 +2557,13 @@ class InterfaceTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
params = {'tx_power': [40]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||
|
||||
def test_vrf(self):
|
||||
vrfs = VRF.objects.all()[:2]
|
||||
params = {'vrf_id': [vrfs[0].pk, vrfs[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'vrf': [vrfs[0].rd, vrfs[1].rd]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
|
||||
class FrontPortTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
queryset = FrontPort.objects.all()
|
||||
|
@ -11,7 +11,7 @@ from netaddr import EUI
|
||||
from dcim.choices import *
|
||||
from dcim.constants import *
|
||||
from dcim.models import *
|
||||
from ipam.models import ASN, RIR, VLAN
|
||||
from ipam.models import ASN, RIR, VLAN, VRF
|
||||
from tenancy.models import Tenant
|
||||
from utilities.testing import ViewTestCases, create_tags, create_test_device
|
||||
from wireless.models import WirelessLAN
|
||||
@ -2105,6 +2105,13 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
)
|
||||
WirelessLAN.objects.bulk_create(wireless_lans)
|
||||
|
||||
vrfs = (
|
||||
VRF(name='VRF 1'),
|
||||
VRF(name='VRF 2'),
|
||||
VRF(name='VRF 3'),
|
||||
)
|
||||
VRF.objects.bulk_create(vrfs)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
cls.form_data = {
|
||||
@ -2124,6 +2131,7 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
'untagged_vlan': vlans[0].pk,
|
||||
'tagged_vlans': [v.pk for v in vlans[1:4]],
|
||||
'wireless_lans': [wireless_lans[0].pk, wireless_lans[1].pk],
|
||||
'vrf': vrfs[0].pk,
|
||||
'tags': [t.pk for t in tags],
|
||||
}
|
||||
|
||||
@ -2143,6 +2151,7 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
'untagged_vlan': vlans[0].pk,
|
||||
'tagged_vlans': [v.pk for v in vlans[1:4]],
|
||||
'wireless_lans': [wireless_lans[0].pk, wireless_lans[1].pk],
|
||||
'vrf': vrfs[0].pk,
|
||||
'tags': [t.pk for t in tags],
|
||||
}
|
||||
|
||||
@ -2159,13 +2168,14 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
'tx_power': 10,
|
||||
'untagged_vlan': vlans[0].pk,
|
||||
'tagged_vlans': [v.pk for v in vlans[1:4]],
|
||||
'vrf': vrfs[1].pk,
|
||||
}
|
||||
|
||||
cls.csv_data = (
|
||||
"device,name,type",
|
||||
"Device 1,Interface 4,1000base-t",
|
||||
"Device 1,Interface 5,1000base-t",
|
||||
"Device 1,Interface 6,1000base-t",
|
||||
f"device,name,type,vrf.pk",
|
||||
f"Device 1,Interface 4,1000base-t,{vrfs[0].pk}",
|
||||
f"Device 1,Interface 5,1000base-t,{vrfs[0].pk}",
|
||||
f"Device 1,Interface 6,1000base-t,{vrfs[0].pk}",
|
||||
)
|
||||
|
||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||
|
@ -108,6 +108,16 @@
|
||||
<th scope="row">802.1Q Mode</th>
|
||||
<td>{{ object.get_mode_display|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">VRF</th>
|
||||
<td>
|
||||
{% if object.vrf %}
|
||||
<a href="{{ object.vrf.get_absolute_url }}">{{ object.vrf }}</a>
|
||||
{% else %}
|
||||
<span class="text-muted">None</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -25,6 +25,7 @@
|
||||
<div class="row mb-2">
|
||||
<h5 class="offset-sm-3">Addressing</h5>
|
||||
</div>
|
||||
{% render_field form.vrf %}
|
||||
{% render_field form.mac_address %}
|
||||
{% render_field form.wwn %}
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user