From 8d9543cb6ad60dbf144f9baa1289eb0745407a67 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 fc0e8e2aaebcc5aa5b776a92a623745dffb7f5c0 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 2cdb527df9febf4e48e6550d36f2178c706eb5ba 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 399a633d9d3baa8f3a22998ea018bc5e82db31bc 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 e5af4f6f1702b7744af52b51f70178f9447e934d 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 b0cd372af9002015213b8676514c60adaeeccd7b 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 0d267d97fee25dd9b592f03bf0e73c41f6c737ef 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 131436fc20340da20bb66dc98dee0b22e1bee175 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 32eee0bede61409dd0e8b1bea6d9f4ddeaf0310a 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 1e7fdbc79ab07015e2b097e6a86a9c7b76b22cc2 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 0af6df3121af37e30cd97f7d2a7ca3d79708a5ea 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 643b0eaf65b6bf0531072045103472716066ae1f 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 862e44e96ffb7de9dca2d4183218fb956a1f7787 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 5dd2f370357317866df923fc2e3ed25fcf219a61 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 ff0a0df4780aa607f0d8d66556a5b09e65334b02 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 8bad3aee745b54ab1f89fe185b9f6e520fdc5ee3 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__)))