diff --git a/ocs_ci/helpers/helpers.py b/ocs_ci/helpers/helpers.py index e3fca3a7176..fa8ff0f4019 100644 --- a/ocs_ci/helpers/helpers.py +++ b/ocs_ci/helpers/helpers.py @@ -756,6 +756,8 @@ def create_storage_class( allow_volume_expansion=True, kernelMountOptions=None, annotations=None, + mapOptions=None, + mounter=None, ): """ Create a storage class @@ -780,6 +782,9 @@ def create_storage_class( allow_volume_expansion(bool): True to create sc with volume expansion kernelMountOptions (str): Mount option for security context annotations(dict): dict of annotations to be added to the storageclass. + mapOptions (str): mapOtions match the configuration of ocs-storagecluster-ceph-rbd-virtualization storage class + mounter (str): mounter to match the configuration of ocs-storagecluster-ceph-rbd-virtualization storage class + Returns: OCS: An OCS instance for the storage class """ @@ -833,6 +838,10 @@ def create_storage_class( if annotations: sc_data["metadata"]["annotations"] = annotations + if mapOptions and mounter: + sc_data["parameters"]["mapOptions"] = mapOptions + sc_data["parameters"]["mounter"] = mounter + sc_data["parameters"]["clusterID"] = config.ENV_DATA["cluster_namespace"] sc_data["reclaimPolicy"] = reclaim_policy sc_data["volumeBindingMode"] = volume_binding_mode diff --git a/ocs_ci/ocs/constants.py b/ocs_ci/ocs/constants.py index 2c14f51b679..7d281348d28 100644 --- a/ocs_ci/ocs/constants.py +++ b/ocs_ci/ocs/constants.py @@ -995,6 +995,7 @@ TEMPLATE_CNV_VM_WORKLOAD_DIR, "dv_role_binding.yaml" ) CNV_VM_TEMPLATE_YAML = os.path.join(TEMPLATE_CNV_VM_WORKLOAD_DIR, "vm.yaml") +CNV_VM_WORKLOADS = os.path.join(TEMPLATE_CNV_VM_WORKLOAD_DIR, "cnv_vm_workload.yaml") METALLB = "metallb-operator" METALLB_CONTROLLER_MANAGER_PREFIX = "metallb-operator-controller-manager" diff --git a/ocs_ci/templates/cnv-vm-workload/cnv_vm_workload.yaml b/ocs_ci/templates/cnv-vm-workload/cnv_vm_workload.yaml new file mode 100644 index 00000000000..38a8eea7d3a --- /dev/null +++ b/ocs_ci/templates/cnv-vm-workload/cnv_vm_workload.yaml @@ -0,0 +1,16 @@ +cnv_vm_configs: + - volume_interface: PVC + access_mode: ReadWriteMany + sc_compression: default + - volume_interface: PVC + access_mode: ReadWriteMany + sc_compression: aggressive + - volume_interface: PVC + access_mode: ReadWriteOnce + sc_compression: default + - volume_interface: DVT + access_mode: ReadWriteMany + sc_compression: default + - volume_interface: DVT + access_mode: ReadWriteMany + sc_compression: aggressive diff --git a/tests/conftest.py b/tests/conftest.py index 70993c5cb3a..1de347e0287 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -197,7 +197,7 @@ ) from ocs_ci.ocs.longevity import start_app_workload from ocs_ci.utility.decorators import switch_to_default_cluster_index_at_last - +from ocs_ci.helpers.keyrotation_helper import PVKeyrotation log = logging.getLogger(__name__) @@ -860,6 +860,8 @@ def factory( allow_volume_expansion=True, kernelMountOptions=None, annotations=None, + mapOptions=None, + mounter=None, ): """ Args: @@ -886,6 +888,8 @@ def factory( allow_volume_expansion (bool): True to Allows volume expansion kernelMountOptions (str): Mount option for security context annotations (dict): dict of annotation to be added to the storageclass. + mapOptions (str): mapOtions match the configuration of ocs-storagecluster-ceph-rbd-virtualization SC + mounter (str): mounter to match the configuration of ocs-storagecluster-ceph-rbd-virtualization SC Returns: object: helpers.create_storage_class instance with links to @@ -925,6 +929,8 @@ def factory( allow_volume_expansion=allow_volume_expansion, kernelMountOptions=kernelMountOptions, annotations=annotations, + mapOptions=mapOptions, + mounter=mounter, ) assert sc_obj, f"Failed to create {interface} storage class" sc_obj.secret = secret @@ -7095,6 +7101,111 @@ def teardown(): return factory +@pytest.fixture() +def multi_cnv_workload( + pv_encryption_kms_setup_factory, storageclass_factory, cnv_workload +): + """ + Fixture to create virtual machines (VMs) with specific configurations. + + This fixture sets up multiple VMs with varying storage configurations as specified + in the `cnv_vm_workload.yaml`. Each VM configuration includes the volume interface type, + access mode, and the storage class to be used. + + The configurations applied to the VMs are: + - Volume interface: `VM_VOLUME_PVC` or `VM_VOLUME_DVT` + - Access mode: `ACCESS_MODE_RWX` or `ACCESS_MODE_RWO` + - Storage class: Custom storage classes, including default compression and aggressive profiles. + + """ + + def factory(namespace=None): + """ + Args: + namespace (str, optional): The namespace to create the vm on. + + Returns: + tuple: tuple containing: + vm_list_default_compr(list): objects of cnv workload class with default comp + vm_list_agg_compr(list): objects of cnv workload class with aggressive compression + sc_obj_def_compr(list): objects of storage class with default comp + sc_obj_aggressive(list): objects of storage class with aggressive compression + + """ + vm_list_agg_compr = [] + vm_list_default_compr = [] + + namespace = ( + namespace if namespace else create_unique_resource_name("vm", "namespace") + ) + + # Setup csi-kms-connection-details configmap + log.info("Setting up csi-kms-connection-details configmap") + kms = pv_encryption_kms_setup_factory(kv_version="v2") + log.info("csi-kms-connection-details setup successful") + + # Create an encryption enabled storageclass for RBD + sc_obj_def_compr = storageclass_factory( + interface=constants.CEPHBLOCKPOOL, + encrypted=True, + encryption_kms_id=kms.kmsid, + new_rbd_pool=True, + mapOptions="krbd:rxbounce", + mounter="rbd", + ) + + sc_obj_aggressive = storageclass_factory( + interface=constants.CEPHBLOCKPOOL, + encrypted=True, + encryption_kms_id=kms.kmsid, + compression="aggressive", + new_rbd_pool=True, + mapOptions="krbd:rxbounce", + mounter="rbd", + ) + + storage_classes = [sc_obj_def_compr, sc_obj_aggressive] + + # Create ceph-csi-kms-token in the tenant namespace + kms.vault_path_token = kms.generate_vault_token() + kms.create_vault_csi_kms_token(namespace=namespace) + + for sc_obj in storage_classes: + pvk_obj = PVKeyrotation(sc_obj) + pvk_obj.annotate_storageclass_key_rotation(schedule="*/3 * * * *") + + # Load VMconfigs from cnv_vm_workload yaml + vm_configs = templating.load_yaml(constants.CNV_VM_WORKLOADS) + + # Loop through vm_configs and create the VMs using the cnv_workload fixture + for index, vm_config in enumerate(vm_configs["cnv_vm_configs"]): + # Determine the storage class based on the compression type + if vm_config["sc_compression"] == "default": + storageclass = sc_obj_def_compr.name + elif vm_config["sc_compression"] == "aggressive": + storageclass = sc_obj_aggressive.name + vm_obj = cnv_workload( + volume_interface=vm_config["volume_interface"], + access_mode=vm_config["access_mode"], + storageclass=storageclass, + pvc_size="30Gi", # Assuming pvc_size is fixed for all + source_url=constants.CNV_FEDORA_SOURCE, # Assuming source_url is the same for all VMs + namespace=namespace, + )[index] + if vm_config["sc_compression"] == "aggressive": + vm_list_agg_compr.append(vm_obj) + else: + vm_list_default_compr.append(vm_obj) + return ( + vm_list_default_compr, + vm_list_agg_compr, + sc_obj_def_compr, + sc_obj_aggressive, + ) + + return factory + + @pytest.fixture() def clone_vm_workload(request): """ diff --git a/tests/functional/workloads/cnv/test_multi_vm_configurations.py b/tests/functional/workloads/cnv/test_multi_vm_configurations.py new file mode 100644 index 00000000000..baa0cf8572a --- /dev/null +++ b/tests/functional/workloads/cnv/test_multi_vm_configurations.py @@ -0,0 +1,128 @@ +import logging +import pytest + +from ocs_ci.framework.testlib import E2ETest +from ocs_ci.framework.pytest_customization.marks import workloads, magenta_squad +from ocs_ci.helpers.cnv_helpers import cal_md5sum_vm +from ocs_ci.helpers.performance_lib import run_oc_command +from ocs_ci.helpers.keyrotation_helper import PVKeyrotation +from ocs_ci.utility.utils import run_cmd +from ocs_ci.ocs import constants + +logger = logging.getLogger(__name__) + + +@magenta_squad +class TestCNVVM(E2ETest): + """ + Includes tests related to CNV+ODF workloads. + + """ + + @pytest.fixture(autouse=True) + def setup(self, project_factory, multi_cnv_workload): + """ + Setting up VMs for tests + + """ + + # Create a project + proj_obj = project_factory() + ( + self.vm_objs_def, + self.vm_objs_aggr, + self.sc_obj_def_compr, + self.sc_obj_aggressive, + ) = multi_cnv_workload(namespace=proj_obj.namespace) + + logger.info("All vms created successfully") + + def verify_keyrotation(self, vm_objs, sc_obj): + """ + Verify the keyrotation is succeed. + + Args: + vm_objs (obj): virtual machine Object + sc_obj (obj): storage class object + + """ + for vm in vm_objs: + if vm.volume_interface == constants.VM_VOLUME_PVC: + pvk_obj = PVKeyrotation(sc_obj) + volume_name = vm.pvc_obj.get().get("spec", {}).get("volumeName") + volume_handle = None + for line in run_oc_command( + f"describe pv {volume_name}", namespace=vm.namespace + ): + if "VolumeHandle:" in line: + volume_handle = line.split()[1] + break + if not volume_handle: + logger.error(f"Cannot get volume handle for pv {volume_name}") + raise Exception("Cannot get volume handle") + assert pvk_obj.wait_till_keyrotation( + volume_handle + ), f"Failed to rotate Key for the PVC {vm.pvc_obj.name}" + + @workloads + @pytest.mark.polarion_id("OCS-6298") + def test_cnv_vms(self, setup, setup_cnv): + """ + Tests to verify configuration for non-GS like environment + + Steps: + 1) Create VMs using fixture multi_cnv_workload + 2) Validate data integrity using md5sum. + a. create file locally and take md5sum + b. copy same file to vm and take md5sum + c. Validate both are same or not + 3) Validate pvc level key rotation + 4) Stop the VM + 5) Delete the VM (as part of factory teardown) + + """ + all_vm_list = self.vm_objs_def + self.vm_objs_aggr + + # 1.Validate data integrity using md5sum. + file_name = "/tmp/dd_file" + vm_filepath = "/home/admin/dd_file1_copy" + + # Create file locally + cmd = f"dd if=/dev/zero of={file_name} bs=1M count=1024" + run_cmd(cmd) + # Calculate the MD5 checksum + if file_name: + cmd = f"md5sum {file_name}" + md5sum_on_local = run_cmd(cmd).split()[0] + if md5sum_on_local: + logger.info(f"MD5 checksum of the file: {md5sum_on_local}") + else: + raise ValueError( + "MD5 checksum could not be calculated. Ensure the file exists and is accessible." + ) + else: + raise ValueError( + "File name is not provided. Please specify a valid file name." + ) + # Copy local file to all vms + for vm_obj in all_vm_list: + vm_obj.scp_to_vm( + local_path=file_name, + vm_dest_path=vm_filepath, + ) + # Take md5sum of copied file and compare with md5sum taken locally + for vm_obj in all_vm_list: + md5sum_on_vm = cal_md5sum_vm(vm_obj, vm_filepath, username=None) + assert ( + md5sum_on_vm == md5sum_on_local + ), f"md5sum has changed after copying file on {vm_obj.name}" + + # 2.Verify PV Keyrotation. + # Process VMs with default compression + self.verify_keyrotation(self.vm_objs_def, self.sc_obj_def_compr) + # Process VMs with aggressive compression + self.verify_keyrotation(self.vm_objs_aggr, self.sc_obj_aggressive) + + # 3.Stop all VMs + for vm_obj in all_vm_list: + vm_obj.stop()