From ef054e324e8e03272b3362c0758eace3648a8dae Mon Sep 17 00:00:00 2001 From: Charles CAPORALI Date: Thu, 31 Oct 2024 17:37:42 +0100 Subject: [PATCH] Adding support of UDN usage Using the UDN ip from the Server pod annotations. Service and IPv6 are not supported Using --udn option create a UserDefinedNetwork object on the netperf ns --- cmd/k8s-netperf/k8s-netperf.go | 31 ++++++++++++++++-- pkg/config/config.go | 1 + pkg/k8s/kubernetes.go | 60 ++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 3 deletions(-) diff --git a/cmd/k8s-netperf/k8s-netperf.go b/cmd/k8s-netperf/k8s-netperf.go index 44fcbebf..aeb30077 100644 --- a/cmd/k8s-netperf/k8s-netperf.go +++ b/cmd/k8s-netperf/k8s-netperf.go @@ -39,6 +39,7 @@ var ( netperf bool iperf3 bool uperf bool + udn string acrossAZ bool full bool vm bool @@ -151,19 +152,36 @@ var rootCmd = &cobra.Command{ os.Exit(1) } - if vm { - s.VM = true + if len(udn) > 1 { + s.Udn = udn // Create a dynamic client dynClient, err := dynamic.NewForConfig(rconfig) if err != nil { log.Error(err) } + s.DClient = dynClient + err = k8s.DeployL2Udn(dynClient, udn) + if err != nil { + log.Error(err) + os.Exit(1) + } + } + + if vm { + s.VM = true + // Create a dynamic client + if s.DClient == nil { + dynClient, err := dynamic.NewForConfig(rconfig) + if err != nil { + log.Error(err) + } + s.DClient = dynClient + } kclient, err := kubevirtv1.NewForConfig(rconfig) if err != nil { log.Error(err) } s.KClient = kclient - s.DClient = dynClient } // Build the SUT (Deployments) @@ -374,6 +392,7 @@ func executeWorkload(nc config.Config, hostNet bool, driverName string, virt bool) result.Data { serverIP := "" + var err error Client := s.Client var driver drivers.Driver if nc.Service { @@ -384,6 +403,11 @@ func executeWorkload(nc config.Config, } else { serverIP = s.NetperfService.Spec.ClusterIP } + } else if len(s.Udn) > 1 { + serverIP, err = k8s.ExtractUdnIp(s) + if err != nil { + log.Fatal(err) + } } else { if hostNet { serverIP = s.ServerHost.Items[0].Status.PodIP @@ -481,6 +505,7 @@ func main() { rootCmd.Flags().BoolVar(&acrossAZ, "across", false, "Place the client and server across availability zones") 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().StringVar(&udn, "udn", "", "Create and use a UDN using this name if provided.") 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") diff --git a/pkg/config/config.go b/pkg/config/config.go index 8195d2c0..d15d7bb8 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -38,6 +38,7 @@ type PerfScenarios struct { Configs []Config VM bool VMHost string + Udn string ServerNodeInfo metrics.NodeInfo ClientNodeInfo metrics.NodeInfo Client apiv1.PodList diff --git a/pkg/k8s/kubernetes.go b/pkg/k8s/kubernetes.go index 8d681f8d..1f48a039 100644 --- a/pkg/k8s/kubernetes.go +++ b/pkg/k8s/kubernetes.go @@ -5,6 +5,8 @@ import ( "fmt" "strings" + "encoding/json" + "github.com/cloud-bulldozer/k8s-netperf/pkg/config" log "github.com/cloud-bulldozer/k8s-netperf/pkg/logging" "github.com/cloud-bulldozer/k8s-netperf/pkg/metrics" @@ -12,9 +14,12 @@ import ( corev1 "k8s.io/api/core/v1" v1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" "k8s.io/utils/pointer" ) @@ -124,6 +129,39 @@ func BuildInfra(client *kubernetes.Clientset) error { return nil } +// Create a User Defined Network for the tests +func DeployL2Udn(dynamicClient *dynamic.DynamicClient, udnName string) error { + udn := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "k8s.ovn.org/v1", + "kind": "UserDefinedNetwork", + "metadata": map[string]interface{}{ + "name": udnName, + "namespace": "netperf", + }, + "spec": map[string]interface{}{ + "topology": "Layer2", + "layer2": map[string]interface{}{ + "role": "Primary", + "subnets": []string{"10.0.0.0/24", "2001:db8::/60"}, + }, + }, + }, + } + + // Specify the GVR for UDN + gvr := schema.GroupVersionResource{ + Group: "k8s.ovn.org", + Version: "v1", + Resource: "userdefinednetworks", + } + _, err := dynamicClient.Resource(gvr).Namespace(namespace).Create(context.TODO(), udn, 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 @@ -449,6 +487,28 @@ func BuildSUT(client *kubernetes.Clientset, s *config.PerfScenarios) error { return nil } +// Extract the UDN Ip address of a pod from the annotations - Support only ipv4 +func ExtractUdnIp(s config.PerfScenarios) (string, error) { + podNetworksJson := s.Server.Items[0].Annotations["k8s.ovn.org/pod-networks"] + var podNetworks map[string]interface{} + err := json.Unmarshal([]byte(podNetworksJson), &podNetworks) + if err != nil { + return "", err + } + UdnJson := podNetworks[namespace+"/"+s.Udn].(map[string]interface{}) + UdnIpAddreses := UdnJson["ip_addresses"].([]interface{}) + // Extract the IPv4 address + var ipv4 string + for _, ip := range UdnIpAddreses { + ipStr := ip.(string) + if strings.Contains(ipStr, ".") { // Check if it's an IPv4 address + ipv4 = strings.Split(ipStr, "/")[0] // Extract the IP address part before the subnet + break + } + } + return ipv4, nil +} + // 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)