diff --git a/ocs_ci/deployment/deployment.py b/ocs_ci/deployment/deployment.py index bfd2a471bba..a7f2994e19e 100644 --- a/ocs_ci/deployment/deployment.py +++ b/ocs_ci/deployment/deployment.py @@ -580,6 +580,19 @@ def do_deploy_odf_provider_mode(self): ): storage_client_deployment_obj.provider_and_native_client_installation() + def do_deploy_cnv(self): + """ + Deploy CNV + We run it in OCP deployment stage, hence `ship_ocs_deployment` is set True. + When we run it in OCS deployment stage, the `skip_ocs_deployment` is set to False automatically and + second installation does not happen. + """ + if ( + config.DEPLOYMENT.get("cnv_deployment") + and config.ENV_DATA["skip_ocs_deployment"] + ): + CNVInstaller().deploy_cnv() + def do_deploy_hosted_clusters(self): """ Deploy Hosted cluster(s) @@ -656,8 +669,7 @@ def deploy_cluster(self, log_cli_level="DEBUG"): self.do_deploy_rdr() self.do_deploy_fusion() self.do_deploy_odf_provider_mode() - if config.DEPLOYMENT.get("cnv_deployment"): - CNVInstaller().deploy_cnv() + self.do_deploy_cnv() self.do_deploy_hosted_clusters() def get_rdr_conf(self): @@ -921,7 +933,7 @@ def wait_for_subscription(self, subscription_name, namespace=None): if not namespace: namespace = self.namespace - if config.multicluster: + if self.muliclusterhub_running(): resource_kind = constants.SUBSCRIPTION_WITH_ACM else: resource_kind = constants.SUBSCRIPTION @@ -1054,7 +1066,7 @@ def deploy_ocs_via_operator(self, image=None): worker_nodes = get_worker_nodes() node_obj = ocp.OCP(kind="node") platform = config.ENV_DATA.get("platform").lower() - if platform != constants.BAREMETAL_PLATFORM: + if platform not in [constants.BAREMETAL_PLATFORM, constants.HCI_BAREMETAL]: for node in worker_nodes: for interface in interfaces: ip_link_cmd = f"ip link set promisc on {interface}" @@ -1297,7 +1309,10 @@ def deploy_ocs_via_operator(self, image=None): cluster_data["spec"]["storageDeviceSets"][0]["replica"] = 1 # set size of request for storage - if self.platform.lower() == constants.BAREMETAL_PLATFORM: + if self.platform.lower() in [ + constants.BAREMETAL_PLATFORM, + constants.HCI_BAREMETAL, + ]: pv_size_list = helpers.get_pv_size( storageclass=self.DEFAULT_STORAGECLASS_LSO ) @@ -2321,6 +2336,28 @@ def deploy_multicluster_hub(self): logger.error(f"Failed to install MultiClusterHub. Exception is: {ex}") return False + def muliclusterhub_running(self): + """ + Check if MultiCluster Hub is running + + Returns: + bool: True if MultiCluster Hub is running, False otherwise + """ + ocp_obj = OCP( + kind=constants.ACM_MULTICLUSTER_HUB, namespace=constants.ACM_HUB_NAMESPACE + ) + try: + mch_running = ocp_obj.wait_for_resource( + condition=constants.STATUS_RUNNING, + resource_name=constants.ACM_MULTICLUSTER_RESOURCE, + column="STATUS", + timeout=6, + sleep=3, + ) + except CommandFailed: + mch_running = False + return mch_running + def create_external_pgsql_secret(): """ @@ -2359,8 +2396,8 @@ def validate_acm_hub_install(): condition=constants.STATUS_RUNNING, resource_name=constants.ACM_MULTICLUSTER_RESOURCE, column="STATUS", - timeout=720, - sleep=5, + timeout=1200, + sleep=30, ) logger.info("MultiClusterHub Deployment Succeeded") diff --git a/ocs_ci/deployment/helpers/hypershift_base.py b/ocs_ci/deployment/helpers/hypershift_base.py index 1b40c496c5f..9408a851d2e 100644 --- a/ocs_ci/deployment/helpers/hypershift_base.py +++ b/ocs_ci/deployment/helpers/hypershift_base.py @@ -238,6 +238,7 @@ def create_kubevirt_ocp_cluster( root_volume_size: str = 40, ocp_version=None, cp_availability_policy=None, + disable_default_sources=None, ): """ Create HyperShift hosted cluster. Default parameters have minimal requirements for the cluster. @@ -251,6 +252,7 @@ def create_kubevirt_ocp_cluster( root_volume_size (str): Root volume size of the cluster, default 40 (Gi is not required) cp_availability_policy (str): Control plane availability policy, default HighlyAvailable, if no value provided and argument is not used in the command the single replica mode cluster will be created + disable_default_sources (bool): Disable default sources on hosted cluster, such as 'redhat-operators' Returns: str: Name of the hosted cluster """ @@ -308,6 +310,9 @@ def create_kubevirt_ocp_cluster( f" --control-plane-availability-policy {cp_availability_policy} " ) + if disable_default_sources: + create_hcp_cluster_cmd += " --olm-disable-default-sources" + logger.info("Creating HyperShift hosted cluster") exec_cmd(create_hcp_cluster_cmd) diff --git a/ocs_ci/deployment/helpers/lso_helpers.py b/ocs_ci/deployment/helpers/lso_helpers.py index bf8b4f22e0f..c730bd27dbf 100644 --- a/ocs_ci/deployment/helpers/lso_helpers.py +++ b/ocs_ci/deployment/helpers/lso_helpers.py @@ -212,7 +212,7 @@ def setup_local_storage(storageclass): # extra_disks is used in vSphere attach_disk() method storage_class_device_count = config.ENV_DATA.get("extra_disks", 1) expected_pvs = len(worker_names) * storage_class_device_count - if platform == constants.BAREMETAL_PLATFORM: + if platform in [constants.BAREMETAL_PLATFORM, constants.HCI_BAREMETAL]: verify_pvs_created(expected_pvs, storageclass, False) else: verify_pvs_created(expected_pvs, storageclass) diff --git a/ocs_ci/deployment/hosted_cluster.py b/ocs_ci/deployment/hosted_cluster.py index 65637ddaa11..730c1f21527 100644 --- a/ocs_ci/deployment/hosted_cluster.py +++ b/ocs_ci/deployment/hosted_cluster.py @@ -371,6 +371,11 @@ def deploy_ocp( cp_availability_policy = ( config.ENV_DATA["clusters"].get(self.name).get("cp_availability_policy") ) + disable_default_sources = ( + config.ENV_DATA["clusters"] + .get(self.name) + .get("disable_default_sources", False) + ) return self.create_kubevirt_ocp_cluster( name=self.name, nodepool_replicas=nodepool_replicas, @@ -378,6 +383,7 @@ def deploy_ocp( memory=memory_per_hosted_cluster, ocp_version=ocp_version, cp_availability_policy=cp_availability_policy, + disable_default_sources=disable_default_sources, ) def deploy_dependencies( @@ -523,7 +529,12 @@ def __init__(self, name: str): self.timeout_check_resources_exist_sec = 6 self.timeout_wait_csvs_minutes = 20 self.timeout_wait_pod_minutes = 30 - self.storage_client_name = None + + # default cluster name picked from the storage client yaml + storage_client_data = templating.load_yaml( + constants.PROVIDER_MODE_STORAGE_CLIENT + ) + self.storage_client_name = storage_client_data["metadata"]["name"] @kubeconfig_exists_decorator def exec_oc_cmd(self, cmd, timeout=300, ignore_error=False, **kwargs): @@ -622,19 +633,9 @@ def do_deploy(self): """ Deploy ODF client on hosted OCP cluster """ - logger.info(f"Deploying ODF client on hosted OCP cluster '{self.name}'") - hosted_odf_version = get_semantic_version( - config.ENV_DATA.get("clusters").get(self.name).get("hosted_odf_version"), - only_major_minor=True, + logger.info( + f"Deploying ODF client on hosted OCP cluster '{self.name}'. Creating ODF client namespace" ) - - no_network_policy_version = version.VERSION_4_16 - - if hosted_odf_version < no_network_policy_version: - logger.info("Applying network policy") - self.apply_network_policy() - - logger.info("Creating ODF client namespace") self.create_ns() if self.odf_csv_installed(): @@ -1086,12 +1087,13 @@ def create_subscription(self): subscription_data = templating.load_yaml(constants.PROVIDER_MODE_SUBSCRIPTION) # since we are allowed to install N+1 on hosted clusters we can not rely on PackageManifest default channel - odf_version = get_semantic_version( - config.ENV_DATA.get("clusters").get(self.name).get("hosted_odf_version"), - only_major_minor=True, + hosted_odf_version = ( + config.ENV_DATA.get("clusters").get(self.name).get("hosted_odf_version") ) + if "latest" in hosted_odf_version: + hosted_odf_version = hosted_odf_version.split("-")[-1] - subscription_data["spec"]["channel"] = f"stable-{str(odf_version)}" + subscription_data["spec"]["channel"] = f"stable-{str(hosted_odf_version)}" subscription_file = tempfile.NamedTemporaryFile( mode="w+", prefix="subscription", delete=False @@ -1140,15 +1142,14 @@ def storage_claim_exists_cephfs(self): Returns: bool: True if storage class claim for CephFS exists, False otherwise """ - if ( - get_semantic_version( - config.ENV_DATA.get("clusters") - .get(self.name) - .get("hosted_odf_version"), - only_major_minor=True, - ) - < version.VERSION_4_16 - ): + + hosted_odf_version = ( + config.ENV_DATA.get("clusters").get(self.name).get("hosted_odf_version") + ) + if "latest" in hosted_odf_version: + hosted_odf_version = hosted_odf_version.split("-")[-1] + + if get_semantic_version(hosted_odf_version, True) < version.VERSION_4_16: ocp = OCP( kind=constants.STORAGECLASSCLAIM, namespace=self.namespace_client, @@ -1228,15 +1229,14 @@ def storage_claim_exists_rbd(self): Returns: bool: True if storage class claim for RBD exists, False otherwise """ - if ( - get_semantic_version( - config.ENV_DATA.get("clusters") - .get(self.name) - .get("hosted_odf_version"), - True, - ) - < version.VERSION_4_16 - ): + + hosted_odf_version = ( + config.ENV_DATA.get("clusters").get(self.name).get("hosted_odf_version") + ) + if "latest" in hosted_odf_version: + hosted_odf_version = hosted_odf_version.split("-")[-1] + + if get_semantic_version(hosted_odf_version, True) < version.VERSION_4_16: ocp = OCP( kind=constants.STORAGECLASSCLAIM, namespace=self.namespace_client, diff --git a/ocs_ci/deployment/metallb.py b/ocs_ci/deployment/metallb.py index 0367a9dafc2..8862dcb6ac4 100644 --- a/ocs_ci/deployment/metallb.py +++ b/ocs_ci/deployment/metallb.py @@ -500,9 +500,9 @@ def deploy_lb(self): logger.info( f"Deploying MetalLB and dependant resources to namespace: '{self.namespace_lb}'" ) - # icsp mirrors necessary to download packages for the downstream version of metallb - self.apply_icsp() + if self.apply_icsp(): + logger.info("ICSP brew-registry applied successfully") if self.create_metallb_namespace(): logger.info(f"Namespace {self.namespace_lb} created successfully") if self.create_catalog_source(): @@ -669,10 +669,26 @@ def wait_csv_installed(self): break return True + def icsp_brew_registry_exists(self): + """ + Check if the ICSP Brew registry exists + + Returns: + bool: True if the ICSP Brew registry exists, False otherwise + """ + return OCP( + kind="ImageContentSourcePolicy", resource_name="brew-registry" + ).check_resource_existence( + timeout=self.timeout_check_resources_existence, should_exist=True + ) + def apply_icsp(self): """ Apply the ICSP to the cluster """ + if self.icsp_brew_registry_exists(): + logger.info("ICSP Brew registry already exists") + return icsp_data = templating.load_yaml(constants.SUBMARINER_DOWNSTREAM_BREW_ICSP) icsp_data_yaml = tempfile.NamedTemporaryFile( mode="w+", prefix="acm_icsp", delete=False @@ -681,3 +697,4 @@ def apply_icsp(self): exec_cmd(f"oc create -f {icsp_data_yaml.name}", timeout=300) wait_for_machineconfigpool_status(node_type="all") logger.info("ICSP applied successfully") + return self.icsp_brew_registry_exists() diff --git a/ocs_ci/helpers/managed_services.py b/ocs_ci/helpers/managed_services.py index c5b776969eb..05c9d496848 100644 --- a/ocs_ci/helpers/managed_services.py +++ b/ocs_ci/helpers/managed_services.py @@ -1,6 +1,7 @@ """ Managed Services related functionalities """ + import logging import re @@ -236,7 +237,7 @@ def verify_osd_distribution_on_provider(): # 4Ti is the size of OSD assert ( osd_count == int(size) / 4 - ), f"Zone {zone} does not have {size/4} osd, but {osd_count}" + ), f"Zone {zone} does not have {size / 4} osd, but {osd_count}" def verify_storageclient( @@ -326,7 +327,7 @@ def get_all_storageclassclaims(namespace=None): sc_claim_obj = OCP(kind=constants.STORAGECLAIM, namespace=namespace) else: sc_claim_obj = OCP(kind=constants.STORAGECLASSCLAIM, namespace=namespace) - sc_claims_data = sc_claim_obj.get()["items"] + sc_claims_data = sc_claim_obj.get(retry=6, wait=30)["items"] log.info(f"storage claims: {sc_claims_data}") return [OCS(**claim_data) for claim_data in sc_claims_data] diff --git a/ocs_ci/ocs/node.py b/ocs_ci/ocs/node.py index d5deac45cf4..4ae62f0b470 100644 --- a/ocs_ci/ocs/node.py +++ b/ocs_ci/ocs/node.py @@ -797,6 +797,7 @@ def get_compute_node_names(no_replace=False): constants.BAREMETAL_PLATFORM, constants.BAREMETALPSI_PLATFORM, constants.IBM_POWER_PLATFORM, + constants.HCI_BAREMETAL, ]: if no_replace: return [ diff --git a/ocs_ci/ocs/resources/storage_client.py b/ocs_ci/ocs/resources/storage_client.py index f2aed4c3bd5..516c3a03363 100644 --- a/ocs_ci/ocs/resources/storage_client.py +++ b/ocs_ci/ocs/resources/storage_client.py @@ -1,6 +1,7 @@ """ Storage client related functions """ + import logging import tempfile import time @@ -15,8 +16,12 @@ from ocs_ci.helpers.managed_services import ( get_all_storageclassclaims, ) +<<<<<<< HEAD from ocs_ci.ocs.resources.ocs import get_ocs_csv from ocs_ci.ocs.resources.storage_cluster import verify_storage_cluster +======= +from ocs_ci.utility.utils import TimeoutSampler +>>>>>>> ea173fb1 (Provider mode deployment with Hosted clusters (#10120)) log = logging.getLogger(__name__) @@ -295,6 +300,7 @@ def create_storageclaim( ) self.ocp_obj.exec_oc_cmd(f"apply -f {storage_classclaim_data_yaml.name}") + @retry(AssertionError, 20, 10, 1) def verify_storage_claim_status( self, storageclient_name=None, @@ -326,6 +332,7 @@ def verify_storage_claim_status( ), "storageclaim is not in expected status" log.info(sc_claim) + @retry(AssertionError, 20, 10, 1) def verify_storagerequest_exists( self, storageclient_name=None, namespace=config.ENV_DATA["cluster_namespace"] ): @@ -336,21 +343,26 @@ def verify_storagerequest_exists( storageclient_name (str): Name of the storageclient to be verified. namespace (str): Namespace where the storageclient is present. - Returns: - storagerequest_exists (bool): returns true if the storagerequest exists - """ - cmd = f"get storagerequests -n {namespace} " "-o=jsonpath='{.items[*]}'" - storage_requests = self.ocp_obj.exec_oc_cmd(command=cmd, out_yaml_format=False) - - log.info(f"The list of storagerequests: {storage_requests}") - return ( - f"ocs.openshift.io/storagerequest-name: {storageclient_name}-cephfs" - in storage_requests - and f"ocs.openshift.io/storagerequest-name: {storageclient_name}-chep-rbd" - in storage_requests + storage_requests = ocp.OCP( + kind="StorageRequest", + namespace=namespace, ) + storage_requests_data = storage_requests.get(retry=6, wait=30)["items"] + + # check that both cephfs and rbd storage requests exist + assert any( + req["metadata"]["labels"]["ocs.openshift.io/storagerequest-name"] + == f"{storageclient_name}-cephfs" + for req in storage_requests_data + ), "cephfs storage request not found" + assert any( + req["metadata"]["labels"]["ocs.openshift.io/storagerequest-name"] + == f"{storageclient_name}-ceph-rbd" + for req in storage_requests_data + ), "rbd storage request not found" + @retry(AssertionError, 12, 10, 1) def verify_storageclient_status( self, @@ -535,3 +547,26 @@ def verify_native_storageclient(self): self.verify_storagerequest_exists( storageclient_name=storageclient_name, namespace=namespace ) + + def get_storageclient_name(self, namespace, timeout=300, sleep=10): + """ + This method fetches the first storageclient name. + Suits well only for native storage client wait and fetch + + Args: + namespace(str): Namespace where the storageclient is created + timeout(int): Time to wait for the storageclient + sleep(int): Time to sleep between each iteration + + Returns: + storageclient_name(str): name of the storageclient + """ + for sample in TimeoutSampler( + timeout, sleep, ocp.OCP, kind=constants.STORAGECLIENT, namespace=namespace + ): + storageclient_data = sample.get().get("items", []) + for storageclient in storageclient_data: + if storageclient.get("metadata", {}).get("name"): + log.info(f"storageclient data, {storageclient}") + return storageclient.get("metadata", {}).get("name") + return None diff --git a/ocs_ci/ocs/resources/storage_cluster.py b/ocs_ci/ocs/resources/storage_cluster.py index 9d340856f8c..00ebfe621b8 100644 --- a/ocs_ci/ocs/resources/storage_cluster.py +++ b/ocs_ci/ocs/resources/storage_cluster.py @@ -1,6 +1,7 @@ """ StorageCluster related functionalities """ + import copy import ipaddress import logging @@ -556,7 +557,8 @@ def ocs_install_verification( # removes duplicate hostname deviceset_pvcs = list(set(deviceset_pvcs)) if ( - config.ENV_DATA.get("platform") == constants.BAREMETAL_PLATFORM + config.ENV_DATA.get("platform") + in [constants.BAREMETAL_PLATFORM, constants.HCI_BAREMETAL] or config.ENV_DATA.get("platform") == constants.AWS_PLATFORM ): deviceset_pvcs = [ diff --git a/ocs_ci/ocs/scale_lib.py b/ocs_ci/ocs/scale_lib.py index 0d17cedffcc..b90589c8c2f 100644 --- a/ocs_ci/ocs/scale_lib.py +++ b/ocs_ci/ocs/scale_lib.py @@ -337,7 +337,7 @@ def create_scale_pods( if actual_itr_counter == expected_itr_counter: logger.info( f"Scaled {scale_pvc_count} PVCs and created " - f"{scale_pvc_count/pvc_per_pod_count} PODs" + f"{scale_pvc_count / pvc_per_pod_count} PODs" ) # TODO: Removing PG balancer validation, due to PG auto_scale enabled # TODO: sometime PG's can't be equally distributed across OSDs @@ -356,13 +356,13 @@ def create_scale_pods( max_pvc_size=max_pvc_size, ) logger.info( - f"Scaled {len(rbd_pvc)+len(fs_pvc)} PVCs and Created " + f"Scaled {len(rbd_pvc) + len(fs_pvc)} PVCs and Created " f"{len(pod_running)} PODs in interation {actual_itr_counter}" ) logger.info( f"Scaled {actual_itr_counter * min_pvc_count} PVC's and Created " - f"{int(actual_itr_counter * (min_pvc_count/pvc_per_pod_count))} PODs" + f"{int(actual_itr_counter * (min_pvc_count / pvc_per_pod_count))} PODs" ) return self.kube_job_pod_list, self.kube_job_pvc_list @@ -1099,10 +1099,9 @@ def check_and_add_enough_worker(worker_count): and config.ENV_DATA["platform"].lower() == constants.VSPHERE_PLATFORM ): raise UnsupportedPlatformError("Unsupported Platform to add worker") - elif ( - config.ENV_DATA["deployment_type"] == "upi" - and config.ENV_DATA["platform"].lower() == constants.BAREMETAL_PLATFORM - ): + elif config.ENV_DATA["deployment_type"] == "upi" and config.ENV_DATA[ + "platform" + ].lower() in [constants.BAREMETAL_PLATFORM, constants.HCI_BAREMETAL]: raise UnsupportedPlatformError("Unsupported Platform to add worker") else: raise UnavailableResourceException( @@ -1303,7 +1302,7 @@ def check_all_pvc_reached_bound_state_in_kube_job( # And if PVCs still not in bound state then there will be assert. if while_iteration_count >= 10: assert logger.error( - f" Listed PVCs took more than {timeout*10} secs to bound {pvc_not_bound_list}" + f" Listed PVCs took more than {timeout * 10} secs to bound {pvc_not_bound_list}" ) break pvc_not_bound_list.clear() @@ -1876,7 +1875,7 @@ def validate_all_expanded_pvc_size_in_kube_job( # And if PVCs size still not extended then there will be assert. if while_iteration_count >= 10: assert logger.error( - f" Listed PVC size not expanded in {timeout*10} secs, PVCs {pvc_not_extended_list}" + f" Listed PVC size not expanded in {timeout * 10} secs, PVCs {pvc_not_extended_list}" ) break pvc_not_extended_list.clear() diff --git a/ocs_ci/ocs/ui/deployment_ui.py b/ocs_ci/ocs/ui/deployment_ui.py index b8f68e48449..585a4d905c3 100644 --- a/ocs_ci/ocs/ui/deployment_ui.py +++ b/ocs_ci/ocs/ui/deployment_ui.py @@ -234,7 +234,10 @@ def install_lso_cluster(self): self.do_click( locator=self.dep_loc["all_nodes_create_sc"], enable_screenshot=True ) - if config.ENV_DATA.get("platform") != constants.BAREMETAL_PLATFORM: + if config.ENV_DATA.get("platform") in [ + constants.BAREMETAL_PLATFORM, + constants.HCI_BAREMETAL, + ]: self.verify_disks_lso_attached() timeout_next = 60 else: diff --git a/tests/libtest/test_provider_create_hosted_cluster.py b/tests/libtest/test_provider_create_hosted_cluster.py index fb84c8d1e85..39ce459a29e 100644 --- a/tests/libtest/test_provider_create_hosted_cluster.py +++ b/tests/libtest/test_provider_create_hosted_cluster.py @@ -1,7 +1,7 @@ import logging import random -from ocs_ci.deployment.deployment import validate_acm_hub_install +from ocs_ci.deployment.deployment import validate_acm_hub_install, Deployment from ocs_ci.deployment.helpers.hypershift_base import ( get_hosted_cluster_names, ) @@ -17,6 +17,16 @@ purple_squad, runs_on_provider, ) +from ocs_ci.ocs import constants +from ocs_ci.ocs.resources.storage_client import StorageClient +from ocs_ci.helpers.helpers import ( + get_all_storageclass_names, + verify_block_pool_exists, +) +from ocs_ci.ocs.rados_utils import ( + verify_cephblockpool_status, + check_phase_of_rados_namespace, +) logger = logging.getLogger(__name__) @@ -184,3 +194,41 @@ def test_download_hcp(self): download_hcp_binary=True, ) assert hypershift_hosted.hcp_binary_exists(), "HCP binary not downloaded" + + @runs_on_provider + def test_mch_status_running(self): + """ + Get MCH status + """ + logger.info("Get MCH status") + depl = Deployment() + assert depl.muliclusterhub_running(), "MCH not running" + + @runs_on_provider + def test_verify_native_storage(self): + """ + Verify native storage client + """ + logger.info("Verify native storage client") + storage_client = StorageClient() + storage_client.verify_native_storageclient() + assert verify_block_pool_exists( + constants.DEFAULT_BLOCKPOOL + ), f"{constants.DEFAULT_BLOCKPOOL} is not created" + assert verify_cephblockpool_status(), "the cephblockpool is not in Ready phase" + + # Validate radosnamespace created and in 'Ready' status + assert ( + check_phase_of_rados_namespace() + ), "The radosnamespace is not in Ready phase" + + # Validate storageclassrequests created + storage_class_classes = get_all_storageclass_names() + storage_class_claims = [ + constants.CEPHBLOCKPOOL_SC, + constants.CEPHFILESYSTEM_SC, + ] + for storage_class in storage_class_claims: + assert ( + storage_class in storage_class_classes + ), "Storage classes ae not created as expected"