Skip to content

Conversation

@yrkurapati
Copy link

• Adds new DumpPortMappings method to OpenFlowService for retrieving port mappings
• Parses ovs-ofctl show output to extract ofport numbers and MAC addresses
• Filters results to include only tap interfaces and LOCAL port
• Returns map of interface names to PortMapping structs containing ofport and MAC
• Includes comprehensive test coverage with 5 test cases

@yrkurapati yrkurapati force-pushed the GATEWAYS-4422-implement-go-openvswitch-api-for-ofport-interface-mapping branch from 24eb418 to 7a2a409 Compare November 14, 2025 23:11
@yrkurapati yrkurapati marked this pull request as ready for review November 17, 2025 18:41
ovs/openflow.go Outdated
// PortMapping contains the ofport number and MAC address for an interface.
type PortMapping struct {
// OfPort specifies the OpenFlow port number.
OfPort int
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggest ofPort to maintain consistency. Ditto for macAddress

Copy link
Author

Choose a reason for hiding this comment

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

Sure

ovs/openflow.go Outdated
mappings := make(map[string]*PortMapping)
scanner := bufio.NewScanner(bytes.NewReader(out))

for scanner.Scan() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you follow semantics similar to parseEach() and define your own parseEachPort() or something like that, instead of regexp?

showPrefix =[]byte("OFPT_FEATURES_REPLY")

ovs/openflow.go Outdated
"errors"
"fmt"
"io"
"regexp"
Copy link
Contributor

Choose a reason for hiding this comment

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

These may not be needed if you switch to parseEach semantics.

ovs/openflow.go Outdated
// portMappingPattern matches port lines from 'ovs-ofctl show' output.
// Pattern matches: " 7(interface1): addr:fe:4f:76:09:88:2b"
// or " 65534(LOCAL): addr:fe:4f:76:09:88:2b"
portMappingPattern = regexp.MustCompile(`^\s*(\d+)\(([^)]+)\):\s*addr:([a-fA-F0-9:]+)`)
Copy link
Contributor

Choose a reason for hiding this comment

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

Should be able to remove this.

}
}

func TestClientOpenFlowDumpPortMappingsOnlyLOCAL(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of a test case specifically for LOCAL, this should be a generic one to test for a specified interface index


// DumpPortMappings retrieves port mappings (ofport and MAC address) for all
// interfaces from the specified bridge. The output is parsed from 'ovs-ofctl show' command.
func (o *OpenFlowService) DumpPortMappings(bridge string) (map[string]*PortMapping, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I would like to add one variant of this function in addition to this function. It should also be possible using another function to return the PortMapping for a single interface index provided as an input. Also, see my comment in the test section - you can modify the test accordingly.

Copy link
Author

Choose a reason for hiding this comment

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

Sure.

- Added JSON tags (ofPort, macAddress) to PortMapping struct for consistency
- Refactored to use parseEachPort() semantics instead of regex, following parseEach() pattern
- Removed unused regexp import and portMappingPattern variable
- Updated test to be generic table-driven instead of LOCAL-specific
- Added DumpPortMapping (singular) function for single interface lookup
- Fixed some error handling consistency, add MAC/ofport validation, and eliminate code duplication
ovs/openflow.go Outdated
// Find the opening parenthesis
openParen := strings.IndexByte(s, '(')
if openParen == -1 {
return fmt.Errorf("invalid port mapping format: missing opening parenthesis")
Copy link
Contributor

Choose a reason for hiding this comment

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

It should be sufficient to indicate invalid format for both open & close paren error cases without being too specific.

Copy link
Author

Choose a reason for hiding this comment

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

I will simplify the error handling - any malformed line would return the generic ‘invalid port mapping format’ message, so we don’t over-specify which character is missing.

ovs/openflow.go Outdated
}

// Find the closing parenthesis
closeParen := strings.IndexByte(s[openParen:], ')')
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not look up in s, like the openParen case? Then you won't have to add the index.

Copy link
Author

@yrkurapati yrkurapati Nov 18, 2025

Choose a reason for hiding this comment

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

Good point - that would make both ( and ) searched directly in the full string, so thers's no extra index math anymore

ovs/openflow.go Outdated

// Find "addr:" after the closing parenthesis
addrPrefix := ": addr:"
addrIdx := strings.Index(s[closeParen:], addrPrefix)
Copy link
Contributor

Choose a reason for hiding this comment

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

Similar comment as above

ovs/openflow.go Outdated
mappings := make(map[string]*PortMapping)
err = parseEachPort(out, showPrefix, func(line []byte) error {
// Check if line matches port format: " 7(interface1): addr:..."
if !bytes.Contains(line, []byte("(")) || !bytes.Contains(line, []byte("): addr:")) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are all these checks needed? L445 call to UnmarshalText will do all this anyway, no?

Copy link
Author

@yrkurapati yrkurapati Nov 18, 2025

Choose a reason for hiding this comment

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

Noted. I will plan to remove them.
After UnmarshalText succeeds, we slice s[openParen+1 : closeParen] without rechecking openParen/closeParen (openflow.go, lines 432-435).
Is it acceptable to trust UnmarshalText (since it already checks parentheses), or do you want a defensive guard to skip unexpected lines?

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think an additional check is necessary. This is a pretty custom function meant only for this purpose. There won't be any other callers for this variant of UnmarshalText. UnmarshalText is supposed to do the heavy lifting including parsing etc and returning the parsed values.

Copy link
Author

Choose a reason for hiding this comment

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

Understood, I will remove the redundant checks.

ovs/openflow.go Outdated
// PortMapping contains the ofport number and MAC address for an interface.
type PortMapping struct {
// OfPort specifies the OpenFlow port number.
OfPort int `json:"ofPort"`
Copy link
Contributor

Choose a reason for hiding this comment

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

Why json encodings? I was suggesting changing the var name from OfPort to ofPort.

Copy link
Author

Choose a reason for hiding this comment

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

I initially interpreted your “ofPort/macAddress” note as wanting camelCase JSON keys, so I added tags instead of renaming the exported fields. Will make the suggested changes.

Copy link
Author

Choose a reason for hiding this comment

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

Current code uses ofPort/macAddress (lowercase) in PortMapping (openflow.go, lines 75-82). This breaks external callers who expect PortMapping.OfPort.

Could you confirm if we were supposed to only rename the exported fields to OfPort/MACAddress, or actually hide them? Should we revert to exported fields and have camelCase in comments/docs instead?

Copy link
Contributor

Choose a reason for hiding this comment

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

Was suggesting a similar case as the type structures at L62 and L70. Since this is a new type definition, I am curious which external callers (other than test) it breaks.
If test isn't able to work with this case, we can ignore this comment.

Copy link
Author

Choose a reason for hiding this comment

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

Got it.

- Removeed JSON tags and use unexported fields (ofPort, macAddress) following L62-66 pattern
- Removed redundant format checks in callback (UnmarshalText handles all validation)
- Fixed string slicing to search full string for closeParen and addrIdx
- Consolidated error messages to generic 'invalid port mapping format'
@yrkurapati yrkurapati requested a review from do-msingh November 18, 2025 01:56
return nil
}

// Extract interface name from the validated line for map key
Copy link
Contributor

Choose a reason for hiding this comment

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

You can move parsing of interface name to UnmarshalText as well. You may need to declare another structure to accomplish this e.g.

type PortAttr struct {
	// ofPort specifies the OpenFlow port number.
	ofPort int

	// macAddress specifies the MAC address of the interface.
	macAddress string
}
type PortMapping struct {
	portAttr PortAttr
	ifName string
}

Copy link
Author

Choose a reason for hiding this comment

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

Noted.

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