diff --git a/netbox/circuits/forms.py b/netbox/circuits/forms.py index 89f7a598f6c..817ff47dece 100644 --- a/netbox/circuits/forms.py +++ b/netbox/circuits/forms.py @@ -3,7 +3,7 @@ from django import forms from django.db.models import Count -from dcim.models import Site, Device, Interface, Rack, VIRTUAL_IFACE_TYPES +from dcim.models import Site, Device, Interface, Rack from extras.forms import CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm from tenancy.forms import TenancyForm from tenancy.models import Tenant @@ -210,7 +210,7 @@ class CircuitTerminationForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelForm ) ) interface = ChainedModelChoiceField( - queryset=Interface.objects.exclude(form_factor__in=VIRTUAL_IFACE_TYPES).select_related( + queryset=Interface.objects.connectable().select_related( 'circuit_termination', 'connected_as_a', 'connected_as_b' ), chains=( diff --git a/netbox/dcim/constants.py b/netbox/dcim/constants.py index 01e146e3ea9..f2c04791071 100644 --- a/netbox/dcim/constants.py +++ b/netbox/dcim/constants.py @@ -66,6 +66,12 @@ IFACE_FF_40GE_QSFP_PLUS = 1400 IFACE_FF_100GE_CFP = 1500 IFACE_FF_100GE_QSFP28 = 1600 +# Wireless +IFACE_FF_80211A = 2600 +IFACE_FF_80211G = 2610 +IFACE_FF_80211N = 2620 +IFACE_FF_80211AC = 2630 +IFACE_FF_80211AD = 2640 # Fibrechannel IFACE_FF_1GFC_SFP = 3010 IFACE_FF_2GFC_SFP = 3020 @@ -117,6 +123,16 @@ [IFACE_FF_100GE_QSFP28, 'QSFP28 (100GE)'], ] ], + [ + 'Wireless', + [ + [IFACE_FF_80211A, 'IEEE 802.11a'], + [IFACE_FF_80211G, 'IEEE 802.11b/g'], + [IFACE_FF_80211N, 'IEEE 802.11n'], + [IFACE_FF_80211AC, 'IEEE 802.11ac'], + [IFACE_FF_80211AD, 'IEEE 802.11ad'], + ] + ], [ 'FibreChannel', [ @@ -134,7 +150,6 @@ [IFACE_FF_E1, 'E1 (2.048 Mbps)'], [IFACE_FF_T3, 'T3 (45 Mbps)'], [IFACE_FF_E3, 'E3 (34 Mbps)'], - [IFACE_FF_E3, 'E3 (34 Mbps)'], ] ], [ @@ -160,6 +175,16 @@ IFACE_FF_LAG, ] +WIRELESS_IFACE_TYPES = [ + IFACE_FF_80211A, + IFACE_FF_80211G, + IFACE_FF_80211N, + IFACE_FF_80211AC, + IFACE_FF_80211AD, +] + +NONCONNECTABLE_IFACE_TYPES = VIRTUAL_IFACE_TYPES + WIRELESS_IFACE_TYPES + # Device statuses STATUS_OFFLINE = 0 STATUS_ACTIVE = 1 diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py index e418d169dd4..66913f182e3 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -11,8 +11,9 @@ from .models import ( ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay, DeviceBayTemplate, DeviceRole, DeviceType, STATUS_CHOICES, IFACE_FF_LAG, Interface, InterfaceConnection, - InterfaceTemplate, Manufacturer, InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, - PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, Region, Site, VIRTUAL_IFACE_TYPES, + InterfaceTemplate, Manufacturer, InventoryItem, NONCONNECTABLE_IFACE_TYPES, Platform, PowerOutlet, + PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, Region, Site, + VIRTUAL_IFACE_TYPES, WIRELESS_IFACE_TYPES, ) @@ -513,13 +514,12 @@ def filter_device(self, queryset, name, value): def filter_type(self, queryset, name, value): value = value.strip().lower() - if value == 'physical': - return queryset.exclude(form_factor__in=VIRTUAL_IFACE_TYPES) - elif value == 'virtual': - return queryset.filter(form_factor__in=VIRTUAL_IFACE_TYPES) - elif value == 'lag': - return queryset.filter(form_factor=IFACE_FF_LAG) - return queryset + return { + 'physical': queryset.exclude(form_factor__in=NONCONNECTABLE_IFACE_TYPES), + 'virtual': queryset.filter(form_factor__in=VIRTUAL_IFACE_TYPES), + 'wireless': queryset.filter(form_factor__in=WIRELESS_IFACE_TYPES), + 'lag': queryset.filter(form_factor=IFACE_FF_LAG), + }.get(value, queryset.none()) def _mac_address(self, queryset, name, value): value = value.strip() diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index e05ffec502a..6b0bfec1d55 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -24,7 +24,7 @@ IFACE_FF_CHOICES, IFACE_FF_LAG, IFACE_ORDERING_CHOICES, InterfaceConnection, InterfaceTemplate, Manufacturer, InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, RACK_FACE_CHOICES, RACK_TYPE_CHOICES, RACK_WIDTH_CHOICES, Rack, RackGroup, RackReservation, RackRole, RACK_WIDTH_19IN, RACK_WIDTH_23IN, - Region, Site, STATUS_CHOICES, SUBDEVICE_ROLE_CHILD, SUBDEVICE_ROLE_PARENT, VIRTUAL_IFACE_TYPES, + Region, Site, STATUS_CHOICES, SUBDEVICE_ROLE_CHILD, SUBDEVICE_ROLE_PARENT, ) @@ -1574,7 +1574,7 @@ class InterfaceConnectionForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelFor ) ) interface_b = ChainedModelChoiceField( - queryset=Interface.objects.exclude(form_factor__in=VIRTUAL_IFACE_TYPES).select_related( + queryset=Interface.objects.connectable().select_related( 'circuit_termination', 'connected_as_a', 'connected_as_b' ), chains=( @@ -1596,9 +1596,7 @@ def __init__(self, device_a, *args, **kwargs): super(InterfaceConnectionForm, self).__init__(*args, **kwargs) # Initialize interface A choices - device_a_interfaces = Interface.objects.order_naturally().filter(device=device_a).exclude( - form_factor__in=VIRTUAL_IFACE_TYPES - ).select_related( + device_a_interfaces = Interface.objects.connectable().order_naturally().filter(device=device_a).select_related( 'circuit_termination', 'connected_as_a', 'connected_as_b' ) self.fields['interface_a'].choices = [ diff --git a/netbox/dcim/migrations/0038_wireless_interfaces.py b/netbox/dcim/migrations/0038_wireless_interfaces.py new file mode 100644 index 00000000000..61cdb3996cf --- /dev/null +++ b/netbox/dcim/migrations/0038_wireless_interfaces.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.1 on 2017-06-16 21:38 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0037_unicode_literals'), + ] + + operations = [ + migrations.AlterField( + model_name='interface', + name='form_factor', + field=models.PositiveSmallIntegerField(choices=[['Virtual interfaces', [[0, 'Virtual'], [200, 'Link Aggregation Group (LAG)']]], ['Ethernet (fixed)', [[800, '100BASE-TX (10/100ME)'], [1000, '1000BASE-T (1GE)'], [1150, '10GBASE-T (10GE)']]], ['Ethernet (modular)', [[1050, 'GBIC (1GE)'], [1100, 'SFP (1GE)'], [1200, 'SFP+ (10GE)'], [1300, 'XFP (10GE)'], [1310, 'XENPAK (10GE)'], [1320, 'X2 (10GE)'], [1350, 'SFP28 (25GE)'], [1400, 'QSFP+ (40GE)'], [1500, 'CFP (100GE)'], [1600, 'QSFP28 (100GE)']]], ['Wireless', [[2600, 'IEEE 802.11a'], [2610, 'IEEE 802.11b/g'], [2620, 'IEEE 802.11n'], [2630, 'IEEE 802.11ac'], [2640, 'IEEE 802.11ad']]], ['FibreChannel', [[3010, 'SFP (1GFC)'], [3020, 'SFP (2GFC)'], [3040, 'SFP (4GFC)'], [3080, 'SFP+ (8GFC)'], [3160, 'SFP+ (16GFC)']]], ['Serial', [[4000, 'T1 (1.544 Mbps)'], [4010, 'E1 (2.048 Mbps)'], [4040, 'T3 (45 Mbps)'], [4050, 'E3 (34 Mbps)']]], ['Stacking', [[5000, 'Cisco StackWise'], [5050, 'Cisco StackWise Plus'], [5100, 'Cisco FlexStack'], [5150, 'Cisco FlexStack Plus'], [5200, 'Juniper VCP']]], ['Other', [[32767, 'Other']]]], default=1200), + ), + migrations.AlterField( + model_name='interfacetemplate', + name='form_factor', + field=models.PositiveSmallIntegerField(choices=[['Virtual interfaces', [[0, 'Virtual'], [200, 'Link Aggregation Group (LAG)']]], ['Ethernet (fixed)', [[800, '100BASE-TX (10/100ME)'], [1000, '1000BASE-T (1GE)'], [1150, '10GBASE-T (10GE)']]], ['Ethernet (modular)', [[1050, 'GBIC (1GE)'], [1100, 'SFP (1GE)'], [1200, 'SFP+ (10GE)'], [1300, 'XFP (10GE)'], [1310, 'XENPAK (10GE)'], [1320, 'X2 (10GE)'], [1350, 'SFP28 (25GE)'], [1400, 'QSFP+ (40GE)'], [1500, 'CFP (100GE)'], [1600, 'QSFP28 (100GE)']]], ['Wireless', [[2600, 'IEEE 802.11a'], [2610, 'IEEE 802.11b/g'], [2620, 'IEEE 802.11n'], [2630, 'IEEE 802.11ac'], [2640, 'IEEE 802.11ad']]], ['FibreChannel', [[3010, 'SFP (1GFC)'], [3020, 'SFP (2GFC)'], [3040, 'SFP (4GFC)'], [3080, 'SFP+ (8GFC)'], [3160, 'SFP+ (16GFC)']]], ['Serial', [[4000, 'T1 (1.544 Mbps)'], [4010, 'E1 (2.048 Mbps)'], [4040, 'T3 (45 Mbps)'], [4050, 'E3 (34 Mbps)']]], ['Stacking', [[5000, 'Cisco StackWise'], [5050, 'Cisco StackWise Plus'], [5100, 'Cisco FlexStack'], [5150, 'Cisco FlexStack Plus'], [5200, 'Juniper VCP']]], ['Other', [[32767, 'Other']]]], default=1200), + ), + ] diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index 6891e191166..b6d345b9864 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -661,6 +661,13 @@ def order_naturally(self, method=IFACE_ORDERING_POSITION): '_vc': "COALESCE(CAST(SUBSTRING({} FROM '\.([0-9]+)$') AS integer), 0)".format(sql_col), }).order_by(*ordering) + def connectable(self): + """ + Return only physical interfaces which are capable of being connected to other interfaces (i.e. not virtual or + wireless). + """ + return self.exclude(form_factor__in=NONCONNECTABLE_IFACE_TYPES) + @python_2_unicode_compatible class InterfaceTemplate(models.Model): @@ -1134,10 +1141,10 @@ def __str__(self): def clean(self): # Virtual interfaces cannot be connected - if self.form_factor in VIRTUAL_IFACE_TYPES and self.is_connected: + if self.form_factor in NONCONNECTABLE_IFACE_TYPES and self.is_connected: raise ValidationError({ - 'form_factor': "Virtual interfaces cannot be connected to another interface or circuit. Disconnect the " - "interface or choose a physical form factor." + 'form_factor': "Virtual and wireless interfaces cannot be connected to another interface or circuit. " + "Disconnect the interface or choose a suitable form factor." }) # An interface's LAG must belong to the same device @@ -1149,7 +1156,7 @@ def clean(self): }) # A virtual interface cannot have a parent LAG - if self.form_factor in VIRTUAL_IFACE_TYPES and self.lag is not None: + if self.form_factor in NONCONNECTABLE_IFACE_TYPES and self.lag is not None: raise ValidationError({ 'lag': "{} interfaces cannot have a parent LAG interface.".format(self.get_form_factor_display()) }) @@ -1166,6 +1173,10 @@ def clean(self): def is_virtual(self): return self.form_factor in VIRTUAL_IFACE_TYPES + @property + def is_wireless(self): + return self.form_factor in WIRELESS_IFACE_TYPES + @property def is_lag(self): return self.form_factor == IFACE_FF_LAG diff --git a/netbox/templates/dcim/inc/interface.html b/netbox/templates/dcim/inc/interface.html index 352574128aa..25d9f6f8a5d 100644 --- a/netbox/templates/dcim/inc/interface.html +++ b/netbox/templates/dcim/inc/interface.html @@ -5,7 +5,7 @@ {% endif %} - + {{ iface.name }} {% if iface.lag %} {{ iface.lag.name }} @@ -22,6 +22,8 @@ {% elif iface.is_virtual %} Virtual interface + {% elif iface.is_wireless %} + Wireless interface {% elif iface.connection %} {% with iface.connected_interface as connected_iface %}