Compare commits

...

221 Commits

Author SHA1 Message Date
Martin Hauser
90717f7c24 chore(ruff): Enable I (isort) and stabilize import ordering
- Adopt Ruff `I` (isort) rules for consistent import sorting
- Update intra-app imports to use relative imports within each Django
  app to avoid circular import issues
- Add two `# isort: split` boundaries to keep required imports pinned
  in `__init__.py` modules
2026-02-12 17:13:30 +01:00
Jason Novinger
6c383f293c Fixes #20435: Fix navigation margin issue when scrollbar appears (#21403)
Override Tabler's problematic margin-left: calc(100vw - 100%) rule that
causes a gap between the sidebar and main content when vertical scrollbar
is present on Windows/Linux browsers.

Uses scrollbar-gutter: stable to match the upstream fix in Tabler PR #2548.
2026-02-12 11:30:33 +01:00
github-actions
5bf516c63d Update source translation strings 2026-02-12 05:28:54 +00:00
Aditya Sharma
7df062d590 Fixes #21358: Prevent exception when sorting by Token column (#21391)
Mark the `token` TemplateColumn as non-orderable since it maps to a
Python property rather than a database field, causing a FieldError
when django-tables2 attempts to sort by it.

Add a regression test for TokenTable following the existing pattern
in circuits and vpn test suites.
2026-02-12 00:21:49 +01:00
Aditya Sharma
4b22be03a0 Fixes #21354: Fix Swagger-UI generating wrong URLs when BASE_PATH is set (#21392) 2026-02-11 11:35:13 -08:00
Dylan Lucci
24769ce127 Closes #21266: Add installed device table columns to DeviceBay table (#21348)
Expose additional properties of the device installed in each bay as
configurable table columns.

- Rename `role` → `installed_role`
- Rename `device_type` → `installed_device_type`
- Add `installed_description`, `installed_serial`, and
  `installed_asset_tag` columns to `DeviceBayTable`

---------

Co-authored-by: Martin Hauser <mhauser@netboxlabs.com>
2026-02-11 13:55:37 +01:00
github-actions
164e9db98d Update source translation strings 2026-02-11 05:29:43 +00:00
Martin Hauser
23f1c86e9c Closes #20211: Use thumbnails for ImageAttachment hover previews to improve page load performance (#21386) 2026-02-10 11:01:33 -06:00
Martin Hauser
02ffdd9d5d Closes #21268: Add Device Type details panel to Device view (#21368) 2026-02-10 10:37:35 -06:00
Martin Hauser
5013297326 feat(virtualization): Refactor VirtualMachine view to UI layout
Migrate the VirtualMachine detail view to SimpleLayout with standardized
panels for attributes, clusters, and resources. Modularize templates
to improve maintainability and reuse.

Fixes #21337
2026-02-10 10:22:18 -05:00
github-actions
584e0a9b8c Update source translation strings 2026-02-10 05:29:34 +00:00
Martin Hauser
3ac9d0b8bf Closes #20981: Enhance JSON rendering for Custom Validators and Protection Rules in Config Revision View (#21376)
* feat(config): Add extra context to ConfigRevisionView

Introduces `get_extra_context` method for `ConfigRevisionView` to
format JSON-based attributes like `CUSTOM_VALIDATORS`,
`DEFAULT_USER_PREFERENCES`, and `PROTECTION_RULES`.
This ensures clearer rendering of configuration data in the UI.

Fixes #20981

* Reduce padding on JSON blocks

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2026-02-09 09:48:39 -05:00
github-actions
b387ea5f58 Update source translation strings 2026-02-06 05:22:42 +00:00
bctiemann
ba9f6bf359 Fixes: #19129 - Richer display of MAC addresses in InterfaceTable when multiple MACs are present (#21270)
* Richer display of MAC addresses in InterfaceTable when multiple MACs are present

* Fix docstring

* Fix docstring

* Use mac_address_display in interface detail page

* Ensure "-" null placeholder still shows up on detail page

* Also include vminterface.html

* Simplify Multiple MAC addresses with additional selectable column for tables in list view and detail view

* Use ManyToManyColumn
2026-02-05 11:16:31 -05:00
Martin Hauser
ee6cbdcefe Fixes #21320: Prevent Rack validation errors when site or optional fields are missing during import (#21321) 2026-02-03 09:32:07 -06:00
bctiemann
de1c5120dd Merge pull request #21346 from netbox-community/release-v4.5.2
Release v4.5.2
2026-02-03 08:42:21 -05:00
Jeremy Stretch
87d2e02c85 Release v4.5.2 2026-02-03 08:09:14 -05:00
github-actions
cbbc4f74b8 Update source translation strings 2026-02-03 05:22:13 +00:00
Martin Hauser
be5bd74d4e feat(ipam): Add parent object fields for Services
Include `parent_object_type` and `parent_object_id` in `clone_fields`
for services. This improves cloning behavior for models using parent
object references, ensuring more accurate data duplication.

Fixes #21168
2026-02-02 16:05:09 -05:00
Jason Novinger
cf12bb5bf5 Fixes #20902: Avoid conflict when Git URL contains embedded username (#21252) 2026-02-02 11:16:32 -08:00
Jeremy Stretch
c060eef1d8 Closes #21300: Cache model-specific custom field lookups for the duration of a request (#21334) 2026-02-02 10:58:12 -08:00
bctiemann
96f0debe6e Merge pull request #21328 from netbox-community/21327-ContentTypeField-caching
Closes #21327: Leverage `get_by_natural_key()` to resolve ContentTypes
2026-02-02 13:46:04 -05:00
Martin Hauser
b26c7f34cd feat(models): Handle GFK attributes in CloningMixin
Extend the CloningMixin to inject GenericForeignKey (GFK) attributes
when both content type and ID fields are present. Improves support for
models using GFK fields during cloning operations.

Fixes #21201
2026-02-02 13:02:32 -05:00
bctiemann
d6428c6aa4 Merge pull request #21314 from marsteel/21233-UI-Add-horizontal-padding-to-Release-info-section
Fixes #21233: UI Add horizontal padding to Release info section in Navigation menu
2026-02-02 11:17:30 -05:00
github-actions
e3eca98897 Update source translation strings 2026-01-31 05:14:50 +00:00
Jeremy Stretch
cdc735fe41 Closes #21302: Avoid redundant uniqueness checks in REST API serializers 2026-01-30 19:36:42 -05:00
Jeremy Stretch
aa4a9da955 Closes #21303: Cache serialized post-change data on object (#21325)
* Closes #21303: Cache serialized post-change data on object

* Set to_objectchange.alters_data

* Restructure logic for determining post-change snapshot
2026-01-30 14:49:12 -05:00
Jeremy Stretch
5c6fc2fb6f Closes #21110: Support for cursor-based pagination in GraphQL API (#21322) 2026-01-30 11:45:35 -08:00
Jeremy Stretch
ad29cb2d66 Closes #21263: Prefetch related objects after creating/updating objects via REST API (#21329)
* Closes #21263: Prefetch related objects after creating/updating objects via REST API

* Add comment re: ordering by PK
2026-01-30 14:13:05 -05:00
Aditya Sharma
bec5ecf6a9 Closes #21209: Accept case-insensitive model names in configuration (#21275)
NetBox now accepts case-insensitive model identifiers in configuration, allowing
both lowercase (e.g. "dcim.site") and PascalCase (e.g. "dcim.Site") for
DEFAULT_DASHBOARD, CUSTOM_VALIDATORS, and PROTECTION_RULES.
This makes model name handling consistent with FIELD_CHOICES.

- Add a shared case-insensitive config lookup helper (get_config_value_ci())
- Use the helper in extras/signals.py and core/signals.py
- Update FIELD_CHOICES ChoiceSetMeta to support case-insensitive replace/extend
  (only compute extend choices if no replacement is defined)
- Add unit tests for get_config_value_ci()
- Add integration tests for case-insensitive FIELD_CHOICES replacement/extension
- Update documentation examples to use PascalCase consistently
2026-01-30 13:48:38 +01:00
github-actions
c98f55dbd2 Update source translation strings 2026-01-30 05:18:59 +00:00
Jeremy Stretch
dfe20532a1 Closes #21327: Leverage get_by_natural_key() to resolve ContentTypes 2026-01-29 19:46:22 -05:00
Martin Hauser
359179fd4a fix(dcim): Add port mapping creation for module install (#21308) 2026-01-29 14:37:57 -08:00
Arthur Hanson
c44e8606f7 21129 Store queue_name in Job so correctly deleted in RQ (#21309)
* Add queue name to Job

* Add queue name to serializer, filterset, detail view

* fix job queue delete

* fix job queue delete

* review feedback
2026-01-29 15:29:33 -05:00
github-actions
8e620ef325 Update source translation strings 2026-01-29 05:17:01 +00:00
Jeremy Stretch
1526e437f1 Closes #21244: Introduce ability to omit specific fields from REST API responses (#21312)
Introduce support for omitting specific serializer fields via an
`omit` parameter, acting as the inverse of `fields`.
Wire it through the API viewset and queryset optimization helpers
so omitted fields don’t trigger unnecessary annotations/prefetches,
and document the new behavior.
2026-01-28 22:06:46 +01:00
Martin Hauser
0b507eb207 fix(ipam): Include scope params in Prefix creation links
Update prefix creation URLs to pass `scope_type` and `scope` (replacing
the legacy `site` query parameter) for both the Child Prefixes
"Add Prefix" button and in-table available-prefix links.
Scope parameters are only rendered when a scope is defined, so
unscoped prefixes remain unchanged.

Fixes #21262
2026-01-28 15:19:44 -05:00
Elliott Balsley
5a36e79215 Fixes #20977: Apply defaults for missing script variables (#21295)
Ensure script variables fall back to their defined defaults when a value is not
submitted (e.g. via "Run again" or other minimal POSTs).

- Populate omitted script variables with their initial/default values before
  validation and job enqueueing
- Treat falsy defaults (e.g. False/0) as valid defaults
- Add a test asserting defaults are included in enqueued job data
- Remove the redundant default from ScriptValidationErrorTest
2026-01-28 15:35:33 +01:00
Martin Hauser
2a0f26623b Fixes #21254: Fix release check failure when stale latest_release cache can't be unpickled (#21282)
* fix(misc): Handle cache unpickling failure in release check

Guard `cache.get('latest_release')` during release checks to prevent a
500 when stale cached data can't be unpickled after dependency upgrades.
On failure, log at debug level and delete the affected cache key.

Fixes #21254

* Correct comment

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2026-01-28 09:28:20 -05:00
MA Gang
43ae52089f Add padding to release info div
Add padding to release info div in layout.html
2026-01-28 14:29:38 +01:00
github-actions
1a603981b2 Update source translation strings 2026-01-28 05:07:33 +00:00
Aditya Sharma
245495b2fe Closes #21228: Add image attachments support to RackType model (#21276) 2026-01-27 09:36:11 -08:00
bctiemann
8d3eb69055 Merge pull request #21264 from netbox-community/19869-provide-information-about-lag-targets-in-lag-members-section
Fixes #19869: Display peer connections for LAG member interfaces
2026-01-27 10:23:14 -05:00
bctiemann
7e3b60f194 Merge pull request #21299 from netbox-community/20172-ability-to-query-for-cabled-interfaces-via-graphql
Closes #20172: Add `cabled` filter for DCIM interfaces in GraphQL
2026-01-27 10:13:27 -05:00
bctiemann
5338c842b8 Merge pull request #21289 from llamafilm/20052-loglevel
Fixes #20052: improve logging for faulty scripts
2026-01-27 10:10:17 -05:00
bctiemann
9186b0edaa Merge pull request #21281 from netbox-community/21176-remove-iprange-checkboxes
Fixes #21176: Remove checkboxes from IP ranges in mixed-type tables
2026-01-27 10:08:37 -05:00
bctiemann
d883be9e56 Merge pull request #21246 from adionit7/21150-docs-config-menu-path
Fixes #21150: Correct Dynamic Configuration menu path in documentation
2026-01-27 08:43:52 -05:00
bctiemann
6fc7fa6c64 Merge pull request #21220 from netbox-community/15801-vlan-overview-device-interfaces-list-with-connection-link
Closes #15801: Add link peer and connection columns to `VLANDeviceTable`
2026-01-27 08:35:33 -05:00
Martin Hauser
3a33df0e43 feat(forms): Add Owner Group support to Filter Forms
Introduces support for `owner_group` in various filter forms, improving
ownership granularity.
Updates DynamicModel fields to handle relationships
between `owner_group` and `owner` effectively.

Fixes #21081
2026-01-27 08:34:42 -05:00
github-actions
433f46746e Update source translation strings 2026-01-27 05:07:09 +00:00
Jeremy Stretch
8f5f91fcfe Closes #21259: Cache ObjectType results for the duration of a request (#21287) 2026-01-26 15:07:13 -08:00
Martin Hauser
1a2175127e Fixes #21202: Avoid clearing scope on clone (#21265) 2026-01-26 16:14:36 -06:00
Martin Hauser
e859807d1d docs(guides): Update Ubuntu reference to 24.04
Update the installation and administration guides to reference
Ubuntu 24.04 instead of 22.04 where applicable, and refresh examples
to match NetBox v4.5.

This includes updates to Python version requirements, NetBox shell
commands, Redis configuration, and sample outputs to align with current
compatibility and best practices.

Fixes #21297
2026-01-26 15:43:59 -05:00
Jeremy Stretch
a8c997ff29 Closes #21260: Defer object serialization for events pipeline (#21286) 2026-01-26 14:35:00 -06:00
adionit7
4a28ab98f4 Fixes #21115: Include attribute_data in ModuleType YAML export
- Added airflow and attribute_data fields to ModuleType.to_yaml() method
- Ensures custom JSON properties from module type profiles are properly exported
- Maintains consistency with import functionality in ModuleTypeImportForm
2026-01-26 15:01:21 -05:00
Martin Hauser
3636d55017 fix(nav): Show Authentication admin menu items based on object perms (#21283)
Replace hardcoded menu entries for Users, Groups, API Tokens, and
Permissions with `get_model_item()`. This drops the `staff_only` gate
and relies on the standard model permission checks, restoring visibility
of these Admin menu items for non-superusers with the relevant object
permissions.

Fixes #21242
2026-01-26 11:34:46 -08:00
Aditya Sharma
aa69e96818 Fixes #21173: Fix plugin menu registration order timing issue (#21248)
* Fixes #21173: Fix plugin menu registration order timing issue

- Converted static MENUS list to dynamic get_menus() function
- Ensures plugin menus are built at request time after all plugins complete ready()
- Fixes issue where only first few plugin menus appeared in navigation sidebar
- Updated navigation template tag to call get_menus() dynamically

* Fix ruff linting errors

- Add missing blank line before get_menus() function definition
- Remove trailing whitespace

* Add @cache decorator to get_menus() for performance optimization

Per reviewer feedback, the menu list is now cached since it doesn't change
without a Django restart. This eliminates redundant list building on each request.

---------

Co-authored-by: adionit7 <adionit7@users.noreply.github.com>
2026-01-26 10:34:57 -08:00
Martin Hauser
1745d2ae93 feat(dcim): Add filter for cabled objects in GraphQL
Introduces a `cabled` filter to the GraphQL API for DCIM. Allows
filtering objects based on whether they are connected to a cable,
improving query customization.

Fixes #20172
2026-01-26 15:39:56 +01:00
Elliott Balsley
e097a848dc display error in UI 2026-01-24 19:04:14 -08:00
Elliott Balsley
595be6dcd4 log the error with error level instead of debug 2026-01-24 19:04:06 -08:00
github-actions
a9e50238eb Update source translation strings 2026-01-24 05:03:22 +00:00
Arthur Hanson
a9a300197a Clear Rack Face when clear Rack (#21182)
* #20383 clear rack face if no rack on edit

* #20383 clear rack face if no rack on edit

* review changes

* review changes
2026-01-23 12:26:27 -05:00
Jeremy Stretch
3dcca73ecc Fixes #21249: Avoid unneeded user query when no event rules are present (#21250) 2026-01-23 09:44:54 -06:00
Jason Novinger
cedbeb7b19 Fixes #21176: Remove checkboxes from IP ranges in mixed-type tables
When IP addresses and IP ranges are displayed together in a prefix's
  IP Addresses tab, only IP addresses should be selectable for bulk
  operations since the bulk delete form doesn't support mixed object types.

  - Override render_pk() in AnnotatedIPAddressTable to conditionally render
    checkboxes only for the table's primary model type (IPAddress)
  - Add warning comment to add_requested_prefixes() about fake Prefix objects
  - Add regression test to verify IPAddress has checkboxes but IPRange does not
2026-01-23 09:36:15 -06:00
Martin Hauser
a45b6b170d feat(dcim): Show peer connections for LAG members
Add `InterfaceLAGMemberTable` for the LAG Members panel on
LAG interface detail views. The table includes the parent device,
member interface/type, and a peer column which renders
connected endpoints (including the peer LAG when present).

Fixes #19869
2026-01-22 20:41:40 +01:00
bctiemann
4b4c542dce Add truncate_middle filter for middle-ellipsis on long filenames (#21253) 2026-01-22 09:40:48 -08:00
github-actions
077d9b1129 Update source translation strings 2026-01-22 05:07:49 +00:00
Aditya Sharma
e81ccb9be6 Fixes #21214: Clean up AutoSyncRecord when detaching from DataSource (#21219)
Co-authored-by: adionit7 <adionit7@users.noreply.github.com>
2026-01-21 16:38:27 -06:00
Jeremy Stretch
bc83d04c8f Introduce performance issue template (#21247) 2026-01-21 16:34:01 -06:00
adionit7
42ecf3cac0 Fixes #21150: Correct Dynamic Configuration menu path in documentation
- Updated menu path from 'Admin > Extras > Configuration Revisions'
  to 'Admin > System > Configuration History'
- Reflects actual location in NetBox admin interface
2026-01-21 22:53:29 +05:30
Matthew Papaleo
339ad455e4 Support for max_length and max_depth standardised for prefix_list, aggreate/prefixes and prefix/prefixes 2026-01-21 10:02:06 -05:00
Martin Hauser
af8e53d8fb feat(ipam): Add connection/link peer to VLANDeviceTable
The VLAN Device Interfaces table now includes `connection` and
`link_peer` columns, using the existing interface templates to render
peer/connection context consistently.

Fixes #15801
2026-01-21 13:04:39 +01:00
github-actions
f24376cfab Update source translation strings 2026-01-21 05:07:22 +00:00
Jeremy Stretch
47d4ae29c1 Release v4.5.1 2026-01-20 14:44:04 -05:00
bctiemann
8fce672682 Merge pull request #21238 from netbox-community/21160-follow-up-null-option
Fixes #21160: Handle "null" choice selection in widgets
2026-01-20 13:39:54 -05:00
Antoine Keranflec'h
f776b97415 fixes #21139 support api filter for core (#21192) 2026-01-20 09:10:27 -08:00
Aditya Sharma
3cc1f30287 Fixes #21213: Make Tag weight field required in forms (#21218)
The weight field was explicitly declared with required=False in TagForm
and TagImportForm, allowing empty submissions that would crash with a
database IntegrityError since the column is NOT NULL.

By removing the explicit field override, Django now auto-generates the
form field from the model, which has default=1000 and is required.

Co-authored-by: adionit7 <adionit7@users.noreply.github.com>
2026-01-20 08:50:31 -08:00
Martin Hauser
6d166aa10d feat(utilities): Handle "null" choice selection in widgets
Enhances widget handling by preserving "null" choice values in both
individual and mixed-object selections. Updates tests to validate UI
rendering and ensure compatibility with null sentinel values.
2026-01-20 17:29:48 +01:00
Aditya Sharma
040a2ae9a9 Enable specifying mask length when creating IP addresses via available-ips endpoint (#21193)
* Enable specifying mask length when creating IP addresses via available-ips endpoint

Fixes #21144

Allow clients to specify an arbitrary mask length when creating IP addresses
from a parent prefix or range using the 'next available' REST API endpoint.

Changes:
- Updated AvailableIPAddressesView to use PrefixLengthSerializer as write_serializer_class
- Enhanced PrefixLengthSerializer to support both 'prefix' and 'parent' context keys
- Added validation to ensure requested prefix_length >= parent mask_length
- Updated prep_object_data to use requested prefix_length if provided, otherwise fall back to parent mask_length for backwards compatibility
- Updated API schema documentation to reflect PrefixLengthSerializer usage

This enables use cases like creating loopback IP addresses with /32 mask length
from a parent prefix with a shorter mask length.

* Refine available-ips prefix length handling

Keep PrefixLengthSerializer strict for available-prefixes and introduce
AvailableIPRequestSerializer for the available-ips endpoint, where
prefix_length is optional and validated against the parent prefix/range.

* Revert PrefixLengthSerializer to original strict state

PrefixLengthSerializer should remain required and strict for the
available-prefixes endpoint. The optional prefix_length functionality
for available-ips is handled by AvailableIPRequestSerializer.

* Add API test; misc cleanup

---------

Co-authored-by: adionit7 <adionit7@users.noreply.github.com>
Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2026-01-20 11:20:02 -05:00
Martin Hauser
39f11f28fb fix(core): Cache table existence for ObjectType checks
Introduces a cached `_table_exists` flag to avoid repeated database
introspection queries for `core_objecttype`.
Improves performance during ObjectType lookups and reduces
redundant query overhead.

Fixes #21231
2026-01-20 11:15:14 -05:00
Jeremy Stretch
62b9025a9e Fixes #21181: Handle AuthenticationFailed exception on /media endpoint (#21224) 2026-01-20 08:07:18 -08:00
Jeremy Stretch
21091f22e6 Closes #21234: Add #20966 to the changelog for v4.4.9 (#21236) 2026-01-20 09:22:03 -06:00
github-actions
3efa23cf8f Update source translation strings 2026-01-20 05:07:49 +00:00
bctiemann
0f62137957 Merge pull request #21199 from netbox-community/21178-change-rack-dimensions-display-to-be-more-consistent
Fixes #21178: Use localized “millimeters” for rack mounting depth (follow-up)
2026-01-19 14:14:24 -05:00
Martin Hauser
7858ccb712 feat(extras): Add AVIF support for image attachments
Extends allowed image file formats to include AVIF for better modern
format support. Introduces a constants mapping for image formats to
centralize file type definitions. Updates form widgets and utilities
to leverage the new constants, enabling more flexible and consistent
image handling.

Fixes #21039
2026-01-19 09:56:06 -05:00
Martin Hauser
6b7b38ee0a fix(users): Refactor object permission query logic
Simplifies the `OBJECTPERMISSION_OBJECT_TYPES` definition by adjusting
query filters and introducing new conditions for specific app labels
and models.

Fixes #21051
2026-01-19 09:30:36 -05:00
matthew-242
c8f17e06a2 Add support to filter on cached relations _location, _region, _site and _site_group to ScopedFilterMixin (#21162) 2026-01-19 09:09:03 -05:00
Jeremy Stretch
edace6aff4 Fixes #21166: Fix support for filtering on unsigned 32-bit integer values in GraphQL API (#21186)
* Fixes #21166: Fix support for filtering on unsigned 32-bit integer values in GraphQL API

* tunnel_id should also use BigIntegerLookup
2026-01-19 08:54:39 -05:00
github-actions
586bc132b6 Update source translation strings 2026-01-17 05:02:55 +00:00
Arthur Hanson
52a2b934a0 Fixes #21160: Fix performance issue rendering FilterSet forms w/ large choicesets (#21200) 2026-01-16 16:34:12 -06:00
Martin Hauser
3d1f18d6dd fix(dcim): Localize mounting depth format string
Replaces the fixed format string for `mounting_depth` with a localized
version using `gettext_lazy`. This ensures proper translation of the
unit label for internationalization purposes.

Fixes #21178
2026-01-16 19:53:49 +01:00
Micky
3e2a26984f Fixes #21165: Changes filterset to show VLAN group instead of site (#21190) 2026-01-16 09:24:29 -06:00
adionit7
f5f0c19860 Remove obsolete pre-commit hook script
The legacy pre-commit hook script was scheduled for removal in NetBox v4.3, as noted in the TODO comment within the file. Users should now use the pre-commit tool instead.
2026-01-16 09:03:08 -05:00
bctiemann
8da9b11ab8 Merge pull request #21154 from netbox-community/21124-moduletype-front-ports
Fixes #21124: Fix rear port selection when creating front ports on a module type
2026-01-16 08:28:39 -05:00
Arthur Hanson
ca67fa9999 Fix #21134: fix bulk rename ModuleType (#21180) 2026-01-16 03:23:28 -06:00
Jeremy Stretch
eff768192e Fixes #21140: Ensure default panel attribute labels are translated (#21153) 2026-01-16 01:35:35 -06:00
github-actions
1e297d55ee Update source translation strings 2026-01-16 05:04:49 +00:00
bctiemann
fdb987ef91 Merge pull request #21183 from netbox-community/21178-change-rack-dimensions-display-to-be-more-consistent
Fixes #21178: Add spacing in mounting depth format string
2026-01-15 17:48:39 -05:00
bctiemann
b5a23db43c Merge pull request #21164 from netbox-community/21118-site
fix performance regression for Site save, use bulk_update for cached fields
2026-01-15 17:48:01 -05:00
bctiemann
366b69aff7 Merge pull request #21143 from netbox-community/21050-device-oob-ip-may-become-orphaned
Fixes #21050: Prevent reassignment of OOB IPs
2026-01-15 17:47:00 -05:00
bctiemann
c3e8c5e69c Merge pull request #21100 from netbox-community/21097-graphql-id-lookups
Fixes #21097: Fix comparison lookups for ID filters in GraphQL API
2026-01-15 17:44:22 -05:00
adionit7
b55f36469d Update CodeQL Action from v3 to v4
- Update github/codeql-action/init from @v3 to @v4
- Update github/codeql-action/analyze from @v3 to @v4

Fixes #21156
2026-01-15 16:46:25 -05:00
Martin Hauser
1c46215cd5 feat(extras): Allow updates to data_source and data_file via API
Adds support for PATCHing ConfigContext and ConfigContextProfile with
integer IDs for `data_source` and `data_file`.
Adds regression tests to validate assignment and API functionality.

Fixes #20933
2026-01-15 14:37:16 -05:00
Martin Hauser
7fded2fd87 fix(dcim): Add spacing in mounting depth format string
Corrects the format string for mounting depth to include a space
between the value and the unit (`mm`) for consistency with other
measurements.

Fixes #21178
2026-01-15 18:52:25 +01:00
Martin Hauser
0ddc5805c4 fix(core): Use gettext_lazy in data.py
Replace `gettext()` with `gettext_lazy()` to avoid locale-dependent
model serialization (and false-positive pending migration warnings).
Also make a missing `ValidationError` message translatable and
format-safe.

Fixes #21175
2026-01-15 12:47:05 -05:00
github-actions
c1bbc026e2 Update source translation strings 2026-01-15 05:05:36 +00:00
Arthur
8cbfe94fba fix performance regression for Site save, use bulk_update for cached fields 2026-01-14 16:30:40 -08:00
Jason Novinger
434334d927 Fixes #20239: Prevent shared mutable state in PluginMenuItem and PluginMenuButton (#21099)
PluginMenuItem and PluginMenuButton classes used mutable class-level
defaults for `permissions` and `buttons` attributes, causing permission
leakage between instances when these attributes were modified without
explicit parameters.

Changed to initialize these attributes as fresh lists per instance in
__init__ when not explicitly provided, following standard Python pattern
for avoiding mutable default arguments.
2026-01-14 12:50:35 -08:00
Jeremy Stretch
fff99fd3ff Fixes #21124: Fix rear port selection when creating front ports on a module type 2026-01-14 09:46:04 -05:00
Jeremy Stretch
6bd083b7ed Closes #21142: Enable filtering device components by site/location/rack directly via GraphQL API (#21145) 2026-01-14 08:06:55 -06:00
bctiemann
f38faf2e01 Merge pull request #21135 from netbox-community/21102-fix-graphiql-explorer
Fixes #21102: Fix GraphiQL explorer UI
2026-01-13 12:33:58 -05:00
Martin Hauser
f4892caa51 fix(ipam): Prevent reassignment of OOB IPs
Disable reassignment of IP addresses designated as primary or OOB for
parent objects. Adds validation to block changes when an IP is marked as
the OOB IP.

Fixes #21050
2026-01-13 18:13:31 +01:00
Mark Robert Coleman
e60807adc5 Fixes #21121: Expand changelog message doc/add cross-references (#21138) 2026-01-13 09:58:06 -06:00
github-actions
e14934e5a5 Update source translation strings 2026-01-13 05:05:43 +00:00
Adam
ae03723e43 Fixes #21105: Update help text for token field on API page. (#21106)
Co-authored-by: Jason Novinger <jnovinger@gmail.com>
2026-01-12 19:17:35 -06:00
Jeremy Stretch
c0f79df91f Introduce a new issue type for feature removals (#21092)
Co-authored-by: Jason Novinger <jnovinger@gmail.com>
2026-01-12 15:41:25 -06:00
Jeremy Stretch
edbfd0bae6 Fixes #21117: Avoid exception when attempting to create v2 token without API_TOKEN_PEPPERS defined (#21132) 2026-01-12 15:40:42 -06:00
Jeremy Stretch
c3e111c769 Fixes #21102: Fix GraphiQL explorer UI 2026-01-12 14:34:17 -05:00
Mario
c11f4b3716 21075-rename-l2vpn-terminations-menu-entry 2026-01-12 10:40:45 -05:00
Jeremy Stretch
a54ad24b47 Fixes #21097: Fix comparison lookups for ID filters in GraphQL API 2026-01-08 16:34:13 -05:00
Martin Hauser
3624b88c3f Closes #21035: Add .gitkeep to track the media directory (#21074) 2026-01-08 14:33:06 -06:00
github-actions
f54ed8bb7f Update source translation strings 2026-01-08 05:04:46 +00:00
Jeremy Stretch
5d0609e729 Bump Python version for update-translation-strings action (#21083) 2026-01-07 15:26:21 -08:00
Brian Tiemann
865b88e724 Make module_bay recursion check on Module.clean tolerant of unset module.module_bay 2026-01-07 10:19:02 -05:00
Jeremy Stretch
e73db97d46 Merge pull request #21079 from netbox-community/feature
Release v4.5.0
2026-01-06 16:12:06 -05:00
Jeremy Stretch
6f2ba5c75c Merge branch 'main' into feature 2026-01-06 13:05:07 -05:00
Jeremy Stretch
fa8a9ef9de Release v4.4.10 2026-01-06 12:30:03 -05:00
Jeremy Stretch
6beb079b97 Revert "Fixed #20950: Add missing module and device properties in module-bay (#21005)"
This reverts commit 860db9590b.
2026-01-06 10:38:41 -05:00
bctiemann
bad688b8aa Merge pull request #21069 from netbox-community/21067-cable-profile-error
Fixes #21067: Force update of cable terminations when changing cable profile
2026-01-06 09:48:54 -05:00
github-actions
c8aad24a1b Update source translation strings 2026-01-06 05:04:58 +00:00
bctiemann
42bd876604 Merge pull request #21072 from netbox-community/21071-exception-request-url
Closes #21071: Include the request method & URL when displaying a server error
2026-01-05 20:20:46 -05:00
bctiemann
f903442cb9 Merge pull request #21065 from netbox-community/21049-clean-stale-cf-data
Fixes #21049: Remove stale custom field data during object validation
2026-01-05 20:19:46 -05:00
Jason Novinger
5a64cb712d Fixes #21064: Ensures that extra choices preserve nested colons 2026-01-05 16:38:16 -05:00
Jason Novinger
4d90d559be Fix permission constraint example error 2026-01-05 16:33:21 -05:00
Jeremy Stretch
19de058f94 Closes #21071: Include the request method & URL when displaying a server error 2026-01-05 16:09:39 -05:00
Jeremy Stretch
d3e4c02807 Fixes #21067: Force update of cable terminations when changing cable profile 2026-01-05 15:14:04 -05:00
Jeremy Stretch
dc00e19c3c Fixes #21063: Check for duplicate choice values when validating a custom field choice set (#21066) 2026-01-05 13:10:04 -06:00
Jeremy Stretch
6ed6da49d9 Update test 2026-01-05 11:00:54 -05:00
Prince Kumar
7154d4ae2e Closes #20953: Show interfaces bridged to an interface in the UI (#21010) 2026-01-05 09:40:38 -06:00
Jeremy Stretch
bc26529be8 Fixes #21049: Remove stale custom field data during object validation 2026-01-05 09:49:32 -05:00
github-actions
da64c564ae Update source translation strings 2026-01-01 05:07:03 +00:00
Jeremy Stretch
6199b3e039 FIxes #19506: Add filter forms for component templates (#21057)
Co-authored-by: Callum <callum@reja.au>
Co-authored-by: Callum <96725140+callumau@users.noreply.github.com>
2025-12-31 09:50:39 -06:00
Jeremy Stretch
ebada4bf72 Closes #21001: Annotate plugin filterset registration in v4.5 release notes (#21058) 2025-12-31 09:42:47 -06:00
github-actions
2a391253a5 Update source translation strings 2025-12-31 05:05:09 +00:00
Jason Novinger
914653d63e Fixes #21045: Allow saving Site with associated Prefix
This was a result of the fix for #20944 optimizing a query to only
include the `id` field with `.only(id)`. Since `Prefix.__init__()`
caches original values from other fields (`_prefix` and `_vrf_id`),
these cached values are `None` at init-time.

This might not normally be a problem, but the sequence of events in
the bug report also end up causing the `handle_prefix_saved` handler
to run, which uses an ORM lookup, (either `net_contained_or_equal`
original`net_contained`) that does not support a query argument of
`None`.
2025-12-30 12:26:48 -05:00
Martin Hauser
3813aad8b1 Fixes #20320: Ensure related interface options availibility in bulk edit (#21006) 2025-12-30 10:17:14 -06:00
Jeremy Stretch
ea5371040e Fixes #20817: Re-enable sync button when disabling scheduled syncing for a data source (#21055) 2025-12-30 10:05:08 -06:00
Unknown
6c824cc48f Fixes #20044: Elevations stuck in light mode (#21037)
Co-authored-by: UnknownTy <meaphunter+git@hotmail.com>
Co-authored-by: Jason Novinger <jnovinger@gmail.com>
2025-12-29 16:27:03 -06:00
Jeremy Stretch
c78b8401dc Fixes #21020: Fix object filtering for image attachments panel (#21030) 2025-12-29 15:19:24 -06:00
Jeremy Stretch
f510e40428 Closes #21047: Add compatibility matrix to plugin setup instructions (#21048) 2025-12-29 11:39:51 -06:00
Prince Kumar
860db9590b Fixed #20950: Add missing module and device properties in module-bay (#21005) 2025-12-23 13:34:06 -06:00
Jeremy Stretch
7c63d001b1 Release v4.4.9 2025-12-23 12:02:30 -05:00
Jeremy Stretch
93119f52c3 Fixes #21032: Avoid subquery in RestrictedQuerySet where unnecessary 2025-12-23 10:15:06 -05:00
github-actions
ee2aa35cba Update source translation strings 2025-12-23 05:04:20 +00:00
bctiemann
edf35e35be Merge pull request #21028 from netbox-community/fix/device-api-missing-owner-field
Fix missing owner field in DeviceWithConfigContextSerializer
2025-12-22 14:28:58 -05:00
bctiemann
7896a48075 Merge pull request #21029 from netbox-community/21011-configrevision-save
Fixes #21011: Avoid updating database when loading active ConfigRevision
2025-12-22 14:19:19 -05:00
bctiemann
eb87c3f304 Merge pull request #21000 from netbox-community/20011-misleading-error-message
Fixes #20011: Provide accurate error for bulk import duplicate IDs
2025-12-22 14:12:36 -05:00
Jeremy Stretch
062a871521 Add missing owner field to device & VM component serializers 2025-12-22 13:52:39 -05:00
Vincent Simonin
3acbb0a08c Fix on delete cascade entity order (#20949)
* Fix on delete cascade entity order

Since [#20708](https://github.com/netbox-community/netbox/pull/20708)
relation with a on delete RESTRICT are not deleted in the proper order.
Then the error `violate not-null constraint` occurs and breaks the
delete cascade feature.

* Revert unrelated and simplify changes
2025-12-22 13:19:02 -05:00
Jeremy Stretch
f67cc47def Fixes #21011: Avoid updating database when loading active ConfigRevision 2025-12-22 11:00:04 -05:00
Martin Hauser
f7219e0672 Closes #20309: Add ASDOT notation support for ASN ranges (#21004)
* feat(ipam): Add ASDOT notation support for ASN ranges

Introduces ASDOT notation for ASN Ranges to improve readability of large
AS numbers. Adds `start_asdot` and `end_asdot` properties, columns, and
display logic for ASN ranges in the UI.

Fixes #20309

* Wrap "ASDOT" with parentheses in column header

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2025-12-22 10:06:08 -05:00
Prince Kumar
e5a975176d Fixed #20944: Ensure cached scope fields stay consistent when Region, Site, or Location changes (#20986) 2025-12-22 09:48:43 -05:00
Mark Coleman
07d8157ccd Fix missing owner field in DeviceWithConfigContextSerializer
Fixes: https://github.com/netbox-community/netbox/issues/21022
2025-12-20 11:02:36 +01:00
github-actions
83ee4fb593 Update source translation strings 2025-12-20 05:02:02 +00:00
bctiemann
db8271c904 Fixes #20114: Preserve parent bay during device bulk import when tags are present (#21019) 2025-12-19 17:05:32 -06:00
Jeremy Stretch
712c743bcb Closes #20954: Add indexes for GFKs (#21015) 2025-12-18 14:49:00 -08:00
Jeremy Stretch
2eb42d4907 Fixes #20997: Enable creating permissions for the Owner model (#21009) 2025-12-18 09:19:40 -08:00
github-actions
5a24f99c9d Update source translation strings 2025-12-18 05:03:18 +00:00
Jeremy Stretch
9318c91405 Closes #20720: Add support for Latvian translations (#21003) 2025-12-17 15:20:04 -06:00
Martin Hauser
5c6aaf2388 Closes #20900: Allow multiple choices in CustomField select filter fields (#20992) 2025-12-17 14:32:46 -06:00
Jason Novinger
265f375595 Fixes #20876: Allow editing IPAddress in IPRange marked populated 2025-12-17 13:03:45 -05:00
bctiemann
a28269b73a Closes: #20930 - Add an ASNSiteSerializer to allow serialization of Site in ASNSerializer (#20991) 2025-12-17 09:18:51 -08:00
Jason Novinger
d95fa8dbb2 Fixes #20011: UI Error msg for duplicate IDs in bulk import 2025-12-17 09:21:17 -06:00
bctiemann
2699149016 Merge pull request #20963 from pheus/20491-normalize-arrayfield-values-to-inclusive-pairs-for-api-tests
Fixes #20491: Normalize numeric range array fields for API test comparisons
2025-12-16 15:40:44 -05:00
vo42
f371004809 Fixes #20969: Fix FrontPortTemplateFilterSet rear_port_id queryset. (#20987) 2025-12-16 11:23:18 -08:00
Jeremy Stretch
44e731a40a Release v4.5.0-beta1 2025-12-16 13:48:45 -05:00
Jason Novinger
a364ee832d Fixes #20929: Require render_config permission for UI config rendering (#20975)
* Closes #20929: Require render_config permission for UI config rendering

- Modified `ObjectRenderConfigView.has_permission()` to require both view and render_config permissions
- Added `remove_permissions()` test helper to remove permissions from existing ObjectPermission objects
- Added regression tests for Device and VirtualMachine render-config permission enforcement

The `render_config` permission action was introduced in #16681 for API endpoints. This extends PR_7604_description
to the UI render-config tabs, preventing users from viewing rendered configurations without explicit permission.

* Address PR feedback

* Address PR feedback
2025-12-16 08:09:25 -05:00
Jeremy Stretch
875e3e7979 Additional work for FR #20788 (#20973) 2025-12-15 14:41:07 -06:00
github-actions
ad29402b87 Update source translation strings 2025-12-13 05:02:00 +00:00
Jason Novinger
598f8d034d Fixes #20912: Clear ModuleBay parent when module assignment removed (#20974) 2025-12-12 13:31:59 -08:00
Arthur Hanson
ec13a79907 Fixes #20875: Fix updating of denormalized fields for component models (#20956) 2025-12-12 13:29:34 -06:00
github-actions
21f4036782 Update source translation strings 2025-12-12 05:03:16 +00:00
bctiemann
ce3738572c Merge pull request #20967 from netbox-community/20966-remove-stick-scroll
Fixes #20966: Fix broken optgroup stickiness in ObjectType multiselect
2025-12-11 19:44:16 -05:00
bctiemann
cbb979934e Merge pull request #20958 from netbox-community/17976-manufacturer-devicetype_count
Fixes #17976: Remove devicetype_count from nested manufacturer to correct OpenAPI schema
2025-12-11 19:42:26 -05:00
bctiemann
642d83a4c6 Merge pull request #20937 from netbox-community/20560-bulk-import-prefix
Fixes #20560: Fix VLAN disambiguation in prefix bulk import
2025-12-11 19:40:59 -05:00
bctiemann
3140060f21 Merge pull request #20951 from netbox-community/20925-comments-oranizationalmodel
Add comments to OrganizationalModel
2025-12-11 19:37:23 -05:00
Brian Tiemann
607a385a12 Fix style 2025-12-11 19:11:54 -05:00
bctiemann
834da4e6cd Merge branch 'feature' into 20925-comments-oranizationalmodel 2025-12-11 19:07:38 -05:00
Jason Novinger
a06c12c6b8 Fixes #20966: Fix broken optgroup stickiness in ObjectType multiselect 2025-12-11 08:59:16 -06:00
Martin Hauser
60fce84c96 feat(ipam): Normalize numeric ranges in API output
Adds logic to handle numeric range fields in API responses by
converting them into inclusive `[low, high]` pairs for consistent
behavior. Updates test cases with `vid_ranges` fields to reflect the
changes.

Closes #20491
2025-12-10 21:11:23 +01:00
Jeremy Stretch
8719fd4a54 Closes #20959: Add moduletype_count to ManufacturerSerializer (#20960) 2025-12-10 10:56:22 -08:00
Jeremy Stretch
59afa0b41d Fix test 2025-12-10 09:01:11 -05:00
Jeremy Stretch
14b246cb8a Fixes #17976: Remove devicetype_count from nested manufacturer to correct OpenAPI schema 2025-12-10 08:23:48 -05:00
github-actions
f0507d00bf Update source translation strings 2025-12-10 05:02:48 +00:00
Arthur Hanson
77b389f105 Fixes #20873: fix webhooks with image fields (#20955) 2025-12-09 22:06:11 -06:00
Jeremy Stretch
f56015e03d Closes #13182: Support PrimaryModel and OrganizationalModel in plugins (#20919) 2025-12-09 13:17:21 -08:00
Arthur
dc09ec3025 fix rackrole detail view 2025-12-09 11:01:12 -08:00
Arthur
4e0265a001 fix manufactuers detail view 2025-12-09 10:53:50 -08:00
Arthur
113c8b7ae6 merge feature 2025-12-09 10:39:48 -08:00
Jeremy Stretch
17d8f78ae3 Closes #20564: Many-to-many pass-through port mappings (#20851) 2025-12-09 09:17:17 -08:00
Jeremy Stretch
97d0a16fd4 Merge branch 'main' into feature 2025-12-09 11:50:37 -05:00
Jeremy Stretch
174b2d5f39 #19095 follow-up: Enable Python 3.14 in CI matrix 2025-12-09 11:45:25 -05:00
Jeremy Stretch
970f2bd4ed Release v4.4.8 2025-12-09 11:28:36 -05:00
Etienne.BRUNEL
a4ee323cb6 Add tenant filter on device components. 2025-12-09 10:04:41 -05:00
Jason Novinger
17e5184a11 Fixes #20759: Group object types by app in permission form (#20931)
* Fixes #20759: Group object types by app in permission form

Modified the ObjectPermissionForm to use optgroups for organizing
object types by application. This shortens the display names (e.g.,
"permission" instead of "Authentication and Authorization | permission")
while maintaining clear organization through visual grouping.

Changes:
- Updated get_object_types_choices() to return nested optgroup structure
- Enhanced AvailableOptions and SelectedOptions widgets to handle optgroups
- Modified TypeScript moveOptions to preserve optgroup structure
- Added hover text showing full model names
- Styled optgroups with bold, padded labels

* Address PR feedback
2025-12-09 08:43:29 -05:00
github-actions
e1548bb290 Update source translation strings 2025-12-09 05:02:02 +00:00
Arthur
27ffc3df6a add to detail view templates 2025-12-08 11:07:07 -08:00
Arthur
7bf84eb400 update fields 2025-12-08 10:49:15 -08:00
Arthur
e910d461ea Add comments to OrganizationalModel 2025-12-08 09:46:38 -08:00
Jason Novinger
269112a565 Fixes #19918: Resolve {module} placeholders in nested module bay labels
ModuleBayTemplate.instantiate() now calls resolve_name() and resolve_label()
to properly resolve {module} placeholders, making it consistent with other
modular components like InterfaceTemplate.

When a module with nested module bays is installed (e.g., a module with SFP
bays in position "A"), the nested bay labels now correctly show "A-21" instead
of "{module}-21".

This also removes the inconsistent fix from #17436 which only handled name
resolution post-instantiation. The proper resolution now happens during
instantiation using the existing resolve methods.
2025-12-08 10:06:46 -05:00
github-actions
c6672538ac Update source translation strings 2025-12-06 05:02:07 +00:00
Jason Novinger
9ae53fc232 Fixes #20560: Fix VLAN disambiguation in prefix bulk import 2025-12-05 16:39:28 -06:00
bctiemann
6efb258b9f Merge pull request #20908 from netbox-community/20068-import-moduletype-attrs
Closes #20068: Enable defining profile attributes when importing module types
2025-12-05 10:18:53 -05:00
github-actions
da1e0f4b53 Update source translation strings 2025-12-04 05:02:04 +00:00
Arthur Hanson
7f39f75d3d Fixes #20878: Use database routing when running script (#20879) 2025-12-03 17:47:31 -06:00
Jeremy Stretch
ebf8f7fa1b Closes #20068: Enable defining profile attributes when importing module types 2025-12-02 16:50:59 -05:00
github-actions
922b08c0ff Update source translation strings 2025-12-02 05:02:22 +00:00
Bapths
84864fa5e1 Closes #20860: Add changlog message support for component object creation (#20898) 2025-12-01 17:04:21 -06:00
Jeremy Stretch
767dfccd8f Fixes #20888: Pass decimal values for min/max on latitude and longitude fields (#20892) 2025-12-01 10:35:44 -08:00
Tom Gamull
dc4bab7477 docs: fix broken bookmarks link in model features table
The bookmarks link was pointing to ../features/customization.md#bookmarks
but the bookmarks section is actually in ../features/user-preferences.md#bookmarks.

This fixes the broken anchor link.
2025-11-26 15:12:52 -05:00
github-actions
60aa952eb1 Update source translation strings 2025-11-26 05:02:03 +00:00
693 changed files with 124870 additions and 80187 deletions

View File

@@ -15,7 +15,7 @@ body:
attributes:
label: NetBox version
description: What version of NetBox are you currently running?
placeholder: v4.4.7
placeholder: v4.5.2
validations:
required: true
- type: dropdown

View File

@@ -27,7 +27,7 @@ body:
attributes:
label: NetBox Version
description: What version of NetBox are you currently running?
placeholder: v4.4.7
placeholder: v4.5.2
validations:
required: true
- type: dropdown

View File

@@ -0,0 +1,43 @@
---
name: 🏁 Performance
type: Performance
description: An opportunity to improve application performance
labels: ["netbox", "type: performance", "status: needs triage"]
body:
- type: input
attributes:
label: NetBox Version
description: What version of NetBox are you currently running?
placeholder: v4.5.2
validations:
required: true
- type: dropdown
attributes:
label: Python Version
description: What version of Python are you currently running?
options:
- "3.12"
- "3.13"
- "3.14"
validations:
required: true
- type: checkboxes
attributes:
label: Area(s) of Concern
description: Which application interface(s) are affected?
options:
- label: User Interface
- label: REST API
- label: GraphQL API
- label: Python ORM
- label: Other
validations:
required: true
- type: textarea
attributes:
label: Details
description: >
Describe in detail the operations being performed and the indications of a performance issue.
Include any relevant testing parameters, benchmarks, and expected results.
validations:
required: true

View File

@@ -1,25 +0,0 @@
---
name: 🗑️ Deprecation
type: Deprecation
description: The removal of an existing feature or resource
labels: ["netbox", "type: deprecation"]
body:
- type: textarea
attributes:
label: Proposed Changes
description: >
Describe in detail the proposed changes. What is being removed?
validations:
required: true
- type: textarea
attributes:
label: Justification
description: Please provide justification for the proposed change(s).
validations:
required: true
- type: textarea
attributes:
label: Impact
description: List all areas of the application that will be affected by this change.
validations:
required: true

View File

@@ -0,0 +1,31 @@
---
name: ⚠️ Deprecation
type: Deprecation
description: Designation of a feature or behavior that will be removed in a future release
labels: ["netbox", "type: deprecation"]
body:
- type: textarea
attributes:
label: Deprecated Functionality
description: >
Describe the feature(s) and/or behavior that is being flagged for deprecation.
validations:
required: true
- type: input
attributes:
label: Scheduled removal
description: In what future release will the deprecated functionality be removed?
validations:
required: true
- type: textarea
attributes:
label: Justification
description: Please provide justification for the deprecation.
validations:
required: true
- type: textarea
attributes:
label: Impact
description: List all areas of the application that will be affected by this change.
validations:
required: true

View File

@@ -0,0 +1,20 @@
---
name: 🗑️ Feature Removal
type: Removal
description: The removal of a deprecated feature or resource
labels: ["netbox", "type: removal"]
body:
- type: input
attributes:
label: Deprecation Issue
description: Specify the issue in which this deprecation was announced.
placeholder: "#1234"
validations:
required: true
- type: textarea
attributes:
label: Summary of Changes
description: >
List all changes necessary to remove the deprecated feature or resource.
validations:
required: true

View File

@@ -31,7 +31,7 @@ jobs:
NETBOX_CONFIGURATION: netbox.configuration_testing
strategy:
matrix:
python-version: ['3.12', '3.13']
python-version: ['3.12', '3.13', '3.14']
node-version: ['20.x']
services:
redis:

View File

@@ -30,13 +30,13 @@ jobs:
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
config-file: .github/codeql/codeql-config.yml
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v4
with:
category: "/language:${{matrix.language}}"

View File

@@ -34,7 +34,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: 3.11
python-version: 3.12
- name: Install system dependencies
run: sudo apt install -y gettext

3
.gitignore vendored
View File

@@ -9,7 +9,8 @@ yarn-error.log*
/netbox/netbox/configuration.py
/netbox/netbox/ldap_config.py
/netbox/local/*
/netbox/media
/netbox/media/*
!/netbox/media/.gitkeep
/netbox/reports/*
!/netbox/reports/__init__.py
/netbox/scripts/*

View File

@@ -5,7 +5,7 @@
<a href="https://github.com/netbox-community/netbox/blob/main/LICENSE.txt"><img src="https://img.shields.io/badge/license-Apache_2.0-blue.svg" alt="License" /></a>
<a href="https://github.com/netbox-community/netbox/graphs/contributors"><img src="https://img.shields.io/github/contributors/netbox-community/netbox?color=blue" alt="Contributors" /></a>
<a href="https://github.com/netbox-community/netbox/stargazers"><img src="https://img.shields.io/github/stars/netbox-community/netbox?style=flat" alt="GitHub stars" /></a>
<a href="https://explore.transifex.com/netbox-community/netbox/"><img src="https://img.shields.io/badge/languages-15-blue" alt="Languages supported" /></a>
<a href="https://explore.transifex.com/netbox-community/netbox/"><img src="https://img.shields.io/badge/languages-16-blue" alt="Languages supported" /></a>
<a href="https://github.com/netbox-community/netbox/actions/workflows/ci.yml"><img src="https://github.com/netbox-community/netbox/actions/workflows/ci.yml/badge.svg" alt="CI status" /></a>
<p>
<strong><a href="https://netboxlabs.com/community/">NetBox Community</a></strong> |

View File

@@ -85,7 +85,7 @@ drf-spectacular-sidecar
feedparser
# WSGI HTTP server
# https://docs.gunicorn.org/en/latest/news.html
# https://gunicorn.org/news/
gunicorn
# Platform-agnostic template rendering engine

File diff suppressed because it is too large Load Diff

View File

@@ -3,29 +3,41 @@
NetBox includes a Python management shell within which objects can be directly queried, created, modified, and deleted. To enter the shell, run the following command:
```
./manage.py nbshell
cd /opt/netbox
source /opt/netbox/venv/bin/activate
python3 netbox/manage.py nbshell
```
This will launch a lightly customized version of [the built-in Django shell](https://docs.djangoproject.com/en/stable/ref/django-admin/#shell) with all relevant NetBox models pre-loaded. (If desired, the stock Django shell is also available by executing `./manage.py shell`.)
This will launch a lightly customized version of [the built-in Django shell](https://docs.djangoproject.com/en/stable/ref/django-admin/#shell) with all relevant NetBox models preloaded. (If desired, the stock Django shell is also available by executing `./manage.py shell`.)
```
$ ./manage.py nbshell
(venv) $ python3 netbox/manage.py nbshell
### NetBox interactive shell (localhost)
### Python 3.7.10 | Django 3.2.5 | NetBox 3.0
### lsmodels() will show available models. Use help(<model>) for more info.
### Python v3.12.3 | Django v5.2.10 | NetBox Community v4.5.1
### lsapps() & lsmodels() will show available models. Use help(<model>) for more info.
```
The function `lsmodels()` will print a list of all available NetBox models:
```
>>> lsmodels()
DCIM:
ConsolePort
ConsolePortTemplate
ConsoleServerPort
ConsoleServerPortTemplate
Device
...
DCIM:
dcim.Cable
dcim.CableTermination
dcim.ConsolePort
dcim.ConsolePortTemplate
dcim.ConsoleServerPort
dcim.ConsoleServerPortTemplate
dcim.Device
...
```
To exit the NetBox shell, type `exit()` or press `Ctrl+D`.
```
>>> exit()
(venv) $
```
!!! warning
@@ -114,7 +126,7 @@ Reverse relationships can be traversed as well. For example, the following will
>>> Device.objects.filter(interfaces__name="em0")
```
Character fields can be filtered against partial matches using the `contains` or `icontains` field lookup (the later of which is case-insensitive).
Character fields can be filtered against partial matches using the `contains` or `icontains` field lookup (the latter of which is case-insensitive).
```
>>> Device.objects.filter(name__icontains="testdevice")

View File

@@ -88,7 +88,7 @@ While permissions are typically assigned to specific groups and/or users, it is
### Viewing Objects
Object-based permissions work by filtering the database query generated by a user's request to restrict the set of objects returned. When a request is received, NetBox first determines whether the user is authenticated and has been granted to perform the requested action. For example, if the requested URL is `/dcim/devices/`, NetBox will check for the `dcim.view_device` permission. If the user has not been assigned this permission (either directly or via a group assignment), NetBox will return a 403 (forbidden) HTTP response.
Object-based permissions work by filtering the database query generated by a user's request to restrict the set of objects returned. When a request is received, NetBox first determines whether the user is authenticated and has been granted permission to perform the requested action. For example, if the requested URL is `/dcim/devices/`, NetBox will check for the `dcim.view_device` permission. If the user has not been assigned this permission (either directly or via a group assignment), NetBox will return a 403 (forbidden) HTTP response.
If the permission _has_ been granted, NetBox will compile any specified constraints for the model and action. For example, suppose two permissions have been assigned to the user granting view access to the device model, with the following constraints:
@@ -102,9 +102,9 @@ If the permission _has_ been granted, NetBox will compile any specified constrai
This grants the user access to view any device that is assigned to a site named NYC1 or NYC2, **or** which has a status of "offline" and has no tenant assigned. These constraints are equivalent to the following ORM query:
```no-highlight
Site.objects.filter(
Device.objects.filter(
Q(site__name__in=['NYC1', 'NYC2']),
Q(status='active', tenant__isnull=True)
Q(status='offline', tenant__isnull=True)
)
```

View File

@@ -8,7 +8,7 @@ This is a mapping of models to [custom validators](../customization/custom-valid
```python
CUSTOM_VALIDATORS = {
"dcim.site": [
"dcim.Site": [
{
"name": {
"min_length": 5,
@@ -17,12 +17,15 @@ CUSTOM_VALIDATORS = {
},
"my_plugin.validators.Validator1"
],
"dcim.device": [
"dcim.Device": [
"my_plugin.validators.Validator1"
]
}
```
!!! info "Case-Insensitive Model Names"
Model identifiers are case-insensitive. Both `dcim.site` and `dcim.Site` are valid and equivalent.
---
## FIELD_CHOICES
@@ -53,6 +56,9 @@ FIELD_CHOICES = {
}
```
!!! info "Case-Insensitive Field Identifiers"
Field identifiers are case-insensitive. Both `dcim.Site.status` and `dcim.site.status` are valid and equivalent.
The following model fields support configurable choices:
* `circuits.Circuit.status`
@@ -98,7 +104,7 @@ This is a mapping of models to [custom validators](../customization/custom-valid
```python
PROTECTION_RULES = {
"dcim.site": [
"dcim.Site": [
{
"status": {
"eq": "decommissioning"
@@ -108,3 +114,6 @@ PROTECTION_RULES = {
]
}
```
!!! info "Case-Insensitive Model Names"
Model identifiers are case-insensitive. Both `dcim.site` and `dcim.Site` are valid and equivalent.

View File

@@ -15,7 +15,7 @@ Some configuration parameters may alternatively be defined either in `configurat
## Dynamic Configuration Parameters
Some configuration parameters are primarily controlled via NetBox's admin interface (under Admin > Extras > Configuration Revisions). These are noted where applicable in the documentation. These settings may also be overridden in `configuration.py` to prevent them from being modified via the UI. A complete list of supported parameters is provided below:
Some configuration parameters are primarily controlled via NetBox's admin interface (under Admin > System > Configuration History). These are noted where applicable in the documentation. These settings may also be overridden in `configuration.py` to prevent them from being modified via the UI. A complete list of supported parameters is provided below:
* [`ALLOWED_URL_SCHEMES`](./security.md#allowed_url_schemes)
* [`BANNER_BOTTOM`](./miscellaneous.md#banner_bottom)

View File

@@ -12,7 +12,7 @@ Depending on its classification, each NetBox model may support various features
| Feature | Feature Mixin | Registry Key | Description |
|------------------------------------------------------------|-------------------------|---------------------|-----------------------------------------------------------------------------------------|
| [Bookmarks](../features/customization.md#bookmarks) | `BookmarksMixin` | `bookmarks` | These models can be bookmarked natively in the user interface |
| [Bookmarks](../features/user-preferences.md#bookmarks) | `BookmarksMixin` | `bookmarks` | These models can be bookmarked natively in the user interface |
| [Change logging](../features/change-logging.md) | `ChangeLoggingMixin` | `change_logging` | Changes to these objects are automatically recorded in the change log |
| Cloning | `CloningMixin` | `cloning` | Provides the `clone()` method to prepare a copy |
| [Contacts](../features/contacts.md) | `ContactsMixin` | `contacts` | Contacts can be associated with these models |

View File

@@ -144,7 +144,7 @@ Then, compile these portable (`.po`) files for use in the application:
* Update the version number and published date in `netbox/release.yaml`. Add or remove the designation (e.g. `beta1`) if applicable.
* Copy the version number from `release.yaml` to `pyproject.toml` in the project root.
* Update the example version numbers in the feature request and bug report templates under `.github/ISSUE_TEMPLATES/`.
* Update the example version numbers in the feature request, bug report, and performance templates under `.github/ISSUE_TEMPLATES/`.
* Add a section for this release at the top of the changelog page for the minor version (e.g. `docs/release-notes/version-4.2.md`) listing all relevant changes made in this release.
!!! tip

View File

@@ -10,9 +10,11 @@ Change records are exposed in the API via the read-only endpoint `/api/extras/ob
## User Messages
!!! info "This feature was introduced in NetBox v4.4."
When creating, modifying, or deleting an object in NetBox, a user has the option of recording an arbitrary message (up to 200 characters) that will appear in the change record. This can be helpful to capture additional context, such as the reason for a change or a reference to an external ticket.
When creating, modifying, or deleting an object in NetBox, a user has the option of recording an arbitrary message that will appear in the change record. This can be helpful to capture additional context, such as the reason for the change.
When editing an object via the web UI, the "Changelog message" field appears at the bottom of the form. This field is optional. The changelog message field is available in object create forms, object edit forms, delete confirmation dialogs, and bulk operations.
For information on including changelog messages when making changes via the REST API, see [Changelog Messages](../integrations/rest-api.md#changelog-messages).
## Correlating Changes by Request

View File

@@ -51,14 +51,14 @@ You can verify that authentication works by executing the `psql` command and pas
```no-highlight
$ psql --username netbox --password --host localhost netbox
Password for user netbox:
psql (12.5 (Ubuntu 12.5-0ubuntu0.20.04.1))
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
Password:
psql (16.11 (Ubuntu 16.11-0ubuntu0.24.04.1))
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off)
Type "help" for help.
netbox=> \conninfo
You are connected to database "netbox" as user "netbox" on host "localhost" (address "127.0.0.1") at port "5432".
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off)
netbox=> \q
```

View File

@@ -36,7 +36,7 @@ sudo ln -s /opt/netbox-X.Y.Z/ /opt/netbox
```
!!! note
It is recommended to install NetBox in a directory named for its version number. For example, NetBox v3.0.0 would be installed into `/opt/netbox-3.0.0`, and a symlink from `/opt/netbox/` would point to this location. (You can verify this configuration with the command `ls -l /opt | grep netbox`.) This allows for future releases to be installed in parallel without interrupting the current installation. When changing to the new release, only the symlink needs to be updated.
It is recommended to install NetBox in a directory named for its version number. For example, NetBox v4.0.0 would be installed into `/opt/netbox-4.0.0`, and a symlink from `/opt/netbox/` would point to this location. (You can verify this configuration with the command `ls -l /opt | grep netbox`.) This allows for future releases to be installed in parallel without interrupting the current installation. When changing to the new release, only the symlink needs to be updated.
### Option B: Clone the Git Repository
@@ -63,12 +63,12 @@ This command should generate output similar to the following:
```
Cloning into '.'...
remote: Enumerating objects: 996, done.
remote: Counting objects: 100% (996/996), done.
remote: Compressing objects: 100% (935/935), done.
remote: Total 996 (delta 148), reused 386 (delta 34), pack-reused 0
Receiving objects: 100% (996/996), 4.26 MiB | 9.81 MiB/s, done.
Resolving deltas: 100% (148/148), done.
remote: Enumerating objects: 148317, done.
remote: Counting objects: 100% (183/183), done.
remote: Compressing objects: 100% (115/115), done.
remote: Total 148317 (delta 127), reused 68 (delta 68), pack-reused 148134 (from 3)
Receiving objects: 100% (148317/148317), 165.12 MiB | 28.71 MiB/s, done.
Resolving deltas: 100% (116428/116428), done.
```
Finally, check out the tag for the desired release. You can find these on our [releases page](https://github.com/netbox-community/netbox/releases). Replace `vX.Y.Z` with your selected release tag below.
@@ -102,7 +102,8 @@ sudo cp configuration_example.py configuration.py
Open `configuration.py` with your preferred editor to begin configuring NetBox. NetBox offers [many configuration parameters](../configuration/index.md), but only the following four are required for new installations:
* `ALLOWED_HOSTS`
* `DATABASES` (or `DATABASE`)
* `API_TOKEN_PEPPERS`
* `DATABASES`
* `REDIS`
* `SECRET_KEY`
@@ -158,7 +159,7 @@ DATABASES = {
### REDIS
Redis is a in-memory key-value store used by NetBox for caching and background task queuing. Redis typically requires minimal configuration; the values below should suffice for most installations. See the [configuration documentation](../configuration/required-parameters.md#redis) for more detail on individual parameters.
Redis is an in-memory key-value store used by NetBox for caching and background task queuing. Redis typically requires minimal configuration; the values below should suffice for most installations. See the [configuration documentation](../configuration/required-parameters.md#redis) for more detail on individual parameters.
Note that NetBox requires the specification of two separate Redis databases: `tasks` and `caching`. These may both be provided by the same Redis service, however each should have a unique numeric database ID.
@@ -252,7 +253,7 @@ Once NetBox has been configured, we're ready to proceed with the actual installa
sudo /opt/netbox/upgrade.sh
```
Note that **Python 3.12 or later is required** for NetBox v4.5 and later releases. If the default Python installation on your server is set to a lesser version, pass the path to the supported installation as an environment variable named `PYTHON`. (Note that the environment variable must be passed _after_ the `sudo` command.)
Note that **Python 3.12 or later is required** for NetBox v4.5 and later releases. If the default Python installation on your server is set to a lesser version, pass the path to the supported installation as an environment variable named `PYTHON`. (Note that the environment variable must be passed _after_ the `sudo` command.)
```no-highlight
sudo PYTHON=/usr/bin/python3.12 /opt/netbox/upgrade.sh
@@ -295,13 +296,12 @@ python3 manage.py runserver 0.0.0.0:8000 --insecure
If successful, you should see output similar to the following:
```no-highlight
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
August 30, 2021 - 18:02:23
Django version 3.2.6, using settings 'netbox.settings'
Starting development server at http://127.0.0.1:8000/
January 26, 2026 - 17:00:00
Django version 5.2.10, using settings 'netbox.settings'
Starting development server at http://0.0.0.0:8000/
Quit the server with CONTROL-C.
```

View File

@@ -43,16 +43,22 @@ You should see output similar to the following:
```no-highlight
● netbox.service - NetBox WSGI Service
Loaded: loaded (/etc/systemd/system/netbox.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 2021-08-30 04:02:36 UTC; 14h ago
Loaded: loaded (/etc/systemd/system/netbox.service; enabled; preset: enabled)
Active: active (running) since Mon 2026-01-26 11:00:00 CST; 7s ago
Docs: https://docs.netbox.dev/
Main PID: 1140492 (gunicorn)
Tasks: 19 (limit: 4683)
Memory: 666.2M
Main PID: 7283 (gunicorn)
Tasks: 6 (limit: 4545)
Memory: 556.1M (peak: 556.3M)
CPU: 3.387s
CGroup: /system.slice/netbox.service
├─1140492 /opt/netbox/venv/bin/python3 /opt/netbox/venv/bin/gunicorn --pid /va>
├─1140513 /opt/netbox/venv/bin/python3 /opt/netbox/venv/bin/gunicorn --pid /va>
├─1140514 /opt/netbox/venv/bin/python3 /opt/netbox/venv/bin/gunicorn --pid /va>
├─7283 /opt/netbox/venv/bin/python3 /opt/netbox/venv/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /opt/netbox/netbox>
├─7285 /opt/netbox/venv/bin/python3 /opt/netbox/venv/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /opt/netbox/netbox>
├─7286 /opt/netbox/venv/bin/python3 /opt/netbox/venv/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /opt/netbox/netbox>
├─7287 /opt/netbox/venv/bin/python3 /opt/netbox/venv/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /opt/netbox/netbox>
├─7288 /opt/netbox/venv/bin/python3 /opt/netbox/venv/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /opt/netbox/netbox>
└─7289 /opt/netbox/venv/bin/python3 /opt/netbox/venv/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /opt/netbox/netbox>
Jan 26 11:00:00 netbox systemd[1]: Started netbox.service - NetBox WSGI Service.
...
```

View File

@@ -3,7 +3,7 @@
This documentation provides example configurations for both [nginx](https://www.nginx.com/resources/wiki/) and [Apache](https://httpd.apache.org/docs/current/), though any HTTP server which supports WSGI should be compatible.
!!! info
For the sake of brevity, only Ubuntu 20.04 instructions are provided here. These tasks are not unique to NetBox and should carry over to other distributions with minimal changes. Please consult your distribution's documentation for assistance if needed.
For the sake of brevity, only Ubuntu 24.04 instructions are provided here. These tasks are not unique to NetBox and should carry over to other distributions with minimal changes. Please consult your distribution's documentation for assistance if needed.
## Obtain an SSL Certificate

View File

@@ -12,12 +12,12 @@
</div>
The installation instructions provided here have been tested to work on Ubuntu 22.04. The particular commands needed to install dependencies on other distributions may vary significantly. Unfortunately, this is outside the control of the NetBox maintainers. Please consult your distribution's documentation for assistance with any errors.
The installation instructions provided here have been tested to work on Ubuntu 24.04. The particular commands needed to install dependencies on other distributions may vary significantly. Unfortunately, this is outside the control of the NetBox maintainers. Please consult your distribution's documentation for assistance with any errors.
The following sections detail how to set up a new instance of NetBox:
1. [PostgreSQL database](1-postgresql.md)
1. [Redis](2-redis.md)
2. [Redis](2-redis.md)
3. [NetBox components](3-netbox.md)
4. [Gunicorn](4a-gunicorn.md) or [uWSGI](4b-uwsgi.md)
5. [HTTP server](5-http-server.md)

View File

@@ -65,7 +65,7 @@ Download and extract the latest version:
```no-highlight
# Set $NEWVER to the NetBox version being installed
NEWVER=3.5.0
NEWVER=4.5.0
wget https://github.com/netbox-community/netbox/archive/v$NEWVER.tar.gz
sudo tar -xzf v$NEWVER.tar.gz -C /opt
sudo ln -sfn /opt/netbox-$NEWVER/ /opt/netbox
@@ -75,7 +75,7 @@ Copy `local_requirements.txt`, `configuration.py`, and `ldap_config.py` (if pres
```no-highlight
# Set $OLDVER to the NetBox version currently installed
OLDVER=3.4.9
OLDVER=4.4.10
sudo cp /opt/netbox-$OLDVER/local_requirements.txt /opt/netbox/
sudo cp /opt/netbox-$OLDVER/netbox/netbox/configuration.py /opt/netbox/netbox/netbox/
sudo cp /opt/netbox-$OLDVER/netbox/netbox/ldap_config.py /opt/netbox/netbox/netbox/
@@ -116,7 +116,7 @@ Check out the desired release by specifying its tag. For example:
```
cd /opt/netbox && \
sudo git fetch --tags && \
sudo git checkout v4.2.7
sudo git checkout v4.5.0
```
## 4. Run the Upgrade Script
@@ -128,7 +128,7 @@ sudo ./upgrade.sh
```
!!! warning
If the default version of Python is not at least 3.10, you'll need to pass the path to a supported Python version as an environment variable when calling the upgrade script. For example:
If the default version of Python is not **at least 3.12**, you'll need to pass the path to a supported Python version as an environment variable when calling the upgrade script. For example:
```no-highlight
sudo PYTHON=/usr/bin/python3.12 ./upgrade.sh

View File

@@ -133,23 +133,67 @@ The field "class_type" is an easy way to distinguish what type of object it is w
## Pagination
Queries can be paginated by specifying pagination in the query and supplying an offset and optionaly a limit in the query. If no limit is given, a default of 100 is used. Queries are not paginated unless requested in the query. An example paginated query is shown below:
The GraphQL API supports two types of pagination. Offset-based pagination operates using an offset relative to the first record in a set, specified by the `offset` parameter. For example, the response to a request specifying an offset of 100 will contain the 101st and later matching records. Offset-based pagination feels very natural, but its performance can suffer when dealing with large data sets due to the overhead involved in calculating the relative offset.
The alternative approach is cursor-based pagination, which operates using absolute (rather than relative) primary key values. (These are the numeric IDs assigned to each object in the database.) When using cursor-based pagination, the response will contain records with a primary key greater than or equal to the specified start value, up to the maximum number of results. This strategy requires keeping track of the last seen primary key from each response when paginating through data, but is extremely performant. The cursor is specified by passing the starting object ID via the `start` parameter.
To ensure consistent ordering, objects will always be ordered by their primary keys when cursor-based pagination is used.
!!! note "Cursor-based pagination was introduced in NetBox v4.5.2."
Both pagination strategies support passing an optional `limit` parameter. In both approaches, this specifies the maximum number of objects to include in the response. If no limit is specified, a default value of 100 is used.
### Offset Pagination
The first page will have an `offset` of zero, or the `offset` parameter will be omitted:
```
query {
device_list(pagination: { offset: 0, limit: 20 }) {
device_list(pagination: {offset: 0, limit: 20}) {
id
}
}
```
The second page will have an offset equal to the size of the first page. If the number of records is less than the specified limit, there are no more records to process. For example, if a request specifies a `limit` of 20 but returns only 13 records, we can conclude that this is the final page of records.
```
query {
device_list(pagination: {offset: 20, limit: 20}) {
id
}
}
```
### Cursor Pagination
Set the `start` value to zero to fetch the first page. Note that if the `start` parameter is omitted, offset-based pagination will be used by default.
```
query {
device_list(pagination: {start: 0, limit: 20}) {
id
}
}
```
To determine the `start` value for the next page, add 1 to the primary key (`id`) of the last record in the previous page.
For example, if the ID of the last record in the previous response was 123, we would specify a `start` value of 124:
```
query {
device_list(pagination: {start: 124, limit: 20}) {
id
}
}
```
This will return up to 20 records with an ID greater than or equal to 124.
## Authentication
NetBox's GraphQL API uses the same API authentication tokens as its REST API. Authentication tokens are included with requests by attaching an `Authorization` HTTP header in the following form:
```
Authorization: Token $TOKEN
```
NetBox's GraphQL API uses the same API authentication tokens as its REST API. See the [REST API authentication](./rest-api.md#authentication) documentation for further detail.
## Disabling the GraphQL API

View File

@@ -215,9 +215,51 @@ http://netbox/api/ipam/ip-addresses/ \
If we wanted to assign this IP address to a virtual machine interface instead, we would have set `assigned_object_type` to `virtualization.vminterface` and updated the object ID appropriately.
### Brief Format
### Specifying Fields
Most API endpoints support an optional "brief" format, which returns only a minimal representation of each object in the response. This is useful when you need only a list of available objects without any related data, such as when populating a drop-down list in a form. As an example, the default (complete) format of a prefix looks like this:
A REST API response will include all available fields for the object type by default. If you wish to return only a subset of the available fields, you can append `?fields=` to the URL followed by a comma-separated list of field names. For example, the following request will return only the `id`, `name`, `status`, and `region` fields for each site in the response.
```
GET /api/dcim/sites/?fields=id,name,status,region
```
```json
{
"id": 1,
"name": "DM-NYC",
"status": {
"value": "active",
"label": "Active"
},
"region": {
"id": 43,
"url": "http://netbox:8000/api/dcim/regions/43/",
"display": "New York",
"name": "New York",
"slug": "us-ny",
"description": "",
"site_count": 0,
"_depth": 2
}
}
```
Similarly, you can opt to omit only specific fields by passing the `omit` parameter:
```
GET /api/dcim/sites/?omit=circuit_count,device_count,virtualmachine_count
```
!!! note "The `omit` parameter was introduced in NetBox v4.5.2."
Strategic use of the `fields` and `omit` parameters can drastically improve REST API performance, as the exclusion of fields which reference related objects reduces the number and complexity of underlying database queries needed to generate the response.
!!! note
The `fields` and `omit` parameters should be considered mutually exclusive. If both are passed, `fields` takes precedence.
#### Brief Format
Most API endpoints support an optional "brief" format, which returns only a minimal representation of each object in the response. This is useful when you need only a list of available objects without any related data, such as when populating a drop-down list in a form. It's also more convenient than listing out individual fields via the `fields` or `omit` parameters. As an example, the default (complete) format of a prefix looks like this:
```no-highlight
GET /api/ipam/prefixes/13980/
@@ -270,10 +312,10 @@ GET /api/ipam/prefixes/13980/
}
```
The brief format is much more terse:
The brief format includes only a few fields:
```no-highlight
GET /api/ipam/prefixes/13980/?brief=1
GET /api/ipam/prefixes/13980/?brief=true
```
```json
@@ -610,9 +652,7 @@ http://netbox/api/dcim/sites/ \
## Changelog Messages
!!! info "This feature was introduced in NetBox v4.4."
Most objects in NetBox support [change logging](../features/change-logging.md), which generates a detailed record each time an object is created, modified, or deleted. Beginning in NetBox v4.4, users can attach a message to the change record as well. This is accomplished via the REST API by including a `changelog_message` field in the object representation.
Most objects in NetBox support [change logging](../features/change-logging.md), which generates a detailed record each time an object is created, modified, or deleted. Additionally, users can attach a message to the change record as well. This is accomplished via the REST API by including a `changelog_message` field in the object representation.
For example, the following API request will create a new site and record a message in the resulting changelog entry:
@@ -628,7 +668,7 @@ http://netbox/api/dcim/sites/ \
}'
```
This approach works when creating, modifying, or deleting objects, either individually or in bulk.
This approach works when creating, modifying, or deleting objects, either individually or in bulk. For more information about change logging, see [Change Logging](../features/change-logging.md).
## Uploading Files

View File

@@ -32,6 +32,14 @@ class MyFilterSet(NetBoxModelFilterSet):
fields = ('some', 'other', 'fields')
```
In addition to the base NetBoxModelFilterSet class, the following filterset classes are also available for subclasses of standard base models.
| Model Class | FilterSet Class |
|-----------------------|--------------------------------------------------|
| `PrimaryModel` | `netbox.filtersets.PrimaryModelFilterSet` |
| `OrganizationalModel` | `netbox.filtersets.OrganizationalModelFilterSet` |
| `NestedGroupModel` | `netbox.filtersets.NestedGroupModelFilterSet` |
### Declaring Filter Sets
To utilize a filter set in a subclass of one of NetBox's generic views (such as `ObjectListView` or `BulkEditView`), define the `filterset` attribute on the view class:

View File

@@ -2,7 +2,7 @@
## Form Classes
NetBox provides several base form classes for use by plugins.
NetBox provides several base form classes for use by plugins. Additional form classes are also available for other standard base model classes (PrimaryModel, OrganizationalModel, and NestedGroupModel).
| Form Class | Purpose |
|----------------------------|--------------------------------------|
@@ -19,7 +19,17 @@ This is the base form for creating and editing NetBox models. It extends Django'
|-------------|---------------------------------------------------------------------------------------|
| `fieldsets` | A tuple of `FieldSet` instances which control how form fields are rendered (optional) |
**Example**
#### Subclasses
The corresponding model-specific subclasses of `NetBoxModelForm` are documented below.
| Model Class | Form Class |
|-----------------------|---------------------------|
| `PrimaryModel` | `PrimaryModelForm` |
| `OrganizationalModel` | `OrganizationalModelForm` |
| `NestedGroupModel` | `NestedGroupModelForm` |
#### Example
```python
from django.utils.translation import gettext_lazy as _
@@ -49,9 +59,19 @@ class MyModelForm(NetBoxModelForm):
### `NetBoxModelImportForm`
This form facilitates the bulk import of new objects from CSV, JSON, or YAML data. As with model forms, you'll need to declare a `Meta` subclass specifying the associated `model` and `fields`. NetBox also provides several form fields suitable for import various types of CSV data, listed below.
This form facilitates the bulk import of new objects from CSV, JSON, or YAML data. As with model forms, you'll need to declare a `Meta` subclass specifying the associated `model` and `fields`. NetBox also provides several form fields suitable for importing various types of CSV data, listed [below](#csv-import-fields).
**Example**
#### Subclasses
The corresponding model-specific subclasses of `NetBoxModelImportForm` are documented below.
| Model Class | Form Class |
|-----------------------|---------------------------------|
| `PrimaryModel` | `PrimaryModelImportForm` |
| `OrganizationalModel` | `OrganizationalModelImportForm` |
| `NestedGroupModel` | `NestedGroupModelImportForm` |
#### Example
```python
from django.utils.translation import gettext_lazy as _
@@ -83,7 +103,17 @@ This form facilitates editing multiple objects in bulk. Unlike a model form, thi
| `fieldsets` | A tuple of `FieldSet` instances which control how form fields are rendered (optional) |
| `nullable_fields` | A tuple of fields which can be nullified (set to empty) using the bulk edit form (optional) |
**Example**
#### Subclasses
The corresponding model-specific subclasses of `NetBoxModelBulkEditForm` are documented below.
| Model Class | Form Class |
|-----------------------|-----------------------------------|
| `PrimaryModel` | `PrimaryModelBulkEditForm` |
| `OrganizationalModel` | `OrganizationalModelBulkEditForm` |
| `NestedGroupModel` | `NestedGroupModelBulkEditForm` |
#### Example
```python
from django import forms
@@ -125,7 +155,17 @@ This form class is used to render a form expressly for filtering a list of objec
| `model` | The model of object being edited |
| `fieldsets` | A tuple of `FieldSet` instances which control how form fields are rendered (optional) |
**Example**
#### Subclasses
The corresponding model-specific subclasses of `NetBoxModelFilterSetForm` are documented below.
| Model Class | Form Class |
|-----------------------|------------------------------------|
| `PrimaryModel` | `PrimaryModelFilterSetForm` |
| `OrganizationalModel` | `OrganizationalModelFilterSetForm` |
| `NestedGroupModel` | `NestedGroupModelFilterSetForm` |
#### Example
```python
from dcim.models import Site

View File

@@ -46,3 +46,19 @@ NetBox provides two object type classes for use by plugins.
::: netbox.graphql.types.NetBoxObjectType
options:
members: false
## GraphQL Filters
NetBox provides a base filter class for use by plugins which employ subclasseses of `NetBoxModel`.
::: netbox.graphql.filters.NetBoxModelFilter
options:
members: false
Additionally, the following filter classes are available for subclasses of standard base models.
| Model Class | FilterSet Class |
|-----------------------|----------------------------------------------------|
| `PrimaryModel` | `netbox.graphql.filters.PrimaryModelFilter` |
| `OrganizationalModel` | `netbox.graphql.filters.OrganizationalModelFilter` |
| `NestedGroupModel` | `netbox.graphql.filters.NestedGroupModelFilter` |

View File

@@ -74,7 +74,7 @@ The plugin source directory contains all the actual Python code and other resour
The `PluginConfig` class is a NetBox-specific wrapper around Django's built-in [`AppConfig`](https://docs.djangoproject.com/en/stable/ref/applications/) class. It is used to declare NetBox plugin functionality within a Python package. Each plugin should provide its own subclass, defining its name, metadata, and default and required configuration parameters. An example is below:
```python
```python title="__init__.py"
from netbox.plugins import PluginConfig
class FooBarConfig(PluginConfig):
@@ -151,7 +151,7 @@ Any additional apps must be installed within the same Python environment as NetB
An example `pyproject.toml` is below:
```
```toml title="pyproject.toml"
# See PEP 518 for the spec of this file
# https://www.python.org/dev/peps/pep-0518/
@@ -179,11 +179,24 @@ classifiers=[
]
requires-python = ">=3.12.0"
```
Many of these are self-explanatory, but for more information, see the [pyproject.toml documentation](https://packaging.python.org/en/latest/specifications/pyproject-toml/).
## Compatibility Matrix
Consider adding a file named `COMPATIBILITY.md` to your plugin project root (alongside `pyproject.toml`). This file should contain a table listing the minimum and maximum supported versions of NetBox (`min_version` and `max_version`) for each release. This serves as a handy reference for users who are upgrading from a previous version of your plugin. An example is shown below:
```markdown title="COMPATIBILITY.md"
# Compatibility Matrix
| Release | Minimum NetBox Version | Maximum NetBox Version |
|---------|------------------------|------------------------|
| 0.2.0 | 4.4.0 | 4.5.x |
| 0.1.1 | 4.3.0 | 4.4.x |
| 0.1.0 | 4.3.0 | 4.4.x |
```
## Create a Virtual Environment
It is strongly recommended to create a Python [virtual environment](https://docs.python.org/3/tutorial/venv.html) for the development of your plugin, as opposed to using system-wide packages. This will afford you complete control over the installed versions of all dependencies and avoid conflict with system packages. This environment can live wherever you'd like;however, it should be excluded from revision control. (A popular convention is to keep all virtual environments in the user's home directory, e.g. `~/.virtualenvs/`.)

View File

@@ -67,6 +67,46 @@ class MyModel(ExportTemplatesMixin, TagsMixin, models.Model):
...
```
### Additional Models
In addition to the base NetBoxModel class, the following additional classes are provided for convenience.
!!! info "These model classes were added to the plugins API in NetBox v4.5."
#### PrimaryModel
PrimaryModel is the go-to class for most object types. It extends NetBoxModel with `description` and `comments` fields, and it introduces support for ownership assignment.
| Field | Required | Unique | Description |
|---------------|----------|--------|---------------------------------------------|
| `owner` | No | No | The object's owner |
| `description` | No | No | A human-friendly description for the object |
| `comments` | No | No | General comments |
#### OrganizationalModel
OrganizationalModel is used by object types whose function is primarily the organization of other objects.
| Field | Required | Unique | Description |
|---------------|----------|--------|---------------------------------------------|
| `name` | Yes | Yes | The name of the object |
| `slug` | Yes | Yes | A unique URL-friendly identifier |
| `owner` | No | No | The object's owner |
| `description` | No | No | A human-friendly description for the object |
#### NestedGroupModel
NestedGroupModel is used for objects which arrange into a recursive hierarchy (like regions and locations) via its self-referential `parent` foreign key.
| Field | Required | Unique | Description |
|---------------|----------|--------|-----------------------------------------------------------------|
| `name` | Yes | Yes | The name of the object |
| `slug` | Yes | Yes | A unique URL-friendly identifier |
| `parent` | No | No | The object (of the same type) under which this object is nested |
| `owner` | No | No | The object's owner |
| `description` | No | No | A human-friendly description for the object |
| `comments` | No | No | General comments |
## Database Migrations
Once you have completed defining the model(s) for your plugin, you'll need to create the database schema migrations. A migration file is essentially a set of instructions for manipulating the PostgreSQL database to support your new model, or to alter existing models. Creating migrations can usually be done automatically using Django's `makemigrations` management command. (Ensure that your plugin has been installed and enabled first, otherwise it won't be found.)

View File

@@ -27,6 +27,14 @@ Serializers are responsible for converting Python objects to JSON data suitable
The default nested representation of an object is defined by the `brief_fields` attributes under the serializer's `Meta` class. (Older versions of NetBox required the definition of a separate nested serializer.)
In addition to the base NetBoxModelSerializer class, the following serializer classes are also available for subclasses of standard base models.
| Model Class | Serializer Class |
|-----------------------|--------------------------------------------------------|
| `PrimaryModel` | `netbox.api.serializers.PrimaryModelSerializer` |
| `OrganizationalModel` | `netbox.api.serializers.OrganizationalModelSerializer` |
| `NestedGroupModel` | `netbox.api.serializers.NestedGroupModelSerializer` |
#### Example
To create a serializer for a plugin model, subclass `NetBoxModelSerializer` in `api/serializers.py`. Specify the model class and the fields to include within the serializer's `Meta` class.

View File

@@ -36,6 +36,14 @@ class MyModelTable(NetBoxTable):
default_columns = ('pk', 'name', ...)
```
In addition to the base NetBoxTable class, the following table classes are also available for subclasses of standard base models.
| Model Class | Table Class |
|-----------------------|------------------------------------------|
| `PrimaryModel` | `netbox.tables.PrimaryModelTable` |
| `OrganizationalModel` | `netbox.tables.OrganizationalModelTable` |
| `NestedGroupModel` | `netbox.tables.NestedGroupModelTable` |
### Table Configuration
The NetBoxTable class features dynamic configuration to allow users to change their column display and ordering preferences. To configure a table for a specific request, simply call its `configure()` method and pass the current HTTPRequest object. For example:

View File

@@ -10,6 +10,14 @@ Minor releases are published in April, August, and December of each calendar yea
This page contains a history of all major and minor releases since NetBox v2.0. For more detail on a specific patch release, please see the release notes page for that specific minor release.
#### [Version 4.5](./version-4.5.md) (January 2026)
* Lookup Modifiers in Filter Forms ([#7604](https://github.com/netbox-community/netbox/issues/7604))
* Improved API Authentication Tokens ([#20210](https://github.com/netbox-community/netbox/issues/20210))
* Object Ownership ([#20304](https://github.com/netbox-community/netbox/issues/20304))
* Advanced Port Mappings ([#20564](https://github.com/netbox-community/netbox/issues/20564))
* Cable Profiles ([#20788](https://github.com/netbox-community/netbox/issues/20788))
#### [Version 4.4](./version-4.4.md) (September 2025)
* Background Jobs for Bulk Operations ([#19589](https://github.com/netbox-community/netbox/issues/19589), [#19891](https://github.com/netbox-community/netbox/issues/19891))

View File

@@ -1,5 +1,69 @@
# NetBox v4.4
## v4.4.10 (2026-01-06)
### Enhancements
* [#20953](https://github.com/netbox-community/netbox/issues/20953) - Show reverse bridge relationships on interface detail pages
* [#21071](https://github.com/netbox-community/netbox/issues/21071) - Include request method & URL when displaying server errors
### Bug Fixes
* [#19506](https://github.com/netbox-community/netbox/issues/19506) - Add filter forms for component templates to ensure object selector support
* [#20044](https://github.com/netbox-community/netbox/issues/20044) - Fix dark mode support for rack elevations
* [#20320](https://github.com/netbox-community/netbox/issues/20320) - Restore support for selecting related interfaces when bulk editing device interfaces
* [#20817](https://github.com/netbox-community/netbox/issues/20817) - Re-enable sync button when disabling scheduled syncing for a data source
* [#21045](https://github.com/netbox-community/netbox/issues/21045) - Fix `ValueError` exception when saving a site with an assigned prefix
* [#21049](https://github.com/netbox-community/netbox/issues/21049) - Ignore stale custom field data when validating an object
* [#21063](https://github.com/netbox-community/netbox/issues/21063) - Check for duplicate choice values when validating a custom field choice set
* [#21064](https://github.com/netbox-community/netbox/issues/21064) - Ensures that extra choices in custom field choice sets preserve escaped colons
---
## v4.4.9 (2025-12-23)
### Enhancements
* [#20309](https://github.com/netbox-community/netbox/issues/20309) - Support ASDOT notation for ASN ranges
* [#20720](https://github.com/netbox-community/netbox/issues/20720) - Add Latvian translations
* [#20900](https://github.com/netbox-community/netbox/issues/20900) - Allow filtering custom choice fields by multiple values in the UI
### Bug Fixes
* [#17976](https://github.com/netbox-community/netbox/issues/17976) - Remove `devicetype_count` from nested manufacturer to correct OpenAPI schema
* [#20011](https://github.com/netbox-community/netbox/issues/20011) - Provide a clear message when encountering duplicate object IDs during bulk import
* [#20114](https://github.com/netbox-community/netbox/issues/20114) - Preserve `parent_bay` during device bulk import when tags are present
* [#20491](https://github.com/netbox-community/netbox/issues/20491) - Improve handling of numeric ranges in tests
* [#20873](https://github.com/netbox-community/netbox/issues/20873) - Fix `AttributeError` exception triggered by event rules associated with an object that supports file attachments
* [#20875](https://github.com/netbox-community/netbox/issues/20875) - Ensure that parent object relations are cached (for filtering) on device/module components during instantiation
* [#20876](https://github.com/netbox-community/netbox/issues/20876) - Allow editing an IP address that resides within a range marked as populated
* [#20912](https://github.com/netbox-community/netbox/issues/20912) - Fix inconsistent clearing of `module` field on ModuleBay
* [#20944](https://github.com/netbox-community/netbox/issues/20944) - Ensure cached scope is updated on child objects when a parent region/site/location is changed
* [#20948](https://github.com/netbox-community/netbox/issues/20948) - Handle the deletion of related objects with `on_delete=RESTRICT` the same as `CASCADE`
* [#20966](https://github.com/netbox-community/netbox/issues/20966) - Fix UI rendering issue when scrolling list of object types in permissions form
* [#20969](https://github.com/netbox-community/netbox/issues/20969) - Fix querying of front port templates by `rear_port_id`
* [#21011](https://github.com/netbox-community/netbox/issues/21011) - Avoid writing to the database when loading active ConfigRevision
* [#21032](https://github.com/netbox-community/netbox/issues/21032) - Avoid SQL subquery in RestrictedQuerySet where unnecessary
---
## v4.4.8 (2025-12-09)
### Enhancements
* [#20068](https://github.com/netbox-community/netbox/issues/20068) - Support the assignment of module type profile attributes via bulk import
* [#20914](https://github.com/netbox-community/netbox/issues/20914) - Enable filtering device components by tenant assigned to device
### Bug Fixes
* [#19918](https://github.com/netbox-community/netbox/issues/19918) - Fix support for `{module}` resolution of components of child modules
* [#20759](https://github.com/netbox-community/netbox/issues/20759) - Improve legibility of object types in permissions form
* [#20860](https://github.com/netbox-community/netbox/issues/20860) - Ensure user-provided changelog message is recorded when creating device components via the UI
* [#20878](https://github.com/netbox-community/netbox/issues/20878) - Use the active database connection when executing custom scripts
* [#20888](https://github.com/netbox-community/netbox/issues/20888) - Resolve warnings about non-decimal values for min/max latitude & longitude fields
---
## v4.4.7 (2025-11-25)
### Enhancements

View File

@@ -0,0 +1,235 @@
# NetBox v4.5
## v4.5.2 (2026-02-03)
### Enhancements
* [#15801](https://github.com/netbox-community/netbox/issues/15801) - Add link peer and connection columns to the VLAN device interfaces table
* [#19221](https://github.com/netbox-community/netbox/issues/19221) - Truncate long image attachment filenames in the UI
* [#19869](https://github.com/netbox-community/netbox/issues/19869) - Display peer connections for LAG member interfaces
* [#20052](https://github.com/netbox-community/netbox/issues/20052) - Increase logging level of error message when a custom script fails to load
* [#20172](https://github.com/netbox-community/netbox/issues/20172) - Add `cabled` filter for interfaces in GraphQL API
* [#21081](https://github.com/netbox-community/netbox/issues/21081) - Add owner group table columns & filters across all supported object list views
* [#21088](https://github.com/netbox-community/netbox/issues/21088) - Add max depth and max length dropdowns for child prefix views
* [#21110](https://github.com/netbox-community/netbox/issues/21110) - Support cursor-based pagination in GraphQL API
* [#21201](https://github.com/netbox-community/netbox/issues/21201) - Pre-populate GenericForeignKey form fields when cloning
* [#21209](https://github.com/netbox-community/netbox/issues/21209) - Ignore case sensitivity for configuration parameters which specify an app label and model name
* [#21228](https://github.com/netbox-community/netbox/issues/21228) - Support image attachments for rack types
* [#21244](https://github.com/netbox-community/netbox/issues/21244) - Enable omitting specific fields from REST API responses with `?omit=` parameter
### Performance Improvements
* [#21249](https://github.com/netbox-community/netbox/issues/21249) - Avoid extraneous user query when no event rules are present
* [#21259](https://github.com/netbox-community/netbox/issues/21259) - Cache ObjectType lookups for the duration of a request
* [#21260](https://github.com/netbox-community/netbox/issues/21260) - Defer object serialization for events pipeline processing
* [#21263](https://github.com/netbox-community/netbox/issues/21263) - Prefetch related objects after creating/updating objects via REST API
* [#21300](https://github.com/netbox-community/netbox/issues/21300) - Cache custom field lookups for the duration of a request
* [#21302](https://github.com/netbox-community/netbox/issues/21302) - Avoid redundant uniqueness checks in ValidatedModelSerializer
* [#21303](https://github.com/netbox-community/netbox/issues/21303) - Cache post-change snapshot on each instance after serialization
* [#21327](https://github.com/netbox-community/netbox/issues/21327) - Always leverage `get_by_natural_key()` to resolve ContentTypes
### Bug Fixes
* [#20212](https://github.com/netbox-community/netbox/issues/20212) - Fix support for image attachment thumbnails when using S3 storage
* [#20383](https://github.com/netbox-community/netbox/issues/20383) - When editing a device, clearing the assigned unit should also clear the rack face selection
* [#20902](https://github.com/netbox-community/netbox/issues/20902) - Avoid `SyncError` exception when Git URL contains an embedded username
* [#20977](https://github.com/netbox-community/netbox/issues/20977) - "Run again" button should respect script variable defaults
* [#21115](https://github.com/netbox-community/netbox/issues/21115) - Include `attribute_data` in ModuleType YAML export
* [#21129](https://github.com/netbox-community/netbox/issues/21129) - Store queue name on the Job model to ensure deletion of associated RQ task when a non-default queue is used
* [#21168](https://github.com/netbox-community/netbox/issues/21168) - Fix Application Service cloning to preserve parent object
* [#21173](https://github.com/netbox-community/netbox/issues/21173) - Ensure all plugin menu items are registered regardless of initialization order
* [#21176](https://github.com/netbox-community/netbox/issues/21176) - Remove checkboxes from IP ranges in mixed-type tables
* [#21202](https://github.com/netbox-community/netbox/issues/21202) - Fix scoped form cloning clearing the `scope` field when `scope_type` changes
* [#21214](https://github.com/netbox-community/netbox/issues/21214) - Clean up AutoSyncRecord when detaching from DataSource
* [#21242](https://github.com/netbox-community/netbox/issues/21242) - Navigation menu items for authentication should not require `staff_only` permission
* [#21254](https://github.com/netbox-community/netbox/issues/21254) - Fix `AttributeError` exception when checking for latest release
* [#21262](https://github.com/netbox-community/netbox/issues/21262) - Assigned scope should be replicated when cloning a prefix
* [#21269](https://github.com/netbox-community/netbox/issues/21269) - Fix replication of front/rear port assignments from the module type when installing a module
---
## v4.5.1 (2026-01-20)
### Enhancements
* [#21018](https://github.com/netbox-community/netbox/issues/21018) - Enable filtering prefixes by location/site/site group/region directly via GraphQL API
* [#21142](https://github.com/netbox-community/netbox/issues/21142) - Enable filtering device components by site/location/rack directly via GraphQL API
* [#21144](https://github.com/netbox-community/netbox/issues/21144) - Enable specifying a prefix length for IP addresses when utilizing the `/api/ipam/prefixes/<id>/available-ips/` REST API endpoint
* [#21165](https://github.com/netbox-community/netbox/issues/21165) - VLAN selector should default to group (instead of site)
* [#21178](https://github.com/netbox-community/netbox/issues/21178) - Improve consistency of rack measurements in UI
### Bug Fixes
* [#19901](https://github.com/netbox-community/netbox/issues/19901) - Fix `RelatedObjectDoesNotExist` exception when importing modules into unnamed devices
* [#20239](https://github.com/netbox-community/netbox/issues/20239) - Prevent shared mutable state in PluginMenuItem & PluginMenuButton
* [#20933](https://github.com/netbox-community/netbox/issues/20933) - Fix writable `data_file` assignment for ConfigContext and ConfigContextProfile via the REST API
* [#21039](https://github.com/netbox-community/netbox/issues/21039) - Fix support for AVIF image uploads
* [#21050](https://github.com/netbox-community/netbox/issues/21050) - Clear device OOB IP assignments when reassigning IP addresses
* [#21051](https://github.com/netbox-community/netbox/issues/21051) - Remove irrelevant object types from permissions form
* [#21097](https://github.com/netbox-community/netbox/issues/21097) - Fix comparison lookups for ID filters in GraphQL API
* [#21102](https://github.com/netbox-community/netbox/issues/21102) - Fix GraphiQL explorer UI
* [#21117](https://github.com/netbox-community/netbox/issues/21117) - Avoid `ValueError` exception when `API_TOKEN_PEPPERS` is not defined
* [#21118](https://github.com/netbox-community/netbox/issues/21118) - Address performance issue when saving sites with many assigned objects
* [#21124](https://github.com/netbox-community/netbox/issues/21124) - Fix front/rear port mapping for module types
* [#21134](https://github.com/netbox-community/netbox/issues/21134) - Fix bulk renaming for module types
* [#21139](https://github.com/netbox-community/netbox/issues/21139) - Support `fields` parameter for job, object change, and object type REST API endpoints
* [#21140](https://github.com/netbox-community/netbox/issues/21140) - Restore translation for object attribute labels on several UI views
* [#21160](https://github.com/netbox-community/netbox/issues/21160) - Fix performance issue loading UI views caused by unintended `APISelect` choices resolution
* [#21166](https://github.com/netbox-community/netbox/issues/21166) - Fix support for 32-bit ASN filtering in GraphQL API
* [#21175](https://github.com/netbox-community/netbox/issues/21175) - Fix pending migrations warning when `DEFAULT_LANGUAGE` is set
* [#21181](https://github.com/netbox-community/netbox/issues/21181) - Handle `AuthenticationFailed` exception when using an invalid API token to fetch media files
* [#21213](https://github.com/netbox-community/netbox/issues/21213) - Tag weight field should be marked as required in UI forms
* [#21231](https://github.com/netbox-community/netbox/issues/21231) - Presence of object types table should be checked only during migration (performance improvement)
---
## v4.5.0 (2026-01-06)
### Breaking Changes
* Python 3.10 and 3.11 are no longer supported. NetBox now requires Python 3.12, 3.13, or 3.14.
* GraphQL API queries which filter by object IDs or enums must now specify a filter lookup similar to other fields. For example, `id: 123` becomes `id: {exact: 123 }`.
* Rendering a device or virtual machine configuration is now restricted to users with the `render_config` permission for the applicable object type.
* Retrieval of API token plaintexts is no longer supported. The `ALLOW_TOKEN_RETRIEVAL` config parameter has been removed.
* API tokens can no longer be reassigned from one user to another.
* A config context assigned to a platform will now also apply to any children of that platform. (Although this is typically desired behavior, it may introduce unanticipated changes for existing deployments.)
* The `/api/dcim/cable-terminations/` REST API endpoint is now read-only. Cable terminations must be set on cables directly via the `/api/dcim/cables/` endpoint.
* The UI view dedicated to swapping A/Z circuit terminations has been removed.
* The experimental HTMX navigation feature has been removed.
* The obsolete boolean field `is_staff` has been removed from the `User` model.
* Removal of deprecated behavior
* The `/api/extras/object-types/` REST API endpoint has been removed. (Use `/api/core/object-types/` instead.)
* Webhooks no longer specify a `model` in payload data. (Reference `object_type` instead, which includes the parent app label.)
* The obsolete module `core.models.contenttypes` has been removed (replaced in v4.4 by `core.models.object_types`).
* The `load_yaml()` and `load_json()` utility methods have been removed from the base class for custom scripts.
### New Features
#### Lookup Modifiers in Filter Forms ([#7604](https://github.com/netbox-community/netbox/issues/7604))
Most object list filters within the UI have been extended to include optional lookup modifiers to support more complex queries. For instance, filters for numeric values now include a dropdown where a user can select "less than," "greater than," or "not" in addition to the default equivalency match. The specific modifiers available depend on the type of each filter. Plugins can register their own filtersets using the `register_filterset()` decorator to enable this new functionality.
(Note that this feature does not introduce any new filters. Rather, it makes available in the UI filters which already exist.)
#### Improved API Authentication Tokens ([#20210](https://github.com/netbox-community/netbox/issues/20210))
This release introduces a new version of API token (v2) which implements several security improvements. HMAC hashing with a cryptographic pepper is used to authenticate these tokens, obviating the need to store plaintexts. The new tokens also employ a non-sensitive key which can be shared to identify tokens without divulging their plaintexts. We've also adopted the standard "bearer" HTTP header format, as shown below.
```
# v1 token header
Authorization: Token <TOKEN>
# v2 token header
Authorization: Bearer nbt_<KEY>.<TOKEN>
```
Note that v2 token keys are prefixed with the fixed string `nbt_`, which can be used to aid in secret detection.
Backward compatibility with legacy (v1) tokens is retained in this release. However, users are strongly encouraged to begin using only v2 tokens, as support for legacy tokens will be removed in NetBox v4.7.
#### Object Ownership ([#20304](https://github.com/netbox-community/netbox/issues/20304))
An optional `owner` foreign key field has been added to most models. This enables the assignment of objects to a new Owner model, which represents a set of users and/or groups. Through this relationship, we can now convey ownership of objects within NetBox natively, without needing to rely on the assignment of tags or custom fields.
(Note that ownership differs significantly in function from tenancy. Ownership determines the parties responsible for the maintenance of an object, whereas as tenancy conveys an operational dependency.)
#### Advanced Port Mappings ([#20564](https://github.com/netbox-community/netbox/issues/20564))
The previous many-to-one mapping of front to rear ports has been expanded to support bidirectional mappings. The `rear_port` and `rear_port_position` fields on the FrontPort model have been replaced with an intermediary PortMapping model, which supports any number of assignments between front port/position pair and a rear port/position pair. This change unlocks the ability to model complex inline devices that swap individual fiber pairs between cables.
#### Cable Profiles ([#20788](https://github.com/netbox-community/netbox/issues/20788))
Cables can now be assigned profiles which determine how they are treated for path tracing. A profile indicates the number of discrete parallel channels or lanes carried by the cable among its endpoints. For example, a 1-to-4 breakout cable has four lanes, shared at one end via a common termination and split out at the other end to four separate terminations. Profiles, when assigned, enable NetBox to more accurately trace a specific connection within a cable, rather than the cable as a whole.
The assignment of cable profiles is optional: Cable tracing will continue to operate as before for cables with no profile assigned.
### Enhancements
* [#16681](https://github.com/netbox-community/netbox/issues/16681) - Introduce a `render_config` permission, which is now required to render a device or virtual machine configuration
* [#18658](https://github.com/netbox-community/netbox/issues/18658) - Add a `start_on_boot` choice field for virtual machines
* [#19095](https://github.com/netbox-community/netbox/issues/19095) - Add support for Python 3.13 and 3.14
* [#19338](https://github.com/netbox-community/netbox/issues/19338) - Enable filter lookups for object IDs and enums in GraphQL API queries
* [#19523](https://github.com/netbox-community/netbox/issues/19523) - Cache the number of instances for device, module, and rack types, and enable filtering by these counts
* [#20417](https://github.com/netbox-community/netbox/issues/20417) - Add an optional `color` field for device type power outlets
* [#20476](https://github.com/netbox-community/netbox/issues/20476) - Once provisioned, the owner of an API token cannot be changed
* [#20492](https://github.com/netbox-community/netbox/issues/20492) - Completely disabled the means to retrieve legacy API token plaintexts (removed the `ALLOW_TOKEN_RETRIEVAL` config parameter)
* [#20639](https://github.com/netbox-community/netbox/issues/20639) - Apply config contexts to devices/VMs assigned any child platform of the parent platform
* [#20834](https://github.com/netbox-community/netbox/issues/20834) - Add an `enabled` boolean field to API tokens
* [#20917](https://github.com/netbox-community/netbox/issues/20917) - Include usage reference on API token views
* [#20925](https://github.com/netbox-community/netbox/issues/20925) - Add optional `comments` field to all subclasses of `OrganizationalModel`
* [#20929](https://github.com/netbox-community/netbox/issues/20929) - Require the `render_config` permission to view a rendered device/VM configuration in the UI
* [#20936](https://github.com/netbox-community/netbox/issues/20936) - Introduce the `/api/authentication-check/` REST API endpoint for validating authentication tokens
* [#20959](https://github.com/netbox-community/netbox/issues/20959) - Include a count of related module types for a manufacturer in the REST API
### Plugins
* [#13182](https://github.com/netbox-community/netbox/issues/13182) - Added `PrimaryModel`, `OrganizationalModel`, and `NestedGroupModel` to the plugins API, as well as their respective base classes for various resources
### Other Changes
* [#16137](https://github.com/netbox-community/netbox/issues/16137) - Remove the obsolete boolean field `is_staff` from the `User` model
* [#17571](https://github.com/netbox-community/netbox/issues/17571) - Remove the experimental HTMX navigation feature
* [#17936](https://github.com/netbox-community/netbox/issues/17936) - Introduce a dedicated `GFKSerializerField` for representing generic foreign keys in API serializers
* [#19889](https://github.com/netbox-community/netbox/issues/19889) - Drop support for Python 3.10 and 3.11
* [#19898](https://github.com/netbox-community/netbox/issues/19898) - Remove the obsolete REST API endpoint `/api/extras/object-types/`
* [#20088](https://github.com/netbox-community/netbox/issues/20088) - Remove the non-deterministic `model` key from webhook payload data
* [#20095](https://github.com/netbox-community/netbox/issues/20095) - Remove the obsolete module `core.models.contenttypes`
* [#20096](https://github.com/netbox-community/netbox/issues/20096) - Remove the `load_yaml()` and `load_json()` utility methods from the `BaseScript` class
* [#20204](https://github.com/netbox-community/netbox/issues/20204) - Started migrating object views from custom HTML templates to declarative layouts
* [#20295](https://github.com/netbox-community/netbox/issues/20295) - Cable terminations may be modified via the REST API only by modifying the cable itself
* [#20617](https://github.com/netbox-community/netbox/issues/20617) - Introduce `BaseModel` as the global base class for models
* [#20683](https://github.com/netbox-community/netbox/issues/20683) - Remove the UI view dedicated to swapping A/Z circuit terminations
* [#20926](https://github.com/netbox-community/netbox/issues/20926) - Standardize naming of GraphQL filters
### REST API Changes
* Most objects now include an optional `owner` foreign key field.
* The `/api/dcim/cable-terminations` endpoint is now read-only.
* Introduced the `/api/authentication-check/` endpoint to test REST API credentials
* `circuits.CircuitGroup`
* Add optional `comments` field
* `circuits.CircuitType`
* Add optional `comments` field
* `circuits.VirtualCircuitType`
* Add optional `comments` field
* `dcim.Cable`
* Add the optional `profile` choice field
* `dcim.FrontPort`
* Removed the `rear_port` and `rear_port_position` fields
* Add the `positions` integer field
* Add the `rear_ports` list for port mappings
* `dcim.InventoryItemRole`
* Add optional `comments` field
* `dcim.Manufacturer`
* Add optional `comments` field
* Add read-only `moduletype_count` integer field
* `dcim.ModuleType`
* Add read-only `module_count` integer field
* `dcim.PowerOutletTemplate`
* Add optional `color` field
* `dcim.RackRole`
* Add optional `comments` field
* `dcim.RackType`
* Add read-only `rack_count` integer field
* `dcim.RearPort`
* Add the `front_ports` list for port mappings
* `ipam.ASNRange`
* Add optional `comments` field
* `ipam.RIR`
* Add optional `comments` field
* `ipam.Role`
* Add optional `comments` field
* `ipam.VLANGroup`
* Add optional `comments` field
* `tenancy.ContactRole`
* Add optional `comments` field
* `users.Token`
* Add `enabled` boolean field
* `virtualization.ClusterGroup`
* Add optional `comments` field
* `virtualization.ClusterType`
* Add optional `comments` field
* `virtualization.VirtualMachine`
* Add optional `start_on_boot` choice field
* `vpn.TunnelGroup`
* Add optional `comments` field

View File

@@ -322,6 +322,7 @@ nav:
- git Cheat Sheet: 'development/git-cheat-sheet.md'
- Release Notes:
- Summary: 'release-notes/index.md'
- Version 4.5: 'release-notes/version-4.5.md'
- Version 4.4: 'release-notes/version-4.4.md'
- Version 4.3: 'release-notes/version-4.3.md'
- Version 4.2: 'release-notes/version-4.2.md'

View File

@@ -1,6 +1,7 @@
from django.urls import include, path
from utilities.urls import get_model_urls
from . import views
app_name = 'account'

View File

@@ -2,14 +2,15 @@ import logging
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import login as auth_login, logout as auth_logout, update_session_auth_hash
from django.contrib.auth import login as auth_login
from django.contrib.auth import logout as auth_logout
from django.contrib.auth import update_session_auth_hash
from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import update_last_login
from django.contrib.auth.signals import user_logged_in
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect
from django.shortcuts import render, resolve_url
from django.shortcuts import get_object_or_404, redirect, render, resolve_url
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.utils.http import urlencode
@@ -18,7 +19,6 @@ from django.views.decorators.debug import sensitive_post_parameters
from django.views.generic import View
from social_core.backends.utils import load_backends
from account.models import UserToken
from core.models import ObjectChange
from core.tables import ObjectChangeTable
from extras.models import Bookmark
@@ -35,11 +35,13 @@ from utilities.request import safe_for_redirect
from utilities.string import remove_linebreaks
from utilities.views import register_model_view
from .models import UserToken
#
# Login/logout
#
class LoginView(View):
"""
Perform user authentication via the web UI.

View File

@@ -1,2 +1,2 @@
from .serializers_.providers import *
from .serializers_.circuits import *
from .serializers_.providers import *

View File

@@ -4,18 +4,28 @@ from rest_framework import serializers
from circuits.choices import CircuitPriorityChoices, CircuitStatusChoices, VirtualCircuitTerminationRoleChoices
from circuits.constants import CIRCUIT_GROUP_ASSIGNMENT_MEMBER_MODELS, CIRCUIT_TERMINATION_TERMINATION_TYPES
from circuits.models import (
Circuit, CircuitGroup, CircuitGroupAssignment, CircuitTermination, CircuitType, VirtualCircuit,
VirtualCircuitTermination, VirtualCircuitType,
Circuit,
CircuitGroup,
CircuitGroupAssignment,
CircuitTermination,
CircuitType,
VirtualCircuit,
VirtualCircuitTermination,
VirtualCircuitType,
)
from dcim.api.serializers_.device_components import InterfaceSerializer
from dcim.api.serializers_.cables import CabledObjectSerializer
from dcim.api.serializers_.device_components import InterfaceSerializer
from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountField
from netbox.api.gfk_fields import GFKSerializerField
from netbox.api.serializers import (
NetBoxModelSerializer, OrganizationalModelSerializer, PrimaryModelSerializer, WritableNestedSerializer,
NetBoxModelSerializer,
OrganizationalModelSerializer,
PrimaryModelSerializer,
WritableNestedSerializer,
)
from netbox.choices import DistanceUnitChoices
from tenancy.api.serializers_.tenants import TenantSerializer
from .providers import ProviderAccountSerializer, ProviderNetworkSerializer, ProviderSerializer
__all__ = (
@@ -38,7 +48,7 @@ class CircuitTypeSerializer(OrganizationalModelSerializer):
class Meta:
model = CircuitType
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'owner', 'tags',
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'owner', 'comments', 'tags',
'custom_fields', 'created', 'last_updated', 'circuit_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'circuit_count')
@@ -71,7 +81,7 @@ class CircuitGroupSerializer(OrganizationalModelSerializer):
class Meta:
model = CircuitGroup
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'description', 'tenant', 'owner', 'tags',
'id', 'url', 'display_url', 'display', 'name', 'slug', 'description', 'tenant', 'owner', 'comments', 'tags',
'custom_fields', 'created', 'last_updated', 'circuit_count'
]
brief_fields = ('id', 'url', 'display', 'name')
@@ -161,7 +171,7 @@ class VirtualCircuitTypeSerializer(OrganizationalModelSerializer):
class Meta:
model = VirtualCircuitType
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'owner', 'tags',
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'owner', 'comments', 'tags',
'custom_fields', 'created', 'last_updated', 'virtual_circuit_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'virtual_circuit_count')

View File

@@ -5,6 +5,7 @@ from ipam.api.serializers_.asns import ASNSerializer
from ipam.models import ASN
from netbox.api.fields import RelatedObjectCountField, SerializedPKRelatedField
from netbox.api.serializers import PrimaryModelSerializer
from .nested import NestedProviderAccountSerializer
__all__ = (

View File

@@ -1,6 +1,6 @@
from netbox.api.routers import NetBoxRouter
from . import views
from . import views
router = NetBoxRouter()
router.APIRootView = views.CircuitsRootView

View File

@@ -4,6 +4,7 @@ from circuits import filtersets
from circuits.models import *
from dcim.api.views import PassThroughPortMixin
from netbox.api.viewsets import NetBoxModelViewSet
from . import serializers

View File

@@ -9,7 +9,8 @@ class CircuitsConfig(AppConfig):
def ready(self):
from netbox.models.features import register_models
from . import signals, search # noqa: F401
from . import search, signals # noqa: F401
from .models import CircuitTermination
# Register models

View File

@@ -2,11 +2,11 @@ from django.utils.translation import gettext_lazy as _
from utilities.choices import ChoiceSet
#
# Circuits
#
class CircuitStatusChoices(ChoiceSet):
key = 'Circuit.status'

View File

@@ -1,6 +1,5 @@
from django.db.models import Q
# models values for ContentTypes which may be CircuitTermination termination types
CIRCUIT_TERMINATION_TERMINATION_TYPES = (
'region', 'sitegroup', 'site', 'location', 'providernetwork',

View File

@@ -9,9 +9,13 @@ from ipam.models import ASN
from netbox.filtersets import NetBoxModelFilterSet, OrganizationalModelFilterSet, PrimaryModelFilterSet
from tenancy.filtersets import ContactModelFilterSet, TenancyFilterSet
from utilities.filters import (
ContentTypeFilter, MultiValueCharFilter, MultiValueNumberFilter, TreeNodeMultipleChoiceFilter,
ContentTypeFilter,
MultiValueCharFilter,
MultiValueNumberFilter,
TreeNodeMultipleChoiceFilter,
)
from utilities.filtersets import register_filterset
from .choices import *
from .models import *
@@ -353,7 +357,7 @@ class CircuitTerminationFilterSet(NetBoxModelFilterSet, CabledObjectFilterSet):
model = CircuitTermination
fields = (
'id', 'termination_id', 'term_side', 'port_speed', 'upstream_speed', 'xconnect_id', 'description',
'mark_connected', 'pp_info', 'cable_end', 'cable_position',
'mark_connected', 'pp_info', 'cable_end', 'cable_connector',
)
def search(self, queryset, name, value):

View File

@@ -4,7 +4,10 @@ from django.core.exceptions import ObjectDoesNotExist
from django.utils.translation import gettext_lazy as _
from circuits.choices import (
CircuitCommitRateChoices, CircuitPriorityChoices, CircuitStatusChoices, VirtualCircuitTerminationRoleChoices,
CircuitCommitRateChoices,
CircuitPriorityChoices,
CircuitStatusChoices,
VirtualCircuitTerminationRoleChoices,
)
from circuits.constants import CIRCUIT_TERMINATION_TERMINATION_TYPES
from circuits.models import *
@@ -15,7 +18,10 @@ from netbox.forms import NetBoxModelBulkEditForm, OrganizationalModelBulkEditFor
from tenancy.models import Tenant
from utilities.forms import add_blank_choice, get_field_value
from utilities.forms.fields import (
ColorField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField,
ColorField,
ContentTypeChoiceField,
DynamicModelChoiceField,
DynamicModelMultipleChoiceField,
)
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import BulkEditNullBooleanSelect, DatePicker, HTMXSelect, NumberWithOptions
@@ -99,7 +105,7 @@ class CircuitTypeBulkEditForm(OrganizationalModelBulkEditForm):
fieldsets = (
FieldSet('color', 'description'),
)
nullable_fields = ('color', 'description')
nullable_fields = ('color', 'description', 'comments')
class CircuitBulkEditForm(PrimaryModelBulkEditForm):
@@ -241,7 +247,7 @@ class CircuitGroupBulkEditForm(OrganizationalModelBulkEditForm):
model = CircuitGroup
nullable_fields = (
'description', 'tenant',
'description', 'tenant', 'comments',
)
@@ -274,7 +280,7 @@ class VirtualCircuitTypeBulkEditForm(OrganizationalModelBulkEditForm):
fieldsets = (
FieldSet('color', 'description'),
)
nullable_fields = ('color', 'description')
nullable_fields = ('color', 'description', 'comments')
class VirtualCircuitBulkEditForm(PrimaryModelBulkEditForm):

View File

@@ -73,7 +73,7 @@ class CircuitTypeImportForm(OrganizationalModelImportForm):
class Meta:
model = CircuitType
fields = ('name', 'slug', 'color', 'description', 'owner', 'tags')
fields = ('name', 'slug', 'color', 'description', 'owner', 'comments', 'tags')
class CircuitImportForm(PrimaryModelImportForm):
@@ -176,7 +176,7 @@ class CircuitGroupImportForm(OrganizationalModelImportForm):
class Meta:
model = CircuitGroup
fields = ('name', 'slug', 'description', 'tenant', 'owner', 'tags')
fields = ('name', 'slug', 'description', 'tenant', 'owner', 'comments', 'tags')
class CircuitGroupAssignmentImportForm(NetBoxModelImportForm):
@@ -199,7 +199,7 @@ class VirtualCircuitTypeImportForm(OrganizationalModelImportForm):
class Meta:
model = VirtualCircuitType
fields = ('name', 'slug', 'color', 'description', 'owner', 'tags')
fields = ('name', 'slug', 'color', 'description', 'owner', 'comments', 'tags')
class VirtualCircuitImportForm(PrimaryModelImportForm):

View File

@@ -2,7 +2,10 @@ from django import forms
from django.utils.translation import gettext as _
from circuits.choices import (
CircuitCommitRateChoices, CircuitPriorityChoices, CircuitStatusChoices, CircuitTerminationSideChoices,
CircuitCommitRateChoices,
CircuitPriorityChoices,
CircuitStatusChoices,
CircuitTerminationSideChoices,
VirtualCircuitTerminationRoleChoices,
)
from circuits.models import *
@@ -10,7 +13,7 @@ from dcim.models import Location, Region, Site, SiteGroup
from ipam.models import ASN
from netbox.choices import DistanceUnitChoices
from netbox.forms import NetBoxModelFilterSetForm, OrganizationalModelFilterSetForm, PrimaryModelFilterSetForm
from tenancy.forms import TenancyFilterForm, ContactModelFilterForm
from tenancy.forms import ContactModelFilterForm, TenancyFilterForm
from utilities.forms import add_blank_choice
from utilities.forms.fields import ColorField, DynamicModelMultipleChoiceField, TagFilterField
from utilities.forms.rendering import FieldSet
@@ -34,9 +37,10 @@ __all__ = (
class ProviderFilterForm(ContactModelFilterForm, PrimaryModelFilterSetForm):
model = Provider
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('region_id', 'site_group_id', 'site_id', name=_('Location')),
FieldSet('asn_id', name=_('ASN')),
FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')),
)
region_id = DynamicModelMultipleChoiceField(
@@ -69,8 +73,9 @@ class ProviderFilterForm(ContactModelFilterForm, PrimaryModelFilterSetForm):
class ProviderAccountFilterForm(ContactModelFilterForm, PrimaryModelFilterSetForm):
model = ProviderAccount
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('provider_id', 'account', name=_('Attributes')),
FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')),
)
provider_id = DynamicModelMultipleChoiceField(
@@ -88,8 +93,9 @@ class ProviderAccountFilterForm(ContactModelFilterForm, PrimaryModelFilterSetFor
class ProviderNetworkFilterForm(PrimaryModelFilterSetForm):
model = ProviderNetwork
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('provider_id', 'service_id', name=_('Attributes')),
FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
)
provider_id = DynamicModelMultipleChoiceField(
queryset=Provider.objects.all(),
@@ -107,8 +113,9 @@ class ProviderNetworkFilterForm(PrimaryModelFilterSetForm):
class CircuitTypeFilterForm(OrganizationalModelFilterSetForm):
model = CircuitType
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('color', name=_('Attributes')),
FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
)
tag = TagFilterField(model)
@@ -121,7 +128,7 @@ class CircuitTypeFilterForm(OrganizationalModelFilterSetForm):
class CircuitFilterForm(TenancyFilterForm, ContactModelFilterForm, PrimaryModelFilterSetForm):
model = Circuit
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('provider_id', 'provider_account_id', 'provider_network_id', name=_('Provider')),
FieldSet(
'type_id', 'status', 'install_date', 'termination_date', 'commit_rate', 'distance', 'distance_unit',
@@ -129,6 +136,7 @@ class CircuitFilterForm(TenancyFilterForm, ContactModelFilterForm, PrimaryModelF
),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', name=_('Location')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')),
)
selector_fields = ('filter_id', 'q', 'region_id', 'site_group_id', 'site_id', 'provider_id', 'provider_network_id')
@@ -274,8 +282,9 @@ class CircuitTerminationFilterForm(NetBoxModelFilterSetForm):
class CircuitGroupFilterForm(TenancyFilterForm, OrganizationalModelFilterSetForm):
model = CircuitGroup
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
)
tag = TagFilterField(model)
@@ -312,8 +321,9 @@ class CircuitGroupAssignmentFilterForm(NetBoxModelFilterSetForm):
class VirtualCircuitTypeFilterForm(OrganizationalModelFilterSetForm):
model = VirtualCircuitType
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('color', name=_('Attributes')),
FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
)
tag = TagFilterField(model)
@@ -326,10 +336,11 @@ class VirtualCircuitTypeFilterForm(OrganizationalModelFilterSetForm):
class VirtualCircuitFilterForm(TenancyFilterForm, ContactModelFilterForm, PrimaryModelFilterSetForm):
model = VirtualCircuit
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('provider_id', 'provider_account_id', 'provider_network_id', name=_('Provider')),
FieldSet('type_id', 'status', name=_('Attributes')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
)
selector_fields = ('filter_id', 'q', 'provider_id', 'provider_network_id')
provider_id = DynamicModelMultipleChoiceField(

View File

@@ -4,7 +4,9 @@ from django.core.exceptions import ObjectDoesNotExist
from django.utils.translation import gettext_lazy as _
from circuits.choices import (
CircuitCommitRateChoices, CircuitTerminationPortSpeedChoices, VirtualCircuitTerminationRoleChoices,
CircuitCommitRateChoices,
CircuitTerminationPortSpeedChoices,
VirtualCircuitTerminationRoleChoices,
)
from circuits.constants import *
from circuits.models import *
@@ -14,7 +16,10 @@ from netbox.forms import NetBoxModelForm, OrganizationalModelForm, PrimaryModelF
from tenancy.forms import TenancyForm
from utilities.forms import get_field_value
from utilities.forms.fields import (
ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField,
ContentTypeChoiceField,
DynamicModelChoiceField,
DynamicModelMultipleChoiceField,
SlugField,
)
from utilities.forms.mixins import DistanceValidationMixin
from utilities.forms.rendering import FieldSet, InlineFields
@@ -97,7 +102,7 @@ class CircuitTypeForm(OrganizationalModelForm):
class Meta:
model = CircuitType
fields = [
'name', 'slug', 'color', 'description', 'tags',
'name', 'slug', 'color', 'description', 'comments', 'tags',
]
@@ -236,7 +241,7 @@ class CircuitGroupForm(TenancyForm, OrganizationalModelForm):
class Meta:
model = CircuitGroup
fields = [
'name', 'slug', 'description', 'tenant_group', 'tenant', 'owner', 'tags',
'name', 'slug', 'description', 'tenant_group', 'tenant', 'owner', 'comments', 'tags',
]
@@ -307,7 +312,7 @@ class VirtualCircuitTypeForm(OrganizationalModelForm):
class Meta:
model = VirtualCircuitType
fields = [
'name', 'slug', 'color', 'description', 'owner', 'tags',
'name', 'slug', 'color', 'description', 'owner', 'comments', 'tags',
]

View File

@@ -1,5 +1,5 @@
from dataclasses import dataclass
from typing import Annotated, TYPE_CHECKING
from typing import TYPE_CHECKING, Annotated
import strawberry
import strawberry_django

View File

@@ -1,10 +1,10 @@
from datetime import date
from typing import Annotated, TYPE_CHECKING
from typing import TYPE_CHECKING, Annotated
import strawberry
import strawberry_django
from strawberry.scalars import ID
from strawberry_django import BaseFilterLookup, FilterLookup, DateFilterLookup
from strawberry_django import BaseFilterLookup, DateFilterLookup, FilterLookup
from circuits import models
from circuits.graphql.filter_mixins import CircuitTypeFilterMixin
@@ -19,6 +19,7 @@ if TYPE_CHECKING:
from dcim.graphql.filters import InterfaceFilter, LocationFilter, RegionFilter, SiteFilter, SiteGroupFilter
from ipam.graphql.filters import ASNFilter
from netbox.graphql.filter_lookups import IntegerLookup
from .enums import *
__all__ = (

View File

@@ -1,4 +1,4 @@
from typing import Annotated, List, TYPE_CHECKING, Union
from typing import TYPE_CHECKING, Annotated, List, Union
import strawberry
import strawberry_django
@@ -8,6 +8,7 @@ from dcim.graphql.mixins import CabledObjectMixin
from extras.graphql.mixins import ContactsMixin, CustomFieldsMixin, TagsMixin
from netbox.graphql.types import BaseObjectType, ObjectType, OrganizationalObjectType, PrimaryObjectType
from tenancy.graphql.types import TenantType
from .filters import *
if TYPE_CHECKING:

View File

@@ -1,7 +1,8 @@
import django.db.models.deletion
from django.db import migrations, models
import ipam.fields
from utilities.json import CustomFieldJSONEncoder
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):

View File

@@ -1,6 +1,6 @@
from django.db import migrations, models
import django.db.models.deletion
import taggit.managers
from django.db import migrations, models
class Migration(migrations.Migration):

View File

@@ -1,6 +1,7 @@
# Generated by Django 4.2.5 on 2023-10-20 21:25
from django.db import migrations
import utilities.fields

View File

@@ -1,8 +1,9 @@
import django.db.models.deletion
import taggit.managers
import utilities.json
from django.db import migrations, models
import utilities.json
class Migration(migrations.Migration):
dependencies = [

View File

@@ -0,0 +1,39 @@
import django.contrib.postgres.fields
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('circuits', '0053_owner'),
]
operations = [
migrations.AddField(
model_name='circuittermination',
name='cable_connector',
field=models.PositiveSmallIntegerField(
blank=True,
null=True,
validators=[
django.core.validators.MinValueValidator(1),
django.core.validators.MaxValueValidator(256)
],
),
),
migrations.AddField(
model_name='circuittermination',
name='cable_positions',
field=django.contrib.postgres.fields.ArrayField(
base_field=models.PositiveSmallIntegerField(
validators=[
django.core.validators.MinValueValidator(1),
django.core.validators.MaxValueValidator(1024),
]
),
blank=True,
null=True,
size=None,
),
),
]

View File

@@ -1,23 +0,0 @@
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('circuits', '0053_owner'),
]
operations = [
migrations.AddField(
model_name='circuittermination',
name='cable_position',
field=models.PositiveIntegerField(
blank=True,
null=True,
validators=[
django.core.validators.MinValueValidator(1),
django.core.validators.MaxValueValidator(1024),
],
),
),
]

View File

@@ -0,0 +1,28 @@
# Generated by Django 5.2.8 on 2025-12-08 17:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('circuits', '0054_cable_connector_positions'),
]
operations = [
migrations.AddField(
model_name='circuitgroup',
name='comments',
field=models.TextField(blank=True),
),
migrations.AddField(
model_name='circuittype',
name='comments',
field=models.TextField(blank=True),
),
migrations.AddField(
model_name='virtualcircuittype',
name='comments',
field=models.TextField(blank=True),
),
]

View File

@@ -0,0 +1,17 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('circuits', '0055_add_comments_to_organizationalmodel'),
('contenttypes', '0002_remove_content_type_name'),
('dcim', '0224_add_comments_to_organizationalmodel'),
('extras', '0134_owner'),
]
operations = [
migrations.AddIndex(
model_name='circuittermination',
index=models.Index(fields=['termination_type', 'termination_id'], name='circuits_ci_termina_505dda_idx'),
),
]

View File

@@ -8,10 +8,16 @@ from django.utils.translation import gettext_lazy as _
from circuits.choices import *
from dcim.models import CabledObjectModel
from netbox.models import ChangeLoggedModel, OrganizationalModel, PrimaryModel
from netbox.models.mixins import DistanceMixin
from netbox.models.features import (
ContactsMixin, CustomFieldsMixin, CustomLinksMixin, ExportTemplatesMixin, ImageAttachmentsMixin, TagsMixin,
ContactsMixin,
CustomFieldsMixin,
CustomLinksMixin,
ExportTemplatesMixin,
ImageAttachmentsMixin,
TagsMixin,
)
from netbox.models.mixins import DistanceMixin
from .base import BaseCircuitType
__all__ = (
@@ -335,6 +341,9 @@ class CircuitTermination(
name='%(app_label)s_%(class)s_unique_circuit_term_side'
),
)
indexes = (
models.Index(fields=('termination_type', 'termination_id')),
)
verbose_name = _('circuit termination')
verbose_name_plural = _('circuit terminations')

View File

@@ -9,6 +9,7 @@ from django.utils.translation import gettext_lazy as _
from circuits.choices import *
from netbox.models import ChangeLoggedModel, PrimaryModel
from netbox.models.features import CustomFieldsMixin, CustomLinksMixin, ExportTemplatesMixin, TagsMixin
from .base import BaseCircuitType
__all__ = (

View File

@@ -1,4 +1,5 @@
from netbox.search import SearchIndex, register_search
from . import models
@@ -20,6 +21,7 @@ class CircuitGroupIndex(SearchIndex):
('name', 100),
('slug', 110),
('description', 500),
('comments', 5000),
)
display_attrs = ('description',)
@@ -44,6 +46,7 @@ class CircuitTypeIndex(SearchIndex):
('name', 100),
('slug', 110),
('description', 500),
('comments', 5000),
)
display_attrs = ('description',)
@@ -109,5 +112,6 @@ class VirtualCircuitTypeIndex(SearchIndex):
('name', 100),
('slug', 110),
('description', 500),
('comments', 5000),
)
display_attrs = ('description',)

View File

@@ -2,6 +2,7 @@ from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver
from dcim.signals import rebuild_paths
from .models import CircuitTermination

View File

@@ -4,6 +4,7 @@ from django.utils.translation import gettext_lazy as _
from circuits.models import *
from netbox.tables import NetBoxTable, OrganizationalModelTable, PrimaryModelTable, columns
from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin
from .columns import CommitRateColumn
__all__ = (
@@ -40,8 +41,8 @@ class CircuitTypeTable(OrganizationalModelTable):
class Meta(OrganizationalModelTable.Meta):
model = CircuitType
fields = (
'pk', 'id', 'name', 'circuit_count', 'color', 'description', 'slug', 'tags', 'created', 'last_updated',
'actions',
'pk', 'id', 'name', 'circuit_count', 'color', 'description', 'slug', 'comments', 'tags', 'created',
'last_updated', 'actions',
)
default_columns = ('pk', 'name', 'circuit_count', 'color', 'description')
@@ -175,7 +176,7 @@ class CircuitGroupTable(OrganizationalModelTable):
class Meta(OrganizationalModelTable.Meta):
model = CircuitGroup
fields = (
'pk', 'name', 'description', 'circuit_group_assignment_count', 'tags',
'pk', 'name', 'description', 'circuit_group_assignment_count', 'comments', 'tags',
'created', 'last_updated', 'actions',
)
default_columns = ('pk', 'name', 'description', 'circuit_group_assignment_count')

View File

@@ -5,7 +5,16 @@ from circuits.filtersets import *
from circuits.models import *
from dcim.choices import InterfaceTypeChoices, LocationStatusChoices
from dcim.models import (
Cable, Device, DeviceRole, DeviceType, Interface, Location, Manufacturer, Region, Site, SiteGroup
Cable,
Device,
DeviceRole,
DeviceType,
Interface,
Location,
Manufacturer,
Region,
Site,
SiteGroup,
)
from ipam.models import ASN, RIR
from netbox.choices import DistanceUnitChoices
@@ -433,7 +442,7 @@ class CircuitTestCase(TestCase, ChangeLoggedFilterSetTests):
class CircuitTerminationTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = CircuitTermination.objects.all()
filterset = CircuitTerminationFilterSet
ignore_fields = ('cable',)
ignore_fields = ('cable', 'cable_positions')
@classmethod
def setUpTestData(cls):

View File

@@ -1,4 +1,4 @@
from django.test import RequestFactory, tag, TestCase
from django.test import RequestFactory, TestCase, tag
from circuits.models import CircuitTermination
from circuits.tables import CircuitTerminationTable

View File

@@ -1,6 +1,7 @@
from django.urls import include, path
from utilities.urls import get_model_urls
from . import views
app_name = 'circuits'

View File

@@ -5,14 +5,15 @@ from netbox.object_actions import AddObject, BulkDelete, BulkEdit, BulkExport, B
from netbox.views import generic
from utilities.query import count_related
from utilities.views import GetRelatedModelsMixin, register_model_view
from . import filtersets, forms, tables
from .models import *
#
# Providers
#
@register_model_view(Provider, 'list', path='', detail=False)
class ProviderListView(generic.ObjectListView):
queryset = Provider.objects.annotate(

View File

@@ -2,10 +2,14 @@ import re
import typing
from collections import OrderedDict
from drf_spectacular.extensions import OpenApiSerializerFieldExtension, OpenApiSerializerExtension, _SchemaType
from drf_spectacular.extensions import OpenApiSerializerExtension, OpenApiSerializerFieldExtension, _SchemaType
from drf_spectacular.openapi import AutoSchema
from drf_spectacular.plumbing import (
build_basic_type, build_choice_field, build_media_type_object, build_object_type, get_doc,
build_basic_type,
build_choice_field,
build_media_type_object,
build_object_type,
get_doc,
)
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import Direction

View File

@@ -1,12 +1,13 @@
from rest_framework import serializers
from core.choices import *
from core.models import ObjectChange
from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.gfk_fields import GFKSerializerField
from netbox.api.serializers import BaseModelSerializer
from users.api.serializers_.users import UserSerializer
from ...choices import *
from ...models import ObjectChange
__all__ = (
'ObjectChangeSerializer',
)

View File

@@ -1,9 +1,10 @@
from core.choices import *
from core.models import DataFile, DataSource
from netbox.api.fields import ChoiceField, RelatedObjectCountField
from netbox.api.serializers import NetBoxModelSerializer, PrimaryModelSerializer
from netbox.utils import get_data_backend_choices
from ...choices import *
from ...models import DataFile, DataSource
__all__ = (
'DataFileSerializer',
'DataSourceSerializer',
@@ -44,3 +45,4 @@ class DataFileSerializer(NetBoxModelSerializer):
'id', 'url', 'display_url', 'display', 'source', 'path', 'last_updated', 'size', 'hash',
]
brief_fields = ('id', 'url', 'display', 'path')
read_only_fields = ['path', 'last_updated', 'size', 'hash']

View File

@@ -1,14 +1,15 @@
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from core.choices import *
from core.models import Job
from netbox.api.exceptions import SerializerNotFound
from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.serializers import BaseModelSerializer
from users.api.serializers_.users import UserSerializer
from utilities.api import get_serializer_for_model
from ...choices import *
from ...models import Job
__all__ = (
'JobSerializer',
)
@@ -31,7 +32,8 @@ class JobSerializer(BaseModelSerializer):
model = Job
fields = [
'id', 'url', 'display_url', 'display', 'object_type', 'object_id', 'object', 'name', 'status', 'created',
'scheduled', 'interval', 'started', 'completed', 'user', 'data', 'error', 'job_id', 'log_entries',
'scheduled', 'interval', 'started', 'completed', 'user', 'data', 'error', 'job_id', 'queue_name',
'log_entries',
]
brief_fields = ('url', 'created', 'completed', 'user', 'status')

View File

@@ -5,10 +5,11 @@ from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from core.models import ObjectType
from netbox.api.serializers import BaseModelSerializer
from utilities.views import get_action_url
from ...models import ObjectType
__all__ = (
'ObjectTypeSerializer',
)

View File

@@ -1,4 +1,5 @@
from netbox.api.routers import NetBoxRouter
from . import views
app_name = 'core-api'

View File

@@ -11,19 +11,19 @@ from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied
from rest_framework.response import Response
from rest_framework.routers import APIRootView
from rest_framework.viewsets import ReadOnlyModelViewSet
from rq.job import Job as RQ_Job
from rq.worker import Worker
from core import filtersets
from core.jobs import SyncDataSourceJob
from core.models import *
from core.utils import delete_rq_job, enqueue_rq_job, get_rq_jobs, requeue_rq_job, stop_rq_job
from netbox.api.authentication import IsAuthenticatedOrLoginNotRequired
from netbox.api.metadata import ContentTypeMetadata
from netbox.api.pagination import LimitOffsetListPagination
from netbox.api.viewsets import NetBoxModelViewSet, NetBoxReadOnlyModelViewSet
from utilities.api import IsSuperuser
from .. import filtersets
from ..jobs import SyncDataSourceJob
from ..models import *
from ..utils import delete_rq_job, enqueue_rq_job, get_rq_jobs, requeue_rq_job, stop_rq_job
from . import serializers
@@ -64,7 +64,7 @@ class DataFileViewSet(NetBoxReadOnlyModelViewSet):
filterset_class = filtersets.DataFileFilterSet
class JobViewSet(ReadOnlyModelViewSet):
class JobViewSet(NetBoxReadOnlyModelViewSet):
"""
Retrieve a list of job results
"""
@@ -73,19 +73,20 @@ class JobViewSet(ReadOnlyModelViewSet):
filterset_class = filtersets.JobFilterSet
class ObjectChangeViewSet(ReadOnlyModelViewSet):
class ObjectChangeViewSet(NetBoxReadOnlyModelViewSet):
"""
Retrieve a list of recent changes.
"""
metadata_class = ContentTypeMetadata
queryset = ObjectChange.objects.all()
serializer_class = serializers.ObjectChangeSerializer
filterset_class = filtersets.ObjectChangeFilterSet
def get_queryset(self):
return ObjectChange.objects.valid_models()
return super().get_queryset().valid_models()
class ObjectTypeViewSet(ReadOnlyModelViewSet):
class ObjectTypeViewSet(NetBoxReadOnlyModelViewSet):
"""
Read-only list of ObjectTypes.
"""
@@ -94,6 +95,16 @@ class ObjectTypeViewSet(ReadOnlyModelViewSet):
serializer_class = serializers.ObjectTypeSerializer
filterset_class = filtersets.ObjectTypeFilterSet
def initial(self, request, *args, **kwargs):
"""
Override initial() to skip the restrict() call since ObjectType (a ContentType proxy)
doesn't use RestrictedQuerySet and is publicly accessible metadata.
"""
# Call GenericViewSet.initial() directly, skipping BaseViewSet.initial()
# which would try to call restrict() on the queryset
from rest_framework.viewsets import GenericViewSet
GenericViewSet.initial(self, request, *args, **kwargs)
class BaseRQViewSet(viewsets.ViewSet):
"""

View File

@@ -5,10 +5,11 @@ from django.db import models
from django.db.migrations.operations import AlterModelOptions
from django.utils.translation import gettext as _
from core.events import *
from netbox.events import EventType, EVENT_TYPE_KIND_DANGER, EVENT_TYPE_KIND_SUCCESS, EVENT_TYPE_KIND_WARNING
from netbox.events import EVENT_TYPE_KIND_DANGER, EVENT_TYPE_KIND_SUCCESS, EVENT_TYPE_KIND_WARNING, EventType
from utilities.migration import custom_deconstruct
from .events import *
# Ignore verbose_name & verbose_name_plural Meta options when calculating model migrations
AlterModelOptions.ALTER_OPTION_KEYS.remove('verbose_name')
AlterModelOptions.ALTER_OPTION_KEYS.remove('verbose_name_plural')
@@ -21,11 +22,12 @@ class CoreConfig(AppConfig):
name = "core"
def ready(self):
from core.api import schema # noqa: F401
from core.checks import check_duplicate_indexes # noqa: F401
from netbox.models.features import register_models
from . import data_backends, events, search # noqa: F401
from netbox import context_managers # noqa: F401
from netbox.models.features import register_models
from . import data_backends, events, search # noqa: F401
from .api import schema # noqa: F401
from .checks import check_duplicate_indexes # noqa: F401
# Register models
register_models(*self.get_models())

View File

@@ -1,6 +1,6 @@
from django.core.checks import Error, register, Tags
from django.db.models import Index, UniqueConstraint
from django.apps import apps
from django.core.checks import Error, Tags, register
from django.db.models import Index, UniqueConstraint
__all__ = (
'check_duplicate_indexes',

View File

@@ -2,11 +2,11 @@ from django.utils.translation import gettext_lazy as _
from utilities.choices import ChoiceSet
#
# Data sources
#
class DataSourceStatusChoices(ChoiceSet):
NEW = 'new'
QUEUED = 'queued'

View File

@@ -15,17 +15,31 @@ from netbox.utils import register_data_backend
from utilities.constants import HTTP_PROXY_SUPPORTED_SCHEMAS, HTTP_PROXY_SUPPORTED_SOCK_SCHEMAS
from utilities.proxy import resolve_proxies
from utilities.socks import ProxyPoolManager
from .exceptions import SyncError
__all__ = (
'GitBackend',
'LocalBackend',
'S3Backend',
'url_has_embedded_credentials',
)
logger = logging.getLogger('netbox.data_backends')
def url_has_embedded_credentials(url):
"""
Check if a URL contains embedded credentials (username in the URL).
URLs like 'https://user@bitbucket.org/...' have embedded credentials.
This is used to avoid passing explicit credentials to dulwich when the
URL already contains them, which would cause authentication conflicts.
"""
parsed = urlparse(url)
return bool(parsed.username)
@register_data_backend()
class LocalBackend(DataBackend):
name = 'local'
@@ -102,7 +116,9 @@ class GitBackend(DataBackend):
clone_args['pool_manager'] = ProxyPoolManager(self.socks_proxy)
if self.url_scheme in ('http', 'https'):
if self.params.get('username'):
# Only pass explicit credentials if URL doesn't already contain embedded username
# to avoid credential conflicts (see #20902)
if not url_has_embedded_credentials(self.url) and self.params.get('username'):
clone_args.update(
{
"username": self.params.get('username'),

View File

@@ -1,5 +1,4 @@
import logging
from dataclasses import dataclass, field
from datetime import datetime

View File

@@ -8,6 +8,7 @@ from netbox.utils import get_data_backend_choices
from users.models import User
from utilities.filters import ContentTypeFilter
from utilities.filtersets import register_filterset
from .choices import *
from .models import *
@@ -129,10 +130,14 @@ class JobFilterSet(BaseFilterSet):
choices=JobStatusChoices,
null_value=None
)
queue_name = django_filters.CharFilter()
class Meta:
model = Job
fields = ('id', 'object_type', 'object_type_id', 'object_id', 'name', 'interval', 'status', 'user', 'job_id')
fields = (
'id', 'object_type', 'object_type_id', 'object_id', 'name', 'interval', 'status', 'user', 'job_id',
'queue_name',
)
def search(self, queryset, name, value):
if not value.strip():

View File

@@ -1,13 +1,14 @@
from django import forms
from django.utils.translation import gettext_lazy as _
from core.choices import JobIntervalChoices
from core.models import *
from netbox.forms import PrimaryModelBulkEditForm
from netbox.utils import get_data_backend_choices
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import BulkEditNullBooleanSelect
from ..choices import JobIntervalChoices
from ..models import *
__all__ = (
'DataSourceBulkEditForm',
)

View File

@@ -1,6 +1,7 @@
from core.models import *
from netbox.forms import PrimaryModelImportForm
from ..models import *
__all__ = (
'DataSourceImportForm',
)

View File

@@ -1,19 +1,23 @@
from django import forms
from django.utils.translation import gettext_lazy as _
from core.choices import *
from core.models import *
from netbox.forms import NetBoxModelFilterSetForm, PrimaryModelFilterSetForm
from netbox.forms.mixins import SavedFiltersMixin
from netbox.utils import get_data_backend_choices
from users.models import User
from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, FilterForm, add_blank_choice
from utilities.forms.fields import (
ContentTypeChoiceField, ContentTypeMultipleChoiceField, DynamicModelMultipleChoiceField, TagFilterField,
ContentTypeChoiceField,
ContentTypeMultipleChoiceField,
DynamicModelMultipleChoiceField,
TagFilterField,
)
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import DateTimePicker
from ..choices import *
from ..models import *
__all__ = (
'ConfigRevisionFilterForm',
'DataFileFilterForm',
@@ -26,8 +30,9 @@ __all__ = (
class DataSourceFilterForm(PrimaryModelFilterSetForm):
model = DataSource
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('type', 'status', 'enabled', 'sync_interval', name=_('Data Source')),
FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
)
type = forms.MultipleChoiceField(
label=_('Type'),
@@ -71,7 +76,7 @@ class JobFilterForm(SavedFiltersMixin, FilterForm):
model = Job
fieldsets = (
FieldSet('q', 'filter_id'),
FieldSet('object_type_id', 'status', name=_('Attributes')),
FieldSet('object_type_id', 'status', 'queue_name', name=_('Attributes')),
FieldSet(
'created__before', 'created__after', 'scheduled__before', 'scheduled__after', 'started__before',
'started__after', 'completed__before', 'completed__after', 'user', name=_('Creation')
@@ -87,6 +92,10 @@ class JobFilterForm(SavedFiltersMixin, FilterForm):
choices=JobStatusChoices,
required=False
)
queue_name = forms.CharField(
label=_('Queue'),
required=False
)
created__after = forms.DateTimeField(
label=_('Created after'),
required=False,

View File

@@ -1,9 +1,10 @@
from django import forms
from django.utils.translation import gettext_lazy as _
from core.models import DataFile, DataSource
from utilities.forms.fields import DynamicModelChoiceField
from ..models import DataFile, DataSource
__all__ = (
'SyncedDataMixin',
)

View File

@@ -6,9 +6,7 @@ from django.conf import settings
from django.forms.fields import JSONField as _JSONField
from django.utils.translation import gettext_lazy as _
from core.forms.mixins import SyncedDataMixin
from core.models import *
from netbox.config import get_config, PARAMS
from netbox.config import PARAMS, get_config
from netbox.forms import NetBoxModelForm, PrimaryModelForm
from netbox.registry import registry
from netbox.utils import get_data_backend_choices
@@ -17,6 +15,9 @@ from utilities.forms.fields import JSONField
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import HTMXSelect
from ..forms.mixins import SyncedDataMixin
from ..models import *
__all__ = (
'ConfigRevisionForm',
'DataSourceForm',

View File

@@ -1,6 +1,6 @@
import strawberry
from core.choices import *
from ..choices import *
__all__ = (
'DataSourceStatusEnum',

View File

@@ -1,6 +1,6 @@
from dataclasses import dataclass
from datetime import datetime
from typing import Annotated, TYPE_CHECKING
from typing import TYPE_CHECKING, Annotated
import strawberry
import strawberry_django

View File

@@ -1,5 +1,5 @@
from datetime import datetime
from typing import Annotated, TYPE_CHECKING
from typing import TYPE_CHECKING, Annotated
import strawberry
import strawberry_django
@@ -7,8 +7,9 @@ from django.contrib.contenttypes.models import ContentType as DjangoContentType
from strawberry.scalars import ID
from strawberry_django import BaseFilterLookup, DatetimeFilterLookup, FilterLookup
from core import models
from netbox.graphql.filters import BaseModelFilter, PrimaryModelFilter
from .. import models
from .enums import *
if TYPE_CHECKING:

View File

@@ -1,14 +1,14 @@
from typing import Annotated, List, TYPE_CHECKING
from typing import TYPE_CHECKING, Annotated, List
import strawberry
import strawberry_django
from django.contrib.contenttypes.models import ContentType
from strawberry.types import Info
from core.models import ObjectChange
from ..models import ObjectChange
if TYPE_CHECKING:
from core.graphql.types import DataFileType, DataSourceType, ObjectChangeType
from .types import DataFileType, DataSourceType, ObjectChangeType
__all__ = (
'ChangelogMixin',

View File

@@ -4,8 +4,9 @@ import strawberry
import strawberry_django
from django.contrib.contenttypes.models import ContentType as DjangoContentType
from core import models
from netbox.graphql.types import BaseObjectType, PrimaryObjectType
from .. import models
from .filters import *
__all__ = (

Some files were not shown because too many files have changed in this diff Show More