From 3fe3151af7db860d8c65c6eff1bb7a27323e0f6c Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 18 Mar 2017 21:10:36 +0100 Subject: [PATCH 01/10] Filter on mac address on interface Extension to be able filter on mac address via API --- netbox/dcim/filters.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py index eca792a12..d8a6e1307 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -405,6 +405,10 @@ class InterfaceFilter(django_filters.FilterSet): method='filter_type', label='Interface type', ) + mac_address = django_filters.CharFilter( + method='_mac_address', + label='MAC address', + ) class Meta: model = Interface @@ -420,6 +424,14 @@ class InterfaceFilter(django_filters.FilterSet): return queryset.filter(form_factor=IFACE_FF_LAG) return queryset + def _mac_address(self, queryset, name, value): + value = value.strip() + if not value: + return queryset + try: + return queryset.filter(mac_address=value).distinct() + except AddrFormatError: + return queryset.none() class ConsoleConnectionFilter(django_filters.FilterSet): site = django_filters.CharFilter( From f2dc287f1429b2338b44169e2c358cc55b95c93a Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 18 Mar 2017 21:21:49 +0100 Subject: [PATCH 02/10] Filter on mac address on interface --- netbox/dcim/filters.py | 1 + 1 file changed, 1 insertion(+) diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py index d8a6e1307..e6c2cd465 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -433,6 +433,7 @@ class InterfaceFilter(django_filters.FilterSet): except AddrFormatError: return queryset.none() + class ConsoleConnectionFilter(django_filters.FilterSet): site = django_filters.CharFilter( method='filter_site', From f26253ec491d699844f995a05f6a9b36e481c724 Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 18 Mar 2017 21:26:33 +0100 Subject: [PATCH 03/10] Filter on mac address on interface --- netbox/dcim/filters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py index e6c2cd465..af8f3a089 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -433,7 +433,7 @@ class InterfaceFilter(django_filters.FilterSet): except AddrFormatError: return queryset.none() - + class ConsoleConnectionFilter(django_filters.FilterSet): site = django_filters.CharFilter( method='filter_site', From 66a6a8f33caedb678c3aab8fd2ae5ff6c24e56e4 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 22 Mar 2017 16:58:56 -0400 Subject: [PATCH 04/10] Closes #983: Include peer device names when listing circuits in device view --- netbox/templates/dcim/inc/interface.html | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/netbox/templates/dcim/inc/interface.html b/netbox/templates/dcim/inc/interface.html index 86c2e4090..7d7d66c2e 100644 --- a/netbox/templates/dcim/inc/interface.html +++ b/netbox/templates/dcim/inc/interface.html @@ -35,7 +35,13 @@ {% if peer_termination %} - {{ peer_termination.site }} via + {% if peer_termination.interface %} + {{ peer_termination.interface.device }} + ({{ peer_termination.site }}) + {% else %} + {{ peer_termination.site }} + {% endif %} + via {% endif %} {{ iface.circuit_termination.circuit }} From 32bf17c076741b0e4e615a796b6129c1e52efd83 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 22 Mar 2017 17:29:47 -0400 Subject: [PATCH 05/10] Closes #978: Allow filtering device types by function and subdevice role --- netbox/dcim/forms.py | 17 ++++++++++++++++- netbox/dcim/tables.py | 13 ++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index c79f65d53..07a28f936 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -23,7 +23,7 @@ from .models import ( Interface, IFACE_FF_CHOICES, IFACE_FF_LAG, IFACE_ORDERING_CHOICES, InterfaceConnection, InterfaceTemplate, Manufacturer, Module, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, RACK_TYPE_CHOICES, RACK_WIDTH_CHOICES, Rack, RackGroup, RackReservation, RackRole, Region, Site, STATUS_CHOICES, SUBDEVICE_ROLE_CHILD, - VIRTUAL_IFACE_TYPES + SUBDEVICE_ROLE_PARENT, VIRTUAL_IFACE_TYPES ) @@ -375,6 +375,21 @@ class DeviceTypeFilterForm(BootstrapMixin, CustomFieldFilterForm): queryset=Manufacturer.objects.annotate(filter_count=Count('device_types')), to_field_name='slug' ) + is_console_server = forms.BooleanField( + required=False, label='Is a console server', widget=forms.CheckboxInput(attrs={'value': 'True'})) + is_pdu = forms.BooleanField( + required=False, label='Is a PDU', widget=forms.CheckboxInput(attrs={'value': 'True'}) + ) + is_network_device = forms.BooleanField( + required=False, label='Is a network device', widget=forms.CheckboxInput(attrs={'value': 'True'}) + ) + subdevice_role = forms.NullBooleanField( + required=False, label='Subdevice role', widget=forms.Select(choices=( + ('', '---------'), + (SUBDEVICE_ROLE_PARENT, 'Parent'), + (SUBDEVICE_ROLE_CHILD, 'Child'), + )) + ) # diff --git a/netbox/dcim/tables.py b/netbox/dcim/tables.py index 42c6a39bf..6af772fde 100644 --- a/netbox/dcim/tables.py +++ b/netbox/dcim/tables.py @@ -100,6 +100,10 @@ DEVICE_PRIMARY_IP = """ {{ record.primary_ip4.address.ip|default:"" }} """ +SUBDEVICE_ROLE_TEMPLATE = """ +{% if record.subdevice_role == True %}Parent{% elif record.subdevice_role == False %}Child{% else %}—{% endif %} +""" + UTILIZATION_GRAPH = """ {% load helpers %} {% utilization_graph value %} @@ -249,11 +253,18 @@ class DeviceTypeTable(BaseTable): model = tables.LinkColumn('dcim:devicetype', args=[Accessor('pk')], verbose_name='Device Type') part_number = tables.Column(verbose_name='Part Number') is_full_depth = tables.BooleanColumn(verbose_name='Full Depth') + is_console_server = tables.BooleanColumn(verbose_name='CS') + is_pdu = tables.BooleanColumn(verbose_name='PDU') + is_network_device = tables.BooleanColumn(verbose_name='Net') + subdevice_role = tables.TemplateColumn(SUBDEVICE_ROLE_TEMPLATE, verbose_name='Subdevice Role') instance_count = tables.Column(verbose_name='Instances') class Meta(BaseTable.Meta): model = DeviceType - fields = ('pk', 'model', 'manufacturer', 'part_number', 'u_height', 'is_full_depth', 'instance_count') + fields = ( + 'pk', 'model', 'manufacturer', 'part_number', 'u_height', 'is_full_depth', 'is_console_server', 'is_pdu', + 'is_network_device', 'subdevice_role', 'instance_count' + ) # From b875cea10d5f6a59a250a37a023d32867285ec8b Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 23 Mar 2017 12:57:35 +0100 Subject: [PATCH 06/10] Filter on mac address on interface via API --- netbox/dcim/filters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py index af8f3a089..55cdadfbb 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -429,7 +429,7 @@ class InterfaceFilter(django_filters.FilterSet): if not value: return queryset try: - return queryset.filter(mac_address=value).distinct() + return queryset.filter(mac_address=value) except AddrFormatError: return queryset.none() From c0417c19897af27c01147449ec57581384a632ef Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 23 Mar 2017 10:07:02 -0400 Subject: [PATCH 07/10] Closes #972: Add ability to filter connections list by device name --- netbox/dcim/filters.py | 48 +++++++++++++++++++++++++++++++----------- netbox/dcim/forms.py | 3 +++ 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py index fb16955c7..bf390e17b 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -443,42 +443,58 @@ class ConsoleConnectionFilter(django_filters.FilterSet): method='filter_site', label='Site (slug)', ) - - class Meta: - model = ConsoleServerPort - fields = [] + device = django_filters.CharFilter( + method='filter_device', + label='Device', + ) def filter_site(self, queryset, name, value): if not value.strip(): return queryset return queryset.filter(cs_port__device__site__slug=value) + def filter_device(self, queryset, name, value): + if not value.strip(): + return queryset + return queryset.filter( + Q(device__name__icontains=value) | + Q(cs_port__device__name__icontains=value) + ) + class PowerConnectionFilter(django_filters.FilterSet): site = django_filters.CharFilter( method='filter_site', label='Site (slug)', ) - - class Meta: - model = PowerOutlet - fields = [] + device = django_filters.CharFilter( + method='filter_device', + label='Device', + ) def filter_site(self, queryset, name, value): if not value.strip(): return queryset return queryset.filter(power_outlet__device__site__slug=value) + def filter_device(self, queryset, name, value): + if not value.strip(): + return queryset + return queryset.filter( + Q(device__name__icontains=value) | + Q(power_outlet__device__name__icontains=value) + ) + class InterfaceConnectionFilter(django_filters.FilterSet): site = django_filters.CharFilter( method='filter_site', label='Site (slug)', ) - - class Meta: - model = InterfaceConnection - fields = [] + device = django_filters.CharFilter( + method='filter_device', + label='Device', + ) def filter_site(self, queryset, name, value): if not value.strip(): @@ -487,3 +503,11 @@ class InterfaceConnectionFilter(django_filters.FilterSet): Q(interface_a__device__site__slug=value) | Q(interface_b__device__site__slug=value) ) + + def filter_device(self, queryset, name, value): + if not value.strip(): + return queryset + return queryset.filter( + Q(interface_a__device__name__icontains=value) | + Q(interface_b__device__name__icontains=value) + ) diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 07a28f936..0e09adbb2 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -1658,14 +1658,17 @@ class PopulateDeviceBayForm(BootstrapMixin, forms.Form): class ConsoleConnectionFilterForm(BootstrapMixin, forms.Form): site = forms.ModelChoiceField(required=False, queryset=Site.objects.all(), to_field_name='slug') + device = forms.CharField(required=False, label='Device name') class PowerConnectionFilterForm(BootstrapMixin, forms.Form): site = forms.ModelChoiceField(required=False, queryset=Site.objects.all(), to_field_name='slug') + device = forms.CharField(required=False, label='Device name') class InterfaceConnectionFilterForm(BootstrapMixin, forms.Form): site = forms.ModelChoiceField(required=False, queryset=Site.objects.all(), to_field_name='slug') + device = forms.CharField(required=False, label='Device name') # From 47120fae0123491cd2bf2c07c4dce80839f194ff Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 23 Mar 2017 15:36:24 -0400 Subject: [PATCH 08/10] Rack assignment is optional for devices --- netbox/templates/dcim/device_import.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/templates/dcim/device_import.html b/netbox/templates/dcim/device_import.html index c3915b9c3..50d2f81db 100644 --- a/netbox/templates/dcim/device_import.html +++ b/netbox/templates/dcim/device_import.html @@ -73,7 +73,7 @@ Rack - Rack name + Rack name (optional) R101 From ef59f38ec4a1528b85719fdd828dd7602e5fd9f4 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 23 Mar 2017 16:24:35 -0400 Subject: [PATCH 09/10] Release v1.9.3 --- netbox/netbox/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 856fee9ff..ae81d2689 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -12,7 +12,7 @@ except ImportError: "the documentation.") -VERSION = '1.9.2-dev' +VERSION = '1.9.3' # Import local configuration for setting in ['ALLOWED_HOSTS', 'DATABASE', 'SECRET_KEY']: From a51f5edbc8b18a2d549212c1a3039eb62a3daa55 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 23 Mar 2017 16:29:42 -0400 Subject: [PATCH 10/10] Post-release version bump --- netbox/netbox/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index ae81d2689..4d8599063 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -12,7 +12,7 @@ except ImportError: "the documentation.") -VERSION = '1.9.3' +VERSION = '1.9.4-dev' # Import local configuration for setting in ['ALLOWED_HOSTS', 'DATABASE', 'SECRET_KEY']: