From 82189de78e4c402034c63135f46a8bacc25a461e Mon Sep 17 00:00:00 2001 From: John Anderson Date: Thu, 14 Jun 2018 13:17:06 -0400 Subject: [PATCH 1/8] implements #2166 - asset tag partial string search --- 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 0d5455aa0..701ea111f 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -509,7 +509,7 @@ class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet): Q(name__icontains=value) | Q(serial__icontains=value.strip()) | Q(inventory_items__serial__icontains=value.strip()) | - Q(asset_tag=value.strip()) | + Q(asset_tag__icontains=value.strip()) | Q(comments__icontains=value) ).distinct() From 02b6ffd59a61f5a48f9f08b8efa678fd74867bbf Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 18 Jul 2018 11:03:22 -0400 Subject: [PATCH 2/8] Added note about passphrase-protected keys (#2189) --- netbox/secrets/forms.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/netbox/secrets/forms.py b/netbox/secrets/forms.py index 8e7fa3b16..7a7e81610 100644 --- a/netbox/secrets/forms.py +++ b/netbox/secrets/forms.py @@ -153,7 +153,8 @@ class UserKeyForm(BootstrapMixin, forms.ModelForm): model = UserKey fields = ['public_key'] help_texts = { - 'public_key': "Enter your public RSA key. Keep the private one with you; you'll need it for decryption.", + 'public_key': "Enter your public RSA key. Keep the private one with you; you'll need it for decryption. " + "Please note that passphrase-protected keys are not supported.", } def clean_public_key(self): From 00d218118c027bf7498add8588b719bcd1f2a1bf Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 18 Jul 2018 11:24:36 -0400 Subject: [PATCH 3/8] Fixes #2231: Remove get_absolute_url() from DeviceRole --- netbox/dcim/models.py | 3 --- netbox/dcim/tables.py | 1 - netbox/templates/dcim/device.html | 2 +- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index 14c9ef393..107dcba51 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -781,9 +781,6 @@ class DeviceRole(models.Model): def __str__(self): return self.name - def get_absolute_url(self): - return "{}?role={}".format(reverse('dcim:device_list'), self.slug) - def to_csv(self): return ( self.name, diff --git a/netbox/dcim/tables.py b/netbox/dcim/tables.py index 427687ef9..eb4f74157 100644 --- a/netbox/dcim/tables.py +++ b/netbox/dcim/tables.py @@ -408,7 +408,6 @@ class DeviceBayTemplateTable(BaseTable): class DeviceRoleTable(BaseTable): pk = ToggleColumn() - name = tables.LinkColumn(verbose_name='Name') device_count = tables.TemplateColumn( template_code=DEVICEROLE_DEVICE_COUNT, accessor=Accessor('devices.count'), diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html index 7e36452cb..cf272449f 100644 --- a/netbox/templates/dcim/device.html +++ b/netbox/templates/dcim/device.html @@ -146,7 +146,7 @@ Role - {{ device.device_role }} + {{ device.device_role }} From 29d9b32b6701acef8b23fb84d22063a87ac3232a Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 18 Jul 2018 14:17:35 -0400 Subject: [PATCH 4/8] Fixes #1977: Don't default master vc_position to 1 when creating a new virtual chassis --- netbox/dcim/signals.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/netbox/dcim/signals.py b/netbox/dcim/signals.py index c29a8a857..80e47391a 100644 --- a/netbox/dcim/signals.py +++ b/netbox/dcim/signals.py @@ -11,13 +11,8 @@ def assign_virtualchassis_master(instance, created, **kwargs): """ When a VirtualChassis is created, automatically assign its master device to the VC. """ - # Default to 1 but don't overwrite an existing position (see #2087) - if instance.master.vc_position is not None: - vc_position = instance.master.vc_position - else: - vc_position = 1 if created: - Device.objects.filter(pk=instance.master.pk).update(virtual_chassis=instance, vc_position=vc_position) + Device.objects.filter(pk=instance.master.pk).update(virtual_chassis=instance, vc_position=None) @receiver(pre_delete, sender=VirtualChassis) From d665d4d62a883940b84b1ee484b189ea163f870c Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 18 Jul 2018 14:46:15 -0400 Subject: [PATCH 5/8] Fixes #1992: Isolate errors when one of multiple NAPALM methods fails --- netbox/dcim/api/views.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/netbox/dcim/api/views.py b/netbox/dcim/api/views.py index ce8b4c349..befde771f 100644 --- a/netbox/dcim/api/views.py +++ b/netbox/dcim/api/views.py @@ -267,7 +267,7 @@ class DeviceViewSet(CustomFieldModelViewSet): import napalm except ImportError: raise ServiceUnavailable("NAPALM is not installed. Please see the documentation for instructions.") - from napalm.base.exceptions import ConnectAuthError, ModuleImportError + from napalm.base.exceptions import ModuleImportError # Validate the configured driver try: @@ -281,16 +281,8 @@ class DeviceViewSet(CustomFieldModelViewSet): if not request.user.has_perm('dcim.napalm_read'): return HttpResponseForbidden() - # Validate requested NAPALM methods + # Connect to the device napalm_methods = request.GET.getlist('method') - for method in napalm_methods: - if not hasattr(driver, method): - return HttpResponseBadRequest("Unknown NAPALM method: {}".format(method)) - elif not method.startswith('get_'): - return HttpResponseBadRequest("Unsupported NAPALM method: {}".format(method)) - - # Connect to the device and execute the requested methods - # TODO: Improve error handling response = OrderedDict([(m, None) for m in napalm_methods]) ip_address = str(device.primary_ip.address.ip) d = driver( @@ -302,12 +294,23 @@ class DeviceViewSet(CustomFieldModelViewSet): ) try: d.open() - for method in napalm_methods: - response[method] = getattr(d, method)() except Exception as e: raise ServiceUnavailable("Error connecting to the device at {}: {}".format(ip_address, e)) + # Validate and execute each specified NAPALM method + for method in napalm_methods: + if not hasattr(driver, method): + response[method] = {'error': 'Unknown NAPALM method'} + continue + if not method.startswith('get_'): + response[method] = {'error': 'Only get_* NAPALM methods are supported'} + continue + try: + response[method] = getattr(d, method)() + except NotImplementedError: + response[method] = {'error': 'Method not implemented for NAPALM driver {}'.format(driver)} d.close() + return Response(response) From 6e037e91d3f750d3b45fdd4e66d06c4c22d91f43 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 18 Jul 2018 15:10:12 -0400 Subject: [PATCH 6/8] Fixes #2202: Ditched half-baked concept of tenancy inheritance via VRF --- netbox/templates/ipam/ipaddress.html | 7 ++++--- netbox/templates/ipam/prefix.html | 7 ------- netbox/tenancy/views.py | 10 ++-------- 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/netbox/templates/ipam/ipaddress.html b/netbox/templates/ipam/ipaddress.html index 1509f35cb..24889972b 100644 --- a/netbox/templates/ipam/ipaddress.html +++ b/netbox/templates/ipam/ipaddress.html @@ -65,10 +65,11 @@ Tenant {% if ipaddress.tenant %} + {% if ipaddress.tenant.group %} + {{ ipaddress.tenant.group }} + + {% endif %} {{ ipaddress.tenant }} - {% elif ipaddress.vrf.tenant %} - {{ ipaddress.vrf.tenant }} - {% else %} None {% endif %} diff --git a/netbox/templates/ipam/prefix.html b/netbox/templates/ipam/prefix.html index 11c5fc405..1b23284f4 100644 --- a/netbox/templates/ipam/prefix.html +++ b/netbox/templates/ipam/prefix.html @@ -35,13 +35,6 @@ {% endif %} {{ prefix.tenant }} - {% elif prefix.vrf.tenant %} - {% if prefix.vrf.tenant.group %} - {{ prefix.vrf.tenant.group }} - - {% endif %} - {{ prefix.vrf.tenant }} - {% else %} None {% endif %} diff --git a/netbox/tenancy/views.py b/netbox/tenancy/views.py index 99c4acc8a..9020a8c19 100644 --- a/netbox/tenancy/views.py +++ b/netbox/tenancy/views.py @@ -78,14 +78,8 @@ class TenantView(View): 'rackreservation_count': RackReservation.objects.filter(tenant=tenant).count(), 'device_count': Device.objects.filter(tenant=tenant).count(), 'vrf_count': VRF.objects.filter(tenant=tenant).count(), - 'prefix_count': Prefix.objects.filter( - Q(tenant=tenant) | - Q(tenant__isnull=True, vrf__tenant=tenant) - ).count(), - 'ipaddress_count': IPAddress.objects.filter( - Q(tenant=tenant) | - Q(tenant__isnull=True, vrf__tenant=tenant) - ).count(), + 'prefix_count': Prefix.objects.filter(tenant=tenant).count(), + 'ipaddress_count': IPAddress.objects.filter(tenant=tenant).count(), 'vlan_count': VLAN.objects.filter(tenant=tenant).count(), 'circuit_count': Circuit.objects.filter(tenant=tenant).count(), 'virtualmachine_count': VirtualMachine.objects.filter(tenant=tenant).count(), From c2573774bfba58862715a8bd75d6b53cd5fbc537 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 18 Jul 2018 15:27:45 -0400 Subject: [PATCH 7/8] Fixes #2222: IP addresses created via the available-ips API endpoint should have the same mask as their parent prefix (not /32) --- netbox/ipam/api/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/netbox/ipam/api/views.py b/netbox/ipam/api/views.py index 31e899afd..c709062b0 100644 --- a/netbox/ipam/api/views.py +++ b/netbox/ipam/api/views.py @@ -196,8 +196,9 @@ class PrefixViewSet(CustomFieldModelViewSet): # Assign addresses from the list of available IPs and copy VRF assignment from the parent prefix available_ips = iter(available_ips) + prefix_length = prefix.prefix.prefixlen for requested_ip in requested_ips: - requested_ip['address'] = next(available_ips) + requested_ip['address'] = '{}/{}'.format(next(available_ips), prefix_length) requested_ip['vrf'] = prefix.vrf.pk if prefix.vrf else None # Initialize the serializer with a list or a single object depending on what was requested From 93ce0ce67098f6341c1907caf41d6e40512c6e6e Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 18 Jul 2018 16:14:57 -0400 Subject: [PATCH 8/8] Further reiterated the policy for pull requests --- .github/PULL_REQUEST_TEMPLATE.md | 2 ++ CONTRIBUTING.md | 12 +++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 4e9185a89..7f3d0935a 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -6,6 +6,8 @@ be able to accept. Please indicate the relevant feature request or bug report below. + IF YOUR PULL REQUEST DOES NOT REFERENCE AN ACCEPTED BUG REPORT OR + FEATURE REQUEST, IT WILL BE MARKED AS INVALID AND CLOSED. --> ### Fixes: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4820e5a85..546e1de09 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -91,11 +91,13 @@ appropriate labels will be applied for categorization. ## Submitting Pull Requests -* Be sure to open an issue before starting work on a pull request, and discuss -your idea with the NetBox maintainers before beginning work​. This will help -prevent wasting time on something that might we might not be able to implement. -When suggesting a new feature, also make sure it won't conflict with any work -that's already in progress. +* Be sure to open an issue **before** starting work on a pull request, and +discuss your idea with the NetBox maintainers before beginning work. This will +help prevent wasting time on something that might we might not be able to +implement. When suggesting a new feature, also make sure it won't conflict with +any work that's already in progress. + +* Any pull request which does _not_ relate to an accepted issue will be closed. * When submitting a pull request, please be sure to work off of the `develop` branch, rather than `master`. The `develop` branch is used for ongoing