From eb40275427047b1007b9a73d14c5d087ac6a7021 Mon Sep 17 00:00:00 2001 From: Saria Hajjar Date: Wed, 8 Jan 2020 17:23:09 +0000 Subject: [PATCH 01/20] Fixes #3623: Word expansion for interfaces --- docs/release-notes/version-2.6.md | 1 + netbox/utilities/forms.py | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/version-2.6.md b/docs/release-notes/version-2.6.md index f3ff8798a..f5fc4beba 100644 --- a/docs/release-notes/version-2.6.md +++ b/docs/release-notes/version-2.6.md @@ -4,6 +4,7 @@ * [#2050](https://github.com/netbox-community/netbox/issues/2050) - Preview image attachments when hovering the link * [#3187](https://github.com/netbox-community/netbox/issues/3187) - Add rack selection field to rack elevations +* [#3623](https://github.com/netbox-community/netbox/issues/3623) - Add word expansion during interface creation ## Bug Fixes diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py index eeee719ae..4cd92ca12 100644 --- a/netbox/utilities/forms.py +++ b/netbox/utilities/forms.py @@ -60,8 +60,13 @@ def parse_alphanumeric_range(string): for n in list(range(int(begin), int(end) + 1)): values.append(n) else: - for n in list(range(ord(begin), ord(end) + 1)): - values.append(chr(n)) + # Value-based + if begin == end: + values.append(begin) + # Range-based + else: + for n in list(range(ord(begin), ord(end) + 1)): + values.append(chr(n)) return values From 396bb28967b040247eea67c92ab7ea47a67a9c62 Mon Sep 17 00:00:00 2001 From: Saria Hajjar Date: Wed, 8 Jan 2020 17:28:31 +0000 Subject: [PATCH 02/20] Added example and handled invalid ranges gracefully --- netbox/utilities/forms.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py index 4cd92ca12..39422c265 100644 --- a/netbox/utilities/forms.py +++ b/netbox/utilities/forms.py @@ -65,6 +65,9 @@ def parse_alphanumeric_range(string): values.append(begin) # Range-based else: + # Not a valid range (more than a single character) + if not len(begin) == len(end) == 1: + raise forms.ValidationError('Range "{}" is invalid.'.format(dash_range)) for n in list(range(ord(begin), ord(end) + 1)): values.append(chr(n)) return values @@ -486,6 +489,7 @@ class ExpandableNameField(forms.CharField): 'Mixed cases and types within a single range are not supported.
' \ 'Examples:' def to_python(self, value): From 1cdbfd6d60ab5cefb6cd3f8497fb3f565618b117 Mon Sep 17 00:00:00 2001 From: Saria Hajjar Date: Thu, 9 Jan 2020 10:00:02 +0000 Subject: [PATCH 03/20] Fixes #3668: search address by DNS name when assigning --- docs/release-notes/version-2.6.md | 1 + netbox/ipam/forms.py | 7 ++++++- netbox/ipam/tables.py | 2 +- netbox/ipam/views.py | 1 + netbox/templates/ipam/ipaddress_assign.html | 1 + 5 files changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/version-2.6.md b/docs/release-notes/version-2.6.md index f3ff8798a..341e62a79 100644 --- a/docs/release-notes/version-2.6.md +++ b/docs/release-notes/version-2.6.md @@ -4,6 +4,7 @@ * [#2050](https://github.com/netbox-community/netbox/issues/2050) - Preview image attachments when hovering the link * [#3187](https://github.com/netbox-community/netbox/issues/3187) - Add rack selection field to rack elevations +* [#3668](https://github.com/netbox-community/netbox/issues/3668) - Search by DNS name when assigning IP address ## Bug Fixes diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index 413e72eaf..f4bcfb2e6 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -943,7 +943,12 @@ class IPAddressAssignForm(BootstrapMixin, forms.Form): ) ) address = forms.CharField( - label='IP Address' + label='IP Address', + required=False, + ) + dns_name = forms.CharField( + label='DNS Name', + required=False, ) diff --git a/netbox/ipam/tables.py b/netbox/ipam/tables.py index e4d2bf8b4..d6e26f6a1 100644 --- a/netbox/ipam/tables.py +++ b/netbox/ipam/tables.py @@ -373,7 +373,7 @@ class IPAddressAssignTable(BaseTable): class Meta(BaseTable.Meta): model = IPAddress - fields = ('address', 'vrf', 'status', 'role', 'tenant', 'parent', 'interface', 'description') + fields = ('address', 'dns_name', 'vrf', 'status', 'role', 'tenant', 'parent', 'interface', 'description') orderable = False diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 2cc1a0ea8..d38366d54 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -745,6 +745,7 @@ class IPAddressAssignView(PermissionRequiredMixin, View): ).filter( vrf=form.cleaned_data['vrf'], address__istartswith=form.cleaned_data['address'], + dns_name__icontains=form.cleaned_data['dns_name'], )[:100] # Limit to 100 results table = tables.IPAddressAssignTable(queryset) diff --git a/netbox/templates/ipam/ipaddress_assign.html b/netbox/templates/ipam/ipaddress_assign.html index 579f2d98b..349c06919 100644 --- a/netbox/templates/ipam/ipaddress_assign.html +++ b/netbox/templates/ipam/ipaddress_assign.html @@ -26,6 +26,7 @@
{% render_field form.vrf %} {% render_field form.address %} + {% render_field form.dns_name %}
From 472486acd60e1e4c37bbba24eb3e967c95d690c0 Mon Sep 17 00:00:00 2001 From: Saria Hajjar Date: Thu, 9 Jan 2020 16:26:11 +0000 Subject: [PATCH 04/20] Changed to `q` filter --- netbox/ipam/forms.py | 10 +++------- netbox/ipam/views.py | 12 +++++------- netbox/templates/ipam/ipaddress_assign.html | 5 ++--- 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index f4bcfb2e6..c3387a5aa 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -933,7 +933,7 @@ class IPAddressBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd class IPAddressAssignForm(BootstrapMixin, forms.Form): - vrf = forms.ModelChoiceField( + vrf_id = forms.ModelChoiceField( queryset=VRF.objects.all(), required=False, label='VRF', @@ -942,13 +942,9 @@ class IPAddressAssignForm(BootstrapMixin, forms.Form): api_url="/api/ipam/vrfs/" ) ) - address = forms.CharField( - label='IP Address', - required=False, - ) - dns_name = forms.CharField( - label='DNS Name', + q = forms.CharField( required=False, + label='Search', ) diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index d6cada398..084cf1205 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -749,14 +749,12 @@ class IPAddressAssignView(PermissionRequiredMixin, View): if form.is_valid(): - queryset = IPAddress.objects.prefetch_related( + addresses = IPAddress.objects.prefetch_related( 'vrf', 'tenant', 'interface__device', 'interface__virtual_machine' - ).filter( - vrf=form.cleaned_data['vrf'], - address__istartswith=form.cleaned_data['address'], - dns_name__icontains=form.cleaned_data['dns_name'], - )[:100] # Limit to 100 results - table = tables.IPAddressAssignTable(queryset) + ) + # Limit to 100 results + addresses = filters.IPAddressFilter(request.POST, addresses).qs[:100] + table = tables.IPAddressAssignTable(addresses) return render(request, 'ipam/ipaddress_assign.html', { 'form': form, diff --git a/netbox/templates/ipam/ipaddress_assign.html b/netbox/templates/ipam/ipaddress_assign.html index 349c06919..ab163533f 100644 --- a/netbox/templates/ipam/ipaddress_assign.html +++ b/netbox/templates/ipam/ipaddress_assign.html @@ -24,9 +24,8 @@
Select IP Address
- {% render_field form.vrf %} - {% render_field form.address %} - {% render_field form.dns_name %} + {% render_field form.vrf_id %} + {% render_field form.q %}
From 581ed52b2424a3cc9f5b6dc1fd998591f1754344 Mon Sep 17 00:00:00 2001 From: Saria Hajjar Date: Thu, 9 Jan 2020 16:30:13 +0000 Subject: [PATCH 05/20] Added changelog for 3009 --- docs/release-notes/version-2.6.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/version-2.6.md b/docs/release-notes/version-2.6.md index 1f3e9a430..afc9b87c1 100644 --- a/docs/release-notes/version-2.6.md +++ b/docs/release-notes/version-2.6.md @@ -4,6 +4,7 @@ * [#2050](https://github.com/netbox-community/netbox/issues/2050) - Preview image attachments when hovering the link * [#2589](https://github.com/netbox-community/netbox/issues/2589) - Toggle for showing available prefixes/ip addresses +* [#3009](https://github.com/netbox-community/netbox/issues/3009) - Search by description when assigning IP address * [#3187](https://github.com/netbox-community/netbox/issues/3187) - Add rack selection field to rack elevations * [#3668](https://github.com/netbox-community/netbox/issues/3668) - Search by DNS name when assigning IP address * [#3851](https://github.com/netbox-community/netbox/issues/3851) - Allow passing initial data to custom script forms From 86865b91f8e8b1a684ed5fec688d7aed4945d48a Mon Sep 17 00:00:00 2001 From: Saria Hajjar Date: Thu, 9 Jan 2020 16:32:01 +0000 Subject: [PATCH 06/20] Added changelog for 3009 again as I accidentally removed it while merging --- docs/release-notes/version-2.6.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/version-2.6.md b/docs/release-notes/version-2.6.md index a6e54cbef..e054e7684 100644 --- a/docs/release-notes/version-2.6.md +++ b/docs/release-notes/version-2.6.md @@ -4,6 +4,7 @@ * [#2050](https://github.com/netbox-community/netbox/issues/2050) - Preview image attachments when hovering the link * [#2589](https://github.com/netbox-community/netbox/issues/2589) - Toggle for showing available prefixes/ip addresses +* [#3009](https://github.com/netbox-community/netbox/issues/3009) - Search by description when assigning IP address * [#3090](https://github.com/netbox-community/netbox/issues/3090) - Add filter field for device interfaces * [#3187](https://github.com/netbox-community/netbox/issues/3187) - Add rack selection field to rack elevations * [#3440](https://github.com/netbox-community/netbox/issues/3440) - Add total length to cable trace From c13b9d87983a1fc7b3e1434e219bb85545f90513 Mon Sep 17 00:00:00 2001 From: Saria Hajjar Date: Thu, 9 Jan 2020 18:26:10 +0000 Subject: [PATCH 07/20] Added tests for IPv4 --- netbox/utilities/tests/test_forms.py | 69 ++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 netbox/utilities/tests/test_forms.py diff --git a/netbox/utilities/tests/test_forms.py b/netbox/utilities/tests/test_forms.py new file mode 100644 index 000000000..0434ff137 --- /dev/null +++ b/netbox/utilities/tests/test_forms.py @@ -0,0 +1,69 @@ +from django.test import TestCase + +from utilities.forms import * + + +class ExpandIPAddress(TestCase): + """ + Validate the operation of expand_ipaddress_pattern(). + """ + def test_ipv4_range(self): + input = '1.2.3.[9-10]/32' + output = sorted([ + '1.2.3.9/32', + '1.2.3.10/32', + ]) + + self.assertEqual(sorted(list(expand_ipaddress_pattern(input, 4))), output) + + def test_ipv4_set(self): + input = '1.2.3.[4,44]/32' + output = sorted([ + '1.2.3.4/32', + '1.2.3.44/32', + ]) + + self.assertEqual(sorted(list(expand_ipaddress_pattern(input, 4))), output) + + def test_ipv4_multiple_ranges(self): + input = '1.[9-10].3.[9-11]/32' + output = sorted([ + '1.9.3.9/32', + '1.9.3.10/32', + '1.9.3.11/32', + '1.10.3.9/32', + '1.10.3.10/32', + '1.10.3.11/32', + ]) + + self.assertEqual(sorted(list(expand_ipaddress_pattern(input, 4))), output) + + def test_ipv4_multiple_sets(self): + input = '1.[2,22].3.[4,44]/32' + output = sorted([ + '1.2.3.4/32', + '1.2.3.44/32', + '1.22.3.4/32', + '1.22.3.44/32', + ]) + + self.assertEqual(sorted(list(expand_ipaddress_pattern(input, 4))), output) + + + def test_ipv4_set_and_range(self): + input = '1.[2,22].3.[9-11]/32' + output = sorted([ + '1.2.3.9/32', + '1.2.3.10/32', + '1.2.3.11/32', + '1.22.3.9/32', + '1.22.3.10/32', + '1.22.3.11/32', + ]) + + self.assertEqual(sorted(list(expand_ipaddress_pattern(input, 4))), output) + + # TODO: IPv6 + # TODO: negative tests + +# TODO: alphanumeric From 883655ce71cf69237f5f583749ebd2e12a335781 Mon Sep 17 00:00:00 2001 From: Saria Hajjar Date: Thu, 9 Jan 2020 20:10:51 +0000 Subject: [PATCH 08/20] Fixes #3393: Paginate circuits at the provider details view --- docs/release-notes/version-2.6.md | 1 + netbox/circuits/views.py | 12 +++++- netbox/templates/circuits/provider.html | 54 +------------------------ 3 files changed, 14 insertions(+), 53 deletions(-) diff --git a/docs/release-notes/version-2.6.md b/docs/release-notes/version-2.6.md index ee6ea2e4d..7d6609863 100644 --- a/docs/release-notes/version-2.6.md +++ b/docs/release-notes/version-2.6.md @@ -9,6 +9,7 @@ * [#3090](https://github.com/netbox-community/netbox/issues/3090) - Add filter field for device interfaces * [#3187](https://github.com/netbox-community/netbox/issues/3187) - Add rack selection field to rack elevations * [#3440](https://github.com/netbox-community/netbox/issues/3440) - Add total length to cable trace +* [#3393](https://github.com/netbox-community/netbox/issues/3393) - Paginate the circuits at the provider details view * [#3851](https://github.com/netbox-community/netbox/issues/3851) - Allow passing initial data to custom script forms ## Bug Fixes diff --git a/netbox/circuits/views.py b/netbox/circuits/views.py index 655b714d7..73b3e5d3e 100644 --- a/netbox/circuits/views.py +++ b/netbox/circuits/views.py @@ -1,3 +1,4 @@ +from django.conf import settings from django.contrib import messages from django.contrib.auth.decorators import permission_required from django.contrib.auth.mixins import PermissionRequiredMixin @@ -5,9 +6,11 @@ from django.db import transaction from django.db.models import Count, OuterRef, Subquery from django.shortcuts import get_object_or_404, redirect, render from django.views.generic import View +from django_tables2 import RequestConfig from extras.models import Graph, GRAPH_TYPE_PROVIDER from utilities.forms import ConfirmationForm +from utilities.paginator import EnhancedPaginator from utilities.views import ( BulkDeleteView, BulkEditView, BulkImportView, ObjectDeleteView, ObjectEditView, ObjectListView, ) @@ -36,11 +39,18 @@ class ProviderView(PermissionRequiredMixin, View): provider = get_object_or_404(Provider, slug=slug) circuits = Circuit.objects.filter(provider=provider).prefetch_related('type', 'tenant', 'terminations__site') + circuits_table = tables.CircuitTable(circuits, orderable=False) show_graphs = Graph.objects.filter(type=GRAPH_TYPE_PROVIDER).exists() + paginate = { + 'paginator_class': EnhancedPaginator, + 'per_page': request.GET.get('per_page', settings.PAGINATE_COUNT) + } + RequestConfig(request, paginate).configure(circuits_table) + return render(request, 'circuits/provider.html', { 'provider': provider, - 'circuits': circuits, + 'circuits_table': circuits_table, 'show_graphs': show_graphs, }) diff --git a/netbox/templates/circuits/provider.html b/netbox/templates/circuits/provider.html index a83a5337a..178e488d8 100644 --- a/netbox/templates/circuits/provider.html +++ b/netbox/templates/circuits/provider.html @@ -125,58 +125,7 @@
Circuits
- - - - - - - - - - {% for c in circuits %} - - - - - - - - - {% empty %} - - - - {% endfor %} -
Circuit IDTypeTenantA SideZ SideDescription
- {{ c.cid }} - - {{ c.type }} - - {% if c.tenant %} - {{ c.tenant }} - {% else %} - - {% endif %} - - {% if c.termination_a %} - {{ c.termination_a.site }} - {% else %} - - {% endif %} - - {% if c.termination_z %} - {{ c.termination_z.site }} - {% else %} - - {% endif %} - - {% if c.description %} - {{ c.description }} - {% else %} - - {% endif %} -
None
+ {% include 'inc/table.html' with table=circuits_table %} {% if perms.circuits.add_circuit %} {% endif %} + {% include 'inc/paginator.html' with paginator=circuits_table.paginator page=circuits_table.page %} {% include 'inc/modal.html' with modal_name='graphs' %} From 94a7d8e49360dd3debefefab9628d0f01e13b5d2 Mon Sep 17 00:00:00 2001 From: Saria Hajjar Date: Thu, 9 Jan 2020 20:15:22 +0000 Subject: [PATCH 09/20] Hid the provider column --- netbox/circuits/views.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/netbox/circuits/views.py b/netbox/circuits/views.py index 73b3e5d3e..5d76e38ee 100644 --- a/netbox/circuits/views.py +++ b/netbox/circuits/views.py @@ -39,9 +39,11 @@ class ProviderView(PermissionRequiredMixin, View): provider = get_object_or_404(Provider, slug=slug) circuits = Circuit.objects.filter(provider=provider).prefetch_related('type', 'tenant', 'terminations__site') - circuits_table = tables.CircuitTable(circuits, orderable=False) show_graphs = Graph.objects.filter(type=GRAPH_TYPE_PROVIDER).exists() + circuits_table = tables.CircuitTable(circuits, orderable=False) + circuits_table.columns.hide('provider') + paginate = { 'paginator_class': EnhancedPaginator, 'per_page': request.GET.get('per_page', settings.PAGINATE_COUNT) From 1e740a70f766fbde6304246002dfd830dbe06889 Mon Sep 17 00:00:00 2001 From: Saria Hajjar Date: Thu, 9 Jan 2020 20:23:16 +0000 Subject: [PATCH 10/20] Corrected placement of changelog --- docs/release-notes/version-2.6.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/release-notes/version-2.6.md b/docs/release-notes/version-2.6.md index 7d6609863..f4691f34e 100644 --- a/docs/release-notes/version-2.6.md +++ b/docs/release-notes/version-2.6.md @@ -8,8 +8,8 @@ * [#2589](https://github.com/netbox-community/netbox/issues/2589) - Toggle for showing available prefixes/ip addresses * [#3090](https://github.com/netbox-community/netbox/issues/3090) - Add filter field for device interfaces * [#3187](https://github.com/netbox-community/netbox/issues/3187) - Add rack selection field to rack elevations -* [#3440](https://github.com/netbox-community/netbox/issues/3440) - Add total length to cable trace * [#3393](https://github.com/netbox-community/netbox/issues/3393) - Paginate the circuits at the provider details view +* [#3440](https://github.com/netbox-community/netbox/issues/3440) - Add total length to cable trace * [#3851](https://github.com/netbox-community/netbox/issues/3851) - Allow passing initial data to custom script forms ## Bug Fixes From 4eacc57522d3bdb4e5e3c23ffca84494c70c00cd Mon Sep 17 00:00:00 2001 From: Saria Hajjar Date: Thu, 9 Jan 2020 21:12:35 +0000 Subject: [PATCH 11/20] Fixes #3876: set min and max values for ASN field --- docs/release-notes/version-2.6.md | 1 + netbox/dcim/fields.py | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/docs/release-notes/version-2.6.md b/docs/release-notes/version-2.6.md index ee6ea2e4d..abc65f12c 100644 --- a/docs/release-notes/version-2.6.md +++ b/docs/release-notes/version-2.6.md @@ -21,6 +21,7 @@ * [#3862](https://github.com/netbox-community/netbox/issues/3862) - Allow filtering device components by multiple device names * [#3864](https://github.com/netbox-community/netbox/issues/3864) - Disallow /0 masks * [#3872](https://github.com/netbox-community/netbox/issues/3872) - Paginate related IPs of an address +* [#3876](https://github.com/netbox-community/netbox/issues/3876) - Fixed min/max to ASN input field at the site creation page --- diff --git a/netbox/dcim/fields.py b/netbox/dcim/fields.py index 9624ce0a3..d8fef0d53 100644 --- a/netbox/dcim/fields.py +++ b/netbox/dcim/fields.py @@ -11,6 +11,11 @@ class ASNField(models.BigIntegerField): MaxValueValidator(4294967295), ] + def formfield(self, **kwargs): + defaults = {'min_value': 1, 'max_value': 4294967295} + defaults.update(**kwargs) + return super().formfield(**defaults) + class mac_unix_expanded_uppercase(mac_unix_expanded): word_fmt = '%.2X' From 6c19c88e99038bae25d9be46b8499960d0ed502a Mon Sep 17 00:00:00 2001 From: Saria Hajjar Date: Thu, 9 Jan 2020 21:58:38 +0000 Subject: [PATCH 12/20] Replaced ASN bounds with constants --- netbox/dcim/constants.py | 4 ++++ netbox/dcim/fields.py | 8 +++++--- netbox/dcim/forms.py | 4 ++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/netbox/dcim/constants.py b/netbox/dcim/constants.py index ccaa48636..f325e34d4 100644 --- a/netbox/dcim/constants.py +++ b/netbox/dcim/constants.py @@ -1,4 +1,8 @@ +# BGP ASN bounds +BGP_ASN_MIN = 1 +BGP_ASN_MAX = 2**32 - 1 + # Rack types RACK_TYPE_2POST = 100 RACK_TYPE_4POST = 200 diff --git a/netbox/dcim/fields.py b/netbox/dcim/fields.py index d8fef0d53..719b6755a 100644 --- a/netbox/dcim/fields.py +++ b/netbox/dcim/fields.py @@ -3,16 +3,18 @@ from django.core.validators import MinValueValidator, MaxValueValidator from django.db import models from netaddr import AddrFormatError, EUI, mac_unix_expanded +from .constants import * + class ASNField(models.BigIntegerField): description = "32-bit ASN field" default_validators = [ - MinValueValidator(1), - MaxValueValidator(4294967295), + MinValueValidator(BGP_ASN_MIN), + MaxValueValidator(BGP_ASN_MAX), ] def formfield(self, **kwargs): - defaults = {'min_value': 1, 'max_value': 4294967295} + defaults = {'min_value': BGP_ASN_MIN, 'max_value': BGP_ASN_MAX} defaults.update(**kwargs) return super().formfield(**defaults) diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index dbb9cff15..6086491d0 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -292,8 +292,8 @@ class SiteBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor ) ) asn = forms.IntegerField( - min_value=1, - max_value=4294967295, + min_value=BGP_ASN_MIN, + max_value=BGP_ASN_MAX, required=False, label='ASN' ) From d88b3456c4acaa3851ef097c2fae40738a727ff7 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 9 Jan 2020 20:13:21 -0500 Subject: [PATCH 13/20] Add configuration file for GitHub Stale bot --- .github/lock.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/lock.yml diff --git a/.github/lock.yml b/.github/lock.yml new file mode 100644 index 000000000..36a41b04e --- /dev/null +++ b/.github/lock.yml @@ -0,0 +1,23 @@ +# Configuration for Lock (https://github.com/apps/lock) + +# Number of days of inactivity before a closed issue or pull request is locked +daysUntilLock: 90 + +# Skip issues and pull requests created before a given timestamp. Timestamp must +# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable +skipCreatedBefore: 2020-01-01 + +# Issues and pull requests with these labels will be ignored. Set to `[]` to disable +exemptLabels: [] + +# Label to add before locking, such as `outdated`. Set to `false` to disable +lockLabel: false + +# Comment to post before locking. Set to `false` to disable +lockComment: false + +# Assign `resolved` as the reason for locking. Set to `false` to disable +setLockReason: true + +# Limit to only `issues` or `pulls` +# only: issues From 0296aa240ab710751ca74224683a35768d27c251 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 9 Jan 2020 20:14:31 -0500 Subject: [PATCH 14/20] Clean up Stale bot config formatting --- .github/stale.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/stale.yml b/.github/stale.yml index 7c8d03f12..61201cc4e 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -1,20 +1,27 @@ +# Configuration for Stale (https://github.com/apps/stale) + # Number of days of inactivity before an issue becomes stale daysUntilStale: 14 + # Number of days of inactivity before a stale issue is closed daysUntilClose: 7 + # Issues with these labels will never be considered stale exemptLabels: - "status: accepted" - "status: gathering feedback" - "status: blocked" + # Label to use when marking an issue as stale staleLabel: wontfix + # Comment to post when marking an issue as stale. Set to `false` to disable markComment: > This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. NetBox is governed by a small group of core maintainers which means not all opened issues may receive direct feedback. Please see our [contributing guide](https://github.com/netbox-community/netbox/blob/develop/CONTRIBUTING.md). + # Comment to post when closing a stale issue. Set to `false` to disable closeComment: > This issue has been automatically closed due to lack of activity. In an From fe89982d4ef663427d91e4efe67b364ccebaa811 Mon Sep 17 00:00:00 2001 From: Saria Hajjar Date: Fri, 10 Jan 2020 10:26:46 +0000 Subject: [PATCH 15/20] Removed redundant list call --- netbox/utilities/tests/test_forms.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/netbox/utilities/tests/test_forms.py b/netbox/utilities/tests/test_forms.py index 0434ff137..1fd09341c 100644 --- a/netbox/utilities/tests/test_forms.py +++ b/netbox/utilities/tests/test_forms.py @@ -14,7 +14,7 @@ class ExpandIPAddress(TestCase): '1.2.3.10/32', ]) - self.assertEqual(sorted(list(expand_ipaddress_pattern(input, 4))), output) + self.assertEqual(sorted(expand_ipaddress_pattern(input, 4)), output) def test_ipv4_set(self): input = '1.2.3.[4,44]/32' @@ -23,7 +23,7 @@ class ExpandIPAddress(TestCase): '1.2.3.44/32', ]) - self.assertEqual(sorted(list(expand_ipaddress_pattern(input, 4))), output) + self.assertEqual(sorted(expand_ipaddress_pattern(input, 4)), output) def test_ipv4_multiple_ranges(self): input = '1.[9-10].3.[9-11]/32' @@ -36,7 +36,7 @@ class ExpandIPAddress(TestCase): '1.10.3.11/32', ]) - self.assertEqual(sorted(list(expand_ipaddress_pattern(input, 4))), output) + self.assertEqual(sorted(expand_ipaddress_pattern(input, 4)), output) def test_ipv4_multiple_sets(self): input = '1.[2,22].3.[4,44]/32' @@ -47,8 +47,7 @@ class ExpandIPAddress(TestCase): '1.22.3.44/32', ]) - self.assertEqual(sorted(list(expand_ipaddress_pattern(input, 4))), output) - + self.assertEqual(sorted(expand_ipaddress_pattern(input, 4)), output) def test_ipv4_set_and_range(self): input = '1.[2,22].3.[9-11]/32' @@ -61,7 +60,7 @@ class ExpandIPAddress(TestCase): '1.22.3.11/32', ]) - self.assertEqual(sorted(list(expand_ipaddress_pattern(input, 4))), output) + self.assertEqual(sorted(expand_ipaddress_pattern(input, 4)), output) # TODO: IPv6 # TODO: negative tests From 2eba84dad5e2c385ea3c3553f89ae09af7a31afd Mon Sep 17 00:00:00 2001 From: Saria Hajjar Date: Fri, 10 Jan 2020 11:06:01 +0000 Subject: [PATCH 16/20] Added tests for IPv6 --- netbox/utilities/tests/test_forms.py | 67 +++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/netbox/utilities/tests/test_forms.py b/netbox/utilities/tests/test_forms.py index 1fd09341c..ea693f42c 100644 --- a/netbox/utilities/tests/test_forms.py +++ b/netbox/utilities/tests/test_forms.py @@ -62,7 +62,72 @@ class ExpandIPAddress(TestCase): self.assertEqual(sorted(expand_ipaddress_pattern(input, 4)), output) - # TODO: IPv6 + def test_ipv6_range(self): + input = 'fec::abcd:[9-b]/64' + output = sorted([ + 'fec::abcd:9/64', + 'fec::abcd:a/64', + 'fec::abcd:b/64', + ]) + + self.assertEqual(sorted(expand_ipaddress_pattern(input, 6)), output) + + def test_ipv6_range_multichar_field(self): + input = 'fec::abcd:[f-11]/64' + output = sorted([ + 'fec::abcd:f/64', + 'fec::abcd:10/64', + 'fec::abcd:11/64', + ]) + + self.assertEqual(sorted(expand_ipaddress_pattern(input, 6)), output) + + def test_ipv6_set(self): + input = 'fec::abcd:[9,ab]/64' + output = sorted([ + 'fec::abcd:9/64', + 'fec::abcd:ab/64', + ]) + + self.assertEqual(sorted(expand_ipaddress_pattern(input, 6)), output) + + def test_ipv6_multiple_ranges(self): + input = 'fec::[1-2]bcd:[9-b]/64' + output = sorted([ + 'fec::1bcd:9/64', + 'fec::1bcd:a/64', + 'fec::1bcd:b/64', + 'fec::2bcd:9/64', + 'fec::2bcd:a/64', + 'fec::2bcd:b/64', + ]) + + self.assertEqual(sorted(expand_ipaddress_pattern(input, 6)), output) + + def test_ipv6_multiple_sets(self): + input = 'fec::[a,f]bcd:[9,ab]/64' + output = sorted([ + 'fec::abcd:9/64', + 'fec::abcd:ab/64', + 'fec::fbcd:9/64', + 'fec::fbcd:ab/64', + ]) + + self.assertEqual(sorted(expand_ipaddress_pattern(input, 6)), output) + + def test_ipv6_set_and_range(self): + input = 'fec::[dead,beaf]:[9-b]/64' + output = sorted([ + 'fec::dead:9/64', + 'fec::dead:a/64', + 'fec::dead:b/64', + 'fec::beaf:9/64', + 'fec::beaf:a/64', + 'fec::beaf:b/64', + ]) + + self.assertEqual(sorted(expand_ipaddress_pattern(input, 6)), output) + # TODO: negative tests # TODO: alphanumeric From acb66c7dc0f99435299bfb81ec7d626dfd632714 Mon Sep 17 00:00:00 2001 From: Saria Hajjar Date: Fri, 10 Jan 2020 11:21:37 +0000 Subject: [PATCH 17/20] Negative tests for expand_ipaddress_pattern --- netbox/utilities/tests/test_forms.py | 34 +++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/netbox/utilities/tests/test_forms.py b/netbox/utilities/tests/test_forms.py index ea693f42c..7bad34e3c 100644 --- a/netbox/utilities/tests/test_forms.py +++ b/netbox/utilities/tests/test_forms.py @@ -128,6 +128,38 @@ class ExpandIPAddress(TestCase): self.assertEqual(sorted(expand_ipaddress_pattern(input, 6)), output) - # TODO: negative tests + def test_invalid_address_family(self): + with self.assertRaisesRegex(Exception, 'Invalid IP address family: 5'): + sorted(expand_ipaddress_pattern(None, 5)) + + def test_invalid_non_pattern(self): + with self.assertRaises(ValueError): + sorted(expand_ipaddress_pattern('1.2.3.4/32', 4)) + + def test_invalid_range(self): + with self.assertRaises(ValueError): + sorted(expand_ipaddress_pattern('1.2.3.[4-]/32', 4)) + + with self.assertRaises(ValueError): + sorted(expand_ipaddress_pattern('1.2.3.[-4]/32', 4)) + + with self.assertRaises(ValueError): + sorted(expand_ipaddress_pattern('1.2.3.[4--5]/32', 4)) + + def test_invalid_range_bounds(self): + self.assertEqual(sorted(expand_ipaddress_pattern('1.2.3.[4-3]/32', 6)), []) + + def test_invalid_set(self): + with self.assertRaises(ValueError): + sorted(expand_ipaddress_pattern('1.2.3.[4]/32', 4)) + + with self.assertRaises(ValueError): + sorted(expand_ipaddress_pattern('1.2.3.[4,]/32', 4)) + + with self.assertRaises(ValueError): + sorted(expand_ipaddress_pattern('1.2.3.[,4]/32', 4)) + + with self.assertRaises(ValueError): + sorted(expand_ipaddress_pattern('1.2.3.[4,,5]/32', 4)) # TODO: alphanumeric From 71120d9899d4f6481a07b9728c10ba3dbc0aa0bd Mon Sep 17 00:00:00 2001 From: Saria Hajjar Date: Fri, 10 Jan 2020 11:54:43 +0000 Subject: [PATCH 18/20] Added tests for alphanumeric --- netbox/utilities/tests/test_forms.py | 120 ++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) diff --git a/netbox/utilities/tests/test_forms.py b/netbox/utilities/tests/test_forms.py index 7bad34e3c..2d7235505 100644 --- a/netbox/utilities/tests/test_forms.py +++ b/netbox/utilities/tests/test_forms.py @@ -1,3 +1,4 @@ +from django import forms from django.test import TestCase from utilities.forms import * @@ -162,4 +163,121 @@ class ExpandIPAddress(TestCase): with self.assertRaises(ValueError): sorted(expand_ipaddress_pattern('1.2.3.[4,,5]/32', 4)) -# TODO: alphanumeric + +class ExpandAlphanumeric(TestCase): + """ + Validate the operation of expand_alphanumeric_pattern(). + """ + def test_range_numberic(self): + input = 'r[9-11]a' + output = sorted([ + 'r9a', + 'r10a', + 'r11a', + ]) + + self.assertEqual(sorted(expand_alphanumeric_pattern(input)), output) + + def test_range_alpha(self): + input = '[r-t]1a' + output = sorted([ + 'r1a', + 's1a', + 't1a', + ]) + + self.assertEqual(sorted(expand_alphanumeric_pattern(input)), output) + + def test_set(self): + input = '[r,t]1a' + output = sorted([ + 'r1a', + 't1a', + ]) + + self.assertEqual(sorted(expand_alphanumeric_pattern(input)), output) + + def test_set_multichar(self): + input = '[ra,tb]1a' + output = sorted([ + 'ra1a', + 'tb1a', + ]) + + self.assertEqual(sorted(expand_alphanumeric_pattern(input)), output) + + def test_multiple_ranges(self): + input = '[r-t]1[a-b]' + output = sorted([ + 'r1a', + 'r1b', + 's1a', + 's1b', + 't1a', + 't1b', + ]) + + self.assertEqual(sorted(expand_alphanumeric_pattern(input)), output) + + def test_multiple_sets(self): + input = '[ra,tb]1[ax,by]' + output = sorted([ + 'ra1ax', + 'ra1by', + 'tb1ax', + 'tb1by', + ]) + + self.assertEqual(sorted(expand_alphanumeric_pattern(input)), output) + + def test_set_and_range(self): + input = '[ra,tb]1[a-c]' + output = sorted([ + 'ra1a', + 'ra1b', + 'ra1c', + 'tb1a', + 'tb1b', + 'tb1c', + ]) + + self.assertEqual(sorted(expand_alphanumeric_pattern(input)), output) + + def test_invalid_non_pattern(self): + with self.assertRaises(ValueError): + sorted(expand_alphanumeric_pattern('r9a')) + + def test_invalid_range(self): + with self.assertRaises(ValueError): + sorted(expand_alphanumeric_pattern('r[8-]a')) + + with self.assertRaises(ValueError): + sorted(expand_alphanumeric_pattern('r[-8]a')) + + with self.assertRaises(ValueError): + sorted(expand_alphanumeric_pattern('r[8--9]a')) + + def test_invalid_range_alphanumeric(self): + self.assertEqual(sorted(expand_alphanumeric_pattern('r[9-a]a')), []) + self.assertEqual(sorted(expand_alphanumeric_pattern('r[a-9]a')), []) + + def test_invalid_range_bounds(self): + self.assertEqual(sorted(expand_alphanumeric_pattern('r[9-8]a')), []) + self.assertEqual(sorted(expand_alphanumeric_pattern('r[b-a]a')), []) + + def test_invalid_range_len(self): + with self.assertRaises(forms.ValidationError): + sorted(expand_alphanumeric_pattern('r[a-bb]a')) + + def test_invalid_set(self): + with self.assertRaises(ValueError): + sorted(expand_alphanumeric_pattern('r[a]a')) + + with self.assertRaises(ValueError): + sorted(expand_alphanumeric_pattern('r[a,]a')) + + with self.assertRaises(ValueError): + sorted(expand_alphanumeric_pattern('r[,a]a')) + + with self.assertRaises(ValueError): + sorted(expand_alphanumeric_pattern('r[a,,b]a')) From 6bc8f2e50bc6e859064f24e4d78c396c70c8e0ca Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 10 Jan 2020 10:21:11 -0500 Subject: [PATCH 19/20] Fixes #3882: Fix filtering of devices by rack group --- docs/release-notes/version-2.6.md | 1 + netbox/dcim/forms.py | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/release-notes/version-2.6.md b/docs/release-notes/version-2.6.md index 025994856..d7832a823 100644 --- a/docs/release-notes/version-2.6.md +++ b/docs/release-notes/version-2.6.md @@ -26,6 +26,7 @@ * [#3864](https://github.com/netbox-community/netbox/issues/3864) - Disallow /0 masks * [#3872](https://github.com/netbox-community/netbox/issues/3872) - Paginate related IPs of an address * [#3876](https://github.com/netbox-community/netbox/issues/3876) - Fixed min/max to ASN input field at the site creation page +* [#3882](https://github.com/netbox-community/netbox/issues/3882) - Fix filtering of devices by rack group --- diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 6086491d0..f0b91c2f5 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -739,7 +739,7 @@ class RackElevationFilterForm(RackFilterForm): # Filter the rack field based on the site and group self.fields['site'].widget.add_filter_for('id', 'site') - self.fields['group_id'].widget.add_filter_for('id', 'group_id') + self.fields['rack_group_id'].widget.add_filter_for('id', 'group_id') # @@ -1791,7 +1791,7 @@ class DeviceBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF class DeviceFilterForm(BootstrapMixin, LocalConfigContextFilterForm, TenancyFilterForm, CustomFieldFilterForm): model = Device field_order = [ - 'q', 'region', 'site', 'group_id', 'rack_id', 'status', 'role', 'tenant_group', 'tenant', + 'q', 'region', 'site', 'rack_group_id', 'rack_id', 'status', 'role', 'tenant_group', 'tenant', 'manufacturer_id', 'device_type_id', 'mac_address', 'has_primary_ip', ] q = forms.CharField( @@ -1817,12 +1817,12 @@ class DeviceFilterForm(BootstrapMixin, LocalConfigContextFilterForm, TenancyFilt api_url="/api/dcim/sites/", value_field="slug", filter_for={ - 'group_id': 'site', + 'rack_group_id': 'site', 'rack_id': 'site', } ) ) - group_id = FilterChoiceField( + rack_group_id = FilterChoiceField( queryset=RackGroup.objects.prefetch_related( 'site' ), From 509a115f687a1242d0b10ed1d458f176b4a862f3 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 10 Jan 2020 12:24:47 -0500 Subject: [PATCH 20/20] Extend section regarding test adaptation --- docs/development/extending-models.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/development/extending-models.md b/docs/development/extending-models.md index 0070c5545..dd44bb2ab 100644 --- a/docs/development/extending-models.md +++ b/docs/development/extending-models.md @@ -69,6 +69,14 @@ If the new field will be included in the object list view, add a column to the m Edit the object's view template to display the new field. There may also be a custom add/edit form template that needs to be updated. -### 11. Adjust API and model tests +### 11. Create/extend test cases -Extend the model and/or API tests to verify that the new field and any accompanying validation logic perform as expected. This is especially important for relational fields. +Create or extend the relevant test cases to verify that the new field and any accompanying validation logic perform as expected. This is especially important for relational fields. NetBox incorporates various test suites, including: + +* API serializer/view tests +* Filter tests +* Form tests +* Model tests +* View tests + +Be diligent to ensure all of the relevant test suites are adapted or extended as necessary to test any new functionality.