diff --git a/conf/deployment/fusion_hci_pc/hypershift_client_bm_2w.yaml b/conf/deployment/fusion_hci_pc/hypershift_client_bm_2w.yaml new file mode 100644 index 00000000000..73e942d82b8 --- /dev/null +++ b/conf/deployment/fusion_hci_pc/hypershift_client_bm_2w.yaml @@ -0,0 +1,10 @@ +ENV_DATA: + platform: 'hci_baremetal' + cluster_type: 'hci_client' + cluster_namespace: "openshift-storage-client" + worker_replicas: 2 + mon_type: 'hostpath' + osd_type: 'ssd' +REPORTING: + ocs_must_gather_image: "quay.io/ocs-dev/ocs-must-gather" + ocs_must_gather_latest_tag: 'latest' diff --git a/conf/deployment/fusion_hci_pc/hypershift_client_bm_3w.yaml b/conf/deployment/fusion_hci_pc/hypershift_client_bm_3w.yaml new file mode 100644 index 00000000000..4b41d399e51 --- /dev/null +++ b/conf/deployment/fusion_hci_pc/hypershift_client_bm_3w.yaml @@ -0,0 +1,10 @@ +ENV_DATA: + platform: 'hci_baremetal' + cluster_type: 'hci_client' + cluster_namespace: "openshift-storage-client" + worker_replicas: 3 + mon_type: 'hostpath' + osd_type: 'ssd' +REPORTING: + ocs_must_gather_image: "quay.io/ocs-dev/ocs-must-gather" + ocs_must_gather_latest_tag: 'latest' diff --git a/ocs_ci/deployment/helpers/hypershift_base.py b/ocs_ci/deployment/helpers/hypershift_base.py index 34e7ae994b4..7f21fad8654 100644 --- a/ocs_ci/deployment/helpers/hypershift_base.py +++ b/ocs_ci/deployment/helpers/hypershift_base.py @@ -1,6 +1,9 @@ import logging import os +import random +import re import shutil +import string import tempfile import time from datetime import datetime @@ -14,7 +17,7 @@ from ocs_ci.ocs.resources.pod import wait_for_pods_to_be_in_statuses_concurrently from ocs_ci.ocs.version import get_ocp_version from ocs_ci.utility.retry import retry -from ocs_ci.utility.utils import exec_cmd, TimeoutSampler +from ocs_ci.utility.utils import exec_cmd, TimeoutSampler, get_latest_release_version """ This module contains the base class for HyperShift hosted cluster management. @@ -62,6 +65,35 @@ def wrapper(self, *args, **kwargs): return wrapper +def get_random_hosted_cluster_name(): + """ + Get a random cluster name + + Returns: + str: random cluster name + """ + # getting the cluster name from the env data, for instance "ibm_cloud_baremetal3; mandatory conf field" + bm_name = config.ENV_DATA.get("baremetal", {}).get("env_name") + ocp_version = get_latest_release_version() + hcp_version = "".join([c for c in ocp_version if c.isdigit()][:3]) + match = re.search(r"\d+$", bm_name) + if match: + random_letters = "".join( + random.choice(string.ascii_lowercase) for _ in range(3) + ) + cluster_name = ( + "hcp" + + hcp_version + + "-bm" + + bm_name[match.start() :] + + "-" + + random_letters + ) + else: + raise ValueError("Cluster name not found in the env data") + return cluster_name + + def get_binary_hcp_version(): """ Get hcp version output. Handles hcp 4.16 and 4.17 cmd differences @@ -582,7 +614,7 @@ def destroy_kubevirt_cluster(self, name): Args: name (str): Name of the cluster """ - destroy_timeout_min = 10 + destroy_timeout_min = 15 logger.info( f"Destroying HyperShift hosted cluster {name}. Timeout: {destroy_timeout_min} min" ) diff --git a/ocs_ci/deployment/hosted_cluster.py b/ocs_ci/deployment/hosted_cluster.py index 730c1f21527..c5ae57bdc9e 100644 --- a/ocs_ci/deployment/hosted_cluster.py +++ b/ocs_ci/deployment/hosted_cluster.py @@ -51,12 +51,9 @@ class HostedClients(HyperShiftBase): def __init__(self): HyperShiftBase.__init__(self) - if not config.ENV_DATA.get("clusters"): - raise ValueError( - "No 'clusters': '{: }' set to ENV_DATA" - ) + self.kubeconfig_paths = [] - def do_deploy(self): + def do_deploy(self, cluster_names=None): """ Deploy multiple hosted OCP clusters on Provider platform and setup ODF client on them Perform the 7 stages of deployment: @@ -73,11 +70,26 @@ def do_deploy(self): solution: disable MCE and install upstream Hypershift on the cluster ! Important ! - due to n-1 logic we are assuming that desired CNV version <= OCP version + due to n-1 logic we are assuming that desired CNV version <= OCP version of managing/Provider cluster + + Args: + cluster_names (list): cluster names to deploy, if None, all clusters from ENV_DATA will be deployed + + Returns: + list: the list of HostedODF objects for all hosted OCP clusters deployed by the method successfully """ # stage 1 deploy multiple hosted OCP clusters - cluster_names = self.deploy_hosted_ocp_clusters() + # If all desired clusters were already deployed and self.deploy_hosted_ocp_clusters() returns None instead of + # the list, in this case we assume the stage of Hosted OCP clusters creation is done, and we + # proceed to ODF installation and storage client setup. + # If specific cluster names were provided, we will deploy only those. + if not cluster_names: + cluster_names = self.deploy_hosted_ocp_clusters() or list( + config.ENV_DATA.get("clusters").keys() + ) + if cluster_names: + cluster_names = self.deploy_hosted_ocp_clusters(cluster_names) # stage 2 verify OCP clusters are ready logger.info( @@ -91,11 +103,6 @@ def do_deploy(self): logger.info("Download kubeconfig for all clusters") kubeconfig_paths = self.download_hosted_clusters_kubeconfig_files() - # if all desired clusters were already deployed and step 1 returns None instead of the list, - # we proceed to ODF installation and storage client setup - if not cluster_names: - cluster_names = list(config.ENV_DATA.get("clusters").keys()) - # stage 4 deploy ODF on all hosted clusters if not already deployed for cluster_name in cluster_names: @@ -112,51 +119,39 @@ def do_deploy(self): # stage 5 verify ODF client is installed on all hosted clusters odf_installed = [] for cluster_name in cluster_names: - - if not self.config_has_hosted_odf_image(cluster_name): + if self.config_has_hosted_odf_image(cluster_name): logger.info( - f"Hosted ODF image not set for cluster '{cluster_name}', skipping ODF validation" + f"Validate ODF client operator installed on hosted OCP cluster '{cluster_name}'" ) - continue - - logger.info( - f"Validate ODF client operator installed on hosted OCP cluster '{cluster_name}'" - ) - hosted_odf = HostedODF(cluster_name) - - if not hosted_odf.odf_client_installed(): - # delete catalogsources help to finish install cluster if nodes have not enough mem - # see oc describe pod ocs-client-operator-controller-manager- -n openshift-storage-client - # when the problem was hit - hosted_odf.exec_oc_cmd( - "delete catalogsource --all -n openshift-marketplace" - ) - logger.info("wait 30 sec and create catalogsource again") - time.sleep(30) - hosted_odf.create_catalog_source() - odf_installed.append(hosted_odf.odf_client_installed()) + hosted_odf = HostedODF(cluster_name) + if not hosted_odf.odf_client_installed(): + hosted_odf.exec_oc_cmd( + "delete catalogsource --all -n openshift-marketplace" + ) + logger.info("wait 30 sec and create catalogsource again") + time.sleep(30) + hosted_odf.create_catalog_source() + odf_installed.append(hosted_odf.odf_client_installed()) # stage 6 setup storage client on all hosted clusters - client_setup = [] + client_setup_res = [] + hosted_odf_clusters_installed = [] for cluster_name in cluster_names: - - if ( - not config.ENV_DATA.get("clusters") - .get(cluster_name) - .get("setup_storage_client", False) - ): + if self.storage_installation_requested(cluster_name): logger.info( - f"Storage client setup not set for cluster '{cluster_name}', skipping storage client setup" + f"Setting up Storage client on hosted OCP cluster '{cluster_name}'" ) - continue - - logger.info( - f"Setting up Storage client on hosted OCP cluster '{cluster_name}'" - ) - hosted_odf = HostedODF(cluster_name) - client_setup.append(hosted_odf.setup_storage_client()) - - # stage 7 verify all hosted clusters are ready and print kubeconfig paths + hosted_odf = HostedODF(cluster_name) + client_installed = hosted_odf.setup_storage_client() + client_setup_res.append(client_installed) + if client_installed: + hosted_odf_clusters_installed.append(hosted_odf) + else: + logger.info( + f"Storage client installation not requested for cluster '{cluster_name}', " + "skipping storage client setup" + ) + # stage 7 verify all hosted clusters are ready and print kubeconfig paths on Agent logger.info( "kubeconfig files for all hosted OCP clusters:\n" + "\n".join( @@ -172,9 +167,11 @@ def do_deploy(self): odf_installed ), "ODF client was not deployed on all hosted OCP clusters" assert all( - client_setup + client_setup_res ), "Storage client was not set up on all hosted ODF clusters" + return hosted_odf_clusters_installed + def config_has_hosted_odf_image(self, cluster_name): """ Check if the config has hosted ODF image set for the cluster @@ -199,23 +196,56 @@ def config_has_hosted_odf_image(self, cluster_name): return regestry_exists and version_exists - def deploy_hosted_ocp_clusters( - self, - ): + def storage_installation_requested(self, cluster_name): + """ + Check if the storage client installation was requested in the config + + Args: + cluster_name (str): Name of the cluster + + Returns: + bool: True if the storage client installation was requested, False otherwise + """ + return ( + config.ENV_DATA.get("clusters", {}) + .get(cluster_name, {}) + .get("setup_storage_client", False) + ) + + def deploy_hosted_ocp_clusters(self, cluster_names_list=None): """ Deploy multiple hosted OCP clusters on Provider platform + Args: + cluster_names_list (list): List of cluster names to deploy. If not provided, all clusters + in config.ENV_DATA["clusters"] will be deployed (optional argument) + Returns: - list: the list of cluster names for all hosted OCP clusters deployed by the func successfully + list: The list of cluster names for all hosted OCP clusters deployed by the func successfully """ - cluster_names_desired = list(config.ENV_DATA["clusters"].keys()) + # Get the list of cluster names to deploy + if cluster_names_list: + cluster_names_desired = [ + name + for name in cluster_names_list + if name in config.ENV_DATA["clusters"].keys() + ] + else: + cluster_names_desired = list(config.ENV_DATA["clusters"].keys()) number_of_clusters_to_deploy = len(cluster_names_desired) - logger.info(f"Deploying '{number_of_clusters_to_deploy}' number of clusters") + deployment_mode = ( + "only specified clusters" + if cluster_names_list + else "clusters from deployment configuration" + ) + logger.info( + f"Deploying '{number_of_clusters_to_deploy}' number of {deployment_mode}" + ) cluster_names = [] - for index, cluster_name in enumerate(config.ENV_DATA["clusters"].keys()): + for index, cluster_name in enumerate(cluster_names_desired): logger.info(f"Creating hosted OCP cluster: {cluster_name}") hosted_ocp_cluster = HypershiftHostedOCP(cluster_name) # we need to ensure that all dependencies are installed so for the first cluster we will install all, @@ -282,22 +312,37 @@ def download_hosted_clusters_kubeconfig_files(self): if not (self.hcp_binary_exists() and self.hypershift_binary_exists()): self.download_hcp_binary_with_podman() - kubeconfig_paths = [] for name in config.ENV_DATA.get("clusters").keys(): path = config.ENV_DATA.get("clusters").get(name).get("hosted_cluster_path") - kubeconfig_paths.append(self.download_hosted_cluster_kubeconfig(name, path)) + self.kubeconfig_paths.append( + self.download_hosted_cluster_kubeconfig(name, path) + ) + + return self.kubeconfig_paths - return kubeconfig_paths + def get_kubeconfig_path(self, cluster_name): + """ + Get the kubeconfig path for the cluster + + Args: + cluster_name (str): Name of the cluster + Returns: + str: Path to the kubeconfig file + """ + if not self.kubeconfig_paths: + self.download_hosted_clusters_kubeconfig_files() + for kubeconfig_path in self.kubeconfig_paths: + if cluster_name in kubeconfig_path: + return kubeconfig_path + return def deploy_multiple_odf_clients(self): """ Deploy multiple ODF clients on hosted OCP clusters. Method tries to deploy ODF client on all hosted OCP clusters If ODF was already deployed on some of the clusters, it will be skipped for those clusters. - Returns: - list: the list of kubeconfig paths for all hosted OCP clusters """ - kubeconfig_paths = self.update_hcp_binary() + self.update_hcp_binary() hosted_cluster_names = get_hosted_cluster_names() @@ -306,8 +351,6 @@ def deploy_multiple_odf_clients(self): hosted_odf = HostedODF(cluster_name) hosted_odf.do_deploy() - return kubeconfig_paths - class HypershiftHostedOCP(HyperShiftBase, MetalLBInstaller, CNVInstaller, Deployment): def __init__(self, name): diff --git a/ocs_ci/framework/__init__.py b/ocs_ci/framework/__init__.py index 4e61f85c14f..8a622ddec07 100644 --- a/ocs_ci/framework/__init__.py +++ b/ocs_ci/framework/__init__.py @@ -6,6 +6,7 @@ https://docs.pytest.org/en/latest/reference.html under section PYTEST_DONT_REWRITE """ + # Use the new python 3.7 dataclass decorator, which provides an object similar # to a namedtuple, but allows type enforcement and defining methods. import os @@ -478,6 +479,43 @@ def __init__(self): primary_index = primary_config.MULTICLUSTER.get("multicluster_index") super().__init__(primary_index) + def insert_cluster_config(self, index, new_config): + """ + Insert a new cluster configuration at the given index + + Args: + index (int): The index at which to insert the new configuration + new_config (Config): The new configuration to insert + + """ + self.clusters.insert(index, new_config) + self.nclusters += 1 + self._refresh_ctx() + + def remove_cluster(self, index): + """ + Remove the cluster at the given index + + Args: + index (int): The index of the cluster to remove + """ + self.clusters.pop(index) + self.nclusters -= 1 + self._refresh_ctx() + + def remove_cluster_by_name(self, cluster_name): + """ + Remove the cluster by the cluster name + + Args: + cluster_name (str): The cluster name to remove + + Raises: + ClusterNotFoundException: In case it didn't find the cluster + + """ + self.remove_cluster(self.get_cluster_index_by_name(cluster_name)) + config = MultiClusterConfig() diff --git a/ocs_ci/ocs/constants.py b/ocs_ci/ocs/constants.py index 5f19f5fe1a7..2b677bf6de3 100644 --- a/ocs_ci/ocs/constants.py +++ b/ocs_ci/ocs/constants.py @@ -17,6 +17,8 @@ # Directories TOP_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) CONF_DIR = os.path.join(TOP_DIR, "conf") +DEPLOYMENT_CONF_DIR = os.path.join(CONF_DIR, "deployment") +FUSION_CONF_DIR = os.path.join(DEPLOYMENT_CONF_DIR, "fusion_hci_pc") FRAMEWORK_CONF_DIR = os.path.join(TOP_DIR, "ocs_ci", "framework", "conf") OCP_VERSION_CONF_DIR = os.path.join(FRAMEWORK_CONF_DIR, "ocp_version") OCS_VERSION_CONF_DIR = os.path.join(FRAMEWORK_CONF_DIR, "ocs_version") diff --git a/ocs_ci/ocs/resources/catalog_source.py b/ocs_ci/ocs/resources/catalog_source.py index aedcd36b2ae..b2c1140861f 100644 --- a/ocs_ci/ocs/resources/catalog_source.py +++ b/ocs_ci/ocs/resources/catalog_source.py @@ -1,6 +1,7 @@ """ CatalogSource related functionalities """ + import logging from time import sleep @@ -180,3 +181,21 @@ def enable_specific_source(source_name): ) logger.info(f"Waiting 20 seconds after enabling source: {source_name}") sleep(20) + + +def get_odf_tag_from_redhat_catsrc(): + """ + Get the ODF tag from the default redhat-operators Catalog Source + + Returns: + str: ODF tag from redhat-operators Catalog Source + """ + from ocs_ci.ocs.ocp import OCP + + catsrc_data = OCP( + kind=constants.CATSRC, + namespace=constants.MARKETPLACE_NAMESPACE, + resource_name="redhat-operators", + ).get() + registry_image = catsrc_data.get("spec").get("image") + return registry_image.split(":")[-1] diff --git a/ocs_ci/utility/json.py b/ocs_ci/utility/json.py new file mode 100644 index 00000000000..569635f04fc --- /dev/null +++ b/ocs_ci/utility/json.py @@ -0,0 +1,19 @@ +import json + + +class SetToListJSONEncoder(json.JSONEncoder): + """ + The CustomJSONEncoder class is a subclass of json.JSONEncoder designed to handle the serialization of Python + objects into JSON format, with a specific focus on converting set objects into lists. + This is necessary because the default JSON encoder in Python does not support set objects, which are not a valid + JSON data type. + This way we avoid "TypeError: Object of type set is not JSON serializable" + + Usage: + json.dumps(data, cls=SetToListJSONEncoder) + """ + + def default(self, obj): + if isinstance(obj, set): + return list(obj) + return super().default(obj) diff --git a/ocs_ci/utility/utils.py b/ocs_ci/utility/utils.py index 86fdd1790f8..781c0acc0ff 100644 --- a/ocs_ci/utility/utils.py +++ b/ocs_ci/utility/utils.py @@ -58,7 +58,6 @@ NoRunningCephToolBoxException, ClusterNotInSTSModeException, ) - from ocs_ci.utility import version as version_module from ocs_ci.utility.flexy import load_cluster_info from ocs_ci.utility.retry import retry diff --git a/tests/conftest.py b/tests/conftest.py index b02fd25226c..32fa2eb7031 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -15,12 +15,15 @@ from functools import partial import boto3 +import yaml from botocore.exceptions import ClientError import pytest from collections import namedtuple from ocs_ci.deployment import factory as dep_factory -from ocs_ci.framework import config as ocsci_config +from ocs_ci.deployment.helpers.hypershift_base import HyperShiftBase +from ocs_ci.deployment.hosted_cluster import HostedClients +from ocs_ci.framework import config as ocsci_config, Config import ocs_ci.framework.pytest_customization.marks from ocs_ci.framework.pytest_customization.marks import ( deployment, @@ -39,6 +42,7 @@ craft_s3_command, put_bucket_policy, ) +from ocs_ci.ocs.constants import FUSION_CONF_DIR from ocs_ci.ocs.dr.dr_workload import BusyBox, BusyBox_AppSet, CnvWorkload from ocs_ci.ocs.exceptions import ( CommandFailed, @@ -120,6 +124,7 @@ get_status_before_execution, get_status_after_execution, ) +from ocs_ci.utility.json import SetToListJSONEncoder from ocs_ci.utility.resource_check import ( create_resource_dct, get_environment_status_after_execution, @@ -7551,7 +7556,6 @@ def finalizer(): @pytest.fixture(scope="session") def scale_noobaa_resources_session(request): - """ Session scoped fixture to scale noobaa resources @@ -7569,7 +7573,6 @@ def scale_noobaa_resources_fixture(request): def scale_noobaa_resources(request): - """ Scale the noobaa pod resources and scale endpoint count @@ -7931,3 +7934,127 @@ def finalizer(): request.addfinalizer(finalizer) return factory + + +@pytest.fixture() +def create_hypershift_clusters(): + """ + Create hosted hyperhift clusters. + + Here we create cluster deployment configuration that was set in the Test. With this configuration we + create a hosted cluster. After successful creation of the hosted cluster, we update the Multicluster Config, + adding the new cluster configuration to the list of the clusters. Now we can operate with new and old clusters + switching the context of Multicluster Config + + Following arguments are necessary to build the hosted cluster configuration: + ENV_DATA: + clusters: + : + hosted_cluster_path: + ocp_version: + cpu_cores_per_hosted_cluster: + memory_per_hosted_cluster: + hosted_odf_registry: + hosted_odf_version: + setup_storage_client: + nodepool_replicas: + + """ + + def factory( + cluster_names, ocp_version, odf_version, setup_storage_client, nodepool_replicas + ): + """ + Factory function implementing the fixture + + Args: + cluster_names (list): List of cluster names + ocp_version (str): OCP version + odf_version (str): ODF version + setup_storage_client (bool): Setup storage client + nodepool_replicas (int): Nodepool replicas; supported values are 2,3 + + """ + hosted_cluster_conf_on_provider = {"ENV_DATA": {"clusters": {}}} + + for cluster_name in cluster_names: + hosted_cluster_conf_on_provider["ENV_DATA"]["clusters"][cluster_name] = { + "hosted_cluster_path": f"~/clusters/{cluster_name}/openshift-cluster-dir", + "ocp_version": ocp_version, + "cpu_cores_per_hosted_cluster": 8, + "memory_per_hosted_cluster": "12Gi", + "hosted_odf_registry": "quay.io/rhceph-dev/ocs-registry", + "hosted_odf_version": odf_version, + "setup_storage_client": setup_storage_client, + "nodepool_replicas": nodepool_replicas, + } + + log.info( + "Creating a hosted clusters with following deployment config: \n%s", + json.dumps( + hosted_cluster_conf_on_provider, indent=4, cls=SetToListJSONEncoder + ), + ) + ocsci_config.update(hosted_cluster_conf_on_provider) + + # During the initial deployment phase, we always deploy Hosting and specific Hosted clusters. + # To distinguish between clusters intended for deployment on deployment CI stage and those intended for + # deployment on the Test stage, we pass the names of the clusters to be deployed to the + # HostedClients().do_deploy() method. + hosted_clients_obj = HostedClients() + deployed_hosted_cluster_objects = hosted_clients_obj.do_deploy(cluster_names) + deployed_clusters = [obj.name for obj in deployed_hosted_cluster_objects] + + for cluster_name in deployed_clusters: + + client_conf_default_dir = os.path.join( + FUSION_CONF_DIR, f"hypershift_client_bm_{nodepool_replicas}w.yaml" + ) + if not os.path.exists(client_conf_default_dir): + raise FileNotFoundError(f"File {client_conf_default_dir} not found") + with open(client_conf_default_dir) as file_stream: + def_client_config_dict = { + k: (v if v is not None else {}) + for (k, v) in yaml.safe_load(file_stream).items() + } + def_client_config_dict.get("ENV_DATA").update( + {"cluster_name": cluster_name} + ) + kubeconfig_path = hosted_clients_obj.get_kubeconfig_path(cluster_name) + log.info(f"Kubeconfig path: {kubeconfig_path}") + def_client_config_dict.setdefault("RUN", {}).update( + {"kubeconfig": kubeconfig_path} + ) + cluster_config = Config() + cluster_config.update(def_client_config_dict) + + log.debug( + "Inserting new hosted cluster config to Multicluster Config " + f"\n{json.dumps(vars(cluster_config), indent=4, cls=SetToListJSONEncoder)}" + ) + ocsci_config.insert_cluster_config( + ocsci_config.nclusters, cluster_config + ) + + return factory + + +@pytest.fixture() +def destroy_hosted_cluster(): + def factory(cluster_name): + ocsci_config.switch_to_provider() + log.info("Destroying hosted cluster. OCS related leftovers are expected") + hypershift_base_obj = HyperShiftBase() + + if not hypershift_base_obj.hcp_binary_exists(): + hypershift_base_obj.update_hcp_binary() + + destroy_res = HyperShiftBase().destroy_kubevirt_cluster(cluster_name) + + if destroy_res: + log.info("Removing cluster from Multicluster Config") + ocsci_config.remove_cluster_by_name(cluster_name) + + return destroy_res + + return factory diff --git a/tests/cross_functional/conftest.py b/tests/cross_functional/conftest.py index 7272bb95534..56bb6b2c63a 100644 --- a/tests/cross_functional/conftest.py +++ b/tests/cross_functional/conftest.py @@ -989,7 +989,6 @@ def setup_rgw_kafka_notification(request, rgw_bucket_factory, rgw_obj): ) = amq.create_kafkadrop() def factory(): - """ Factory function implementing the fixture @@ -1106,9 +1105,9 @@ def factory( event = Event() executor = ThreadPoolExecutor( - max_workers=5 - len(skip_any_features) - if skip_any_features is not None - else 5 + max_workers=( + 5 - len(skip_any_features) if skip_any_features is not None else 5 + ) ) skip_any_features = list() if skip_any_features is None else skip_any_features diff --git a/tests/libtest/test_provider_create_hosted_cluster.py b/tests/libtest/test_provider_create_hosted_cluster.py index 39ce459a29e..7e9c564cb09 100644 --- a/tests/libtest/test_provider_create_hosted_cluster.py +++ b/tests/libtest/test_provider_create_hosted_cluster.py @@ -4,6 +4,7 @@ from ocs_ci.deployment.deployment import validate_acm_hub_install, Deployment from ocs_ci.deployment.helpers.hypershift_base import ( get_hosted_cluster_names, + get_random_hosted_cluster_name, ) from ocs_ci.deployment.hosted_cluster import ( HypershiftHostedOCP, @@ -11,12 +12,20 @@ HostedClients, ) from ocs_ci.framework import config +from ocs_ci.framework.logger_helper import log_step from ocs_ci.framework.pytest_customization.marks import ( hci_provider_required, libtest, purple_squad, runs_on_provider, ) +from ocs_ci.ocs.ocp import OCP +from ocs_ci.ocs.resources.catalog_source import get_odf_tag_from_redhat_catsrc +from ocs_ci.utility.utils import ( + get_latest_release_version, +) +from ocs_ci.utility.version import get_ocs_version_from_csv +from ocs_ci.framework import config as ocsci_config from ocs_ci.ocs import constants from ocs_ci.ocs.resources.storage_client import StorageClient from ocs_ci.helpers.helpers import ( @@ -132,6 +141,77 @@ def test_storage_client_connected(self): cluster_names = list(config.ENV_DATA["clusters"].keys()) assert HostedODF(cluster_names[-1]).get_storage_client_status() == "Connected" + @runs_on_provider + @hci_provider_required + def test_create_hosted_cluster_with_fixture( + self, create_hypershift_clusters, destroy_hosted_cluster + ): + """ + Test create hosted cluster with fixture + """ + log_step("Create hosted client") + cluster_name = get_random_hosted_cluster_name() + odf_version = str(get_ocs_version_from_csv()).replace(".stable", "") + if "rhodf" in odf_version: + odf_version = get_odf_tag_from_redhat_catsrc() + ocp_version = get_latest_release_version() + nodepool_replicas = 2 + + create_hypershift_clusters( + cluster_names=[cluster_name], + ocp_version=ocp_version, + odf_version=odf_version, + setup_storage_client=True, + nodepool_replicas=nodepool_replicas, + ) + + log_step("Switch to the hosted cluster") + ocsci_config.switch_to_cluster_by_name(cluster_name) + + server = str(OCP().exec_oc_cmd("whoami --show-server", out_yaml_format=False)) + + assert ( + cluster_name in server + ), f"Failed to switch to cluster '{cluster_name}' and fetch data" + + @runs_on_provider + @hci_provider_required + def test_create_destroy_hosted_cluster_with_fixture( + self, create_hypershift_clusters, destroy_hosted_cluster + ): + """ + Test create hosted cluster with fixture and destroy cluster abruptly + Important that ceph resources associate with the cluster will not be cleaned up + """ + log_step("Create hosted client") + cluster_name = get_random_hosted_cluster_name() + odf_version = str(get_ocs_version_from_csv()).replace(".stable", "") + if "rhodf" in odf_version: + odf_version = get_odf_tag_from_redhat_catsrc() + + ocp_version = get_latest_release_version() + nodepool_replicas = 2 + + create_hypershift_clusters( + cluster_names=[cluster_name], + ocp_version=ocp_version, + odf_version=odf_version, + setup_storage_client=True, + nodepool_replicas=nodepool_replicas, + ) + + log_step("Switch to the hosted cluster") + ocsci_config.switch_to_cluster_by_name(cluster_name) + + server = str(OCP().exec_oc_cmd("whoami --show-server", out_yaml_format=False)) + + assert ( + cluster_name in server + ), f"Failed to switch to cluster '{cluster_name}' and fetch data" + + log_step("Destroy hosted cluster") + assert destroy_hosted_cluster(cluster_name), "Failed to destroy hosted cluster" + @runs_on_provider @hci_provider_required def test_deploy_acm(self):