Skip to content

[fix] WireGuard auto tunnel provisioning broken with IPv6 #721#1255

Open
devangpratap wants to merge 7 commits intoopenwisp:masterfrom
devangpratap:master
Open

[fix] WireGuard auto tunnel provisioning broken with IPv6 #721#1255
devangpratap wants to merge 7 commits intoopenwisp:masterfrom
devangpratap:master

Conversation

@devangpratap
Copy link

When using an IPv6 subnet from OpenWISP IPAM for a WireGuard VPN, the auto-generated client template incorrectly set the address family to 'ipv4' with mask 32 because netjsonconfig's wireguard_auto_client hardcodes these values regardless of IP version.

After calling wireguard_auto_client, the resulting config is now post-processed to set family='ipv6' and mask=128 when the VPN subnet is IPv6. A regression test is also included.

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 #721

Description of Changes

The wireguard_auto_client method in netjsonconfig's OpenWrt backend hardcodes family: ipv4 and mask: 32 when generating the WireGuard interface address, regardless of the actual IP version of the assigned subnet. When a user sets up a WireGuard VPN using an IPv6 IPAM subnet and auto-configures a VPN client template, the generated template gets an incorrect IPv4 address family. If the user then manually corrects the family to ipv6, schema validation fails.
The fix post-processes the output of wireguard_auto_client in AbstractVpn.auto_client() (openwisp_controller/config/base/vpn.py): when self.subnet.subnet.version == 6, all WireGuard interface addresses in the auto-generated config are updated to family: ipv6 and mask: 128.

Screenshot

N/A

When using an IPv6 subnet from OpenWISP IPAM for a WireGuard VPN,
the auto-generated client template incorrectly set the address family
to 'ipv4' with mask 32 because netjsonconfig's wireguard_auto_client
hardcodes these values regardless of IP version.

After calling wireguard_auto_client, the resulting config is now
post-processed to set family='ipv6' and mask=128 when the VPN subnet
is IPv6. A regression test is also included.

Fixes openwisp#721
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 5, 2026

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 change updates AbstractVpn.auto_client so that when generating a WireGuard auto-client configuration for an IPv6 subnet and a provided template_backend_class, any WireGuard interface in the produced output has its address entries annotated with family "ipv6" and mask 128. Three tests were added verifying auto_client produces a WireGuard interface address with family "ipv6" and mask 128 for WireGuard, VXLAN+WireGuard, and a generic VPN template when given an IPv6 subnet. CHANGES.rst was updated to record the bugfix.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main fix: resolving WireGuard auto tunnel provisioning for IPv6 subnets, directly addressing issue #721.
Description check ✅ Passed The description provides a comprehensive explanation of the bug, the fix implemented, and includes a completed checklist; only documentation update is missing.
Linked Issues check ✅ Passed The PR successfully addresses issue #721 by post-processing wireguard_auto_client output to set family='ipv6' and mask=128 for IPv6 subnets, with comprehensive test coverage.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the IPv6 WireGuard issue: core fix, tests, test utilities, and changelog entry with no unrelated modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

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 the current code and only fix it if needed.

Inline comments:
In `@openwisp_controller/config/tests/test_vpn.py`:
- Around line 652-655: The test currently assumes the WireGuard interface is at
interfaces[0]; instead, find the interface in auto["interfaces"] whose "type" ==
"wireguard" and then set addresses = that_interface["addresses"] (and fail the
test with an informative message if no such interface is found) so the
subsequent assertions on addresses[0]["family"] and addresses[0]["mask"] are
resilient to backend ordering changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0d7ba452-5e76-45e2-a78f-7a4f4de4a40b

📥 Commits

Reviewing files that changed from the base of the PR and between 55df52f and 5e2f99e.

📒 Files selected for processing (2)
  • openwisp_controller/config/base/vpn.py
  • openwisp_controller/config/tests/test_vpn.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.13 | 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.12 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=4.2.0
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2026-01-15T15:05:49.557Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/management/commands/clear_last_ip.py:38-42
Timestamp: 2026-01-15T15:05:49.557Z
Learning: In Django projects, when using select_related() to traverse relations (for example, select_related("organization__config_settings")), the traversed relation must not be deferred. If you also use .only() in the same query, include the relation name or FK field (e.g., "organization" or "organization_id") in the .only() list to avoid the error "Field X cannot be both deferred and traversed using select_related at the same time." Apply this guideline to Django code in openwisp_controller/config/management/commands/clear_last_ip.py and similar modules by ensuring any select_related with an accompanying only() includes the related field names to prevent deferred/traversed conflicts.

Applied to files:

  • openwisp_controller/config/base/vpn.py
  • openwisp_controller/config/tests/test_vpn.py
📚 Learning: 2026-02-17T19:13:10.088Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/whois/commands.py:0-0
Timestamp: 2026-02-17T19:13:10.088Z
Learning: In reviews for the openwisp/openwisp-controller repository, do not propose changes based on Ruff warnings. The project does not use Ruff as its linter; ignore Ruff-related suggestions and follow the repository’s established linting and configuration rules. This guidance applies to all Python files under the openwisp_controller directory.

Applied to files:

  • openwisp_controller/config/base/vpn.py
  • openwisp_controller/config/tests/test_vpn.py
📚 Learning: 2026-01-15T15:07:17.354Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/geo/estimated_location/tests/tests.py:172-175
Timestamp: 2026-01-15T15:07:17.354Z
Learning: In this repository, flake8 enforces E501 (line too long) via setup.cfg (max-line-length = 88) while ruff ignores E501 via ruff.toml. Therefore, use '# noqa: E501' on lines that intentionally exceed 88 characters to satisfy flake8 without affecting ruff checks. This applies to Python files across the project (any .py) and is relevant for tests as well. Use sparingly and only where breaking lines is not feasible without hurting readability or functionality.

Applied to files:

  • openwisp_controller/config/base/vpn.py
  • openwisp_controller/config/tests/test_vpn.py
🔇 Additional comments (1)
openwisp_controller/config/base/vpn.py (1)

632-637: IPv6 WireGuard address normalization is correctly implemented.

This is a good, targeted fix: it only applies for IPv6 subnets and rewrites WireGuard interface addresses to family="ipv6" with /128, matching expected client semantics.

Pass matching organization to both the VPN and its subnet in
test_auto_client_ipv6 so they satisfy the org-relation validation
(_validate_org_relation("subnet")) in openwisp-users.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
openwisp_controller/config/tests/test_vpn.py (1)

654-654: ⚠️ Potential issue | 🟡 Minor

Make the IPv6 regression assertion order-independent.

Line 654 assumes the WireGuard interface is always at index 0. That can make this regression test flaky if interface ordering changes.

Suggested hardening
-        addresses = auto["interfaces"][0]["addresses"]
+        wg_interface = next(
+            (i for i in auto["interfaces"] if i.get("type") == "wireguard"), None
+        )
+        self.assertIsNotNone(
+            wg_interface, "WireGuard interface missing in auto_client output"
+        )
+        addresses = wg_interface["addresses"]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openwisp_controller/config/tests/test_vpn.py` at line 654, The test currently
assumes the WireGuard interface is at interfaces[0] by using
auto["interfaces"][0]["addresses"]; change the assertion to be order-independent
by locating the interface that contains the expected IPv6 addresses (e.g.,
search auto["interfaces"] for an interface entry whose "addresses" contains the
target IPv6 / whose "family" == "inet6" or whose "name" matches the WireGuard
interface name) and then assert against that interface's "addresses" list;
update any references to auto["interfaces"][0]["addresses"] to use the
found_interface variable (or equivalent) so the test no longer depends on list
ordering.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@openwisp_controller/config/tests/test_vpn.py`:
- Line 654: The test currently assumes the WireGuard interface is at
interfaces[0] by using auto["interfaces"][0]["addresses"]; change the assertion
to be order-independent by locating the interface that contains the expected
IPv6 addresses (e.g., search auto["interfaces"] for an interface entry whose
"addresses" contains the target IPv6 / whose "family" == "inet6" or whose "name"
matches the WireGuard interface name) and then assert against that interface's
"addresses" list; update any references to auto["interfaces"][0]["addresses"] to
use the found_interface variable (or equivalent) so the test no longer depends
on list ordering.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: adfa8ee3-c5dd-41bd-b469-f09c51641a8c

📥 Commits

Reviewing files that changed from the base of the PR and between 5e2f99e and 2229fbb.

📒 Files selected for processing (1)
  • openwisp_controller/config/tests/test_vpn.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.13 | django~=5.1.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
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=5.1.0
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2026-01-15T15:05:49.557Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/management/commands/clear_last_ip.py:38-42
Timestamp: 2026-01-15T15:05:49.557Z
Learning: In Django projects, when using select_related() to traverse relations (for example, select_related("organization__config_settings")), the traversed relation must not be deferred. If you also use .only() in the same query, include the relation name or FK field (e.g., "organization" or "organization_id") in the .only() list to avoid the error "Field X cannot be both deferred and traversed using select_related at the same time." Apply this guideline to Django code in openwisp_controller/config/management/commands/clear_last_ip.py and similar modules by ensuring any select_related with an accompanying only() includes the related field names to prevent deferred/traversed conflicts.

Applied to files:

  • openwisp_controller/config/tests/test_vpn.py
📚 Learning: 2026-02-17T19:13:10.088Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/whois/commands.py:0-0
Timestamp: 2026-02-17T19:13:10.088Z
Learning: In reviews for the openwisp/openwisp-controller repository, do not propose changes based on Ruff warnings. The project does not use Ruff as its linter; ignore Ruff-related suggestions and follow the repository’s established linting and configuration rules. This guidance applies to all Python files under the openwisp_controller directory.

Applied to files:

  • openwisp_controller/config/tests/test_vpn.py
📚 Learning: 2026-01-15T15:07:17.354Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/geo/estimated_location/tests/tests.py:172-175
Timestamp: 2026-01-15T15:07:17.354Z
Learning: In this repository, flake8 enforces E501 (line too long) via setup.cfg (max-line-length = 88) while ruff ignores E501 via ruff.toml. Therefore, use '# noqa: E501' on lines that intentionally exceed 88 characters to satisfy flake8 without affecting ruff checks. This applies to Python files across the project (any .py) and is relevant for tests as well. Use sparingly and only where breaking lines is not feasible without hurting readability or functionality.

Applied to files:

  • openwisp_controller/config/tests/test_vpn.py

@coveralls
Copy link

coveralls commented Mar 5, 2026

Coverage Status

coverage: 98.673% (+0.001%) from 98.672%
when pulling c3abc81 on devangpratap:master
into ffbefa5 on openwisp:master.

@kilo-code-bot
Copy link

kilo-code-bot bot commented Mar 7, 2026

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Overview

This PR effectively fixes WireGuard auto tunnel provisioning broken with IPv6 subnets (issue #721).

Changes Reviewed

File Change Assessment
openwisp_controller/config/base/vpn.py Added IPv6 detection and address configuration for WireGuard interfaces ✅ Correct - safely checks for subnet existence and version before setting family="ipv6" and mask=128
openwisp_controller/config/tests/test_vpn.py Added test_auto_client_ipv6 for both WireGuard and VXLAN backends ✅ Good - regression tests verify the fix works
openwisp_controller/config/tests/utils.py Updated _create_vxlan_tunnel to accept organization and subnet via kwargs ✅ Clean - enables flexible test configuration
CHANGES.rst Added changelog entry for the bugfix ✅ Proper documentation

Implementation Quality

  • Safety: Uses short-circuit evaluation (self.subnet and self.subnet.subnet.version == 6) to avoid AttributeError if subnet is None
  • Defensive coding: Uses .get() methods when accessing nested dictionary keys (interfaces, addresses)
  • Comprehensive tests: Both WireGuard and VXLAN backends have IPv6-specific tests
  • Test coverage: Tests verify both family and mask are correctly set to IPv6 values
Files Reviewed (4 files)
  • CHANGES.rst - Changelog update
  • openwisp_controller/config/base/vpn.py - Core fix for IPv6 subnet handling
  • openwisp_controller/config/tests/test_vpn.py - IPv6 regression tests
  • openwisp_controller/config/tests/utils.py - Test utility improvements

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 7, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
openwisp_controller/config/tests/test_vpn.py (1)

643-657: 🧹 Nitpick | 🔵 Trivial

Make the assertion resilient to interface ordering.

The test assumes auto["interfaces"][0] is the WireGuard interface. If backend ordering changes, this could cause false negatives. Consider finding the interface by type:

-        addresses = auto["interfaces"][0]["addresses"]
+        wg_interface = next(
+            (i for i in auto["interfaces"] if i.get("type") == "wireguard"), None
+        )
+        self.assertIsNotNone(wg_interface, "WireGuard interface not found")
+        addresses = wg_interface["addresses"]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openwisp_controller/config/tests/test_vpn.py` around lines 643 - 657, In
test_auto_client_ipv6 update the assertions to find the WireGuard interface from
auto["interfaces"] instead of assuming index 0: call
vpn.auto_client(template_backend_class=template.backend_class) as before, then
locate the interface object where its type/name indicates WireGuard (e.g., check
interface.get("type") == "wireguard" or interface.get("name") contains "wg") and
run the assertions against that interface's "addresses" list (length == 1,
family == "ipv6", mask == 128) so the test no longer depends on interface
ordering.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@openwisp_controller/config/tests/test_vpn.py`:
- Around line 1015-1031: The test test_auto_client_ipv6 assumes the WireGuard
interface is at auto["interfaces"][0]; instead, locate the interface by its type
before asserting addresses: call vpn.auto_client(...) to get auto, find the
interface in auto["interfaces"] where interface["type"] == "wireguard" (or the
correct interface type used elsewhere), then inspect that interface's
"addresses" to assert length, family ("ipv6") and mask (128); update references
to avoid using fixed index and use a filter/next pattern on the interfaces list.

---

Duplicate comments:
In `@openwisp_controller/config/tests/test_vpn.py`:
- Around line 643-657: In test_auto_client_ipv6 update the assertions to find
the WireGuard interface from auto["interfaces"] instead of assuming index 0:
call vpn.auto_client(template_backend_class=template.backend_class) as before,
then locate the interface object where its type/name indicates WireGuard (e.g.,
check interface.get("type") == "wireguard" or interface.get("name") contains
"wg") and run the assertions against that interface's "addresses" list (length
== 1, family == "ipv6", mask == 128) so the test no longer depends on interface
ordering.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4ddcf04c-e7c8-4bfe-9b8f-26322f68c343

📥 Commits

Reviewing files that changed from the base of the PR and between 2229fbb and 9e80d2e.

📒 Files selected for processing (2)
  • CHANGES.rst
  • openwisp_controller/config/tests/test_vpn.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.11 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=4.2.0
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2026-01-15T15:05:49.557Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/management/commands/clear_last_ip.py:38-42
Timestamp: 2026-01-15T15:05:49.557Z
Learning: In Django projects, when using select_related() to traverse relations (for example, select_related("organization__config_settings")), the traversed relation must not be deferred. If you also use .only() in the same query, include the relation name or FK field (e.g., "organization" or "organization_id") in the .only() list to avoid the error "Field X cannot be both deferred and traversed using select_related at the same time." Apply this guideline to Django code in openwisp_controller/config/management/commands/clear_last_ip.py and similar modules by ensuring any select_related with an accompanying only() includes the related field names to prevent deferred/traversed conflicts.

Applied to files:

  • openwisp_controller/config/tests/test_vpn.py
📚 Learning: 2026-02-17T19:13:10.088Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/whois/commands.py:0-0
Timestamp: 2026-02-17T19:13:10.088Z
Learning: In reviews for the openwisp/openwisp-controller repository, do not propose changes based on Ruff warnings. The project does not use Ruff as its linter; ignore Ruff-related suggestions and follow the repository’s established linting and configuration rules. This guidance applies to all Python files under the openwisp_controller directory.

Applied to files:

  • openwisp_controller/config/tests/test_vpn.py
📚 Learning: 2026-01-15T15:07:17.354Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/geo/estimated_location/tests/tests.py:172-175
Timestamp: 2026-01-15T15:07:17.354Z
Learning: In this repository, flake8 enforces E501 (line too long) via setup.cfg (max-line-length = 88) while ruff ignores E501 via ruff.toml. Therefore, use '# noqa: E501' on lines that intentionally exceed 88 characters to satisfy flake8 without affecting ruff checks. This applies to Python files across the project (any .py) and is relevant for tests as well. Use sparingly and only where breaking lines is not feasible without hurting readability or functionality.

Applied to files:

  • openwisp_controller/config/tests/test_vpn.py
🔇 Additional comments (1)
CHANGES.rst (1)

7-11: LGTM!

The changelog entry is properly formatted and accurately describes the bugfix for WireGuard auto tunnel provisioning with IPv6 subnets. The RST formatting and link structure are consistent with other entries in the file.

@openwisp-companion
Copy link

CI Failure Analysis

Hello @devangpratap,
(Analysis for commit 9e80d2e)

There are two distinct failures in the CI logs:

  1. Commit Message: The commit message does not adhere to the expected format.
  2. Test Failure: Several tests failed during the execution of ./runtests.

Failures & Remediation

  1. Explanation: The CI pipeline failed because the commit message does not follow the required format. Specifically, the commit message references an issue (#721) in the title but fails to include a corresponding closing keyword (like Fixes, Closes, or Related to) in the body.

    Remediation: Update the commit message to include the issue number in the body with a closing keyword.

    [fix] Add VXLAN+IPv6 regression test and update changelog for #721
    
    Fixes #721
    
  2. Explanation: The CI pipeline failed due to multiple test failures reported by ./runtests. The logs indicate that 401 slow tests were detected, and specific tests within openwisp_controller.config.tests.test_selenium.TestDeviceAdmin and openwisp_controller.config.tests.test_selenium.TestDeviceAdminUnsavedChanges took an excessive amount of time (e.g., 13.87s, 9.48s, 18.89s, 7.30s, 5.57s, 7.63s, 4.11s, 25.70s, 2.18s, 6.29s, 6.32s, 6.31s, 3.15s, 3.14s, 3.15s, 5.20s, 5.12s, 8.47s, 4.31s, 7.27s, 6.36s, 3.90s, 3.01s, 3.40s, 10.99s). This suggests potential performance issues or tests that are not properly tagged as "slow".

    Remediation:

    • Investigate Slow Tests: The most immediate action is to investigate the specific slow tests identified in the logs. For tests marked with [31;1m (red color), which indicate failures, and those with long execution times, analyze their purpose and optimize them if possible.
    • Tag Slow Tests: If the tests are inherently slow but necessary, ensure they are correctly tagged with @tag('slow') so they can be optionally skipped during regular CI runs if needed. However, given the number of slow tests, optimization is preferred.
    • Address Test Failures: The logs show many tests marked with [31;1m (red). These indicate actual test failures, not just slowness. These need to be debugged and fixed. For example, test_change_device_group_action_changes_templates and test_group_templates_apply in openwisp_controller.config.tests.test_admin.TestDeviceGroupAdminTransaction are taking a very long time and are marked as failures. The root cause of these specific failures needs to be identified and resolved.

    Without more specific error messages for the test failures (beyond just the time taken and the red color), it's difficult to provide a precise code fix. However, the general approach is to:

    1. Run the tests locally (./runtests).
    2. Focus on the tests marked in red and those with exceptionally long execution times.
    3. Debug the logic of the failing tests and the corresponding code to identify the root cause.
    4. Fix the test logic or the application code as needed.

… helper

Pop 'organization' and 'subnet' from kwargs before passing them explicitly
to _create_vpn, preventing 'got multiple values for keyword argument' error
when test_auto_client_ipv6 supplies these via vpn_options.
@openwisp-companion
Copy link

CI Failure Analysis

Hello @devangpratap,
(Analysis for commit ebf14ff)

Build/Infrastructure/Other: GitHub Actions job timed out

Explanation:
The CI job JOB 66231111587 failed because it exceeded the maximum allowed execution time, resulting in a timeout. This is indicated by the final error message: ##[error]Process completed with exit code 1. and the subsequent HTML content suggesting a GitHub Actions page that is taking too long to load.

Remediation:
Since the failure is due to a timeout, it's likely a transient issue or an indication that the job is taking longer than usual to complete. The most straightforward remediation is to re-run the CI job. If the problem persists across multiple runs, it might indicate a need to investigate the job's execution time, potentially by optimizing the steps within the CI pipeline or increasing the timeout limits if feasible and appropriate.

# Re-run the CI job

Find WireGuard interface by name instead of assuming index 0,
as suggested by CodeRabbit review.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
openwisp_controller/config/tests/test_vpn.py (1)

654-656: ⚠️ Potential issue | 🟡 Minor

Use the interface type instead of a "wg" name substring.

These tests are still coupled to a naming convention, so they can fail if the WireGuard interface is renamed while the generated schema remains valid. Filtering by type == "wireguard" is the stable discriminator here.

Suggested test hardening
-        wg_interface = next(
-            (i for i in auto["interfaces"] if "wg" in i.get("name", "")), None
-        )
+        wg_interface = next(
+            (i for i in auto["interfaces"] if i.get("type") == "wireguard"), None
+        )
         self.assertIsNotNone(wg_interface, "WireGuard interface not found")

Also applies to: 1032-1034

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openwisp_controller/config/tests/test_vpn.py` around lines 654 - 656, The
test currently selects the WireGuard interface by name substring using
wg_interface = next((i for i in auto["interfaces"] if "wg" in i.get("name",
"")), None); change this to filter by the interface type instead (e.g., next((i
for i in auto["interfaces"] if i.get("type") == "wireguard"), None)) so the test
uses the stable discriminator "type" == "wireguard" when locating the interface
in the auto["interfaces"] list (apply the same change for the other occurrence
around lines 1032-1034).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@openwisp_controller/config/tests/test_vpn.py`:
- Around line 654-656: The test currently selects the WireGuard interface by
name substring using wg_interface = next((i for i in auto["interfaces"] if "wg"
in i.get("name", "")), None); change this to filter by the interface type
instead (e.g., next((i for i in auto["interfaces"] if i.get("type") ==
"wireguard"), None)) so the test uses the stable discriminator "type" ==
"wireguard" when locating the interface in the auto["interfaces"] list (apply
the same change for the other occurrence around lines 1032-1034).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6b0e3ec2-da8c-44f4-8463-5e5b92dcee34

📥 Commits

Reviewing files that changed from the base of the PR and between ebf14ff and 2670b9b.

📒 Files selected for processing (1)
  • openwisp_controller/config/tests/test_vpn.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=5.1.0
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2026-01-15T15:05:49.557Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/management/commands/clear_last_ip.py:38-42
Timestamp: 2026-01-15T15:05:49.557Z
Learning: In Django projects, when using select_related() to traverse relations (for example, select_related("organization__config_settings")), the traversed relation must not be deferred. If you also use .only() in the same query, include the relation name or FK field (e.g., "organization" or "organization_id") in the .only() list to avoid the error "Field X cannot be both deferred and traversed using select_related at the same time." Apply this guideline to Django code in openwisp_controller/config/management/commands/clear_last_ip.py and similar modules by ensuring any select_related with an accompanying only() includes the related field names to prevent deferred/traversed conflicts.

Applied to files:

  • openwisp_controller/config/tests/test_vpn.py
📚 Learning: 2026-02-17T19:13:10.088Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/whois/commands.py:0-0
Timestamp: 2026-02-17T19:13:10.088Z
Learning: In reviews for the openwisp/openwisp-controller repository, do not propose changes based on Ruff warnings. The project does not use Ruff as its linter; ignore Ruff-related suggestions and follow the repository’s established linting and configuration rules. This guidance applies to all Python files under the openwisp_controller directory.

Applied to files:

  • openwisp_controller/config/tests/test_vpn.py
📚 Learning: 2026-01-15T15:07:17.354Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/geo/estimated_location/tests/tests.py:172-175
Timestamp: 2026-01-15T15:07:17.354Z
Learning: In this repository, flake8 enforces E501 (line too long) via setup.cfg (max-line-length = 88) while ruff ignores E501 via ruff.toml. Therefore, use '# noqa: E501' on lines that intentionally exceed 88 characters to satisfy flake8 without affecting ruff checks. This applies to Python files across the project (any .py) and is relevant for tests as well. Use sparingly and only where breaking lines is not feasible without hurting readability or functionality.

Applied to files:

  • openwisp_controller/config/tests/test_vpn.py

@openwisp-companion
Copy link

CI Failure Analysis

Hello @devangpratap,
(Analysis for commit c3abc81)

The CI pipeline failed due to a Test Failure.

Explanation:

The double free or corruption (fasttop) error and the subsequent Fatal Python error: Aborted indicate a memory corruption issue during the execution of Selenium browser tests. This often occurs due to issues in how resources are managed or cleaned up, particularly in complex test environments involving external drivers like Selenium. The traceback points to the tearDownClass method in openwisp_utils/tests/selenium.py, which is responsible for stopping the WebDriver. The memory corruption suggests that the WebDriver or its associated resources might not be properly released, leading to this crash.

Remediation:

  1. Review Selenium Resource Management: Examine the tearDownClass method in openwisp_utils/tests/selenium.py and related cleanup logic. Ensure that all Selenium WebDriver instances and associated browser processes are correctly quit and terminated. Pay close attention to any asynchronous operations or potential race conditions that might prevent proper cleanup.
  2. Isolate the Failing Test: If the issue is specific to a particular Selenium test, try to isolate it. Temporarily commenting out other Selenium tests might help pinpoint which specific test case triggers the memory corruption.
  3. Update Dependencies: Ensure that selenium, webdriver-manager (if used), and the browser drivers (e.g., ChromeDriver, GeckoDriver) are all updated to their latest compatible versions. Outdated dependencies can sometimes lead to such memory management issues.
  4. Check for Known Issues: Search the issue trackers for selenium, webdriver-manager, and the specific browser driver being used for similar "double free or corruption" errors. There might be known bugs or specific configurations required to avoid this problem.
  5. Consider Environment Differences: The failure occurred in a CI environment. There might be subtle differences in how resources are managed in the CI environment compared to a local development setup. Ensure the CI environment is configured correctly for running browser tests.

If the issue persists after these steps, it might be necessary to add more detailed logging within the Selenium test setup and teardown to capture the exact state leading to the crash.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] Wireguard template auto tunnel provisioning breaks using IPV6

2 participants