From dcc311ac18f473756761945cbe2f881361c69438 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 28 Jul 2023 10:31:54 -0400 Subject: [PATCH 01/31] PRVB --- docs/release-notes/version-3.5.md | 4 ++++ netbox/netbox/settings.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-3.5.md b/docs/release-notes/version-3.5.md index 5fc6961fc..66c2cabce 100644 --- a/docs/release-notes/version-3.5.md +++ b/docs/release-notes/version-3.5.md @@ -1,5 +1,9 @@ # NetBox v3.5 +## v3.5.8 (FUTURE) + +--- + ## v3.5.7 (2023-07-28) ### Enhancements diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index aec4f76f6..58256d079 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -25,7 +25,7 @@ from netbox.constants import RQ_QUEUE_DEFAULT, RQ_QUEUE_HIGH, RQ_QUEUE_LOW # Environment setup # -VERSION = '3.5.7' +VERSION = '3.5.8-dev' # Hostname HOSTNAME = platform.node() From 5e6dcfd506cef27252af89bf1e16796c00f2d164 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Sun, 30 Jul 2023 13:34:08 -0400 Subject: [PATCH 02/31] Satisfy PEP8 E721 linter complaints --- netbox/dcim/graphql/gfk_mixins.py | 64 ++++++++++++------------ netbox/ipam/graphql/gfk_mixins.py | 30 +++++------ netbox/ipam/utils.py | 2 +- netbox/utilities/forms/bulk_import.py | 4 +- netbox/utilities/templatetags/helpers.py | 2 +- 5 files changed, 51 insertions(+), 51 deletions(-) diff --git a/netbox/dcim/graphql/gfk_mixins.py b/netbox/dcim/graphql/gfk_mixins.py index f3b8b696b..c97aa4c2b 100644 --- a/netbox/dcim/graphql/gfk_mixins.py +++ b/netbox/dcim/graphql/gfk_mixins.py @@ -53,23 +53,23 @@ class LinkPeerType(graphene.Union): @classmethod def resolve_type(cls, instance, info): - if type(instance) == CircuitTermination: + if type(instance) is CircuitTermination: return CircuitTerminationType - if type(instance) == ConsolePortType: + if type(instance) is ConsolePortType: return ConsolePortType - if type(instance) == ConsoleServerPort: + if type(instance) is ConsoleServerPort: return ConsoleServerPortType - if type(instance) == FrontPort: + if type(instance) is FrontPort: return FrontPortType - if type(instance) == Interface: + if type(instance) is Interface: return InterfaceType - if type(instance) == PowerFeed: + if type(instance) is PowerFeed: return PowerFeedType - if type(instance) == PowerOutlet: + if type(instance) is PowerOutlet: return PowerOutletType - if type(instance) == PowerPort: + if type(instance) is PowerPort: return PowerPortType - if type(instance) == RearPort: + if type(instance) is RearPort: return RearPortType @@ -89,23 +89,23 @@ class CableTerminationTerminationType(graphene.Union): @classmethod def resolve_type(cls, instance, info): - if type(instance) == CircuitTermination: + if type(instance) is CircuitTermination: return CircuitTerminationType - if type(instance) == ConsolePortType: + if type(instance) is ConsolePortType: return ConsolePortType - if type(instance) == ConsoleServerPort: + if type(instance) is ConsoleServerPort: return ConsoleServerPortType - if type(instance) == FrontPort: + if type(instance) is FrontPort: return FrontPortType - if type(instance) == Interface: + if type(instance) is Interface: return InterfaceType - if type(instance) == PowerFeed: + if type(instance) is PowerFeed: return PowerFeedType - if type(instance) == PowerOutlet: + if type(instance) is PowerOutlet: return PowerOutletType - if type(instance) == PowerPort: + if type(instance) is PowerPort: return PowerPortType - if type(instance) == RearPort: + if type(instance) is RearPort: return RearPortType @@ -123,19 +123,19 @@ class InventoryItemTemplateComponentType(graphene.Union): @classmethod def resolve_type(cls, instance, info): - if type(instance) == ConsolePortTemplate: + if type(instance) is ConsolePortTemplate: return ConsolePortTemplateType - if type(instance) == ConsoleServerPortTemplate: + if type(instance) is ConsoleServerPortTemplate: return ConsoleServerPortTemplateType - if type(instance) == FrontPortTemplate: + if type(instance) is FrontPortTemplate: return FrontPortTemplateType - if type(instance) == InterfaceTemplate: + if type(instance) is InterfaceTemplate: return InterfaceTemplateType - if type(instance) == PowerOutletTemplate: + if type(instance) is PowerOutletTemplate: return PowerOutletTemplateType - if type(instance) == PowerPortTemplate: + if type(instance) is PowerPortTemplate: return PowerPortTemplateType - if type(instance) == RearPortTemplate: + if type(instance) is RearPortTemplate: return RearPortTemplateType @@ -153,17 +153,17 @@ class InventoryItemComponentType(graphene.Union): @classmethod def resolve_type(cls, instance, info): - if type(instance) == ConsolePort: + if type(instance) is ConsolePort: return ConsolePortType - if type(instance) == ConsoleServerPort: + if type(instance) is ConsoleServerPort: return ConsoleServerPortType - if type(instance) == FrontPort: + if type(instance) is FrontPort: return FrontPortType - if type(instance) == Interface: + if type(instance) is Interface: return InterfaceType - if type(instance) == PowerOutlet: + if type(instance) is PowerOutlet: return PowerOutletType - if type(instance) == PowerPort: + if type(instance) is PowerPort: return PowerPortType - if type(instance) == RearPort: + if type(instance) is RearPort: return RearPortType diff --git a/netbox/ipam/graphql/gfk_mixins.py b/netbox/ipam/graphql/gfk_mixins.py index 31742c4a4..01c79690a 100644 --- a/netbox/ipam/graphql/gfk_mixins.py +++ b/netbox/ipam/graphql/gfk_mixins.py @@ -24,11 +24,11 @@ class IPAddressAssignmentType(graphene.Union): @classmethod def resolve_type(cls, instance, info): - if type(instance) == Interface: + if type(instance) is Interface: return InterfaceType - if type(instance) == FHRPGroup: + if type(instance) is FHRPGroup: return FHRPGroupType - if type(instance) == VMInterface: + if type(instance) is VMInterface: return VMInterfaceType @@ -42,11 +42,11 @@ class L2VPNAssignmentType(graphene.Union): @classmethod def resolve_type(cls, instance, info): - if type(instance) == Interface: + if type(instance) is Interface: return InterfaceType - if type(instance) == VLAN: + if type(instance) is VLAN: return VLANType - if type(instance) == VMInterface: + if type(instance) is VMInterface: return VMInterfaceType @@ -59,9 +59,9 @@ class FHRPGroupInterfaceType(graphene.Union): @classmethod def resolve_type(cls, instance, info): - if type(instance) == Interface: + if type(instance) is Interface: return InterfaceType - if type(instance) == VMInterface: + if type(instance) is VMInterface: return VMInterfaceType @@ -79,17 +79,17 @@ class VLANGroupScopeType(graphene.Union): @classmethod def resolve_type(cls, instance, info): - if type(instance) == Cluster: + if type(instance) is Cluster: return ClusterType - if type(instance) == ClusterGroup: + if type(instance) is ClusterGroup: return ClusterGroupType - if type(instance) == Location: + if type(instance) is Location: return LocationType - if type(instance) == Rack: + if type(instance) is Rack: return RackType - if type(instance) == Region: + if type(instance) is Region: return RegionType - if type(instance) == Site: + if type(instance) is Site: return SiteType - if type(instance) == SiteGroup: + if type(instance) is SiteGroup: return SiteGroupType diff --git a/netbox/ipam/utils.py b/netbox/ipam/utils.py index 93a40e5a0..262fd8d46 100644 --- a/netbox/ipam/utils.py +++ b/netbox/ipam/utils.py @@ -121,7 +121,7 @@ def add_available_vlans(vlans, vlan_group=None): }) vlans = list(vlans) + new_vlans - vlans.sort(key=lambda v: v.vid if type(v) == VLAN else v['vid']) + vlans.sort(key=lambda v: v.vid if type(v) is VLAN else v['vid']) return vlans diff --git a/netbox/utilities/forms/bulk_import.py b/netbox/utilities/forms/bulk_import.py index b7f432e63..6bdfd5662 100644 --- a/netbox/utilities/forms/bulk_import.py +++ b/netbox/utilities/forms/bulk_import.py @@ -123,9 +123,9 @@ class BulkImportForm(BootstrapMixin, SyncedDataMixin, forms.Form): records = [] try: for data in yaml.load_all(data, Loader=yaml.SafeLoader): - if type(data) == list: + if type(data) is list: records.extend(data) - elif type(data) == dict: + elif type(data) is dict: records.append(data) else: raise forms.ValidationError({ diff --git a/netbox/utilities/templatetags/helpers.py b/netbox/utilities/templatetags/helpers.py index 09a083112..aaee9679c 100644 --- a/netbox/utilities/templatetags/helpers.py +++ b/netbox/utilities/templatetags/helpers.py @@ -114,7 +114,7 @@ def annotated_date(date_value): if not date_value: return '' - if type(date_value) == datetime.date: + if type(date_value) is datetime.date: long_ts = date(date_value, 'DATE_FORMAT') short_ts = date(date_value, 'SHORT_DATE_FORMAT') else: From d0108cd14a81be2d44b5bf9a116e6929cf780005 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 2 Aug 2023 08:55:38 -0400 Subject: [PATCH 03/31] Remove hard-coded test runner --- netbox/netbox/settings.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 58256d079..2744ba701 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -461,8 +461,6 @@ LOGIN_REDIRECT_URL = f'/{BASE_PATH}' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' -TEST_RUNNER = "django_rich.test.RichRunner" - # Exclude potentially sensitive models from wildcard view exemption. These may still be exempted # by specifying the model individually in the EXEMPT_VIEW_PERMISSIONS configuration parameter. EXEMPT_EXCLUDE_MODELS = ( From a01fd302f1e2cffb08c307ce0497e0717c531cb3 Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Wed, 2 Aug 2023 17:25:54 +0530 Subject: [PATCH 04/31] fixes provider_network_id for related circuits #13343 --- netbox/circuits/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/circuits/views.py b/netbox/circuits/views.py index f1cfdd1d5..64dd82682 100644 --- a/netbox/circuits/views.py +++ b/netbox/circuits/views.py @@ -163,7 +163,7 @@ class ProviderNetworkView(generic.ObjectView): related_models = ( ( Circuit.objects.restrict(request.user, 'view').filter(terminations__provider_network=instance), - 'providernetwork_id', + 'provider_network_id', ), ) From 011bce92f1655d2d713eea7602de8a2d5a4000d9 Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Tue, 1 Aug 2023 20:35:16 +0530 Subject: [PATCH 05/31] fixes dummy payload URL for webhook test --- netbox/extras/tests/test_webhooks.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/netbox/extras/tests/test_webhooks.py b/netbox/extras/tests/test_webhooks.py index 19264dabb..ef7637765 100644 --- a/netbox/extras/tests/test_webhooks.py +++ b/netbox/extras/tests/test_webhooks.py @@ -31,8 +31,8 @@ class WebhookTest(APITestCase): def setUpTestData(cls): site_ct = ContentType.objects.get_for_model(Site) - DUMMY_URL = "http://localhost/" - DUMMY_SECRET = "LOOKATMEIMASECRETSTRING" + DUMMY_URL = 'http://localhost:9000/' + DUMMY_SECRET = 'LOOKATMEIMASECRETSTRING' webhooks = Webhook.objects.bulk_create(( Webhook(name='Webhook 1', type_create=True, payload_url=DUMMY_URL, secret=DUMMY_SECRET, additional_headers='X-Foo: Bar'), @@ -259,7 +259,7 @@ class WebhookTest(APITestCase): name='Conditional Webhook', type_create=True, type_update=True, - payload_url='http://localhost/', + payload_url='http://localhost:9000/', conditions={ 'and': [ { From 0c8efdbb7dd3b9400c80871c6837c57bcc975482 Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Thu, 3 Aug 2023 01:15:09 +0530 Subject: [PATCH 06/31] Adds assigned bool for IP address API (#13301) * adds assigned bool for ip address API #13151 * Add filterset test --------- Co-authored-by: Jeremy Stretch --- netbox/ipam/filtersets.py | 16 ++++++++++++++++ netbox/ipam/tests/test_filtersets.py | 6 ++++++ 2 files changed, 22 insertions(+) diff --git a/netbox/ipam/filtersets.py b/netbox/ipam/filtersets.py index d011472d9..9b57cb273 100644 --- a/netbox/ipam/filtersets.py +++ b/netbox/ipam/filtersets.py @@ -591,6 +591,10 @@ class IPAddressFilterSet(NetBoxModelFilterSet, TenancyFilterSet): method='_assigned_to_interface', label=_('Is assigned to an interface'), ) + assigned = django_filters.BooleanFilter( + method='_assigned', + label=_('Is assigned'), + ) status = django_filters.MultipleChoiceFilter( choices=IPAddressStatusChoices, null_value=None @@ -706,6 +710,18 @@ class IPAddressFilterSet(NetBoxModelFilterSet, TenancyFilterSet): assigned_object_id__isnull=False ) + def _assigned(self, queryset, name, value): + if value: + return queryset.exclude( + assigned_object_type__isnull=True, + assigned_object_id__isnull=True + ) + else: + return queryset.filter( + assigned_object_type__isnull=True, + assigned_object_id__isnull=True + ) + class FHRPGroupFilterSet(NetBoxModelFilterSet): protocol = django_filters.MultipleChoiceFilter( diff --git a/netbox/ipam/tests/test_filtersets.py b/netbox/ipam/tests/test_filtersets.py index 3d9a66567..0ae7544ab 100644 --- a/netbox/ipam/tests/test_filtersets.py +++ b/netbox/ipam/tests/test_filtersets.py @@ -992,6 +992,12 @@ class IPAddressTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'fhrpgroup_id': [fhrp_groups[0].pk, fhrp_groups[1].pk]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_assigned(self): + params = {'assigned': 'true'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 8) + params = {'assigned': 'false'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) + def test_assigned_to_interface(self): params = {'assigned_to_interface': 'true'} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 6) From 7c65b5c286ee432ad8c1cb770b55b35ef4226e8d Mon Sep 17 00:00:00 2001 From: Matej Vadnjal Date: Wed, 2 Aug 2023 22:08:14 +0200 Subject: [PATCH 07/31] Fixes #13033: add formatted speed column to Interfaces (#13275) * Fixes #13033: add formatted speed column to Interfaces * use TemplateColumn instead of own class --- netbox/dcim/tables/devices.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py index db2655d27..42b34e999 100644 --- a/netbox/dcim/tables/devices.py +++ b/netbox/dcim/tables/devices.py @@ -545,6 +545,11 @@ class InterfaceTable(ModularDeviceComponentTable, BaseInterfaceTable, PathEndpoi } ) mgmt_only = columns.BooleanColumn() + speed_formatted = columns.TemplateColumn( + template_code='{% load helpers %}{{ value|humanize_speed }}', + accessor=Accessor('speed'), + verbose_name='Speed' + ) wireless_link = tables.Column( linkify=True ) @@ -568,7 +573,7 @@ class InterfaceTable(ModularDeviceComponentTable, BaseInterfaceTable, PathEndpoi model = models.Interface fields = ( 'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'enabled', 'type', 'mgmt_only', 'mtu', - 'speed', 'duplex', 'mode', 'mac_address', 'wwn', 'poe_mode', 'poe_type', 'rf_role', 'rf_channel', + 'speed', 'speed_formatted', 'duplex', 'mode', 'mac_address', 'wwn', 'poe_mode', 'poe_type', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'description', 'mark_connected', 'cable', 'cable_color', 'wireless_link', 'wireless_lans', 'link_peer', 'connection', 'tags', 'vdcs', 'vrf', 'l2vpn', 'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'created', 'last_updated', From a65f9f3770577c6af489ea7ada659231574d3b7e Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 3 Aug 2023 14:53:58 -0400 Subject: [PATCH 08/31] Fixes #13369: Fix job termination status for failed reports --- netbox/extras/reports.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/netbox/extras/reports.py b/netbox/extras/reports.py index 8f3af2a09..6af81a9d9 100644 --- a/netbox/extras/reports.py +++ b/netbox/extras/reports.py @@ -214,20 +214,18 @@ class Report(object): self.active_test = method_name test_method = getattr(self, method_name) test_method() + job.data = self._results if self.failed: self.logger.warning("Report failed") - job.status = JobStatusChoices.STATUS_FAILED + job.terminate(status=JobStatusChoices.STATUS_FAILED) else: self.logger.info("Report completed successfully") - job.status = JobStatusChoices.STATUS_COMPLETED + job.terminate() except Exception as e: stacktrace = traceback.format_exc() self.log_failure(None, f"An exception occurred: {type(e).__name__}: {e}
{stacktrace}
") logger.error(f"Exception raised during report execution: {e}") job.terminate(status=JobStatusChoices.STATUS_ERRORED) - finally: - job.data = self._results - job.terminate() # Perform any post-run tasks self.post_run() From 053bdd28314ff83ed2bf4134ad92232863541bee Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 4 Aug 2023 08:55:43 -0400 Subject: [PATCH 09/31] Add stadium analogy and behavior anti-patterns --- CONTRIBUTING.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6b71fb515..301fac079 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,12 +14,25 @@

-Some general tips for engaging here on GitHub: +## :information_source: Welcome to the Stadium! + +In her book [Working in Public](https://www.amazon.com/Working-Public-Making-Maintenance-Software/dp/0578675862), Nadia Eghbal defines four production models for open source projects, categorized by contributor and user growth: federations, clubs, toys, and stadiums. The NetBox project fits her definition of a stadium very well: + +> Stadiums are projects with low contributor growth and high user growth. While they may receive casual contributions, their regular contributor base does not grow proportionately to their users. As a result, they tend to be powered by one or a few developers. + +The bulk of NetBox's development is carried out by a handful of core maintainers, with occasional contributions from collaborators in the community. We find the stadium analogy very useful in conveying the roles and obligations of both contributors and users. + +If you're a contributor, actively working on the center stage, you have an obligation to produce quality content that will benefit the project as a whole. Conversely, if you're in the audience consuming the work being produced, you have the option of making requests and suggestions, but must also recognize that contributors are under no obligation to act on them. + +NetBox users are welcome to participate in either role, on stage or in the crowd. We ask only that you acknowledge the role you've chosen and respect the roles of others. + +### General Tips for Working on GitHub * Register for a free [GitHub account](https://github.com/signup) if you haven't already. * You can use [GitHub Markdown](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax) for formatting text and adding images. * To help mitigate notification spam, please avoid "bumping" issues with no activity. (To vote an issue up or down, use a :thumbsup: or :thumbsdown: reaction.) * Please avoid pinging members with `@` unless they've previously expressed interest or involvement with that particular issue. +* Familiarize yourself with [this list of discussion anti-patterns](https://github.com/bradfitz/issue-tracker-behaviors) and make every effort to avoid them. ## :bug: Reporting Bugs From 886f68c1c105a06cc39de51167de6085428bc0b6 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 4 Aug 2023 10:12:15 -0400 Subject: [PATCH 10/31] Include notes re: demo data and netbox-docker --- docs/development/release-checklist.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/development/release-checklist.md b/docs/development/release-checklist.md index efb0f44b9..000948ee7 100644 --- a/docs/development/release-checklist.md +++ b/docs/development/release-checklist.md @@ -43,10 +43,22 @@ Follow these instructions to perform a new installation of NetBox in a temporary Submit a pull request to merge the `feature` branch into the `develop` branch in preparation for its release. Once it has been merged, continue with the section for patch releases below. +### Rebuild Demo Data (After Release) + +After the release of a new minor version, generate a new demo data snapshot compatible with the new release. See the [`netbox-demo-data`](https://github.com/netbox-community/netbox-demo-data) repository for instructions. + --- ## Patch Releases +### Notify netbox-docker Project of Any Relevant Changes + +Notify the [`netbox-docker`](https://github.com/netbox-community/netbox-docker) maintainers (in **#netbox-docker**) of any changes that may be relevant to their build process, including: + +* Significant changes to `upgrade.sh` +* Increases in minimum versions for service dependencies (PostgreSQL, Redis, etc.) +* Any changes to the reference installation + ### Update Requirements Before each release, update each of NetBox's Python dependencies to its most recent stable version. These are defined in `requirements.txt`, which is updated from `base_requirements.txt` using `pip`. To do this: From 85c620ad24ec8d27c97c9118b93c7cc5a92469a0 Mon Sep 17 00:00:00 2001 From: Henrik Strand Date: Fri, 4 Aug 2023 17:32:52 +0200 Subject: [PATCH 11/31] Adding interface TYPE_400GE_CFP2/400gbase-x-cfp2 (#13338) * Added 400G CFP2 to InterfaceTypeChoices * Added new type to choises --- netbox/dcim/choices.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/netbox/dcim/choices.py b/netbox/dcim/choices.py index e850a8c51..21bd3ed7e 100644 --- a/netbox/dcim/choices.py +++ b/netbox/dcim/choices.py @@ -834,6 +834,7 @@ class InterfaceTypeChoices(ChoiceSet): TYPE_200GE_CFP2 = '200gbase-x-cfp2' TYPE_200GE_QSFP56 = '200gbase-x-qsfp56' TYPE_200GE_QSFP_DD = '200gbase-x-qsfpdd' + TYPE_400GE_CFP2 = '400gbase-x-cfp2' TYPE_400GE_QSFP_DD = '400gbase-x-qsfpdd' TYPE_400GE_OSFP = '400gbase-x-osfp' TYPE_400GE_CDFP = '400gbase-x-cdfp' @@ -976,6 +977,7 @@ class InterfaceTypeChoices(ChoiceSet): (TYPE_100GE_CFP, 'CFP (100GE)'), (TYPE_100GE_CFP2, 'CFP2 (100GE)'), (TYPE_200GE_CFP2, 'CFP2 (200GE)'), + (TYPE_400GE_CFP2, 'CFP2 (400GE)'), (TYPE_100GE_CFP4, 'CFP4 (100GE)'), (TYPE_100GE_CXP, 'CXP (100GE)'), (TYPE_100GE_CPAK, 'Cisco CPAK (100GE)'), From 851a60bb2fea3c6a8057c298dc6b2d692e18c5e6 Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Tue, 1 Aug 2023 18:55:42 +0530 Subject: [PATCH 12/31] adds delete for SyncedDataMixin when related AutoSyncRecord is available #12750 --- netbox/netbox/models/features.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/netbox/netbox/models/features.py b/netbox/netbox/models/features.py index 8bacba534..1e55ec2a3 100644 --- a/netbox/netbox/models/features.py +++ b/netbox/netbox/models/features.py @@ -442,6 +442,19 @@ class SyncedDataMixin(models.Model): return ret + def delete(self, *args, **kwargs): + from core.models import AutoSyncRecord + + # Delete AutoSyncRecord + content_type = ContentType.objects.get_for_model(self) + AutoSyncRecord.objects.filter( + datafile=self.data_file, + object_type=content_type, + object_id=self.pk + ).delete() + + return super().delete(*args, **kwargs) + def resolve_data_file(self): """ Determine the designated DataFile object identified by its parent DataSource and its path. Returns None if From 6f56d29f124949c4b41a8f38b5720b518d2a13f5 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 4 Aug 2023 13:36:33 -0400 Subject: [PATCH 13/31] Changelog for #12750, #12889, #13033, #13151, #13343, #13369 --- docs/release-notes/version-3.5.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/release-notes/version-3.5.md b/docs/release-notes/version-3.5.md index 66c2cabce..4347d9837 100644 --- a/docs/release-notes/version-3.5.md +++ b/docs/release-notes/version-3.5.md @@ -2,6 +2,18 @@ ## v3.5.8 (FUTURE) +### Enhancements + +* [#12889](https://github.com/netbox-community/netbox/issues/12889) - Add 400GE CFP2 interface type +* [#13033](https://github.com/netbox-community/netbox/issues/13033) - Add human-friendly speed column to interfaces table +* [#13151](https://github.com/netbox-community/netbox/issues/13151) - Add "assigned" filter for IP addresses + +### Bug Fixes + +* [#12750](https://github.com/netbox-community/netbox/issues/12750) - Automatically delete an AutoSyncRecord when its object is deleted +* [#13343](https://github.com/netbox-community/netbox/issues/13343) - Fix filtering of circuits under provider network view +* [#13369](https://github.com/netbox-community/netbox/issues/13369) - Fix job termination status for failed reports + --- ## v3.5.7 (2023-07-28) From 5e8b6664ab1810851b0d4e51de83826c0496d72d Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 4 Aug 2023 14:05:41 -0400 Subject: [PATCH 14/31] Closes #11675: Add support for specifying import/export route targets during VRF bulk import --- netbox/ipam/forms/bulk_import.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/netbox/ipam/forms/bulk_import.py b/netbox/ipam/forms/bulk_import.py index 683d40f49..3bce26249 100644 --- a/netbox/ipam/forms/bulk_import.py +++ b/netbox/ipam/forms/bulk_import.py @@ -1,7 +1,6 @@ from django import forms from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError -from django.db.models import Q from django.utils.translation import gettext as _ from dcim.models import Device, Interface, Site @@ -10,7 +9,9 @@ from ipam.constants import * from ipam.models import * from netbox.forms import NetBoxModelImportForm from tenancy.models import Tenant -from utilities.forms.fields import CSVChoiceField, CSVContentTypeField, CSVModelChoiceField, SlugField +from utilities.forms.fields import ( + CSVChoiceField, CSVContentTypeField, CSVModelChoiceField, CSVModelMultipleChoiceField, SlugField +) from virtualization.models import VirtualMachine, VMInterface __all__ = ( @@ -41,10 +42,25 @@ class VRFImportForm(NetBoxModelImportForm): to_field_name='name', help_text=_('Assigned tenant') ) + import_targets = CSVModelMultipleChoiceField( + queryset=RouteTarget.objects.all(), + required=False, + to_field_name='name', + help_text=_('Import route targets') + ) + export_targets = CSVModelMultipleChoiceField( + queryset=RouteTarget.objects.all(), + required=False, + to_field_name='name', + help_text=_('Export route targets') + ) class Meta: model = VRF - fields = ('name', 'rd', 'tenant', 'enforce_unique', 'description', 'comments', 'tags') + fields = ( + 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'import_targets', 'export_targets', 'comments', + 'tags', + ) class RouteTargetImportForm(NetBoxModelImportForm): From e30fe8cfe742ca74dce987ec72bc8a64bc979395 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 4 Aug 2023 14:37:40 -0400 Subject: [PATCH 15/31] Closes #11922: Populate assigned VDCs when adding a child interface --- netbox/dcim/forms/model_forms.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/netbox/dcim/forms/model_forms.py b/netbox/dcim/forms/model_forms.py index 219216045..3c02e6e4e 100644 --- a/netbox/dcim/forms/model_forms.py +++ b/netbox/dcim/forms/model_forms.py @@ -1042,6 +1042,9 @@ class InterfaceForm(InterfaceCommonForm, ModularDeviceComponentForm): queryset=VirtualDeviceContext.objects.all(), required=False, label='Virtual Device Contexts', + initial_params={ + 'interfaces': '$parent', + }, query_params={ 'device_id': '$device', } From 94c717392d71062efcb7460c31264d264864058a Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 7 Aug 2023 10:48:41 -0400 Subject: [PATCH 16/31] Closes #13400: Add 'name' property to BaseTable class --- netbox/netbox/tables/tables.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/netbox/netbox/tables/tables.py b/netbox/netbox/tables/tables.py index 20eab822d..975311e4a 100644 --- a/netbox/netbox/tables/tables.py +++ b/netbox/netbox/tables/tables.py @@ -54,7 +54,7 @@ class BaseTable(tables.Table): # 3. Meta.fields selected_columns = None if user is not None and not isinstance(user, AnonymousUser): - selected_columns = user.config.get(f"tables.{self.__class__.__name__}.columns") + selected_columns = user.config.get(f"tables.{self.name}.columns") if not selected_columns: selected_columns = getattr(self.Meta, 'default_columns', self.Meta.fields) @@ -113,6 +113,10 @@ class BaseTable(tables.Table): columns.append((name, column.verbose_name)) return columns + @property + def name(self): + return self.__class__.__name__ + @property def available_columns(self): return self._get_columns(visible=False) @@ -138,17 +142,16 @@ class BaseTable(tables.Table): """ # Save ordering preference if request.user.is_authenticated: - table_name = self.__class__.__name__ if self.prefixed_order_by_field in request.GET: if request.GET[self.prefixed_order_by_field]: # If an ordering has been specified as a query parameter, save it as the # user's preferred ordering for this table. ordering = request.GET.getlist(self.prefixed_order_by_field) - request.user.config.set(f'tables.{table_name}.ordering', ordering, commit=True) + request.user.config.set(f'tables.{self.name}.ordering', ordering, commit=True) else: # If the ordering has been set to none (empty), clear any existing preference. - request.user.config.clear(f'tables.{table_name}.ordering', commit=True) - elif ordering := request.user.config.get(f'tables.{table_name}.ordering'): + request.user.config.clear(f'tables.{self.name}.ordering', commit=True) + elif ordering := request.user.config.get(f'tables.{self.name}.ordering'): # If no ordering has been specified, set the preferred ordering (if any). self.order_by = ordering From f830aae0fbaf156c60faaf4b7aa6cec036ae8b50 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 7 Aug 2023 15:29:20 -0400 Subject: [PATCH 17/31] Closes #13368: Report installed plugins during server error (#13387) * Introduce get_installed_plugins() utility * Extend 500 error template to list installed plugins * Move get_plugin_config() to extras.plugins.utils --- netbox/extras/plugins/__init__.py | 21 ---------------- netbox/extras/plugins/utils.py | 37 +++++++++++++++++++++++++++++ netbox/extras/tests/test_plugins.py | 3 ++- netbox/netbox/api/views.py | 11 ++------- netbox/netbox/views/errors.py | 3 +++ netbox/templates/500.html | 5 +++- 6 files changed, 48 insertions(+), 32 deletions(-) create mode 100644 netbox/extras/plugins/utils.py diff --git a/netbox/extras/plugins/__init__.py b/netbox/extras/plugins/__init__.py index 83c7a7bb0..8736a3197 100644 --- a/netbox/extras/plugins/__init__.py +++ b/netbox/extras/plugins/__init__.py @@ -2,7 +2,6 @@ import collections from importlib import import_module from django.apps import AppConfig -from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.utils.module_loading import import_string from packaging import version @@ -146,23 +145,3 @@ class PluginConfig(AppConfig): for setting, value in cls.default_settings.items(): if setting not in user_config: user_config[setting] = value - - -# -# Utilities -# - -def get_plugin_config(plugin_name, parameter, default=None): - """ - Return the value of the specified plugin configuration parameter. - - Args: - plugin_name: The name of the plugin - parameter: The name of the configuration parameter - default: The value to return if the parameter is not defined (default: None) - """ - try: - plugin_config = settings.PLUGINS_CONFIG[plugin_name] - return plugin_config.get(parameter, default) - except KeyError: - raise ImproperlyConfigured(f"Plugin {plugin_name} is not registered.") diff --git a/netbox/extras/plugins/utils.py b/netbox/extras/plugins/utils.py new file mode 100644 index 000000000..c260f156d --- /dev/null +++ b/netbox/extras/plugins/utils.py @@ -0,0 +1,37 @@ +from django.apps import apps +from django.conf import settings +from django.core.exceptions import ImproperlyConfigured + +__all__ = ( + 'get_installed_plugins', + 'get_plugin_config', +) + + +def get_installed_plugins(): + """ + Return a dictionary mapping the names of installed plugins to their versions. + """ + plugins = {} + for plugin_name in settings.PLUGINS: + plugin_name = plugin_name.rsplit('.', 1)[-1] + plugin_config = apps.get_app_config(plugin_name) + plugins[plugin_name] = getattr(plugin_config, 'version', None) + + return dict(sorted(plugins.items())) + + +def get_plugin_config(plugin_name, parameter, default=None): + """ + Return the value of the specified plugin configuration parameter. + + Args: + plugin_name: The name of the plugin + parameter: The name of the configuration parameter + default: The value to return if the parameter is not defined (default: None) + """ + try: + plugin_config = settings.PLUGINS_CONFIG[plugin_name] + return plugin_config.get(parameter, default) + except KeyError: + raise ImproperlyConfigured(f"Plugin {plugin_name} is not registered.") diff --git a/netbox/extras/tests/test_plugins.py b/netbox/extras/tests/test_plugins.py index cb7629ad2..42dde43fd 100644 --- a/netbox/extras/tests/test_plugins.py +++ b/netbox/extras/tests/test_plugins.py @@ -5,8 +5,9 @@ from django.core.exceptions import ImproperlyConfigured from django.test import Client, TestCase, override_settings from django.urls import reverse -from extras.plugins import PluginMenu, get_plugin_config +from extras.plugins import PluginMenu from extras.tests.dummy_plugin import config as dummy_config +from extras.plugins.utils import get_plugin_config from netbox.graphql.schema import Query from netbox.registry import registry diff --git a/netbox/netbox/api/views.py b/netbox/netbox/api/views.py index 5c55697ff..97f690762 100644 --- a/netbox/netbox/api/views.py +++ b/netbox/netbox/api/views.py @@ -11,6 +11,7 @@ from rest_framework.reverse import reverse from rest_framework.views import APIView from rq.worker import Worker +from extras.plugins.utils import get_installed_plugins from netbox.api.authentication import IsAuthenticatedOrLoginNotRequired @@ -61,19 +62,11 @@ class StatusView(APIView): installed_apps[app_config.name] = version installed_apps = {k: v for k, v in sorted(installed_apps.items())} - # Gather installed plugins - plugins = {} - for plugin_name in settings.PLUGINS: - plugin_name = plugin_name.rsplit('.', 1)[-1] - plugin_config = apps.get_app_config(plugin_name) - plugins[plugin_name] = getattr(plugin_config, 'version', None) - plugins = {k: v for k, v in sorted(plugins.items())} - return Response({ 'django-version': DJANGO_VERSION, 'installed-apps': installed_apps, 'netbox-version': settings.VERSION, - 'plugins': plugins, + 'plugins': get_installed_plugins(), 'python-version': platform.python_version(), 'rq-workers-running': Worker.count(get_connection('default')), }) diff --git a/netbox/netbox/views/errors.py b/netbox/netbox/views/errors.py index c74c67cef..a81d45cb5 100644 --- a/netbox/netbox/views/errors.py +++ b/netbox/netbox/views/errors.py @@ -11,6 +11,8 @@ from django.views.defaults import ERROR_500_TEMPLATE_NAME, page_not_found from django.views.generic import View from sentry_sdk import capture_message +from extras.plugins.utils import get_installed_plugins + __all__ = ( 'handler_404', 'handler_500', @@ -53,4 +55,5 @@ def handler_500(request, template_name=ERROR_500_TEMPLATE_NAME): 'exception': str(type_), 'netbox_version': settings.VERSION, 'python_version': platform.python_version(), + 'plugins': get_installed_plugins(), })) diff --git a/netbox/templates/500.html b/netbox/templates/500.html index 6cface941..0257e7c43 100644 --- a/netbox/templates/500.html +++ b/netbox/templates/500.html @@ -30,7 +30,10 @@ {{ error }} Python version: {{ python_version }} -NetBox version: {{ netbox_version }} +NetBox version: {{ netbox_version }} +Plugins: {% for plugin, version in plugins.items %} + {{ plugin }}: {{ version }}{% empty %}None installed{% endfor %} +

If further assistance is required, please post to the NetBox discussion forum on GitHub.

From a8c024fca50e8da747ca586e21713b3db112a086 Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Wed, 9 Aug 2023 23:46:03 +0530 Subject: [PATCH 18/31] Adds generic object children template (#13388) * adds generic tab view template #12110 * Rename view_tab.html and move to generic/ * Fix console ports template * Move bulk operations view resolution to template * Avoid setting default template_name on ObjectChildrenView * Move base_template and table_config context vars to base context * removed bulk_delete_control from templates * refactored bulk_controls view * fixed table_config * renamed object_tab.html to objectchildren_list.html * removed unused import * Refactor template blocks for bulk operation buttons * Rename object children generic template * Move disconnect bulk action into a separate template for device components * Fix cluster devices & VM interfaces views * minor button label change --------- Co-authored-by: Jeremy Stretch --- netbox/dcim/views.py | 13 +++ netbox/ipam/views.py | 10 +-- netbox/netbox/views/generic/object_views.py | 3 + .../dcim/device/components_base.html | 15 ++++ .../templates/dcim/device/consoleports.html | 74 +++++------------ .../dcim/device/consoleserverports.html | 74 +++++------------ netbox/templates/dcim/device/devicebays.html | 57 +++---------- netbox/templates/dcim/device/frontports.html | 74 +++++------------ netbox/templates/dcim/device/interfaces.html | 83 +++++-------------- netbox/templates/dcim/device/inventory.html | 57 +++---------- netbox/templates/dcim/device/modulebays.html | 53 +++--------- .../templates/dcim/device/poweroutlets.html | 74 +++++------------ netbox/templates/dcim/device/powerports.html | 74 +++++------------ netbox/templates/dcim/device/rearports.html | 74 +++++------------ .../dcim/rack/non_racked_devices.html | 43 +--------- netbox/templates/dcim/rack/reservations.html | 49 ++--------- netbox/templates/generic/object_children.html | 57 +++++++++++++ netbox/templates/ipam/aggregate/prefixes.html | 39 +-------- netbox/templates/ipam/asnrange/asns.html | 36 -------- .../ipam/ipaddress/ip_addresses.html | 19 ----- .../templates/ipam/iprange/ip_addresses.html | 41 +-------- .../templates/ipam/prefix/ip_addresses.html | 39 +-------- netbox/templates/ipam/prefix/ip_ranges.html | 39 +-------- netbox/templates/ipam/prefix/prefixes.html | 40 +-------- netbox/templates/ipam/vlan/interfaces.html | 20 ----- netbox/templates/ipam/vlan/vminterfaces.html | 20 ----- netbox/templates/tenancy/object_contacts.html | 19 +---- .../virtualization/cluster/devices.html | 37 +++------ .../cluster/virtual_machines.html | 35 -------- .../virtualmachine/interfaces.html | 54 +++--------- netbox/tenancy/views.py | 5 -- netbox/virtualization/views.py | 19 ++++- 32 files changed, 337 insertions(+), 1009 deletions(-) create mode 100644 netbox/templates/dcim/device/components_base.html create mode 100644 netbox/templates/generic/object_children.html delete mode 100644 netbox/templates/ipam/asnrange/asns.html delete mode 100644 netbox/templates/ipam/ipaddress/ip_addresses.html delete mode 100644 netbox/templates/ipam/vlan/interfaces.html delete mode 100644 netbox/templates/ipam/vlan/vminterfaces.html delete mode 100644 netbox/templates/virtualization/cluster/virtual_machines.html diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 5b93e5f0b..fca222f47 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -1,4 +1,5 @@ import traceback +from collections import defaultdict from django.contrib import messages from django.contrib.contenttypes.models import ContentType @@ -45,6 +46,15 @@ CABLE_TERMINATION_TYPES = { class DeviceComponentsView(generic.ObjectChildrenView): + actions = ('add', 'import', 'export', 'bulk_edit', 'bulk_delete', 'bulk_rename', 'bulk_disconnect') + action_perms = defaultdict(set, **{ + 'add': {'add'}, + 'import': {'add'}, + 'bulk_edit': {'change'}, + 'bulk_delete': {'delete'}, + 'bulk_rename': {'change'}, + 'bulk_disconnect': {'change'}, + }) queryset = Device.objects.all() def get_children(self, request, parent): @@ -1997,6 +2007,7 @@ class DeviceModuleBaysView(DeviceComponentsView): table = tables.DeviceModuleBayTable filterset = filtersets.ModuleBayFilterSet template_name = 'dcim/device/modulebays.html' + actions = ('add', 'import', 'export', 'bulk_edit', 'bulk_delete', 'bulk_rename') tab = ViewTab( label=_('Module Bays'), badge=lambda obj: obj.modulebays.count(), @@ -2012,6 +2023,7 @@ class DeviceDeviceBaysView(DeviceComponentsView): table = tables.DeviceDeviceBayTable filterset = filtersets.DeviceBayFilterSet template_name = 'dcim/device/devicebays.html' + actions = ('add', 'import', 'export', 'bulk_edit', 'bulk_delete', 'bulk_rename') tab = ViewTab( label=_('Device Bays'), badge=lambda obj: obj.devicebays.count(), @@ -2023,6 +2035,7 @@ class DeviceDeviceBaysView(DeviceComponentsView): @register_model_view(Device, 'inventory') class DeviceInventoryView(DeviceComponentsView): + actions = ('add', 'import', 'export', 'bulk_edit', 'bulk_delete', 'bulk_rename') child_model = InventoryItem table = tables.DeviceInventoryItemTable filterset = filtersets.InventoryItemFilterSet diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 32badd2d5..d8e4d8b47 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -216,7 +216,7 @@ class ASNRangeASNsView(generic.ObjectChildrenView): child_model = ASN table = tables.ASNTable filterset = filtersets.ASNFilterSet - template_name = 'ipam/asnrange/asns.html' + template_name = 'generic/object_children.html' tab = ViewTab( label=_('ASNs'), badge=lambda x: x.get_child_asns().count(), @@ -816,7 +816,6 @@ class IPAddressAssignView(generic.ObjectView): table = None if form.is_valid(): - addresses = self.queryset.prefetch_related('vrf', 'tenant') # Limit to 100 results addresses = filtersets.IPAddressFilterSet(request.POST, addresses).qs[:100] @@ -866,7 +865,7 @@ class IPAddressRelatedIPsView(generic.ObjectChildrenView): child_model = IPAddress table = tables.IPAddressTable filterset = filtersets.IPAddressFilterSet - template_name = 'ipam/ipaddress/ip_addresses.html' + template_name = 'generic/object_children.html' tab = ViewTab( label=_('Related IPs'), badge=lambda x: x.get_related_ips().count(), @@ -963,7 +962,6 @@ class FHRPGroupView(generic.ObjectView): queryset = FHRPGroup.objects.all() def get_extra_context(self, request, instance): - # Get assigned interfaces members_table = tables.FHRPGroupAssignmentTable( data=FHRPGroupAssignment.objects.restrict(request.user, 'view').filter(group=instance), @@ -1077,7 +1075,7 @@ class VLANInterfacesView(generic.ObjectChildrenView): child_model = Interface table = tables.VLANDevicesTable filterset = InterfaceFilterSet - template_name = 'ipam/vlan/interfaces.html' + template_name = 'generic/object_children.html' tab = ViewTab( label=_('Device Interfaces'), badge=lambda x: x.get_interfaces().count(), @@ -1095,7 +1093,7 @@ class VLANVMInterfacesView(generic.ObjectChildrenView): child_model = VMInterface table = tables.VLANVirtualMachinesTable filterset = VMInterfaceFilterSet - template_name = 'ipam/vlan/vminterfaces.html' + template_name = 'generic/object_children.html' tab = ViewTab( label=_('VM Interfaces'), badge=lambda x: x.get_vminterfaces().count(), diff --git a/netbox/netbox/views/generic/object_views.py b/netbox/netbox/views/generic/object_views.py index 1ba789cf1..99d8ff540 100644 --- a/netbox/netbox/views/generic/object_views.py +++ b/netbox/netbox/views/generic/object_views.py @@ -143,9 +143,12 @@ class ObjectChildrenView(ObjectView, ActionsMixin, TableMixin): return render(request, self.get_template_name(), { 'object': instance, 'child_model': self.child_model, + 'base_template': f'{instance._meta.app_label}/{instance._meta.model_name}.html', 'table': table, + 'table_config': f'{table.name}_config', 'actions': actions, 'tab': self.tab, + 'return_url': request.get_full_path(), **self.get_extra_context(request, instance), }) diff --git a/netbox/templates/dcim/device/components_base.html b/netbox/templates/dcim/device/components_base.html new file mode 100644 index 000000000..1e3d8a39d --- /dev/null +++ b/netbox/templates/dcim/device/components_base.html @@ -0,0 +1,15 @@ +{% extends 'generic/object_children.html' %} +{% load helpers %} + +{% block bulk_edit_controls %} + {{ block.super }} + {% with bulk_rename_view=child_model|validated_viewname:"bulk_rename" %} + {% if 'bulk_rename' in actions and bulk_rename_view %} + + {% endif %} + {% endwith %} +{% endblock bulk_edit_controls %} diff --git a/netbox/templates/dcim/device/consoleports.html b/netbox/templates/dcim/device/consoleports.html index ccd12f61c..6e1c1b699 100644 --- a/netbox/templates/dcim/device/consoleports.html +++ b/netbox/templates/dcim/device/consoleports.html @@ -1,57 +1,27 @@ -{% extends 'dcim/device/base.html' %} -{% load render_table from django_tables2 %} +{% extends 'dcim/device/components_base.html' %} {% load helpers %} -{% load static %} -{% block content %} - {% include 'inc/table_controls_htmx.html' with table_modal="DeviceConsolePortTable_config" %} - -
- {% csrf_token %} - -
-
- {% include 'htmx/table.html' %} -
-
- -
-
- {% if 'bulk_edit' in actions %} -
- - -
{% endif %} -
- {% if 'bulk_delete' in actions %} - - {% endif %} - {% if 'bulk_edit' in actions %} - - {% endif %} -
-
- {% if perms.dcim.add_consoleport %} - - {% endif %} -
-
-{% endblock %} + {% endwith %} +{% endblock bulk_delete_controls %} -{% block modals %} - {{ block.super }} - {% table_config_form table %} -{% endblock modals %} +{% block bulk_extra_controls %} + {{ block.super }} + {% if perms.dcim.add_consoleport %} + + {% endif %} +{% endblock bulk_extra_controls %} diff --git a/netbox/templates/dcim/device/consoleserverports.html b/netbox/templates/dcim/device/consoleserverports.html index 43396651d..637f06118 100644 --- a/netbox/templates/dcim/device/consoleserverports.html +++ b/netbox/templates/dcim/device/consoleserverports.html @@ -1,57 +1,27 @@ -{% extends 'dcim/device/base.html' %} -{% load render_table from django_tables2 %} +{% extends 'dcim/device/components_base.html' %} {% load helpers %} -{% load static %} -{% block content %} - {% include 'inc/table_controls_htmx.html' with table_modal="DeviceConsoleServerPortTable_config" %} - -
- {% csrf_token %} - -
-
- {% include 'htmx/table.html' %} -
-
- -
-
- {% if 'bulk_edit' in actions %} -
- - -
{% endif %} -
- {% if 'bulk_delete' in actions %} - - {% endif %} - {% if 'bulk_edit' in actions %} - - {% endif %} -
-
- {% if perms.dcim.add_consoleserverport %} - - {% endif %} -
-
-{% endblock %} + {% endwith %} +{% endblock bulk_delete_controls %} -{% block modals %} - {{ block.super }} - {% table_config_form table %} -{% endblock modals %} +{% block bulk_extra_controls %} + {{ block.super }} + {% if perms.dcim.add_consoleserverport %} + + {% endif %} +{% endblock bulk_extra_controls %} \ No newline at end of file diff --git a/netbox/templates/dcim/device/devicebays.html b/netbox/templates/dcim/device/devicebays.html index 9453b9a59..0a7bbba7f 100644 --- a/netbox/templates/dcim/device/devicebays.html +++ b/netbox/templates/dcim/device/devicebays.html @@ -1,50 +1,13 @@ -{% extends 'dcim/device/base.html' %} -{% load render_table from django_tables2 %} -{% load helpers %} -{% load static %} +{% extends 'dcim/device/components_base.html' %} -{% block content %} - {% include 'inc/table_controls_htmx.html' with table_modal="DeviceDeviceBayTable_config" %} - -
- {% csrf_token %} - -
-
- {% include 'htmx/table.html' %} -
-
- -
-
- {% if 'bulk_edit' in actions %} -
- - -
- {% endif %} - {% if 'bulk_delete' in actions %} - - {% endif %} -
- {% if perms.dcim.add_devicebay %} +{% block bulk_extra_controls %} + {{ block.super }} + {% if perms.dcim.add_devicebay %} - {% endif %} -
-
-{% endblock %} - -{% block modals %} - {{ block.super }} - {% table_config_form table %} -{% endblock modals %} + {% endif %} +{% endblock bulk_extra_controls %} diff --git a/netbox/templates/dcim/device/frontports.html b/netbox/templates/dcim/device/frontports.html index dd0767d95..453064611 100644 --- a/netbox/templates/dcim/device/frontports.html +++ b/netbox/templates/dcim/device/frontports.html @@ -1,57 +1,27 @@ -{% extends 'dcim/device/base.html' %} -{% load render_table from django_tables2 %} +{% extends 'dcim/device/components_base.html' %} {% load helpers %} -{% load static %} -{% block content %} - {% include 'inc/table_controls_htmx.html' with table_modal="DeviceFrontPortTable_config" %} - -
- {% csrf_token %} - -
-
- {% include 'htmx/table.html' %} -
-
- -
-
- {% if 'bulk_edit' in actions %} -
- - -
{% endif %} -
- {% if 'bulk_delete' in actions %} - - {% endif %} - {% if 'bulk_edit' in actions %} - - {% endif %} -
-
- {% if perms.dcim.add_frontport %} - - {% endif %} -
-
-{% endblock %} + {% endwith %} +{% endblock bulk_delete_controls %} -{% block modals %} - {{ block.super }} - {% table_config_form table %} -{% endblock modals %} +{% block bulk_extra_controls %} + {{ block.super }} + {% if perms.dcim.add_frontport %} + + {% endif %} +{% endblock bulk_extra_controls %} \ No newline at end of file diff --git a/netbox/templates/dcim/device/interfaces.html b/netbox/templates/dcim/device/interfaces.html index c0e9a38b6..778101265 100644 --- a/netbox/templates/dcim/device/interfaces.html +++ b/netbox/templates/dcim/device/interfaces.html @@ -1,66 +1,27 @@ -{% extends 'dcim/device/base.html' %} -{% load render_table from django_tables2 %} +{% extends 'dcim/device/components_base.html' %} {% load helpers %} -{% load static %} -{% block content %} - {% include 'dcim/device/inc/interface_table_controls.html' with table_modal="DeviceInterfaceTable_config" %} - -
- {% csrf_token %} - -
-
- {% include 'htmx/table.html' %} -
-
- -
-
- {% if 'bulk_edit' in actions %} -
- - -
- {% endif %} -
- {% if 'bulk_delete' in actions %} - +{% block bulk_delete_controls %} + {{ block.super }} + {% with bulk_disconnect_view=child_model|validated_viewname:"bulk_disconnect" %} + {% if 'bulk_disconnect' in actions and bulk_disconnect_view %} + {% endif %} - {% if 'bulk_edit' in actions %} - - {% endif %} -
-
+ {% endwith %} +{% endblock bulk_delete_controls %} + +{% block bulk_extra_controls %} + {{ block.super }} {% if perms.dcim.add_interface %} - + {% endif %} -
-
-{% endblock %} - -{% block modals %} - {{ block.super }} - {% table_config_form table %} -{% endblock modals %} +{% endblock bulk_extra_controls %} diff --git a/netbox/templates/dcim/device/inventory.html b/netbox/templates/dcim/device/inventory.html index 9e11031ec..d4c9a9b68 100644 --- a/netbox/templates/dcim/device/inventory.html +++ b/netbox/templates/dcim/device/inventory.html @@ -1,50 +1,13 @@ -{% extends 'dcim/device/base.html' %} -{% load render_table from django_tables2 %} -{% load helpers %} -{% load static %} +{% extends 'dcim/device/components_base.html' %} -{% block content %} - {% include 'inc/table_controls_htmx.html' with table_modal="DeviceInventoryItemTable_config" %} - -
- {% csrf_token %} - -
-
- {% include 'htmx/table.html' %} -
-
- -
-
- {% if 'bulk_edit' in actions %} -
- - -
- {% endif %} - {% if 'bulk_delete' in actions %} - - {% endif %} -
- {% if perms.dcim.add_inventoryitem %} +{% block bulk_extra_controls %} + {{ block.super }} + {% if perms.dcim.add_inventoryitem %} - {% endif %} -
-
-{% endblock %} - -{% block modals %} - {{ block.super }} - {% table_config_form table %} -{% endblock modals %} + {% endif %} +{% endblock bulk_extra_controls %} diff --git a/netbox/templates/dcim/device/modulebays.html b/netbox/templates/dcim/device/modulebays.html index 7f0aacf1f..fc616f828 100644 --- a/netbox/templates/dcim/device/modulebays.html +++ b/netbox/templates/dcim/device/modulebays.html @@ -1,46 +1,13 @@ -{% extends 'dcim/device/base.html' %} -{% load render_table from django_tables2 %} -{% load helpers %} -{% load static %} +{% extends 'dcim/device/components_base.html' %} -{% block content %} - {% include 'inc/table_controls_htmx.html' with table_modal="DeviceModuleBayTable_config" %} - -
- {% csrf_token %} - -
-
- {% include 'htmx/table.html' %} -
-
- -
-
- {% if 'bulk_edit' in actions %} -
- - -
- {% endif %} - {% if 'bulk_delete' in actions %} - - {% endif %} -
- {% if perms.dcim.add_modulebay %} +{% block bulk_extra_controls %} + {{ block.super }} + {% if perms.dcim.add_modulebay %} - {% endif %} -
-
- {% table_config_form table %} -{% endblock %} + {% endif %} +{% endblock bulk_extra_controls %} \ No newline at end of file diff --git a/netbox/templates/dcim/device/poweroutlets.html b/netbox/templates/dcim/device/poweroutlets.html index 66b21b7af..f31067453 100644 --- a/netbox/templates/dcim/device/poweroutlets.html +++ b/netbox/templates/dcim/device/poweroutlets.html @@ -1,57 +1,27 @@ -{% extends 'dcim/device/base.html' %} -{% load render_table from django_tables2 %} +{% extends 'dcim/device/components_base.html' %} {% load helpers %} -{% load static %} -{% block content %} - {% include 'inc/table_controls_htmx.html' with table_modal="DevicePowerOutletTable_config" %} - -
- {% csrf_token %} - -
-
- {% include 'htmx/table.html' %} -
-
- -
-
- {% if 'bulk_edit' in actions %} -
- - -
{% endif %} -
- {% if 'bulk_delete' in actions %} - - {% endif %} - {% if 'bulk_edit' in actions %} - - {% endif %} -
-
- {% if perms.dcim.add_poweroutlet %} - - {% endif %} -
-
-{% endblock %} + {% endwith %} +{% endblock bulk_delete_controls %} -{% block modals %} - {{ block.super }} - {% table_config_form table %} -{% endblock modals %} +{% block bulk_extra_controls %} + {{ block.super }} + {% if perms.dcim.add_poweroutlet %} + + {% endif %} +{% endblock bulk_extra_controls %} diff --git a/netbox/templates/dcim/device/powerports.html b/netbox/templates/dcim/device/powerports.html index d9e1e121a..ad1dbacd8 100644 --- a/netbox/templates/dcim/device/powerports.html +++ b/netbox/templates/dcim/device/powerports.html @@ -1,57 +1,27 @@ -{% extends 'dcim/device/base.html' %} -{% load render_table from django_tables2 %} +{% extends 'dcim/device/components_base.html' %} {% load helpers %} -{% load static %} -{% block content %} - {% include 'inc/table_controls_htmx.html' with table_modal="DevicePowerPortTable_config" %} - -
- {% csrf_token %} - -
-
- {% include 'htmx/table.html' %} -
-
- -
-
- {% if 'bulk_edit' in actions %} -
- - -
{% endif %} -
- {% if 'bulk_delete' in actions %} - - {% endif %} - {% if 'bulk_edit' in actions %} - - {% endif %} -
-
- {% if perms.dcim.add_powerport %} - - {% endif %} -
-
-{% endblock %} + {% endwith %} +{% endblock bulk_delete_controls %} -{% block modals %} - {{ block.super }} - {% table_config_form table %} -{% endblock modals %} +{% block bulk_extra_controls %} + {{ block.super }} + {% if perms.dcim.add_powerport %} + + {% endif %} +{% endblock bulk_extra_controls %} diff --git a/netbox/templates/dcim/device/rearports.html b/netbox/templates/dcim/device/rearports.html index ce194cc78..dfa406386 100644 --- a/netbox/templates/dcim/device/rearports.html +++ b/netbox/templates/dcim/device/rearports.html @@ -1,57 +1,27 @@ -{% extends 'dcim/device/base.html' %} -{% load render_table from django_tables2 %} -{% load static %} +{% extends 'dcim/device/components_base.html' %} {% load helpers %} -{% block content %} - {% include 'inc/table_controls_htmx.html' with table_modal="DeviceRearPortTable_config" %} - -
- {% csrf_token %} - -
-
- {% include 'htmx/table.html' %} -
-
- -
-
- {% if 'bulk_edit' in actions %} -
- - -
{% endif %} -
- {% if 'bulk_delete' in actions %} - - {% endif %} - {% if 'bulk_edit' in actions %} - - {% endif %} -
-
- {% if perms.dcim.add_rearport %} - - {% endif %} -
-
-{% endblock %} + {% endwith %} +{% endblock bulk_delete_controls %} -{% block modals %} - {{ block.super }} - {% table_config_form table %} -{% endblock modals %} +{% block bulk_extra_controls %} + {{ block.super }} + {% if perms.dcim.add_rearport %} + + {% endif %} +{% endblock bulk_extra_controls %} \ No newline at end of file diff --git a/netbox/templates/dcim/rack/non_racked_devices.html b/netbox/templates/dcim/rack/non_racked_devices.html index 700c66369..e52b8647f 100644 --- a/netbox/templates/dcim/rack/non_racked_devices.html +++ b/netbox/templates/dcim/rack/non_racked_devices.html @@ -1,5 +1,4 @@ -{% extends 'dcim/rack/base.html' %} -{% load helpers %} +{% extends 'generic/object_children.html' %} {% block extra_controls %} {% if perms.dcim.add_device %} @@ -10,42 +9,4 @@ {% endif %} -{% endblock %} - -{% block content %} - {% include 'inc/table_controls_htmx.html' with table_modal="DeviceTable_config" %} - -
- {% csrf_token %} - -
-
- {% include 'htmx/table.html' %} -
-
- -
-
- {% if 'bulk_edit' in actions %} - - {% endif %} - {% if 'bulk_delete' in actions %} - - {% endif %} -
-
-
-{% endblock content %} - -{% block modals %} - {{ block.super }} - {% table_config_form table %} -{% endblock modals %} +{% endblock extra_controls %} diff --git a/netbox/templates/dcim/rack/reservations.html b/netbox/templates/dcim/rack/reservations.html index fb357e592..a01cf3b7e 100644 --- a/netbox/templates/dcim/rack/reservations.html +++ b/netbox/templates/dcim/rack/reservations.html @@ -1,43 +1,12 @@ -{% extends 'dcim/rack/base.html' %} -{% load helpers %} +{% extends 'generic/object_children.html' %} -{% block content %} - {% include 'inc/table_controls_htmx.html' with table_modal="RackReservationTable_config" %} - -
- {% csrf_token %} - -
-
- {% include 'htmx/table.html' %} -
-
- -
-
- {% if 'bulk_edit' in actions %} - - {% endif %} - {% if 'bulk_delete' in actions %} - - {% endif %} -
- {% if perms.dcim.add_rackreservation %} +{% block extra_controls %} + {% if perms.dcim.add_rackreservation %} - {% endif %} -
-
-{% endblock %} - -{% block modals %} - {{ block.super }} - {% table_config_form table %} -{% endblock modals %} + {% endif %} +{% endblock extra_controls %} diff --git a/netbox/templates/generic/object_children.html b/netbox/templates/generic/object_children.html new file mode 100644 index 000000000..eb5c65827 --- /dev/null +++ b/netbox/templates/generic/object_children.html @@ -0,0 +1,57 @@ +{% extends base_template %} +{% load helpers %} + +{% block content %} + {% include 'inc/table_controls_htmx.html' with table_modal=table_config %} +
+ {% csrf_token %} +
+
+ {% include 'htmx/table.html' %} +
+
+
+ {% block bulk_controls %} +
+
+ {# Bulk edit buttons #} + {% block bulk_edit_controls %} + {% with bulk_edit_view=child_model|validated_viewname:"bulk_edit" %} + {% if 'bulk_edit' in actions and bulk_edit_view %} + + {% endif %} + {% endwith %} + {% endblock bulk_edit_controls %} +
+
+ {# Bulk delete buttons #} + {% block bulk_delete_controls %} + {% with bulk_delete_view=child_model|validated_viewname:"bulk_delete" %} + {% if 'bulk_delete' in actions and bulk_delete_view %} + + {% endif %} + {% endwith %} + {% endblock bulk_delete_controls %} +
+
+
+ {# Other bulk action buttons #} + {% block bulk_extra_controls %}{% endblock %} +
+ {% endblock bulk_controls %} +
+
+{% endblock content %} + +{% block modals %} + {{ block.super }} + {% table_config_form table %} +{% endblock modals %} diff --git a/netbox/templates/ipam/aggregate/prefixes.html b/netbox/templates/ipam/aggregate/prefixes.html index a1d3bd276..7820e121e 100644 --- a/netbox/templates/ipam/aggregate/prefixes.html +++ b/netbox/templates/ipam/aggregate/prefixes.html @@ -1,5 +1,4 @@ -{% extends 'ipam/aggregate/base.html' %} -{% load helpers %} +{% extends 'generic/object_children.html' %} {% block extra_controls %} {% include 'ipam/inc/toggle_available.html' %} @@ -9,38 +8,4 @@ {% endif %} {{ block.super }} -{% endblock %} - -{% block content %} - {% include 'inc/table_controls_htmx.html' with table_modal="PrefixTable_config" %} - -
- {% csrf_token %} - -
-
- {% include 'htmx/table.html' %} -
-
- -
-
- {% if 'bulk_edit' in actions %} - - {% endif %} - {% if 'bulk_delete' in actions %} - - {% endif %} -
-
-
-{% endblock %} - -{% block modals %} - {{ block.super }} - {% table_config_form table %} -{% endblock modals %} +{% endblock extra_controls %} diff --git a/netbox/templates/ipam/asnrange/asns.html b/netbox/templates/ipam/asnrange/asns.html deleted file mode 100644 index 69d4e8abb..000000000 --- a/netbox/templates/ipam/asnrange/asns.html +++ /dev/null @@ -1,36 +0,0 @@ -{% extends 'ipam/asnrange/base.html' %} -{% load helpers %} - -{% block content %} - {% include 'inc/table_controls_htmx.html' with table_modal="ASNTable_config" %} - -
- {% csrf_token %} - -
-
- {% include 'htmx/table.html' %} -
-
- -
-
- {% if 'bulk_edit' in actions %} - - {% endif %} - {% if 'bulk_delete' in actions %} - - {% endif %} -
-
-
-{% endblock %} - -{% block modals %} - {{ block.super }} - {% table_config_form table %} -{% endblock modals %} diff --git a/netbox/templates/ipam/ipaddress/ip_addresses.html b/netbox/templates/ipam/ipaddress/ip_addresses.html deleted file mode 100644 index b82ec2375..000000000 --- a/netbox/templates/ipam/ipaddress/ip_addresses.html +++ /dev/null @@ -1,19 +0,0 @@ -{% extends 'ipam/ipaddress/base.html' %} -{% load helpers %} - -{% block content %} - {% include 'inc/table_controls_htmx.html' with table_modal="IPAddressTable_config" %} -
- {% csrf_token %} -
-
- {% include 'htmx/table.html' %} -
-
-
-{% endblock %} - -{% block modals %} - {{ block.super }} - {% table_config_form table %} -{% endblock modals %} diff --git a/netbox/templates/ipam/iprange/ip_addresses.html b/netbox/templates/ipam/iprange/ip_addresses.html index 9f77f6c78..869fd0fa1 100644 --- a/netbox/templates/ipam/iprange/ip_addresses.html +++ b/netbox/templates/ipam/iprange/ip_addresses.html @@ -1,44 +1,9 @@ -{% extends 'ipam/iprange/base.html' %} -{% load helpers %} +{% extends 'generic/object_children.html' %} {% block extra_controls %} - {% if perms.ipam.add_ipaddress and active_tab == 'ip-addresses' and object.first_available_ip %} + {% if perms.ipam.add_ipaddress and object.first_available_ip %} Add IP Address {% endif %} -{% endblock %} - -{% block content %} - {% include 'inc/table_controls_htmx.html' with table_modal="IPAddressTable_config" %} - -
- {% csrf_token %} - -
-
- {% include 'htmx/table.html' %} -
-
- -
-
- {% if 'bulk_edit' in actions %} - - {% endif %} - {% if 'bulk_delete' in actions %} - - {% endif %} -
-
-
-{% endblock %} - -{% block modals %} - {{ block.super }} - {% table_config_form table %} -{% endblock modals %} +{% endblock extra_controls %} diff --git a/netbox/templates/ipam/prefix/ip_addresses.html b/netbox/templates/ipam/prefix/ip_addresses.html index fe68039f8..f9d5febbe 100644 --- a/netbox/templates/ipam/prefix/ip_addresses.html +++ b/netbox/templates/ipam/prefix/ip_addresses.html @@ -1,5 +1,4 @@ -{% extends 'ipam/prefix/base.html' %} -{% load helpers %} +{% extends 'generic/object_children.html' %} {% block extra_controls %} {% if perms.ipam.add_ipaddress and first_available_ip %} @@ -7,38 +6,4 @@ Add IP Address {% endif %} -{% endblock %} - -{% block content %} - {% include 'inc/table_controls_htmx.html' with table_modal="IPAddressTable_config" %} - -
- {% csrf_token %} - -
-
- {% include 'htmx/table.html' %} -
-
- -
-
- {% if 'bulk_edit' in actions %} - - {% endif %} - {% if 'bulk_delete' in actions %} - - {% endif %} -
-
-
-{% endblock %} - -{% block modals %} - {{ block.super }} - {% table_config_form table %} -{% endblock modals %} +{% endblock extra_controls %} diff --git a/netbox/templates/ipam/prefix/ip_ranges.html b/netbox/templates/ipam/prefix/ip_ranges.html index 4452fd5a7..8371de81d 100644 --- a/netbox/templates/ipam/prefix/ip_ranges.html +++ b/netbox/templates/ipam/prefix/ip_ranges.html @@ -1,5 +1,4 @@ -{% extends 'ipam/prefix/base.html' %} -{% load helpers %} +{% extends 'generic/object_children.html' %} {% block extra_controls %} {% if perms.ipam.add_iprange and first_available_ip %} @@ -7,38 +6,4 @@ Add IP Range {% endif %} -{% endblock %} - -{% block content %} - {% include 'inc/table_controls_htmx.html' with table_modal="IPRangeTable_config" %} - -
- {% csrf_token %} - -
-
- {% include 'htmx/table.html' %} -
-
- -
-
- {% if 'bulk_edit' in actions %} - - {% endif %} - {% if 'bulk_delete' in actions %} - - {% endif %} -
-
-
-{% endblock %} - -{% block modals %} - {{ block.super }} - {% table_config_form table %} -{% endblock modals %} +{% endblock extra_controls %} diff --git a/netbox/templates/ipam/prefix/prefixes.html b/netbox/templates/ipam/prefix/prefixes.html index 5fc931f74..41407e870 100644 --- a/netbox/templates/ipam/prefix/prefixes.html +++ b/netbox/templates/ipam/prefix/prefixes.html @@ -1,5 +1,4 @@ -{% extends 'ipam/prefix/base.html' %} -{% load helpers %} +{% extends 'generic/object_children.html' %} {% block extra_controls %} {% include 'ipam/inc/toggle_available.html' %} @@ -8,39 +7,4 @@ Add Prefix {% endif %} - {{ block.super }} -{% endblock %} - -{% block content %} - {% include 'inc/table_controls_htmx.html' with table_modal="PrefixTable_config" %} - -
- {% csrf_token %} - -
-
- {% include 'htmx/table.html' %} -
-
- -
-
- {% if 'bulk_edit' in actions %} - - {% endif %} - {% if 'bulk_delete' in actions %} - - {% endif %} -
-
-
-{% endblock %} - -{% block modals %} - {{ block.super }} - {% table_config_form table %} -{% endblock modals %} +{% endblock extra_controls %} diff --git a/netbox/templates/ipam/vlan/interfaces.html b/netbox/templates/ipam/vlan/interfaces.html deleted file mode 100644 index f7bcc8563..000000000 --- a/netbox/templates/ipam/vlan/interfaces.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends 'ipam/vlan/base.html' %} -{% load helpers %} - -{% block content %} - {% include 'inc/table_controls_htmx.html' with table_modal="VLANDevicesTable_config" %} - -
- {% csrf_token %} -
-
- {% include 'htmx/table.html' %} -
-
-
-{% endblock content %} - -{% block modals %} - {{ block.super }} - {% table_config_form table %} -{% endblock modals %} diff --git a/netbox/templates/ipam/vlan/vminterfaces.html b/netbox/templates/ipam/vlan/vminterfaces.html deleted file mode 100644 index a485b33eb..000000000 --- a/netbox/templates/ipam/vlan/vminterfaces.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends 'ipam/vlan/base.html' %} -{% load helpers %} - -{% block content %} - {% include 'inc/table_controls_htmx.html' with table_modal="VLANVirtualMachinesTable_config" %} - -
- {% csrf_token %} -
-
- {% include 'htmx/table.html' %} -
-
-
-{% endblock content %} - -{% block modals %} - {{ block.super }} - {% table_config_form table %} -{% endblock modals %} diff --git a/netbox/templates/tenancy/object_contacts.html b/netbox/templates/tenancy/object_contacts.html index e13fedc43..95727604c 100644 --- a/netbox/templates/tenancy/object_contacts.html +++ b/netbox/templates/tenancy/object_contacts.html @@ -1,4 +1,4 @@ -{% extends base_template %} +{% extends 'generic/object_children.html' %} {% load helpers %} {% block extra_controls %} @@ -10,20 +10,3 @@ {% endwith %} {% endif %} {% endblock %} - -{% block content %} - {% include 'inc/table_controls_htmx.html' with table_modal="ContactAssignmentTable_config" %} -
- {% csrf_token %} -
-
- {% include 'htmx/table.html' %} -
-
-
-{% endblock content %} - -{% block modals %} - {{ block.super }} - {% table_config_form table %} -{% endblock modals %} diff --git a/netbox/templates/virtualization/cluster/devices.html b/netbox/templates/virtualization/cluster/devices.html index 083798233..271240ed1 100644 --- a/netbox/templates/virtualization/cluster/devices.html +++ b/netbox/templates/virtualization/cluster/devices.html @@ -1,30 +1,13 @@ -{% extends 'virtualization/cluster/base.html' %} +{% extends 'generic/object_children.html' %} {% load helpers %} -{% load render_table from django_tables2 %} -{% block content %} - {% include 'inc/table_controls_htmx.html' with table_modal="DeviceTable_config" %} - -
- {% csrf_token %} -
-
- {% include 'htmx/table.html' %} -
-
-
-
- {% if perms.virtualization.change_cluster %} - - {% endif %} -
-
-
-{% endblock content %} - -{% block modals %} - {{ block.super }} - {% table_config_form table %} -{% endblock modals %} + + {% endif %} +{% endblock bulk_delete_controls %} diff --git a/netbox/templates/virtualization/cluster/virtual_machines.html b/netbox/templates/virtualization/cluster/virtual_machines.html deleted file mode 100644 index 79c489d6b..000000000 --- a/netbox/templates/virtualization/cluster/virtual_machines.html +++ /dev/null @@ -1,35 +0,0 @@ -{% extends 'virtualization/cluster/base.html' %} -{% load helpers %} -{% load render_table from django_tables2 %} - -{% block content %} - {% include 'inc/table_controls_htmx.html' with table_modal="VirtualMachineTable_config" %} - -
- {% csrf_token %} -
-
- {% include 'htmx/table.html' %} -
-
-
-
- {% if 'bulk_edit' in actions %} - - {% endif %} - {% if 'bulk_delete' in actions %} - - {% endif %} -
-
-
-{% endblock content %} - -{% block modals %} - {{ block.super }} - {% table_config_form table %} -{% endblock modals %} diff --git a/netbox/templates/virtualization/virtualmachine/interfaces.html b/netbox/templates/virtualization/virtualmachine/interfaces.html index 71456d104..ee4e76926 100644 --- a/netbox/templates/virtualization/virtualmachine/interfaces.html +++ b/netbox/templates/virtualization/virtualmachine/interfaces.html @@ -1,47 +1,13 @@ -{% extends 'virtualization/virtualmachine/base.html' %} -{% load render_table from django_tables2 %} +{% extends 'generic/object_children.html' %} {% load helpers %} -{% block content %} - {% include 'inc/table_controls_htmx.html' with table_modal="VirtualMachineVMInterfaceTable_config" %} - -
- {% csrf_token %} - -
-
- {% include 'htmx/table.html' %} -
-
- -
- {% if perms.virtualization.change_vminterface %} -
- - -
- {% endif %} - {% if perms.virtualization.delete_vminterface %} - - {% endif %} - {% if perms.virtualization.add_vminterface %} - - {% endif %} -
-
-{% endblock content %} - -{% block modals %} - {{ block.super }} - {% table_config_form table %} -{% endblock modals %} + {% endif %} +{% endblock bulk_edit_controls %} diff --git a/netbox/tenancy/views.py b/netbox/tenancy/views.py index 23020e794..3025e7e04 100644 --- a/netbox/tenancy/views.py +++ b/netbox/tenancy/views.py @@ -41,11 +41,6 @@ class ObjectContactsView(generic.ObjectChildrenView): return table - def get_extra_context(self, request, instance): - return { - 'base_template': f'{instance._meta.app_label}/{instance._meta.model_name}.html', - } - # # Tenant groups # diff --git a/netbox/virtualization/views.py b/netbox/virtualization/views.py index 75e83f9e1..92a91f47e 100644 --- a/netbox/virtualization/views.py +++ b/netbox/virtualization/views.py @@ -1,3 +1,5 @@ +from collections import defaultdict + from django.contrib import messages from django.db import transaction from django.db.models import Prefetch, Sum @@ -175,7 +177,7 @@ class ClusterVirtualMachinesView(generic.ObjectChildrenView): child_model = VirtualMachine table = tables.VirtualMachineTable filterset = filtersets.VirtualMachineFilterSet - template_name = 'virtualization/cluster/virtual_machines.html' + template_name = 'generic/object_children.html' tab = ViewTab( label=_('Virtual Machines'), badge=lambda obj: obj.virtual_machines.count(), @@ -194,6 +196,13 @@ class ClusterDevicesView(generic.ObjectChildrenView): table = DeviceTable filterset = DeviceFilterSet template_name = 'virtualization/cluster/devices.html' + actions = ('add', 'import', 'export', 'bulk_edit', 'bulk_remove_devices') + action_perms = defaultdict(set, **{ + 'add': {'add'}, + 'import': {'add'}, + 'bulk_edit': {'change'}, + 'bulk_remove_devices': {'change'}, + }) tab = ViewTab( label=_('Devices'), badge=lambda obj: obj.devices.count(), @@ -353,6 +362,14 @@ class VirtualMachineInterfacesView(generic.ObjectChildrenView): permission='virtualization.view_vminterface', weight=500 ) + actions = ('add', 'import', 'export', 'bulk_edit', 'bulk_delete', 'bulk_rename') + action_perms = defaultdict(set, **{ + 'add': {'add'}, + 'import': {'add'}, + 'bulk_edit': {'change'}, + 'bulk_delete': {'delete'}, + 'bulk_rename': {'change'}, + }) def get_children(self, request, parent): return parent.interfaces.restrict(request.user, 'view').prefetch_related( From 20edfdbe96041e8d599e5b7ed06f766db5ebf539 Mon Sep 17 00:00:00 2001 From: kkthxbye-code Date: Wed, 9 Aug 2023 11:56:30 +0200 Subject: [PATCH 19/31] Don't hide HIDDEN_IFUNSET custom fields from bulk import fields --- netbox/netbox/forms/base.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/netbox/netbox/forms/base.py b/netbox/netbox/forms/base.py index 83c238e0f..b406ab04e 100644 --- a/netbox/netbox/forms/base.py +++ b/netbox/netbox/forms/base.py @@ -78,7 +78,10 @@ class NetBoxModelImportForm(CSVModelForm, NetBoxModelForm): def _get_custom_fields(self, content_type): return CustomField.objects.filter(content_types=content_type).filter( - ui_visibility=CustomFieldVisibilityChoices.VISIBILITY_READ_WRITE + ui_visibility__in=[ + CustomFieldVisibilityChoices.VISIBILITY_READ_WRITE, + CustomFieldVisibilityChoices.VISIBILITY_HIDDEN_IFUNSET, + ] ) def _get_form_field(self, customfield): From 391ad4f588b7bdaae385114f0c6b21111f4bfc71 Mon Sep 17 00:00:00 2001 From: Arthur Date: Wed, 9 Aug 2023 12:28:35 +0700 Subject: [PATCH 20/31] 12665 add semicolon to link sanitation safe string --- netbox/extras/models/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/extras/models/models.py b/netbox/extras/models/models.py index fcf5c26a2..c76a5a76f 100644 --- a/netbox/extras/models/models.py +++ b/netbox/extras/models/models.py @@ -285,7 +285,7 @@ class CustomLink(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): text = clean_html(text, allowed_schemes) # Sanitize link - link = urllib.parse.quote(link, safe='/:?&=%+[]@#,') + link = urllib.parse.quote(link, safe='/:?&=%+[]@#,;') # Verify link scheme is allowed result = urllib.parse.urlparse(link) From f3d60e2121900df5232cc732e519a10aa1040e20 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 9 Aug 2023 15:02:49 -0400 Subject: [PATCH 21/31] Changelog for #11675, #11922, #12665, #13368, #13414 --- docs/release-notes/version-3.5.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/release-notes/version-3.5.md b/docs/release-notes/version-3.5.md index 4347d9837..cf47a3b23 100644 --- a/docs/release-notes/version-3.5.md +++ b/docs/release-notes/version-3.5.md @@ -4,15 +4,20 @@ ### Enhancements +* [#11675](https://github.com/netbox-community/netbox/issues/11675) - Add support for specifying import/export route targets during VRF bulk import +* [#11922](https://github.com/netbox-community/netbox/issues/11922) - Automatically populate any VDC assignments from the parent when adding a child interface via the UI * [#12889](https://github.com/netbox-community/netbox/issues/12889) - Add 400GE CFP2 interface type * [#13033](https://github.com/netbox-community/netbox/issues/13033) - Add human-friendly speed column to interfaces table * [#13151](https://github.com/netbox-community/netbox/issues/13151) - Add "assigned" filter for IP addresses +* [#13368](https://github.com/netbox-community/netbox/issues/13368) - List installed plugins on the server error report page ### Bug Fixes +* [#12665](https://github.com/netbox-community/netbox/issues/12665) - Avoid escaping semicolons when rendering custom links * [#12750](https://github.com/netbox-community/netbox/issues/12750) - Automatically delete an AutoSyncRecord when its object is deleted * [#13343](https://github.com/netbox-community/netbox/issues/13343) - Fix filtering of circuits under provider network view * [#13369](https://github.com/netbox-community/netbox/issues/13369) - Fix job termination status for failed reports +* [#13414](https://github.com/netbox-community/netbox/issues/13414) - Fix support for "hide-if-unset" custom fields on bulk import forms --- From d88e2d27911586bdf74e2fb32d63ca12be9072e0 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 11 Aug 2023 08:56:58 -0400 Subject: [PATCH 22/31] Fixes #13446: Don't disable bulk edit/delete buttons after deselecting "select all" checkbox --- docs/release-notes/version-3.5.md | 1 + netbox/project-static/dist/netbox.js | Bin 530613 -> 530368 bytes netbox/project-static/dist/netbox.js.map | Bin 450868 -> 450659 bytes .../project-static/src/buttons/selectAll.ts | 30 +----------------- 4 files changed, 2 insertions(+), 29 deletions(-) diff --git a/docs/release-notes/version-3.5.md b/docs/release-notes/version-3.5.md index cf47a3b23..fe0832c3b 100644 --- a/docs/release-notes/version-3.5.md +++ b/docs/release-notes/version-3.5.md @@ -18,6 +18,7 @@ * [#13343](https://github.com/netbox-community/netbox/issues/13343) - Fix filtering of circuits under provider network view * [#13369](https://github.com/netbox-community/netbox/issues/13369) - Fix job termination status for failed reports * [#13414](https://github.com/netbox-community/netbox/issues/13414) - Fix support for "hide-if-unset" custom fields on bulk import forms +* [#13446](https://github.com/netbox-community/netbox/issues/13446) - Don't disable bulk edit/delete buttons after deselecting "select all" checkbox --- diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js index b62436d757a2b299e5bf0f33dcc4fbd67ec21e8c..84bfecae34920e732ecf52670208390ce6712040 100644 GIT binary patch delta 9766 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 R=VTT5U$4QMX#G;={{R{n`nLc8 delta 9842 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 diff --git a/netbox/project-static/dist/netbox.js.map b/netbox/project-static/dist/netbox.js.map index ed3833f982a5d0f0d81c6bcf7e90a86aeb99d789..7f2400ed2610973ed909703e534ec9b73c19c8ae 100644 GIT binary patch delta 44 zcmV+{0Mq}p!W-kj8-RoXgaU*Egaot&HT?rsH$s;W>ID^-;r#_p1Vd~^cDHc;1;pZx C4H8xW delta 254 zcmYL@%?bfw6o&INYb&MP!YM_R<$I1XO&XJiG)qlHgzVU`n0uI|o0zhdTnRh(Fy%XD zVRL#;y?^g}J$=+KaUm|orMR40?d%-wg+k!SG& r%d8<_AO4F%b#|B{*bT`;&4S3Z%JLU7(OR8kvdj+Y_D{EKW<6hDCyPpW diff --git a/netbox/project-static/src/buttons/selectAll.ts b/netbox/project-static/src/buttons/selectAll.ts index 64b98d390..f40520e26 100644 --- a/netbox/project-static/src/buttons/selectAll.ts +++ b/netbox/project-static/src/buttons/selectAll.ts @@ -1,4 +1,4 @@ -import { getElement, getElements, findFirstAdjacent } from '../util'; +import { getElements, findFirstAdjacent } from '../util'; /** * If any PK checkbox is checked, uncheck the select all table checkbox and the select all @@ -63,29 +63,6 @@ function handleSelectAllToggle(event: Event): void { } } -/** - * Synchronize the select all confirmation checkbox state with the select all confirmation button - * disabled state. If the select all confirmation checkbox is checked, the buttons should be - * enabled. If not, the buttons should be disabled. - * - * @param event Change Event - */ -function handleSelectAll(event: Event): void { - const target = event.currentTarget as HTMLInputElement; - const selectAllBox = getElement('select-all-box'); - if (selectAllBox !== null) { - for (const button of selectAllBox.querySelectorAll( - 'button[type="submit"]', - )) { - if (target.checked) { - button.disabled = false; - } else { - button.disabled = true; - } - } - } -} - /** * Initialize table select all elements. */ @@ -98,9 +75,4 @@ export function initSelectAll(): void { for (const element of getElements('input[type="checkbox"][name="pk"]')) { element.addEventListener('change', handlePkCheck); } - const selectAll = getElement('select-all'); - - if (selectAll !== null) { - selectAll.addEventListener('change', handleSelectAll); - } } From 30b2760332d1fc06548ae4507f83651f8e6a5516 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Fri, 11 Aug 2023 20:49:03 +0700 Subject: [PATCH 23/31] 11578 mark swagger available- apis to accept lists in post (#13445) * 11578 change swagger for available-ips to accept lists * 11578 change swagger for available-xxx to accept lists --- netbox/ipam/api/views.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/netbox/ipam/api/views.py b/netbox/ipam/api/views.py index 99b4c023d..feffc3ff2 100644 --- a/netbox/ipam/api/views.py +++ b/netbox/ipam/api/views.py @@ -224,7 +224,10 @@ class AvailableASNsView(ObjectValidationMixin, APIView): return Response(serializer.data) - @extend_schema(methods=["post"], responses={201: serializers.ASNSerializer(many=True)}) + @extend_schema(methods=["post"], + responses={201: serializers.ASNSerializer(many=True)}, + request=serializers.ASNSerializer(many=True), + ) @advisory_lock(ADVISORY_LOCK_KEYS['available-asns']) def post(self, request, pk): self.queryset = self.queryset.restrict(request.user, 'add') @@ -293,7 +296,10 @@ class AvailablePrefixesView(ObjectValidationMixin, APIView): return Response(serializer.data) - @extend_schema(methods=["post"], responses={201: serializers.PrefixSerializer(many=True)}) + @extend_schema(methods=["post"], + responses={201: serializers.PrefixSerializer(many=True)}, + request=serializers.PrefixSerializer(many=True), + ) @advisory_lock(ADVISORY_LOCK_KEYS['available-prefixes']) def post(self, request, pk): self.queryset = self.queryset.restrict(request.user, 'add') @@ -388,7 +394,10 @@ class AvailableIPAddressesView(ObjectValidationMixin, APIView): return Response(serializer.data) - @extend_schema(methods=["post"], responses={201: serializers.IPAddressSerializer(many=True)}) + @extend_schema(methods=["post"], + responses={201: serializers.IPAddressSerializer(many=True)}, + request=serializers.IPAddressSerializer(many=True), + ) @advisory_lock(ADVISORY_LOCK_KEYS['available-ips']) def post(self, request, pk): self.queryset = self.queryset.restrict(request.user, 'add') @@ -468,7 +477,10 @@ class AvailableVLANsView(ObjectValidationMixin, APIView): return Response(serializer.data) - @extend_schema(methods=["post"], responses={201: serializers.VLANSerializer(many=True)}) + @extend_schema(methods=["post"], + responses={201: serializers.VLANSerializer(many=True)}, + request=serializers.VLANSerializer(many=True), + ) @advisory_lock(ADVISORY_LOCK_KEYS['available-vlans']) def post(self, request, pk): self.queryset = self.queryset.restrict(request.user, 'add') From bc181a7de61cb6c4c2093507f6be4e1a5a282108 Mon Sep 17 00:00:00 2001 From: "Daniel W. Anner" Date: Fri, 11 Aug 2023 11:00:26 -0400 Subject: [PATCH 24/31] Feature - Schema Generation (#13353) * Schema generation is working * Added option to either dump to a file or the console * Moving schema file and utilizing settings definition for file paths * Cleaning up the imports and fixing a few pythonic issues * Tweak command flags * Clean up choices mapping * Misc cleanup * Rename & move template file * Move management command from extras to dcim * Update release checklist --------- Co-authored-by: Jeremy Stretch --- contrib/generated_schema.json | 561 ++++++++++++++++++ docs/development/release-checklist.md | 10 + .../dcim/management/commands/buildschema.py | 62 ++ .../extras/schema/devicetype_schema.jinja2 | 93 +++ 4 files changed, 726 insertions(+) create mode 100644 contrib/generated_schema.json create mode 100644 netbox/dcim/management/commands/buildschema.py create mode 100644 netbox/templates/extras/schema/devicetype_schema.jinja2 diff --git a/contrib/generated_schema.json b/contrib/generated_schema.json new file mode 100644 index 000000000..8dbcb2847 --- /dev/null +++ b/contrib/generated_schema.json @@ -0,0 +1,561 @@ +{ + "type": "object", + "additionalProperties": false, + "definitions": { + "airflow": { + "type": "string", + "enum": [ + "front-to-rear", + "rear-to-front", + "left-to-right", + "right-to-left", + "side-to-rear", + "passive", + "mixed" + ] + }, + "weight-unit": { + "type": "string", + "enum": [ + "kg", + "g", + "lb", + "oz" + ] + }, + "subdevice-role": { + "type": "string", + "enum": [ + "parent", + "child" + ] + }, + "console-port": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "de-9", + "db-25", + "rj-11", + "rj-12", + "rj-45", + "mini-din-8", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "other" + ] + } + } + }, + "console-server-port": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "de-9", + "db-25", + "rj-11", + "rj-12", + "rj-45", + "mini-din-8", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "other" + ] + } + } + }, + "power-port": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "iec-60320-c6", + "iec-60320-c8", + "iec-60320-c14", + "iec-60320-c16", + "iec-60320-c20", + "iec-60320-c22", + "iec-60309-p-n-e-4h", + "iec-60309-p-n-e-6h", + "iec-60309-p-n-e-9h", + "iec-60309-2p-e-4h", + "iec-60309-2p-e-6h", + "iec-60309-2p-e-9h", + "iec-60309-3p-e-4h", + "iec-60309-3p-e-6h", + "iec-60309-3p-e-9h", + "iec-60309-3p-n-e-4h", + "iec-60309-3p-n-e-6h", + "iec-60309-3p-n-e-9h", + "iec-60906-1", + "nbr-14136-10a", + "nbr-14136-20a", + "nema-1-15p", + "nema-5-15p", + "nema-5-20p", + "nema-5-30p", + "nema-5-50p", + "nema-6-15p", + "nema-6-20p", + "nema-6-30p", + "nema-6-50p", + "nema-10-30p", + "nema-10-50p", + "nema-14-20p", + "nema-14-30p", + "nema-14-50p", + "nema-14-60p", + "nema-15-15p", + "nema-15-20p", + "nema-15-30p", + "nema-15-50p", + "nema-15-60p", + "nema-l1-15p", + "nema-l5-15p", + "nema-l5-20p", + "nema-l5-30p", + "nema-l5-50p", + "nema-l6-15p", + "nema-l6-20p", + "nema-l6-30p", + "nema-l6-50p", + "nema-l10-30p", + "nema-l14-20p", + "nema-l14-30p", + "nema-l14-50p", + "nema-l14-60p", + "nema-l15-20p", + "nema-l15-30p", + "nema-l15-50p", + "nema-l15-60p", + "nema-l21-20p", + "nema-l21-30p", + "nema-l22-30p", + "cs6361c", + "cs6365c", + "cs8165c", + "cs8265c", + "cs8365c", + "cs8465c", + "ita-c", + "ita-e", + "ita-f", + "ita-ef", + "ita-g", + "ita-h", + "ita-i", + "ita-j", + "ita-k", + "ita-l", + "ita-m", + "ita-n", + "ita-o", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "usb-3-b", + "usb-3-micro-b", + "dc-terminal", + "saf-d-grid", + "neutrik-powercon-20", + "neutrik-powercon-32", + "neutrik-powercon-true1", + "neutrik-powercon-true1-top", + "ubiquiti-smartpower", + "hardwired", + "other" + ] + } + } + }, + "power-outlet": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "iec-60320-c5", + "iec-60320-c7", + "iec-60320-c13", + "iec-60320-c15", + "iec-60320-c19", + "iec-60320-c21", + "iec-60309-p-n-e-4h", + "iec-60309-p-n-e-6h", + "iec-60309-p-n-e-9h", + "iec-60309-2p-e-4h", + "iec-60309-2p-e-6h", + "iec-60309-2p-e-9h", + "iec-60309-3p-e-4h", + "iec-60309-3p-e-6h", + "iec-60309-3p-e-9h", + "iec-60309-3p-n-e-4h", + "iec-60309-3p-n-e-6h", + "iec-60309-3p-n-e-9h", + "iec-60906-1", + "nbr-14136-10a", + "nbr-14136-20a", + "nema-1-15r", + "nema-5-15r", + "nema-5-20r", + "nema-5-30r", + "nema-5-50r", + "nema-6-15r", + "nema-6-20r", + "nema-6-30r", + "nema-6-50r", + "nema-10-30r", + "nema-10-50r", + "nema-14-20r", + "nema-14-30r", + "nema-14-50r", + "nema-14-60r", + "nema-15-15r", + "nema-15-20r", + "nema-15-30r", + "nema-15-50r", + "nema-15-60r", + "nema-l1-15r", + "nema-l5-15r", + "nema-l5-20r", + "nema-l5-30r", + "nema-l5-50r", + "nema-l6-15r", + "nema-l6-20r", + "nema-l6-30r", + "nema-l6-50r", + "nema-l10-30r", + "nema-l14-20r", + "nema-l14-30r", + "nema-l14-50r", + "nema-l14-60r", + "nema-l15-20r", + "nema-l15-30r", + "nema-l15-50r", + "nema-l15-60r", + "nema-l21-20r", + "nema-l21-30r", + "nema-l22-30r", + "CS6360C", + "CS6364C", + "CS8164C", + "CS8264C", + "CS8364C", + "CS8464C", + "ita-e", + "ita-f", + "ita-g", + "ita-h", + "ita-i", + "ita-j", + "ita-k", + "ita-l", + "ita-m", + "ita-n", + "ita-o", + "ita-multistandard", + "usb-a", + "usb-micro-b", + "usb-c", + "dc-terminal", + "hdot-cx", + "saf-d-grid", + "neutrik-powercon-20a", + "neutrik-powercon-32a", + "neutrik-powercon-true1", + "neutrik-powercon-true1-top", + "ubiquiti-smartpower", + "hardwired", + "other" + ] + }, + "feed-leg": { + "type": "string", + "enum": [ + "A", + "B", + "C" + ] + } + } + }, + "interface": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "virtual", + "bridge", + "lag", + "100base-fx", + "100base-lfx", + "100base-tx", + "100base-t1", + "1000base-t", + "2.5gbase-t", + "5gbase-t", + "10gbase-t", + "10gbase-cx4", + "1000base-x-gbic", + "1000base-x-sfp", + "10gbase-x-sfpp", + "10gbase-x-xfp", + "10gbase-x-xenpak", + "10gbase-x-x2", + "25gbase-x-sfp28", + "50gbase-x-sfp56", + "40gbase-x-qsfpp", + "50gbase-x-sfp28", + "100gbase-x-cfp", + "100gbase-x-cfp2", + "200gbase-x-cfp2", + "100gbase-x-cfp4", + "100gbase-x-cxp", + "100gbase-x-cpak", + "100gbase-x-dsfp", + "100gbase-x-sfpdd", + "100gbase-x-qsfp28", + "100gbase-x-qsfpdd", + "200gbase-x-qsfp56", + "200gbase-x-qsfpdd", + "400gbase-x-qsfpdd", + "400gbase-x-osfp", + "400gbase-x-cdfp", + "400gbase-x-cfp8", + "800gbase-x-qsfpdd", + "800gbase-x-osfp", + "1000base-kx", + "10gbase-kr", + "10gbase-kx4", + "25gbase-kr", + "40gbase-kr4", + "50gbase-kr", + "100gbase-kp4", + "100gbase-kr2", + "100gbase-kr4", + "ieee802.11a", + "ieee802.11g", + "ieee802.11n", + "ieee802.11ac", + "ieee802.11ad", + "ieee802.11ax", + "ieee802.11ay", + "ieee802.15.1", + "other-wireless", + "gsm", + "cdma", + "lte", + "sonet-oc3", + "sonet-oc12", + "sonet-oc48", + "sonet-oc192", + "sonet-oc768", + "sonet-oc1920", + "sonet-oc3840", + "1gfc-sfp", + "2gfc-sfp", + "4gfc-sfp", + "8gfc-sfpp", + "16gfc-sfpp", + "32gfc-sfp28", + "64gfc-qsfpp", + "128gfc-qsfp28", + "infiniband-sdr", + "infiniband-ddr", + "infiniband-qdr", + "infiniband-fdr10", + "infiniband-fdr", + "infiniband-edr", + "infiniband-hdr", + "infiniband-ndr", + "infiniband-xdr", + "t1", + "e1", + "t3", + "e3", + "xdsl", + "docsis", + "gpon", + "xg-pon", + "xgs-pon", + "ng-pon2", + "epon", + "10g-epon", + "cisco-stackwise", + "cisco-stackwise-plus", + "cisco-flexstack", + "cisco-flexstack-plus", + "cisco-stackwise-80", + "cisco-stackwise-160", + "cisco-stackwise-320", + "cisco-stackwise-480", + "cisco-stackwise-1t", + "juniper-vcp", + "extreme-summitstack", + "extreme-summitstack-128", + "extreme-summitstack-256", + "extreme-summitstack-512", + "other" + ] + }, + "poe_mode": { + "type": "string", + "enum": [ + "pd", + "pse" + ] + }, + "poe_type": { + "type": "string", + "enum": [ + "type1-ieee802.3af", + "type2-ieee802.3at", + "type3-ieee802.3bt", + "type4-ieee802.3bt", + "passive-24v-2pair", + "passive-24v-4pair", + "passive-48v-2pair", + "passive-48v-4pair" + ] + } + } + }, + "front-port": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "8p8c", + "8p6c", + "8p4c", + "8p2c", + "6p6c", + "6p4c", + "6p2c", + "4p4c", + "4p2c", + "gg45", + "tera-4p", + "tera-2p", + "tera-1p", + "110-punch", + "bnc", + "f", + "n", + "mrj21", + "fc", + "lc", + "lc-pc", + "lc-upc", + "lc-apc", + "lsh", + "lsh-pc", + "lsh-upc", + "lsh-apc", + "lx5", + "lx5-pc", + "lx5-upc", + "lx5-apc", + "mpo", + "mtrj", + "sc", + "sc-pc", + "sc-upc", + "sc-apc", + "st", + "cs", + "sn", + "sma-905", + "sma-906", + "urm-p2", + "urm-p4", + "urm-p8", + "splice", + "other" + ] + } + } + }, + "rear-port": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "8p8c", + "8p6c", + "8p4c", + "8p2c", + "6p6c", + "6p4c", + "6p2c", + "4p4c", + "4p2c", + "gg45", + "tera-4p", + "tera-2p", + "tera-1p", + "110-punch", + "bnc", + "f", + "n", + "mrj21", + "fc", + "lc", + "lc-pc", + "lc-upc", + "lc-apc", + "lsh", + "lsh-pc", + "lsh-upc", + "lsh-apc", + "lx5", + "lx5-pc", + "lx5-upc", + "lx5-apc", + "mpo", + "mtrj", + "sc", + "sc-pc", + "sc-upc", + "sc-apc", + "st", + "cs", + "sn", + "sma-905", + "sma-906", + "urm-p2", + "urm-p4", + "urm-p8", + "splice", + "other" + ] + } + } + } + } +} diff --git a/docs/development/release-checklist.md b/docs/development/release-checklist.md index 000948ee7..68b777111 100644 --- a/docs/development/release-checklist.md +++ b/docs/development/release-checklist.md @@ -70,6 +70,16 @@ Before each release, update each of NetBox's Python dependencies to its most rec In cases where upgrading a dependency to its most recent release is breaking, it should be constrained to its current minor version in `base_requirements.txt` with an explanatory comment and revisited for the next major NetBox release (see the [Address Constrained Dependencies](#address-constrained-dependencies) section above). +### Rebuild the Device Type Definition Schema + +Run the following command to update the device type definition validation schema: + +```nohighlight +./manage.py buildschema --write +``` + +This will automatically update the schema file at `contrib/generated_schema.json`. + ### Update Version and Changelog * Update the `VERSION` constant in `settings.py` to the new release version. diff --git a/netbox/dcim/management/commands/buildschema.py b/netbox/dcim/management/commands/buildschema.py new file mode 100644 index 000000000..44a0e95f2 --- /dev/null +++ b/netbox/dcim/management/commands/buildschema.py @@ -0,0 +1,62 @@ +import json +import os + +from django.conf import settings +from django.core.management.base import BaseCommand +from jinja2 import FileSystemLoader, Environment + +from dcim.choices import * + +TEMPLATE_FILENAME = 'devicetype_schema.jinja2' +OUTPUT_FILENAME = 'contrib/generated_schema.json' + +CHOICES_MAP = { + 'airflow_choices': DeviceAirflowChoices, + 'weight_unit_choices': WeightUnitChoices, + 'subdevice_role_choices': SubdeviceRoleChoices, + 'console_port_type_choices': ConsolePortTypeChoices, + 'console_server_port_type_choices': ConsolePortTypeChoices, + 'power_port_type_choices': PowerPortTypeChoices, + 'power_outlet_type_choices': PowerOutletTypeChoices, + 'power_outlet_feedleg_choices': PowerOutletFeedLegChoices, + 'interface_type_choices': InterfaceTypeChoices, + 'interface_poe_mode_choices': InterfacePoEModeChoices, + 'interface_poe_type_choices': InterfacePoETypeChoices, + 'front_port_type_choices': PortTypeChoices, + 'rear_port_type_choices': PortTypeChoices, +} + + +class Command(BaseCommand): + help = "Generate JSON schema for validating NetBox device type definitions" + + def add_arguments(self, parser): + parser.add_argument( + '--write', + action='store_true', + help="Write the generated schema to file" + ) + + def handle(self, *args, **kwargs): + # Initialize template + template_loader = FileSystemLoader(searchpath=f'{settings.TEMPLATES_DIR}/extras/schema/') + template_env = Environment(loader=template_loader) + template = template_env.get_template(TEMPLATE_FILENAME) + + # Render template + context = { + key: json.dumps(choices.values()) + for key, choices in CHOICES_MAP.items() + } + rendered = template.render(**context) + + if kwargs['write']: + # $root/contrib/generated_schema.json + filename = os.path.join(os.path.split(settings.BASE_DIR)[0], OUTPUT_FILENAME) + with open(filename, mode='w', encoding='UTF-8') as f: + f.write(json.dumps(json.loads(rendered), indent=4)) + f.write('\n') + f.close() + self.stdout.write(self.style.SUCCESS(f"Schema written to {filename}.")) + else: + self.stdout.write(rendered) diff --git a/netbox/templates/extras/schema/devicetype_schema.jinja2 b/netbox/templates/extras/schema/devicetype_schema.jinja2 new file mode 100644 index 000000000..b08ab24de --- /dev/null +++ b/netbox/templates/extras/schema/devicetype_schema.jinja2 @@ -0,0 +1,93 @@ +{ + "type": "object", + "additionalProperties": false, + "definitions": { + "airflow": { + "type": "string", + "enum": {{ airflow_choices }} + }, + "weight-unit": { + "type": "string", + "enum": {{ weight_unit_choices }} + }, + "subdevice-role": { + "type": "string", + "enum": {{ subdevice_role_choices }} + }, + "console-port": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": {{ console_port_type_choices }} + } + } + }, + "console-server-port": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": {{ console_server_port_type_choices }} + } + } + }, + "power-port": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": {{ power_port_type_choices }} + } + } + }, + "power-outlet": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": {{ power_outlet_type_choices }} + }, + "feed-leg": { + "type": "string", + "enum": {{ power_outlet_feedleg_choices }} + } + } + }, + "interface": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": {{ interface_type_choices }} + }, + "poe_mode": { + "type": "string", + "enum": {{ interface_poe_mode_choices }} + }, + "poe_type": { + "type": "string", + "enum": {{ interface_poe_type_choices }} + } + } + }, + "front-port": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": {{ front_port_type_choices }} + } + } + }, + "rear-port": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": {{ rear_port_type_choices}} + } + } + } + } +} From 23d085f488504b587913b6006f06300e0a9d5861 Mon Sep 17 00:00:00 2001 From: kkthxbye <400797+kkthxbye-code@users.noreply.github.com> Date: Fri, 11 Aug 2023 17:53:16 +0200 Subject: [PATCH 25/31] Fixes #12639 - Make sure name expansions throws a validation error on decrementing ranges (#13326) * Fixes #12639 - Make sure name expansions throws a validation error on decrementing ranges * Fix pep8 * Also fail on equal start & end values --------- Co-authored-by: Jeremy Stretch --- netbox/dcim/forms/object_create.py | 5 ++++- netbox/utilities/forms/utils.py | 7 +++++++ netbox/utilities/tests/test_forms.py | 5 +++-- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/netbox/dcim/forms/object_create.py b/netbox/dcim/forms/object_create.py index 9589ab533..f37edee0a 100644 --- a/netbox/dcim/forms/object_create.py +++ b/netbox/dcim/forms/object_create.py @@ -52,7 +52,10 @@ class ComponentCreateForm(forms.Form): super().clean() # Validate that all replication fields generate an equal number of values - pattern_count = len(self.cleaned_data[self.replication_fields[0]]) + if not (patterns := self.cleaned_data.get(self.replication_fields[0])): + return + + pattern_count = len(patterns) for field_name in self.replication_fields: value_count = len(self.cleaned_data[field_name]) if self.cleaned_data[field_name] and value_count != pattern_count: diff --git a/netbox/utilities/forms/utils.py b/netbox/utilities/forms/utils.py index 5100b1714..4d737f163 100644 --- a/netbox/utilities/forms/utils.py +++ b/netbox/utilities/forms/utils.py @@ -60,6 +60,9 @@ def parse_alphanumeric_range(string): except ValueError: begin, end = dash_range, dash_range if begin.isdigit() and end.isdigit(): + if int(begin) >= int(end): + raise forms.ValidationError(f'Range "{dash_range}" is invalid.') + for n in list(range(int(begin), int(end) + 1)): values.append(n) else: @@ -71,6 +74,10 @@ def parse_alphanumeric_range(string): # Not a valid range (more than a single character) if not len(begin) == len(end) == 1: raise forms.ValidationError(f'Range "{dash_range}" is invalid.') + + if ord(begin) >= ord(end): + raise forms.ValidationError(f'Range "{dash_range}" is invalid.') + for n in list(range(ord(begin), ord(end) + 1)): values.append(chr(n)) return values diff --git a/netbox/utilities/tests/test_forms.py b/netbox/utilities/tests/test_forms.py index b8cff2996..79ba3f4d8 100644 --- a/netbox/utilities/tests/test_forms.py +++ b/netbox/utilities/tests/test_forms.py @@ -264,8 +264,9 @@ class ExpandAlphanumeric(TestCase): self.assertEqual(sorted(expand_alphanumeric_pattern('r[a-9]a')), []) def test_invalid_range_bounds(self): - self.assertEqual(sorted(expand_alphanumeric_pattern('r[9-8]a')), []) - self.assertEqual(sorted(expand_alphanumeric_pattern('r[b-a]a')), []) + with self.assertRaises(forms.ValidationError): + sorted(expand_alphanumeric_pattern('r[9-8]a')) + sorted(expand_alphanumeric_pattern('r[b-a]a')) def test_invalid_range_len(self): with self.assertRaises(forms.ValidationError): From cefad9478a8ea102731f1498f9bab16f23457b9a Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Mon, 14 Aug 2023 13:29:11 +0530 Subject: [PATCH 26/31] Fixed spelling for Attributes #13460 --- netbox/ipam/forms/filtersets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/ipam/forms/filtersets.py b/netbox/ipam/forms/filtersets.py index 53fecfe2f..f00082863 100644 --- a/netbox/ipam/forms/filtersets.py +++ b/netbox/ipam/forms/filtersets.py @@ -253,7 +253,7 @@ class IPRangeFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm): model = IPRange fieldsets = ( (None, ('q', 'filter_id', 'tag')), - ('Attriubtes', ('family', 'vrf_id', 'status', 'role_id', 'mark_utilized')), + ('Attributes', ('family', 'vrf_id', 'status', 'role_id', 'mark_utilized')), ('Tenant', ('tenant_group_id', 'tenant_id')), ) family = forms.ChoiceField( From 156b87f60687d69e4782b2cfb20401474b1ffecb Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 14 Aug 2023 08:51:16 -0400 Subject: [PATCH 27/31] Fixes #13451: Disable table ordering for custom link columns --- docs/release-notes/version-3.5.md | 1 + netbox/netbox/tables/columns.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/release-notes/version-3.5.md b/docs/release-notes/version-3.5.md index fe0832c3b..30ffef393 100644 --- a/docs/release-notes/version-3.5.md +++ b/docs/release-notes/version-3.5.md @@ -19,6 +19,7 @@ * [#13369](https://github.com/netbox-community/netbox/issues/13369) - Fix job termination status for failed reports * [#13414](https://github.com/netbox-community/netbox/issues/13414) - Fix support for "hide-if-unset" custom fields on bulk import forms * [#13446](https://github.com/netbox-community/netbox/issues/13446) - Don't disable bulk edit/delete buttons after deselecting "select all" checkbox +* [#13451](https://github.com/netbox-community/netbox/issues/13451) - Disable table ordering for custom link columns --- diff --git a/netbox/netbox/tables/columns.py b/netbox/netbox/tables/columns.py index 9ef327026..399b3c184 100644 --- a/netbox/netbox/tables/columns.py +++ b/netbox/netbox/tables/columns.py @@ -504,9 +504,9 @@ class CustomLinkColumn(tables.Column): """ def __init__(self, customlink, *args, **kwargs): self.customlink = customlink - kwargs['accessor'] = Accessor('pk') - if 'verbose_name' not in kwargs: - kwargs['verbose_name'] = customlink.name + kwargs.setdefault('accessor', Accessor('pk')) + kwargs.setdefault('orderable', False) + kwargs.setdefault('verbose_name', customlink.name) super().__init__(*args, **kwargs) From 2241a256fa38e63622b0ede0c8e990b0e27cfc74 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 14 Aug 2023 08:55:47 -0400 Subject: [PATCH 28/31] Changelog for #10030, #11578, #12639 --- docs/release-notes/version-3.5.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/release-notes/version-3.5.md b/docs/release-notes/version-3.5.md index 30ffef393..cc8f61802 100644 --- a/docs/release-notes/version-3.5.md +++ b/docs/release-notes/version-3.5.md @@ -4,6 +4,7 @@ ### Enhancements +* [#10030](https://github.com/netbox-community/netbox/issues/10030) - Ship a validation schema for the device type library with each release * [#11675](https://github.com/netbox-community/netbox/issues/11675) - Add support for specifying import/export route targets during VRF bulk import * [#11922](https://github.com/netbox-community/netbox/issues/11922) - Automatically populate any VDC assignments from the parent when adding a child interface via the UI * [#12889](https://github.com/netbox-community/netbox/issues/12889) - Add 400GE CFP2 interface type @@ -13,6 +14,8 @@ ### Bug Fixes +* [#11578](https://github.com/netbox-community/netbox/issues/11578) - Fix schema definition for available IP & VLAN REST API endpoints +* [#12639](https://github.com/netbox-community/netbox/issues/12639) - Raise validation error for invalid alphanumeric ranges when creating objects * [#12665](https://github.com/netbox-community/netbox/issues/12665) - Avoid escaping semicolons when rendering custom links * [#12750](https://github.com/netbox-community/netbox/issues/12750) - Automatically delete an AutoSyncRecord when its object is deleted * [#13343](https://github.com/netbox-community/netbox/issues/13343) - Fix filtering of circuits under provider network view From 34a0fde22ce9a8a2bcd91e9bde50ac1327fd618a Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Mon, 14 Aug 2023 18:57:26 +0530 Subject: [PATCH 29/31] adds object view to allow changelog page to be opened #13463 --- netbox/templates/extras/imageattachment.html | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 netbox/templates/extras/imageattachment.html diff --git a/netbox/templates/extras/imageattachment.html b/netbox/templates/extras/imageattachment.html new file mode 100644 index 000000000..1968344cc --- /dev/null +++ b/netbox/templates/extras/imageattachment.html @@ -0,0 +1,4 @@ +{% extends 'generic/object.html' %} + +{% block tabs %} +{% endblock %} From a7a9de805ed6e57da69c82fdec550beb9fdae237 Mon Sep 17 00:00:00 2001 From: "Joel D. Tague" Date: Fri, 11 Aug 2023 13:36:34 -0400 Subject: [PATCH 30/31] feat: add 200Gbps & 400Gbps interface speed options --- netbox/dcim/choices.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/netbox/dcim/choices.py b/netbox/dcim/choices.py index 21bd3ed7e..ba722508a 100644 --- a/netbox/dcim/choices.py +++ b/netbox/dcim/choices.py @@ -1141,6 +1141,8 @@ class InterfaceSpeedChoices(ChoiceSet): (25000000, '25 Gbps'), (40000000, '40 Gbps'), (100000000, '100 Gbps'), + (200000000, '200 Gbps'), + (400000000, '400 Gbps'), ] From a4a8b7fdc46b9a7033a374f9c624fc02b3b427ae Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 15 Aug 2023 09:18:15 -0400 Subject: [PATCH 31/31] Release v3.5.8 --- .github/ISSUE_TEMPLATE/bug_report.yaml | 2 +- .github/ISSUE_TEMPLATE/feature_request.yaml | 2 +- docs/release-notes/version-3.5.md | 3 ++- netbox/netbox/settings.py | 2 +- requirements.txt | 10 +++++----- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 42a716ae7..b43968731 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -14,7 +14,7 @@ body: attributes: label: NetBox version description: What version of NetBox are you currently running? - placeholder: v3.5.7 + placeholder: v3.5.8 validations: required: true - type: dropdown diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index b04fda1b6..5df3069ba 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.5.7 + placeholder: v3.5.8 validations: required: true - type: dropdown diff --git a/docs/release-notes/version-3.5.md b/docs/release-notes/version-3.5.md index cc8f61802..6d9ae5509 100644 --- a/docs/release-notes/version-3.5.md +++ b/docs/release-notes/version-3.5.md @@ -1,6 +1,6 @@ # NetBox v3.5 -## v3.5.8 (FUTURE) +## v3.5.8 (2023-08-15) ### Enhancements @@ -11,6 +11,7 @@ * [#13033](https://github.com/netbox-community/netbox/issues/13033) - Add human-friendly speed column to interfaces table * [#13151](https://github.com/netbox-community/netbox/issues/13151) - Add "assigned" filter for IP addresses * [#13368](https://github.com/netbox-community/netbox/issues/13368) - List installed plugins on the server error report page +* [#13442](https://github.com/netbox-community/netbox/issues/13442) - Add 200 and 400 Gbps speeds to dropdown choices on interface form ### Bug Fixes diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 2744ba701..acad437fc 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -25,7 +25,7 @@ from netbox.constants import RQ_QUEUE_DEFAULT, RQ_QUEUE_HIGH, RQ_QUEUE_LOW # Environment setup # -VERSION = '3.5.8-dev' +VERSION = '3.5.8' # Hostname HOSTNAME = platform.node() diff --git a/requirements.txt b/requirements.txt index f1235fa2c..2ea0f2522 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,8 @@ bleach==6.0.0 -boto3==1.28.14 +boto3==1.28.26 Django==4.1.10 django-cors-headers==4.2.0 -django-debug-toolbar==4.1.0 +django-debug-toolbar==4.2.0 django-filter==23.2 django-graphiql-debug-toolbar==0.2.0 django-mptt==0.14 @@ -16,7 +16,7 @@ django-taggit==4.0.0 django-timezone-field==5.1 djangorestframework==3.14.0 drf-spectacular==0.26.4 -drf-spectacular-sidecar==2023.7.1 +drf-spectacular-sidecar==2023.8.1 dulwich==0.21.5 feedparser==6.0.10 graphene-django==3.0.0 @@ -27,9 +27,9 @@ mkdocs-material==9.1.21 mkdocstrings[python-legacy]==0.22.0 netaddr==0.8.0 Pillow==10.0.0 -psycopg2-binary==2.9.6 +psycopg2-binary==2.9.7 PyYAML==6.0.1 -sentry-sdk==1.28.1 +sentry-sdk==1.29.2 social-auth-app-django==5.2.0 social-auth-core[openidconnect]==4.4.2 svgwrite==1.4.3