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

Adding the support of Linux bridge interface for VM setup #158

Merged
merged 3 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,40 @@ If the two above are in place, users can orhestrate k8s-netperf to launch VMs by

`k8s-netperf --vm`

### Using a linux bridge interface
When using `--bridge`, a NetworkAttachmentDefinition defining a bridge interface is attached to the VMs and is used for the test. It requires the name of the bridge as it is defined in the NetworkNodeConfigurationPolicy, NMstate operator is required. For example:
```yaml
apiVersion: nmstate.io/v1alpha1
kind: NodeNetworkConfigurationPolicy
metadata:
name: br0-eth1
spec:
desiredState:
interfaces:
- name: br0
description: Linux bridge with eno2 as a port
type: linux-bridge
state: up
ipv4:
dhcp: true
enabled: true
bridge:
options:
stp:
enabled: false
port:
- name: eno2
```
Then you can launch a test using the bridge interface:
```
./bin/amd64/k8s-netperf --vm --bridge br0
```
By default, it will read the `bridgeNetwork.json` file from the git repository. If the default IP addresses (10.10.10.12/24 and 10.10.10.14/24) are not available for your setup, it is possible to change it by passing a JSON file as a parameter with `--bridgeNetwork`, like follow:
```
k8s-netperf --vm --bridge br0 --bridgeNetwork /path/to/my/bridgeConfig.json

```

### Config file
#### Config File v2
The v2 config file will be executed in the order the tests are presented in the config file.
Expand Down
4 changes: 4 additions & 0 deletions bridgeNetwork.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"bridgeServerNetwork": "10.10.10.12/24",
"bridgeClientNetwork": "10.10.10.14/24"
}
95 changes: 74 additions & 21 deletions cmd/k8s-netperf/k8s-netperf.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package main

import (
"context"
encodeJson "encoding/json"
"fmt"
"io"
"os"
"regexp"
"strings"
Expand Down Expand Up @@ -33,27 +35,29 @@ const index = "k8s-netperf"
const retry = 3

var (
cfgfile string
nl bool
clean bool
netperf bool
iperf3 bool
uperf bool
udn bool
acrossAZ bool
full bool
vm bool
vmimage string
debug bool
promURL string
id string
searchURL string
showMetrics bool
tcpt float64
json bool
version bool
csvArchive bool
searchIndex string
cfgfile string
nl bool
clean bool
netperf bool
iperf3 bool
uperf bool
udn bool
acrossAZ bool
full bool
vm bool
vmimage string
debug bool
bridge string
bridgeNetwork string
promURL string
id string
searchURL string
showMetrics bool
tcpt float64
json bool
version bool
csvArchive bool
searchIndex string
)

var rootCmd = &cobra.Command{
Expand Down Expand Up @@ -184,6 +188,16 @@ var rootCmd = &cobra.Command{
log.Error(err)
}
s.KClient = kclient
if len(bridge) > 0 {
err := k8s.DeployNADBridge(s.DClient, bridge)
if err != nil {
log.Error(err)
}
s.BridgeServerNetwork, s.BridgeClientNetwork, err = parseNetworkConfig(bridgeNetwork)
if err != nil {
log.Error(err)
}
}
}

// Build the SUT (Deployments)
Expand Down Expand Up @@ -395,6 +409,40 @@ func cleanup(client *kubernetes.Clientset) {
}
}

// Function to parse the JSON from a file and return the IP parts (before '/')
func parseNetworkConfig(jsonFile string) (string, string, error) {

// Open the JSON file
file, err := os.Open(jsonFile)
if err != nil {
return "", "", fmt.Errorf("error opening file: %v", err)
}
defer file.Close()

// Read the file contents
log.Debugf("Reading BridgeNetwork configuration from JSON file: %s ", jsonFile)
content, err := io.ReadAll(file)
if err != nil {
return "", "", fmt.Errorf("error reading file: %v", err)
}

// Create an instance of the struct
var netConfig config.BridgeNetworkConfig

// Unmarshal the JSON string into the struct
err = encodeJson.Unmarshal(content, &netConfig)
if err != nil {
return "", "", fmt.Errorf("error parsing JSON: %v", err)
}

// Extract the IP parts (before '/')
serverIP := netConfig.BridgeServerNetwork
clientIP := netConfig.BridgeClientNetwork

// Return the extracted IPs
return serverIP, clientIP, nil
}

// executeWorkload executes the workload and returns the result data.
func executeWorkload(nc config.Config,
s config.PerfScenarios,
Expand All @@ -417,6 +465,9 @@ func executeWorkload(nc config.Config,
if err != nil {
log.Fatal(err)
}
//when using a bridge
} else if s.BridgeServerNetwork != "" {
serverIP = strings.Split(s.BridgeServerNetwork, "/")[0]
} else {
if hostNet {
serverIP = s.ServerHost.Items[0].Status.PodIP
Expand Down Expand Up @@ -516,6 +567,8 @@ func main() {
rootCmd.Flags().BoolVar(&full, "all", false, "Run all tests scenarios - hostNet and podNetwork (if possible)")
rootCmd.Flags().BoolVar(&debug, "debug", false, "Enable debug log")
rootCmd.Flags().BoolVar(&udn, "udn", false, "Create and use a UDN called 'udn-l2-primary' as primary network.")
rootCmd.Flags().StringVar(&bridge, "bridge", "", "Name of the NNCP to be used for creating bridge interface - VM only.")
rootCmd.Flags().StringVar(&bridgeNetwork, "bridgeNetwork", "bridgeNetwork.json", "Json file for the network defined by the bridge interface - bridge should be enabled")
rootCmd.Flags().StringVar(&promURL, "prom", "", "Prometheus URL")
rootCmd.Flags().StringVar(&id, "uuid", "", "User provided UUID")
rootCmd.Flags().StringVar(&searchURL, "search", "", "OpenSearch URL, if you have auth, pass in the format of https://user:pass@url:port")
Expand Down
54 changes: 31 additions & 23 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,29 +32,37 @@ type Config struct {

// PerfScenarios describes the different scenarios
type PerfScenarios struct {
NodeLocal bool
AcrossAZ bool
HostNetwork bool
Configs []Config
VM bool
VMImage string
VMHost string
Udn bool
ServerNodeInfo metrics.NodeInfo
ClientNodeInfo metrics.NodeInfo
Client apiv1.PodList
Server apiv1.PodList
ClientAcross apiv1.PodList
ClientHost apiv1.PodList
ServerHost apiv1.PodList
NetperfService *apiv1.Service
IperfService *apiv1.Service
UperfService *apiv1.Service
RestConfig rest.Config
ClientSet *kubernetes.Clientset
KClient *kubevirtv1.KubevirtV1Client
DClient *dynamic.DynamicClient
SSHClient *goph.Client
NodeLocal bool
AcrossAZ bool
HostNetwork bool
Configs []Config
VM bool
VMImage string
VMHost string
Udn bool
BridgeServerNetwork string

Choose a reason for hiding this comment

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

BridgeServerAddress ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I prefer Network, as it include the subnet mask, like '10.10.10.12/24'

BridgeClientNetwork string
ServerNodeInfo metrics.NodeInfo
ClientNodeInfo metrics.NodeInfo
Client apiv1.PodList
Server apiv1.PodList
ClientAcross apiv1.PodList
ClientHost apiv1.PodList
ServerHost apiv1.PodList
NetperfService *apiv1.Service
IperfService *apiv1.Service
UperfService *apiv1.Service
RestConfig rest.Config
ClientSet *kubernetes.Clientset
KClient *kubevirtv1.KubevirtV1Client
DClient *dynamic.DynamicClient
SSHClient *goph.Client
}

// struct for bridge options
type BridgeNetworkConfig struct {
BridgeServerNetwork string `json:"bridgeServerNetwork"`
BridgeClientNetwork string `json:"bridgeClientNetwork"`
}

// Tests we will support in k8s-netperf
Expand Down
34 changes: 32 additions & 2 deletions pkg/k8s/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,36 @@ func DeployL2Udn(dynamicClient *dynamic.DynamicClient) error {
return nil
}

// Create a NetworkAttachcmentDefinition object for a bridge connection
func DeployNADBridge(dyn *dynamic.DynamicClient, bridgeName string) error {
nadBridge := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "k8s.cni.cncf.io/v1",
"kind": "NetworkAttachmentDefinition",
"metadata": map[string]interface{}{
"name": "br-netperf",
"namespace": "netperf",
"annotations": map[string]interface{}{
"k8s.v1.cni.cncf.io/resourceName": "bridge.network.kubevirt.io/" + bridgeName,
},
},
"spec": map[string]interface{}{
"config": `{"cniVersion": "0.3.1", "type": "bridge", "name": "br-netperf", "bridge": "` + bridgeName + `"}`,
},
},
}
gvr := schema.GroupVersionResource{
Group: "k8s.cni.cncf.io",
Version: "v1",
Resource: "network-attachment-definitions",
}
_, err := dyn.Resource(gvr).Namespace(namespace).Create(context.TODO(), nadBridge, metav1.CreateOptions{})
if err != nil {
return err
}
return nil
}

// BuildSUT Build the k8s env to run network performance tests
func BuildSUT(client *kubernetes.Clientset, s *config.PerfScenarios) error {
var netperfDataPorts []int32
Expand Down Expand Up @@ -557,7 +587,7 @@ func ExtractUdnIp(s config.PerfScenarios) (string, error) {

// launchServerVM will create the ServerVM with the specific node and pod affinity.
func launchServerVM(perf *config.PerfScenarios, name string, podAff *corev1.PodAntiAffinity, nodeAff *corev1.NodeAffinity) error {
_, err := CreateVMServer(perf.KClient, serverRole, serverRole, *podAff, *nodeAff, perf.VMImage)
_, err := CreateVMServer(perf.KClient, serverRole, serverRole, *podAff, *nodeAff, perf.VMImage, perf.BridgeServerNetwork)
if err != nil {
return err
}
Expand All @@ -582,7 +612,7 @@ func launchServerVM(perf *config.PerfScenarios, name string, podAff *corev1.PodA

// launchClientVM will create the ClientVM with the specific node and pod affinity.
func launchClientVM(perf *config.PerfScenarios, name string, podAff *corev1.PodAntiAffinity, nodeAff *corev1.NodeAffinity) error {
host, err := CreateVMClient(perf.KClient, perf.ClientSet, perf.DClient, name, podAff, nodeAff, perf.VMImage)
host, err := CreateVMClient(perf.KClient, perf.ClientSet, perf.DClient, name, podAff, nodeAff, perf.VMImage, perf.BridgeClientNetwork)
if err != nil {
return err
}
Expand Down
Loading
Loading