Skip to content
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

Network: Add OVS bridge information #15044

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

ghadi-rahme
Copy link

Adds extra functions to ovs.go to allow for the needed OVS bridge information to be read.
These are needed for GetNetworkState() to be able to retrieve OVS bridge information.

Fixes #14606

@ghadi-rahme ghadi-rahme force-pushed the adding-ovs-bridge-info branch 2 times, most recently from bf95f29 to ce0e829 Compare February 25, 2025 16:17
@markylaing markylaing changed the title Network: Add OVS bridge informatin Network: Add OVS bridge information Feb 26, 2025
@ghadi-rahme
Copy link
Author

Size increase justification

Functions were added to the openvswitch package, which itself depends on other packages. The openvswitch package was then imported into the network.go file in the resources package. This leads to an increase in the packages used by lxd-agent causing the size of the binary to increase.

@tomponline
Copy link
Member

Size increase justification

Functions were added to the openvswitch package, which itself depends on other packages. The openvswitch package was then imported into the network.go file in the resources package. This leads to an increase in the packages used by lxd-agent causing the size of the binary to increase.

IIRC we dont want lxd-agent needing to depend on ovs package

Copy link
Contributor

@markylaing markylaing left a comment

Choose a reason for hiding this comment

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

Overall I'm happy to add these functions to the openvswitch package but the parsing of ovs-appctl stp/show output could use some work. Ideally a format can be specified or ovs-vsctl can be used instead. Additionally, I think the best long-term solution for having lxd/resources as a dependency will be to refactor the package so that it does not import state.State.

// Default STP priority is 32768 (0x8000). This value will be used if the
// priority cannot be retrieved or if STP is disabled
func GetStpPriority(bridgeName string) (string, error) {
output, err := shared.RunCommand("ovs-appctl", "stp/show", bridgeName)
Copy link
Contributor

Choose a reason for hiding this comment

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

I think RunCommand is deprecated. Instead you can use RunCommandContext and pass a context.Context into this method.

// priority cannot be retrieved or if STP is disabled
func GetStpPriority(bridgeName string) (string, error) {
output, err := shared.RunCommand("ovs-appctl", "stp/show", bridgeName)
if err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please can you use errors.Is and/or errors.As for these kind of checks.

return "8000", nil
}
}
return "8000", err
Copy link
Contributor

Choose a reason for hiding this comment

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

You will always need a newline after a closing brace and before any subsequent statements.

return "8000", err
}
hexStp := fmt.Sprintf("%4X", decimalStp)
return hexStp, nil
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we return the STP as a hex string?

Copy link
Author

Choose a reason for hiding this comment

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

The reason is because the IEEE802.1D standard represents the STP priority in binary terms. Linux (and many switches) already uses the hexadecimal format to represent them but OVS defaults to showing the value in base 10. Also to represent a bridge ID which is a combination of the STP priority and the MAC address [1], the STP will need to be in hex format just like the MAC address is. In fact IEEE802.1D is the same standard that defines the MAC address as also being represented in binary format (hexadecimal is used on MAC addresses to simplify the representation, the same is done for STP priority).

[1] https://www.kernel.org/doc/Documentation/networking/bridge.rst

Bridge ID: The Bridge ID is composed of two components: Bridge Priority
     and the MAC address of the bridge. It uniquely identifies each bridge
     in the network. The Bridge ID is used to compare the priorities of
     different bridges.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks, can you add this reasoning to a comment?

Comment on lines 396 to 431
var stpPriority string
var bridgeSection bool
lines := strings.Split(output, "\n")

for _, line := range lines {
line = strings.TrimSpace(line)
if line == "Bridge ID:" {
bridgeSection = true
}
if bridgeSection {
if strings.HasPrefix(line, "stp-priority") {
sections := strings.Fields(line)
if len(sections) > 1 {
stpPriority = sections[1]
}
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Most ovs-* commands have options for formatting. Are there any options you can use here to simplify the output format? Or perhaps rather than calling ovs-appctl stp/show there are ovs-vsctl commands you can use to get the value directly from the database?

Copy link
Author

Choose a reason for hiding this comment

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

I have rewrote this function to make use of ovs-vsctl. I had avoided this approach before because the error codes returned when querying the DB for config variables do not differentiate between a bad variable name (not used) and an undefined variable.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks. Ultimately all of these functions will be migrated to use libovsdb. When that happens, MAAS will need to instantiate and use the openvswitch package differently. Even the import path will change.

stpOvsDelay = stpOvsDelay[:len(stpOvsDelay)-1]

stpFwDelay, err := strconv.ParseUint(stpOvsDelay, 10, 64)
return stpFwDelay * 1000, err
Copy link
Contributor

Choose a reason for hiding this comment

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

Why the multiplier? I'm guessing that the delay is reported by OVS in seconds and you would like it in milliseconds?

Copy link
Author

Choose a reason for hiding this comment

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

That is correct, the struct api.NetworkStateBridge takes the stpFwdDelay in ms, which is why I converted it.

	// Delay on port join (ms)
	// Example: 1500
	ForwardDelay uint64 `json:"forward_delay" yaml:"forward_delay"`


bridge := api.NetworkStateBridge{}

if !exists {
Copy link
Contributor

Choose a reason for hiding this comment

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

exists is a confusing variable name. Can you please call it something like isOVSBridge?

network.Bridge = &bridge

Copy link
Contributor

Choose a reason for hiding this comment

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

Rather than this large if/else block, can you please refactor to two functions: getOVSBridgeState and getNativeBridgeState?

@@ -5,12 +5,14 @@ package state
import (
"context"

clusterConfig "github.com/canonical/lxd/lxd/cluster/config"
Copy link
Contributor

Choose a reason for hiding this comment

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

We don't wan't to add anything to this file that isn't absolutely necessary for the LXD agent to function. I suggest if state.State is causing issues, refactor the resources package to not require it (e.g. only pass in the fields of state.State that are actually used).

Copy link
Author

Choose a reason for hiding this comment

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

I am not sure how feasible this is without splitting the openvswitch package in two packages.
The state.State dependency comes from importing openvswitch into the network.go file and openvswitch makes use of state.State.
The only other solution I could think off would be to move the functions in the ovs.go into their own separate package and import that into network.go, which I was already told not to do.
Let me know if you are aware of any other workarounds or how you would like me to proceed.

Copy link
Author

Choose a reason for hiding this comment

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

Also one extra note on the scenario of splitting the openvswitch package in two. If ovs.go were to be in a separate package from ovn.go, it would still need to import the openvswitch package and result in state.State still being used because functions such as OVNBridgeMappingAdd and OVNBridgeMappings make calls to functions present in ovn.go .
This means that these functions will need to be moved back to the openvswitch package resulting in ovs related tasks being split across two packages.... which is messy.

Copy link
Contributor

Choose a reason for hiding this comment

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

The only usage of state.State is where it is passed into openvswitch.NewOVN, where it access (*State).GlobalConfig. I have a PR to remove it and once merged you should be able to use the OVS utils with no issues.

@ghadi-rahme
Copy link
Author

Size increase justification

Functions were added to the openvswitch package, which itself depends on other packages. The openvswitch package was then imported into the network.go file in the resources package. This leads to an increase in the packages used by lxd-agent causing the size of the binary to increase.

IIRC we dont want lxd-agent needing to depend on ovs package

resources needs to access these function in order to retrieve the data from ovs. One solution I though off was to split the openvswitch package in two, but I was told not to do it (and I agree it does make things messy).

@markylaing
Copy link
Contributor

Once #15084 is merged and this is rebased I'll take another look.

tomponline added a commit that referenced this pull request Feb 28, 2025
State was imported only to get some config keys for setting up the OVN
command connection arguments. We should try to not import state where
possible, and only import the fields we need at the lowest level. This
helps not only with reducing the size of imports, but also for use of
packages externally, and also for unit testing.

This is required for using the OVS package externally. See #15044 and
#14606. cc @ghadi-rahme
Add the following OVS features:
  - Support for retrieving the STP priority
  - Generating Bridge ID
  - Retrieving the state of STP
  - Retrieving the STP forward delay
  - Checking if VLAN filtering is enabled
  - Retrieving the PVID

Signed-off-by: Ghadi Elie Rahme <[email protected]>
…tion

This allows OVS bridge information to be read by LXD. Other
applications such as MAAS make use of this function and require
the ability to retrieve OVS bridge information.

Fixes canonical#14606

Signed-off-by: Ghadi Elie Rahme <[email protected]>
@ghadi-rahme
Copy link
Author

I applied the requested changes. I also made the context a timeout context with a 5s duration for each exec function.
Let me know if the current context is good or if I should switch to shared.RunCommandLocal maybe.
Hopefully I didn't miss anything.

@ghadi-rahme ghadi-rahme force-pushed the adding-ovs-bridge-info branch from 71d20d5 to 530360c Compare March 1, 2025 20:14
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.

GetNetworkState can't detect bridge interfaces configured with OVS
3 participants