Skip to content

Add Cisco support#5622

Open
shpaulch wants to merge 1 commit into
openconfig:mainfrom
b4firex:shpaulch_gNMI-1.15-fix
Open

Add Cisco support#5622
shpaulch wants to merge 1 commit into
openconfig:mainfrom
b4firex:shpaulch_gNMI-1.15-fix

Conversation

@shpaulch

Copy link
Copy Markdown
Contributor

1) Breakout channel count type

  • breakout.numPhysicalChannels changed from *uint8 to uint8.
  • Simplifies handling of channel counts and avoids pointer dereference/assignment issues.
  • All call sites now use the direct value (data.numPhysicalChannels) rather than *data.numPhysicalChannels.

2) NumBreakouts calculation in containerOp.push

  • gp.NumBreakouts changed from:
    • ygot.Uint8(*data.numPhysicalChannels + 1)
  • to:
    • ygot.Uint8(data.numPhysicalChannels)
  • Removes the extra +1 offset and aligns breakout count with computed physical channels.
  • Device receives the exact computed breakout count.

3) Subport iteration update

  • Subport loop changed from pointer-based count:
    • for i := 0; i < int(*data.numPhysicalChannels); i++
  • to value-based count:
    • for i := 0; i < int(data.numPhysicalChannels); i++
  • Matches the struct type change and avoids pointer dereference.

4) Breakout candidate matching logic in addMissingConfigForContainerReplace

  • Matching changed from strict channel equality:
    • if hwp+"/"+channel == name { ... }
  • to prefix-based matching:
    • if strings.HasPrefix(name, hwp+"/") { ... }
  • Includes all breakout subport interfaces under a hardware port instead of relying on one strict channel value.
  • More complete breakout candidate detection, especially for multi-channel breakout groups.

5) Speed tracking support and counting logic

  • Added handling for additional speeds when building breakout candidates:
    • SPEED_800GB
    • SPEED_400GB
    • SPEED_100GB
    • SPEED_10GB
  • Reworked channel counting to increment per hardware-port occurrence:
    • numChannels := port[hwPort] + 1
    • port[hwPort] = numChannels
    • breakoutPortsMap[hwPort] = breakout{numPhysicalChannels: numChannels, breakoutSpeed: trackspeed}
  • Ensures 400G/800G interfaces are included and channel count reflects discovered breakout members.

6) Static route recurse handling for interface next-hop

  • Added helper setStaticRouteRecurseForPhysicalInterface(config *oc.Root).
  • The helper walks network-instance -> protocol -> static -> next-hop and sets:
    • nh.Recurse = ygot.Bool(false)
    • only when next-hop has interface-ref and isPhysicalInterface(...) returns true.
  • Goal: avoid DUT rejection for interface-based next-hop when recurse is true.

7) Physical interface detection logic

  • Added helper isPhysicalInterface(config *oc.Root, ifName string) bool.
  • Detection is intentionally simple and strict:
    • lookup interface from config.GetInterface(ifName)
    • treat as physical only when type is one of:
      • ethernetCsmacd
      • ieee8023adLag
  • Removed prefix-based name heuristics from earlier iterations to keep behavior deterministic.

8) Push path integration points

  • Hooked recurse normalization into all three push strategies before issuing SetRequest:
    • rootOp.push
    • containerOp.push
    • itemOp.push
  • Each path now calls the shared helper:
    • setStaticRouteRecurseForPhysicalInterface(config)

9) Breakout mode struct enhancement

  • Added parentInterfaceName field to the breakout struct:
    • parentInterfaceName string // e.g., "FourHundredGigE0/0/0/22"
  • Tracks the actual Cisco interface name prefix (e.g., FourHundredGigE, HundredGigE) separately from the normalized hardware port identifier.
  • Resolves issue where child port names were constructed using normalized names (e.g., Port0/0/0/22/0) instead of actual interface names (e.g., FourHundredGigE0/0/0/22/0).

10) Breakout port discovery enhancement

  • Updated addMissingConfigForContainerReplace to extract and store parent interface name:
    • When discovering breakout child interfaces, the actual parent interface name is extracted by removing the last /index suffix.
    • Example: from interface FourHundredGigE0/0/0/22/0, extract FourHundredGigE0/0/0/22.
    • Stored in breakout.parentInterfaceName for later use in child port name construction.

11) Breakout port online verification

  • Added checkBreakoutPortsOnline helper function called after ContainerOp batch.Set() for Cisco devices.
  • Two-stage verification:
    1. Breakout mode state check: Uses gnmi.Lookup to check if Component/Port/BreakoutMode/Group/0/num-breakouts state is available.
      • Non-blocking: logs result if state exists, does not fail on missing state.
      • Handles devices that may not populate this telemetry leaf.
    2. Child interface oper-status check: Uses gnmi.Await with 3-minute timeout to verify each child interface reaches UP.
      • Constructs correct child port names using parentInterfaceName (e.g., FourHundredGigE0/0/0/22/0).
      • Ensures breakout interfaces are operationally online before test completion.

@shpaulch shpaulch requested a review from a team as a code owner June 25, 2026 23:04
@OpenConfigBot

Copy link
Copy Markdown

Pull Request Functional Test Report for #5622 / e29905e

Virtual Devices

Device Test Test Documentation Job Raw Log
Arista cEOS status
gNMI-1.15: Set Requests
Cisco 8000E status
gNMI-1.15: Set Requests
Cisco XRd status
gNMI-1.15: Set Requests
Juniper ncPTX status
gNMI-1.15: Set Requests
Nokia SR Linux status
gNMI-1.15: Set Requests
Openconfig Lemming status
gNMI-1.15: Set Requests

Hardware Devices

Device Test Test Documentation Raw Log
Arista 7808 status
gNMI-1.15: Set Requests
Cisco 8808 status
gNMI-1.15: Set Requests
Juniper PTX10008 status
gNMI-1.15: Set Requests
Nokia 7250 IXR-10e status
gNMI-1.15: Set Requests

Help

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the testing framework's compatibility with Cisco hardware by implementing robust breakout port management and interface-specific configuration adjustments. It introduces logic to correctly identify and verify breakout interfaces, improves static route handling for physical interfaces, and refines configuration sanitization to ensure cleaner test environments.

Highlights

  • Cisco Device Support: Introduced comprehensive support for Cisco devices, including specific handling for breakout port configuration and operational status verification.
  • Breakout Logic Enhancements: Refactored breakout channel counting and matching logic to use prefix-based detection, supporting multi-channel breakout groups and additional speeds (800G, 400G, 100G, 10G).
  • Static Route Recursion: Added a normalization helper to disable recursion for physical interface next-hops, preventing potential configuration rejection on Cisco devices.
  • Configuration Cleanup: Updated internal test utilities to clear IPv4 and IPv6 neighbor entries during device configuration retrieval.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces support for 400GB and 800GB breakout speeds, adds static route recursion disabling for physical interfaces on Cisco devices, and implements a check to ensure breakout ports come online. It also clears IPv4 and IPv6 neighbors in fptest configuration. Feedback on the changes highlights a bug where the shared trackspeed variable is overwritten across loop iterations, potentially assigning incorrect speeds, along with a potential nil pointer dereference. Additionally, it is recommended to rename childInterfaceName to parentInterfaceName to accurately reflect its contents.

Comment on lines 1173 to +1187
var speed oc.E_IfEthernet_ETHERNET_SPEED

_, keyExists := breakoutPortsMap[intf.GetHardwarePort()]
if !keyExists && speed == oc.IfEthernet_ETHERNET_SPEED_UNSET {
if intf.GetEthernet().PortSpeed.String() == "SPEED_100GB" {
if intf.GetEthernet().PortSpeed.String() == "SPEED_800GB" {
trackspeed = oc.IfEthernet_ETHERNET_SPEED_SPEED_800GB
} else if intf.GetEthernet().PortSpeed.String() == "SPEED_400GB" {
trackspeed = oc.IfEthernet_ETHERNET_SPEED_SPEED_400GB
} else if intf.GetEthernet().PortSpeed.String() == "SPEED_100GB" {
trackspeed = oc.IfEthernet_ETHERNET_SPEED_SPEED_100GB
} else if intf.GetEthernet().PortSpeed.String() == "SPEED_10GB" {
trackspeed = oc.IfEthernet_ETHERNET_SPEED_SPEED_10GB
}
}
hwPort := intf.GetHardwarePort()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The trackspeed variable is declared outside the loop (at line 1144). When iterating over interfaces, if a new hardware port is encountered (!keyExists), trackspeed is updated to that port's speed. However, because breakoutPortsMap[hwPort] is updated on every iteration (including when keyExists is true) using the shared trackspeed variable, if the loop processes an interface for a different hardware port in between, the trackspeed variable will have been overwritten with the other port's speed. This results in incorrect breakout speeds being assigned to previously discovered ports.

Additionally, calling intf.GetEthernet().PortSpeed without checking if intf.GetEthernet() is nil can lead to a nil pointer dereference panic.

We can resolve both issues by retrieving the existing speed from the map if it exists, or safely checking and determining it from the current interface if it does not.

Suggested change
var speed oc.E_IfEthernet_ETHERNET_SPEED
_, keyExists := breakoutPortsMap[intf.GetHardwarePort()]
if !keyExists && speed == oc.IfEthernet_ETHERNET_SPEED_UNSET {
if intf.GetEthernet().PortSpeed.String() == "SPEED_100GB" {
if intf.GetEthernet().PortSpeed.String() == "SPEED_800GB" {
trackspeed = oc.IfEthernet_ETHERNET_SPEED_SPEED_800GB
} else if intf.GetEthernet().PortSpeed.String() == "SPEED_400GB" {
trackspeed = oc.IfEthernet_ETHERNET_SPEED_SPEED_400GB
} else if intf.GetEthernet().PortSpeed.String() == "SPEED_100GB" {
trackspeed = oc.IfEthernet_ETHERNET_SPEED_SPEED_100GB
} else if intf.GetEthernet().PortSpeed.String() == "SPEED_10GB" {
trackspeed = oc.IfEthernet_ETHERNET_SPEED_SPEED_10GB
}
}
hwPort := intf.GetHardwarePort()
hwPort := intf.GetHardwarePort()
if existing, ok := breakoutPortsMap[hwPort]; ok {
trackspeed = existing.breakoutSpeed
} else if intf.GetEthernet() != nil {
switch intf.GetEthernet().PortSpeed.String() {
case "SPEED_800GB":
trackspeed = oc.IfEthernet_ETHERNET_SPEED_SPEED_800GB
case "SPEED_400GB":
trackspeed = oc.IfEthernet_ETHERNET_SPEED_SPEED_400GB
case "SPEED_100GB":
trackspeed = oc.IfEthernet_ETHERNET_SPEED_SPEED_100GB
case "SPEED_10GB":
trackspeed = oc.IfEthernet_ETHERNET_SPEED_SPEED_10GB
}
}

breakoutSpeed oc.E_IfEthernet_ETHERNET_SPEED
numPhysicalChannels *uint8
numPhysicalChannels uint8
childInterfaceName string

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The field childInterfaceName actually stores the parent interface name (e.g., FourHundredGigE0/0/0/22 after stripping the breakout index suffix). Naming it childInterfaceName is highly misleading. Please rename it to parentInterfaceName to improve code readability and maintainability.

Remember to also update its usages at lines 1195 and 1255.

Suggested change
childInterfaceName string
parentInterfaceName string

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.

2 participants