Repository: red-hat-storage/ocs-ci
Pull request #11922: [GSS]Close loop BZ:2260550 Automation
Author: login: avd-sagare
Labels: name: Verified
description: Mark when PR was verified and log provided
name: team/e2e
description: E2E team related issues/PRs
name: size/L
description: PR that changes 100-499 lines
name: Customer defects
description: Defects automated aspart of GSS closed loop
name: lgtm
Created: 2025-04-15T10:19:13Z | Merged: 2025-05-22T11:22:52Z



Diff:
diff --git a/ocs_ci/framework/pytest_customization/marks.py b/ocs_ci/framework/pytest_customization/marks.py
index d828d5b9237..c2efd013d66 100644
--- a/ocs_ci/framework/pytest_customization/marks.py
+++ b/ocs_ci/framework/pytest_customization/marks.py
@@ -218,6 +218,11 @@
     reason="This test cannot run on MCG-Only deployments",
 )
 
+mcg_only_required = pytest.mark.skipif(
+    config.ENV_DATA.get("mcg_only_deployment", "") is not True,
+    reason="This test runs only on MCG-only deployments",
+)
+
 skipif_fips_enabled = pytest.mark.skipif(
     config.ENV_DATA.get("fips") == "true",
     reason="This test cannot run on FIPS enabled cluster",
diff --git a/ocs_ci/ocs/node.py b/ocs_ci/ocs/node.py
index 53770b69834..cdca7bcebaf 100644
--- a/ocs_ci/ocs/node.py
+++ b/ocs_ci/ocs/node.py
@@ -6,6 +6,7 @@
 from collections import defaultdict
 from operator import itemgetter
 import random
+import json
 
 from subprocess import TimeoutExpired
 from semantic_version import Version
@@ -3307,3 +3308,70 @@ def get_ceph_tools_running_node():
     ct_pod = pod.get_ceph_tools_pod(wait=True, skip_creating_pod=True)
     ct_pod_running_node = ct_pod.data["spec"].get("nodeName")
     return ct_pod_running_node
+
+
+def apply_node_affinity_for_noobaa_pod():
+    """
+    Apply node affinity for noobaa operator pod.
+
+    Returns:
+        bool: True if node affinity applied successfully
+
+    """
+    resource_name = constants.DEFAULT_CLUSTERNAME
+    storagecluster_obj = ocp.OCP(
+        resource_name=resource_name,
+        namespace=config.ENV_DATA["cluster_namespace"],
+        kind=constants.STORAGECLUSTER,
+    )
+
+    nodeaffinity_toleration = {
+        "noobaa-standalone": {
+            "nodeAffinity": {
+                "requiredDuringSchedulingIgnoredDuringExecution": {
+                    "nodeSelectorTerms": [
+                        {
+                            "matchExpressions": [
+                                {"key": "kubernetes.io/hostname", "operator": "Exists"}
+                            ]
+                        }
+                    ]
+                }
+            },
+            "tolerations": [
+                {
+                    "effect": "NoExecute",
+                    "key": "node-role.kubernetes.io/infra",
+                    "value": "reserved",
+                },
+                {
+                    "effect": "NoSchedule",
+                    "key": "node-role.kubernetes.io/infra",
+                    "value": "reserved",
+                },
+            ],
+        }
+    }
+
+    param = {"spec": {"placement": nodeaffinity_toleration}}
+
+    param_str = json.dumps(param)
+    try:
+        storagecluster_obj.patch(params=param_str, format_type="merge")
+        log.info("Successfully applied node affinity")
+    except Exception as e:
+        log.error(f"Failed to apply patch: {e}")
+        return False
+
+    # check node affinity is added in storagecluster or not.
+    sc_dict = storagecluster_obj.get()
+
+    node_affinity = (
+        sc_dict.get("spec", {}).get("placement", {}).get("noobaa-standalone")
+    )
+    if node_affinity:
+        log.info("nodeAffinity is present in 'noobaa-standalone' placement.")
+        return True
+    else:
+        log.error("'noobaa-standalone' placement is missing or empty.")
+        return False
diff --git a/tests/functional/object/mcg/test_standalone_noobaa_pod_node_affinity.py b/tests/functional/object/mcg/test_standalone_noobaa_pod_node_affinity.py
new file mode 100644
index 00000000000..fc7d6b8676c
--- /dev/null
+++ b/tests/functional/object/mcg/test_standalone_noobaa_pod_node_affinity.py
@@ -0,0 +1,61 @@
+import logging
+import pytest
+
+from ocs_ci.framework import config
+from ocs_ci.ocs import ocp, constants
+from ocs_ci.ocs.node import (
+    apply_node_affinity_for_noobaa_pod,
+)
+from ocs_ci.ocs.resources.pod import (
+    wait_for_pods_to_be_running,
+)
+from ocs_ci.framework.pytest_customization.marks import (
+    red_squad,
+    tier4c,
+    mcg_only_required,
+    mcg,
+)
+
+log = logging.getLogger(__name__)
+
+
+@tier4c
+@mcg
+@mcg_only_required
+@red_squad
+class TestNoobaaPodNodeAffinity:
+    @pytest.fixture(scope="class", autouse=True)
+    def teardown(self, request):
+        """
+        This teardown will bring storage cluster with default values.
+        """
+
+        def finalizer():
+            """
+            Finalizer will take care of below activities:
+            1. Removes nodeaffinity to bring storage cluster with default values.
+
+            """
+            resource_name = constants.DEFAULT_CLUSTERNAME
+            storagecluster_obj = ocp.OCP(
+                resource_name=resource_name,
+                namespace=config.ENV_DATA["cluster_namespace"],
+                kind=constants.STORAGECLUSTER,
+            )
+            params = '[{"op": "remove", "path": "/spec/placement/noobaa-standalone"}]'
+            storagecluster_obj.patch(params=params, format_type="json")
+            log.info("Patched storage cluster  back to the default")
+            assert (
+                wait_for_pods_to_be_running()
+            ), "some of the pods didn't came up running"
+
+        request.addfinalizer(finalizer)
+
+    def test_tolerations_on_standalone_noobaa(self):
+        """
+        This test verifies whether standalone noobaa toleration is
+        gets added in storagecluster or not.
+        https://bugzilla.redhat.com/show_bug.cgi?id=2260550#c21
+        """
+
+        assert apply_node_affinity_for_noobaa_pod(), "Failed to apply toleration."


Reviews:
user: login: ocs-ci
body: PR validation on existing cluster 


Cluster Name:           asagare-nbst
Cluster Configuration:  
PR Test Suite:          tier1
PR Test Path:           tests/functional/pod_and_daemons/test_standalone_noobaa_pod_node_affinity.py
Additional Test Params: 
OCP VERSION:            4.19
OCS VERSION:            4.19
tested against branch:  master


 [Job FAILED](http://SHORTENED-URL-ERROR/job/qe-trigger-test-pr/8673//testReport) (installation failed, tests not executed).
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-23T09:15:14Z
user: login: ocs-ci
body: PR validation on existing cluster 


Cluster Name:           asagare-nbst
Cluster Configuration:  
PR Test Suite:          
PR Test Path:           tests/functional/pod_and_daemons/test_standalone_noobaa_pod_node_affinity.py
Additional Test Params: 
OCP VERSION:            4.19
OCS VERSION:            4.19
tested against branch:  master


 [Job UNSTABLE](http://SHORTENED-URL-ERROR/job/qe-trigger-test-pr/8674//testReport) (some or all tests failed).
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-23T12:21:16Z
user: login: ocs-ci
body: PR validation on existing cluster 


Cluster Name:           asagare-nbst
Cluster Configuration:  
PR Test Suite:          
PR Test Path:           tests/functional/pod_and_daemons/test_standalone_noobaa_pod_node_affinity.py
Additional Test Params: 
OCP VERSION:            4.19
OCS VERSION:            4.19
tested against branch:  master


 [Job PASSED](https://url.corp.redhat.com/72ef786).
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-23T14:48:40Z
user: login: PrasadDesala
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T07:56:38Z
user: login: avd-sagare
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T06:40:13Z
user: login: avd-sagare
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T05:56:13Z
user: login: udaysk23
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T06:13:23Z
user: login: ocs-ci
body: PR validation on existing cluster 


Cluster Name:           asagare-nbst12
Cluster Configuration:  
PR Test Suite:          
PR Test Path:           tests/functional/pod_and_daemons/test_standalone_noobaa_pod_node_affinity.py
Additional Test Params: 
OCP VERSION:            4.19
OCS VERSION:            4.19
tested against branch:  master


 [Job PASSED](https://url.corp.redhat.com/0a3fc6f).
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-13T09:30:43Z
user: login: udaysk23
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T11:21:30Z
user: login: udaysk23
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T11:21:48Z
user: login: ocs-ci
body: PR validation on existing cluster 


Cluster Name:           asagare-nbst12
Cluster Configuration:  
PR Test Suite:          
PR Test Path:           tests/functional/pod_and_daemons/test_standalone_noobaa_pod_node_affinity.py
Additional Test Params: 
OCP VERSION:            4.19
OCS VERSION:            4.19
tested against branch:  master


 [Job PASSED](https://url.corp.redhat.com/97fd81c).
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-13T11:55:59Z
user: login: ocs-ci
body: PR validation on existing cluster 


Cluster Name:           asagare-nbst12
Cluster Configuration:  
PR Test Suite:          
PR Test Path:           tests/functional/pod_and_daemons/test_standalone_noobaa_pod_node_affinity.py
Additional Test Params: 
OCP VERSION:            4.19
OCS VERSION:            4.19
tested against branch:  master


 [Job PASSED](https://url.corp.redhat.com/2b6e525).
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-13T12:41:56Z
user: login: ocs-ci
body: PR validation on existing cluster 


Cluster Name:           asagare-nbst12
Cluster Configuration:  conf/ocsci/mcg_only_deployment.yaml
PR Test Suite:          
PR Test Path:           tests/functional/pod_and_daemons/test_standalone_noobaa_pod_node_affinity.py
Additional Test Params: 
OCP VERSION:            4.19
OCS VERSION:            4.19
tested against branch:  master


 [Job PASSED](https://url.corp.redhat.com/3fc3173).
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-13T14:02:11Z
user: login: ocs-ci
body: PR validation 


Cluster Name:           asagare-nbst13
Cluster Configuration:  conf/ocsci/mcg_only_deployment.yaml
PR Test Suite:          
PR Test Path:           tests/functional/pod_and_daemons/test_standalone_noobaa_pod_node_affinity.py
Additional Test Params: 
OCP VERSION:            4.19
OCS VERSION:            4.19
tested against branch:  master


 [Job FAILED](https://url.corp.redhat.com/4b029a1) (installation failed, tests not executed).
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-14T05:17:46Z
user: login: ocs-ci
body: PR validation on existing cluster 


Cluster Name:           ukurundw-mcg-1
Cluster Configuration:  conf/ocsci/mcg_only_deployment.yaml
PR Test Suite:          
PR Test Path:           tests/functional/pod_and_daemons/test_standalone_noobaa_pod_node_affinity.py
Additional Test Params: 
OCP VERSION:            4.19
OCS VERSION:            4.19
tested against branch:  master


 [Job UNSTABLE](https://url.corp.redhat.com/5a438ed) (some or all tests failed).
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-15T15:52:25Z
user: login: ocs-ci
body: PR validation on existing cluster 


Cluster Name:           asagare-mcg
Cluster Configuration:  conf/ocsci/mcg_only_deployment.yaml
PR Test Suite:          
PR Test Path:           tests/functional/pod_and_daemons/test_standalone_noobaa_pod_node_affinity.py
Additional Test Params: 
OCP VERSION:            4.19
OCS VERSION:            4.19
tested against branch:  master


 [Job PASSED](https://url.corp.redhat.com/9422157).
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-20T06:20:59Z
user: login: udaysk23
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T06:33:30Z
user: login: PrasadDesala
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T11:48:56Z
user: login: udaysk23
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T05:27:13Z
user: login: PrasadDesala
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T06:27:52Z
user: login: udaysk23
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T06:48:21Z
user: login: PrasadDesala
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T10:30:09Z
user: login: udaysk23
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T11:15:56Z
user: login: PrasadDesala
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T11:22:32Z

Review comments:
diff_hunk: @@ -0,0 +1,51 @@
+import logging
+import pytest
+
+from ocs_ci.framework import config
+from ocs_ci.ocs import ocp, constants
+from ocs_ci.ocs.node import (
+    apply_node_affinity_for_noobaa_pod,
+)
+from ocs_ci.ocs.resources.pod import (
+    wait_for_pods_to_be_running,
+)
+from ocs_ci.framework.pytest_customization.marks import brown_squad
+
+log = logging.getLogger(__name__)
+
+
+@brown_squad
+class TestNoobaaPodNodeAffinity:
+    @pytest.fixture(scope="session", autouse=True)
+    def teardown(self, request):
+        def finalizer():
+            """
+            Finalizer will take care of below activities:
+            1. Removes nodeaffinity to bring storage cluster with default values.
+
+            """
+            resource_name = constants.DEFAULT_CLUSTERNAME
+            if config.DEPLOYMENT["external_mode"]:
+                resource_name = constants.DEFAULT_CLUSTERNAME_EXTERNAL_MODE
+            storagecluster_obj = ocp.OCP(
+                resource_name=resource_name,
+                namespace=config.ENV_DATA["cluster_namespace"],
+                kind=constants.STORAGECLUSTER,
+            )
+            params = '[{"op": "remove", "path": "/spec/placement/noobaa-standalone"},]'
path: tests/functional/pod_and_daemons/test_standalone_noobaa_pod_node_affinity.py
user: login: PrasadDesala
body: do you need comma at the end? If not mandatory please remove it to have a cleaner code
created_at: 2025-04-25T07:41:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,51 @@
+import logging
+import pytest
+
+from ocs_ci.framework import config
+from ocs_ci.ocs import ocp, constants
+from ocs_ci.ocs.node import (
+    apply_node_affinity_for_noobaa_pod,
+)
+from ocs_ci.ocs.resources.pod import (
+    wait_for_pods_to_be_running,
+)
+from ocs_ci.framework.pytest_customization.marks import brown_squad
+
+log = logging.getLogger(__name__)
+
+
+@brown_squad
+class TestNoobaaPodNodeAffinity:
+    @pytest.fixture(scope="session", autouse=True)
+    def teardown(self, request):
+        def finalizer():
path: tests/functional/pod_and_daemons/test_standalone_noobaa_pod_node_affinity.py
user: login: PrasadDesala
body: add an empty line above this and add doc strings
created_at: 2025-04-25T07:44:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,51 @@
+import logging
+import pytest
+
+from ocs_ci.framework import config
+from ocs_ci.ocs import ocp, constants
+from ocs_ci.ocs.node import (
+    apply_node_affinity_for_noobaa_pod,
+)
+from ocs_ci.ocs.resources.pod import (
+    wait_for_pods_to_be_running,
+)
+from ocs_ci.framework.pytest_customization.marks import brown_squad
+
+log = logging.getLogger(__name__)
+
+
path: tests/functional/pod_and_daemons/test_standalone_noobaa_pod_node_affinity.py
user: login: PrasadDesala
body: missing tier marker
created_at: 2025-04-25T07:44:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,51 @@
+import logging
+import pytest
+
+from ocs_ci.framework import config
+from ocs_ci.ocs import ocp, constants
+from ocs_ci.ocs.node import (
+    apply_node_affinity_for_noobaa_pod,
+)
+from ocs_ci.ocs.resources.pod import (
+    wait_for_pods_to_be_running,
+)
+from ocs_ci.framework.pytest_customization.marks import brown_squad
+
+log = logging.getLogger(__name__)
+
+
+@brown_squad
+class TestNoobaaPodNodeAffinity:
path: tests/functional/pod_and_daemons/test_standalone_noobaa_pod_node_affinity.py
user: login: PrasadDesala
body: you need to also add the relevant marker to allow this test to run only on noobaa standalone
created_at: 2025-04-25T07:46:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,51 @@
+import logging
+import pytest
+
+from ocs_ci.framework import config
+from ocs_ci.ocs import ocp, constants
+from ocs_ci.ocs.node import (
+    apply_node_affinity_for_noobaa_pod,
+)
+from ocs_ci.ocs.resources.pod import (
+    wait_for_pods_to_be_running,
+)
+from ocs_ci.framework.pytest_customization.marks import brown_squad
+
+log = logging.getLogger(__name__)
+
+
+@brown_squad
+class TestNoobaaPodNodeAffinity:
+    @pytest.fixture(scope="session", autouse=True)
+    def teardown(self, request):
+        def finalizer():
+            """
+            Finalizer will take care of below activities:
+            1. Removes nodeaffinity to bring storage cluster with default values.
+
+            """
+            resource_name = constants.DEFAULT_CLUSTERNAME
+            if config.DEPLOYMENT["external_mode"]:
+                resource_name = constants.DEFAULT_CLUSTERNAME_EXTERNAL_MODE
path: tests/functional/pod_and_daemons/test_standalone_noobaa_pod_node_affinity.py
user: login: PrasadDesala
body: this is a standalone noobaa specific bug, do we really need this external mode check?
created_at: 2025-04-25T07:49:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3275,3 +3276,71 @@ def get_ceph_tools_running_node():
     ct_pod = pod.get_ceph_tools_pod(wait=True, skip_creating_pod=True)
     ct_pod_running_node = ct_pod.data["spec"].get("nodeName")
     return ct_pod_running_node
+
+
+def apply_node_affinity_for_noobaa_pod():
+    """
+    Apply node affinity for noobaa operator pod.
+
+    """
+    resource_name = constants.DEFAULT_CLUSTERNAME
+    if config.DEPLOYMENT["external_mode"]:
+        resource_name = constants.DEFAULT_CLUSTERNAME_EXTERNAL_MODE
path: ocs_ci/ocs/node.py
user: login: PrasadDesala
body: this is a standalone noobaa specific bug, do we really need this external mode check?
created_at: 2025-04-25T07:49:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3275,3 +3276,71 @@ def get_ceph_tools_running_node():
     ct_pod = pod.get_ceph_tools_pod(wait=True, skip_creating_pod=True)
     ct_pod_running_node = ct_pod.data["spec"].get("nodeName")
     return ct_pod_running_node
+
+
+def apply_node_affinity_for_noobaa_pod():
+    """
+    Apply node affinity for noobaa operator pod.
+
+    """
+    resource_name = constants.DEFAULT_CLUSTERNAME
+    if config.DEPLOYMENT["external_mode"]:
+        resource_name = constants.DEFAULT_CLUSTERNAME_EXTERNAL_MODE
+
+    storagecluster_obj = ocp.OCP(
+        resource_name=resource_name,
+        namespace=config.ENV_DATA["cluster_namespace"],
+        kind=constants.STORAGECLUSTER,
+    )
+
+    nodeaffinity = {
path: ocs_ci/ocs/node.py
user: login: PrasadDesala
body: you are adding both node affinity and tolerations, please update the var accordingly
created_at: 2025-04-25T07:53:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3275,3 +3276,71 @@ def get_ceph_tools_running_node():
     ct_pod = pod.get_ceph_tools_pod(wait=True, skip_creating_pod=True)
     ct_pod_running_node = ct_pod.data["spec"].get("nodeName")
     return ct_pod_running_node
+
+
+def apply_node_affinity_for_noobaa_pod():
+    """
+    Apply node affinity for noobaa operator pod.
+
path: ocs_ci/ocs/node.py
user: login: PrasadDesala
body: add doc string for return
created_at: 2025-04-25T07:53:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3275,3 +3276,71 @@ def get_ceph_tools_running_node():
     ct_pod = pod.get_ceph_tools_pod(wait=True, skip_creating_pod=True)
     ct_pod_running_node = ct_pod.data["spec"].get("nodeName")
     return ct_pod_running_node
+
+
+def apply_node_affinity_for_noobaa_pod():
+    """
+    Apply node affinity for noobaa operator pod.
+
+    """
+    resource_name = constants.DEFAULT_CLUSTERNAME
+    if config.DEPLOYMENT["external_mode"]:
+        resource_name = constants.DEFAULT_CLUSTERNAME_EXTERNAL_MODE
+
+    storagecluster_obj = ocp.OCP(
+        resource_name=resource_name,
+        namespace=config.ENV_DATA["cluster_namespace"],
+        kind=constants.STORAGECLUSTER,
+    )
+
+    nodeaffinity = {
+        "noobaa-standalone": {
+            "nodeAffinity": {
+                "requiredDuringSchedulingIgnoredDuringExecution": {
+                    "nodeSelectorTerms": [
+                        {
+                            "matchExpressions": [
+                                {"key": "kubernetes.io/hostname", "operator": "Exists"}
+                            ]
+                        }
+                    ]
+                }
+            },
+            "tolerations": [
+                {
+                    "effect": "NoExecute",
+                    "key": "node-role.kubernetes.io/infra",
+                    "value": "reserved",
+                },
+                {
+                    "effect": "NoSchedule",
+                    "key": "node-role.kubernetes.io/infra",
+                    "value": "reserved",
+                },
+            ],
+        }
+    }
+
+    param = {"spec": {"placement": nodeaffinity}}
+
+    param_str = json.dumps(param)
+    try:
+        storagecluster_obj.patch(params=param_str, format_type="merge")
+        log.info("Successfully applied node affinity")
+    except Exception as e:
+        log.error(f"Failed to apply patch: {e}")
+        return False
+
+    # check node affinity is added in storagecluster or not.
+    sc_dict = storagecluster_obj.get()
+    try:
+        node_affinity = sc_dict["spec"]["placement"]["noobaa-standalone"]
path: ocs_ci/ocs/node.py
user: login: PrasadDesala
body: consider using get to avoid KeyError
created_at: 2025-04-25T07:54:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3275,3 +3276,71 @@ def get_ceph_tools_running_node():
     ct_pod = pod.get_ceph_tools_pod(wait=True, skip_creating_pod=True)
     ct_pod_running_node = ct_pod.data["spec"].get("nodeName")
     return ct_pod_running_node
+
+
+def apply_node_affinity_for_noobaa_pod():
+    """
+    Apply node affinity for noobaa operator pod.
+
+    """
+    resource_name = constants.DEFAULT_CLUSTERNAME
+    if config.DEPLOYMENT["external_mode"]:
+        resource_name = constants.DEFAULT_CLUSTERNAME_EXTERNAL_MODE
+
+    storagecluster_obj = ocp.OCP(
+        resource_name=resource_name,
+        namespace=config.ENV_DATA["cluster_namespace"],
+        kind=constants.STORAGECLUSTER,
+    )
+
+    nodeaffinity = {
+        "noobaa-standalone": {
+            "nodeAffinity": {
+                "requiredDuringSchedulingIgnoredDuringExecution": {
+                    "nodeSelectorTerms": [
+                        {
+                            "matchExpressions": [
+                                {"key": "kubernetes.io/hostname", "operator": "Exists"}
+                            ]
+                        }
+                    ]
+                }
+            },
+            "tolerations": [
+                {
+                    "effect": "NoExecute",
+                    "key": "node-role.kubernetes.io/infra",
+                    "value": "reserved",
+                },
+                {
+                    "effect": "NoSchedule",
+                    "key": "node-role.kubernetes.io/infra",
+                    "value": "reserved",
+                },
+            ],
+        }
+    }
+
+    param = {"spec": {"placement": nodeaffinity}}
+
+    param_str = json.dumps(param)
+    try:
+        storagecluster_obj.patch(params=param_str, format_type="merge")
+        log.info("Successfully applied node affinity")
+    except Exception as e:
+        log.error(f"Failed to apply patch: {e}")
+        return False
+
+    # check node affinity is added in storagecluster or not.
+    sc_dict = storagecluster_obj.get()
+    try:
+        node_affinity = sc_dict["spec"]["placement"]["noobaa-standalone"]
+        if node_affinity:
+            log.info("nodeAffinity is present in 'noobaa-standalone' placement.")
+            return True
+        else:
+            log.error("nodeAffinity is empty.")
+            return False
+    except KeyError:
+        log.error("'noobaa-standalone' is not present in placement.")
+        return False
path: ocs_ci/ocs/node.py
user: login: PrasadDesala
body: ```suggestion
node_affinity = sc_dict.get("spec", {}).get("placement", {}).get("noobaa-standalone")
if node_affinity:
    log.info("nodeAffinity is present in 'noobaa-standalone' placement.")
    return True
else:
    log.error("'noobaa-standalone' placement is missing or empty.")
    return False

```
created_at: 2025-04-25T07:56:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,51 @@
+import logging
+import pytest
+
+from ocs_ci.framework import config
+from ocs_ci.ocs import ocp, constants
+from ocs_ci.ocs.node import (
+    apply_node_affinity_for_noobaa_pod,
+)
+from ocs_ci.ocs.resources.pod import (
+    wait_for_pods_to_be_running,
+)
+from ocs_ci.framework.pytest_customization.marks import brown_squad
+
+log = logging.getLogger(__name__)
+
+
path: tests/functional/pod_and_daemons/test_standalone_noobaa_pod_node_affinity.py
user: login: avd-sagare
body: Added Markers.
created_at: 2025-05-09T06:39:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,51 @@
+import logging
+import pytest
+
+from ocs_ci.framework import config
+from ocs_ci.ocs import ocp, constants
+from ocs_ci.ocs.node import (
+    apply_node_affinity_for_noobaa_pod,
+)
+from ocs_ci.ocs.resources.pod import (
+    wait_for_pods_to_be_running,
+)
+from ocs_ci.framework.pytest_customization.marks import brown_squad
+
+log = logging.getLogger(__name__)
+
+
+@brown_squad
+class TestNoobaaPodNodeAffinity:
path: tests/functional/pod_and_daemons/test_standalone_noobaa_pod_node_affinity.py
user: login: avd-sagare
body: Added Markert
created_at: 2025-05-09T06:39:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,51 @@
+import logging
+import pytest
+
+from ocs_ci.framework import config
+from ocs_ci.ocs import ocp, constants
+from ocs_ci.ocs.node import (
+    apply_node_affinity_for_noobaa_pod,
+)
+from ocs_ci.ocs.resources.pod import (
+    wait_for_pods_to_be_running,
+)
+from ocs_ci.framework.pytest_customization.marks import brown_squad
+
+log = logging.getLogger(__name__)
+
+
+@brown_squad
+class TestNoobaaPodNodeAffinity:
+    @pytest.fixture(scope="session", autouse=True)
+    def teardown(self, request):
+        def finalizer():
path: tests/functional/pod_and_daemons/test_standalone_noobaa_pod_node_affinity.py
user: login: avd-sagare
body: Added doc string
created_at: 2025-05-09T06:40:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3275,3 +3276,71 @@ def get_ceph_tools_running_node():
     ct_pod = pod.get_ceph_tools_pod(wait=True, skip_creating_pod=True)
     ct_pod_running_node = ct_pod.data["spec"].get("nodeName")
     return ct_pod_running_node
+
+
+def apply_node_affinity_for_noobaa_pod():
+    """
+    Apply node affinity for noobaa operator pod.
+
path: ocs_ci/ocs/node.py
user: login: avd-sagare
body: Done
created_at: 2025-05-13T05:56:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,59 @@
+import logging
+import pytest
+
+from ocs_ci.framework import config
+from ocs_ci.ocs import ocp, constants
+from ocs_ci.ocs.node import (
+    apply_node_affinity_for_noobaa_pod,
+)
+from ocs_ci.ocs.resources.pod import (
+    wait_for_pods_to_be_running,
+)
+from ocs_ci.framework.pytest_customization.marks import (
+    brown_squad,
+    tier1,
+    mcg_only_required,
+)
+
+log = logging.getLogger(__name__)
+
+
+@tier1
+@mcg_only_required
+@brown_squad
+class TestNoobaaPodNodeAffinity:
+    @pytest.fixture(scope="session", autouse=True)
path: tests/functional/pod_and_daemons/test_standalone_noobaa_pod_node_affinity.py
user: login: udaysk23
body: Please change scope from session to class
created_at: 2025-05-13T06:07:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,59 @@
+import logging
+import pytest
+
+from ocs_ci.framework import config
+from ocs_ci.ocs import ocp, constants
+from ocs_ci.ocs.node import (
+    apply_node_affinity_for_noobaa_pod,
+)
+from ocs_ci.ocs.resources.pod import (
+    wait_for_pods_to_be_running,
+)
+from ocs_ci.framework.pytest_customization.marks import (
+    brown_squad,
+    tier1,
+    mcg_only_required,
+)
+
+log = logging.getLogger(__name__)
+
+
+@tier1
+@mcg_only_required
+@brown_squad
+class TestNoobaaPodNodeAffinity:
+    @pytest.fixture(scope="session", autouse=True)
+    def teardown(self, request):
+        """
+        This teardown will bring storage cluster with default values.
+        """
+
+        def finalizer():
+            """
+            Finalizer will take care of below activities:
+            1. Removes nodeaffinity to bring storage cluster with default values.
+
+            """
+            resource_name = constants.DEFAULT_CLUSTERNAME
+            storagecluster_obj = ocp.OCP(
+                resource_name=resource_name,
+                namespace=config.ENV_DATA["cluster_namespace"],
+                kind=constants.STORAGECLUSTER,
+            )
+            params = '[{"op": "remove", "path": "/spec/placement/noobaa-standalone"}]'
+            storagecluster_obj.patch(params=params, format_type="json")
+            log.info("Patched storage cluster  back to the default")
+            assert (
+                wait_for_pods_to_be_running()
+            ), "some of the pods didn't came up running"
+
+        request.addfinalizer(finalizer)
path: tests/functional/pod_and_daemons/test_standalone_noobaa_pod_node_affinity.py
user: login: udaysk23
body: It would be great if we write this code inside of `apply_node_affinity_for_noobaa_pod()` method present in `ocs_ci/ocs/node.py` file.
created_at: 2025-05-13T06:12:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,59 @@
+import logging
+import pytest
+
+from ocs_ci.framework import config
+from ocs_ci.ocs import ocp, constants
+from ocs_ci.ocs.node import (
+    apply_node_affinity_for_noobaa_pod,
+)
+from ocs_ci.ocs.resources.pod import (
+    wait_for_pods_to_be_running,
+)
+from ocs_ci.framework.pytest_customization.marks import (
+    brown_squad,
+    tier1,
+    mcg_only_required,
+)
+
+log = logging.getLogger(__name__)
+
+
+@tier1
+@mcg_only_required
+@brown_squad
+class TestNoobaaPodNodeAffinity:
+    @pytest.fixture(scope="session", autouse=True)
+    def teardown(self, request):
+        """
+        This teardown will bring storage cluster with default values.
+        """
+
+        def finalizer():
+            """
+            Finalizer will take care of below activities:
+            1. Removes nodeaffinity to bring storage cluster with default values.
+
+            """
+            resource_name = constants.DEFAULT_CLUSTERNAME
+            storagecluster_obj = ocp.OCP(
+                resource_name=resource_name,
+                namespace=config.ENV_DATA["cluster_namespace"],
+                kind=constants.STORAGECLUSTER,
+            )
+            params = '[{"op": "remove", "path": "/spec/placement/noobaa-standalone"}]'
+            storagecluster_obj.patch(params=params, format_type="json")
+            log.info("Patched storage cluster  back to the default")
+            assert (
+                wait_for_pods_to_be_running()
+            ), "some of the pods didn't came up running"
+
+        request.addfinalizer(finalizer)
path: tests/functional/pod_and_daemons/test_standalone_noobaa_pod_node_affinity.py
user: login: udaysk23
body: Please ignore this review comment
created_at: 2025-05-13T11:21:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,59 @@
+import logging
+import pytest
+
+from ocs_ci.framework import config
+from ocs_ci.ocs import ocp, constants
+from ocs_ci.ocs.node import (
+    apply_node_affinity_for_noobaa_pod,
+)
+from ocs_ci.ocs.resources.pod import (
+    wait_for_pods_to_be_running,
+)
+from ocs_ci.framework.pytest_customization.marks import (
+    brown_squad,
+    tier1,
+    mcg_only_required,
+)
+
+log = logging.getLogger(__name__)
+
+
+@tier1
+@mcg_only_required
+@brown_squad
+class TestNoobaaPodNodeAffinity:
path: tests/functional/pod_and_daemons/test_standalone_noobaa_pod_node_affinity.py
user: login: PrasadDesala
body: IIRC, we have a separate job for noobaa standalone. Should this test be moved to https://github.com/red-hat-storage/noobaa-sa-ci/tree/main/tests? Please check with Red Squad
created_at: 2025-05-21T11:48:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,59 @@
+import logging
+import pytest
+
+from ocs_ci.framework import config
+from ocs_ci.ocs import ocp, constants
+from ocs_ci.ocs.node import (
+    apply_node_affinity_for_noobaa_pod,
+)
+from ocs_ci.ocs.resources.pod import (
+    wait_for_pods_to_be_running,
+)
+from ocs_ci.framework.pytest_customization.marks import (
+    brown_squad,
+    tier1,
+    mcg_only_required,
+)
+
+log = logging.getLogger(__name__)
+
+
+@tier1
+@mcg_only_required
+@brown_squad
+class TestNoobaaPodNodeAffinity:
path: tests/functional/pod_and_daemons/test_standalone_noobaa_pod_node_affinity.py
user: login: udaysk23
body: @PrasadDesala Noobaa-SA-CI is for rpm based noobaa validation repo. This test is not suitable for that repo.
created_at: 2025-05-22T05:27:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,59 @@
+import logging
+import pytest
+
+from ocs_ci.framework import config
+from ocs_ci.ocs import ocp, constants
+from ocs_ci.ocs.node import (
+    apply_node_affinity_for_noobaa_pod,
+)
+from ocs_ci.ocs.resources.pod import (
+    wait_for_pods_to_be_running,
+)
+from ocs_ci.framework.pytest_customization.marks import (
+    brown_squad,
+    tier1,
+    mcg_only_required,
+)
+
+log = logging.getLogger(__name__)
+
+
+@tier1
+@mcg_only_required
+@brown_squad
+class TestNoobaaPodNodeAffinity:
path: tests/functional/pod_and_daemons/test_standalone_noobaa_pod_node_affinity.py
user: login: PrasadDesala
body: ok, what about the placement of this test and tier? should we move it under tests/functional/object/mcg/?  
IIUC, almost all the mcg tests are placed in under the above dir
created_at: 2025-05-22T06:27:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,59 @@
+import logging
+import pytest
+
+from ocs_ci.framework import config
+from ocs_ci.ocs import ocp, constants
+from ocs_ci.ocs.node import (
+    apply_node_affinity_for_noobaa_pod,
+)
+from ocs_ci.ocs.resources.pod import (
+    wait_for_pods_to_be_running,
+)
+from ocs_ci.framework.pytest_customization.marks import (
+    brown_squad,
+    tier1,
+    mcg_only_required,
+)
+
+log = logging.getLogger(__name__)
+
+
+@tier1
+@mcg_only_required
+@brown_squad
+class TestNoobaaPodNodeAffinity:
path: tests/functional/pod_and_daemons/test_standalone_noobaa_pod_node_affinity.py
user: login: udaysk23
body: Yes, We can move this test to mcg dir
created_at: 2025-05-22T06:48:21Z
author_association: CONTRIBUTOR

PR comments:
user: login: openshift-ci[bot]
created_at: 2025-05-22T11:22:37Z
body: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/red-hat-storage/ocs-ci/pull/11922#" title="Author self-approved">avd-sagare</a>*, *<a href="https://github.com/red-hat-storage/ocs-ci/pull/11922#pullrequestreview-2860850195" title="Approved">PrasadDesala</a>*, *<a href="https://github.com/red-hat-storage/ocs-ci/pull/11922#pullrequestreview-2860826860" title="Approved">udaysk23</a>*


The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=red-hat-storage%2Focs-ci).

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

- **[OWNERS](https://github.com/red-hat-storage/ocs-ci/blob/master/OWNERS)**

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