From 268b145f21a7b65cc2f02be2ee95328893352e8d Mon Sep 17 00:00:00 2001 From: Douglas Thor Date: Thu, 1 Mar 2018 15:05:51 -0800 Subject: [PATCH 01/16] Add note about copying reports to `upgrading.md` The `upgrading.md` file does not mention reports. If the user created reports in the old version's default directory (`./netbox/reports`), then the reports will not be transferred to the new version. --- docs/installation/upgrading.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/installation/upgrading.md b/docs/installation/upgrading.md index 02a08716b..59b109c36 100644 --- a/docs/installation/upgrading.md +++ b/docs/installation/upgrading.md @@ -27,6 +27,12 @@ Be sure to replicate your uploaded media as well. (The exact action necessary wi # cp -pr /opt/netbox-X.Y.Z/netbox/media/ /opt/netbox/netbox/ ``` +Also make sure to copy over any reports that you've made. Note that if you made them in a separate directory (`/opt/netbox-reports` for example), then you will not need to copy them - the config file that you copied earlier will point to the correct location. + +```no-highlight +# cp -r /opt/netbox-X.Y.X/netbox/reports /opt/netbox/netbox/reports/ +``` + If you followed the original installation guide to set up gunicorn, be sure to copy its configuration as well: ```no-highlight From b7ff2273a492eb2212a3168bb99a12e01eafc3f9 Mon Sep 17 00:00:00 2001 From: Grokzen Date: Tue, 8 May 2018 16:06:53 +0200 Subject: [PATCH 02/16] Add export button to rack roles list view. --- netbox/templates/dcim/rackrole_list.html | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/netbox/templates/dcim/rackrole_list.html b/netbox/templates/dcim/rackrole_list.html index e18bf7941..51cb19781 100644 --- a/netbox/templates/dcim/rackrole_list.html +++ b/netbox/templates/dcim/rackrole_list.html @@ -1,22 +1,17 @@ {% extends '_base.html' %} -{% load helpers %} +{% load buttons %} {% block content %}
{% if perms.dcim.add_rackrole %} - - - Add a rack role - - - - Import rack roles - + {% add_button 'dcim:rackrole_add' %} + {% import_button 'dcim:rackrole_import' %} {% endif %} + {% export_button content_type %}

{% block title %}Rack Roles{% endblock %}

-
+
{% include 'utilities/obj_table.html' with bulk_delete_url='dcim:rackrole_bulk_delete' %}
From bcf0b6c7889bba66627732cbc543439e69de8f13 Mon Sep 17 00:00:00 2001 From: Erik Hetland Date: Sat, 19 May 2018 11:50:03 +0200 Subject: [PATCH 03/16] Fixing typo in permission check for ClusterView. --- netbox/virtualization/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/virtualization/views.py b/netbox/virtualization/views.py index 6de6b86c7..a23bc2133 100644 --- a/netbox/virtualization/views.py +++ b/netbox/virtualization/views.py @@ -115,7 +115,7 @@ class ClusterView(View): 'site', 'rack', 'tenant', 'device_type__manufacturer' ) device_table = DeviceTable(list(devices), orderable=False) - if request.user.has_perm('virtualization:change_cluster'): + if request.user.has_perm('virtualization.change_cluster'): device_table.columns.show('pk') return render(request, 'virtualization/cluster.html', { From 51710f87a45d76cf05cf3bbb16ee7bfa31fc38ca Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 19 Apr 2018 11:17:17 -0400 Subject: [PATCH 04/16] Post-release version bump --- netbox/dcim/views.py | 2 ++ netbox/utilities/views.py | 7 +++++-- netbox/virtualization/views.py | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 924fe67fb..6e7aa070c 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -157,6 +157,7 @@ class RegionBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): permission_required = 'dcim.delete_region' cls = Region queryset = Region.objects.annotate(site_count=Count('sites')) + filter = filters.RegionFilter table = tables.RegionTable default_return_url = 'dcim:region_list' @@ -491,6 +492,7 @@ class RackReservationBulkEditView(PermissionRequiredMixin, BulkEditView): class RackReservationBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): permission_required = 'dcim.delete_rackreservation' cls = RackReservation + filter = filters.RackReservationFilter table = tables.RackReservationTable default_return_url = 'dcim:rackreservation_list' diff --git a/netbox/utilities/views.py b/netbox/utilities/views.py index d060e53d7..94b44fc48 100644 --- a/netbox/utilities/views.py +++ b/netbox/utilities/views.py @@ -626,8 +626,11 @@ class BulkDeleteView(View): return_url = reverse(self.default_return_url) # Are we deleting *all* objects in the queryset or just a selected subset? - if request.POST.get('_all') and self.filter is not None: - pk_list = [obj.pk for obj in self.filter(request.GET, self.cls.objects.only('pk')).qs] + if request.POST.get('_all'): + if self.filter is not None: + pk_list = [obj.pk for obj in self.filter(request.GET, self.cls.objects.only('pk')).qs] + else: + pk_list = self.cls.objects.values_list('pk', flat=True) else: pk_list = [int(pk) for pk in request.POST.getlist('pk')] diff --git a/netbox/virtualization/views.py b/netbox/virtualization/views.py index a23bc2133..5aef710c1 100644 --- a/netbox/virtualization/views.py +++ b/netbox/virtualization/views.py @@ -160,6 +160,7 @@ class ClusterBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): permission_required = 'virtualization.delete_cluster' cls = Cluster queryset = Cluster.objects.all() + filter = filters.ClusterFilter table = tables.ClusterTable default_return_url = 'virtualization:cluster_list' From 9b79d71ca3e1894fc4b8c86c4285d92e68530d8d Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 21 May 2018 17:31:43 -0400 Subject: [PATCH 05/16] Fixes #2093: Fix link to circuit termination in device interfaces table --- netbox/templates/dcim/inc/interface.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/templates/dcim/inc/interface.html b/netbox/templates/dcim/inc/interface.html index aa0a9cbd5..33e30b126 100644 --- a/netbox/templates/dcim/inc/interface.html +++ b/netbox/templates/dcim/inc/interface.html @@ -105,7 +105,7 @@ - + {% else %} From d4292ffad4fc5f0d536eea9a071f1fcb061b4302 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 22 May 2018 13:56:11 -0400 Subject: [PATCH 06/16] Fixes #2066: Catch AddrFormatError on invalid IP addresses --- netbox/ipam/fields.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/netbox/ipam/fields.py b/netbox/ipam/fields.py index e0842d10b..8c7dbb690 100644 --- a/netbox/ipam/fields.py +++ b/netbox/ipam/fields.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from django.core.exceptions import ValidationError from django.db import models -from netaddr import IPNetwork +from netaddr import AddrFormatError, IPNetwork from .formfields import IPFormField from . import lookups @@ -26,7 +26,9 @@ class BaseIPField(models.Field): return value try: return IPNetwork(value) - except ValueError as e: + except AddrFormatError as e: + raise ValidationError("Invalid IP address format: {}".format(value)) + except (TypeError, ValueError) as e: raise ValidationError(e) def get_prep_value(self, value): From d9baff4d5c5eb22c3db9637e6272a2cd9c609fd4 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 22 May 2018 14:09:06 -0400 Subject: [PATCH 07/16] Fixes #2075: Enable tenant assignment when creating a rack reservation via the API --- netbox/dcim/api/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index d458bc646..e37354d47 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -233,7 +233,7 @@ class WritableRackReservationSerializer(ValidatedModelSerializer): class Meta: model = RackReservation - fields = ['id', 'rack', 'units', 'user', 'description'] + fields = ['id', 'rack', 'units', 'user', 'tenant', 'description'] # From 56b65d7341257a9b50ffd0f4de8e44582e353a55 Mon Sep 17 00:00:00 2001 From: "Reimann, Timo" Date: Mon, 5 Mar 2018 09:21:10 +0100 Subject: [PATCH 08/16] Changed upgrading documentation for ease of use --- docs/installation/upgrading.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/installation/upgrading.md b/docs/installation/upgrading.md index 59b109c36..b2700596e 100644 --- a/docs/installation/upgrading.md +++ b/docs/installation/upgrading.md @@ -12,19 +12,19 @@ Download and extract the latest version: # wget https://github.com/digitalocean/netbox/archive/vX.Y.Z.tar.gz # tar -xzf vX.Y.Z.tar.gz -C /opt # cd /opt/ -# ln -sf netbox-X.Y.Z/ netbox +# ln -sfn netbox-X.Y.Z/ netbox ``` Copy the 'configuration.py' you created when first installing to the new version: ```no-highlight -# cp /opt/netbox-X.Y.Z/netbox/netbox/configuration.py /opt/netbox/netbox/netbox/configuration.py +# cp netbox-X.Y.Z/netbox/netbox/configuration.py netbox/netbox/netbox/configuration.py ``` Be sure to replicate your uploaded media as well. (The exact action necessary will depend on where you choose to store your media, but in general moving or copying the media directory will suffice.) ```no-highlight -# cp -pr /opt/netbox-X.Y.Z/netbox/media/ /opt/netbox/netbox/ +# cp -pr netbox-X.Y.Z/netbox/media/ netbox/netbox/ ``` Also make sure to copy over any reports that you've made. Note that if you made them in a separate directory (`/opt/netbox-reports` for example), then you will not need to copy them - the config file that you copied earlier will point to the correct location. @@ -36,13 +36,13 @@ Also make sure to copy over any reports that you've made. Note that if you made If you followed the original installation guide to set up gunicorn, be sure to copy its configuration as well: ```no-highlight -# cp /opt/netbox-X.Y.Z/gunicorn_config.py /opt/netbox/gunicorn_config.py +# cp netbox-X.Y.Z/gunicorn_config.py netbox/gunicorn_config.py ``` Copy the LDAP configuration if using LDAP: ```no-highlight -# cp /opt/netbox-X.Y.Z/netbox/netbox/ldap_config.py /opt/netbox/netbox/netbox/ldap_config.py +# cp netbox-X.Y.Z/netbox/netbox/ldap_config.py netbox/netbox/netbox/ldap_config.py ``` ## Option B: Clone the Git Repository (latest master release) From e772f62e3f193923dd393a51bb3b98a4a4bd269e Mon Sep 17 00:00:00 2001 From: Mandar Gokhale Date: Wed, 23 May 2018 17:41:52 -0400 Subject: [PATCH 09/16] Add "does" to error messages Those error messages looked a bit strange when I got them, hence the fix. --- netbox/dcim/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index ac1affdef..450dc8a1b 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -1236,7 +1236,7 @@ class ConsoleServerPort(models.Model): raise ValidationError("Console server ports must be assigned to devices.") device_type = self.device.device_type if not device_type.is_console_server: - raise ValidationError("The {} {} device type not support assignment of console server ports.".format( + raise ValidationError("The {} {} device type does not support assignment of console server ports.".format( device_type.manufacturer, device_type )) @@ -1318,7 +1318,7 @@ class PowerOutlet(models.Model): raise ValidationError("Power outlets must be assigned to devices.") device_type = self.device.device_type if not device_type.is_pdu: - raise ValidationError("The {} {} device type not support assignment of power outlets.".format( + raise ValidationError("The {} {} device type does not support assignment of power outlets.".format( device_type.manufacturer, device_type )) @@ -1403,7 +1403,7 @@ class Interface(models.Model): if self.device is not None: device_type = self.device.device_type if not device_type.is_network_device: - raise ValidationError("The {} {} device type not support assignment of network interfaces.".format( + raise ValidationError("The {} {} device type does not support assignment of network interfaces.".format( device_type.manufacturer, device_type )) From 6a906ad987ed15cc83eeb5dbf82c83d9c8bcc101 Mon Sep 17 00:00:00 2001 From: zmoody Date: Tue, 5 Jun 2018 10:26:33 -0500 Subject: [PATCH 10/16] Fixes #2143 - PUTs to Site Endpoint Requires Value for time_zone Allow null values for `time_zone` field in the writeable serializer for the sites endpoint. --- netbox/dcim/api/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index e37354d47..988a2d59f 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -80,7 +80,7 @@ class NestedSiteSerializer(serializers.ModelSerializer): class WritableSiteSerializer(CustomFieldModelSerializer): - time_zone = TimeZoneField(required=False) + time_zone = TimeZoneField(required=False, allow_null=True) class Meta: model = Site From f405103c339338b7c62d851e6e652d9e4009826b Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 7 Jun 2018 10:55:30 -0400 Subject: [PATCH 11/16] Fixes #2150: Fix display of LLDP neighbors when interface name contains a colon --- netbox/templates/dcim/device_lldp_neighbors.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/templates/dcim/device_lldp_neighbors.html b/netbox/templates/dcim/device_lldp_neighbors.html index 4fe914f64..0e423ad56 100644 --- a/netbox/templates/dcim/device_lldp_neighbors.html +++ b/netbox/templates/dcim/device_lldp_neighbors.html @@ -53,7 +53,7 @@ $(document).ready(function() { success: function(json) { $.each(json['get_lldp_neighbors'], function(iface, neighbors) { var neighbor = neighbors[0]; - var row = $('#' + iface.split(".")[0].replace(/(\/)/g, "\\$1")); + var row = $('#' + iface.split(".")[0].replace(/([\/:])/g, "\\$1")); // Glean configured hostnames/interfaces from the DOM var configured_device = row.children('td.configured_device').attr('data'); From ebcf8b6ece70ed87db93afaf5d1a820a34e8ccae Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 7 Jun 2018 14:22:56 -0400 Subject: [PATCH 12/16] Fixes #2127: Prevent non-conntectable interfaces from being connected --- netbox/dcim/models.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index 450dc8a1b..39bd4ad3d 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -1536,6 +1536,18 @@ class InterfaceConnection(models.Model): raise ValidationError({ 'interface_b': "Cannot connect an interface to itself." }) + if self.interface_a.form_factor in NONCONNECTABLE_IFACE_TYPES: + raise ValidationError({ + 'interface_a': '{} is not a connectable interface type.'.format( + self.interface_a.get_form_factor_display() + ) + }) + if self.interface_b.form_factor in NONCONNECTABLE_IFACE_TYPES: + raise ValidationError({ + 'interface_b': '{} is not a connectable interface type.'.format( + self.interface_b.get_form_factor_display() + ) + }) except ObjectDoesNotExist: pass From b31d3764df9e1bcd6564a5a905fbea0f8af64e27 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 7 Jun 2018 14:51:27 -0400 Subject: [PATCH 13/16] Fixes #2148: Do not force timezone selection when editing sites in bulk --- netbox/dcim/forms.py | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 05dc0ea6f..da8dc0457 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -166,13 +166,37 @@ class SiteCSVForm(forms.ModelForm): class SiteBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm): - pk = forms.ModelMultipleChoiceField(queryset=Site.objects.all(), widget=forms.MultipleHiddenInput) - status = forms.ChoiceField(choices=add_blank_choice(SITE_STATUS_CHOICES), required=False, initial='') - region = TreeNodeChoiceField(queryset=Region.objects.all(), required=False) - tenant = forms.ModelChoiceField(queryset=Tenant.objects.all(), required=False) - asn = forms.IntegerField(min_value=1, max_value=4294967295, required=False, label='ASN') - description = forms.CharField(max_length=100, required=False) - time_zone = TimeZoneFormField(required=False) + pk = forms.ModelMultipleChoiceField( + queryset=Site.objects.all(), + widget=forms.MultipleHiddenInput + ) + status = forms.ChoiceField( + choices=add_blank_choice(SITE_STATUS_CHOICES), + required=False, + initial='' + ) + region = TreeNodeChoiceField( + queryset=Region.objects.all(), + required=False + ) + tenant = forms.ModelChoiceField( + queryset=Tenant.objects.all(), + required=False + ) + asn = forms.IntegerField( + min_value=1, + max_value=4294967295, + required=False, + label='ASN' + ) + description = forms.CharField( + max_length=100, + required=False + ) + time_zone = TimeZoneFormField( + choices=add_blank_choice(TimeZoneFormField().choices), + required=False + ) class Meta: nullable_fields = ['region', 'tenant', 'asn', 'description', 'time_zone'] From d1e2d7b9d4ff2cfe108b69dea8ab49825c643989 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 7 Jun 2018 15:32:19 -0400 Subject: [PATCH 14/16] Fixes #2087: Don't overwrite existing vc_position of master device when creating a virtual chassis --- netbox/dcim/signals.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/netbox/dcim/signals.py b/netbox/dcim/signals.py index 1e8888e97..c29a8a857 100644 --- a/netbox/dcim/signals.py +++ b/netbox/dcim/signals.py @@ -11,8 +11,13 @@ 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=1) + Device.objects.filter(pk=instance.master.pk).update(virtual_chassis=instance, vc_position=vc_position) @receiver(pre_delete, sender=VirtualChassis) From fd13e4a1fad2c9dfa04fa3065314c9cfe7f038ad Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 7 Jun 2018 15:53:05 -0400 Subject: [PATCH 15/16] Release v2.3.4 --- 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 b74cbe7f4..4d507bb46 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -22,7 +22,7 @@ if sys.version_info[0] < 3: DeprecationWarning ) -VERSION = '2.3.4-dev' +VERSION = '2.3.4' BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) From b8df6243b9e0776c5bb162236a0a79c206cab26d Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 7 Jun 2018 16:22:36 -0400 Subject: [PATCH 16/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 4d507bb46..8cb98f268 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -22,7 +22,7 @@ if sys.version_info[0] < 3: DeprecationWarning ) -VERSION = '2.3.4' +VERSION = '2.3.5-dev' BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))