From 8d095016297b02b52d7bc260473b047d5159ce51 Mon Sep 17 00:00:00 2001 From: Shrivaibavi Raghaventhiran Date: Mon, 19 Feb 2024 17:55:30 +0530 Subject: [PATCH] New fixture to deploy apps and new func to delete app from UI is added Signed-off-by: Shrivaibavi Raghaventhiran --- ocs_ci/helpers/dr_helpers.py | 2 +- ocs_ci/helpers/dr_helpers_ui.py | 44 +++++++ ocs_ci/ocs/dr/dr_workload.py | 20 +-- ocs_ci/ocs/ui/views.py | 6 + tests/conftest.py | 123 +++++++++++++----- ...est_multiple_apps_failover_and_relocate.py | 6 +- 6 files changed, 158 insertions(+), 43 deletions(-) diff --git a/ocs_ci/helpers/dr_helpers.py b/ocs_ci/helpers/dr_helpers.py index c0fb946cd700..18d605b81b56 100644 --- a/ocs_ci/helpers/dr_helpers.py +++ b/ocs_ci/helpers/dr_helpers.py @@ -822,7 +822,7 @@ def get_all_drclusters(): list: List of all DRClusters """ config.switch_acm_ctx() - drclusters_obj = ocp.OCP(kind="DRCluster") + drclusters_obj = ocp.OCP(kind=constants.DRCLUSTER) drclusters = [] for cluster in drclusters_obj.get().get("items"): drclusters.append(cluster.get("metadata").get("name")) diff --git a/ocs_ci/helpers/dr_helpers_ui.py b/ocs_ci/helpers/dr_helpers_ui.py index 9bc6638f6e2d..9bd5476675d0 100644 --- a/ocs_ci/helpers/dr_helpers_ui.py +++ b/ocs_ci/helpers/dr_helpers_ui.py @@ -392,3 +392,47 @@ def verify_failover_relocate_status_ui( log.info("Close button found") acm_obj.do_click_by_xpath("//*[text()='Close']") log.info("Data policy modal page closed") + + +def delete_application_ui(acm_obj, workload_to_delete=None, timeout=60): + """ + Function to delete specified workloads on ACM UI + + Args: + acm_obj (AcmAddClusters): ACM Page Navigator Class + workload_to_delete (str): Specify the workload to delete, otherwise none will be deleted + timeout (int): timeout to wait for certain elements to be found on the ACM UI + + """ + if workload_to_delete: + ocp_version = get_ocp_version() + acm_loc = locators[ocp_version]["acm_page"] + acm_obj.navigate_applications_page() + log.info("Click on search bar") + acm_obj.do_click(acm_loc["search-bar"]) + log.info("Clear existing text from search bar if any") + acm_obj.do_clear(acm_loc["search-bar"]) + log.info("Enter the workload to be searched") + acm_obj.do_send_keys(acm_loc["search-bar"], text=workload_to_delete) + log.info("Click on kebab menu option") + acm_obj.do_click(acm_loc["kebab-action"], enable_screenshot=True) + acm_obj.do_click(acm_loc["delete-app"], enable_screenshot=True, timeout=timeout) + if not acm_obj.get_checkbox_status(acm_loc["remove-app-resources"]): + acm_obj.select_checkbox_status(acm_loc["remove-app-resources"], status=True) + initiate_btn = acm_obj.find_an_element_by_xpath( + "//button[@id='modal-intiate-action']" + ) + aria_disabled = initiate_btn.get_attribute("aria-disabled") + if aria_disabled == "false": + acm_obj.do_click(acm_loc["delete"], enable_screenshot=True, timeout=timeout) + close_action_modal = acm_obj.wait_until_expected_text_is_found( + acm_loc["close-action-modal"], expected_text="Close", timeout=timeout + ) + if close_action_modal: + log.info("Close button found") + acm_obj.do_click_by_xpath("//*[text()='Close']") + log.info("Closed deletion of application page") + else: + log.error("No workload specified to delete, Please specify") + return False + return True diff --git a/ocs_ci/ocs/dr/dr_workload.py b/ocs_ci/ocs/dr/dr_workload.py index d7b2f3db36b5..03f7b49c7a37 100644 --- a/ocs_ci/ocs/dr/dr_workload.py +++ b/ocs_ci/ocs/dr/dr_workload.py @@ -164,6 +164,9 @@ def __init__(self, **kwargs): self.drpc_yaml_file = os.path.join( self.workload_subscription_dir, self.workload_name, "drpc.yaml" ) + self.app_yaml_file = os.path.join( + self.workload_subscription_dir, self.workload_name, "app.yaml" + ) self.namespace_yaml_file = os.path.join( self.workload_subscription_dir, self.workload_name, "namespace.yaml" ) @@ -189,17 +192,12 @@ def deploy_workload(self, primary_cluster=None, secondary_cluster=None): self._deploy_prereqs() workload_namespaces = [] - # By default it deploys apps on primary cluster if not set to false - clusters = [] - if primary_cluster: - clusters = [self.preferred_primary_cluster] + # By default, it deploys apps on primary cluster if not set to false + clusters = [self.preferred_primary_cluster] if secondary_cluster: clusters = [self.preferred_secondary_cluster] if primary_cluster and secondary_cluster: - clusters = [ - self.preferred_primary_cluster, - self.preferred_secondary_cluster, - ] + clusters.append(self.preferred_secondary_cluster) for cluster in clusters: # load workload-repo namespace.yaml @@ -276,6 +274,12 @@ def deploy_workload(self, primary_cluster=None, secondary_cluster=None): subscription_yaml_data, self.subscription_yaml_file ) + # load app yaml + app_yaml_data = templating.load_yaml(self.app_yaml_file) + app_yaml_data["metadata"]["name"] = helpers.create_unique_resource_name( + resource_type="app", resource_description="busybox" + ) + # load workload kustomization.yaml workload_kustomization_yaml_data = templating.load_yaml( self.workload_kustomization_yaml_file diff --git a/ocs_ci/ocs/ui/views.py b/ocs_ci/ocs/ui/views.py index ac1c7ff39eab..102fa2b270ba 100644 --- a/ocs_ci/ocs/ui/views.py +++ b/ocs_ci/ocs/ui/views.py @@ -1002,6 +1002,12 @@ ), "failover-app": ("//button[normalize-space()='Failover application']", By.XPATH), "relocate-app": ("//button[normalize-space()='Relocate application']", By.XPATH), + "delete-app": ("//button[normalize-space()='Delete application']", By.XPATH), + # "remove-app-resources": ( + # "//button[@type='checkbox'][@class='pf-c-check__input'][@id='remove-app-resources']", By.XPATH + # ), + "remove-app-resources": ("@id='remove-app-resources'", By.ID), + "delete": ("//button[@type='button'][@class='pf-c-button pf-m-danger']", By.XPATH), "policy-dropdown": ("#drPolicy-selection", By.CSS_SELECTOR), "select-policy": ('//*[text()="{}"]', By.XPATH), "target-cluster-dropdown": ( diff --git a/tests/conftest.py b/tests/conftest.py index a4dc5280e6f2..c21728e6e57f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -32,7 +32,7 @@ ) from ocs_ci.helpers.proxy import update_container_with_proxy_env from ocs_ci.ocs import constants, defaults, fio_artefacts, node, ocp, platform_nodes -from ocs_ci.ocs.acm.acm import login_to_acm +from ocs_ci.ocs.acm.acm import login_to_acm, AcmAddClusters from ocs_ci.ocs.awscli_pod import create_awscli_pod, awscli_pod_cleanup from ocs_ci.ocs.bucket_utils import ( craft_s3_command, @@ -146,7 +146,7 @@ run_cmd, ceph_health_check_multi_storagecluster_external, ) -from ocs_ci.helpers import helpers, dr_helpers +from ocs_ci.helpers import helpers, dr_helpers, dr_helpers_ui from ocs_ci.helpers.helpers import ( create_unique_resource_name, create_ocs_object_from_kind_and_name, @@ -6559,34 +6559,6 @@ def dr_workload(request): instances = [] ctx = [] - def factory( - num_of_subscription=1, - num_of_appset=0, - appset_model="pull", - pvc_interface=constants.CEPHBLOCKPOOL, - switch_ctx=None, - ): - """ - Args: - num_of_subscription (int): Number of Subscription type workload to be created on each cluster - num_of_appset (int): Number of ApplicationSet type workload to be created on each cluster - pvc_interface (str): 'CephBlockPool' or 'CephFileSystem'. - This decides whether a RBD based or CephFS based resource is created. RBD is default. - switch_ctx (int): The cluster index by the cluster name - appset_model (str): Appset Gitops deployment now supports "pull" model starting ACM 2.10 which is now the - default selection in addition to "push" model. - This decides whether a RBD based or CephFS based resource is created. RBD is default - - Raises: - ResourceNotDeleted: In case workload resources not deleted properly - - Returns: - instances (list): objects of appset workload class - - """ - instances = [] - ctx = [] - def factory( num_of_subscription=1, num_of_appset=0, @@ -6667,6 +6639,97 @@ def teardown(): return factory +@pytest.fixture(scope="class") +def dr_workloads_on_managed_clusters(request): + """ + Deploying subscription apps on both primary and secondary managed clusters + """ + + primary_cluster_instances = [] + secondary_cluster_instances = [] + + def factory( + num_of_subscription=1, + pvc_interface=constants.CEPHBLOCKPOOL, + primary_cluster=None, + secondary_cluster=None, + ): + """ + Args: + num_of_subscription (int): Number of Subscription type workload to be created on each cluster + pvc_interface (str): 'CephBlockPool' or 'CephFileSystem'. + This decides whether a RBD based or CephFS based resource is created. RBD is default + primary_cluster (bool): True if apps to be deployed on primary cluster, false otherwise + secondary_cluster (bool): True if apps to be deployed on secondary cluster, false otherwise + + Raises: + ResourceNotDeleted: In case workload resources not deleted properly + + Returns: + instances (list): objects of appset workload class + primary_cluster_instances (list): objects of subscription workload class on primary + secondary_cluster_instances (list): objects of subscription workload class on secondary + + """ + total_pvc_count = 0 + workload_key = "dr_workload_subscription" + if pvc_interface == constants.CEPHFILESYSTEM: + workload_key = "dr_workload_subscription_cephfs" + + if primary_cluster: + for index in range(num_of_subscription): + workload_details = ocsci_config.ENV_DATA[workload_key][index] + + workload = BusyBox( + workload_dir=workload_details["workload_dir"], + workload_pod_count=workload_details["pod_count"], + workload_pvc_count=workload_details["pvc_count"], + ) + primary_cluster_instances.append(workload) + total_pvc_count += workload_details["pvc_count"] + workload.deploy_workload( + primary_cluster=primary_cluster, secondary_cluster=None + ) + + if secondary_cluster: + for index in range(num_of_subscription): + workload_details = ocsci_config.ENV_DATA[workload_key][index] + + workload = BusyBox( + workload_dir=workload_details["workload_dir"], + workload_pod_count=workload_details["pod_count"], + workload_pvc_count=workload_details["pvc_count"], + ) + secondary_cluster_instances.append(workload) + total_pvc_count += workload_details["pvc_count"] + workload.deploy_workload( + primary_cluster=None, secondary_cluster=secondary_cluster + ) + + return primary_cluster_instances, secondary_cluster_instances + + def teardown(): + failed_to_delete = False + acm_obj = AcmAddClusters() + instances = [primary_cluster_instances, secondary_cluster_instances] + for instance in instances: + for workload in instance: + try: + dr_helpers_ui.delete_application_ui( + acm_obj, workload_to_delete=workload.name + ) + except ResourceNotDeleted: + failed_to_delete = True + + if failed_to_delete: + raise ResourceNotDeleted( + "Workload deletion was unsuccessful. Leftover resources were removed from the managed clusters." + ) + + request.addfinalizer(teardown) + return factory + + @pytest.fixture() def cnv_dr_workload(request): """ diff --git a/tests/functional/disaster-recovery/metro-dr/test_multiple_apps_failover_and_relocate.py b/tests/functional/disaster-recovery/metro-dr/test_multiple_apps_failover_and_relocate.py index ee526e5d9388..bdb2400e0e88 100644 --- a/tests/functional/disaster-recovery/metro-dr/test_multiple_apps_failover_and_relocate.py +++ b/tests/functional/disaster-recovery/metro-dr/test_multiple_apps_failover_and_relocate.py @@ -67,14 +67,12 @@ def test_application_failover_and_relocate( self, setup_acm_ui, nodes_multicluster, - dr_workload, + dr_workloads_on_managed_clusters, workload_type, ): """ Tests to failover and relocate of applications from both the managed clusters - This test is also compatible to be run from UI, - pass the yaml conf/ocsci/dr_ui.yaml to trigger it. """ self.workload_namespaces = [] if config.RUN.get("mdr_failover_via_ui"): @@ -90,7 +88,7 @@ def test_application_failover_and_relocate( secondary_instances = [] if workload_type == constants.SUBSCRIPTION: - instances, primary_instances, secondary_instances = dr_workload( + primary_instances, secondary_instances = dr_workloads_on_managed_clusters( num_of_subscription=1, primary_cluster=True, secondary_cluster=True )