Repository: rh-ecosystem-edge/nvidia-ci
Pull request #154: Added support to create IPoIBNetwork Custom CR and infiniband RDMA
Author: login: wabouhamad
Labels: name: approved
Created: 2025-04-26T13:17:49Z | Merged: 2025-04-26T14:06:17Z

Added support to run RDMA Shared Device Infiniband Test Case.

Updated files:
- README.md
- internal/nvidianetworkconfig/config.go
- internal/wait/networkwait.go
- tests/nvidianetwork/deploy-nno-test.go

Added file:
- pkg/nvidianetwork/ipoibnetwork.go

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Added support for creating, updating, and managing IPoIBNetwork resources for InfiniBand network configurations.
  - Enhanced test suite to deploy and validate IPoIBNetwork resources alongside existing network types.

- **Documentation**
  - Updated instructions and examples to include new environment variables and configuration steps for IPoIBNetwork.
  - Corrected minor typos and clarified usage for RDMA shared device tests with both Ethernet and InfiniBand.

- **Bug Fixes**
  - Fixed a typo in the MacvlanNetwork Custom Resource documentation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/README.md b/README.md
index c1a611544..766abd36f 100644
--- a/README.md
+++ b/README.md
@@ -96,13 +96,16 @@ NVIDIA Network Operator-specific (NNO) parameters for the script are controlled
 - `NVIDIANETWORK_RDMA_MLX_DEVICE:` mlx5 device ID corresponding to the interface port connected to Spectrum or Infiniband switch - _required_
 - `NVIDIANETWORK_RDMA_CLIENT_HOSTNAME:` RDMA Client hostname of first worker node for ib_write_bw test - _required when running the RDMA testcase_ 
 - `NVIDIANETWORK_RDMA_SERVER_HOSTNAME:` RDMA Server hostname of second worker node for ib_write_bw test - _required when running the RDMA testcase_   
-- `NVIDIANETWORK_RDMA_TEST_IMAGE:` RDMA Test Container Image that runs the entrypoint.sh script with optional arguments specified in the pod spec.  This container will clone the "https://github.com/linux-rdma/perftest" repo and builds the ib_write_bw binaries with or without cuda headers.  It will also run the ib_write_bw command with arguments either in CLient or Server mode.  Defaults to "quay.io/wabouham/ecosys-nvidia/rdma-tools:0.0.1" - _optional_
+- `NVIDIANETWORK_RDMA_TEST_IMAGE:` RDMA Test Container Image that runs the entrypoint.sh script with optional arguments specified in the pod spec.  This container will clone the "https://github.com/linux-rdma/perftest" repo and builds the ib_write_bw binaries with or without cuda headers.  It will also run the ib_write_bw command with arguments either in CLient or Server mode.  Defaults to "quay.io/wabouham/ecosys-nvidia/rdma-tools:0.0.2" - _optional_
 - `NVIDIANETWORK_MELLANOX_ETH_INTERFACE_NAME`: Mellanox Ethernet Interface Name - Defaults to "ens8f0np0" if not specified - _optional_  
 - `NVIDIANETWORK_MELLANOX_IB_INTERFACE_NAME`:  Mellanox Infiniband Interface Name - Defaults to "ens8f0np0" if not specified - _optional_   
-- `NVIDIANETWORK_MACVLANNETWORK_NAME`: MacvlanNetwork Custom Resource instance name  - Defaults to name from Cluster Service Bersion alm-examples section if not specified  - _optional_ 
+- `NVIDIANETWORK_MACVLANNETWORK_NAME`: MacvlanNetwork Custom Resource instance name  - Defaults to name from Cluster Service Version alm-examples section if not specified  - _optional_ 
 - `NVIDIANETWORK_MACVLANNETWORK_IPAM_RANGE`: MacvlanNetwork Custom Resource instance IPAM or IP Address/Subnet mask range for Eth or IB interface - _required_    
-- `NVIDIANETWORK_MACVLANNETWORK_IPAM_GATEWAY`: MacvlanNetwork Custom Resource instance IPAM Default Gateway for specified ip address range - _required_         
-
+- `NVIDIANETWORK_MACVLANNETWORK_IPAM_GATEWAY`: MacvlanNetwork Custom Resource instance IPAM Default Gateway for specified ip address range - _required_
+- `NVIDIANETWORK_IPOIBNETWORK_NAME`: IPoIBNetwork Custom Resource instance name  - Defaults to name from Cluster Service Version alm-examples section if not specified  - _optional_
+- `NVIDIANETWORK_IPOIBNETWORK_IPAM_RANGE`: IPoIBNetwork Custom Resource instance IPAM or IP Address/Subnet mask range for Eth or IB interface - _required_
+- `export NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP1`: IPoIBNetwork Custom Resource instance IPAM first IP Address and Mask excluded from range - _required_
+- `export NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP2`: IPoIBNetwork Custom Resource instance IPAM second IP Address and Mask excluded from range - _required_
 
 It is recommended to execute the runner script through the `make run-tests` make target.
 
@@ -169,7 +172,7 @@ $ export KUBECONFIG=/path/to/kubeconfig
 $ export DUMP_FAILED_TESTS=true
 $ export REPORTS_DUMP_DIR=/tmp/nvidia-nno-ci-logs-dir
 $ export TEST_FEATURES="nvidianetwork"
-$ export TEST_LABELS='nno,rdma'
+$ export TEST_LABELS='nno,rdma-shared-dev'
 $ export TEST_TRACE=true
 $ export VERBOSE_LEVEL=100
 $ export NVIDIANETWORK_CATALOGSOURCE="certified-operators"
@@ -186,14 +189,23 @@ $ export NVIDIANETWORK_DEPLOY_FROM_BUNDLE=true
 $ export NVIDIANETWORK_BUNDLE_IMAGE="nvcr.io/.../network-operator-bundle:v25.1.0-rc.2"
 $ export NVIDIANETWORK_MELLANOX_ETH_INTERFACE_NAME="ens8f0np0"
 $ export NVIDIANETWORK_MELLANOX_IB_INTERFACE_NAME="ibs2f0"
-$ export NVIDIANETWORK_MACVLANNETWORK_NAME="rdmashared-net"
 $ export NVIDIANETWORK_RDMA_WORKLOAD_NAMESPACE="default"
+# RDMA Shared Device Test Link Type: Infiniband or Ethernet
+$ export NVIDIANETWORK_RDMA_LINK_TYPE="infiniband"
+# For RDMA Shared Device Ethernet:
+$ export NVIDIANETWORK_MACVLANNETWORK_NAME="rdmashared-net"
 $ export NVIDIANETWORK_RDMA_LINK_TYPE="ethernet"
 $ export NVIDIANETWORK_RDMA_MLX_DEVICE="mlx5_2"
+# For RDMA Shared Device Infiniband:
+$ export NVIDIANETWORK_RDMA_MLX_DEVICE="mlx5_3"
+$ export NVIDIANETWORK_IPOIBNETWORK_NAME="example-ipoibnetwork"
+$ export NVIDIANETWORK_IPOIBNETWORK_IPAM_RANGE=192.168.6.225/24
+$ export NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP1=192.168.6.229/30
+$ export NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP2=192.168.6.236/32
 
 
 $ make run-tests
 Executing nvidiagpu test-runner script
 scripts/test-runner.sh
-ginkgo -timeout=24h --keep-going --require-suite -r -vv --trace --label-filter="nno,rdma" ./tests/nvidianetwork
+ginkgo -timeout=24h --keep-going --require-suite -r -vv --trace --label-filter="nno,rdma-shared-dev" ./tests/nvidianetwork
 ```
\ No newline at end of file
diff --git a/internal/nvidianetworkconfig/config.go b/internal/nvidianetworkconfig/config.go
index b24966baf..bb873438e 100644
--- a/internal/nvidianetworkconfig/config.go
+++ b/internal/nvidianetworkconfig/config.go
@@ -26,6 +26,10 @@ type NvidiaNetworkConfig struct {
 	MacvlanNetworkName                 string `envconfig:"NVIDIANETWORK_MACVLANNETWORK_NAME"`
 	MacvlanNetworkIPAMRange            string `envconfig:"NVIDIANETWORK_MACVLANNETWORK_IPAM_RANGE"`
 	MacvlanNetworkIPAMGateway          string `envconfig:"NVIDIANETWORK_MACVLANNETWORK_IPAM_GATEWAY"`
+	IPoIBNetworkName                   string `envconfig:"NVIDIANETWORK_IPOIBNETWORK_NAME"`
+	IPoIBNetworkIPAMRange              string `envconfig:"NVIDIANETWORK_IPOIBNETWORK_IPAM_RANGE"`
+	IPoIBNetworkIPAMExcludeIP1         string `envconfig:"NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP1"`
+	IPoIBNetworkIPAMExcludeIP2         string `envconfig:"NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP2"`
 	OperatorUpgradeToChannel           string `envconfig:"NVIDIANETWORK_SUBSCRIPTION_UPGRADE_TO_CHANNEL"`
 	NNOFallbackCatalogsourceIndexImage string `envconfig:"NVIDIANETWORK_NNO_FALLBACK_CATALOGSOURCE_INDEX_IMAGE"`
 	NFDFallbackCatalogsourceIndexImage string `envconfig:"NVIDIANETWORK_NFD_FALLBACK_CATALOGSOURCE_INDEX_IMAGE"`
diff --git a/internal/wait/networkwait.go b/internal/wait/networkwait.go
index 16da6b27d..4ef57c4a8 100644
--- a/internal/wait/networkwait.go
+++ b/internal/wait/networkwait.go
@@ -55,3 +55,24 @@ func MacvlanNetworkReady(apiClient *clients.Settings, macvlanNetworkName string,
 			return macVlanNetwork.Object.Status.State == networkoperator.StateReady, nil
 		})
 }
+
+// IPoIBNetworkReady Waits until ipoibNetwork is Ready.
+func IPoIBNetworkReady(apiClient *clients.Settings, ipoibNetworkName string, pollInterval,
+	timeout time.Duration) error {
+	return wait.PollUntilContextTimeout(
+		context.Background(), pollInterval, timeout, true, func(ctx context.Context) (bool, error) {
+			ipoIBNetwork, err := nvidianetwork.PullIPoIBNetwork(apiClient, ipoibNetworkName)
+
+			if err != nil {
+				glog.V(networkparams.LogLevel).Infof("IPoIBNetwork pull from cluster error: %s\n", err)
+
+				return false, err
+			}
+
+			glog.V(networkparams.LogLevel).Infof("IPoIBNetwork %s in now in %s state",
+				ipoIBNetwork.Object.Name, ipoIBNetwork.Object.Status.State)
+
+			// returns true, nil when IPoIBNetwork is ready, this exits out of the PollUntilContextTimeout()
+			return ipoIBNetwork.Object.Status.State == networkoperator.StateReady, nil
+		})
+}
diff --git a/pkg/nvidianetwork/ipoibnetwork.go b/pkg/nvidianetwork/ipoibnetwork.go
new file mode 100644
index 000000000..fb387d407
--- /dev/null
+++ b/pkg/nvidianetwork/ipoibnetwork.go
@@ -0,0 +1,282 @@
+package nvidianetwork
+
+import (
+	"context"
+	"fmt"
+	nvidianetworkv1alpha1 "github.com/Mellanox/network-operator/api/v1alpha1"
+
+	"github.com/golang/glog"
+	"github.com/rh-ecosystem-edge/nvidia-ci/pkg/clients"
+	"github.com/rh-ecosystem-edge/nvidia-ci/pkg/msg"
+	k8serrors "k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/util/json"
+	goclient "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+// IPoIBNetworkBuilder  provides a struct for IPoIBNetwork object
+// from the cluster and a IPoIBNetwork definition.
+type IPoIBNetworkBuilder struct {
+	// IPoIBNetworkBuilder  definition. Used to create
+	// IPoIBNetworkBuilder  object with minimum set of required elements.
+	Definition *nvidianetworkv1alpha1.IPoIBNetwork
+	// Created IPoIBNetworkBuilder  object on the cluster.
+	Object *nvidianetworkv1alpha1.IPoIBNetwork
+	// api client to interact with the cluster.
+	apiClient *clients.Settings
+	// errorMsg is processed before IPoIBNetworkBuilder  object is created.
+	errorMsg string
+}
+
+// NewIPoIBNetworkBuilderFromObjectString creates a IPoIBNetworkBuilder  object from CSV alm-examples.
+func NewIPoIBNetworkBuilderFromObjectString(apiClient *clients.Settings, almExample string) *IPoIBNetworkBuilder {
+	glog.V(100).Infof(
+		"Initializing new IPoIBNetworkBuilder  structure from almExample string")
+
+	IPoIBNetwork, err := getIPoIBNetworkFromAlmExample(almExample)
+
+	if err != nil {
+		glog.V(100).Infof(
+			"Error initializing IPoIBNetwork from alm-examples: %s", err.Error())
+
+		builder := IPoIBNetworkBuilder{
+			apiClient: apiClient,
+			errorMsg:  fmt.Sprintf("Error initializing IPoIBNetwork from alm-examples: %s", err.Error()),
+		}
+
+		return &builder
+	}
+
+	if IPoIBNetwork == nil {
+		builder := IPoIBNetworkBuilder{
+			apiClient: apiClient,
+			errorMsg:  "IPoIBNetwork is nil after parsing almExample",
+		}
+		return &builder
+	}
+
+	glog.V(100).Infof(
+		"Initializing new IPoIBNetworkBuilder  structure from almExample string with IPoIBNetwork name: %s",
+		IPoIBNetwork.Name)
+
+	builder := IPoIBNetworkBuilder{
+		apiClient:  apiClient,
+		Definition: IPoIBNetwork,
+	}
+
+	if builder.Definition == nil {
+		glog.V(100).Infof("The IPoIBNetwork object definition is nil")
+
+		builder.errorMsg = "IPoIBNetwork 'Object.Definition' is nil"
+	}
+
+	return &builder
+}
+
+// Get returns IPoIBNetwork object if found.
+func (builder *IPoIBNetworkBuilder) Get() (*nvidianetworkv1alpha1.IPoIBNetwork, error) {
+	if valid, err := builder.validate(); !valid {
+		return nil, err
+	}
+
+	glog.V(100).Infof(
+		"Collecting IPoIBNetwork object %s", builder.Definition.Name)
+
+	IPoIBNetwork := &nvidianetworkv1alpha1.IPoIBNetwork{}
+	err := builder.apiClient.Get(context.TODO(), goclient.ObjectKey{
+		Name: builder.Definition.Name,
+	}, IPoIBNetwork)
+
+	if err != nil {
+		glog.V(100).Infof(
+			"IPoIBNetwork object %s doesn't exist", builder.Definition.Name)
+
+		return nil, err
+	}
+
+	return IPoIBNetwork, err
+}
+
+// PullIPoIBNetwork loads an existing IPoIBNetwork into IPoIBNetworkBuilder  struct.
+func PullIPoIBNetwork(apiClient *clients.Settings, name string) (*IPoIBNetworkBuilder, error) {
+	glog.V(100).Infof("Pulling existing IPoIBNetwork name: %s", name)
+
+	builder := IPoIBNetworkBuilder{
+		apiClient: apiClient,
+		Definition: &nvidianetworkv1alpha1.IPoIBNetwork{
+			ObjectMeta: metav1.ObjectMeta{
+				Name: name,
+			},
+		},
+	}
+
+	if name == "" {
+		glog.V(100).Infof("IPoIBNetwork name is empty")
+
+		builder.errorMsg = "IPoIBNetwork 'name' cannot be empty"
+		return nil, fmt.Errorf(builder.errorMsg)
+	}
+
+	if !builder.Exists() {
+		return nil, fmt.Errorf("IPoIBNetwork object %s doesn't exist", name)
+	}
+
+	builder.Definition = builder.Object
+
+	return &builder, nil
+}
+
+// Exists checks whether the given IPoIBNetwork exists.
+func (builder *IPoIBNetworkBuilder) Exists() bool {
+	if valid, _ := builder.validate(); !valid {
+		return false
+	}
+
+	glog.V(100).Infof(
+		"Checking if IPoIBNetwork %s exists", builder.Definition.Name)
+
+	var err error
+	builder.Object, err = builder.Get()
+
+	if err != nil {
+		glog.V(100).Infof("Failed to collect IPoIBNetwork object due to %s", err.Error())
+	}
+
+	return err == nil || !k8serrors.IsNotFound(err)
+}
+
+// Delete removes a IPoIBNetwork.
+func (builder *IPoIBNetworkBuilder) Delete() (*IPoIBNetworkBuilder, error) {
+	if valid, err := builder.validate(); !valid {
+		return builder, err
+	}
+
+	glog.V(100).Infof("Deleting IPoIBNetwork %s", builder.Definition.Name)
+
+	if !builder.Exists() {
+		return builder, fmt.Errorf("IPoIBNetwork cannot be deleted because it does not exist")
+	}
+
+	err := builder.apiClient.Delete(context.TODO(), builder.Definition)
+
+	if err != nil {
+		return builder, fmt.Errorf("cannot delete IPoIBNetwork: %w", err)
+	}
+
+	builder.Object = nil
+
+	return builder, nil
+}
+
+// Create makes a IPoIBNetwork in the cluster and stores the created object in struct.
+func (builder *IPoIBNetworkBuilder) Create() (*IPoIBNetworkBuilder, error) {
+	if valid, err := builder.validate(); !valid {
+		return builder, err
+	}
+
+	glog.V(100).Infof("Creating the IPoIBNetwork %s", builder.Definition.Name)
+
+	var err error
+	if !builder.Exists() {
+		err = builder.apiClient.Create(context.TODO(), builder.Definition)
+
+		if err == nil {
+			builder.Object = builder.Definition
+		} else {
+			glog.V(100).Infof("Error creating the IPoIBNetwork '%s' : '%s'",
+				builder.Definition.Name, err.Error())
+		}
+	}
+
+	return builder, err
+}
+
+// Update renovates the existing IPoIBNetwork object with the definition in builder.
+func (builder *IPoIBNetworkBuilder) Update(force bool) (*IPoIBNetworkBuilder, error) {
+	if valid, err := builder.validate(); !valid {
+		return builder, err
+	}
+
+	glog.V(100).Infof("Updating the IPoIBNetwork object named:  %s", builder.Definition.Name)
+
+	err := builder.apiClient.Update(context.TODO(), builder.Definition)
+
+	if err != nil {
+		if force {
+			glog.V(100).Infof(msg.FailToUpdateNotification("IPoIBNetwork", builder.Definition.Name))
+
+			builder, err := builder.Delete()
+
+			if err != nil {
+				glog.V(100).Infof(
+					msg.FailToUpdateError("IPoIBNetwork", builder.Definition.Name))
+
+				return nil, err
+			}
+
+			return builder.Create()
+		}
+	}
+
+	return builder, err
+}
+
+// getIPoIBNetworkFromAlmExample extracts the IPoIBNetwork from the alm-examples block.
+func getIPoIBNetworkFromAlmExample(almExample string) (*nvidianetworkv1alpha1.IPoIBNetwork, error) {
+	IPoIBNetworkList := &nvidianetworkv1alpha1.IPoIBNetworkList{}
+
+	if almExample == "" {
+		return nil, fmt.Errorf("almExample is an empty string")
+	}
+
+	err := json.Unmarshal([]byte(almExample), &IPoIBNetworkList.Items)
+
+	if err != nil {
+		glog.V(100).Infof("Error unmarshalling IPoIBNetwork from almExamples: '%s'", err.Error())
+
+		return nil, err
+	}
+
+	if len(IPoIBNetworkList.Items) == 0 {
+		return nil, fmt.Errorf("failed to get alm examples")
+	}
+
+	for i := range IPoIBNetworkList.Items {
+		if IPoIBNetworkList.Items[i].Kind == "IPoIBNetwork" {
+			return &IPoIBNetworkList.Items[i], nil
+		}
+	}
+
+	return nil, fmt.Errorf("IPoIBNetwork not found in alm examples")
+}
+
+// validate will check that the builder and builder definition are properly initialized before
+// accessing any member fields.
+func (builder *IPoIBNetworkBuilder) validate() (bool, error) {
+	resourceCRD := nvidianetworkv1alpha1.IPoIBNetworkCRDName
+	if builder == nil {
+		glog.V(100).Infof("The %s builder is uninitialized", resourceCRD)
+
+		return false, fmt.Errorf("error: received nil %s builder", resourceCRD)
+	}
+
+	if builder.Definition == nil {
+		glog.V(100).Infof("The %s is undefined", resourceCRD)
+
+		builder.errorMsg = msg.UndefinedCrdObjectErrString(resourceCRD)
+	}
+
+	if builder.apiClient == nil {
+		glog.V(100).Infof("The %s builder apiclient is nil", resourceCRD)
+
+		builder.errorMsg = fmt.Sprintf("%s builder cannot have nil apiClient", resourceCRD)
+	}
+
+	if builder.errorMsg != "" {
+		glog.V(100).Infof("The %s builder has error message: %s", resourceCRD, builder.errorMsg)
+
+		return false, fmt.Errorf(builder.errorMsg)
+	}
+
+	return true, nil
+}
diff --git a/tests/nvidianetwork/deploy-nno-test.go b/tests/nvidianetwork/deploy-nno-test.go
index 397430483..d15fe292a 100644
--- a/tests/nvidianetwork/deploy-nno-test.go
+++ b/tests/nvidianetwork/deploy-nno-test.go
@@ -7,6 +7,7 @@ import (
 	"github.com/rh-ecosystem-edge/nvidia-ci/internal/inittools"
 	"github.com/rh-ecosystem-edge/nvidia-ci/internal/nvidianetworkconfig"
 	rdmatest "github.com/rh-ecosystem-edge/nvidia-ci/internal/rdma"
+	"github.com/rh-ecosystem-edge/nvidia-ci/pkg/deployment"
 	"github.com/rh-ecosystem-edge/nvidia-ci/pkg/nfdcheck"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -15,7 +16,6 @@ import (
 	"github.com/golang/glog"
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
-	"github.com/rh-ecosystem-edge/nvidia-ci/pkg/deployment"
 	. "github.com/rh-ecosystem-edge/nvidia-ci/pkg/global"
 	"github.com/rh-ecosystem-edge/nvidia-ci/pkg/namespace"
 	"github.com/rh-ecosystem-edge/nvidia-ci/pkg/nfd"
@@ -72,6 +72,11 @@ var (
 	macvlanNetworkIPAMRange   = UndefinedValue
 	macvlanNetworkIPAMGateway = UndefinedValue
 
+	ipoibNetworkName           = UndefinedValue
+	ipoibNetworkIPAMRange      = UndefinedValue
+	ipoibNetworkIPAMExcludeIP1 = UndefinedValue
+	ipoibNetworkIPAMExcludeIP2 = UndefinedValue
+
 	ofedDriverVersion    = UndefinedValue
 	ofedDriverRepository = UndefinedValue
 )
@@ -80,23 +85,23 @@ const (
 	nvidiaNetworkLabel                      = "feature.node.kubernetes.io/pci-15b3.present"
 	networkOperatorDefaultMasterBundleImage = "registry.gitlab.com/nvidia/kubernetes/network-operator/staging/network-operator-bundle:main-latest"
 
-	nnoNamespace                 = "nvidia-network-operator"
-	nnoOperatorGroupName         = "nno-og"
-	nnoDeployment                = "nvidia-network-operator-controller-manager"
-	nnoSubscriptionName          = "nno-subscription"
-	nnoSubscriptionNamespace     = "nvidia-network-operator"
-	nnoCatalogSourceDefault      = "certified-operators"
-	nnoCatalogSourceNamespace    = nfd.CatalogSourceNamespace
-	nnoPackage                   = "nvidia-network-operator"
-	nnoNicClusterPolicyName      = "nic-cluster-policy"
-	nnoMacvlanNetworkNameDefault = "rdmashared-net"
-
+	nnoNamespace                        = "nvidia-network-operator"
+	nnoOperatorGroupName                = "nno-og"
+	nnoDeployment                       = "nvidia-network-operator-controller-manager"
+	nnoSubscriptionName                 = "nno-subscription"
+	nnoSubscriptionNamespace            = "nvidia-network-operator"
+	nnoCatalogSourceDefault             = "certified-operators"
+	nnoCatalogSourceNamespace           = nfd.CatalogSourceNamespace
+	nnoPackage                          = "nvidia-network-operator"
+	nnoNicClusterPolicyName             = "nic-cluster-policy"
+	nnoMacvlanNetworkNameDefault        = "rdmashared-net"
+	nnoIPoIBNetworkNameDefault          = "example-ipoibnetwork"
 	nnoCustomCatalogSourcePublisherName = "Red Hat"
 	nnoCustomCatalogSourceDisplayName   = "Certified Operators Custom"
 
-	rdmaTestImageDefault                   = "quay.io/wabouham/ecosys-nvidia/rdma-tools:0.0.1"
-	mellanoxEthernetInterfaceNameDefault   = "ens8f0np0"
-	mellanoxInfinibandInterfaceNameDefault = "ibs2f0"
+	rdmaTestImageDefault                   = "quay.io/wabouham/ecosys-nvidia/rdma-tools:0.0.2"
+	mellanoxEthernetInterfaceNameDefault   = "ens1f0np0"
+	mellanoxInfinibandInterfaceNameDefault = "ibs1f1"
 )
 
 var _ = Describe("NNO", Ordered, Label(tsparams.LabelSuite), func() {
@@ -204,6 +209,52 @@ var _ = Describe("NNO", Ordered, Label(tsparams.LabelSuite), func() {
 					"NVIDIANETWORK_MACVLANNETWORK_IPAM_GATEWAY value '%s'", macvlanNetworkIPAMGateway)
 			}
 
+			if nvidiaNetworkConfig.IPoIBNetworkName == "" {
+				glog.V(networkparams.LogLevel).Infof("env variable NVIDIANETWORK_IPOIBNETWORK_NAME"+
+					" is not set, will use default name '%s'", nnoIPoIBNetworkNameDefault)
+				ipoibNetworkName = nnoIPoIBNetworkNameDefault
+			} else {
+				ipoibNetworkName = nvidiaNetworkConfig.IPoIBNetworkName
+				glog.V(networkparams.LogLevel).Infof("ipoibNetworkName is set to env variable "+
+					"NVIDIANETWORK_IPOIBNETWORK_NAME value '%s'", ipoibNetworkName)
+			}
+
+			if nvidiaNetworkConfig.IPoIBNetworkIPAMRange == "" {
+				glog.V(networkparams.LogLevel).Infof("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_RANGE" +
+					" is not set, skipping test case execution")
+				glog.V(networkparams.LogLevel).Infof("Skipping testcase:  env variable " +
+					"NVIDIANETWORK_IPOIBNETWORK_IPAM_RANGE is not set")
+				Skip("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_RANGE is not set")
+			} else {
+				ipoibNetworkIPAMRange = nvidiaNetworkConfig.IPoIBNetworkIPAMRange
+				glog.V(networkparams.LogLevel).Infof("ipoibNetworkIPAMRange is set to env variable "+
+					"NVIDIANETWORK_IPOIBNETWORK_IPAM_RANGE value '%s'", ipoibNetworkIPAMRange)
+			}
+
+			if nvidiaNetworkConfig.IPoIBNetworkIPAMExcludeIP1 == "" {
+				glog.V(networkparams.LogLevel).Infof("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP1" +
+					" is not set, skipping test case execution")
+				glog.V(networkparams.LogLevel).Infof("Skipping testcase:  env variable " +
+					"NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP1 is not set")
+				Skip("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP1 is not set")
+			} else {
+				ipoibNetworkIPAMExcludeIP1 = nvidiaNetworkConfig.IPoIBNetworkIPAMExcludeIP1
+				glog.V(networkparams.LogLevel).Infof("ipoibNetworkIPAMExcludeIP1 is set to env variable "+
+					"NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP1 value '%s'", ipoibNetworkIPAMExcludeIP1)
+			}
+
+			if nvidiaNetworkConfig.IPoIBNetworkIPAMExcludeIP2 == "" {
+				glog.V(networkparams.LogLevel).Infof("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP2" +
+					" is not set, skipping test case execution")
+				glog.V(networkparams.LogLevel).Infof("Skipping testcase:  env variable " +
+					"NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP2 is not set")
+				Skip("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP2 is not set")
+			} else {
+				ipoibNetworkIPAMExcludeIP2 = nvidiaNetworkConfig.IPoIBNetworkIPAMExcludeIP2
+				glog.V(networkparams.LogLevel).Infof("ipoibNetworkIPAMExcludeIP2 is set to env variable "+
+					"NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP2 value '%s'", ipoibNetworkIPAMExcludeIP2)
+			}
+
 			if nvidiaNetworkConfig.RdmaClientHostname == "" {
 				glog.V(networkparams.LogLevel).Infof("env variable NVIDIANETWORK_RDMA_CLIENT_HOSTNAME" +
 					" is not set skipping test case execution")
@@ -238,8 +289,6 @@ var _ = Describe("NNO", Ordered, Label(tsparams.LabelSuite), func() {
 					"NVIDIANETWORK_RDMA_TEST_IMAGE value '%v'", rdmaTestImage)
 			}
 
-			// === 04-09-2025
-			// RdmaWorkloadNamespace                       string `envconfig:"NVIDIANETWORK_RDMA_WORKLOAD_NAMESPACE"`
 			if nvidiaNetworkConfig.RdmaWorkloadNamespace == "" {
 				glog.V(networkparams.LogLevel).Infof("env variable NVIDIANETWORK_RDMA_WORKLOAD_NAMESPACE" +
 					" is not set skipping test case execution")
@@ -815,6 +864,7 @@ var _ = Describe("NNO", Ordered, Label(tsparams.LabelSuite), func() {
 				        }
 
 
+
 			*/
 
 			// Define the JSON structure in Go structs
@@ -838,13 +888,11 @@ var _ = Describe("NNO", Ordered, Label(tsparams.LabelSuite), func() {
 						ResourceName: "rdma_shared_device_ib",
 						RdmaHcaMax:   63,
 						Selectors:    Selector{IfNames: []string{mellanoxInfinibandInterfaceName}},
-						// Selectors:    Selector{IfNames: []string{"ibs2f0"}},
 					},
 					{
 						ResourceName: "rdma_shared_device_eth",
 						RdmaHcaMax:   63,
 						Selectors:    Selector{IfNames: []string{mellanoxEthernetInterfaceName}},
-						// Selectors:    Selector{IfNames: []string{"ens8f0np0"}},
 					},
 				},
 			}
@@ -994,25 +1042,118 @@ var _ = Describe("NNO", Ordered, Label(tsparams.LabelSuite), func() {
 			Expect(err).ToNot(HaveOccurred(), "error pulling MacvlanNetwork %s from cluster: "+
 				" %v ", macvlanNetworkName, err)
 
-			cpReadyJSON, err := json.MarshalIndent(pulledReadyMacvlanNetwork, "", " ")
+			mvnReadyJSON, err := json.MarshalIndent(pulledReadyMacvlanNetwork, "", " ")
 
 			if err == nil {
 				glog.V(networkparams.LogLevel).Infof("The ready MacvlanNetwork just has name:  %v",
 					pulledReadyMacvlanNetwork.Definition.Name)
 				glog.V(networkparams.LogLevel).Infof("The ready MacvlanNetwork just marshalled "+
-					"in json: %v", string(cpReadyJSON))
+					"in json: %v", string(mvnReadyJSON))
 			} else {
 				glog.V(networkparams.LogLevel).Infof("Error Marshalling the ready MacvlanNetwork into "+
 					"json:  %v", err)
 			}
 
+			By("Deploy IPoIBNetwork")
+			glog.V(networkparams.LogLevel).Infof("Creating IPoIBNetwork from CSV almExamples")
+			ipoibNetworkBuilder := nvidianetwork.NewIPoIBNetworkBuilderFromObjectString(inittools.APIClient,
+				almExamples)
+
+			By("Updating IPoIBNetworkBuilder object name from value in env vars")
+			glog.V(networkparams.LogLevel).Infof("Updating IPoIBNetworkBuilder object value passed in "+
+				"env variable '%s'", ipoibNetworkName)
+			ipoibNetworkBuilder.Definition.Name = ipoibNetworkName
+
+			By("Updating IPoIBNetworkBuilder object ipam and master from values in env vars")
+			glog.V(networkparams.LogLevel).Infof("Updating IPoIBNetworkBuilder object ipam and " +
+				"master with values passed in env variables")
+			/*
+			   ipam: |
+			     {
+			       "type": "whereabouts",
+			       "range": "192.168.6.225/28",
+			       "exclude": [
+			        "192.168.6.229/30",
+			        "192.168.6.236/32"
+			       ]
+			     }
+			   master: ibs1f1
+			*/
+
+			ipoibIpamConfig := fmt.Sprintf(
+				`{"type": "whereabouts", "range": "%s", "exclude": ["%s", "%s"]}`,
+				ipoibNetworkIPAMRange, ipoibNetworkIPAMExcludeIP1, ipoibNetworkIPAMExcludeIP2,
+			)
+
+			fmt.Println(ipoibIpamConfig)
+			ipoibNetworkBuilder.Definition.Spec.IPAM = ipoibIpamConfig
+
+			ipoibNetworkBuilder.Definition.Spec.Master = mellanoxInfinibandInterfaceName
+
+			By("Deploy IPoIBNetwork")
+			createdIPoIBNetworkBuilder, err := ipoibNetworkBuilder.Create()
+			Expect(err).ToNot(HaveOccurred(), "Error Creating IPoIBNetwork from csv "+
+				"almExamples  %v ", err)
+			glog.V(networkparams.LogLevel).Infof("IPoIBNetwork '%s' is successfully created",
+				createdIPoIBNetworkBuilder.Definition.Name)
+
+			defer func() {
+				if cleanupAfterTest {
+					_, err := createdIPoIBNetworkBuilder.Delete()
+					Expect(err).ToNot(HaveOccurred())
+				}
+			}()
+
+			By("Pull the IPoIBNetwork just created from cluster, with updated fields")
+			pulledIPoIBNetwork, err := nvidianetwork.PullIPoIBNetwork(inittools.APIClient, ipoibNetworkName)
+			Expect(err).ToNot(HaveOccurred(), "error pulling IPoIBNetwork %s from cluster: "+
+				" %v ", ipoibNetworkName, err)
+
+			ipoibJSON, err := json.MarshalIndent(pulledIPoIBNetwork, "", " ")
+
+			if err == nil {
+				glog.V(networkparams.LogLevel).Infof("The IPoIBNetwork just created has name:  %v",
+					pulledIPoIBNetwork.Definition.Name)
+				glog.V(networkparams.LogLevel).Infof("The IPoIBNetwork just created marshalled "+
+					"in json: %v", string(ipoibJSON))
+			} else {
+				glog.V(networkparams.LogLevel).Infof("Error Marshalling IPoIBNetwork into json:  %v",
+					err)
+			}
+
+			By("Wait up to 5 minutes for IPoIBNetwork to be ready")
+			glog.V(networkparams.LogLevel).Infof("Waiting for IPoIBNetwork to be ready")
+			err = wait.IPoIBNetworkReady(inittools.APIClient, ipoibNetworkName, 60*time.Second,
+				5*time.Minute)
+
+			glog.V(networkparams.LogLevel).Infof("error waiting for IPoIBNetwork to be Ready:  %v ", err)
+			Expect(err).ToNot(HaveOccurred(), "error waiting for IPoIBNetwork to be Ready: "+
+				" %v ", err)
+
+			By("Pull the ready IPoIBNetwork from cluster, with updated fields")
+			pulledReadyIPoIBNetwork, err := nvidianetwork.PullIPoIBNetwork(inittools.APIClient, ipoibNetworkName)
+			Expect(err).ToNot(HaveOccurred(), "error pulling IPoIBNetwork %s from cluster: "+
+				" %v ", ipoibNetworkName, err)
+
+			ipoibReadyJSON, err := json.MarshalIndent(pulledReadyIPoIBNetwork, "", " ")
+
+			if err == nil {
+				glog.V(networkparams.LogLevel).Infof("The ready IPoIBNetwork just has name:  %v",
+					pulledReadyIPoIBNetwork.Definition.Name)
+				glog.V(networkparams.LogLevel).Infof("The ready IPoIBNetwork just marshalled "+
+					"in json: %v", string(ipoibReadyJSON))
+			} else {
+				glog.V(networkparams.LogLevel).Infof("Error Marshalling the ready IPoIBNetwork into "+
+					"json:  %v", err)
+			}
+
 		})
 
-		It("Run RDMA connectivity test with ib_write_bw", Label("rdma"), func() {
+		It("Run RDMA connectivity test with ib_write_bw", Label("rdma-shared-dev"), func() {
 
 			var (
-				rdmaServerPodNamePrefix = "rdma-server-ci"
-				rdmaClientPodNamePrefix = "rdma-client-ci"
+				rdmaServerPodNamePrefix = "rdma-shared-dev-server-ci"
+				rdmaClientPodNamePrefix = "rdma-shared-dev-client-ci"
 			)
 
 			rdmaServerPodName := rdmaServerPodNamePrefix + "-" + rdmaLinkType


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>🔭 Outside diff range comments (1)</summary><blockquote>

<details>
<summary>tests/nvidianetwork/deploy-nno-test.go (1)</summary><blockquote>

`118-120`: _⚠️ Potential issue_

**Fix incorrect default assignment for Infiniband interface**

The Infiniband interface name is incorrectly assigned to the Ethernet interface name default instead of using the Infiniband interface name default.

```diff
if mellanoxInfinibandInterfaceName == "" {
	glog.V(networkparams.LogLevel).Infof("env variable NVIDIANETWORK_MELLANOX_IB_INTERFACE_NAME"+
		" is not set, will use default infiniband interface name '%s'", mellanoxInfinibandInterfaceNameDefault)
-	mellanoxInfinibandInterfaceName = mellanoxEthernetInterfaceNameDefault
+	mellanoxInfinibandInterfaceName = mellanoxInfinibandInterfaceNameDefault
}
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (8)</summary><blockquote>

<details>
<summary>internal/wait/networkwait.go (1)</summary><blockquote>

`59-78`: **LGTM: New IPoIBNetworkReady function aligns with existing patterns**

The implementation of `IPoIBNetworkReady` follows the same pattern as the existing wait functions, which is good for consistency. The function correctly polls for the IPoIBNetwork resource's "Ready" state.


There's a minor typo in the log message: "in now in" should be "is now in".

```diff
-			glog.V(networkparams.LogLevel).Infof("IPoIBNetwork %s in now in %s state",
+			glog.V(networkparams.LogLevel).Infof("IPoIBNetwork %s is now in %s state",
				ipoIBNetwork.Object.Name, ipoIBNetwork.Object.Status.State)
```

</blockquote></details>
<details>
<summary>README.md (1)</summary><blockquote>

`193-204`: **Minor inconsistency in environment variable examples**

The examples for Infiniband testing include variables for IPoIBNetwork IPAM settings, but there's no example line for `NVIDIANETWORK_IPOIBNETWORK_NAME` even though this variable is documented earlier in the README.


Consider adding the following line to the Infiniband example section for completeness:
```
$ export NVIDIANETWORK_IPOIBNETWORK_NAME="example-ipoibnetwork"
```

</blockquote></details>
<details>
<summary>pkg/nvidianetwork/ipoibnetwork.go (6)</summary><blockquote>

`17-29`: **Structure looks good, but comment formatting needs attention.**

The IPoIBNetworkBuilder struct is well-designed with proper field definitions. However, there are extra spaces in the comments for "IPoIBNetworkBuilder" on lines 17, 20, 21, 23, and 27 that should be removed for consistency.

```diff
-// IPoIBNetworkBuilder  provides a struct for IPoIBNetwork object
+// IPoIBNetworkBuilder provides a struct for IPoIBNetwork object
 // from the cluster and a IPoIBNetwork definition.
 type IPoIBNetworkBuilder struct {
-	// IPoIBNetworkBuilder  definition. Used to create
-	// IPoIBNetworkBuilder  object with minimum set of required elements.
+	// IPoIBNetworkBuilder definition. Used to create
+	// IPoIBNetworkBuilder object with minimum set of required elements.
 	Definition *nvidianetworkv1alpha1.IPoIBNetwork
-	// Created IPoIBNetworkBuilder  object on the cluster.
+	// Created IPoIBNetworkBuilder object on the cluster.
 	Object *nvidianetworkv1alpha1.IPoIBNetwork
 	// api client to interact with the cluster.
 	apiClient *clients.Settings
-	// errorMsg is processed before IPoIBNetworkBuilder  object is created.
+	// errorMsg is processed before IPoIBNetworkBuilder object is created.
 	errorMsg string
 }
```

---

`31-35`: **Extra spaces in comments should be removed.**

Similar to the struct definition, the function comments have extra spaces after "IPoIBNetworkBuilder".

```diff
-// NewIPoIBNetworkBuilderFromObjectString creates a IPoIBNetworkBuilder  object from CSV alm-examples.
+// NewIPoIBNetworkBuilderFromObjectString creates a IPoIBNetworkBuilder object from CSV alm-examples.
 func NewIPoIBNetworkBuilderFromObjectString(apiClient *clients.Settings, almExample string) *IPoIBNetworkBuilder {
 	glog.V(100).Infof(
-		"Initializing new IPoIBNetworkBuilder  structure from almExample string")
+		"Initializing new IPoIBNetworkBuilder structure from almExample string")
```

---

`58-60`: **Extra space in log message.**

Remove the extra space after "IPoIBNetworkBuilder" in the log message for consistency.

```diff
 	glog.V(100).Infof(
-		"Initializing new IPoIBNetworkBuilder  structure from almExample string with IPoIBNetwork name: %s",
+		"Initializing new IPoIBNetworkBuilder structure from almExample string with IPoIBNetwork name: %s",
 		IPoIBNetwork.Name)
```

---

`100-117`: **Improve error handling in `PullIPoIBNetwork`.**

The function correctly validates that the name is not empty, but it could be improved in two ways:
1. The error message is saved to the builder but then a separate error is returned
2. The validation should happen before creating the builder to prevent a partially initialized object


```diff
 func PullIPoIBNetwork(apiClient *clients.Settings, name string) (*IPoIBNetworkBuilder, error) {
 	glog.V(100).Infof("Pulling existing IPoIBNetwork name: %s", name)
 
+	if name == "" {
+		errMsg := "IPoIBNetwork 'name' cannot be empty"
+		glog.V(100).Infof(errMsg)
+		return nil, fmt.Errorf(errMsg)
+	}
+
 	builder := IPoIBNetworkBuilder{
 		apiClient: apiClient,
 		Definition: &nvidianetworkv1alpha1.IPoIBNetwork{
 			ObjectMeta: metav1.ObjectMeta{
 				Name: name,
 			},
 		},
 	}
 
-	if name == "" {
-		glog.V(100).Infof("IPoIBNetwork name is empty")
-
-		builder.errorMsg = "IPoIBNetwork 'name' cannot be empty"
-		return nil, fmt.Errorf(builder.errorMsg)
-	}
```

---

`138-145`: **Improve error handling in `Exists` method.**

The method logs the error but does not propagate it clearly through the return value, which could make debugging more difficult. Consider clarifying the implementation to make the error handling more explicit.

```diff
 	var err error
 	builder.Object, err = builder.Get()
 
 	if err != nil {
 		glog.V(100).Infof("Failed to collect IPoIBNetwork object due to %s", err.Error())
+		// Only log the error, not returning it as this method only indicates existence
 	}
 
 	return err == nil || !k8serrors.IsNotFound(err)
```

---

`232-238`: **Consider more specific error handling in JSON unmarshaling.**

When unmarshaling JSON fails, it would be helpful to include a portion of the input in the error message to help with debugging. This is especially useful when working with large JSON payloads.

```diff
 	err := json.Unmarshal([]byte(almExample), &IPoIBNetworkList.Items)
 
 	if err != nil {
-		glog.V(100).Infof("Error unmarshalling IPoIBNetwork from almExamples: '%s'", err.Error())
+		// Include a snippet of the almExample in the log for debugging
+		previewLen := 100
+		if len(almExample) > previewLen {
+			almExample = almExample[:previewLen] + "..."
+		}
+		glog.V(100).Infof("Error unmarshalling IPoIBNetwork from almExamples (preview: %s): '%s'", 
+			almExample, err.Error())
 
 		return nil, err
 	}
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 6e966df5fa8cfe73ae3d4742c54f67983bffbb8a and bc6ed070bfdd70ac7acc12633b9bc3d15739e244.

</details>

<details>
<summary>📒 Files selected for processing (5)</summary>

* `README.md` (3 hunks)
* `internal/nvidianetworkconfig/config.go` (1 hunks)
* `internal/wait/networkwait.go` (1 hunks)
* `pkg/nvidianetwork/ipoibnetwork.go` (1 hunks)
* `tests/nvidianetwork/deploy-nno-test.go` (6 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (2)</summary>

<details>
<summary>internal/wait/networkwait.go (5)</summary><blockquote>

<details>
<summary>pkg/clients/clients.go (1)</summary>

* `Settings` (45-60)

</details>
<details>
<summary>pkg/nvidianetwork/ipoibnetwork.go (1)</summary>

* `PullIPoIBNetwork` (101-127)

</details>
<details>
<summary>vendor/github.com/golang/glog/glog.go (2)</summary>

* `V` (401-403)
* `Infof` (507-509)

</details>
<details>
<summary>internal/networkparams/const.go (1)</summary>

* `LogLevel` (8-8)

</details>
<details>
<summary>vendor/github.com/Mellanox/network-operator/api/v1alpha1/nicclusterpolicy_types.go (2)</summary>

* `State` (29-29)
* `StateReady` (34-34)

</details>

</blockquote></details>
<details>
<summary>pkg/nvidianetwork/ipoibnetwork.go (3)</summary><blockquote>

<details>
<summary>vendor/github.com/Mellanox/network-operator/api/v1alpha1/ipoibnetwork_types.go (3)</summary>

* `IPoIBNetwork` (56-62)
* `IPoIBNetworkList` (68-72)
* `IPoIBNetworkCRDName` (24-24)

</details>
<details>
<summary>vendor/github.com/golang/glog/glog.go (4)</summary>

* `V` (401-403)
* `Infof` (507-509)
* `Error` (591-593)
* `Errorf` (615-617)

</details>
<details>
<summary>pkg/msg/msg.go (3)</summary>

* `FailToUpdateNotification` (11-18)
* `FailToUpdateError` (21-28)
* `UndefinedCrdObjectErrString` (6-8)

</details>

</blockquote></details>

</details><details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

<details>
<summary>README.md</summary>

99-99: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

99-99: Bare URL used
null

(MD034, no-bare-urls)

---

100-100: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

101-101: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

102-102: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

103-103: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

104-104: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

105-105: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

106-106: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

107-107: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

108-108: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (10)</summary><blockquote>

<details>
<summary>internal/nvidianetworkconfig/config.go (1)</summary>

`29-32`: **LGTM: New IPoIB configuration fields added correctly**

The new configuration fields for IPoIBNetwork follow the existing naming conventions and environment variable pattern. This implementation properly extends the config struct to support the new IPoIB network feature.

</details>
<details>
<summary>tests/nvidianetwork/deploy-nno-test.go (5)</summary>

`75-78`: **LGTM: IPoIB network variables properly declared**

The variables for IPoIBNetwork configuration are correctly declared following the existing code style.

---

`98-99`: **LGTM: Constants for IPoIBNetwork default name**

The constant for the default IPoIBNetwork name is appropriately defined.

---

`102-104`: **LGTM: Updated test image and interface defaults**

The RDMA test image version is updated, and interface defaults are properly defined.

---

`1057-1148`: **LGTM: IPoIBNetwork creation and validation**

The implementation for creating, configuring, and validating the IPoIBNetwork resource follows the same pattern as the existing MacvlanNetwork code, which is good for consistency and maintainability.

---

`1152-1153`: **LGTM: Updated test label to rdma-shared-dev**

The test label has been updated from "rdma" to "rdma-shared-dev" to better reflect the test's purpose.

</details>
<details>
<summary>README.md (2)</summary>

`99-108`: **LGTM: Updated documentation for IPoIBNetwork support**

The README has been updated with appropriate documentation for the new IPoIBNetwork features, including environment variable descriptions and requirements.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

99-99: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

99-99: Bare URL used
null

(MD034, no-bare-urls)

---

100-100: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

101-101: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

102-102: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

103-103: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

104-104: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

105-105: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

106-106: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

107-107: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

108-108: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

</details>

</details>

---

`175-176`: **LGTM: Updated test label in examples**

The TEST_LABELS environment variable has been updated from 'nno,rdma' to 'nno,rdma-shared-dev' to match the new test labeling.

</details>
<details>
<summary>pkg/nvidianetwork/ipoibnetwork.go (2)</summary>

`1-283`: **LGTM: Overall implementation is well-structured and follows best practices.**

The implementation of the IPoIBNetworkBuilder follows the builder pattern well and provides a comprehensive API for managing IPoIBNetwork custom resources. The code includes proper validation, error handling, and logging throughout.

Key strengths:
1. Consistent error handling pattern
2. Good separation of concerns between building, validation, and API operations
3. Comprehensive functionality covering the entire lifecycle (create, get, update, delete)
4. Proper use of context in API calls

The minor issues noted in other comments don't affect the overall quality of the implementation.

---

`86-88`: <details>
<summary>❓ Verification inconclusive</summary>

**Consider adding namespace to the object key.**

The client.Get call is using only the name in the ObjectKey, which assumes the resource is cluster-scoped. If IPoIBNetwork is namespace-scoped, consider adding the namespace to the ObjectKey.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if IPoIBNetwork is namespace-scoped or cluster-scoped
kubectl get crd ipoibnetworks.mellanox.com -o jsonpath='{.spec.scope}' 2>/dev/null || echo "CRD not found"

# Check how namespaces are handled in similar resources in the codebase
rg -A 2 -B 2 "ObjectKey\{" --type go
```

Length of output: 2430

---

🏁 Script executed:

```shell
#!/bin/bash
# Locate all kubebuilder resource annotations in nvidianetwork v1alpha1 API definitions
rg -n "+kubebuilder:resource" -n pkg/apis/nvidianetwork/v1alpha1

# Specifically inspect the IPoIBNetwork type definition for its resource annotation
rg -n "IPoIBNetwork" -A3 -B3 pkg/apis/nvidianetwork/v1alpha1/ipoinetwork_types.go
```

Length of output: 336

---

```bash
#!/bin/bash
# Find the file containing the IPoIBNetwork type
api_file=$(rg -l "type IPoIBNetwork" -R pkg/apis)
if [ -z "$api_file" ]; then
  echo "IPoIBNetwork type not found under pkg/apis"
  exit 0
fi
echo "Found API definition in: $api_file"
echo

# Show the first 50 lines to inspect kubebuilder annotations (including resource scope)
head -n 50 "$api_file" | sed -n '1,50p'
```

---

```bash
#!/bin/bash
# Locate the file defining IPoIBNetwork
api_file=$(rg -l "type IPoIBNetwork" -R pkg)
if [ -z "$api_file" ]; then
  echo "IPoIBNetwork type definition not found under pkg/"
  exit 0
fi

echo "Found IPoIBNetwork definition in: $api_file"
echo

# Show the top of the file to inspect kubebuilder annotations (including resource scope)
sed -n '1,30p' "$api_file"
```

---

🏁 Script executed:

```shell
#!/bin/bash
# Locate the file defining IPoIBNetwork
api_file=$(rg -l "type IPoIBNetwork" -R pkg)
if [ -z "$api_file" ]; then
  echo "IPoIBNetwork type definition not found under pkg/"
  exit 0
fi

echo "Found IPoIBNetwork definition in: $api_file"
echo

# Show kubebuilder:resource annotation (to inspect scope) and the first 50 lines
grep -n "+kubebuilder:resource" "$api_file" || true
sed -n '1,50p' "$api_file"
```

Length of output: 586

---



</details>

**Verify IPoIBNetwork resource scope**

I wasn’t able to locate the `+kubebuilder:resource` annotation or CRD manifest for IPoIBNetwork in the repo, and the live cluster query didn’t return the CRD. Before treating this as cluster-scoped, please:

- Check the `+kubebuilder:resource` annotation on the `IPoIBNetwork` type under `pkg/apis/nvidianetwork/v1alpha1` (or wherever your APIs live) – look for `scope=Cluster` vs `Namespaced`.  
- Or inspect the CRD YAML in `config/crd` (or equivalent) and confirm `.spec.scope`.

If the resource is namespaced, update the `Get` call in `pkg/nvidianetwork/ipoibnetwork.go` to include the namespace:

```diff
- err := builder.apiClient.Get(context.TODO(), client.ObjectKey{
-   Name: builder.Definition.Name,
- }, IPoIBNetwork)
+ err := builder.apiClient.Get(context.TODO(), client.ObjectKey{
+   Name:      builder.Definition.Name,
+   Namespace: builder.Definition.Namespace,
+ }, IPoIBNetwork)
```

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-26T13:22:50Z
user: login: wabouhamad
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-26T13:29:33Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-26T13:29:54Z

Review comments:
diff_hunk: @@ -204,6 +209,52 @@ var _ = Describe("NNO", Ordered, Label(tsparams.LabelSuite), func() {
 					"NVIDIANETWORK_MACVLANNETWORK_IPAM_GATEWAY value '%s'", macvlanNetworkIPAMGateway)
 			}
 
+			if nvidiaNetworkConfig.IPoIBNetworkName == "" {
+				glog.V(networkparams.LogLevel).Infof("env variable NVIDIANETWORK_IPOIBNETWORK_NAME"+
+					" is not set, will use default name '%s'", nnoIPoIBNetworkNameDefault)
+				ipoibNetworkName = nnoIPoIBNetworkNameDefault
+			} else {
+				ipoibNetworkName = nvidiaNetworkConfig.IPoIBNetworkName
+				glog.V(networkparams.LogLevel).Infof("ipoibNetworkName is set to env variable "+
+					"NVIDIANETWORK_IPOIBNETWORK_NAME value '%s'", ipoibNetworkName)
+			}
+
+			if nvidiaNetworkConfig.IPoIBNetworkIPAMRange == "" {
+				glog.V(networkparams.LogLevel).Infof("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_RANGE" +
+					" is not set, skipping test case execution")
+				glog.V(networkparams.LogLevel).Infof("Skipping testcase:  env variable " +
+					"NVIDIANETWORK_IPOIBNETWORK_IPAM_RANGE is not set")
+				Skip("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_RANGE is not set")
+			} else {
+				ipoibNetworkIPAMRange = nvidiaNetworkConfig.IPoIBNetworkIPAMRange
+				glog.V(networkparams.LogLevel).Infof("ipoibNetworkIPAMRange is set to env variable "+
+					"NVIDIANETWORK_IPOIBNETWORK_IPAM_RANGE value '%s'", ipoibNetworkIPAMRange)
+			}
+
+			if nvidiaNetworkConfig.IPoIBNetworkIPAMExcludeIP1 == "" {
+				glog.V(networkparams.LogLevel).Infof("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP1" +
+					" is not set, skipping test case execution")
+				glog.V(networkparams.LogLevel).Infof("Skipping testcase:  env variable " +
+					"NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP1 is not set")
+				Skip("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP1 is not set")
+			} else {
+				ipoibNetworkIPAMExcludeIP1 = nvidiaNetworkConfig.IPoIBNetworkIPAMExcludeIP1
+				glog.V(networkparams.LogLevel).Infof("ipoibNetworkIPAMExcludeIP1 is set to env variable "+
+					"NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP1 value '%s'", ipoibNetworkIPAMExcludeIP1)
+			}
+
+			if nvidiaNetworkConfig.IPoIBNetworkIPAMExcludeIP2 == "" {
+				glog.V(networkparams.LogLevel).Infof("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP2" +
+					" is not set, skipping test case execution")
+				glog.V(networkparams.LogLevel).Infof("Skipping testcase:  env variable " +
+					"NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP2 is not set")
+				Skip("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP2 is not set")
+			} else {
+				ipoibNetworkIPAMExcludeIP2 = nvidiaNetworkConfig.IPoIBNetworkIPAMExcludeIP1
+				glog.V(networkparams.LogLevel).Infof("ipoibNetworkIPAMExcludeIP1 is set to env variable "+
+					"NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP1 value '%s'", ipoibNetworkIPAMExcludeIP2)
+			}
path: tests/nvidianetwork/deploy-nno-test.go
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**LGTM: IPoIBNetwork environment variable handling**

The code for handling IPoIBNetwork environment variables follows the established pattern in the codebase. It correctly reads the configuration values or uses the defaults when not specified.


However, there's a critical bug in the IPoIBNetworkIPAMExcludeIP2 handling:

```diff
if nvidiaNetworkConfig.IPoIBNetworkIPAMExcludeIP2 == "" {
    glog.V(networkparams.LogLevel).Infof("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP2" +
        " is not set, skipping test case execution")
    glog.V(networkparams.LogLevel).Infof("Skipping testcase:  env variable " +
        "NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP2 is not set")
    Skip("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP2 is not set")
} else {
-    ipoibNetworkIPAMExcludeIP2 = nvidiaNetworkConfig.IPoIBNetworkIPAMExcludeIP1
-    glog.V(networkparams.LogLevel).Infof("ipoibNetworkIPAMExcludeIP1 is set to env variable "+
-        "NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP1 value '%s'", ipoibNetworkIPAMExcludeIP2)
+    ipoibNetworkIPAMExcludeIP2 = nvidiaNetworkConfig.IPoIBNetworkIPAMExcludeIP2
+    glog.V(networkparams.LogLevel).Infof("ipoibNetworkIPAMExcludeIP2 is set to env variable "+
+        "NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP2 value '%s'", ipoibNetworkIPAMExcludeIP2)
}
```

<!-- This is an auto-generated reply by CodeRabbit -->

✅ Addressed in commit e24484f

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-26T13:22:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,282 @@
+package nvidianetwork
+
+import (
+	"context"
+	"fmt"
+	nvidianetworkv1alpha1 "github.com/Mellanox/network-operator/api/v1alpha1"
+
+	"github.com/golang/glog"
+	"github.com/rh-ecosystem-edge/nvidia-ci/pkg/clients"
+	"github.com/rh-ecosystem-edge/nvidia-ci/pkg/msg"
+	k8serrors "k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/util/json"
+	goclient "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+// IPoIBNetworkBuilder  provides a struct for IPoIBNetwork object
+// from the cluster and a IPoIBNetwork definition.
+type IPoIBNetworkBuilder struct {
+	// IPoIBNetworkBuilder  definition. Used to create
+	// IPoIBNetworkBuilder  object with minimum set of required elements.
+	Definition *nvidianetworkv1alpha1.IPoIBNetwork
+	// Created IPoIBNetworkBuilder  object on the cluster.
+	Object *nvidianetworkv1alpha1.IPoIBNetwork
+	// api client to interact with the cluster.
+	apiClient *clients.Settings
+	// errorMsg is processed before IPoIBNetworkBuilder  object is created.
+	errorMsg string
+}
+
+// NewIPoIBNetworkBuilderFromObjectString creates a IPoIBNetworkBuilder  object from CSV alm-examples.
+func NewIPoIBNetworkBuilderFromObjectString(apiClient *clients.Settings, almExample string) *IPoIBNetworkBuilder {
+	glog.V(100).Infof(
+		"Initializing new IPoIBNetworkBuilder  structure from almExample string")
+
+	IPoIBNetwork, err := getIPoIBNetworkFromAlmExample(almExample)
+
+	if err != nil {
+		glog.V(100).Infof(
+			"Error initializing IPoIBNetwork from alm-examples: %s", err.Error())
+
+		builder := IPoIBNetworkBuilder{
+			apiClient: apiClient,
+			errorMsg:  fmt.Sprintf("Error initializing IPoIBNetwork from alm-examples: %s", err.Error()),
+		}
+
+		return &builder
+	}
+
+	if IPoIBNetwork == nil {
+		builder := IPoIBNetworkBuilder{
+			apiClient: apiClient,
+			errorMsg:  "IPoIBNetwork is nil after parsing almExample",
+		}
+		return &builder
+	}
+
+	glog.V(100).Infof(
+		"Initializing new IPoIBNetworkBuilder  structure from almExample string with IPoIBNetwork name: %s",
+		IPoIBNetwork.Name)
+
+	builder := IPoIBNetworkBuilder{
+		apiClient:  apiClient,
+		Definition: IPoIBNetwork,
+	}
+
+	if builder.Definition == nil {
+		glog.V(100).Infof("The IPoIBNetwork object definition is nil")
+
+		builder.errorMsg = "IPoIBNetwork 'Object.Definition' is nil"
+	}
+
+	return &builder
+}
+
+// Get returns IPoIBNetwork object if found.
+func (builder *IPoIBNetworkBuilder) Get() (*nvidianetworkv1alpha1.IPoIBNetwork, error) {
+	if valid, err := builder.validate(); !valid {
+		return nil, err
+	}
+
+	glog.V(100).Infof(
+		"Collecting IPoIBNetwork object %s", builder.Definition.Name)
+
+	IPoIBNetwork := &nvidianetworkv1alpha1.IPoIBNetwork{}
+	err := builder.apiClient.Get(context.TODO(), goclient.ObjectKey{
+		Name: builder.Definition.Name,
+	}, IPoIBNetwork)
+
+	if err != nil {
+		glog.V(100).Infof(
+			"IPoIBNetwork object %s doesn't exist", builder.Definition.Name)
+
+		return nil, err
+	}
+
+	return IPoIBNetwork, err
+}
+
+// PullIPoIBNetwork loads an existing IPoIBNetwork into IPoIBNetworkBuilder  struct.
+func PullIPoIBNetwork(apiClient *clients.Settings, name string) (*IPoIBNetworkBuilder, error) {
+	glog.V(100).Infof("Pulling existing IPoIBNetwork name: %s", name)
+
+	builder := IPoIBNetworkBuilder{
+		apiClient: apiClient,
+		Definition: &nvidianetworkv1alpha1.IPoIBNetwork{
+			ObjectMeta: metav1.ObjectMeta{
+				Name: name,
+			},
+		},
+	}
+
+	if name == "" {
+		glog.V(100).Infof("IPoIBNetwork name is empty")
+
+		builder.errorMsg = "IPoIBNetwork 'name' cannot be empty"
+		return nil, fmt.Errorf(builder.errorMsg)
+	}
+
+	if !builder.Exists() {
+		return nil, fmt.Errorf("IPoIBNetwork object %s doesn't exist", name)
+	}
+
+	builder.Definition = builder.Object
+
+	return &builder, nil
+}
+
+// Exists checks whether the given IPoIBNetwork exists.
+func (builder *IPoIBNetworkBuilder) Exists() bool {
+	if valid, _ := builder.validate(); !valid {
+		return false
+	}
+
+	glog.V(100).Infof(
+		"Checking if IPoIBNetwork %s exists", builder.Definition.Name)
+
+	var err error
+	builder.Object, err = builder.Get()
+
+	if err != nil {
+		glog.V(100).Infof("Failed to collect IPoIBNetwork object due to %s", err.Error())
+	}
+
+	return err == nil || !k8serrors.IsNotFound(err)
+}
+
+// Delete removes a IPoIBNetwork.
+func (builder *IPoIBNetworkBuilder) Delete() (*IPoIBNetworkBuilder, error) {
+	if valid, err := builder.validate(); !valid {
+		return builder, err
+	}
+
+	glog.V(100).Infof("Deleting IPoIBNetwork %s", builder.Definition.Name)
+
+	if !builder.Exists() {
+		return builder, fmt.Errorf("IPoIBNetwork cannot be deleted because it does not exist")
+	}
+
+	err := builder.apiClient.Delete(context.TODO(), builder.Definition)
+
+	if err != nil {
+		return builder, fmt.Errorf("cannot delete IPoIBNetwork: %w", err)
+	}
+
+	builder.Object = nil
+
+	return builder, nil
+}
+
+// Create makes a IPoIBNetwork in the cluster and stores the created object in struct.
+func (builder *IPoIBNetworkBuilder) Create() (*IPoIBNetworkBuilder, error) {
+	if valid, err := builder.validate(); !valid {
+		return builder, err
+	}
+
+	glog.V(100).Infof("Creating the IPoIBNetwork %s", builder.Definition.Name)
+
+	var err error
+	if !builder.Exists() {
+		err = builder.apiClient.Create(context.TODO(), builder.Definition)
+
+		if err == nil {
+			builder.Object = builder.Definition
+		} else {
+			glog.V(100).Infof("Error creating the IPoIBNetwork '%s' : '%s'",
+				builder.Definition.Name, err.Error())
+		}
+	}
+
+	return builder, err
+}
+
+// Update renovates the existing IPoIBNetwork object with the definition in builder.
+func (builder *IPoIBNetworkBuilder) Update(force bool) (*IPoIBNetworkBuilder, error) {
+	if valid, err := builder.validate(); !valid {
+		return builder, err
+	}
+
+	glog.V(100).Infof("Updating the IPoIBNetwork object named:  %s", builder.Definition.Name)
+
+	err := builder.apiClient.Update(context.TODO(), builder.Definition)
+
+	if err != nil {
+		if force {
+			glog.V(100).Infof(msg.FailToUpdateNotification("IPoIBNetwork", builder.Definition.Name))
+
+			builder, err := builder.Delete()
+
+			if err != nil {
+				glog.V(100).Infof(
+					msg.FailToUpdateError("IPoIBNetwork", builder.Definition.Name))
+
+				return nil, err
+			}
path: pkg/nvidianetwork/ipoibnetwork.go
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix potential null pointer dereference in the `Update` method.**

When force update fails, the function sets the builder to nil before returning. This could lead to a null pointer dereference if the caller tries to use the builder after an error. Consider returning the original builder with the error instead.

```diff
 		if force {
 			glog.V(100).Infof(msg.FailToUpdateNotification("IPoIBNetwork", builder.Definition.Name))
 
-			builder, err := builder.Delete()
+			deletedBuilder, deleteErr := builder.Delete()
 
-			if err != nil {
+			if deleteErr != nil {
 				glog.V(100).Infof(
 					msg.FailToUpdateError("IPoIBNetwork", builder.Definition.Name))
 
-				return nil, err
+				return builder, deleteErr
 			}
 
-			return builder.Create()
+			return deletedBuilder.Create()
 		}
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
		if force {
			glog.V(100).Infof(msg.FailToUpdateNotification("IPoIBNetwork", builder.Definition.Name))

			deletedBuilder, deleteErr := builder.Delete()

			if deleteErr != nil {
				glog.V(100).Infof(
					msg.FailToUpdateError("IPoIBNetwork", builder.Definition.Name))

				return builder, deleteErr
			}

			return deletedBuilder.Create()
		}
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-26T13:22:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -204,6 +209,52 @@ var _ = Describe("NNO", Ordered, Label(tsparams.LabelSuite), func() {
 					"NVIDIANETWORK_MACVLANNETWORK_IPAM_GATEWAY value '%s'", macvlanNetworkIPAMGateway)
 			}
 
+			if nvidiaNetworkConfig.IPoIBNetworkName == "" {
+				glog.V(networkparams.LogLevel).Infof("env variable NVIDIANETWORK_IPOIBNETWORK_NAME"+
+					" is not set, will use default name '%s'", nnoIPoIBNetworkNameDefault)
+				ipoibNetworkName = nnoIPoIBNetworkNameDefault
+			} else {
+				ipoibNetworkName = nvidiaNetworkConfig.IPoIBNetworkName
+				glog.V(networkparams.LogLevel).Infof("ipoibNetworkName is set to env variable "+
+					"NVIDIANETWORK_IPOIBNETWORK_NAME value '%s'", ipoibNetworkName)
+			}
+
+			if nvidiaNetworkConfig.IPoIBNetworkIPAMRange == "" {
+				glog.V(networkparams.LogLevel).Infof("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_RANGE" +
+					" is not set, skipping test case execution")
+				glog.V(networkparams.LogLevel).Infof("Skipping testcase:  env variable " +
+					"NVIDIANETWORK_IPOIBNETWORK_IPAM_RANGE is not set")
+				Skip("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_RANGE is not set")
+			} else {
+				ipoibNetworkIPAMRange = nvidiaNetworkConfig.IPoIBNetworkIPAMRange
+				glog.V(networkparams.LogLevel).Infof("ipoibNetworkIPAMRange is set to env variable "+
+					"NVIDIANETWORK_IPOIBNETWORK_IPAM_RANGE value '%s'", ipoibNetworkIPAMRange)
+			}
+
+			if nvidiaNetworkConfig.IPoIBNetworkIPAMExcludeIP1 == "" {
+				glog.V(networkparams.LogLevel).Infof("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP1" +
+					" is not set, skipping test case execution")
+				glog.V(networkparams.LogLevel).Infof("Skipping testcase:  env variable " +
+					"NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP1 is not set")
+				Skip("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP1 is not set")
+			} else {
+				ipoibNetworkIPAMExcludeIP1 = nvidiaNetworkConfig.IPoIBNetworkIPAMExcludeIP1
+				glog.V(networkparams.LogLevel).Infof("ipoibNetworkIPAMExcludeIP1 is set to env variable "+
+					"NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP1 value '%s'", ipoibNetworkIPAMExcludeIP1)
+			}
+
+			if nvidiaNetworkConfig.IPoIBNetworkIPAMExcludeIP2 == "" {
+				glog.V(networkparams.LogLevel).Infof("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP2" +
+					" is not set, skipping test case execution")
+				glog.V(networkparams.LogLevel).Infof("Skipping testcase:  env variable " +
+					"NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP2 is not set")
+				Skip("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP2 is not set")
+			} else {
+				ipoibNetworkIPAMExcludeIP2 = nvidiaNetworkConfig.IPoIBNetworkIPAMExcludeIP1
+				glog.V(networkparams.LogLevel).Infof("ipoibNetworkIPAMExcludeIP1 is set to env variable "+
+					"NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP1 value '%s'", ipoibNetworkIPAMExcludeIP2)
+			}
path: tests/nvidianetwork/deploy-nno-test.go
user: login: wabouhamad
body: Updated in next commit
created_at: 2025-04-26T13:29:33Z
author_association: COLLABORATOR
diff_hunk: @@ -204,6 +209,52 @@ var _ = Describe("NNO", Ordered, Label(tsparams.LabelSuite), func() {
 					"NVIDIANETWORK_MACVLANNETWORK_IPAM_GATEWAY value '%s'", macvlanNetworkIPAMGateway)
 			}
 
+			if nvidiaNetworkConfig.IPoIBNetworkName == "" {
+				glog.V(networkparams.LogLevel).Infof("env variable NVIDIANETWORK_IPOIBNETWORK_NAME"+
+					" is not set, will use default name '%s'", nnoIPoIBNetworkNameDefault)
+				ipoibNetworkName = nnoIPoIBNetworkNameDefault
+			} else {
+				ipoibNetworkName = nvidiaNetworkConfig.IPoIBNetworkName
+				glog.V(networkparams.LogLevel).Infof("ipoibNetworkName is set to env variable "+
+					"NVIDIANETWORK_IPOIBNETWORK_NAME value '%s'", ipoibNetworkName)
+			}
+
+			if nvidiaNetworkConfig.IPoIBNetworkIPAMRange == "" {
+				glog.V(networkparams.LogLevel).Infof("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_RANGE" +
+					" is not set, skipping test case execution")
+				glog.V(networkparams.LogLevel).Infof("Skipping testcase:  env variable " +
+					"NVIDIANETWORK_IPOIBNETWORK_IPAM_RANGE is not set")
+				Skip("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_RANGE is not set")
+			} else {
+				ipoibNetworkIPAMRange = nvidiaNetworkConfig.IPoIBNetworkIPAMRange
+				glog.V(networkparams.LogLevel).Infof("ipoibNetworkIPAMRange is set to env variable "+
+					"NVIDIANETWORK_IPOIBNETWORK_IPAM_RANGE value '%s'", ipoibNetworkIPAMRange)
+			}
+
+			if nvidiaNetworkConfig.IPoIBNetworkIPAMExcludeIP1 == "" {
+				glog.V(networkparams.LogLevel).Infof("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP1" +
+					" is not set, skipping test case execution")
+				glog.V(networkparams.LogLevel).Infof("Skipping testcase:  env variable " +
+					"NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP1 is not set")
+				Skip("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP1 is not set")
+			} else {
+				ipoibNetworkIPAMExcludeIP1 = nvidiaNetworkConfig.IPoIBNetworkIPAMExcludeIP1
+				glog.V(networkparams.LogLevel).Infof("ipoibNetworkIPAMExcludeIP1 is set to env variable "+
+					"NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP1 value '%s'", ipoibNetworkIPAMExcludeIP1)
+			}
+
+			if nvidiaNetworkConfig.IPoIBNetworkIPAMExcludeIP2 == "" {
+				glog.V(networkparams.LogLevel).Infof("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP2" +
+					" is not set, skipping test case execution")
+				glog.V(networkparams.LogLevel).Infof("Skipping testcase:  env variable " +
+					"NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP2 is not set")
+				Skip("env variable NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP2 is not set")
+			} else {
+				ipoibNetworkIPAMExcludeIP2 = nvidiaNetworkConfig.IPoIBNetworkIPAMExcludeIP1
+				glog.V(networkparams.LogLevel).Infof("ipoibNetworkIPAMExcludeIP1 is set to env variable "+
+					"NVIDIANETWORK_IPOIBNETWORK_IPAM_EXCLUDEIP1 value '%s'", ipoibNetworkIPAMExcludeIP2)
+			}
path: tests/nvidianetwork/deploy-nno-test.go
user: login: coderabbitai[bot]
body: @wabouhamad Great! Thank you for addressing the issue with the IPoIBNetworkIPAMExcludeIP2 handling in your next commit. That fix will ensure the correct environment variable values are used.



(\_/)
(^.^)
(> <) "Bug fixed!"

---

<!-- This is an auto-generated comment: tweet message by coderabbit.ai -->

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=Catch%20critical%20bug%20in%20IPoIBNetworkIPAMExcludeIP2%20env%20var%20handling!%20Fix%20ensures%20correct%20variable%20assignment%2C%20preventing%20config%20errors.%20Boost%20your%20code%20quality%20with%20%40coderabbitai%20%F0%9F%90%87%F0%9F%94%8D&url=https%3A%2F%2Fgithub.com%2Frh-ecosystem-edge%2Fnvidia-ci%2Fpull%2F154%23discussion_r2061305324)?

Thank you for using CodeRabbit!

<!-- end of auto-generated comment: tweet message by coderabbit.ai -->




<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@wabouhamad Great! Thank you for addressing the issue with the IPoIBNetworkIPAMExcludeIP2 handling in your next commit. That fix will ensure the correct environment variable values are used.



(\_/)
(^.^)
(> <) "Bug fixed!"

---

&lt;!-- This is an auto-generated comment: tweet message by coderabbit.ai --&gt;

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=Catch%20critical%20bug%20in%20IPoIBNetworkIPAMExcludeIP2%20env%20var%20handling!%20Fix%20ensures%20correct%20variable%20assignment%2C%20preventing%20config%20errors.%20Boost%20your%20code%20quality%20with%20%40coderabbitai%20%F0%9F%90%87%F0%9F%94%8D&url=https%3A%2F%2Fgithub.com%2Frh-ecosystem-edge%2Fnvidia-ci%2Fpull%2F154%23discussion_r2061305324)?

Thank you for using CodeRabbit!

&lt;!-- end of auto-generated comment: tweet message by coderabbit.ai --&gt;

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-26T13:29:54Z
author_association: CONTRIBUTOR

PR comments:
user: login: openshift-ci[bot]
created_at: 2025-04-26T13:17:56Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/rh-ecosystem-edge/nvidia-ci/pull/154#" title="Author self-approved">wabouhamad</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=rh-ecosystem-edge%2Fnvidia-ci).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/rh-ecosystem-edge/nvidia-ci/blob/main/OWNERS)~~ [wabouhamad]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
author_association: NONE
user: login: coderabbitai[bot]
created_at: 2025-04-26T13:17:56Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

This update introduces support for managing IPoIBNetwork custom resources in the NVIDIA Network Operator test suite. It adds new configuration fields for IPoIBNetwork to the configuration struct, implements a builder for creating, updating, and deleting IPoIBNetwork resources, and provides a readiness wait function. The test deployment logic is extended to deploy and validate an IPoIBNetwork resource using environment variables. Documentation is updated to reflect these changes, including new environment variables and usage instructions for both Ethernet and Infiniband RDMA test scenarios.

## Changes

| Files/Paths                                         | Change Summary                                                                                                                           |
|-----------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------|
| README.md                                           | Updated documentation: new/changed environment variables for IPoIBNetwork, corrected typos, revised usage instructions and examples.      |
| internal/nvidianetworkconfig/config.go              | Added four new string fields to `NvidiaNetworkConfig` struct for IPoIBNetwork configuration.                                             |
| internal/wait/networkwait.go                        | Added `IPoIBNetworkReady` function to poll and wait for IPoIBNetwork resources to reach "Ready" state.                                   |
| pkg/nvidianetwork/ipoibnetwork.go                   | New file: Introduced `IPoIBNetworkBuilder` struct and methods for managing IPoIBNetwork CRs (create, update, delete, get, exists, etc.). |
| tests/nvidianetwork/deploy-nno-test.go              | Extended test logic to deploy, configure, and validate IPoIBNetwork resources using new environment variables and builder.               |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Test as Test Suite
    participant Config as NvidiaNetworkConfig
    participant Builder as IPoIBNetworkBuilder
    participant Cluster as Kubernetes Cluster

    Test->>Config: Read IPoIBNetwork env variables
    Test->>Builder: Create IPoIBNetworkBuilder from almExample
    Builder->>Builder: Set name, IPAM range, excluded IPs, master interface
    Builder->>Cluster: Create IPoIBNetwork resource
    Test->>Cluster: Wait for IPoIBNetworkReady
    Cluster-->>Test: IPoIBNetwork in Ready state
    Test->>Cluster: Pull and log IPoIBNetwork status
```

## Possibly related PRs

- rh-ecosystem-edge/nvidia-ci#109: Adds analogous support for MacvlanNetwork resources, modifying similar patterns and files for a different network CR type.
- rh-ecosystem-edge/nvidia-ci#46: Introduces foundational Nvidia Network Operator code and types but without IPoIBNetwork-specific logic; related as a base for network operator features.

## Suggested labels

`lgtm`

## Suggested reviewers

- empovit
- ggordaniRed

## Poem

> A hop and a skip, new networks appear,  
> Infiniband magic, the test suite draws near.  
> With builders and waiters, the cluster’s in tune,  
> IPoIB now ready—oh, what a boon!  
> 🐇✨  
> Docs are refreshed, the code’s all in place,  
> The rabbits of CI set off for the race!

<!-- walkthrough_end -->


---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between bc6ed070bfdd70ac7acc12633b9bc3d15739e244 and e24484ff0257b8a084fed368343236890732885a.

</details>

<details>
<summary>📒 Files selected for processing (5)</summary>

* `README.md` (3 hunks)
* `internal/nvidianetworkconfig/config.go` (1 hunks)
* `internal/wait/networkwait.go` (1 hunks)
* `pkg/nvidianetwork/ipoibnetwork.go` (1 hunks)
* `tests/nvidianetwork/deploy-nno-test.go` (6 hunks)

</details>

<details>
<summary>✅ Files skipped from review due to trivial changes (1)</summary>

* internal/nvidianetworkconfig/config.go

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (2)</summary>

* internal/wait/networkwait.go
* pkg/nvidianetwork/ipoibnetwork.go

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>tests/nvidianetwork/deploy-nno-test.go (6)</summary><blockquote>

<details>
<summary>pkg/global/const.go (1)</summary>

* `UndefinedValue` (5-5)

</details>
<details>
<summary>pkg/nfd/consts.go (2)</summary>

* `CatalogSourceNamespace` (12-12)
* `LogLevel` (20-20)

</details>
<details>
<summary>internal/networkparams/const.go (2)</summary>

* `LogLevel` (8-8)
* `Label` (5-5)

</details>
<details>
<summary>pkg/nvidianetwork/ipoibnetwork.go (2)</summary>

* `NewIPoIBNetworkBuilderFromObjectString` (32-74)
* `PullIPoIBNetwork` (101-127)

</details>
<details>
<summary>internal/inittools/inittools.go (1)</summary>

* `APIClient` (19-19)

</details>
<details>
<summary>internal/wait/networkwait.go (1)</summary>

* `IPoIBNetworkReady` (60-78)

</details>

</blockquote></details>

</details><details>
<summary>🪛 LanguageTool</summary>

<details>
<summary>README.md</summary>

[uncategorized] ~99-~99: Possible missing comma found.
Context: ...l also run the ib_write_bw command with arguments either in CLient or Server mode.  Defau...

(AI_HYDRA_LEO_MISSING_COMMA)

</details>

</details>
<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

<details>
<summary>README.md</summary>

99-99: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

99-99: Bare URL used
null

(MD034, no-bare-urls)

---

100-100: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

101-101: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

102-102: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

103-103: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

104-104: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

105-105: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

106-106: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

107-107: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

108-108: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (9)</summary><blockquote>

<details>
<summary>tests/nvidianetwork/deploy-nno-test.go (4)</summary>

`75-78`: **Well-structured IPoIBNetwork variables**

These new variables are properly defined to support the IPoIBNetwork Custom Resource functionality, following the same pattern as the existing MacvlanNetwork variables.

---

`98-99`: **Good default value for IPoIBNetwork**

The default value for the IPoIBNetwork name is well-defined and matches the documentation in README.md.

---

`1057-1148`: **Well-implemented IPoIBNetwork deployment logic**

The implementation for deploying the IPoIBNetwork custom resource follows the same structure as the MacvlanNetwork deployment. It properly:
1. Creates an IPoIBNetworkBuilder from the almExamples JSON
2. Updates the network parameters from environment variables 
3. Sets the master interface to the Infiniband interface
4. Creates the resource and waits for readiness
5. Retrieves and logs the resource after creation

The code is well-structured and consistent with the project's patterns.

---

`1152-1157`: **Updated test labels and pod prefixes for RDMA shared device**

The test labels and pod name prefixes have been updated to use "rdma-shared-dev", which is consistent with the changes in README.md and clarifies that this test is for RDMA shared device functionality.

</details>
<details>
<summary>README.md (5)</summary>

`99-99`: **Container image version updated**

The RDMA test container image version has been updated from 0.0.1 to 0.0.2. This likely contains important fixes or features for the RDMA test.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 LanguageTool</summary>

[uncategorized] ~99-~99: Possible missing comma found.
Context: ...l also run the ib_write_bw command with arguments either in CLient or Server mode.  Defau...

(AI_HYDRA_LEO_MISSING_COMMA)

</details>
<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

99-99: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

99-99: Bare URL used
null

(MD034, no-bare-urls)

</details>

</details>

---

`105-108`: **Well-documented IPoIBNetwork environment variables**

The added environment variables for IPoIBNetwork configuration are well-documented, matching the implementation in the code. This will help users understand how to configure the IPoIBNetwork test.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

105-105: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

106-106: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

107-107: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

---

108-108: Unordered list style
Expected: asterisk; Actual: dash

(MD004, ul-style)

</details>

</details>

---

`175-175`: **Updated test label in example**

The example is correctly updated to use the new test label 'rdma-shared-dev' instead of 'rdma', matching the implementation in the code.

---

`193-204`: **Clear examples for Ethernet and Infiniband tests**

The example environment variables are now organized into separate sections for Ethernet and Infiniband RDMA tests, which improves clarity and makes it easier for users to configure the tests correctly.

---

`210-210`: **Updated command with new test label**

The ginkgo command is correctly updated to use the 'rdma-shared-dev' label instead of 'rdma', matching the implementation in the code.

</details>

</blockquote></details>

</details>
<!-- internal state start -->


<!-- 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= -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=rh-ecosystem-edge/nvidia-ci&utm_content=154):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: wabouhamad
created_at: 2025-04-26T13:18:31Z
body: /uncc @empovit 

author_association: COLLABORATOR