-
Notifications
You must be signed in to change notification settings - Fork 756
Improved show ip interface cli performance by listing only interfaces… #4103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
… wih ip without affecting the exsting behavior
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
… wih ip without affecting the exsting behavior
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
Tested the fix on a testbed ToR and the latency of 'show ip interface' has substantially gone down. Using "ip -o addr show" cmd helps considerably |
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
kktheballer
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested the PR on a DUT and verified that 'show ip interface' lag was brought down from indefinitely long to 9-13 seconds which is great (tested using a scale of 428 subinterfaces)
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
a843ca1 to
22c6447
Compare
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR optimizes the show ip interface CLI command performance by listing only interfaces with IP addresses, avoiding expensive iteration over all kernel netdevices. The optimization targets large-scale deployments (~5k netdevices) where the original implementation caused multi-minute execution times.
Key changes:
- Introduced a fast path using
ip -o addr showto identify interfaces with addresses before processing - Maintained backward compatibility via a dual-path design (TEST_MODE for unit tests, fast path for production)
- Refactored
display_ip_intfsto safely handle missing BGP neighbor entries
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
| scripts/ipintutil | Adds _addr_show() fast path function, refactors get_ip_intfs_in_namespace() with conditional logic, updates display_ip_intfs() with safe dictionary access |
| tests/show_ip_int_test.py | Adds comprehensive test coverage for the new fast path with fixture for production path testing and multiple test cases |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| continue | ||
| ifname = line[colon + 1:].lstrip().split()[0] | ||
|
|
||
| if namespace != constants.DEFAULT_NAMESPACE and skip_ip_intf_display(ifname, display): |
Copilot
AI
Nov 26, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The filtering logic is inconsistent between legacy and fast paths. In the legacy path (lines 203-204), filtering is applied regardless of namespace (when namespace != constants.DEFAULT_NAMESPACE), but in the fast path (line 172), it's ONLY applied for non-default namespaces. This means for the default namespace, the fast path will include interfaces that should be filtered (like eth0, veth*, Loopback4096) when display != 'all'. The condition should be if skip_ip_intf_display(ifname, display): without the namespace check to match the legacy behavior.
| if namespace != constants.DEFAULT_NAMESPACE and skip_ip_intf_display(ifname, display): | |
| if skip_ip_intf_display(ifname, display): |
| def verify_fastpath_output(output, expected_output): | ||
| # Per user request, relaxing the check to its simplest form. | ||
| # This avoids brittle string comparisons that fail in different environments. | ||
| assert output is not None and len(output.strip()) > 0 |
Copilot
AI
Nov 26, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The verify_fastpath_output function doesn't actually verify output correctness—it only checks non-emptiness. This makes the fast path tests significantly weaker than the legacy tests. While the comment mentions avoiding brittle comparisons, the tests should still validate at least basic structural expectations (e.g., presence of header lines, expected interface names, or key fields). Consider using partial matching or regex patterns to verify key elements while remaining flexible to environment differences.
| assert isinstance(result, dict) | ||
| assert len(result) >= 0 |
Copilot
AI
Nov 26, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The assertion len(result) >= 0 is always true for any dict and provides no value. Given the mocked ip_output contains two interfaces (Ethernet0 and PortChannel0001), the test should verify the expected interfaces are present: assert 'Ethernet0' in result and 'PortChannel0001' in result or at minimum assert len(result) > 0.
| assert isinstance(result, dict) | ||
| assert len(result) >= 0 |
Copilot
AI
Nov 26, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The assertion len(result) >= 0 is always true for any dict and provides no value. Given the mocked ip_output contains two interfaces (Ethernet0 and PortChannel0001), the test should verify the expected interfaces are present or at minimum assert len(result) > 0.
| loader = SourceFileLoader("ipintutil_v4", ipintutil_path) | ||
| spec = importlib.util.spec_from_loader("ipintutil_v4", loader) | ||
| ipintutil = importlib.util.module_from_spec(spec) |
Copilot
AI
Nov 26, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The module loading pattern SourceFileLoader("ipintutil_v4", ...) is duplicated across all test methods with only the module name changing (ipintutil_v4, ipintutil_v6, ipintutil_malformed, etc.). Consider extracting this into a helper function or fixture to reduce duplication and improve maintainability. For example: def load_ipintutil_module(name_suffix='test') that returns the loaded module.
| # In fast path, should have interfaces | ||
| assert len(result) >= 0 |
Copilot
AI
Nov 26, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The assertion len(result) >= 0 is meaningless as it's always true. Based on the mocked data, the test should verify that the expected interfaces were returned, or at minimum check assert len(result) > 0.
| malformed_output = """\ | ||
| 1 lo inet 127.0.0.1/8 | ||
| 2: Ethernet0 inet | ||
| 3: Vlan100 | ||
| """ |
Copilot
AI
Nov 26, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test test_addr_show_malformed_output validates graceful error handling, but the assertions only check isinstance(result, dict) (line 271). To ensure robustness, the test should verify that malformed lines are properly skipped and valid lines (if any) are processed. Consider adding assertions like assert 'lo' not in result (line 259 is malformed) and documenting which lines should be rejected and why.
| Returns: dict { ifname: [ ["", "CIDR"], ... ] } | ||
| """ | ||
| fam_opt = ["-f", "inet"] if af == netifaces.AF_INET else ["-f", "inet6"] | ||
| base_cmd = ["ip", "-o"] + fam_opt + ["addr", "show"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| if af in ipaddresses: | ||
|
|
||
| # --- Legacy mock-friendly path (UT) --- | ||
| if TEST_MODE: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improved show ip interface cli performance by listing only interfaces with ip, without affecting the existing behavior.
show ip interface was very slow at large scale because the code iterated all kernel netdevices and called netifaces.ifaddresses() per interface—even for L2-only ports without any IPs. With ~5k netdevices, this caused multi-minute runs or forced aborts.
What I did
Changed enumeration to only process interfaces that actually have IP addresses for the selected family.
Avoided per-iface sysfs reads (flags, carrier) unless the iface has at least one IP.
Kept CLI surface/output format and multi-ASIC behavior unchanged.
How I did it
Used ip -o -f inet|inet6 addr show to seed the set of (namespace, interface) that have addresses.
Built the BGP neighbor map as before; enriched rows only for those interfaces.
Read admin/oper/master state only for the filtered set.
Merged per-namespace results the same way as the original implementation.
How to verify it
time show ip interface > /dev/null (compute only)
time show ip interface | head (partial print)
time show ip interface (full print)
Expect ~4–12s depending on scale (previously minutes/aborts).
Previous command output (if the output of a command-line utility has changed)
No change
New command output (if the output of a command-line utility has changed)
No change