Skip to content

[fix] Record monitoring metrics for sessions closed via bulk updates#735

Merged
nemesifier merged 3 commits into
masterfrom
traffic-accounting-chart-mismatch
Jul 2, 2026
Merged

[fix] Record monitoring metrics for sessions closed via bulk updates#735
nemesifier merged 3 commits into
masterfrom
traffic-accounting-chart-mismatch

Conversation

@nemesifier

@nemesifier nemesifier commented Jun 24, 2026

Copy link
Copy Markdown
Member

Checklist

  • I have read the OpenWISP Contributing Guidelines.
  • I have manually tested the changes proposed in this pull request.
  • I have written new test cases for new code and/or updated existing tests for changes to existing code.
  • I have updated the documentation.

Reference to Existing Issue

Closes #734.

Description of Changes

The RADIUS monitoring integration only writes traffic snapshots when Django's post_save signal is emitted. However, two common session closure paths use bulk updates which bypass this signal:

  • NAS reboot handling (Accounting-On) closes stale sessions via QuerySet.update().
  • The close_previous_radius_accounting_sessions receiver uses bulk_update().

As a result, traffic from sessions closed through these paths is present in the SQL accounting table but missing from the RADIUS traffic chart, leading to undercounted totals.

This change introduces a new radius_accounting_closed signal and a helper emit_radius_accounting_closed() that integrations can call after bulk updates. The monitoring integration now listens to both post_save and this new signal, ensuring every closed session is snapshotted.

Additionally, the monitoring metric timestamp is now aligned with the session's stop_time instead of the task execution time. This prevents traffic from being credited to the wrong time bucket when a session is closed or snapshotted later.

Regression tests are added to ensure these paths dispatch the monitoring task correctly and use the correct timestamp.

Screenshot

Not applicable (no UI changes).

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR adds a radius_accounting_closed signal, emits it when accounting sessions become closed through save() and NAS-boot bulk closure paths, and routes the monitoring integration to that signal. The bulk close path now updates sessions in chunks and emits one closure event per session after commit. Docs and tests are updated for the new signal and timestamp behavior.

Sequence Diagram(s)

sequenceDiagram
  participant AbstractRadiusAccounting
  participant RadiusAccounting
  participant transaction_on_commit
  participant radius_accounting_closed
  participant radius_accounting_closed_handler
  participant post_save_radiusaccounting_delay

  AbstractRadiusAccounting->>transaction_on_commit: schedule radius_accounting_closed after save
  RadiusAccounting->>transaction_on_commit: emit_radius_accounting_closed(closed_sessions)
  transaction_on_commit->>radius_accounting_closed: send(instance)
  radius_accounting_closed->>radius_accounting_closed_handler: deliver instance
  radius_accounting_closed_handler->>post_save_radiusaccounting_delay: enqueue metric with time=stop_time
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is descriptive and matches the main change: recording monitoring metrics for sessions closed via bulk updates.
Description check ✅ Passed The description matches the template with checklist, issue reference, change summary, and screenshot section.
Linked Issues check ✅ Passed The NAS reboot stale-session closure path now emits the new closed-session signal, so monitoring metrics are queued as required by #734.
Out of Scope Changes check ✅ Passed The added signal, monitoring handler, timestamp fix, docs, and tests all support the stated fix and linked issue.
Bug Fixes ✅ Passed The NAS-reboot bulk-update path now emits a closed-session signal, and regression tests cover the bug scenario and commit-time signal behavior deterministically.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch traffic-accounting-chart-mismatch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@openwisp_radius/base/models.py`:
- Around line 587-596: In close_previous_radius_accounting_sessions / the stale
session closing block, add an empty-list check before calling
cls.objects.bulk_update so the method consistently returns 0 when there are no
sessions to close. Use the closed_sessions list (and
emit_radius_accounting_closed only when it’s non-empty) to avoid calling
bulk_update([]), which can return None on older Django versions and causes
inconsistent closed_count behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1de49c1a-ff2e-4a3c-a057-ec7ad3fa59e7

📥 Commits

Reviewing files that changed from the base of the PR and between cd6a22b and e39f623.

📒 Files selected for processing (8)
  • docs/developer/utils.rst
  • openwisp_radius/base/models.py
  • openwisp_radius/integrations/monitoring/apps.py
  • openwisp_radius/integrations/monitoring/receivers.py
  • openwisp_radius/integrations/monitoring/tests/test_metrics.py
  • openwisp_radius/receivers.py
  • openwisp_radius/signals.py
  • openwisp_radius/utils.py
📜 Review details
⏰ Context from checks skipped due to timeout. (7)
  • GitHub Check: auto-assign-issue / run-bot
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Kilo Code Review
🧰 Additional context used
📓 Path-based instructions (2)
**/{docs,documentation}/**/*.{md,rst,adoc,html}

📄 CodeRabbit inference engine (Custom checks)

**/{docs,documentation}/**/*.{md,rst,adoc,html}: Documentation must be updated if a change affects behavior that is already documented
Feature documentation must mention the new feature. If the feature is heavily UI-related, a new section or page is appropriate

Files:

  • docs/developer/utils.rst
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Place imports at the top of the file. Only defer imports when necessary (e.g., Django model imports inside functions or methods where the app registry is not yet ready).
Avoid unnecessary blank lines inside function and method bodies.
Mark user-facing strings for translation with Django i18n helpers in Django code.
Write comments and docstrings only when they explain why code is shaped a certain way. Put comments before the relevant code block instead of scattering them inside it.

Files:

  • openwisp_radius/utils.py
  • openwisp_radius/receivers.py
  • openwisp_radius/signals.py
  • openwisp_radius/integrations/monitoring/receivers.py
  • openwisp_radius/base/models.py
  • openwisp_radius/integrations/monitoring/apps.py
  • openwisp_radius/integrations/monitoring/tests/test_metrics.py
🔇 Additional comments (9)
openwisp_radius/signals.py (1)

4-4: LGTM!

openwisp_radius/utils.py (1)

37-42: LGTM!

openwisp_radius/base/models.py (1)

58-58: LGTM!

openwisp_radius/receivers.py (1)

15-15: LGTM!

Also applies to: 129-129

openwisp_radius/integrations/monitoring/receivers.py (2)

7-12: 🎯 Functional Correctness

Confirm metrics should no longer be written for open/interim accounting updates.

The new instance.stop_time is None guard means post_save_radiusaccounting now skips sessions without a stop_time (open sessions and interim accounting updates), whereas previously every post_save enqueued a metric. This appears intentional (octets are cumulative, so recording only at close avoids over-counting) and is asserted by test_post_save_radiusaccouting_open_session, but please confirm no downstream dashboards relied on interim-update metrics.


13-31: LGTM!

openwisp_radius/integrations/monitoring/apps.py (1)

124-142: LGTM!

openwisp_radius/integrations/monitoring/tests/test_metrics.py (1)

21-21: LGTM!

Also applies to: 93-93, 144-144, 181-277, 307-307, 391-391, 468-468, 530-530, 580-580

docs/developer/utils.rst (1)

30-31: 📐 Maintainability & Code Quality

The section underline matches the title length exactly, so no Sphinx build warning will occur.

This does not appear to be an issue.

			> Likely an incorrect or invalid review comment.

Comment thread openwisp_radius/base/models.py Outdated
@kilo-code-bot

kilo-code-bot Bot commented Jun 24, 2026

Copy link
Copy Markdown

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Nicely scoped fix. The new radius_accounting_closed signal + emit_radius_accounting_closed() helper correctly snapshot sessions closed by the two bulk-update paths (NAS reboot via _close_stale_sessions_on_nas_boot and close_previous_radius_accounting_sessions), and aligning the metric timestamp with stop_time prevents traffic from landing in the wrong time bucket.

Verification notes:

  • Signal sender (session.__class__) matches the connected RadiusAccounting sender; the empty-list path is a no-op (bulk_update([]) returns 0, the emit loop is empty).
  • No double-counting: bulk-update paths bypass post_save, and both handlers share the stop_time is None guard + transaction.on_commit enqueue, so each closed session is snapshotted exactly once.
  • bulk_update returns a row count on all CI-supported Django versions (4.2/5.1/5.2), preserving the closed_count-based log in _handle_accounting_on.
  • Naive stop_time is made timezone-aware before being passed as time to the task.
  • Regression tests cover both bulk-closure paths and the timestamp alignment; docs updated in utils.rst.
Files Reviewed (8 files)
  • docs/developer/utils.rst
  • openwisp_radius/base/models.py
  • openwisp_radius/integrations/monitoring/apps.py
  • openwisp_radius/integrations/monitoring/receivers.py
  • openwisp_radius/integrations/monitoring/tests/test_metrics.py
  • openwisp_radius/receivers.py
  • openwisp_radius/signals.py
  • openwisp_radius/utils.py

Reviewed by glm-5.2 · Input: 173K · Output: 18.8K · Cached: 592.4K

@openwisp-companion

Copy link
Copy Markdown

Migrations Check Failed

Hello @nemesifier,
(Analysis for commit e39f623)

The CI build failed because of a migrations check. The error message ERROR: Migrations check failed! Models' changes not migrated, please run './manage.py makemigrations' to solve the issue! indicates that there are unapplied migrations.

Fix:
Run ./manage.py makemigrations to generate the necessary migration files and then commit them.

Comment thread docs/developer/utils.rst
Comment on lines +30 to +31
``radius_accounting_closed``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will it make more sense to always send this signal when the RadiusAccounting session is closed? It will help in cleaner implementation downstream (openwisp-monitoring integration).

Right now, we will have to listen to two different signals, which is fine. but requires developers to be aware of the context.

Comment thread openwisp_radius/base/models.py Outdated
Comment on lines 587 to 594
closed_sessions = list(stale_sessions)
stop_time = now()
for session in closed_sessions:
session.stop_time = stop_time
session.terminate_cause = "NAS-Reboot"
closed_count = cls.objects.bulk_update(
closed_sessions, fields=["stop_time", "terminate_cause"]
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We typically use iterator() with bulk_update with a chuksize of 1000 (default in Django). Right now, the code will load all the objects in the memory which is not ideal.

We can make the query efficient by only fetching the id in the queryset. We'll probably need to check that it does not increase the number of queries.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
openwisp_radius/integrations/monitoring/tests/test_metrics.py (1)

175-183: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Patch .delay in the open-session regression.

Line 183 checks the task object itself, but the receiver calls post_save_radiusaccounting.delay(...). This test still passes if .delay() is invoked, so it does not actually guard the “no metric for open sessions” path.

Suggested fix
-    `@patch`("openwisp_radius.integrations.monitoring.tasks.post_save_radiusaccounting")
-    def test_post_save_radiusaccouting_open_session(self, mocked_task):
+    `@patch`(
+        "openwisp_radius.integrations.monitoring.tasks.post_save_radiusaccounting.delay"
+    )
+    def test_post_save_radiusaccouting_open_session(self, mocked_delay):
         """
         Regression test ensuring open or interim accounting sessions do not
         write monitoring metrics before their cumulative octet values are final.
         """
         radius_options = _RADACCT.copy()
         radius_options["unique_id"] = "117"
         session = self._create_radius_accounting(**radius_options)
         self.assertEqual(session.stop_time, None)
-        mocked_task.assert_not_called()
+        mocked_delay.assert_not_called()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openwisp_radius/integrations/monitoring/tests/test_metrics.py` around lines
175 - 183, The open-session regression test is asserting on the task object
instead of the actual async call, so it won’t fail when
`post_save_radiusaccounting.delay(...)` is invoked; update the test to patch and
assert the `.delay` method on the `post_save_radiusaccounting` symbol used by
the receiver, so the “no metric for open sessions” path is properly guarded.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@openwisp_radius/base/models.py`:
- Around line 613-620: The NAS reboot closure path in the session bulk update is
overwriting rows that may have been closed concurrently because it only writes
by primary key after materializing `closed_sessions`. Update the logic around
the `while closed_sessions := list(islice(stale_sessions, 1000))` loop and
`cls.objects.bulk_update(...)` in `base/models.py` so the write still enforces
`stop_time__isnull=True` at update time or locks the selected rows before
loading them, and only emit `emit_radius_accounting_closed` for sessions that
were actually updated.

---

Outside diff comments:
In `@openwisp_radius/integrations/monitoring/tests/test_metrics.py`:
- Around line 175-183: The open-session regression test is asserting on the task
object instead of the actual async call, so it won’t fail when
`post_save_radiusaccounting.delay(...)` is invoked; update the test to patch and
assert the `.delay` method on the `post_save_radiusaccounting` symbol used by
the receiver, so the “no metric for open sessions” path is properly guarded.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 00e682bf-e14a-4b18-b165-2249b0bcd7e9

📥 Commits

Reviewing files that changed from the base of the PR and between e39f623 and 721aa6b.

📒 Files selected for processing (8)
  • docs/developer/utils.rst
  • openwisp_radius/apps.py
  • openwisp_radius/base/models.py
  • openwisp_radius/integrations/monitoring/apps.py
  • openwisp_radius/integrations/monitoring/receivers.py
  • openwisp_radius/integrations/monitoring/tests/test_metrics.py
  • openwisp_radius/receivers.py
  • openwisp_radius/utils.py
💤 Files with no reviewable changes (1)
  • openwisp_radius/integrations/monitoring/receivers.py
📜 Review details
⏰ Context from checks skipped due to timeout. (11)
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.2.0
🧰 Additional context used
📓 Path-based instructions (2)
**/{docs,documentation}/**/*.{md,rst,adoc,html}

📄 CodeRabbit inference engine (Custom checks)

**/{docs,documentation}/**/*.{md,rst,adoc,html}: Documentation must be updated if a change affects behavior that is already documented
Feature documentation must mention the new feature. If the feature is heavily UI-related, a new section or page is appropriate

Files:

  • docs/developer/utils.rst
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Place imports at the top of the file. Only defer imports when necessary (e.g., Django model imports inside functions or methods where the app registry is not yet ready).
Avoid unnecessary blank lines inside function and method bodies.
Mark user-facing strings for translation with Django i18n helpers in Django code.
Write comments and docstrings only when they explain why code is shaped a certain way. Put comments before the relevant code block instead of scattering them inside it.

Files:

  • openwisp_radius/utils.py
  • openwisp_radius/apps.py
  • openwisp_radius/integrations/monitoring/apps.py
  • openwisp_radius/receivers.py
  • openwisp_radius/base/models.py
  • openwisp_radius/integrations/monitoring/tests/test_metrics.py
🔇 Additional comments (3)
openwisp_radius/apps.py (1)

18-18: LGTM!

Also applies to: 131-135

openwisp_radius/integrations/monitoring/apps.py (1)

14-14: LGTM!

Also applies to: 123-134

openwisp_radius/integrations/monitoring/tests/test_metrics.py (1)

93-93: LGTM!

Also applies to: 144-144, 188-331, 361-361, 445-445, 522-522, 584-584, 634-634

Comment thread openwisp_radius/base/models.py Outdated
@openwisp-companion

Copy link
Copy Markdown

The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (1/3).

@coveralls

coveralls commented Jun 25, 2026

Copy link
Copy Markdown

Coverage Status

coverage: 97.964% (-0.1%) from 98.091% — traffic-accounting-chart-mismatch into master

Comment thread openwisp_radius/receivers.py Outdated
emit_radius_accounting_closed(closed_sessions)


def emit_radius_accounting_closed_on_save(instance, created, *args, **kwargs):

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is too AI sloppy, I will improve tomorrow.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@openwisp_radius/base/models.py`:
- Around line 540-543: The Session model __init__ currently calls
_set_initial_stop_time unconditionally, which forces deferred stop_time values
to be loaded when instances are built from _close_stale_sessions_on_nas_boot().
Update the __init__ / _set_initial_stop_time flow so it does not access
stop_time through the descriptor when the field is deferred; instead read from
__dict__ or skip cache initialization if stop_time is not already present. Keep
the fix localized to the Session initialization path and its stop-time cache
handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 454088db-8754-4169-9472-b85b55f7a4c9

📥 Commits

Reviewing files that changed from the base of the PR and between 721aa6b and a52f7f6.

📒 Files selected for processing (4)
  • openwisp_radius/base/models.py
  • openwisp_radius/receivers.py
  • openwisp_radius/tests/test_models.py
  • openwisp_radius/utils.py
💤 Files with no reviewable changes (1)
  • openwisp_radius/receivers.py
📜 Review details
⏰ Context from checks skipped due to timeout. (11)
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.11 | django~=5.1.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=5.2.0
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Place imports at the top of the file. Only defer imports when necessary (e.g., Django model imports inside functions or methods where the app registry is not yet ready).
Avoid unnecessary blank lines inside function and method bodies.
Mark user-facing strings for translation with Django i18n helpers in Django code.
Write comments and docstrings only when they explain why code is shaped a certain way. Put comments before the relevant code block instead of scattering them inside it.

Files:

  • openwisp_radius/utils.py
  • openwisp_radius/tests/test_models.py
  • openwisp_radius/base/models.py
🔇 Additional comments (4)
openwisp_radius/tests/test_models.py (2)

159-225: LGTM!


827-827: LGTM!

Also applies to: 862-862

openwisp_radius/utils.py (1)

39-45: LGTM!

openwisp_radius/base/models.py (1)

628-635: 🗄️ Data Integrity & Integration | 🏗️ Heavy lift

Concurrent close can still be overwritten. Rows are read while stop_time IS NULL and written back by primary key only, so an Accounting-Stop that closes a session between read and bulk_update will be clobbered with NAS-reboot values and emit a second radius_accounting_closed. This was raised on a prior commit and does not appear resolved.

Comment thread openwisp_radius/base/models.py
@nemesifier
nemesifier force-pushed the traffic-accounting-chart-mismatch branch from a52f7f6 to 9f97063 Compare June 26, 2026 22:07
@nemesifier

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nemesifier
nemesifier force-pushed the traffic-accounting-chart-mismatch branch 2 times, most recently from 3b481e8 to 036091a Compare June 26, 2026 22:35
@nemesifier

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@openwisp_radius/base/models.py`:
- Around line 551-556: The AbstractRadiusAccounting.save() method backfills
start_time in memory, but when update_fields is provided without start_time
Django will not persist that change. Update the save() logic so that when
start_time is set inside save() it is also added to kwargs["update_fields"] in
the update_fields path before calling super(), ensuring the saved row and any
signals reflect the same persisted value.

In `@openwisp_radius/integrations/monitoring/tests/test_metrics.py`:
- Around line 173-183: The regression test is patching the task object instead
of the enqueue method, so it won’t catch accidental calls to
post_save_radiusaccounting.delay for open sessions. Update the test in
test_post_save_radiusaccouting_open_session to patch the .delay path on
post_save_radiusaccounting, then keep the assertion focused on verifying that
.delay is never invoked when _create_radius_accounting returns a session with no
stop_time.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 41f3315c-fbf8-4a2d-b5c8-ec3593a20c9a

📥 Commits

Reviewing files that changed from the base of the PR and between 072d17e and 0990823.

📒 Files selected for processing (8)
  • docs/developer/utils.rst
  • openwisp_radius/base/models.py
  • openwisp_radius/integrations/monitoring/apps.py
  • openwisp_radius/integrations/monitoring/receivers.py
  • openwisp_radius/integrations/monitoring/tests/test_metrics.py
  • openwisp_radius/receivers.py
  • openwisp_radius/signals.py
  • openwisp_radius/tests/test_models.py
📜 Review details
⚠️ CI failures not shown inline (27)

GitHub Actions: OpenWISP RADIUS CI Build / Python==3.10 django~=5.2.0: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

Post job cleanup.
 [command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/openwisp-radius/openwisp-radius --files-from manifest.txt --use-compress-program zstdmt
 /usr/bin/tar: ../../../../../var/cache/apt/archives/partial: Cannot open: Permission denied
 /usr/bin/tar: ../../../../../var/cache/apt/archives/lock: Cannot open: Permission denied
 /usr/bin/tar: Exiting with failure status due to previous errors
 ##[warning]Failed to save: "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2

GitHub Actions: OpenWISP RADIUS CI Build / Python==3.12 django~=5.2.0: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

Post job cleanup.
 [command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/openwisp-radius/openwisp-radius --files-from manifest.txt --use-compress-program zstdmt
 /usr/bin/tar: ../../../../../var/cache/apt/archives/partial: Cannot open: Permission denied
 /usr/bin/tar: ../../../../../var/cache/apt/archives/lock: Cannot open: Permission denied
 /usr/bin/tar: Exiting with failure status due to previous errors
 ##[warning]Failed to save: "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2

GitHub Actions: OpenWISP RADIUS CI Build / Python==3.10 django~=4.2.0: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

Post job cleanup.
 [command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/openwisp-radius/openwisp-radius --files-from manifest.txt --use-compress-program zstdmt
 /usr/bin/tar: ../../../../../var/cache/apt/archives/partial: Cannot open: Permission denied
 /usr/bin/tar: ../../../../../var/cache/apt/archives/lock: Cannot open: Permission denied
 /usr/bin/tar: Exiting with failure status due to previous errors
 ##[warning]Failed to save: "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2

GitHub Actions: OpenWISP RADIUS CI Build / Python==3.13 django~=5.1.0: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

Post job cleanup.
 [command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/openwisp-radius/openwisp-radius --files-from manifest.txt --use-compress-program zstdmt
 /usr/bin/tar: ../../../../../var/cache/apt/archives/partial: Cannot open: Permission denied
 /usr/bin/tar: ../../../../../var/cache/apt/archives/lock: Cannot open: Permission denied
 /usr/bin/tar: Exiting with failure status due to previous errors
 ##[warning]Failed to save: "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2

GitHub Actions: OpenWISP RADIUS CI Build / Python==3.13 django~=5.2.0: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

Post job cleanup.
 [command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/openwisp-radius/openwisp-radius --files-from manifest.txt --use-compress-program zstdmt
 /usr/bin/tar: ../../../../../var/cache/apt/archives/partial: Cannot open: Permission denied
 /usr/bin/tar: ../../../../../var/cache/apt/archives/lock: Cannot open: Permission denied
 /usr/bin/tar: Exiting with failure status due to previous errors
 ##[warning]Failed to save: "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2

GitHub Actions: OpenWISP RADIUS CI Build / Python==3.12 django~=4.2.0: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

Post job cleanup.
 [command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/openwisp-radius/openwisp-radius --files-from manifest.txt --use-compress-program zstdmt
 /usr/bin/tar: ../../../../../var/cache/apt/archives/partial: Cannot open: Permission denied
 /usr/bin/tar: ../../../../../var/cache/apt/archives/lock: Cannot open: Permission denied
 /usr/bin/tar: Exiting with failure status due to previous errors
 ##[warning]Failed to save: "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2

GitHub Actions: OpenWISP RADIUS CI Build / Python==3.11 django~=5.2.0: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

Post job cleanup.
 [command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/openwisp-radius/openwisp-radius --files-from manifest.txt --use-compress-program zstdmt
 /usr/bin/tar: ../../../../../var/cache/apt/archives/partial: Cannot open: Permission denied
 /usr/bin/tar: ../../../../../var/cache/apt/archives/lock: Cannot open: Permission denied
 /usr/bin/tar: Exiting with failure status due to previous errors
 ##[warning]Failed to save: "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2

GitHub Actions: OpenWISP RADIUS CI Build / Python==3.11 django~=5.1.0: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

Post job cleanup.
 [command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/openwisp-radius/openwisp-radius --files-from manifest.txt --use-compress-program zstdmt
 /usr/bin/tar: ../../../../../var/cache/apt/archives/partial: Cannot open: Permission denied
 /usr/bin/tar: ../../../../../var/cache/apt/archives/lock: Cannot open: Permission denied
 /usr/bin/tar: Exiting with failure status due to previous errors
 ##[warning]Failed to save: "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2

GitHub Actions: OpenWISP RADIUS CI Build / Python==3.12 django~=5.1.0: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

Post job cleanup.
 [command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/openwisp-radius/openwisp-radius --files-from manifest.txt --use-compress-program zstdmt
 /usr/bin/tar: ../../../../../var/cache/apt/archives/partial: Cannot open: Permission denied
 /usr/bin/tar: ../../../../../var/cache/apt/archives/lock: Cannot open: Permission denied
 /usr/bin/tar: Exiting with failure status due to previous errors
 ##[warning]Failed to save: "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2

GitHub Actions: OpenWISP RADIUS CI Build / Python==3.11 django~=4.2.0: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

Post job cleanup.
 [command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/openwisp-radius/openwisp-radius --files-from manifest.txt --use-compress-program zstdmt
 /usr/bin/tar: ../../../../../var/cache/apt/archives/partial: Cannot open: Permission denied
 /usr/bin/tar: ../../../../../var/cache/apt/archives/lock: Cannot open: Permission denied
 /usr/bin/tar: Exiting with failure status due to previous errors
 ##[warning]Failed to save: "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2

GitHub Actions: OpenWISP RADIUS CI Build / Python==3.10 django~=5.1.0: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

Post job cleanup.
 [command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/openwisp-radius/openwisp-radius --files-from manifest.txt --use-compress-program zstdmt
 /usr/bin/tar: ../../../../../var/cache/apt/archives/partial: Cannot open: Permission denied
 /usr/bin/tar: ../../../../../var/cache/apt/archives/lock: Cannot open: Permission denied
 /usr/bin/tar: Exiting with failure status due to previous errors
 ##[warning]Failed to save: "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2

GitHub Actions: OpenWISP RADIUS CI Build / 2_Python==3.13 django~=5.1.0.txt: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

>openwisp-radius==1.3a0) (4.3.0)
 Requirement already satisfied: celery~=5.6.1 in /opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/site-packages (from openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (5.6.3)
 Requirement already satisfied: billiard<5.0,>=4.2.1 in /opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/site-packages (from celery~=5.6.1->openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (4.2.4)
 Requirement already satisfied: kombu>=5.6.0 in /opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/site-packages (from celery~=5.6.1->openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (5.6.2)
 Requirement already satisfied: vine<6.0,>=5.1.0 in /opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/site-packages (from celery~=5.6.1->openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (5.1.0)
 Requirement already satisfied: click<9.0,>=8.1.2 in /opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/site-packages (from celery~=5.6.1->openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (8.4.2)
 Requirement already satisfied: click-didyoumean>=0.3.0 in /opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/site-packages (from ce...

GitHub Actions: OpenWISP RADIUS CI Build / Python==3.12 django~=5.1.0: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

mand).run_from_argv(self.argv)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/django/core/management/commands/test.py", line 24, in run_from_argv
     super().run_from_argv(argv)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/django/core/management/base.py", line 413, in run_from_argv
     self.execute(*args, **cmd_options)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/django/core/management/base.py", line 459, in execute
     output = self.handle(*args, **options)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/django/core/management/commands/test.py", line 63, in handle
     failures = test_runner.run_tests(test_labels)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/django/test/runner.py", line 1073, in run_tests
     result = self.run_suite(suite)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/django/test/runner.py", line 1000, in run_suite
     return runner.run(suite)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/unittest/runner.py", line 240, in run
     test(result)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/unittest/suite.py", line 84, in __call__
     return self.run(*args, **kwds)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/django/test/runner.py", line 516, in run
     pool = multiprocessing.Pool(
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/multiprocessing/context.py", line 119, in Pool
     return Pool(processes, initializer, initargs, maxtasksperchild,
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/multiprocessing/pool.py", line 215, in __init__
     self._repopulate_pool()
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool
     return self._repopulate_pool_static(self._ctx, self.Process,
   F...

GitHub Actions: OpenWISP RADIUS CI Build / 1_Python==3.10 django~=5.2.0.txt: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

penwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (2.7.0)
 Requirement already satisfied: distro~=1.9.0 in /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages (from openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (1.9.0)
 Requirement already satisfied: celery~=5.6.1 in /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages (from openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (5.6.3)
 Requirement already satisfied: channels~=4.3.0 in /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages (from channels[daphne]~=4.3.0; extra == "channels"->openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (4.3.2)
 Requirement already satisfied: channels_redis<4.4.0,>=4.2.1 in /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages (from openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (4.3.0)
 Requirement already satisfied: djangorestframework~=3.17.1 in /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages (from openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/ar...

GitHub Actions: OpenWISP RADIUS CI Build / 8_Python==3.12 django~=5.2.0.txt: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

2.13/x64/lib/python3.12/site-packages (from channels[daphne]~=4.3.0; extra == "channels"->openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (4.3.2)
 Requirement already satisfied: channels_redis<4.4.0,>=4.2.1 in /opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages (from openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (4.3.0)
 Requirement already satisfied: celery~=5.6.1 in /opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages (from openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (5.6.3)
 Requirement already satisfied: billiard<5.0,>=4.2.1 in /opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages (from celery~=5.6.1->openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (4.2.4)
 Requirement already satisfied: kombu>=5.6.0 in /opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages (from celery~=5.6.1->openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (5.6.2)
 Requirement already satisfied: vine<6.0,>=5.1.0 in /opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages (from celery~=5.6.1->openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3....

GitHub Actions: OpenWISP RADIUS CI Build / 5_Python==3.11 django~=5.1.0.txt: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

aphne]~=4.3.0; extra == "channels"->openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (4.3.2)
 Requirement already satisfied: channels_redis<4.4.0,>=4.2.1 in /opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages (from openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (4.3.0)
 Requirement already satisfied: celery~=5.6.1 in /opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages (from openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (5.6.3)
 Requirement already satisfied: billiard<5.0,>=4.2.1 in /opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages (from celery~=5.6.1->openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (4.2.4)
 Requirement already satisfied: kombu>=5.6.0 in /opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages (from celery~=5.6.1->openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (5.6.2)
 Requirement already satisfied: vine<6.0,>=5.1.0 in /opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages (from celery~=5.6.1->openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.c...

GitHub Actions: OpenWISP RADIUS CI Build / 11_Python==3.10 django~=5.1.0.txt: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

sp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (5.6.3)
 Requirement already satisfied: djangorestframework~=3.17.1 in /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages (from openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (3.17.1)
 Requirement already satisfied: django-filter<26.0,>=25.1 in /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages (from openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (25.2)
 Requirement already satisfied: drf-yasg<1.22.0,>=1.21.14 in /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages (from openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (1.21.15)
 Requirement already satisfied: channels~=4.3.0 in /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages (from channels[daphne]~=4.3.0; extra == "channels"->openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (4.3.2)
 Requirement already satisfied: channels_redis<4.4.0,>=4.2.1 in /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages (from openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/head...

GitHub Actions: OpenWISP RADIUS CI Build / Python==3.11 django~=5.2.0: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

om_argv(self.argv)
   File "/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages/django/core/management/commands/test.py", line 24, in run_from_argv
     super().run_from_argv(argv)
   File "/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages/django/core/management/base.py", line 420, in run_from_argv
     self.execute(*args, **cmd_options)
   File "/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages/django/core/management/base.py", line 464, in execute
     output = self.handle(*args, **options)
   File "/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages/django/core/management/commands/test.py", line 63, in handle
     failures = test_runner.run_tests(test_labels)
   File "/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages/django/test/runner.py", line 1087, in run_tests
     result = self.run_suite(suite)
   File "/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages/django/test/runner.py", line 1014, in run_suite
     return runner.run(suite)
   File "/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/unittest/runner.py", line 217, in run
     test(result)
   File "/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/unittest/suite.py", line 84, in __call__
     return self.run(*args, **kwds)
   File "/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages/django/test/runner.py", line 516, in run
     pool = multiprocessing.Pool(
   File "/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/multiprocessing/context.py", line 119, in Pool
     return Pool(processes, initializer, initargs, maxtasksperchild,
   File "/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/multiprocessing/pool.py", line 215, in __init__
     self._repopulate_pool()
   File "/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/multiprocessing/pool.py", line 306, in _repopulate_pool
     return self._repopulate_pool_static(self._ctx, self.Process,
   File "/opt/ho...

GitHub Actions: OpenWISP RADIUS CI Build / 9_Python==3.11 django~=4.2.0.txt: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

1.15/x64/lib/python3.11/site-packages (from openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (3.17.1)
 Requirement already satisfied: django-filter<26.0,>=25.1 in /opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages (from openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (25.2)
 Requirement already satisfied: drf-yasg<1.22.0,>=1.21.14 in /opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages (from openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (1.21.15)
 Requirement already satisfied: channels~=4.3.0 in /opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages (from channels[daphne]~=4.3.0; extra == "channels"->openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (4.3.2)
 Requirement already satisfied: channels_redis<4.4.0,>=4.2.1 in /opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages (from openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[celery,rest] @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-radius==1.3a0) (4.3.0)
 Requirement already satisfied: celery~=5.6.1 in /opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages (from openwisp-utils @ https://github.com/openwisp/openwisp-utils/archive/refs/heads/1.3.tar.gz->openwisp-utils[cel...

GitHub Actions: OpenWISP RADIUS CI Build / Python==3.12 django~=4.2.0: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

 1043, in do_run
     runner.run()
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/coverage/execfile.py", line 213, in run
     exec(code, main_mod.__dict__)
   File "/home/runner/work/openwisp-radius/openwisp-radius/runtests.py", line 24, in <module>
     execute_from_command_line(args)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line
     utility.execute()
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/django/core/management/__init__.py", line 436, in execute
     self.fetch_command(subcommand).run_from_argv(self.argv)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/django/core/management/commands/test.py", line 24, in run_from_argv
     super().run_from_argv(argv)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/django/core/management/base.py", line 412, in run_from_argv
     self.execute(*args, **cmd_options)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/django/core/management/base.py", line 458, in execute
     output = self.handle(*args, **options)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/django/core/management/commands/test.py", line 68, in handle
     failures = test_runner.run_tests(test_labels)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/django/test/runner.py", line 1061, in run_tests
     result = self.run_suite(suite)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/django/test/runner.py", line 983, in run_suite
     return runner.run(suite)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/unittest/runner.py", line 240, in run
     test(result)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/unittest/suite.py", line 84, in __call__
     return self.run(*args, **kwds)
   ...

GitHub Actions: OpenWISP RADIUS CI Build / Python==3.10 django~=4.2.0: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

subset(ttfont)
   File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/fontTools/subset/__init__.py", line 3859, in subset
     self._prune_post_subset(font)
   File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/fontTools/subset/__init__.py", line 3841, in _prune_post_subset
     table = font[tag]
   File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/fontTools/ttLib/ttFont.py", line 805, in __getitem__
     table = self._readTable(tag)
   File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/fontTools/ttLib/ttFont.py", line 821, in _readTable
     log.debug("Decompiling '%s' table", tag)
 Message: "Decompiling '%s' table"
 Arguments: ('name',)
 --- Logging error ---
 Traceback (most recent call last):
   File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/openwisp_utils/tests/utils.py", line 97, in wrapped_function
     function(*args, **kwargs)
 TypeError: TestCommands.test_prefix_add_users_command() takes 1 positional argument but 2 were given
 During handling of the above exception, another exception occurred:
 Traceback (most recent call last):
   File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/logging/__init__.py", line 1103, in emit
     stream.write(msg + self.terminator)
 ValueError: I/O operation on closed file
 Call stack:
   File "/opt/hostedtoolcache/Python/3.10.20/x64/bin/coverage", line 6, in <module>
     sys.exit(main())
   File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/coverage/cmdline.py", line 1164, in main
     status = CoverageScript().command_line(argv)
   File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/coverage/cmdline.py", line 853, in command_line
     return self.do_run(options, args)
   File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/coverage/cmdline.py", line 1043, in do_run
     runner.run()
   File "/opt/hostedtoolcache/Python/3...

GitHub Actions: OpenWISP RADIUS CI Build / Python==3.13 django~=5.2.0: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

s, **cmd_options)
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/site-packages/django/core/management/base.py", line 464, in execute
     output = self.handle(*args, **options)
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/site-packages/django/core/management/commands/test.py", line 63, in handle
     failures = test_runner.run_tests(test_labels)
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/site-packages/django/test/runner.py", line 1087, in run_tests
     result = self.run_suite(suite)
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/site-packages/django/test/runner.py", line 1014, in run_suite
     return runner.run(suite)
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/unittest/runner.py", line 240, in run
     test(result)
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/unittest/suite.py", line 84, in __call__
     return self.run(*args, **kwds)
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/site-packages/django/test/runner.py", line 516, in run
     pool = multiprocessing.Pool(
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/multiprocessing/context.py", line 119, in Pool
     return Pool(processes, initializer, initargs, maxtasksperchild,
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/multiprocessing/pool.py", line 215, in __init__
     self._repopulate_pool()
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/multiprocessing/pool.py", line 306, in _repopulate_pool
     return self._repopulate_pool_static(self._ctx, self.Process,
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/multiprocessing/pool.py", line 329, in _repopulate_pool_static
     w.start()
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/multiprocessing/process.py", line 121, in start
     self._popen = self._Popen(self)
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/multiprocessing/con...

GitHub Actions: OpenWISP RADIUS CI Build / 7_Python==3.12 django~=4.2.0.txt: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

nit__
     self._repopulate_pool()
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/multiprocessing/pool.py", line 306, in _repopulate_pool
     return self._repopulate_pool_static(self._ctx, self.Process,
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/multiprocessing/pool.py", line 329, in _repopulate_pool_static
     w.start()
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/multiprocessing/process.py", line 121, in start
     self._popen = self._Popen(self)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/multiprocessing/context.py", line 282, in _Popen
     return Popen(process_obj)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/multiprocessing/popen_fork.py", line 19, in __init__
     self._launch(process_obj)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/multiprocessing/popen_fork.py", line 71, in _launch
     code = process_obj._bootstrap(parent_sentinel=child_r)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/coverage/multiproc.py", line 51, in _bootstrap
     return original_bootstrap(self, *args, **kwargs)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/multiprocessing/process.py", line 314, in _bootstrap
     self.run()
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/multiprocessing/process.py", line 108, in run
     self._target(*self._args, **self._kwargs)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/multiprocessing/pool.py", line 125, in worker
     result = (True, func(*args, **kwds))
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/django/test/runner.py", line 449, in _run_subsuite
     result = runner.run(subsuite)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/django/test/runner.py", line 366, in run
     test(result)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/unittest/suite.py", line 84, in...

GitHub Actions: OpenWISP RADIUS CI Build / 10_Python==3.12 django~=5.1.0.txt: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

ile "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/multiprocessing/popen_fork.py", line 19, in __init__
     self._launch(process_obj)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/multiprocessing/popen_fork.py", line 71, in _launch
     code = process_obj._bootstrap(parent_sentinel=child_r)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/coverage/multiproc.py", line 51, in _bootstrap
     return original_bootstrap(self, *args, **kwargs)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/multiprocessing/process.py", line 314, in _bootstrap
     self.run()
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/multiprocessing/process.py", line 108, in run
     self._target(*self._args, **self._kwargs)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/multiprocessing/pool.py", line 125, in worker
     result = (True, func(*args, **kwds))
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/django/test/runner.py", line 454, in _run_subsuite
     result = runner.run(subsuite)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/django/test/runner.py", line 369, in run
     test(result)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/unittest/suite.py", line 84, in __call__
     return self.run(*args, **kwds)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/unittest/suite.py", line 122, in run
     test(result)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/django/test/testcases.py", line 317, in __call__
     self._setup_and_call(result)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/django/test/testcases.py", line 369, in _setup_and_call
     super().__call__(result)
   File "/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/unittest/case.py", line 690, in __call__
     return self.run(*args, **kwds)
   File "/opt/hostedtoolcache...

GitHub Actions: OpenWISP RADIUS CI Build / 6_Python==3.13 django~=5.2.0.txt: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

ent_sentinel=child_r)
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/site-packages/coverage/multiproc.py", line 51, in _bootstrap
     return original_bootstrap(self, *args, **kwargs)
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/multiprocessing/process.py", line 313, in _bootstrap
     self.run()
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/multiprocessing/process.py", line 108, in run
     self._target(*self._args, **self._kwargs)
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/multiprocessing/pool.py", line 125, in worker
     result = (True, func(*args, **kwds))
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/site-packages/django/test/runner.py", line 454, in _run_subsuite
     result = runner.run(subsuite)
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/site-packages/django/test/runner.py", line 369, in run
     test(result)
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/unittest/suite.py", line 84, in __call__
     return self.run(*args, **kwds)
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/unittest/suite.py", line 122, in run
     test(result)
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/site-packages/django/test/testcases.py", line 321, in __call__
     self._setup_and_call(result)
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/site-packages/django/test/testcases.py", line 376, in _setup_and_call
     super().__call__(result)
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/unittest/case.py", line 707, in __call__
     return self.run(*args, **kwds)
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/unittest/case.py", line 651, in run
     self._callTestMethod(testMethod)
   File "/opt/hostedtoolcache/Python/3.13.14/x64/lib/python3.13/unittest/case.py", line 606, in _callTestMethod
     if method() is not None:
   File "/opt/hostedtoolcache/Python/3.13.14/x6...

GitHub Actions: OpenWISP RADIUS CI Build / 3_Python==3.10 django~=4.2.0.txt: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

ache/Python/3.10.20/x64/lib/python3.10/site-packages/django/core/management/__init__.py", line 436, in execute
     self.fetch_command(subcommand).run_from_argv(self.argv)
   File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/django/core/management/commands/test.py", line 24, in run_from_argv
     super().run_from_argv(argv)
   File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/django/core/management/base.py", line 412, in run_from_argv
     self.execute(*args, **cmd_options)
   File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/django/core/management/base.py", line 458, in execute
     output = self.handle(*args, **options)
   File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/django/core/management/commands/test.py", line 68, in handle
     failures = test_runner.run_tests(test_labels)
   File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/django/test/runner.py", line 1061, in run_tests
     result = self.run_suite(suite)
   File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/django/test/runner.py", line 983, in run_suite
     return runner.run(suite)
   File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/unittest/runner.py", line 184, in run
     test(result)
   File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/unittest/suite.py", line 84, in __call__
     return self.run(*args, **kwds)
   File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/django/test/runner.py", line 510, in run
     pool = multiprocessing.Pool(
   File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/multiprocessing/context.py", line 119, in Pool
     return Pool(processes, initializer, initargs, maxtasksperchild,
   File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/multiprocessing/pool.py", line 215, in __init__
     self._repopulate_pool()
   File "/opt/hostedtoolcache/Python/3.10.20/x64/l...

GitHub Actions: OpenWISP RADIUS CI Build / 4_Python==3.11 django~=5.2.0.txt: [fix] Record monitoring metrics for sessions closed via bulk updates

Conclusion: failure

View job details

stedtoolcache/Python/3.11.15/x64/lib/python3.11/multiprocessing/popen_fork.py", line 19, in __init__
     self._launch(process_obj)
   File "/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/multiprocessing/popen_fork.py", line 71, in _launch
     code = process_obj._bootstrap(parent_sentinel=child_r)
   File "/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages/coverage/multiproc.py", line 51, in _bootstrap
     return original_bootstrap(self, *args, **kwargs)
   File "/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/multiprocessing/process.py", line 314, in _bootstrap
     self.run()
   File "/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/multiprocessing/process.py", line 108, in run
     self._target(*self._args, **self._kwargs)
   File "/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/multiprocessing/pool.py", line 125, in worker
     result = (True, func(*args, **kwds))
   File "/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages/django/test/runner.py", line 454, in _run_subsuite
     result = runner.run(subsuite)
   File "/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages/django/test/runner.py", line 369, in run
     test(result)
   File "/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/unittest/suite.py", line 84, in __call__
     return self.run(*args, **kwds)
   File "/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/unittest/suite.py", line 122, in run
     test(result)
   File "/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages/django/test/testcases.py", line 321, in __call__
     self._setup_and_call(result)
   File "/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages/django/test/testcases.py", line 376, in _setup_and_call
     super().__call__(result)
   File "/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/unittest/case.py", line 678, in __call__
     return self.run(*args, **kwds)
   File "/opt/hostedtoolcache/Python/3.11...
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Place imports at the top of the file. Only defer imports when necessary (e.g., Django model imports inside functions or methods where the app registry is not yet ready).
Avoid unnecessary blank lines inside function and method bodies.
Mark user-facing strings for translation with Django i18n helpers in Django code.
Write comments and docstrings only when they explain why code is shaped a certain way. Put comments before the relevant code block instead of scattering them inside it.

Files:

  • openwisp_radius/signals.py
  • openwisp_radius/receivers.py
  • openwisp_radius/integrations/monitoring/receivers.py
  • openwisp_radius/tests/test_models.py
  • openwisp_radius/integrations/monitoring/apps.py
  • openwisp_radius/integrations/monitoring/tests/test_metrics.py
  • openwisp_radius/base/models.py
**/{docs,documentation}/**/*.{md,rst,adoc,html}

📄 CodeRabbit inference engine (Custom checks)

**/{docs,documentation}/**/*.{md,rst,adoc,html}: Documentation must be updated if a change affects behavior that is already documented
Feature documentation must mention the new feature. If the feature is heavily UI-related, a new section or page is appropriate

Files:

  • docs/developer/utils.rst
🪛 ast-grep (0.44.0)
openwisp_radius/integrations/monitoring/apps.py

[warning] 125-125: Loading a Keras model from an untrusted file can execute arbitrary code via Lambda layers or custom objects. Load only trusted models and avoid deserializing custom objects from untrusted sources.
Context: load_model("openwisp_radius", "RadiusAccounting")
Note: [CWE-502] Deserialization of Untrusted Data.

(keras-load-model-python)

🔇 Additional comments (4)
openwisp_radius/base/models.py (2)

644-651: The NAS-boot bulk-close race is still present.

This still reads open sessions and later writes them back by primary key only, so a concurrent Accounting-Stop can be overwritten and re-emitted as closed a second time.


8-9: LGTM!

Also applies to: 57-57, 541-550, 557-591, 631-643, 652-652

openwisp_radius/signals.py (1)

4-4: LGTM!

docs/developer/utils.rst (1)

30-56: LGTM!

Comment thread openwisp_radius/base/models.py
Comment thread openwisp_radius/integrations/monitoring/tests/test_metrics.py Outdated
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 27, 2026
The integration with OpenWISP Monitoring now is triggered
exclusively when RADIUS accounting sessions close, instead
of the previous approach which used the generic post_save
signal and used custom logic to determine if the RADIUS
session was being closed or not.
@nemesifier
nemesifier force-pushed the traffic-accounting-chart-mismatch branch from 3bc8863 to a9d9163 Compare July 2, 2026 18:36
The RADIUS monitoring integration previously relied only on
Django's ``post_save`` signal to write traffic snapshots.

However, sessions closed via ``QuerySet.update()``
(``Accounting-On`` handling) and ``bulk_update()``
(``close_previous_radius_accounting_sessions``) bypassed this
signal, causing traffic to be missing from RADIUS traffic
charts.

Sessions closed via ``QuerySet.update()`` and ``bulk_update()``
now emit the ``radius_accounting_closed`` signal, ensuring
that traffic metrics are correctly snapshotted by the
RADIUS monitoring integration.

Closes #734
@nemesifier
nemesifier force-pushed the traffic-accounting-chart-mismatch branch from a9d9163 to 14b333b Compare July 2, 2026 20:38
@nemesifier
nemesifier merged commit 3058ae3 into master Jul 2, 2026
19 checks passed
@nemesifier
nemesifier deleted the traffic-accounting-chart-mismatch branch July 2, 2026 21:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Development

Successfully merging this pull request may close these issues.

[bug] NAS reboot path bypasses post_save signal for stale accounting sessions

3 participants