Commit Graph

15851 Commits

Author SHA1 Message Date
Jeremy Stretch 09cd3f2dfd Populate execution_time for existing jobs 2026-08-05 14:49:50 -04:00
Jeremy Stretch 19bdc9c7f7 Rearrange filter form field groups 2026-08-05 14:48:18 -04:00
Martin Hauser 75ceb55754
fix(extras): Honor Script defaults when triggered by Event Rules
Scripts triggered by Event Rules now respect notifications_default and
job_timeout from script Meta class. Updates documentation to clarify
this behavior and adds regression test coverage.

Fixes #22852
2026-08-05 18:01:38 +02:00
github-actions 280e32fcc9 Update source translation strings 2026-08-05 05:54:34 +00:00
Jason Novinger 0701a42a94 Fixes #22812: Avoid loading all jobs into memory when deleting a JobsMixin object
Deleting a Script (or any JobsMixin object) with thousands of associated jobs could
consume several GB of memory and exhaust the host, because Django's deletion collector
loads every related Job into memory. Jobs can never be fast-deleted (a global pre_delete
receiver forces per-instance signal dispatch), and each Job carries potentially large
data and log_entries payloads.

Two paths loaded the full job set independently, so both are addressed:

- The delete cascade: JobsMixin.delete() now deletes the object's jobs in batches before
  delegating to super().delete(), wrapped in a transaction so a failure in the parent
  delete rolls the job deletions back. After the loop the cascade collector finds no jobs
  to materialize.

- The delete-confirmation page: _get_dependent_objects() uses a ConfirmCollector that
  counts the jobs relation rather than descending into it, so the page never instantiates
  the jobs. Counted relations render as a non-expandable row (via a CountOnly stand-in)
  alongside the itemized dependents.
2026-08-04 16:39:35 -05:00
Elliott Balsley 3d3bebcb78
Closes #22787: Improve GraphQL query efficiency when resolving assigned objects (#22792) 2026-08-04 15:28:20 -04:00
bctiemann 8b203e55a8
Merge pull request #22850 from netbox-community/22844-CustomFieldChoiceSetSerializer-base_choices
Fixes #22844: Allow null value for CustomFieldChoiceSet `base_choices` in REST API
2026-08-04 13:54:25 -04:00
Jeremy Stretch 852f73b081 Release v4.6.8-rc2 2026-08-04 13:24:01 -04:00
Jason Satein c36e72876f
Closes #22567: Warn that a custom script file name must not shadow an installed Python module (#22804) 2026-08-04 08:36:11 -07:00
Arthur Hanson da1db0055d
Closes #22447: Add Cooling infrastructure modeling (#22517) 2026-08-04 10:23:50 -04:00
github-actions bb29246033 Update source translation strings 2026-08-04 05:56:44 +00:00
Jason Novinger a158ed3794 Fixes #22825: Handle CircuitTermination origins in cable path tracing
CablePath.save() and delete() wrote the _path back-reference onto the path's
origin object, and PathTraceView built the trace SVG URL from the origin's REST
API action. Both assume the origin is a PathEndpoint, but a CircuitTermination
is a valid cable-path origin (per from_origin) without the _path field or a
-trace API action, so those paths raised FieldDoesNotExist and NoReverseMatch
respectively. Guard the _path writes and the SVG URL on PathEndpoint membership,
and skip the SVG block in the template when no URL is available.
2026-08-03 16:10:32 -05:00
Jeremy Stretch be72e841f5 Closes #22837: Omit implicit pagination on GraphQL to-one prefetches 2026-08-03 14:57:42 -04:00
Jason Novinger a2c32f137b
Closes #22161: Rename filterset test mixin base classes to *TestMixin (#22856)
* Fixes #22161: Rename filterset test mixin base classes to *TestMixin

Completes the test-class naming standardization begun in #22097, which
renamed concrete test classes to the *TestCase suffix but deliberately
left four filterset test mixin base classes untouched because renaming
them is breaking for plugins that inherit from them.

These four are pure mixins, not concrete test cases, so they follow
NetBox's existing mixin naming convention (RQQueueTestMixin,
ComponentTraceMixin) rather than the *TestCase suffix the issue
originally proposed. The literal *TestCase names also collide with two
existing concrete classes (BaseFilterSetTestCase in
utilities/tests/test_filters.py and ChangeLoggedFilterSetTestCase in
extras/tests/test_filtersets.py).

  BaseFilterSetTests                     -> BaseFilterSetTestMixin
  ChangeLoggedFilterSetTests             -> ChangeLoggedFilterSetTestMixin
  DeviceComponentFilterSetTests          -> DeviceComponentFilterSetTestMixin
  DeviceComponentTemplateFilterSetTests  -> DeviceComponentTemplateFilterSetTestMixin

This is a breaking change for plugins whose test suites import the two
exported mixins from utilities.testing; they must update their imports.

* Fixes #22161: Update add-model skill for renamed test mixin

The add-model skill still referenced ChangeLoggedFilterSetTests in its
example filterset test. Update it to ChangeLoggedFilterSetTestMixin.
2026-08-03 13:23:02 -04:00
Jeremy Stretch 4afdb31b89
Fixes #22848: Ensure deterministic ordering for duplicate IP addresses (#22849) 2026-08-03 11:58:24 -05:00
Jeremy Stretch 6047ce1a37
Fixes #22854: Set `USE_SHADOW_DOM=False` to fix GraphiQL queries w/debug enabled (#22855) 2026-08-03 11:13:40 -05:00
bctiemann 071c78d172
Closes #22828: Validate Webhook.payload_url as a URL or Jinja2 template (#22832) 2026-08-03 10:46:24 -05:00
Arthur Hanson 0270cf1495
#20285 - Support multiple protocols per application service via port mappings (#22692) 2026-08-03 10:58:01 -04:00
Jeremy Stretch 492cb83cc3 Fixes #22844: Allow null value for CustomFieldChoiceSet base_choices in REST API 2026-08-03 09:00:07 -04:00
Jeremy Stretch 4877d1167f Correct release date for v4.6.7 2026-07-31 16:48:04 -04:00
bctiemann d2024a1edc
Closes #22770: Allow plugins to register Event Rule action handlers (#22793)
* Closes #22770: Allow plugins to register Event Rule action handlers

Introduces an EventRuleAction registration API (netbox.event_rules /
netbox.extras.event_rules) so plugins can add new EventRule action types
the same way they already register search indexes and event types,
replacing the hardcoded webhook/script/notification elif-chain. Core's
own three action types are refactored onto this mechanism.

An EventRule referencing an unregistered action (e.g. its providing
plugin is uninstalled) remains stored, is skipped during processing
without affecting other rules, is visibly marked unavailable in the
UI/API, and triggers a new extras.W001 system check, resuming
automatically once the plugin is reinstalled, with no need to re-save.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Fix CI failure: extras.W001 check must tolerate a not-yet-migrated database

check_event_rule_actions() queried EventRule unconditionally, which broke
`manage.py makemigrations --check` (and a fresh `manage.py migrate`) on a
database with no tables yet, since Django runs system checks before
verifying/applying migrations. Wrap the query and swallow DatabaseError,
matching the existing check_postgresql_version precedent for a database
that may not be ready. Verified against a fresh, unmigrated database that
makemigrations --check, migrate, and manage.py check all behave correctly,
and that the warning still fires once a qualifying EventRule exists.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Fix EventRuleForm action_type widget: HTMXSelect was silently ignored

Meta.widgets only applies to fields the ModelForm auto-generates; action_type
is an explicit class-level field, so its HTMXSelect assignment in Meta.widgets
never took effect, and switching Action type in the browser never refreshed
the action_choice field's label/queryset. Move the widget onto the field
declaration itself.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Address review feedback from Jeremy Stretch on PR #22793

- Revert action_object_type on_delete to CASCADE (was changed to SET_NULL)
- Make action_type choices dynamic via the model field's own callable
  choices=, simplifying EventRule.clean() and making any unavailable
  action_type invalid on save, whether new or unchanged
- Rename is_action_available to action_is_available
- Fold new dispatch tests into the existing RQQueueTestMixin test class to
  fix a flaky --parallel run (two such classes cross-flush each other's
  Redis queues)
- Only catch broad exceptions around plugin-provided actions in
  process_event_rules(); let a core action's own bugs propagate
- Add value_action_type() so table exports don't leak the "unavailable"
  badge's HTML markup
- Drop the frozen CSVChoiceField on action_type and make action_object
  optional at the field level, so bulk import of an object-less action
  works
- Map ValidationErrors on unexposed model fields to NON_FIELD_ERRORS in
  bulk import instead of letting them surface as a raw ValueError
- Restore EventRuleActionEnum/the enum-based GraphQL filter, built from
  the live action registry instead of the static EventRuleActionChoices
- Default EventRuleAction.object_required to False, matching
  object_model's default of None; set it explicitly on the three core
  actions
- Drop the unused request parameter on get_object_queryset()
- Fix action_object_type's serializer queryset, which incorrectly used
  the triggering object_types' feature flag
- Use .format() instead of % in get_action_type_display()
- Remove the extras.W001 system check (a DB query on every management
  command) in favor of an action_is_available field on the REST API
- Raise ValidationError instead of a bare Exception on duplicate action
  slug registration
- Shorten a couple of overly verbose inline comments
- Split EventRuleAction.validate() into an internal _validate() and a
  public no-op validate(), so a subclass's custom validation doesn't
  need to remember to call super()

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Trim verbose comments/docstrings added while addressing review feedback

Shortened a number of overly long inline comments and test docstrings
introduced across the previous commit's review-feedback fixes (the
EventRuleActionEnum comment, the _validate()/validate() docstrings, and
several test docstrings that restated context already given elsewhere).
Also drops the auto-generated "Generated by Django" header comment from
migration 0143, matching the rest of this app's hand-touched migrations.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Address findings from automated follow-up review of #22793

- Clear stale action_object_type/action_object_id when an action declares
  object_model but is left with no object selected (object_required=False);
  previously neither branch of the if/elif fired and the old value from
  before the edit silently persisted. Fixed in both EventRuleForm and
  EventRuleImportForm (the latter matters for CSV updates of an existing
  row). Also resolve the content type from the actual selected object
  rather than the action's declared object_model, correctly handling
  subclass/proxy instances.
- Validate action slugs at registration time (format, and collision via
  enum_key() with an already-registered slug) so a bad third-party slug
  is rejected immediately instead of crashing GraphQL schema assembly at
  startup.
- Strip the dead-code label/description duplication out of
  EventRuleActionChoices.CHOICES -- nothing reads it, and it risked
  drifting from WebhookAction/ScriptAction/NotificationAction's own.
- Record whether an action is plugin-provided at registration time
  instead of introspecting its module on every dispatch; core's three
  actions now register with is_plugin_provided=False explicitly.
- Add an action_is_available filter (API + UI) so event rules with a
  now-unavailable action can still be found in bulk, now that the
  extras.W001 system check is gone.
- Update the plugin dev docs: fix the OpenTicketAction example (it was
  missing object_required=True, the exact gap the action_object fix
  above addresses), note that an unavailable rule can't be saved at all
  (not just skipped), and move an internal-only note out of the
  published class docstring.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Address findings from second automated follow-up review of #22793

- Drop the ChoiceSet base from EventRuleActionChoices. With CHOICES=(),
  the previous version made ChoiceField(choices=EventRuleActionChoices)
  -- the idiomatic pattern used for every other ChoiceSet in this
  codebase, and reachable via `from extras.choices import *` -- silently
  reject every value instead of failing at first use.
- Reject slugs containing hyphens or a leading underscore at
  registration time: a hyphenated slug (plausible, since plugin
  distribution names are conventionally hyphenated) or a slug starting
  with an underscore both currently pass validation but produce a
  GraphQL-invalid or GraphQL-reserved enum member name once sanitized,
  crashing schema assembly at startup. Document the constraint in the
  plugin docs and the published slug docstring.
- Raise ImproperlyConfigured instead of ValidationError for all three
  registration-failure cases in register_event_rule_action() -- these
  are packaging/configuration mistakes surfaced from AppConfig.ready(),
  not user input, matching the convention ChoiceSetMeta already uses for
  the same class of error.
- Remove is_plugin_provided's class-level default; nothing reads it
  before an action is registered in any real code path, and the default
  masked a class-vs-instance inconsistency. Move its documentation out
  of the published Attributes docstring into a plain comment.
- Simplify EventRuleImportForm.clean()'s action_object_type/id
  assignment to match EventRuleForm.clean()'s approach (set both fields
  once, unconditionally, from the resolved object) rather than assigning
  via the GFK setter and then conditionally overwriting the content
  type.
- Split a dense doc sentence in eventrule.md onto its own line.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Address findings from third automated follow-up review of #22793

- Restore is_plugin_provided's class-level default of True. Its only
  read is inside process_event_rules()'s exception handler; without a
  default, an action reaching dispatch without going through
  register_event_rule_action() (e.g. inserted into the registry dict
  directly) raised AttributeError while already handling the real
  exception, masking it entirely instead of degrading gracefully.
- Move the slug/label presence checks out of __init_subclass__ (which
  fired at class-definition time, raising TypeError) and into
  register_event_rule_action() as ImproperlyConfigured, unifying them
  with the other three registration-time checks. This also resolves a
  still-open item from the very first automated review: an intermediate
  base class shared by several concrete plugin actions couldn't
  previously be defined without a placeholder slug/label of its own.
- Restore the GFK assignment (self.instance.action_object = obj) in
  EventRuleImportForm.clean() alongside the explicit content-type
  assignment, so EventRule.clean()'s later access to action_object hits
  the descriptor cache instead of an extra SELECT per imported row.
- Clarify the slug docstring/docs wording (leading underscore
  specifically, not underscores in general; tell authors to use an
  underscore instead of a hyphen) and document that intermediate base
  classes are now supported. Add a test for an uppercase slug.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Document the frozen-at-import tradeoff on EventRuleSerializer.action_type

The EventRuleActionAPITestCase docstring in test_api.py pointed here for
an explanation of why the choices are materialized once at module-import
time rather than dynamically, but the field itself had no such comment.

* Address findings from fourth automated follow-up review of #22793

- Restore the "must start with a letter" slug constraint dropped from
  the docs page and class docstring by the previous round's rewording;
  reword to "must begin with a lowercase letter", which covers the
  leading-digit case SLUG_RE actually rejects and matches the
  ImproperlyConfigured message an author will hit.
- Scope the GFK-cache-priming comment in EventRuleImportForm.clean() to
  the non-proxy case it actually holds for, rather than claiming it
  unconditionally.
- Trim comments and docstrings that had regrown into reviewer-facing
  rationale (why a prior finding was reverted, why a check isn't in
  __init_subclass__ anymore rather than API documentation, in
  event_rules.py and test_event_rules.py.
EOF
)

* Misc cleanup

* Misc cleanup

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2026-07-31 16:28:01 -04:00
github-actions 42cb0326fb Update source translation strings 2026-07-31 06:00:45 +00:00
Jeremy Stretch a1fc744556 Release v4.6.7 2026-07-30 16:36:25 -04:00
Jason Novinger 1f3aac25db
Closes #22810: Skip cached scope rebuild when scope fields are unchanged (#22811) 2026-07-30 15:22:11 -05:00
Martin Hauser 642b4e5c2a chore(ci): Add production PyPI publishing workflow
Introduces production PyPI publishing triggered by v* tag pushes, while
Test PyPI now requires manual dispatch. Both indexes never receive the
same run, ensuring proper separation between rehearsal and production.

Fixes #22786
2026-07-30 13:45:12 -04:00
Jeremy Stretch aefe938c63 Closes #22823: Avoid extraneous DB queries when fetching IP/prefix family via GraphQL API 2026-07-30 13:38:47 -04:00
bctiemann b5c6619108
Merge pull request #22806 from netbox-community/22800-circuitgroupassignmentspanel-not-filtered-by-circuit-type
Fixes #22800: Fix filtering of Circuit Group Assignments by member type
2026-07-30 13:03:53 -04:00
bctiemann 6f9c6080e5
Merge pull request #22827 from netbox-community/22822-graphql-rack-units
Closes #22822: Avoid extra DB query when fetching rack reservation units via GraphQL API
2026-07-30 12:25:44 -04:00
Jeremy Stretch d3735f2db5 Closes #22822: Avoid extra DB query when fetching rack reservation units via GraphQL API 2026-07-30 11:50:18 -04:00
Jeremy Stretch a1df1b99b0 Fixes #22813: Fix extraneous database queries when fetching custom field data via GraphQL API 2026-07-30 11:30:29 -04:00
github-actions 6a1b4afc43 Update source translation strings 2026-07-30 05:53:04 +00:00
Martin Hauser 052506fe9c
Fixes #22738: Correct IPAM availability under constrained object permissions (#22785) 2026-07-29 17:27:09 -04:00
Martin Hauser 29860aaf76
fix(circuits): Add member_type_id filter to CircuitGroupAssignment
Adds `member_type_id` filter field to support filtering by
ContentType ID alongside existing `member_type` filter. Includes
test coverage verifying correct filtering when member IDs overlap across
different Content Types.

Fixes #22800
2026-07-29 15:07:42 +02:00
github-actions 10ac9ffe8d Update source translation strings 2026-07-29 05:58:26 +00:00
Jeremy Stretch 567524b7b8
Update claude-code-action; change model to Opus 5 (#22788) 2026-07-28 16:25:52 -05:00
Jeremy Stretch d2df19790f Merge branch 'main' into feature
# Conflicts:
#	contrib/openapi.json
#	docs/release-notes/version-4.6.md
#	netbox/dcim/choices.py
#	netbox/dcim/forms/mixins.py
#	netbox/dcim/models/device_component_templates.py
#	netbox/dcim/models/device_components.py
#	netbox/extras/dashboard/widgets.py
#	netbox/extras/graphql/types.py
#	netbox/extras/models/configs.py
#	netbox/extras/tests/test_templatetags.py
#	netbox/ipam/choices.py
#	netbox/ipam/forms/model_forms.py
#	netbox/netbox/configuration_example.py
#	netbox/netbox/filtersets.py
#	netbox/netbox/tests/test_api.py
#	netbox/netbox/tests/test_scaffold.py
#	netbox/netbox/tests/test_tables.py
#	netbox/project-static/dist/netbox.js
#	netbox/project-static/dist/netbox.js.map
#	netbox/project-static/package.json
#	netbox/project-static/yarn.lock
#	netbox/release.yaml
#	netbox/translations/cs/LC_MESSAGES/django.mo
#	netbox/translations/cs/LC_MESSAGES/django.po
#	netbox/translations/da/LC_MESSAGES/django.mo
#	netbox/translations/da/LC_MESSAGES/django.po
#	netbox/translations/de/LC_MESSAGES/django.mo
#	netbox/translations/de/LC_MESSAGES/django.po
#	netbox/translations/en/LC_MESSAGES/django.po
#	netbox/translations/es/LC_MESSAGES/django.mo
#	netbox/translations/es/LC_MESSAGES/django.po
#	netbox/translations/fr/LC_MESSAGES/django.mo
#	netbox/translations/fr/LC_MESSAGES/django.po
#	netbox/translations/it/LC_MESSAGES/django.mo
#	netbox/translations/it/LC_MESSAGES/django.po
#	netbox/translations/ja/LC_MESSAGES/django.mo
#	netbox/translations/ja/LC_MESSAGES/django.po
#	netbox/translations/ko/LC_MESSAGES/django.mo
#	netbox/translations/ko/LC_MESSAGES/django.po
#	netbox/translations/lv/LC_MESSAGES/django.mo
#	netbox/translations/lv/LC_MESSAGES/django.po
#	netbox/translations/nl/LC_MESSAGES/django.mo
#	netbox/translations/nl/LC_MESSAGES/django.po
#	netbox/translations/pl/LC_MESSAGES/django.mo
#	netbox/translations/pl/LC_MESSAGES/django.po
#	netbox/translations/pt/LC_MESSAGES/django.mo
#	netbox/translations/pt/LC_MESSAGES/django.po
#	netbox/translations/ru/LC_MESSAGES/django.mo
#	netbox/translations/ru/LC_MESSAGES/django.po
#	netbox/translations/tr/LC_MESSAGES/django.mo
#	netbox/translations/tr/LC_MESSAGES/django.po
#	netbox/translations/uk/LC_MESSAGES/django.mo
#	netbox/translations/uk/LC_MESSAGES/django.po
#	netbox/translations/zh/LC_MESSAGES/django.mo
#	netbox/translations/zh/LC_MESSAGES/django.po
#	netbox/utilities/jinja2.py
#	netbox/utilities/tests/test_filters.py
#	requirements.txt
2026-07-28 14:24:24 -04:00
Jeremy Stretch fb8c455ba6 Release v4.6.6 2026-07-28 13:25:44 -04:00
Arthur Hanson 7472c5d067
22752 - Restore rear port fields on front port bulk import (#22776) 2026-07-28 10:02:41 -04:00
studioussagar eaa2816964
Closes #22522: Render colored badges for Custom Field Choices in tables (#22663)
Render select and multiselect custom field values as colored badges in
table views when their associated choices define colors.

For multiselect fields, render all selected values as badges when any
selected choice has a color, using the secondary badge color for
uncolored choices. Preserve comma-separated text when none of the
selected choices has a color.

Add test coverage for colored, uncolored, empty, mixed, and
HTML-sensitive choice values.

Co-authored-by: Martin Hauser <mhauser@netboxlabs.com>
2026-07-28 15:35:30 +02:00
Abhirupmandal 749e1b6579
Fixes #22690: Restore the left border on the quick search field 2026-07-28 09:32:57 -04:00
Martin Hauser 1e48c81666 docs(permissions): Add note about JSON escaping in constraint definitions
Clarifies that backslashes in constraint values must be escaped in JSON.
Includes example showing regex pattern escaping and adds table entry
demonstrating regex constraint usage.

Fixes #22498
2026-07-28 09:30:59 -04:00
Jason Novinger b3c27b951d
Closes #22790: Enforce SavedFilter visibility when applied via filter/filter_id (#22791)
BaseFilterSet resolved referenced SavedFilters without a visibility check, so
a private (shared=False) filter owned by one user could have its parameters
applied to another user's request. Restrict resolution to shared or owned
filters via restrict_to_shared(), matching the visibility enforced on the UI,
REST, and GraphQL SavedFilter surfaces.
2026-07-28 10:51:14 +02:00
bctiemann 8aa7b3b2c8
Merge pull request #22780 from netbox-community/22768-cable-removal-stores-an-empty-string-instead-of-null-in
Fixes #22768: Store null values for cable_end when removing Cables
2026-07-27 15:02:52 -04:00
bctiemann f951ba0219
Merge pull request #22778 from netbox-community/22745-script2
22766 - Map GraphQL array length lookup to Django's len transform
2026-07-27 15:01:38 -04:00
Martin Hauser 7d0a86ef52 fix(search): Add comments field to search indexing
Include comments field with weight 5000 in search indexes for
DeviceRole, L2VPN, MACAddress, and RouteTarget models to enable
full-text search on comment content.

Fixes #22767
2026-07-27 13:30:13 -04:00
Martin Hauser 34d6c170d8
Fixes #22773: Fix TypeError when bulk adding Module Bays to Devices (#22782) 2026-07-27 09:27:08 -05:00
Martin Hauser 91b51f62d6
fix(dcim): Nullify empty cable_end values instead of empty strings
Replace empty strings with null values for cable_end fields across
cable termination models. Adds data migrations to clean up inconsistent
values from earlier versions that wrote empty strings when cables were
deleted.

Fixes #22768
2026-07-25 13:52:12 +02:00
Arthur 567940ebd7 add tests 2026-07-24 17:55:07 -07:00
Arthur f8e7950004 22766 - Map GraphQL array length lookup to Django's len transform 2026-07-24 17:26:15 -07:00
bctiemann e87899f208
Merge pull request #22734 from netbox-community/22486-webhook
22486 - Add Configurable timeout for webhooks
2026-07-24 14:51:11 -04:00