Skip to content

T8989: prefix_lists module#12

Open
omnom62 wants to merge 4 commits into
mainfrom
T8989_prefix_lists
Open

T8989: prefix_lists module#12
omnom62 wants to merge 4 commits into
mainfrom
T8989_prefix_lists

Conversation

@omnom62

@omnom62 omnom62 commented Jun 14, 2026

Copy link
Copy Markdown
Collaborator

Change Summary

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Code style update (formatting, renaming)
  • Refactoring (no functional changes)
  • Migration from an old Vyatta component to vyos-1x, please link to related PR inside obsoleted component
  • Other (please describe):

Related Task(s)

Related PR(s)

Component(s) name

Proposed changes

How to test

Test results

  • Sanity tests passed
  • Unit tests passed

Tested against VyOS versions:

  • 1.3.8
  • 1.4-rolling-202201010100

Checklist:

  • I have read the CONTRIBUTING document
  • I have linked this PR to one or more Phabricator Task(s)
  • I have run the ansible sanity and unit tests
  • My commit headlines contain a valid Task id
  • My change requires a change to the documentation
  • I have updated the documentation accordingly
  • I have added unit tests to cover my changes
  • I have added a file to changelogs/fragments to describe the changes

@omnom62 omnom62 requested a review from a team as a code owner June 14, 2026 10:12
@omnom62 omnom62 requested review from andamasov, dmbaturin and gaige June 14, 2026 10:12
@omnom62 omnom62 marked this pull request as draft June 14, 2026 10:12
@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added vyos_prefix_lists module to manage IPv4/IPv6 prefix lists on VyOS devices via REST API, supporting merged, replaced, overridden, deleted, and gathered states.
  • Documentation

    • Updated README to indicate VyOS REST modules are now available.
    • Added comprehensive documentation for the new prefix-lists module.
  • Tests

    • Added unit tests for the prefix-lists module functionality.

Walkthrough

Adds the vyos_prefix_lists Ansible module for managing IPv4/IPv6 prefix-lists on VyOS devices via REST API. Includes device state parsing, idempotent command generation for merged/replaced/overridden/deleted/gathered states, comprehensive unit tests, RST documentation, and README/changelog updates.

Changes

vyos_prefix_lists module

Layer / File(s) Summary
Module metadata and config parsing
plugins/modules/vyos_prefix_lists.py
Defines DOCUMENTATION, EXAMPLES, RETURN, and ARGUMENT_SPEC. Implements get_running_config to fetch and parse policy data from device into structured per-AFI entries with typed rule fields. Adds _normalize to convert desired and existing configs into keyed per-AFI/per-list/per-sequence maps, filtering None ge/le values.
Command generation and main integration
plugins/modules/vyos_prefix_lists.py
Implements _rule_cmds to emit set commands only for differing rule fields. build_commands dispatches across deleted, overridden, replaced, merged states with idempotent delete-and-rebuild logic. main() wires Ansible params, check mode, command application, config save, and before/after results.
Unit tests and fixtures
tests/unit/modules/test_vyos_prefix_lists.py, tests/unit/fixtures/prefix_lists_running.json
Test scaffolding with load_fixture and VyOSModuleTestCase base. Three test classes validate get_running_config parsing for IPv4/IPv6/empty, _normalize field filtering and None omission, and build_commands for merged/deleted/replaced transitions including idempotency and delete-before-rebuild ordering. Fixture removes per-rule description fields.
RST documentation, README, and changelog
docs/vyos.rest.vyos_prefix_lists_module.rst, README.md, changelogs/fragments/t8989_prefix_lists.yml
Full RST page with parameters schema, operational notes, YAML examples for merged/deleted/gathered states, and return-value table. README removes not-yet-available warning for vyos_prefix_lists. Changelog fragment records module addition with tests and docs.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning PR description consists of an unfilled template with no substantive content describing the changes, implementation, or testing performed. Complete the PR description with: feature overview, implementation details, how to test, test results with version confirmation, and explanation of changes made across all modified files.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed Title directly describes the main feature being added: a new prefix_lists module for VyOS REST API management.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch T8989_prefix_lists

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

@mergify

mergify Bot commented Jun 14, 2026

Copy link
Copy Markdown

Merge Protections

Your pull request matches the following merge protections and will not be merged until they are valid.

🔴 invalid-task-id label must be absent to merge

Waiting for

  • label != invalid-task-id
This rule is failing.

Block merge while the invalid-task-id label is present. Set by the per-repo product T-ID rule (product repos only); dormant where the label is never applied.

  • label != invalid-task-id

@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: 5

🤖 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 `@changelogs/fragments/t8989_prefix_lists.yml`:
- Around line 1-3: The release note for the vyos_prefix_lists module is
currently classified under the trivial category, but since this is a new
user-facing feature module with tests and documentation, it should be moved to a
feature category. Remove the entry from the trivial section and add it under
minor_changes (or major_changes if this is a breaking change) to properly
reflect that this is a feature addition rather than a trivial infrastructure
change.

In `@docs/vyos.rest.vyos_prefix_lists_module.rst`:
- Around line 37-257: Replace all duplicate `id="parameter-"` anchor IDs with
unique, parameter-specific IDs throughout the documentation table. For each
parameter row, use the parameter name to generate a unique ID following the
pattern `id="parameter-{parameter_name}"`. For example, replace the empty ID in
the config parameter with `id="parameter-config"`, the afi parameter with
`id="parameter-afi"`, the prefix_lists parameter with
`id="parameter-prefix_lists"`, and similarly for all other parameters including
description, entries, action, ge, le, prefix, sequence, and name. This ensures
each anchor is unique and the permalink functionality works correctly for
cross-references.

In `@plugins/modules/vyos_prefix_lists.py`:
- Around line 279-310: The state == "overridden" path only deletes prefix-lists
that are absent from want_afi (lines 279-280) but then applies generic update
logic (lines 282-310) that preserves existing rules and descriptions instead of
replacing them. When state == "overridden", surviving prefix-lists (those in
both have_afi and want_afi) should be fully replaced, not merged. Add a
condition in the loop starting at line 282 that checks if state == "overridden"
and pl_name exists in have_afi, then apply the same deletion-and-rebuild logic
currently used only for state == "replaced" (lines 289-301) to ensure extra
rules and stale descriptions are removed rather than preserved.
- Around line 315-320: The ARGUMENT_SPEC dictionary defines config as a generic
list of dictionaries without specifying the required nested schema, while the
module docstring at lines 22-75 documents the required structure with fields
like afi, name, and sequence. This mismatch causes KeyError exceptions in lines
267, 213, 216, and 269 when the code accesses these keys directly using bracket
notation. Move the documented suboptions schema from the module docstring into
the ARGUMENT_SPEC config parameter definition, converting the documented
structure (with required fields, types, and descriptions) into proper Ansible
argument specification using the suboptions keyword. This will enforce
validation at argument parsing time and provide clear error messages instead of
unhandled exceptions.

In `@tests/unit/modules/test_vyos_prefix_lists.py`:
- Around line 12-20: The sys.path.insert statement on line 12 traverses four
levels up from the test file location, which goes above the repository root and
prevents the import of tests.unit.modules.base on line 20 from working. Reduce
the number of parent directory traversals from four to three in the
sys.path.insert call so that it correctly points to the repository root, making
the relative import paths valid. This will allow pytest to properly collect and
import the test module and its dependencies.
🪄 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: Repository YAML (base), Central YAML (inherited), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 224a62b5-2aa7-4b3a-8343-15f6e55a23f9

📥 Commits

Reviewing files that changed from the base of the PR and between db8c9bd and a31610f.

📒 Files selected for processing (9)
  • README.md
  • changelogs/fragments/t8989_prefix_lists.yml
  • docs/vyos.rest.vyos_prefix_lists_module.rst
  • plugins/modules/vyos_prefix_lists.py
  • tests/integration/targets/vyos_prefix_lists/tests/httpapi/deleted.yaml
  • tests/integration/targets/vyos_prefix_lists/tests/httpapi/gathered.yaml
  • tests/integration/targets/vyos_prefix_lists/tests/httpapi/merged.yaml
  • tests/integration/targets/vyos_prefix_lists/tests/httpapi/replaced.yaml
  • tests/unit/modules/test_vyos_prefix_lists.py
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • ansible/ansible (manual)
📜 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). (3)
  • GitHub Check: sanity / sanity-py3.11-2.19
  • GitHub Check: Mergify Merge Protections
  • GitHub Check: Summary
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Format Python code using black with line length of 100 characters

Files:

  • tests/unit/modules/test_vyos_prefix_lists.py
  • plugins/modules/vyos_prefix_lists.py
**/test_*.py

📄 CodeRabbit inference engine (AGENTS.md)

Use pytest with pytest-xdist (parallel execution with -n 2) for running collection tests

Files:

  • tests/unit/modules/test_vyos_prefix_lists.py
🪛 GitHub Actions: Code Coverage / 0_Code Coverage Python 3.10.txt
tests/unit/modules/test_vyos_prefix_lists.py

[error] 20-20: Pytest failed to import test module due to ModuleNotFoundError: No module named 'tests'. Import at tests/unit/modules/test_vyos_prefix_lists.py:20: 'from tests.unit.modules.base import VyOSModuleTestCase, load_fixture'.

🪛 GitHub Actions: Code Coverage / Code Coverage Python 3.10
tests/unit/modules/test_vyos_prefix_lists.py

[error] 20-20: Pytest ImportError while importing test module. ModuleNotFoundError: No module named 'tests'.

🪛 Ruff (0.15.15)
plugins/modules/vyos_prefix_lists.py

[warning] 226-226: Consider [*base, "rule", str(seq)] instead of concatenation

Replace with [*base, "rule", str(seq)]

(RUF005)


[warning] 235-235: Consider [*rbase, api_key, want_rule[field]] instead of concatenation

Replace with [*rbase, api_key, want_rule[field]]

(RUF005)


[warning] 237-237: Consider [*rbase, "ge", str(want_rule["ge"])] instead of concatenation

Replace with [*rbase, "ge", str(want_rule["ge"])]

(RUF005)


[warning] 239-239: Consider [*rbase, "le", str(want_rule["le"])] instead of concatenation

Replace with [*rbase, "le", str(want_rule["le"])]

(RUF005)


[warning] 248-248: Consider [*rbase, api_key, want_rule[field]] instead of concatenation

Replace with [*rbase, api_key, want_rule[field]]

(RUF005)


[warning] 251-251: Consider [*rbase, field, str(want_rule[field])] instead of concatenation

Replace with [*rbase, field, str(want_rule[field])]

(RUF005)


[warning] 306-306: Consider [*base, "description", want_pl["description"]] instead of concatenation

Replace with [*base, "description", want_pl["description"]]

(RUF005)

🔍 Remote MCP Context7

Based on the search results, I've found important contextual information for your PR review. Let me search for more specific details about REST API modules in the vyos collection.## Summary of Relevant Context for PR Review

Based on the additional research, here are key contextual facts relevant to reviewing this PR:

VyOS REST API Module Architecture

VyOS previously used shell session emulation for programmatic access, but has since moved toward HTTP API support. This PR appears to be introducing modules for the newer vyos.rest collection that leverages REST APIs with httpapi connection type, which is a newer approach compared to the existing network_cli modules.

VyOS Prefix Lists Functionality

The prefix lists being implemented have specific VyOS configuration capabilities:

  • Prefix lists provide the most powerful prefix-based filtering mechanism, with prefix length range specification in addition to access-list functionality
  • Prefix filtering can be done using both prefix-list (IPv4) and prefix-list6 (IPv6)
  • Key configurable fields include: action (permit/deny), prefix, sequence number, description, and optional ge (greater than/equal to) and le (less than/equal to) netmask length constraints

Module Testing and Idempotency

The PR includes comprehensive unit tests that validate:

  • IPv4 and IPv6 prefix list parsing from VyOS running config
  • Normalization of configurations (omitting None values)
  • Idempotent behavior for merged, replaced, and deleted states
  • Proper command generation for state transitions

Ansible Collection Context

The VyOS collection has been tested against VyOS 1.3.8, 1.4.1, and the current rolling release for 1.5, with compatibility maintained where possible for older versions. The VyOS collection primarily supports network_cli connections, though this PR appears to introduce modules using httpapi instead.

Key Review Considerations

  1. REST vs CLI: This PR introduces REST API modules (vyos.rest namespace based on the file paths), which differ from the existing vyos.vyos collection modules that use CLI/SSH connections
  2. Idempotency: The module implements proper normalization and command generation to ensure idempotent behavior across merged, replaced, and deleted states
  3. Documentation: Documentation is comprehensive, including parameters, operational examples, and return values following Ansible standards
  4. Test Coverage: Unit tests cover parsing, normalization, and command generation scenarios

[::Web Search::],

🔇 Additional comments (1)
README.md (1)

83-86: LGTM!

Comment on lines +1 to +3
---
trivial:
- vyos_prefix_lists - Added new module and tests and documentation.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check changelog guidelines and structure
find . -name "*.md" -o -name "*.rst" | grep -i changelog | head -20

Repository: vyos/rest.vyos

Length of output: 40


🏁 Script executed:

#!/bin/bash
# Look at changelog examples to understand category usage
find ./changelogs -type f | head -20

Repository: vyos/rest.vyos

Length of output: 229


🏁 Script executed:

#!/bin/bash
# Check if vyos_prefix_lists module exists
find . -name "*prefix_list*" -type f

Repository: vyos/rest.vyos

Length of output: 284


🏁 Script executed:

#!/bin/bash
# Check the actual changelog fragment content
cat changelogs/fragments/t8989_prefix_lists.yml

Repository: vyos/rest.vyos

Length of output: 141


🏁 Script executed:

#!/bin/bash
# Check changelog configuration for category definitions
cat changelogs/config.yaml

Repository: vyos/rest.vyos

Length of output: 878


🏁 Script executed:

#!/bin/bash
# Look at other changelog fragments to understand category patterns
cat changelogs/fragments/*.yml | head -50

Repository: vyos/rest.vyos

Length of output: 301


🏁 Script executed:

#!/bin/bash
# Check if there's documentation on changelog categories
find . -type f \( -name "CHANGELOG*" -o -name "CHANGES*" -o -name "CONTRIBUTING*" \) | head -10

Repository: vyos/rest.vyos

Length of output: 40


🏁 Script executed:

#!/bin/bash
# Check the CHANGELOG to see examples of what goes into trivial vs feature categories
head -100 CHANGELOG.rst

Repository: vyos/rest.vyos

Length of output: 131


🏁 Script executed:

#!/bin/bash
# Check if there's a README or contribution guide that explains changelog categories
cat README.md 2>/dev/null | head -50 || cat CONTRIBUTING.md 2>/dev/null | head -50 || echo "No README/CONTRIBUTING found"

Repository: vyos/rest.vyos

Length of output: 1163


🏁 Script executed:

#!/bin/bash
# Look at git history to see what types of entries go into trivial
cd changelogs && ls -la

Repository: vyos/rest.vyos

Length of output: 353


Move this release note out of trivial.

A new module with tests and documentation is a feature addition and should be classified as minor_changes or major_changes, not trivial. The trivial category is appropriate only for infrastructure changes (like the tox configuration entry in the same fragment directory), not user-facing features.

🤖 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 `@changelogs/fragments/t8989_prefix_lists.yml` around lines 1 - 3, The release
note for the vyos_prefix_lists module is currently classified under the trivial
category, but since this is a new user-facing feature module with tests and
documentation, it should be moved to a feature category. Remove the entry from
the trivial section and add it under minor_changes (or major_changes if this is
a breaking change) to properly reflect that this is a feature addition rather
than a trivial infrastructure change.

Comment on lines +37 to +257
<tr>
<td colspan="4">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>config</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">list</span>
/ <span style="color: purple">elements=dictionary</span>
</div>
</td>
<td>
</td>
<td>
<div>List of prefix-list configurations grouped by address family.</div>
</td>
</tr>
<tr>
<td class="elbow-placeholder"></td>
<td colspan="3">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>afi</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">string</span>
/ <span style="color: red">required</span>
</div>
</td>
<td>
<ul style="margin: 0; padding: 0"><b>Choices:</b>
<li>ipv4</li>
<li>ipv6</li>
</ul>
</td>
<td>
<div>Address family identifier.</div>
</td>
</tr>
<tr>
<td class="elbow-placeholder"></td>
<td colspan="3">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>prefix_lists</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">list</span>
/ <span style="color: purple">elements=dictionary</span>
</div>
</td>
<td>
</td>
<td>
<div>Named prefix lists.</div>
</td>
</tr>
<tr>
<td class="elbow-placeholder"></td>
<td class="elbow-placeholder"></td>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>description</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">string</span>
</div>
</td>
<td>
</td>
<td>
<div>Prefix list description.</div>
</td>
</tr>
<tr>
<td class="elbow-placeholder"></td>
<td class="elbow-placeholder"></td>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>entries</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">list</span>
/ <span style="color: purple">elements=dictionary</span>
</div>
</td>
<td>
</td>
<td>
<div>Prefix list rules.</div>
</td>
</tr>
<tr>
<td class="elbow-placeholder"></td>
<td class="elbow-placeholder"></td>
<td class="elbow-placeholder"></td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>action</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">string</span>
</div>
</td>
<td>
<ul style="margin: 0; padding: 0"><b>Choices:</b>
<li>permit</li>
<li>deny</li>
</ul>
</td>
<td>
<div>Permit or deny.</div>
</td>
</tr>
<tr>
<td class="elbow-placeholder"></td>
<td class="elbow-placeholder"></td>
<td class="elbow-placeholder"></td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>description</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">string</span>
</div>
</td>
<td>
</td>
<td>
<div>Rule description.</div>
</td>
</tr>
<tr>
<td class="elbow-placeholder"></td>
<td class="elbow-placeholder"></td>
<td class="elbow-placeholder"></td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>ge</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">integer</span>
</div>
</td>
<td>
</td>
<td>
<div>Minimum prefix length.</div>
</td>
</tr>
<tr>
<td class="elbow-placeholder"></td>
<td class="elbow-placeholder"></td>
<td class="elbow-placeholder"></td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>le</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">integer</span>
</div>
</td>
<td>
</td>
<td>
<div>Maximum prefix length.</div>
</td>
</tr>
<tr>
<td class="elbow-placeholder"></td>
<td class="elbow-placeholder"></td>
<td class="elbow-placeholder"></td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>prefix</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">string</span>
</div>
</td>
<td>
</td>
<td>
<div>Network prefix to match.</div>
</td>
</tr>
<tr>
<td class="elbow-placeholder"></td>
<td class="elbow-placeholder"></td>
<td class="elbow-placeholder"></td>
<td colspan="1">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>sequence</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">integer</span>
/ <span style="color: red">required</span>
</div>
</td>
<td>
</td>
<td>
<div>Rule sequence number.</div>
</td>
</tr>

<tr>
<td class="elbow-placeholder"></td>
<td class="elbow-placeholder"></td>
<td colspan="2">
<div class="ansibleOptionAnchor" id="parameter-"></div>
<b>name</b>
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
<div style="font-size: small">
<span style="color: purple">string</span>
/ <span style="color: red">required</span>
</div>
</td>
<td>
</td>
<td>
<div>Prefix list name.</div>
</td>
</tr>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check file size and examine the anchor IDs in both sections
wc -l docs/vyos.rest.vyos_prefix_lists_module.rst
echo "---"
# View the entire parameter and return-values sections
sed -n '37,257p' docs/vyos.rest.vyos_prefix_lists_module.rst | grep -E 'id="(parameter|return)-"' | head -20
echo "---"
# View return-values section
sed -n '353,442p' docs/vyos.rest.vyos_prefix_lists_module.rst | grep -E 'id="(parameter|return)-"' | head -20

Repository: vyos/rest.vyos

Length of output: 1464


Use unique anchor IDs in parameter and return-values tables.

The id="parameter-" anchors in lines 37-257 and id="return-" anchors in lines 353-442 are duplicated across all rows. This breaks permalink functionality—all option links in a section target the same anchor instead of their respective parameters. Emit option-specific IDs to fix documentation cross-references.

🤖 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 `@docs/vyos.rest.vyos_prefix_lists_module.rst` around lines 37 - 257, Replace
all duplicate `id="parameter-"` anchor IDs with unique, parameter-specific IDs
throughout the documentation table. For each parameter row, use the parameter
name to generate a unique ID following the pattern
`id="parameter-{parameter_name}"`. For example, replace the empty ID in the
config parameter with `id="parameter-config"`, the afi parameter with
`id="parameter-afi"`, the prefix_lists parameter with
`id="parameter-prefix_lists"`, and similarly for all other parameters including
description, entries, action, ge, le, prefix, sequence, and name. This ensures
each anchor is unique and the permalink functionality works correctly for
cross-references.

Comment on lines +279 to +310
if state == "overridden":
for pl_name in set(have_afi) - set(want_afi):
cmds.append(("delete", ["policy", api_key, pl_name]))

for pl_name, want_pl in want_afi.items():
have_pl = have_afi.get(pl_name, {})
base = ["policy", api_key, pl_name]
want_rules = want_pl.get("rules", {})
have_rules = have_pl.get("rules", {})

if state == "replaced" and pl_name in have_afi:
# Only delete and rebuild if something actually differs
want_desc = want_pl.get("description")
have_desc = have_pl.get("description")
extra_seqs = set(have_rules.keys()) - set(want_rules.keys())
test_cmds = []
for seq, want_rule in want_rules.items():
test_cmds += _rule_cmds(base, seq, want_rule, have_rules, state)
if test_cmds or extra_seqs or want_desc != have_desc:
cmds.append(("delete", ["policy", api_key, pl_name]))
have_pl = {}
have_rules = {}
else:
continue # already matches — idempotent

# Description
if want_pl.get("description") and want_pl["description"] != have_pl.get("description"):
cmds.append(("set", base + ["description", want_pl["description"]]))

# Rules
for seq, want_rule in want_rules.items():
cmds += _rule_cmds(base, seq, want_rule, have_rules, state)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

state: overridden still merges surviving prefix-lists.

Line 279 only deletes list names absent from want_afi. For names that remain, Lines 289-310 never remove extra rules or stale descriptions, so overridden behaves like merged for those lists instead of replacing them.

Suggested fix
-            if state == "replaced" and pl_name in have_afi:
+            if state in {"replaced", "overridden"} and pl_name in have_afi:
                 # Only delete and rebuild if something actually differs
                 want_desc = want_pl.get("description")
                 have_desc = have_pl.get("description")
                 extra_seqs = set(have_rules.keys()) - set(want_rules.keys())
🧰 Tools
🪛 Ruff (0.15.15)

[warning] 306-306: Consider [*base, "description", want_pl["description"]] instead of concatenation

Replace with [*base, "description", want_pl["description"]]

(RUF005)

🤖 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 `@plugins/modules/vyos_prefix_lists.py` around lines 279 - 310, The state ==
"overridden" path only deletes prefix-lists that are absent from want_afi (lines
279-280) but then applies generic update logic (lines 282-310) that preserves
existing rules and descriptions instead of replacing them. When state ==
"overridden", surviving prefix-lists (those in both have_afi and want_afi)
should be fully replaced, not merged. Add a condition in the loop starting at
line 282 that checks if state == "overridden" and pl_name exists in have_afi,
then apply the same deletion-and-rebuild logic currently used only for state ==
"replaced" (lines 289-301) to ensure extra rules and stale descriptions are
removed rather than preserved.

Comment on lines +315 to +320
ARGUMENT_SPEC = dict(
config=dict(type="list", elements="dict"),
state=dict(
default="merged",
choices=["merged", "replaced", "overridden", "deleted", "gathered"],
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, locate the file and check its size
find . -name "vyos_prefix_lists.py" -type f
wc -l plugins/modules/vyos_prefix_lists.py 2>/dev/null || echo "File not found at that path"

Repository: vyos/rest.vyos

Length of output: 138


🏁 Script executed:

#!/bin/bash
# Read the relevant sections of the file
# Lines 315-320 (ARGUMENT_SPEC)
echo "=== Lines 315-320 (ARGUMENT_SPEC) ==="
sed -n '315,320p' plugins/modules/vyos_prefix_lists.py

# Lines 212, 215, 266 (dictionary accesses)
echo -e "\n=== Line 212 ==="
sed -n '210,215p' plugins/modules/vyos_prefix_lists.py

echo -e "\n=== Line 215 ==="
sed -n '213,218p' plugins/modules/vyos_prefix_lists.py

echo -e "\n=== Line 266 ==="
sed -n '264,270p' plugins/modules/vyos_prefix_lists.py

Repository: vyos/rest.vyos

Length of output: 1117


🏁 Script executed:

#!/bin/bash
# Search for validation logic and imports
echo "=== VyOSModule import ==="
grep -n "VyOSModule\|from\|import" plugins/modules/vyos_prefix_lists.py | head -20

echo -e "\n=== Full ARGUMENT_SPEC definition ==="
sed -n '315,350p' plugins/modules/vyos_prefix_lists.py

Repository: vyos/rest.vyos

Length of output: 1347


🏁 Script executed:

#!/bin/bash
# Check for any validation on config items
echo "=== Lines around 212, 215, 266 with context ==="
sed -n '200,220p' plugins/modules/vyos_prefix_lists.py
echo "---"
sed -n '260,275p' plugins/modules/vyos_prefix_lists.py

# Search for "config" usage and any validation
echo -e "\n=== Search for config validation or normalization ==="
grep -n "config\|validate\|fail_json" plugins/modules/vyos_prefix_lists.py | head -30

Repository: vyos/rest.vyos

Length of output: 2887


🏁 Script executed:

#!/bin/bash
# Check the module documentation more thoroughly
echo "=== Documentation section (lines 20-100) ==="
sed -n '20,100p' plugins/modules/vyos_prefix_lists.py

# Look for VyOSModule implementation
echo -e "\n=== Search for VyOSModule file ==="
find . -path "*module_utils*vyos*" -name "*.py" | head -5

Repository: vyos/rest.vyos

Length of output: 2686


🏁 Script executed:

#!/bin/bash
# Look at the documented config structure in YAML documentation
sed -n '22,75p' plugins/modules/vyos_prefix_lists.py

Repository: vyos/rest.vyos

Length of output: 1747


Define config schema in ARGUMENT_SPEC to enforce validation before code execution.

The documented schema (lines 22–75) specifies required fields (afi, name, sequence), but ARGUMENT_SPEC at line 316 only declares config=dict(type="list", elements="dict") without nested structure. Lines 267, 213, 216, and 269 access these keys directly with [], causing KeyError if playbook data is malformed. Move the documented suboptions from the module docstring into ARGUMENT_SPEC to enforce validation through Ansible's argument validator, ensuring clean error messages instead of unhandled exceptions.

🤖 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 `@plugins/modules/vyos_prefix_lists.py` around lines 315 - 320, The
ARGUMENT_SPEC dictionary defines config as a generic list of dictionaries
without specifying the required nested schema, while the module docstring at
lines 22-75 documents the required structure with fields like afi, name, and
sequence. This mismatch causes KeyError exceptions in lines 267, 213, 216, and
269 when the code accesses these keys directly using bracket notation. Move the
documented suboptions schema from the module docstring into the ARGUMENT_SPEC
config parameter definition, converting the documented structure (with required
fields, types, and descriptions) into proper Ansible argument specification
using the suboptions keyword. This will enforce validation at argument parsing
time and provide clear error messages instead of unhandled exceptions.

Comment thread tests/unit/modules/test_vyos_prefix_lists.py Outdated
@codecov

codecov Bot commented Jun 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@omnom62 omnom62 changed the title T8989: REST API Modules T8989: prefix_lists module Jun 14, 2026
@omnom62 omnom62 marked this pull request as ready for review June 14, 2026 19:48

@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.

Caution

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

⚠️ Outside diff range comments (1)
tests/unit/modules/test_vyos_prefix_lists.py (1)

217-217: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fragile assertion: substring search can match unintended paths.

The check "deny" in c[1] searches for substring "deny" anywhere in the command path list. A more robust approach would verify the specific path element, e.g., c[1] == ["policy", "prefix-list", "PL1", "rule", "10", "action", "deny"] or check c[1][-1] == "deny" if the last element is always the action value.

🔍 Proposed fix
-        set_idx = next(i for i, c in enumerate(ops) if c[0] == "set" and "deny" in c[1])
+        set_idx = next(i for i, c in enumerate(ops) if c[0] == "set" and c[1][-1] == "deny")
🤖 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 `@tests/unit/modules/test_vyos_prefix_lists.py` at line 217, The substring
search `"deny" in c[1]` in the assertion at line 217 is fragile because it
matches "deny" anywhere within the command path list element, not just as the
action value. Replace this fragile check with a more robust approach that
verifies the specific path element—either by checking the last element of the
path with `c[1][-1] == "deny"` if the action is always the final element, or by
comparing against the complete expected path. This ensures the assertion matches
the intended operation rather than accidentally matching any path containing the
substring "deny".
🧹 Nitpick comments (1)
tests/unit/modules/test_vyos_prefix_lists.py (1)

129-219: ⚡ Quick win

Consider adding tests for overridden and gathered states.

The test suite covers merged, deleted, and replaced states but omits overridden and gathered. Adding tests for these states would improve coverage and validate edge cases like deleting unmentioned prefix-lists (overridden) and read-only config retrieval (gathered).

🤖 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 `@tests/unit/modules/test_vyos_prefix_lists.py` around lines 129 - 219, Add
test methods to the TestVyOSPrefixListsBuildCommands class to cover the
`overridden` and `gathered` states. For the `overridden` state, create a test
method like test_overridden that verifies unmentioned prefix-lists are deleted
while specified ones replace existing configurations. For the `gathered` state,
create a test method like test_gathered that validates read-only config
retrieval returns the current state without applying any changes. Reuse the
helper methods _have_empty() and _have_with_ipv4_pl() as needed to provide test
data and call build_commands with the appropriate state parameter to validate
the expected behavior.
🤖 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.

Outside diff comments:
In `@tests/unit/modules/test_vyos_prefix_lists.py`:
- Line 217: The substring search `"deny" in c[1]` in the assertion at line 217
is fragile because it matches "deny" anywhere within the command path list
element, not just as the action value. Replace this fragile check with a more
robust approach that verifies the specific path element—either by checking the
last element of the path with `c[1][-1] == "deny"` if the action is always the
final element, or by comparing against the complete expected path. This ensures
the assertion matches the intended operation rather than accidentally matching
any path containing the substring "deny".

---

Nitpick comments:
In `@tests/unit/modules/test_vyos_prefix_lists.py`:
- Around line 129-219: Add test methods to the TestVyOSPrefixListsBuildCommands
class to cover the `overridden` and `gathered` states. For the `overridden`
state, create a test method like test_overridden that verifies unmentioned
prefix-lists are deleted while specified ones replace existing configurations.
For the `gathered` state, create a test method like test_gathered that validates
read-only config retrieval returns the current state without applying any
changes. Reuse the helper methods _have_empty() and _have_with_ipv4_pl() as
needed to provide test data and call build_commands with the appropriate state
parameter to validate the expected behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: e6d1f4f6-594c-4b0f-bcbd-58bbd3245fe3

📥 Commits

Reviewing files that changed from the base of the PR and between a31610f and c08f3fa.

📒 Files selected for processing (2)
  • tests/unit/fixtures/prefix_lists_running.json
  • tests/unit/modules/test_vyos_prefix_lists.py
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • ansible/ansible (manual)
📜 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). (2)
  • GitHub Check: Mergify Merge Protections
  • GitHub Check: Summary
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Format Python code using black with line length of 100 characters

Files:

  • tests/unit/modules/test_vyos_prefix_lists.py
**/test_*.py

📄 CodeRabbit inference engine (AGENTS.md)

Use pytest with pytest-xdist (parallel execution with -n 2) for running collection tests

Files:

  • tests/unit/modules/test_vyos_prefix_lists.py
🔍 Remote MCP Context7

Perfect. Now I have sufficient concrete information to provide a comprehensive summary. Let me compile the findings.

Additional Relevant Context for PR Review

Based on targeted research, here are the concrete facts relevant to reviewing this pull request:

Ansible Network Resource Module Standards

State Management Requirements:
According to Ansible's network resource module development guidelines, modules maintaining the vyos.rest collection should support the following state values:

  • merged: Combines provided configuration with existing device settings
  • replaced: Replaces specific configuration subsections (with caution to avoid disrupting device access)
  • overridden: Replaces entire resource configuration
  • deleted: Removes configuration subsections and restores defaults
  • gathered: Retrieves current resource details from the device (read-only, no changes)
  • rendered and parsed: Device-native rendering and parsing (optional, offline-capable)

The module implementation includes all required states (merged, replaced, deleted, gathered), which aligns with Ansible standards.

HTTP API Connection Pattern:
The PR correctly uses connection: httpapi as the execution mode for REST-based modules, following Ansible's httpapi plugin patterns.

VyOS REST API and Prefix Lists Configuration

REST API Endpoint Availability:
VyOS provides a REST API accessible via HTTPS with the following authentication mechanism:

  • API key-based authentication (configured via set service https api keys id <name> key <apikey>)
  • Endpoints require form-based data submission with JSON payloads

VyOS Prefix List Configuration Structure:
The VyOS policy prefix-list configuration hierarchy supports:

  • IPv4 prefix-lists (policy prefix-list <name>)
  • IPv6 prefix-lists (policy prefix-list6 <name>)
  • Rule attributes per rule: action (permit/deny), prefix (IPv4/IPv6 CIDR), description (optional), ge (greater-than-equal prefix length, 0-32 for IPv4, 0-128 for IPv6), le (less-than-equal prefix length)

The module's handling of ge/le normalization (filtering out None values, as shown in unit tests) aligns with VyOS's optional range constraints.

Code Review Observations

Key Strengths:

  1. Comprehensive unit test coverage across configuration parsing, normalization, and command generation
  2. Idempotent behavior validated for state transitions (merged, replaced, deleted)
  3. Proper handling of IPv4 and IPv6 configurations separately
  4. Documentation follows Ansible standards with parameters, examples, and return values

Areas to Verify During Review:

  1. Verify the module correctly handles the VyOS REST API response structure when retrieving running prefix-list configuration
  2. Confirm the build_commands logic properly generates set policy prefix-list[6] <name> rule <seq> ... syntax for all state transitions
  3. Validate that the commit and save operations are handled (or documented as device responsibility) after configuration changes
  4. Test compatibility with multiple VyOS versions (given VyOS has version-specific API differences between branches)
🔇 Additional comments (4)
tests/unit/modules/test_vyos_prefix_lists.py (3)

7-34: LGTM!


36-81: LGTM!


84-127: LGTM!

tests/unit/fixtures/prefix_lists_running.json (1)

1-42: LGTM!

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

Development

Successfully merging this pull request may close these issues.

1 participant