From 1149f91edb987eb3abec42b57587619c5dcae214 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 14 Mar 2017 17:18:05 -0400 Subject: [PATCH 01/16] 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 66065e9e7..856fee9ff 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -12,7 +12,7 @@ except ImportError: "the documentation.") -VERSION = '1.9.2' +VERSION = '1.9.2-dev' # Import local configuration for setting in ['ALLOWED_HOSTS', 'DATABASE', 'SECRET_KEY']: From b73de50340fcd4fce85a42451fa26bf3a7910510 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 15 Mar 2017 12:00:53 -0400 Subject: [PATCH 02/16] Added survey announcement to README --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 66c35250b..f2b430929 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +**The [2017 NetBox User Survey](https://goo.gl/forms/75HnNS2iE0Y1hVFH3) is open!** Please consider taking a moment to respond. Your feedback helps shape the pace and focus of NetBox development. The survey will remain open until 2017-03-31. Results will be published on the mailing list. + +--- + ![NetBox](docs/netbox_logo.png "NetBox logo") NetBox is an IP address management (IPAM) and data center infrastructure management (DCIM) tool. Initially conceived by the network engineering team at [DigitalOcean](https://www.digitalocean.com/), NetBox was developed specifically to address the needs of network and infrastructure engineers. From 28c77b183b0c7d7b35a3845213ccdad5d4f4546c Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 15 Mar 2017 12:16:46 -0400 Subject: [PATCH 03/16] Added examples to the graphs documentation --- docs/data-model/extras.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/data-model/extras.md b/docs/data-model/extras.md index 58da76cee..ec424fec2 100644 --- a/docs/data-model/extras.md +++ b/docs/data-model/extras.md @@ -90,6 +90,22 @@ NetBox does not have the ability to generate graphs natively, but this feature a * **Source URL:** The source of the image to be embedded. The associated object will be available as a template variable named `obj`. * **Link URL (optional):** A URL to which the graph will be linked. The associated object will be available as a template variable named `obj`. +## Examples + +You only need to define one graph object for each graph you want to include when viewing an object. For example, if you want to include a graph of traffic through an interface over the past five minutes, your graph source might looks like this: + +``` +https://my.nms.local/graphs/?node={{ obj.device.name }}&interface={{ obj.name }}&duration=5m +``` + +You can define several graphs to provide multiple contexts when viewing an object. For example: + +``` +https://my.nms.local/graphs/?type=throughput&node={{ obj.device.name }}&interface={{ obj.name }}&duration=60m +https://my.nms.local/graphs/?type=throughput&node={{ obj.device.name }}&interface={{ obj.name }}&duration=24h +https://my.nms.local/graphs/?type=errors&node={{ obj.device.name }}&interface={{ obj.name }}&duration=60m +``` + # Topology Maps NetBox can generate simple topology maps from the physical network connections recorded in its database. First, you'll need to create a topology map definition under the admin UI at Extras > Topology Maps. From 86e73a825f0946e46ef8b4d7ea0e1afb47246e60 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 16 Mar 2017 22:27:01 -0400 Subject: [PATCH 04/16] Fix error when assigning a new interface to a LAG --- netbox/dcim/views.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index cb307324e..246fe06f0 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -90,7 +90,12 @@ class ComponentCreateView(View): self.parent_field: parent.pk, 'name': name, } - component_data.update(data) + # Replace objects with their primary key to keep component_form.clean() happy + for k, v in data.items(): + if hasattr(v, 'pk'): + component_data[k] = v.pk + else: + component_data[k] = v component_form = self.model_form(component_data) if component_form.is_valid(): new_components.append(component_form.save(commit=False)) From eff1cb8e2faaed49b1c9a97a7b7af6efa02b1899 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 17 Mar 2017 21:43:46 -0400 Subject: [PATCH 05/16] Limit tests to one per major Python version --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b23c9d8fc..1576da4cf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,9 +9,7 @@ env: language: python python: - "2.7" - - "3.4" - "3.5" - - "3.6" install: - pip install -r requirements.txt - pip install pep8 From 6662b34fac453a6176fa6ddad5c025dfb2bc50fd Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 18 Mar 2017 21:10:36 +0100 Subject: [PATCH 06/16] 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 a7df85acdfb1ae241d542a01e03116414ef4f8dd Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 18 Mar 2017 21:21:49 +0100 Subject: [PATCH 07/16] 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 2c273eef03ef03c01e5c23a11966eac5b43d0765 Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 18 Mar 2017 21:26:33 +0100 Subject: [PATCH 08/16] 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 023b67ca4a4ec70fae42da5780a08b368a80b4b6 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 20 Mar 2017 14:05:26 -0400 Subject: [PATCH 09/16] Added a footer link to the GitHub wiki --- netbox/templates/_base.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/netbox/templates/_base.html b/netbox/templates/_base.html index 6f0dfced6..0ebc4f5b4 100644 --- a/netbox/templates/_base.html +++ b/netbox/templates/_base.html @@ -295,7 +295,8 @@

Docs · API · - Code + Code · + Help

From 74fb6f1a4da47f2b978e69cad0b96fc44783e29c Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 22 Mar 2017 09:39:30 -0400 Subject: [PATCH 10/16] Closes #981: Allow filtering primary objects by a given set of IDs --- netbox/circuits/filters.py | 4 +++- netbox/dcim/filters.py | 6 +++++- netbox/ipam/filters.py | 8 +++++++- netbox/secrets/filters.py | 2 ++ netbox/tenancy/filters.py | 3 ++- netbox/utilities/filters.py | 11 +++++++++++ 6 files changed, 30 insertions(+), 4 deletions(-) diff --git a/netbox/circuits/filters.py b/netbox/circuits/filters.py index b07e87068..087512028 100644 --- a/netbox/circuits/filters.py +++ b/netbox/circuits/filters.py @@ -5,12 +5,13 @@ from django.db.models import Q from dcim.models import Site from extras.filters import CustomFieldFilterSet from tenancy.models import Tenant -from utilities.filters import NullableModelMultipleChoiceFilter +from utilities.filters import NullableModelMultipleChoiceFilter, NumericInFilter from .models import Provider, Circuit, CircuitType class ProviderFilter(CustomFieldFilterSet, django_filters.FilterSet): + id__in = NumericInFilter(name='id', lookup_expr='in') q = django_filters.CharFilter( method='search', label='Search', @@ -42,6 +43,7 @@ class ProviderFilter(CustomFieldFilterSet, django_filters.FilterSet): class CircuitFilter(CustomFieldFilterSet, django_filters.FilterSet): + id__in = NumericInFilter(name='id', lookup_expr='in') q = django_filters.CharFilter( method='search', label='Search', diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py index eca792a12..63624dc30 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -5,7 +5,7 @@ from django.db.models import Q from extras.filters import CustomFieldFilterSet from tenancy.models import Tenant -from utilities.filters import NullableModelMultipleChoiceFilter +from utilities.filters import NullableModelMultipleChoiceFilter, NumericInFilter from .models import ( ConsolePort, ConsoleServerPort, Device, DeviceRole, DeviceType, IFACE_FF_LAG, Interface, InterfaceConnection, Manufacturer, Platform, PowerOutlet, PowerPort, Rack, RackGroup, RackReservation, RackRole, Region, Site, @@ -14,6 +14,7 @@ from .models import ( class SiteFilter(CustomFieldFilterSet, django_filters.FilterSet): + id__in = NumericInFilter(name='id', lookup_expr='in') q = django_filters.CharFilter( method='search', label='Search', @@ -81,6 +82,7 @@ class RackGroupFilter(django_filters.FilterSet): class RackFilter(CustomFieldFilterSet, django_filters.FilterSet): + id__in = NumericInFilter(name='id', lookup_expr='in') q = django_filters.CharFilter( method='search', label='Search', @@ -157,6 +159,7 @@ class RackReservationFilter(django_filters.FilterSet): class DeviceTypeFilter(CustomFieldFilterSet, django_filters.FilterSet): + id__in = NumericInFilter(name='id', lookup_expr='in') q = django_filters.CharFilter( method='search', label='Search', @@ -191,6 +194,7 @@ class DeviceTypeFilter(CustomFieldFilterSet, django_filters.FilterSet): class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet): + id__in = NumericInFilter(name='id', lookup_expr='in') q = django_filters.CharFilter( method='search', label='Search', diff --git a/netbox/ipam/filters.py b/netbox/ipam/filters.py index 10a18d1b7..3c39a4308 100644 --- a/netbox/ipam/filters.py +++ b/netbox/ipam/filters.py @@ -7,12 +7,13 @@ from django.db.models import Q from dcim.models import Site, Device, Interface from extras.filters import CustomFieldFilterSet from tenancy.models import Tenant -from utilities.filters import NullableModelMultipleChoiceFilter +from utilities.filters import NullableModelMultipleChoiceFilter, NumericInFilter from .models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF class VRFFilter(CustomFieldFilterSet, django_filters.FilterSet): + id__in = NumericInFilter(name='id', lookup_expr='in') q = django_filters.CharFilter( method='search', label='Search', @@ -44,6 +45,7 @@ class VRFFilter(CustomFieldFilterSet, django_filters.FilterSet): class RIRFilter(django_filters.FilterSet): + id__in = NumericInFilter(name='id', lookup_expr='in') class Meta: model = RIR @@ -51,6 +53,7 @@ class RIRFilter(django_filters.FilterSet): class AggregateFilter(CustomFieldFilterSet, django_filters.FilterSet): + id__in = NumericInFilter(name='id', lookup_expr='in') q = django_filters.CharFilter( method='search', label='Search', @@ -84,6 +87,7 @@ class AggregateFilter(CustomFieldFilterSet, django_filters.FilterSet): class PrefixFilter(CustomFieldFilterSet, django_filters.FilterSet): + id__in = NumericInFilter(name='id', lookup_expr='in') q = django_filters.CharFilter( method='search', label='Search', @@ -182,6 +186,7 @@ class PrefixFilter(CustomFieldFilterSet, django_filters.FilterSet): class IPAddressFilter(CustomFieldFilterSet, django_filters.FilterSet): + id__in = NumericInFilter(name='id', lookup_expr='in') q = django_filters.CharFilter( method='search', label='Search', @@ -283,6 +288,7 @@ class VLANGroupFilter(django_filters.FilterSet): class VLANFilter(CustomFieldFilterSet, django_filters.FilterSet): + id__in = NumericInFilter(name='id', lookup_expr='in') q = django_filters.CharFilter( method='search', label='Search', diff --git a/netbox/secrets/filters.py b/netbox/secrets/filters.py index 5f59daad4..f2cab9691 100644 --- a/netbox/secrets/filters.py +++ b/netbox/secrets/filters.py @@ -4,9 +4,11 @@ from django.db.models import Q from .models import Secret, SecretRole from dcim.models import Device +from utilities.filters import NumericInFilter class SecretFilter(django_filters.FilterSet): + id__in = NumericInFilter(name='id', lookup_expr='in') q = django_filters.CharFilter( method='search', label='Search', diff --git a/netbox/tenancy/filters.py b/netbox/tenancy/filters.py index ed1721102..b96345980 100644 --- a/netbox/tenancy/filters.py +++ b/netbox/tenancy/filters.py @@ -3,11 +3,12 @@ import django_filters from django.db.models import Q from extras.filters import CustomFieldFilterSet -from utilities.filters import NullableModelMultipleChoiceFilter +from utilities.filters import NullableModelMultipleChoiceFilter, NumericInFilter from .models import Tenant, TenantGroup class TenantFilter(CustomFieldFilterSet, django_filters.FilterSet): + id__in = NumericInFilter(name='id', lookup_expr='in') q = django_filters.CharFilter( method='search', label='Search', diff --git a/netbox/utilities/filters.py b/netbox/utilities/filters.py index d1dbf39b8..c352f0f41 100644 --- a/netbox/utilities/filters.py +++ b/netbox/utilities/filters.py @@ -6,6 +6,17 @@ from django.db.models import Q from django.utils.encoding import force_text +# +# Filters +# + +class NumericInFilter(django_filters.BaseInFilter, django_filters.NumberFilter): + """ + Filters for a set of numeric values. Example: id__in=100,200,300 + """ + pass + + class NullableModelMultipleChoiceField(forms.ModelMultipleChoiceField): """ This field operates like a normal ModelMultipleChoiceField except that it allows for one additional choice which is From 30e584d017603827b25651bfe261190940c28808 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 22 Mar 2017 16:58:56 -0400 Subject: [PATCH 11/16] 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 a8960b3fd2db5f71f97a6a4de601c7d75735a8f2 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 22 Mar 2017 17:29:47 -0400 Subject: [PATCH 12/16] 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 a020c4c56b1aa059bdd749ce7c51d56a7e0a8fa2 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 23 Mar 2017 12:57:35 +0100 Subject: [PATCH 13/16] 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 7d3831ee25f841d47957bda210622485dea7843e Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 23 Mar 2017 10:07:02 -0400 Subject: [PATCH 14/16] 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 3febbaf060418bbf430b27f5f93af6b33334be8c Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 23 Mar 2017 15:36:24 -0400 Subject: [PATCH 15/16] 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 d1fbaa7695ecf93970ddf6db2f6652882f213279 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 23 Mar 2017 16:24:35 -0400 Subject: [PATCH 16/16] 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']: