From 19c984bdab8427324a14031de12f9eb9c5e7e1f3 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 29 Sep 2021 09:46:39 -0400 Subject: [PATCH 01/24] PRVB --- docs/release-notes/version-3.0.md | 6 ++++++ netbox/netbox/settings.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-3.0.md b/docs/release-notes/version-3.0.md index bd3817986..d25c4c441 100644 --- a/docs/release-notes/version-3.0.md +++ b/docs/release-notes/version-3.0.md @@ -1,5 +1,9 @@ # NetBox v3.0 +## v3.0.5 (FUTURE) + +--- + ## v3.0.4 (2021-09-29) ### Enhancements @@ -30,6 +34,8 @@ * [#7374](https://github.com/netbox-community/netbox/issues/7374) - Add missing `face` parameter to API elevations request when editing device * [#7392](https://github.com/netbox-community/netbox/issues/7392) - Fix "help" links for custom fields, other models +--- + ## v3.0.3 (2021-09-20) ### Enhancements diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 15680bea0..2610fa7b1 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -16,7 +16,7 @@ from django.core.validators import URLValidator # Environment setup # -VERSION = '3.0.4' +VERSION = '3.0.5-dev' # Hostname HOSTNAME = platform.node() From 47c3a20fda145cd8cf8aa24cd86ccb565d094c7f Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 29 Sep 2021 12:40:20 -0400 Subject: [PATCH 02/24] Correct version number referenced for installation video --- docs/installation/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation/index.md b/docs/installation/index.md index 893b1f639..bd1074b5e 100644 --- a/docs/installation/index.md +++ b/docs/installation/index.md @@ -11,7 +11,7 @@ The following sections detail how to set up a new instance of NetBox: 5. [HTTP server](5-http-server.md) 6. [LDAP authentication](6-ldap.md) (optional) -The video below demonstrates the installation of NetBox v2.10.3 on Ubuntu 20.04 for your reference. +The video below demonstrates the installation of NetBox v3.0 on Ubuntu 20.04 for your reference. From 14b065cf5f1221dee59f2c2a32a5d2347a2c525e Mon Sep 17 00:00:00 2001 From: thatmattlove Date: Wed, 29 Sep 2021 17:44:28 -0700 Subject: [PATCH 03/24] Fixes #7373: Improve handling of mismatched server, client, and browser color-mode preferences --- docs/release-notes/version-3.0.md | 4 ++ netbox/templates/base/base.html | 89 ++++++++++++++++++++----------- 2 files changed, 61 insertions(+), 32 deletions(-) diff --git a/docs/release-notes/version-3.0.md b/docs/release-notes/version-3.0.md index d25c4c441..140edba09 100644 --- a/docs/release-notes/version-3.0.md +++ b/docs/release-notes/version-3.0.md @@ -2,6 +2,10 @@ ## v3.0.5 (FUTURE) +### Bug Fixes + +* [#7373](https://github.com/netbox-community/netbox/issues/7373) - Fix flashing when server, client, and browser color-mode preferences are mismatched + --- ## v3.0.4 (2021-09-29) diff --git a/netbox/templates/base/base.html b/netbox/templates/base/base.html index 71172e901..78dc2b744 100644 --- a/netbox/templates/base/base.html +++ b/netbox/templates/base/base.html @@ -6,11 +6,15 @@ lang="en" data-netbox-url-name="{{ request.resolver_match.url_name }}" data-netbox-base-path="{{ settings.BASE_PATH }}" - {% if preferences|get_key:'ui.colormode' == 'dark'%} - data-netbox-color-mode="dark" - {% else %} - data-netbox-color-mode="light" - {% endif %} + {% with preferences|get_key:'ui.colormode' as color_mode %} + {% if color_mode == 'dark'%} + data-netbox-color-mode="dark" + {% elif color_mode == 'light' %} + data-netbox-color-mode="light" + {% else %} + data-netbox-color-mode="unset" + {% endif %} + {% endwith %} > @@ -23,34 +27,55 @@ {% block title %}Home{% endblock %} | NetBox {# Static resources #} From 1f1a05dc67852a97648f9a84ab2bd277b6f1a994 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 29 Sep 2021 21:00:45 -0400 Subject: [PATCH 04/24] Fixes #6895: Remove errant markup for null values in CSV export --- docs/release-notes/version-3.0.md | 1 + netbox/dcim/tables/cables.py | 4 ++-- netbox/dcim/tables/devices.py | 8 ++++---- netbox/dcim/tables/template_code.py | 6 +----- netbox/ipam/tables/ip.py | 18 +++++------------- netbox/ipam/tables/vlans.py | 22 ++++++---------------- netbox/ipam/tables/vrfs.py | 10 ++++------ netbox/utilities/tables.py | 19 +++++++++++++++++++ 8 files changed, 42 insertions(+), 46 deletions(-) diff --git a/docs/release-notes/version-3.0.md b/docs/release-notes/version-3.0.md index 140edba09..3d4c4cb25 100644 --- a/docs/release-notes/version-3.0.md +++ b/docs/release-notes/version-3.0.md @@ -4,6 +4,7 @@ ### Bug Fixes +* [#6895](https://github.com/netbox-community/netbox/issues/6895) - Remove errant markup for null values in CSV export * [#7373](https://github.com/netbox-community/netbox/issues/7373) - Fix flashing when server, client, and browser color-mode preferences are mismatched --- diff --git a/netbox/dcim/tables/cables.py b/netbox/dcim/tables/cables.py index 9509ec2bc..14cf34505 100644 --- a/netbox/dcim/tables/cables.py +++ b/netbox/dcim/tables/cables.py @@ -2,7 +2,7 @@ import django_tables2 as tables from django_tables2.utils import Accessor from dcim.models import Cable -from utilities.tables import BaseTable, ChoiceFieldColumn, ColorColumn, TagColumn, ToggleColumn +from utilities.tables import BaseTable, ChoiceFieldColumn, ColorColumn, TagColumn, TemplateColumn, ToggleColumn from .template_code import CABLE_LENGTH, CABLE_TERMINATION_PARENT __all__ = ( @@ -45,7 +45,7 @@ class CableTable(BaseTable): verbose_name='Termination B' ) status = ChoiceFieldColumn() - length = tables.TemplateColumn( + length = TemplateColumn( template_code=CABLE_LENGTH, order_by='_abs_length' ) diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py index 306b29b09..c22e673b7 100644 --- a/netbox/dcim/tables/devices.py +++ b/netbox/dcim/tables/devices.py @@ -9,7 +9,7 @@ from dcim.models import ( from tenancy.tables import TenantColumn from utilities.tables import ( BaseTable, BooleanColumn, ButtonsColumn, ChoiceFieldColumn, ColorColumn, ColoredLabelColumn, LinkedCountColumn, - MarkdownColumn, TagColumn, ToggleColumn, + MarkdownColumn, TagColumn, TemplateColumn, ToggleColumn, ) from .template_code import ( CABLETERMINATION, CONSOLEPORT_BUTTONS, CONSOLESERVERPORT_BUTTONS, DEVICE_LINK, DEVICEBAY_BUTTONS, DEVICEBAY_STATUS, @@ -258,7 +258,7 @@ class CableTerminationTable(BaseTable): orderable=False, verbose_name='Cable Color' ) - cable_peer = tables.TemplateColumn( + cable_peer = TemplateColumn( accessor='_cable_peer', template_code=CABLETERMINATION, orderable=False, @@ -268,7 +268,7 @@ class CableTerminationTable(BaseTable): class PathEndpointTable(CableTerminationTable): - connection = tables.TemplateColumn( + connection = TemplateColumn( accessor='_path.last_node', template_code=CABLETERMINATION, verbose_name='Connection', @@ -470,7 +470,7 @@ class BaseInterfaceTable(BaseTable): verbose_name='IP Addresses' ) untagged_vlan = tables.Column(linkify=True) - tagged_vlans = tables.TemplateColumn( + tagged_vlans = TemplateColumn( template_code=INTERFACE_TAGGED_VLANS, orderable=False, verbose_name='Tagged VLANs' diff --git a/netbox/dcim/tables/template_code.py b/netbox/dcim/tables/template_code.py index 65a30ac61..2f359e1b9 100644 --- a/netbox/dcim/tables/template_code.py +++ b/netbox/dcim/tables/template_code.py @@ -5,13 +5,11 @@ CABLETERMINATION = """ {% endif %} {{ value }} -{% else %} - — {% endif %} """ CABLE_LENGTH = """ -{% if record.length %}{{ record.length }} {{ record.get_length_unit_display }}{% else %}—{% endif %} +{% if record.length %}{{ record.length }} {{ record.get_length_unit_display }}{% endif %} """ CABLE_TERMINATION_PARENT = """ @@ -63,8 +61,6 @@ INTERFACE_TAGGED_VLANS = """ {% endfor %} {% elif record.mode == 'tagged-all' %} All -{% else %} - — {% endif %} """ diff --git a/netbox/ipam/tables/ip.py b/netbox/ipam/tables/ip.py index 57adbb1b8..485e4a123 100644 --- a/netbox/ipam/tables/ip.py +++ b/netbox/ipam/tables/ip.py @@ -39,15 +39,7 @@ PREFIXFLAT_LINK = """ {% if record.pk %} {{ record.prefix }} {% else %} - — -{% endif %} -""" - -PREFIX_ROLE_LINK = """ -{% if record.role %} - {{ record.role }} -{% else %} - — + {{ record.prefix }} {% endif %} """ @@ -218,8 +210,8 @@ class PrefixTable(BaseTable): linkify=True, verbose_name='VLAN' ) - role = tables.TemplateColumn( - template_code=PREFIX_ROLE_LINK + role = tables.Column( + linkify=True ) is_pool = BooleanColumn( verbose_name='Pool' @@ -264,8 +256,8 @@ class IPRangeTable(BaseTable): status = ChoiceFieldColumn( default=AVAILABLE_LABEL ) - role = tables.TemplateColumn( - template_code=PREFIX_ROLE_LINK + role = tables.Column( + linkify=True ) tenant = TenantColumn() diff --git a/netbox/ipam/tables/vlans.py b/netbox/ipam/tables/vlans.py index 4219a8a52..fd1e92be8 100644 --- a/netbox/ipam/tables/vlans.py +++ b/netbox/ipam/tables/vlans.py @@ -6,7 +6,7 @@ from dcim.models import Interface from tenancy.tables import TenantColumn from utilities.tables import ( BaseTable, BooleanColumn, ButtonsColumn, ChoiceFieldColumn, ContentTypeColumn, LinkedCountColumn, TagColumn, - ToggleColumn, + TemplateColumn, ToggleColumn, ) from virtualization.models import VMInterface from ipam.models import * @@ -35,19 +35,9 @@ VLAN_LINK = """ VLAN_PREFIXES = """ {% for prefix in record.prefixes.all %} {{ prefix }}{% if not forloop.last %}
{% endif %} -{% empty %} - — {% endfor %} """ -VLAN_ROLE_LINK = """ -{% if record.role %} - {{ record.role }} -{% else %} - — -{% endif %} -""" - VLANGROUP_ADD_VLAN = """ {% with next_vid=record.get_next_available_vid %} {% if next_vid and perms.ipam.add_vlan %} @@ -115,10 +105,10 @@ class VLANTable(BaseTable): status = ChoiceFieldColumn( default=AVAILABLE_LABEL ) - role = tables.TemplateColumn( - template_code=VLAN_ROLE_LINK + role = tables.Column( + linkify=True ) - prefixes = tables.TemplateColumn( + prefixes = TemplateColumn( template_code=VLAN_PREFIXES, orderable=False, verbose_name='Prefixes' @@ -190,8 +180,8 @@ class InterfaceVLANTable(BaseTable): ) tenant = TenantColumn() status = ChoiceFieldColumn() - role = tables.TemplateColumn( - template_code=VLAN_ROLE_LINK + role = tables.Column( + linkify=True ) class Meta(BaseTable.Meta): diff --git a/netbox/ipam/tables/vrfs.py b/netbox/ipam/tables/vrfs.py index bea2a6b1f..3a351a856 100644 --- a/netbox/ipam/tables/vrfs.py +++ b/netbox/ipam/tables/vrfs.py @@ -1,7 +1,7 @@ import django_tables2 as tables from tenancy.tables import TenantColumn -from utilities.tables import BaseTable, BooleanColumn, TagColumn, ToggleColumn +from utilities.tables import BaseTable, BooleanColumn, TagColumn, TemplateColumn, ToggleColumn from ipam.models import * __all__ = ( @@ -11,9 +11,7 @@ __all__ = ( VRF_TARGETS = """ {% for rt in value.all %} - {{ rt }}{% if not forloop.last %}
{% endif %} -{% empty %} - — + {{ rt }}{% if not forloop.last %}
{% endif %} {% endfor %} """ @@ -34,11 +32,11 @@ class VRFTable(BaseTable): enforce_unique = BooleanColumn( verbose_name='Unique' ) - import_targets = tables.TemplateColumn( + import_targets = TemplateColumn( template_code=VRF_TARGETS, orderable=False ) - export_targets = tables.TemplateColumn( + export_targets = TemplateColumn( template_code=VRF_TARGETS, orderable=False ) diff --git a/netbox/utilities/tables.py b/netbox/utilities/tables.py index 0a8e8c3ba..446438b5c 100644 --- a/netbox/utilities/tables.py +++ b/netbox/utilities/tables.py @@ -157,6 +157,25 @@ class BooleanColumn(tables.Column): return str(value) +class TemplateColumn(tables.TemplateColumn): + """ + Overrides the stock TemplateColumn to render a placeholder if the returned value is an empty string. + """ + PLACEHOLDER = mark_safe('—') + + def render(self, *args, **kwargs): + ret = super().render(*args, **kwargs) + if not ret.strip(): + return self.PLACEHOLDER + return ret + + def value(self, **kwargs): + ret = super().value(**kwargs) + if ret == self.PLACEHOLDER: + return '' + return ret + + class ButtonsColumn(tables.TemplateColumn): """ Render edit, delete, and changelog buttons for an object. From a9761e8dd21883f43e7b109c7eb845ce4f47edf9 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 29 Sep 2021 21:09:12 -0400 Subject: [PATCH 05/24] Fixes #7397: Fix AttributeError exception when rendering export template for devices via REST API --- docs/release-notes/version-3.0.md | 1 + netbox/netbox/api/views.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-3.0.md b/docs/release-notes/version-3.0.md index 3d4c4cb25..346923937 100644 --- a/docs/release-notes/version-3.0.md +++ b/docs/release-notes/version-3.0.md @@ -6,6 +6,7 @@ * [#6895](https://github.com/netbox-community/netbox/issues/6895) - Remove errant markup for null values in CSV export * [#7373](https://github.com/netbox-community/netbox/issues/7373) - Fix flashing when server, client, and browser color-mode preferences are mismatched +* [#7397](https://github.com/netbox-community/netbox/issues/7397) - Fix AttributeError exception when rendering export template for devices via REST API --- diff --git a/netbox/netbox/api/views.py b/netbox/netbox/api/views.py index 46aa429bd..74000e978 100644 --- a/netbox/netbox/api/views.py +++ b/netbox/netbox/api/views.py @@ -230,7 +230,7 @@ class ModelViewSet(BulkUpdateModelMixin, BulkDestroyModelMixin, ModelViewSet_): Overrides ListModelMixin to allow processing ExportTemplates. """ if 'export' in request.GET: - content_type = ContentType.objects.get_for_model(self.serializer_class.Meta.model) + content_type = ContentType.objects.get_for_model(self.get_serializer_class().Meta.model) et = get_object_or_404(ExportTemplate, content_type=content_type, name=request.GET['export']) queryset = self.filter_queryset(self.get_queryset()) return et.render_to_response(queryset) From 0fdd0818695e2673c1c81a0cd5911956b68f244a Mon Sep 17 00:00:00 2001 From: maximumG Date: Thu, 30 Sep 2021 09:17:33 +0200 Subject: [PATCH 06/24] feat: scripts within a module can now be ordered --- netbox/extras/scripts.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/netbox/extras/scripts.py b/netbox/extras/scripts.py index 156b88065..6ccf7f48a 100644 --- a/netbox/extras/scripts.py +++ b/netbox/extras/scripts.py @@ -470,7 +470,6 @@ def get_scripts(use_names=False): defined name in place of the actual module name. """ scripts = OrderedDict() - # Iterate through all modules within the reports path. These are the user-created files in which reports are # defined. for importer, module_name, _ in pkgutil.iter_modules([settings.SCRIPTS_ROOT]): @@ -478,11 +477,14 @@ def get_scripts(use_names=False): if use_names and hasattr(module, 'name'): module_name = module.name module_scripts = OrderedDict() - for name, cls in inspect.getmembers(module, is_script): - module_scripts[name] = cls + script_order = getattr(module, "script_order", ()) + ordered_scripts = [cls for cls in script_order if is_script(cls)] + unordered_scripts = [cls for _, cls in inspect.getmembers(module, is_script) if cls not in script_order] + for cls in [*ordered_scripts, *unordered_scripts]: + module_scripts[cls.__name__] = cls if module_scripts: scripts[module_name] = module_scripts - + return scripts From 7337630704b195ac3771ebee1280d67061323c70 Mon Sep 17 00:00:00 2001 From: maximumG Date: Thu, 30 Sep 2021 09:21:38 +0200 Subject: [PATCH 07/24] chore: introduce the script_order notion in the documentation --- docs/customization/custom-scripts.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/customization/custom-scripts.md b/docs/customization/custom-scripts.md index 252e65f90..cf052f918 100644 --- a/docs/customization/custom-scripts.md +++ b/docs/customization/custom-scripts.md @@ -45,6 +45,20 @@ Defining script variables is optional: You may create a script with only a `run( Any output generated by the script during its execution will be displayed under the "output" tab in the UI. +By default, scripts within a module are ordered alphabetically in the scripts list page. To return scripts in a specific order, you can define the `script_order` variable at the end of your module. The `script_order` variable is a tuple which contains each Script class in the desired order. Any scripts that are omitted from this list will be listed last. + +```python +from extras.scripts import Script + +class MyCustomScript(Script): + ... + +class AnotherCustomScript(Script): + ... + +script_order = (MyCustomScript, AnotherCustomScript) +``` + ## Module Attributes ### `name` From d9f178e315df01a0b120be7d05446e8d5bcbc604 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Thu, 30 Sep 2021 11:36:16 -0400 Subject: [PATCH 08/24] Fixes #7411: Fix exception in UI when adding member devices to virtual chassis --- docs/release-notes/version-3.0.md | 1 + netbox/dcim/forms/models.py | 1 + .../dcim/virtualchassis_add_member.html | 26 +++++++------------ 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/docs/release-notes/version-3.0.md b/docs/release-notes/version-3.0.md index 346923937..ac3ae018f 100644 --- a/docs/release-notes/version-3.0.md +++ b/docs/release-notes/version-3.0.md @@ -7,6 +7,7 @@ * [#6895](https://github.com/netbox-community/netbox/issues/6895) - Remove errant markup for null values in CSV export * [#7373](https://github.com/netbox-community/netbox/issues/7373) - Fix flashing when server, client, and browser color-mode preferences are mismatched * [#7397](https://github.com/netbox-community/netbox/issues/7397) - Fix AttributeError exception when rendering export template for devices via REST API +* [#7411](https://github.com/netbox-community/netbox/issues/7411) - Fix exception in UI when adding member devices to virtual chassis --- diff --git a/netbox/dcim/forms/models.py b/netbox/dcim/forms/models.py index 8331cbb10..90023f0fe 100644 --- a/netbox/dcim/forms/models.py +++ b/netbox/dcim/forms/models.py @@ -52,6 +52,7 @@ __all__ = ( 'RegionForm', 'SiteForm', 'SiteGroupForm', + 'VCMemberSelectForm', 'VirtualChassisForm', ) diff --git a/netbox/templates/dcim/virtualchassis_add_member.html b/netbox/templates/dcim/virtualchassis_add_member.html index e8c0dac59..17ffd64d9 100644 --- a/netbox/templates/dcim/virtualchassis_add_member.html +++ b/netbox/templates/dcim/virtualchassis_add_member.html @@ -4,25 +4,19 @@ {% block title %}Add New Member to Virtual Chassis {{ virtual_chassis }}{% endblock %} {% block content %} -
+ {% csrf_token %} -
-
-
-
Add New Member
-
- {% render_form member_select_form %} - {% render_form membership_form %} -
-
+
+
Add New Member
+
+ {% render_form member_select_form %} + {% render_form membership_form %}
-
-
- Cancel - - -
+
+ Cancel + +
{% endblock %} From 114500e7f487d964ba8b6efec31379524b32feb1 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Thu, 30 Sep 2021 11:41:32 -0400 Subject: [PATCH 09/24] Fixes #7401: Pin jsonschema package to v3.2.0 to fix REST API docs rendering --- docs/release-notes/version-3.0.md | 1 + requirements.txt | 3 +++ 2 files changed, 4 insertions(+) diff --git a/docs/release-notes/version-3.0.md b/docs/release-notes/version-3.0.md index ac3ae018f..b1e1522fe 100644 --- a/docs/release-notes/version-3.0.md +++ b/docs/release-notes/version-3.0.md @@ -7,6 +7,7 @@ * [#6895](https://github.com/netbox-community/netbox/issues/6895) - Remove errant markup for null values in CSV export * [#7373](https://github.com/netbox-community/netbox/issues/7373) - Fix flashing when server, client, and browser color-mode preferences are mismatched * [#7397](https://github.com/netbox-community/netbox/issues/7397) - Fix AttributeError exception when rendering export template for devices via REST API +* [#7401](https://github.com/netbox-community/netbox/issues/7401) - Pin `jsonschema` package to v3.2.0 to fix REST API docs rendering * [#7411](https://github.com/netbox-community/netbox/issues/7411) - Fix exception in UI when adding member devices to virtual chassis --- diff --git a/requirements.txt b/requirements.txt index 5b9c10e87..0e925ff0b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -25,3 +25,6 @@ psycopg2-binary==2.9.1 PyYAML==5.4.1 svgwrite==1.4.1 tablib==3.0.0 + +# Workaround for #7401 +jsonschema==3.2.0 From a5b99e7148eada4036d36054ec6b2b7d826c2283 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Thu, 30 Sep 2021 12:29:08 -0400 Subject: [PATCH 10/24] Fixes #7412: Fix exception in UI when adding child device to device bay --- docs/release-notes/version-3.0.md | 1 + netbox/dcim/forms/models.py | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/release-notes/version-3.0.md b/docs/release-notes/version-3.0.md index b1e1522fe..0ad55a343 100644 --- a/docs/release-notes/version-3.0.md +++ b/docs/release-notes/version-3.0.md @@ -9,6 +9,7 @@ * [#7397](https://github.com/netbox-community/netbox/issues/7397) - Fix AttributeError exception when rendering export template for devices via REST API * [#7401](https://github.com/netbox-community/netbox/issues/7401) - Pin `jsonschema` package to v3.2.0 to fix REST API docs rendering * [#7411](https://github.com/netbox-community/netbox/issues/7411) - Fix exception in UI when adding member devices to virtual chassis +* [#7412](https://github.com/netbox-community/netbox/issues/7412) - Fix exception in UI when adding child device to device bay --- diff --git a/netbox/dcim/forms/models.py b/netbox/dcim/forms/models.py index 90023f0fe..009e1fe3f 100644 --- a/netbox/dcim/forms/models.py +++ b/netbox/dcim/forms/models.py @@ -38,6 +38,7 @@ __all__ = ( 'LocationForm', 'ManufacturerForm', 'PlatformForm', + 'PopulateDeviceBayForm', 'PowerFeedForm', 'PowerOutletForm', 'PowerOutletTemplateForm', From d1f5988db7ce3e24922f4b60e4eeb086459b8f3e Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Fri, 1 Oct 2021 08:57:17 -0400 Subject: [PATCH 11/24] Closes #7319: Remove migrations check from upgrade.sh to avoid misleading error messages --- upgrade.sh | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/upgrade.sh b/upgrade.sh index 6ac8d6122..67b8aaa89 100755 --- a/upgrade.sh +++ b/upgrade.sh @@ -61,22 +61,6 @@ else echo "Skipping local dependencies (local_requirements.txt not found)" fi -# Test schema migrations integrity -COMMAND="python3 netbox/manage.py showmigrations" -eval $COMMAND > /dev/null 2>&1 || { - echo "--------------------------------------------------------------------" - echo "ERROR: Database schema migrations are out of synchronization. (No" - echo "data has been lost.) If attempting to upgrade to NetBox v3.0 or" - echo "later, first upgrade to a v2.11 release to ensure schema migrations" - echo "have been correctly prepared. For further detail on the exact error," - echo "run the following commands:" - echo "" - echo " source ${VIRTUALENV}/bin/activate" - echo " ${COMMAND}" - echo "--------------------------------------------------------------------" - exit 1 -} - # Apply any database migrations COMMAND="python3 netbox/manage.py migrate" echo "Applying database migrations ($COMMAND)..." From ed3bc7cdcc76b1aea3b34954b4ee6c8fe54ea6d6 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Fri, 1 Oct 2021 09:30:17 -0400 Subject: [PATCH 12/24] Changelog for #7387 --- docs/release-notes/version-3.0.md | 4 ++++ netbox/extras/scripts.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-3.0.md b/docs/release-notes/version-3.0.md index 0ad55a343..16d3b8d18 100644 --- a/docs/release-notes/version-3.0.md +++ b/docs/release-notes/version-3.0.md @@ -2,6 +2,10 @@ ## v3.0.5 (FUTURE) +### Enhancements + +* [#7387](https://github.com/netbox-community/netbox/issues/7387) - Enable arbitrary ordering of custom scripts + ### Bug Fixes * [#6895](https://github.com/netbox-community/netbox/issues/6895) - Remove errant markup for null values in CSV export diff --git a/netbox/extras/scripts.py b/netbox/extras/scripts.py index 6ccf7f48a..fd84747b9 100644 --- a/netbox/extras/scripts.py +++ b/netbox/extras/scripts.py @@ -484,7 +484,7 @@ def get_scripts(use_names=False): module_scripts[cls.__name__] = cls if module_scripts: scripts[module_name] = module_scripts - + return scripts From 257c0afdb5b62c7ea84159d2b46d53ce5a43f3a3 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Fri, 1 Oct 2021 12:02:04 -0400 Subject: [PATCH 13/24] Closes #6423: Cache rendered REST API specifications --- docs/release-notes/version-3.0.md | 1 + netbox/netbox/urls.py | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/release-notes/version-3.0.md b/docs/release-notes/version-3.0.md index 16d3b8d18..277a37692 100644 --- a/docs/release-notes/version-3.0.md +++ b/docs/release-notes/version-3.0.md @@ -4,6 +4,7 @@ ### Enhancements +* [#6423](https://github.com/netbox-community/netbox/issues/6423) - Cache rendered REST API specifications * [#7387](https://github.com/netbox-community/netbox/issues/7387) - Enable arbitrary ordering of custom scripts ### Bug Fixes diff --git a/netbox/netbox/urls.py b/netbox/netbox/urls.py index 53e20351c..3d4c60c93 100644 --- a/netbox/netbox/urls.py +++ b/netbox/netbox/urls.py @@ -17,7 +17,7 @@ from .admin import admin_site openapi_info = openapi.Info( title="NetBox API", - default_version='v2', + default_version='v3', description="API to access NetBox", terms_of_service="https://github.com/netbox-community/netbox", license=openapi.License(name="Apache v2 License"), @@ -59,9 +59,9 @@ _patterns = [ path('api/users/', include('users.api.urls')), path('api/virtualization/', include('virtualization.api.urls')), path('api/status/', StatusView.as_view(), name='api-status'), - path('api/docs/', schema_view.with_ui('swagger'), name='api_docs'), - path('api/redoc/', schema_view.with_ui('redoc'), name='api_redocs'), - re_path(r'^api/swagger(?P.json|.yaml)$', schema_view.without_ui(), name='schema_swagger'), + path('api/docs/', schema_view.with_ui('swagger', cache_timeout=86400), name='api_docs'), + path('api/redoc/', schema_view.with_ui('redoc', cache_timeout=86400), name='api_redocs'), + re_path(r'^api/swagger(?P.json|.yaml)$', schema_view.without_ui(cache_timeout=86400), name='schema_swagger'), # GraphQL path('graphql/', csrf_exempt(GraphQLView.as_view(graphiql=True, schema=schema)), name='graphql'), From 460e3fd5d61ad760a05faf6427dc90a11f96f641 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Fri, 1 Oct 2021 12:34:30 -0400 Subject: [PATCH 14/24] Introduce a common URL for the creation of image attachments --- netbox/dcim/urls.py | 6 +----- netbox/extras/urls.py | 1 + netbox/extras/views.py | 22 +++++++++++++--------- netbox/templates/dcim/device.html | 2 +- netbox/templates/dcim/location.html | 2 +- netbox/templates/dcim/rack.html | 2 +- netbox/templates/dcim/site.html | 2 +- 7 files changed, 19 insertions(+), 18 deletions(-) diff --git a/netbox/dcim/urls.py b/netbox/dcim/urls.py index 11ffd4458..01e470e5c 100644 --- a/netbox/dcim/urls.py +++ b/netbox/dcim/urls.py @@ -1,6 +1,6 @@ from django.urls import path -from extras.views import ImageAttachmentEditView, ObjectChangeLogView, ObjectJournalView +from extras.views import ObjectChangeLogView, ObjectJournalView from ipam.views import ServiceEditView from utilities.views import SlugRedirectView from . import views @@ -43,7 +43,6 @@ urlpatterns = [ path('sites//delete/', views.SiteDeleteView.as_view(), name='site_delete'), path('sites//changelog/', ObjectChangeLogView.as_view(), name='site_changelog', kwargs={'model': Site}), path('sites//journal/', ObjectJournalView.as_view(), name='site_journal', kwargs={'model': Site}), - path('sites//images/add/', ImageAttachmentEditView.as_view(), name='site_add_image', kwargs={'model': Site}), # Locations path('locations/', views.LocationListView.as_view(), name='location_list'), @@ -55,7 +54,6 @@ urlpatterns = [ path('locations//edit/', views.LocationEditView.as_view(), name='location_edit'), path('locations//delete/', views.LocationDeleteView.as_view(), name='location_delete'), path('locations//changelog/', ObjectChangeLogView.as_view(), name='location_changelog', kwargs={'model': Location}), - path('locations//images/add/', ImageAttachmentEditView.as_view(), name='location_add_image', kwargs={'model': Location}), # Rack roles path('rack-roles/', views.RackRoleListView.as_view(), name='rackrole_list'), @@ -92,7 +90,6 @@ urlpatterns = [ path('racks//delete/', views.RackDeleteView.as_view(), name='rack_delete'), path('racks//changelog/', ObjectChangeLogView.as_view(), name='rack_changelog', kwargs={'model': Rack}), path('racks//journal/', ObjectJournalView.as_view(), name='rack_journal', kwargs={'model': Rack}), - path('racks//images/add/', ImageAttachmentEditView.as_view(), name='rack_add_image', kwargs={'model': Rack}), # Manufacturers path('manufacturers/', views.ManufacturerListView.as_view(), name='manufacturer_list'), @@ -229,7 +226,6 @@ urlpatterns = [ path('devices//lldp-neighbors/', views.DeviceLLDPNeighborsView.as_view(), name='device_lldp_neighbors'), path('devices//config/', views.DeviceConfigView.as_view(), name='device_config'), path('devices//services/assign/', ServiceEditView.as_view(), name='device_service_assign'), - path('devices//images/add/', ImageAttachmentEditView.as_view(), name='device_add_image', kwargs={'model': Device}), # Console ports path('console-ports/', views.ConsolePortListView.as_view(), name='consoleport_list'), diff --git a/netbox/extras/urls.py b/netbox/extras/urls.py index 4dafb25f2..de8ef1531 100644 --- a/netbox/extras/urls.py +++ b/netbox/extras/urls.py @@ -78,6 +78,7 @@ urlpatterns = [ kwargs={'model': models.ConfigContext}), # Image attachments + path('image-attachments/add/', views.ImageAttachmentEditView.as_view(), name='imageattachment_add'), path('image-attachments//edit/', views.ImageAttachmentEditView.as_view(), name='imageattachment_edit'), path('image-attachments//delete/', views.ImageAttachmentDeleteView.as_view(), name='imageattachment_delete'), diff --git a/netbox/extras/views.py b/netbox/extras/views.py index b23dc0230..d39f50c79 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -472,22 +472,26 @@ class ImageAttachmentEditView(generic.ObjectEditView): queryset = ImageAttachment.objects.all() model_form = forms.ImageAttachmentForm - def alter_obj(self, imageattachment, request, args, kwargs): - if not imageattachment.pk: + def alter_obj(self, instance, request, args, kwargs): + if not instance.pk: # Assign the parent object based on URL kwargs - model = kwargs.get('model') - imageattachment.parent = get_object_or_404(model, pk=kwargs['object_id']) - return imageattachment + try: + app_label, model = request.GET.get('content_type').split('.') + except (AttributeError, ValueError): + raise Http404("Content type not specified") + content_type = get_object_or_404(ContentType, app_label=app_label, model=model) + instance.parent = get_object_or_404(content_type.model_class(), pk=request.GET.get('object_id')) + return instance - def get_return_url(self, request, imageattachment): - return imageattachment.parent.get_absolute_url() + def get_return_url(self, request, obj=None): + return obj.parent.get_absolute_url() if obj else super().get_return_url(request) class ImageAttachmentDeleteView(generic.ObjectDeleteView): queryset = ImageAttachment.objects.all() - def get_return_url(self, request, imageattachment): - return imageattachment.parent.get_absolute_url() + def get_return_url(self, request, obj=None): + return obj.parent.get_absolute_url() if obj else super().get_return_url(request) # diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html index aa3f834d6..7c2359db5 100644 --- a/netbox/templates/dcim/device.html +++ b/netbox/templates/dcim/device.html @@ -299,7 +299,7 @@
{% if perms.extras.add_imageattachment %} {% if perms.extras.add_imageattachment %} {% if perms.extras.add_imageattachment %} {% if perms.extras.add_imageattachment %} {% endif %}
-
-
- Images -
-
- {% include 'inc/image_attachments.html' with images=object.images.all %} -
- {% if perms.extras.add_imageattachment %} - - {% endif %} -
+ {% include 'inc/image_attachments_panel.html' %}
Related Devices diff --git a/netbox/templates/dcim/location.html b/netbox/templates/dcim/location.html index d0e981e5a..b062ddcb5 100644 --- a/netbox/templates/dcim/location.html +++ b/netbox/templates/dcim/location.html @@ -59,22 +59,7 @@
{% include 'inc/custom_fields_panel.html' %} -
-
- Images -
-
- {% include 'inc/image_attachments.html' with images=object.images.all %} -
- {% if perms.extras.add_imageattachment %} - - {% endif %} -
+ {% include 'inc/image_attachments_panel.html' %} {% plugin_right_page object %}
diff --git a/netbox/templates/dcim/rack.html b/netbox/templates/dcim/rack.html index 87c20999f..bf9a11819 100644 --- a/netbox/templates/dcim/rack.html +++ b/netbox/templates/dcim/rack.html @@ -210,22 +210,7 @@ {% endif %} -
-
- Images -
-
- {% include 'inc/image_attachments.html' with images=object.images.all %} -
- {% if perms.extras.add_imageattachment %} - - {% endif %} -
+ {% include 'inc/image_attachments_panel.html' %}
Reservations diff --git a/netbox/templates/dcim/site.html b/netbox/templates/dcim/site.html index 36ed9bff4..1ee8cfce0 100644 --- a/netbox/templates/dcim/site.html +++ b/netbox/templates/dcim/site.html @@ -242,22 +242,7 @@ {% endif %}
-
-
- Images -
-
- {% include 'inc/image_attachments.html' with images=object.images.all %} -
- {% if perms.extras.add_imageattachment %} - - {% endif %} -
+ {% include 'inc/image_attachments_panel.html' %} {% plugin_right_page object %} diff --git a/netbox/templates/inc/image_attachments.html b/netbox/templates/inc/image_attachments.html deleted file mode 100644 index 5ec47af7e..000000000 --- a/netbox/templates/inc/image_attachments.html +++ /dev/null @@ -1,38 +0,0 @@ -{% load helpers %} - -{% if images %} - - - - - - - - {% for attachment in images %} - - - - - - - {% endfor %} -
NameSizeCreated
- - {{ attachment }} - {{ attachment.size|filesizeformat }}{{ attachment.created|annotated_date }} - {% if perms.extras.change_imageattachment %} - - - - {% endif %} - {% if perms.extras.delete_imageattachment %} - - - - {% endif %} -
-{% else %} -
- None -
-{% endif %} diff --git a/netbox/templates/inc/image_attachments_panel.html b/netbox/templates/inc/image_attachments_panel.html new file mode 100644 index 000000000..ca7312901 --- /dev/null +++ b/netbox/templates/inc/image_attachments_panel.html @@ -0,0 +1,52 @@ +{% load helpers %} + +
+
+ Images +
+
+ {% with images=object.images.all %} + {% if images.exists %} + + + + + + + + {% for attachment in images %} + + + + + + + {% endfor %} +
NameSizeCreated
+ + {{ attachment }} + {{ attachment.size|filesizeformat }}{{ attachment.created|annotated_date }} + {% if perms.extras.change_imageattachment %} + + + + {% endif %} + {% if perms.extras.delete_imageattachment %} + + + + {% endif %} +
+ {% else %} +
None
+ {% endif %} + {% endwith %} +
+ {% if perms.extras.add_imageattachment %} + + {% endif %} +
From f3fe3f9a1800e3416332f16f391d285732624528 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Fri, 1 Oct 2021 12:50:51 -0400 Subject: [PATCH 16/24] Closes #6708: Add image attachment support for circuits, power panels --- docs/release-notes/version-3.0.md | 1 + netbox/circuits/models.py | 4 ++++ netbox/dcim/models/power.py | 4 ++++ netbox/templates/circuits/circuit.html | 1 + netbox/templates/dcim/powerpanel.html | 3 ++- 5 files changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-3.0.md b/docs/release-notes/version-3.0.md index 277a37692..5f479b126 100644 --- a/docs/release-notes/version-3.0.md +++ b/docs/release-notes/version-3.0.md @@ -5,6 +5,7 @@ ### Enhancements * [#6423](https://github.com/netbox-community/netbox/issues/6423) - Cache rendered REST API specifications +* [#6708](https://github.com/netbox-community/netbox/issues/6708) - Add image attachment support for circuits, power panels * [#7387](https://github.com/netbox-community/netbox/issues/7387) - Enable arbitrary ordering of custom scripts ### Bug Fixes diff --git a/netbox/circuits/models.py b/netbox/circuits/models.py index 39f38d0b0..bc7dcc219 100644 --- a/netbox/circuits/models.py +++ b/netbox/circuits/models.py @@ -1,3 +1,4 @@ +from django.contrib.contenttypes.fields import GenericRelation from django.core.exceptions import ValidationError from django.db import models from django.urls import reverse @@ -202,6 +203,9 @@ class Circuit(PrimaryModel): comments = models.TextField( blank=True ) + images = GenericRelation( + to='extras.ImageAttachment' + ) # Cache associated CircuitTerminations termination_a = models.ForeignKey( diff --git a/netbox/dcim/models/power.py b/netbox/dcim/models/power.py index f81abd328..0e9520b36 100644 --- a/netbox/dcim/models/power.py +++ b/netbox/dcim/models/power.py @@ -1,3 +1,4 @@ +from django.contrib.contenttypes.fields import GenericRelation from django.core.exceptions import ValidationError from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models @@ -39,6 +40,9 @@ class PowerPanel(PrimaryModel): name = models.CharField( max_length=100 ) + images = GenericRelation( + to='extras.ImageAttachment' + ) objects = RestrictedQuerySet.as_manager() diff --git a/netbox/templates/circuits/circuit.html b/netbox/templates/circuits/circuit.html index e68465c82..b863a8a0e 100644 --- a/netbox/templates/circuits/circuit.html +++ b/netbox/templates/circuits/circuit.html @@ -72,6 +72,7 @@
{% include 'circuits/inc/circuit_termination.html' with termination=object.termination_a side='A' %} {% include 'circuits/inc/circuit_termination.html' with termination=object.termination_z side='Z' %} + {% include 'inc/image_attachments_panel.html' %} {% plugin_right_page object %}
diff --git a/netbox/templates/dcim/powerpanel.html b/netbox/templates/dcim/powerpanel.html index c0bfbcf25..b1367aa1e 100644 --- a/netbox/templates/dcim/powerpanel.html +++ b/netbox/templates/dcim/powerpanel.html @@ -39,11 +39,12 @@ + {% include 'extras/inc/tags_panel.html' with tags=object.tags.all url='dcim:powerpanel_list' %} {% plugin_left_page object %}
{% include 'inc/custom_fields_panel.html' %} - {% include 'extras/inc/tags_panel.html' with tags=object.tags.all url='dcim:powerpanel_list' %} + {% include 'inc/image_attachments_panel.html' %} {% plugin_right_page object %}
From 724997cb48b2f444951e99dc0af96e3bdeab4371 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Fri, 1 Oct 2021 13:39:29 -0400 Subject: [PATCH 17/24] Closes #5925: Always show IP addresses tab under prefix view --- docs/release-notes/version-3.0.md | 1 + netbox/templates/ipam/prefix/base.html | 12 +++++------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/release-notes/version-3.0.md b/docs/release-notes/version-3.0.md index 5f479b126..16b3c6482 100644 --- a/docs/release-notes/version-3.0.md +++ b/docs/release-notes/version-3.0.md @@ -4,6 +4,7 @@ ### Enhancements +* [#5925](https://github.com/netbox-community/netbox/issues/5925) - Always show IP addresses tab under prefix view * [#6423](https://github.com/netbox-community/netbox/issues/6423) - Cache rendered REST API specifications * [#6708](https://github.com/netbox-community/netbox/issues/6708) - Add image attachment support for circuits, power panels * [#7387](https://github.com/netbox-community/netbox/issues/7387) - Enable arbitrary ordering of custom scripts diff --git a/netbox/templates/ipam/prefix/base.html b/netbox/templates/ipam/prefix/base.html index 35f36e1ec..7d13eb1aa 100644 --- a/netbox/templates/ipam/prefix/base.html +++ b/netbox/templates/ipam/prefix/base.html @@ -25,11 +25,9 @@ Child Ranges {% badge object.get_child_ranges.count %} - {% if perms.ipam.view_ipaddress and object.status != 'container' %} - - {% endif %} + {% endblock %} From a1f271d7d9cb1728a5f97379c3af10cae6f3b633 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Fri, 1 Oct 2021 14:07:26 -0400 Subject: [PATCH 18/24] Fixes #7417: Prevent exception when filtering objects list by invalid tag --- docs/release-notes/version-3.0.md | 1 + netbox/utilities/templatetags/helpers.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/docs/release-notes/version-3.0.md b/docs/release-notes/version-3.0.md index 16b3c6482..b4d6bb4ac 100644 --- a/docs/release-notes/version-3.0.md +++ b/docs/release-notes/version-3.0.md @@ -17,6 +17,7 @@ * [#7401](https://github.com/netbox-community/netbox/issues/7401) - Pin `jsonschema` package to v3.2.0 to fix REST API docs rendering * [#7411](https://github.com/netbox-community/netbox/issues/7411) - Fix exception in UI when adding member devices to virtual chassis * [#7412](https://github.com/netbox-community/netbox/issues/7412) - Fix exception in UI when adding child device to device bay +* [#7417](https://github.com/netbox-community/netbox/issues/7417) - Prevent exception when filtering objects list by invalid tag --- diff --git a/netbox/utilities/templatetags/helpers.py b/netbox/utilities/templatetags/helpers.py index 532eea19b..a900d59e2 100644 --- a/netbox/utilities/templatetags/helpers.py +++ b/netbox/utilities/templatetags/helpers.py @@ -398,6 +398,9 @@ def applied_filters(form, query_params): applied_filters = [] for filter_name in form.changed_data: + if filter_name not in form.cleaned_data: + continue + querydict = query_params.copy() if filter_name not in querydict: continue From 376c776520795d64fa4df450f00d233a89f843d2 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Fri, 1 Oct 2021 15:29:22 -0400 Subject: [PATCH 19/24] Fixes #7425: Housekeeping command should honor zero verbosity --- docs/release-notes/version-3.0.md | 1 + .../management/commands/housekeeping.py | 62 ++++++++++++------- 2 files changed, 40 insertions(+), 23 deletions(-) diff --git a/docs/release-notes/version-3.0.md b/docs/release-notes/version-3.0.md index b4d6bb4ac..7e0198260 100644 --- a/docs/release-notes/version-3.0.md +++ b/docs/release-notes/version-3.0.md @@ -18,6 +18,7 @@ * [#7411](https://github.com/netbox-community/netbox/issues/7411) - Fix exception in UI when adding member devices to virtual chassis * [#7412](https://github.com/netbox-community/netbox/issues/7412) - Fix exception in UI when adding child device to device bay * [#7417](https://github.com/netbox-community/netbox/issues/7417) - Prevent exception when filtering objects list by invalid tag +* [#7425](https://github.com/netbox-community/netbox/issues/7425) - Housekeeping command should honor zero verbosity --- diff --git a/netbox/extras/management/commands/housekeeping.py b/netbox/extras/management/commands/housekeeping.py index 4dbfa6725..a4d617c9a 100644 --- a/netbox/extras/management/commands/housekeeping.py +++ b/netbox/extras/management/commands/housekeeping.py @@ -18,48 +18,60 @@ class Command(BaseCommand): def handle(self, *args, **options): # Clear expired authentication sessions (essentially replicating the `clearsessions` command) - self.stdout.write("[*] Clearing expired authentication sessions") - if options['verbosity'] >= 2: - self.stdout.write(f"\tConfigured session engine: {settings.SESSION_ENGINE}") + if options['verbosity']: + self.stdout.write("[*] Clearing expired authentication sessions") + if options['verbosity'] >= 2: + self.stdout.write(f"\tConfigured session engine: {settings.SESSION_ENGINE}") engine = import_module(settings.SESSION_ENGINE) try: engine.SessionStore.clear_expired() - self.stdout.write("\tSessions cleared.", self.style.SUCCESS) + if options['verbosity']: + self.stdout.write("\tSessions cleared.", self.style.SUCCESS) except NotImplementedError: - self.stdout.write( - f"\tThe configured session engine ({settings.SESSION_ENGINE}) does not support " - f"clearing sessions; skipping." - ) + if options['verbosity']: + self.stdout.write( + f"\tThe configured session engine ({settings.SESSION_ENGINE}) does not support " + f"clearing sessions; skipping." + ) # Delete expired ObjectRecords - self.stdout.write("[*] Checking for expired changelog records") + if options['verbosity']: + self.stdout.write("[*] Checking for expired changelog records") if settings.CHANGELOG_RETENTION: cutoff = timezone.now() - timedelta(days=settings.CHANGELOG_RETENTION) if options['verbosity'] >= 2: - self.stdout.write(f"Retention period: {settings.CHANGELOG_RETENTION} days") + self.stdout.write(f"\tRetention period: {settings.CHANGELOG_RETENTION} days") self.stdout.write(f"\tCut-off time: {cutoff}") expired_records = ObjectChange.objects.filter(time__lt=cutoff).count() if expired_records: - self.stdout.write(f"\tDeleting {expired_records} expired records... ", self.style.WARNING, ending="") - self.stdout.flush() + if options['verbosity']: + self.stdout.write( + f"\tDeleting {expired_records} expired records... ", + self.style.WARNING, + ending="" + ) + self.stdout.flush() ObjectChange.objects.filter(time__lt=cutoff)._raw_delete(using=DEFAULT_DB_ALIAS) - self.stdout.write("Done.", self.style.WARNING) - else: - self.stdout.write("\tNo expired records found.") - else: + if options['verbosity']: + self.stdout.write("Done.", self.style.SUCCESS) + elif options['verbosity']: + self.stdout.write("\tNo expired records found.", self.style.SUCCESS) + elif options['verbosity']: self.stdout.write( f"\tSkipping: No retention period specified (CHANGELOG_RETENTION = {settings.CHANGELOG_RETENTION})" ) # Check for new releases (if enabled) - self.stdout.write("[*] Checking for latest release") + if options['verbosity']: + self.stdout.write("[*] Checking for latest release") if settings.RELEASE_CHECK_URL: headers = { 'Accept': 'application/vnd.github.v3+json', } try: - self.stdout.write(f"\tFetching {settings.RELEASE_CHECK_URL}") + if options['verbosity'] >= 2: + self.stdout.write(f"\tFetching {settings.RELEASE_CHECK_URL}") response = requests.get( url=settings.RELEASE_CHECK_URL, headers=headers, @@ -73,15 +85,19 @@ class Command(BaseCommand): continue releases.append((version.parse(release['tag_name']), release.get('html_url'))) latest_release = max(releases) - self.stdout.write(f"\tFound {len(response.json())} releases; {len(releases)} usable") - self.stdout.write(f"\tLatest release: {latest_release[0]}") + if options['verbosity'] >= 2: + self.stdout.write(f"\tFound {len(response.json())} releases; {len(releases)} usable") + if options['verbosity']: + self.stdout.write(f"\tLatest release: {latest_release[0]}", self.style.SUCCESS) # Cache the most recent release cache.set('latest_release', latest_release, None) except requests.exceptions.RequestException as exc: - self.stdout.write(f"\tRequest error: {exc}") + self.stdout.write(f"\tRequest error: {exc}", self.style.ERROR) else: - self.stdout.write(f"\tSkipping: RELEASE_CHECK_URL not set") + if options['verbosity']: + self.stdout.write(f"\tSkipping: RELEASE_CHECK_URL not set") - self.stdout.write("Finished.", self.style.SUCCESS) + if options['verbosity']: + self.stdout.write("Finished.", self.style.SUCCESS) From 1be748b479f3dc669f09c6cf9f8122c048c6f326 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Fri, 1 Oct 2021 16:21:16 -0400 Subject: [PATCH 20/24] Fixes #6433: Fix bulk editing of child prefixes under aggregate view --- docs/release-notes/version-3.0.md | 1 + netbox/ipam/views.py | 1 + netbox/templates/ipam/aggregate.html | 6 +++--- netbox/templates/utilities/obj_table.html | 2 +- netbox/utilities/tables.py | 10 ++++++++++ 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/docs/release-notes/version-3.0.md b/docs/release-notes/version-3.0.md index 7e0198260..9a5862f6c 100644 --- a/docs/release-notes/version-3.0.md +++ b/docs/release-notes/version-3.0.md @@ -11,6 +11,7 @@ ### Bug Fixes +* [#6433](https://github.com/netbox-community/netbox/issues/6433) - Fix bulk editing of child prefixes under aggregate view * [#6895](https://github.com/netbox-community/netbox/issues/6895) - Remove errant markup for null values in CSV export * [#7373](https://github.com/netbox-community/netbox/issues/7373) - Fix flashing when server, client, and browser color-mode preferences are mismatched * [#7397](https://github.com/netbox-community/netbox/issues/7397) - Fix AttributeError exception when rendering export template for devices via REST API diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 015d47065..c24a80124 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -240,6 +240,7 @@ class AggregateView(generic.ObjectView): return { 'prefix_table': prefix_table, 'permissions': permissions, + 'bulk_querystring': f'within={instance.prefix}', 'show_available': request.GET.get('show_available', 'true') == 'true', } diff --git a/netbox/templates/ipam/aggregate.html b/netbox/templates/ipam/aggregate.html index 29aeec1ef..c254d9d63 100644 --- a/netbox/templates/ipam/aggregate.html +++ b/netbox/templates/ipam/aggregate.html @@ -75,8 +75,8 @@
-
- {% include 'utilities/obj_table.html' with table=prefix_table heading='Child Prefixes' bulk_edit_url='ipam:prefix_bulk_edit' bulk_delete_url='ipam:prefix_bulk_delete' %} -
+
+ {% include 'utilities/obj_table.html' with table=prefix_table heading='Child Prefixes' bulk_edit_url='ipam:prefix_bulk_edit' bulk_delete_url='ipam:prefix_bulk_delete' %} +
{% endblock %} diff --git a/netbox/templates/utilities/obj_table.html b/netbox/templates/utilities/obj_table.html index 99ab5c0c7..10337ff43 100644 --- a/netbox/templates/utilities/obj_table.html +++ b/netbox/templates/utilities/obj_table.html @@ -24,7 +24,7 @@
diff --git a/netbox/utilities/tables.py b/netbox/utilities/tables.py index 446438b5c..09fb8350c 100644 --- a/netbox/utilities/tables.py +++ b/netbox/utilities/tables.py @@ -111,6 +111,16 @@ class BaseTable(tables.Table): def selected_columns(self): return self._get_columns(visible=True) + @property + def objects_count(self): + """ + Return the total number of real objects represented by the Table. This is useful when dealing with + prefixes/IP addresses/etc., where some table rows may represent available address space. + """ + if not hasattr(self, '_objects_count'): + self._objects_count = sum(1 for obj in self.data if getattr(obj, 'pk')) + return self._objects_count + # # Table columns From c9c537a1b9b4cadb9c29579f984e1f96c2583792 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Fri, 1 Oct 2021 20:22:54 -0400 Subject: [PATCH 21/24] Fixes #6817: Custom field columns should be removed from tables upon their deletion --- docs/release-notes/version-3.0.md | 1 + netbox/utilities/tables.py | 28 +++++++++++++++++++--------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/docs/release-notes/version-3.0.md b/docs/release-notes/version-3.0.md index 9a5862f6c..7bba5c4db 100644 --- a/docs/release-notes/version-3.0.md +++ b/docs/release-notes/version-3.0.md @@ -12,6 +12,7 @@ ### Bug Fixes * [#6433](https://github.com/netbox-community/netbox/issues/6433) - Fix bulk editing of child prefixes under aggregate view +* [#6817](https://github.com/netbox-community/netbox/issues/6817) - Custom field columns should be removed from tables upon their deletion * [#6895](https://github.com/netbox-community/netbox/issues/6895) - Remove errant markup for null values in CSV export * [#7373](https://github.com/netbox-community/netbox/issues/7373) - Fix flashing when server, client, and browser color-mode preferences are mismatched * [#7397](https://github.com/netbox-community/netbox/issues/7397) - Fix AttributeError exception when rendering export template for devices via REST API diff --git a/netbox/utilities/tables.py b/netbox/utilities/tables.py index 09fb8350c..c8d0a0e43 100644 --- a/netbox/utilities/tables.py +++ b/netbox/utilities/tables.py @@ -29,13 +29,18 @@ class BaseTable(tables.Table): 'class': 'table table-hover object-list', } - def __init__(self, *args, user=None, **kwargs): + def __init__(self, *args, user=None, extra_columns=None, **kwargs): # Add custom field columns obj_type = ContentType.objects.get_for_model(self._meta.model) - for cf in CustomField.objects.filter(content_types=obj_type): - self.base_columns[f'cf_{cf.name}'] = CustomFieldColumn(cf) + cf_columns = [ + (f'cf_{cf.name}', CustomFieldColumn(cf)) for cf in CustomField.objects.filter(content_types=obj_type) + ] + if extra_columns is not None: + extra_columns.extend(cf_columns) + else: + extra_columns = cf_columns - super().__init__(*args, **kwargs) + super().__init__(*args, extra_columns=extra_columns, **kwargs) # Set default empty_text if none was provided if self.empty_text is None: @@ -50,17 +55,22 @@ class BaseTable(tables.Table): # Apply custom column ordering for user if user is not None and not isinstance(user, AnonymousUser): - columns = user.config.get(f"tables.{self.__class__.__name__}.columns") - if columns: + selected_columns = user.config.get(f"tables.{self.__class__.__name__}.columns") + if selected_columns: pk = self.base_columns.pop('pk', None) actions = self.base_columns.pop('actions', None) - for name, column in self.base_columns.items(): - if name in columns: + for name, column in self.columns.items(): + if name in selected_columns: self.columns.show(name) else: self.columns.hide(name) - self.sequence = [c for c in columns if c in self.base_columns] + # Rearrange the sequence to list selected columns first, followed by all remaining columns + # TODO: There's probably a more clever way to accomplish this + self.sequence = [ + *[c for c in selected_columns if c in self.columns.names()], + *[c for c in self.columns.names() if c not in selected_columns] + ] # Always include PK and actions column, if defined on the table if pk: From c818d63043f0ec1ab2ce360ac0666ea79f5e99b5 Mon Sep 17 00:00:00 2001 From: thatmattlove Date: Mon, 4 Oct 2021 09:19:18 -0700 Subject: [PATCH 22/24] Fixes #7427: Don't select hidden rows when selecting all in a table --- docs/release-notes/version-3.0.md | 1 + netbox/project-static/dist/netbox.js | Bin 322492 -> 322508 bytes netbox/project-static/dist/netbox.js.map | Bin 310799 -> 310799 bytes .../project-static/src/buttons/selectAll.ts | 2 +- 4 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-3.0.md b/docs/release-notes/version-3.0.md index 7bba5c4db..cd33faea2 100644 --- a/docs/release-notes/version-3.0.md +++ b/docs/release-notes/version-3.0.md @@ -8,6 +8,7 @@ * [#6423](https://github.com/netbox-community/netbox/issues/6423) - Cache rendered REST API specifications * [#6708](https://github.com/netbox-community/netbox/issues/6708) - Add image attachment support for circuits, power panels * [#7387](https://github.com/netbox-community/netbox/issues/7387) - Enable arbitrary ordering of custom scripts +* [#7427](https://github.com/netbox-community/netbox/issues/7427) - Don't select hidden rows when selecting all in a table ### Bug Fixes diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js index d4be77e327333507d80c1cb7ffe48fee86fc8dae..24524fad37ab67fb5c75fb4c80eb9ec14dc382a0 100644 GIT binary patch delta 40 wcmdmUUHHs(;f5B*7N!>FEi4Q62$U3A<>i-X=%wi9<>#epDr{e{hebvo08(cUMgRZ+ delta 23 fcmX?eU3kxR;f5B*7N!>FEi4Q6Y~QelMMfV0fldka diff --git a/netbox/project-static/dist/netbox.js.map b/netbox/project-static/dist/netbox.js.map index aadc192730cc44bd310fce4829ca501a9b533bfe..be7ed66c0701aa6c317101644c1248000206b25f 100644 GIT binary patch delta 25 hcmeDGBh>##sG)_ig{g&k3rq5RMwjg=^I2BQ0RW0|3DW=o delta 25 hcmeDGBh>##sG)_ig{g&k3rq5RM(6D*^I2BQ0RW0>3DN)n diff --git a/netbox/project-static/src/buttons/selectAll.ts b/netbox/project-static/src/buttons/selectAll.ts index 8b62ef0a0..64b98d390 100644 --- a/netbox/project-static/src/buttons/selectAll.ts +++ b/netbox/project-static/src/buttons/selectAll.ts @@ -36,7 +36,7 @@ function handleSelectAllToggle(event: Event): void { if (table !== null) { for (const element of table.querySelectorAll( - 'input[type="checkbox"][name="pk"]', + 'tr:not(.d-none) input[type="checkbox"][name="pk"]', )) { if (tableSelectAll.checked) { // Check all PK checkboxes if the select all checkbox is checked. From b5884a5b54609dbba54e27f9e5c4ac44864450a1 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 4 Oct 2021 13:41:16 -0400 Subject: [PATCH 23/24] Fixes #7215: Prevent rack elevations from overlapping when higher width is specified --- docs/release-notes/version-3.0.md | 1 + netbox/templates/dcim/rack_elevation_list.html | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-3.0.md b/docs/release-notes/version-3.0.md index cd33faea2..f367c54e6 100644 --- a/docs/release-notes/version-3.0.md +++ b/docs/release-notes/version-3.0.md @@ -15,6 +15,7 @@ * [#6433](https://github.com/netbox-community/netbox/issues/6433) - Fix bulk editing of child prefixes under aggregate view * [#6817](https://github.com/netbox-community/netbox/issues/6817) - Custom field columns should be removed from tables upon their deletion * [#6895](https://github.com/netbox-community/netbox/issues/6895) - Remove errant markup for null values in CSV export +* [#7215](https://github.com/netbox-community/netbox/issues/7215) - Prevent rack elevations from overlapping when higher width is specified * [#7373](https://github.com/netbox-community/netbox/issues/7373) - Fix flashing when server, client, and browser color-mode preferences are mismatched * [#7397](https://github.com/netbox-community/netbox/issues/7397) - Fix AttributeError exception when rendering export template for devices via REST API * [#7401](https://github.com/netbox-community/netbox/issues/7401) - Pin `jsonschema` package to v3.2.0 to fix REST API docs rendering diff --git a/netbox/templates/dcim/rack_elevation_list.html b/netbox/templates/dcim/rack_elevation_list.html index 522bac60e..468d44f76 100644 --- a/netbox/templates/dcim/rack_elevation_list.html +++ b/netbox/templates/dcim/rack_elevation_list.html @@ -30,7 +30,7 @@ {% if page %}
{% for rack in page %} -
+
{{ rack.name }} From 339bcb89bb57538d4579e0a6b882944022d96c03 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 4 Oct 2021 13:46:34 -0400 Subject: [PATCH 24/24] Release v3.0.5 --- .github/ISSUE_TEMPLATE/bug_report.yaml | 2 +- .github/ISSUE_TEMPLATE/feature_request.yaml | 2 +- docs/release-notes/version-3.0.md | 2 +- netbox/netbox/settings.py | 2 +- requirements.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index f63a8f170..26ece9049 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -17,7 +17,7 @@ body: What version of NetBox are you currently running? (If you don't have access to the most recent NetBox release, consider testing on our [demo instance](https://demo.netbox.dev/) before opening a bug report to see if your issue has already been addressed.) - placeholder: v3.0.4 + placeholder: v3.0.5 validations: required: true - type: dropdown diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index d39c2210e..ecbef8b09 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -14,7 +14,7 @@ body: attributes: label: NetBox version description: What version of NetBox are you currently running? - placeholder: v3.0.4 + placeholder: v3.0.5 validations: required: true - type: dropdown diff --git a/docs/release-notes/version-3.0.md b/docs/release-notes/version-3.0.md index f367c54e6..978c860ea 100644 --- a/docs/release-notes/version-3.0.md +++ b/docs/release-notes/version-3.0.md @@ -1,6 +1,6 @@ # NetBox v3.0 -## v3.0.5 (FUTURE) +## v3.0.5 (2021-10-04) ### Enhancements diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 2610fa7b1..94024f364 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -16,7 +16,7 @@ from django.core.validators import URLValidator # Environment setup # -VERSION = '3.0.5-dev' +VERSION = '3.0.5' # Hostname HOSTNAME = platform.node() diff --git a/requirements.txt b/requirements.txt index 0e925ff0b..5f5e158ed 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,7 +18,7 @@ gunicorn==20.1.0 Jinja2==3.0.1 Markdown==3.3.4 markdown-include==0.6.0 -mkdocs-material==7.3.0 +mkdocs-material==7.3.1 netaddr==0.8.0 Pillow==8.3.2 psycopg2-binary==2.9.1