Skip to content

bgp: T9404: do not render "bgp default local-preference" when the value is 100 - #5536

Open
alexk37 wants to merge 1 commit into
vyos:rollingfrom
alexk37:bgp-default-local-pref-100
Open

alexk37 wants to merge 1 commit into
vyos:rollingfrom
alexk37:bgp-default-local-pref-100

Conversation

@alexk37

@alexk37 alexk37 commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Change summary

bgp default local-preference is now rendered only when the configured value differs from the default, 100:

  • interface-definitions/include/bgp/protocol-common-config.xml.i: <defaultValue>100</defaultValue> on parameters default local-pref (shared by the global and VRF instances).
  • python/vyos/frrender.py: the default is added to the BGP dict as xml_default_local_pref (from conf.get_config_defaults()), for the global instance and for every VRF instance.
  • data/templates/frr/bgpd.frr.j2: renders the line only when parameters.default.local_pref differs from xml_default_local_pref.

FRR does not print bgp default local-preference 100 in its running configuration, because 100 is the default. frr-reload therefore treated the rendered line as missing and sent it again on every reload. FRR re-processes the routes of every peer whenever that command is applied, even with an unchanged value, so every commit made the router run all received routes through the import policy again (with soft-reconfiguration inbound) or ask every peer to send its whole table again. Not rendering the default value removes the repeated command; FRR's effective value stays 100.

Changing the value still reaches FRR: from 100 to another value the line is rendered and applied; from another value back to 100, frr-reload sends no bgp default local-preference <old>, which restores FRR's default.

Measured on a VyOS 1.5.1 VM with about 1.76M BGP paths (2 route servers, 2 transits, 80 IXP peers, soft-reconfiguration inbound), default local-pref 100 configured:

before after
maximum-prefix change on the IXP peer-group 139–142 s 16.5 s
its revert 134–140 s 9.9 s

On rolling (FRR 10.6.1) with one eBGP session, counted as route-refresh messages sent by the router per commit:

commit before after
unrelated (neighbor description set / delete) 3 / 3 0 / 0
100 → 200 2 2
200 → 100 4 1
100 → deleted / deleted → 100 0 / 3 0 / 0

FRR's local preference was correct after each step.

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)

https://vyos.dev/T9404

Related PR(s)

How to test / Smoketest result

test_bgp_107_default_local_pref_default_value in smoketest/scripts/cli/test_protocols_bgp.py reads the rendered /run/frr/config/vyos.frr.conf (FRR never prints 100, so the running configuration cannot show it): no bgp default local-preference line when local-pref is not configured or is 100; the line with 200, in the file and in FRR's running configuration; gone again after going back to 100; and the same for a VRF instance, where 200 appears only in the VRF block.

test_protocols_bgp.py  Ran 41 tests  OK

Checklist:

  • I have read the CONTRIBUTING document
  • I have linked this PR to one or more Phabricator Task(s)
  • I have run the components SMOKETESTS if applicable
  • I have thoroughly reviewed, understood, and tested the code contained in the PR, including any code produced by GenAI tools
  • My commit headlines contain a valid Task id
  • My change requires a change to the documentation
  • I have updated the documentation accordingly

@coderabbitai

coderabbitai Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository YAML (base), Central YAML (inherited), Organization UI (inherited)
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e1886b49-f550-4483-be79-3aa941187142
📥 Commits

Reviewing files that changed from the base of the PR and between 50b4d40 and df22720.

📒 Files selected for processing (4)
  • data/templates/frr/bgpd.frr.j2
  • interface-definitions/include/bgp/protocol-common-config.xml.i
  • python/vyos/frrender.py
  • smoketest/scripts/cli/test_protocols_bgp.py
🔗 Linked repositories identified

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

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 1 remain after this review.

📜 Recent review details
⏰ Context from checks skipped due to timeout. (9)
  • GitHub Check: build_iso
  • GitHub Check: darker-ruff-lint / darker-ruff-lint
  • GitHub Check: codeql-analysis-call / Analyze (python)
  • GitHub Check: codeql-analysis-call / Analyze (c-cpp)
  • GitHub Check: typos
  • GitHub Check: check-prs-conflict / Check PRs status: conflicts
  • GitHub Check: Mergify Merge Queue
  • GitHub Check: Mergify Merge Protections
  • GitHub Check: Summary
⚠️ CI failures not shown inline (2)

GitHub Actions: Python Lint (Darker + Ruff) / 0_darker-ruff-lint _ darker-ruff-lint.txt: bgp: T9404: do not render "bgp default local-preference" when the value is 100

Conclusion: failure

View job details

##[group]Run echo "### 🧪 Lint Results"
 �[36;1mecho "### 🧪 Lint Results"�[0m
 �[36;1mdarker_failed="1"�[0m
 �[36;1mgraylint_failed=""�[0m
 �[36;1m�[0m
 �[36;1mif [[ "$darker_failed" == "1" ]]; then�[0m
 �[36;1m  echo "- ❌ **Darker** check failed"�[0m
 �[36;1melse�[0m
 �[36;1m  echo "- ✅ **Darker** check passed"�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1mif [[ "$graylint_failed" == "1" ]]; then�[0m
 �[36;1m  echo "- ❌ **Graylint (ruff check)** failed"�[0m
 �[36;1melse�[0m
 �[36;1m  echo "- ✅ **Graylint (ruff check)** passed"�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1mif [[ "$darker_failed" == "1" || "$graylint_failed" == "1" ]]; then�[0m
 �[36;1m  echo "::error::One or more linters failed. See above for details."�[0m

GitHub Actions: Python Lint (Darker + Ruff) / darker-ruff-lint _ darker-ruff-lint: bgp: T9404: do not render "bgp default local-preference" when the value is 100

Conclusion: failure

View job details

##[group]Run echo "### 🧪 Lint Results"
 �[36;1mecho "### 🧪 Lint Results"�[0m
 �[36;1mdarker_failed="1"�[0m
 �[36;1mgraylint_failed=""�[0m
 �[36;1m�[0m
 �[36;1mif [[ "$darker_failed" == "1" ]]; then�[0m
 �[36;1m  echo "- ❌ **Darker** check failed"�[0m
 �[36;1melse�[0m
 �[36;1m  echo "- ✅ **Darker** check passed"�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1mif [[ "$graylint_failed" == "1" ]]; then�[0m
 �[36;1m  echo "- ❌ **Graylint (ruff check)** failed"�[0m
 �[36;1melse�[0m
 �[36;1m  echo "- ✅ **Graylint (ruff check)** passed"�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1mif [[ "$darker_failed" == "1" || "$graylint_failed" == "1" ]]; then�[0m
 �[36;1m  echo "::error::One or more linters failed. See above for details."�[0m
🧰 Additional context used
🔍 Remote MCP vyos.dev

vyos.dev

  • PREF-100 resolves to task T9404, whose title concerns repeated BGP route processing when local preference 100 is configured. The task is In progress, with priority Normal in the retrieved record. (Source: vyos.dev · maniphest-get)
  • The task says FRR omits local preference 100 from show running-config; frr-reload therefore resends the rendered line, and FRR reprocesses routes for every peer even when the value is unchanged. It describes this behavior on rolling and 1.5.x, and on 1.4.x for BGP configuration commits. (Source: vyos.dev · maniphest-get)
  • For a VyOS 1.5.1 VM running FRR 10.5.2 with about 1.76M paths, the task reports 98 seconds of bgpd CPU for one application of the line; an IXP peer-group maximum-prefix change took 139–142 seconds with the line rendered versus 16.5 seconds when omitted. (Source: vyos.dev · maniphest-get)
🔇 Additional comments (4)
interface-definitions/include/bgp/protocol-common-config.xml.i (1)

1430-1430: LGTM!

python/vyos/frrender.py (1)

300-305: LGTM!

Also applies to: 531-532

data/templates/frr/bgpd.frr.j2 (1)

622-623: LGTM!

smoketest/scripts/cli/test_protocols_bgp.py (1)

2129-2192: LGTM!


📝 Summary

Summary by CodeRabbit

  • Bug Fixes
    • BGP configurations now omit the default local preference of 100 when it is unset or explicitly configured, including within VRFs.
    • Non-default values, such as 200, are applied in the appropriate global or VRF configuration. Returning to 100 removes the override.

Walkthrough

The BGP XML default for local preference is 100. FRR rendering data includes this default for global BGP and VRFs. The template omits the directive for 100 and renders other configured values. Smoke tests cover both contexts.

Changes

BGP local preference

Layer / File(s) Summary
Render and verify local preference
interface-definitions/include/bgp/protocol-common-config.xml.i, python/vyos/frrender.py, data/templates/frr/bgpd.frr.j2, smoketest/scripts/cli/test_protocols_bgp.py
The XML defines 100 as the default. FRR rendering data passes the XML default for global BGP and VRFs. The template emits the directive only when the configured value differs from the default. The smoke test checks unset, default, non-default, and reset values in global and VRF configurations.

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to df227

No confirmed issue prevents merging. The added test covers the return to the default value, subject to normal test execution.

Security Architecture Review

Security architecture risk: 🔵 Low · up to df227

The change keeps the existing configuration interface and separates global and VRF settings. No introduced security issue was established. Remaining uncertainty concerns whether resetting or removing a non-default value reliably converges after an interrupted or failed reload.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The relevant policy scope is the configured global or named-VRF BGP instance on the router. A reconciliation error could affect routing preference within that instance; the inspected changes do not establish broader service or credential exposure.

Security Findings and Attack Paths

  • observed — The supplied public-entrypoint range is a unittest method under smoketest. It calls existing test helpers and reads configuration output; it is not a newly exposed production endpoint.

Trust Boundaries and Controls

  • observed — The existing numeric validation remains in the schema, and application continues through the existing reload test and reload commands. The new template condition does not introduce a new command executor or credential source.

Resilience and Maintainability Implications

  • inferred — The inspected commit daemon processes requests synchronously with one long-lived renderer, providing serialization within that process. Cross-process coordination and convergence after interruption remain unverified.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 2 files. (2 skipped: 2 … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title clearly identifies the BGP default local-preference change and the condition for omitting the command.
Description check ✅ Passed The description explains the change, its purpose, implementation, and test results. It is directly related to the changeset.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 2 files. (2 skipped: 2 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
✨ Simplify code
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@mergify mergify Bot added the rolling label Oct 6, 2026
@mergify mergify Bot assigned alexk37 Oct 6, 2026
@sever-sever
sever-sever requested a review from c-po October 6, 2026 14:27
sever-sever
sever-sever previously approved these changes Oct 6, 2026

@sever-sever sever-sever left a comment •

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.

Local-pref 100 is the default for FRR/BGP
Do not render/configure the default local-pref option if we have the configured local-pref 100

I wonder if we should catch and remove it from the Python config dictionary instead. Or remove the default value from XML

@sever-sever
sever-sever requested a review from sarthurdev October 6, 2026 14:38
@mergify

mergify Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

This pull request does not currently match the merge queue conditions, so it cannot be queued from here. The box comes back if it matches again.

@c-po c-po left a comment

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.

The PR itself catches a nice corner case - but the implementation can be improved.

The FRR default value should be added as proper XML default https://github.com/vyos/vyos-1x/blob/rolling/interface-definitions/include/bgp/protocol-common-config.xml.i#L1430

And then the proper XML default should be read-back in the Jina template for comparison, this will avoid multiple hardocded places of default 100

…ue is 100

FRR does not print "bgp default local-preference 100" in show
running-config because 100 is its default, so frr-reload never finds the
line in the running config and sends it again on every reload. The
command handler calls bgp_clear_star_soft_in() whether or not the value
changed, so every reload ran "clear bgp * soft in" (once in pass 0 and
twice in pass 1), a full inbound re-evaluation of every peer.

Add the default (100) to the XML definition of parameters default
local-pref. Templates cannot query the XML definition while rendering,
so frrender takes the value from the config defaults of "parameters
default" (conf.get_config_defaults) and adds it to the BGP dict as the
key xml_default_local_pref, for the global instance and for every VRF
instance. bgpd.frr.j2 renders the line only when the configured value
differs from xml_default_local_pref, so the value 100 is not hardcoded
in the template. A real value change (100 to 200, or 200 to 100) is
still sent: frr-reload sees the difference between the rendered file and
the running config.
@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown

CI integration ❌ failed!

Details

CI logs

  • CLI Smoketests ❌ failed
  • CLI Smoketests (interfaces only) ❌ failed
  • Config tests ❌ failed
  • RAID1 tests ❌ failed
  • CLI Smoketests VPP ⏭️ skipped
  • Config tests VPP ⏭️ skipped
  • TPM tests ⏭️ skipped

@alexk37
alexk37 requested review from c-po and sever-sever October 6, 2026 18:31
@alexk37

alexk37 commented Oct 6, 2026

Copy link
Copy Markdown
Contributor Author

@c-po Updated:

  • parameters default local-pref has <defaultValue>100</defaultValue> in protocol-common-config.xml.i.
  • frrender.py adds the default to the BGP dict as xml_default_local_pref, taken from conf.get_config_defaults(): in the global block after get_config_dict(), and for each VRF instance from the default_values it already fetches there, since every VRF is rendered from its own dict.
  • In the global block I ask get_config_defaults() only for parameters default, not recursively for the whole BGP node: the recursive call is not cached and recomputes all BGP defaults, which added about 1.1 s per commit (~7 %) on a config with 300 neighbors, 20 peer-groups and 4 VRFs. With the narrow call the commit time is the same as without the change.
  • bgpd.frr.j2 renders bgp default local-preference only when the value differs from xml_default_local_pref. No 100 in the template.
  • test_bgp_107 covers not configured, 100, 200, back to 100, and a VRF instance with 100 and 200.

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

Labels

Development

Successfully merging this pull request may close these issues.

3 participants