Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions tests/storage/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,15 @@

TEST_FILE_NAME = "test-file.txt"
TEST_FILE_CONTENT = "test-content"

Comment thread
jpeimer marked this conversation as resolved.
Comment thread
jpeimer marked this conversation as resolved.
STORAGE_CLASS_A = "storage_class_a"
STORAGE_CLASS_B = "storage_class_b"

NO_STORAGE_CLASS_FAILURE_MESSAGE = (
f"Test failed: {'{storage_class}'} storage class is not deployed. "
f"Available storage classes: {'{cluster_storage_classes_names}'}. "
"Ensure the correct storage_class is set in the global_config, "
"or override it with the pytest params: "
f"--tc={STORAGE_CLASS_A}:<storage_class_name> "
f"--tc={STORAGE_CLASS_B}:<storage_class_name>"
)
59 changes: 50 additions & 9 deletions tests/storage/cross_cluster_live_migration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import pytest
import requests
import yaml
from kubernetes.dynamic import DynamicClient
from kubernetes.dynamic.exceptions import NotFoundError
from ocp_resources.data_source import DataSource
from ocp_resources.datavolume import DataVolume
Expand All @@ -18,6 +19,7 @@
from ocp_resources.resource import get_client
from ocp_resources.route import Route
from ocp_resources.secret import Secret
from ocp_resources.storage_class import StorageClass
from ocp_resources.storage_map import StorageMap
from ocp_resources.virtual_machine_cluster_instancetype import (
VirtualMachineClusterInstancetype,
Expand All @@ -33,6 +35,7 @@
configure_hco_live_migration_network,
get_vm_boot_id_via_console,
)
from tests.storage.utils import get_storage_class_for_storage_migration
from utilities.artifactory import (
get_artifactory_config_map,
get_artifactory_secret,
Expand Down Expand Up @@ -344,22 +347,53 @@ def local_cluster_mtv_provider_for_local_cluster(admin_client, mtv_namespace):


@pytest.fixture(scope="module")
def remote_cluster_storage_classes_names(remote_admin_client: DynamicClient) -> list[str]:
"""Get list of all storage class names available in the remote cluster."""
Comment thread
jpeimer marked this conversation as resolved.
return [sc.name for sc in list(StorageClass.get(client=remote_admin_client))]


@pytest.fixture(scope="class")
def remote_cluster_source_storage_class(
request: pytest.FixtureRequest, remote_cluster_storage_classes_names: list[str]
) -> str:
"""Storage class for creating VMs in the remote cluster before migration."""
Comment thread
jpeimer marked this conversation as resolved.
return get_storage_class_for_storage_migration(
storage_class=request.param["source_storage_class"],
cluster_storage_classes_names=remote_cluster_storage_classes_names,
)


@pytest.fixture(scope="class")
def local_cluster_target_storage_class(request: pytest.FixtureRequest, cluster_storage_classes_names: list[str]) -> str:
"""Storage class for migrated VMs in the local cluster."""
Comment thread
jpeimer marked this conversation as resolved.
return get_storage_class_for_storage_migration(
storage_class=request.param["target_storage_class"],
cluster_storage_classes_names=cluster_storage_classes_names,
)


@pytest.fixture(scope="class")
def local_cluster_mtv_storage_map(
admin_client, local_cluster_mtv_provider_for_local_cluster, local_cluster_mtv_provider_for_remote_cluster
admin_client,
local_cluster_mtv_provider_for_local_cluster,
local_cluster_mtv_provider_for_remote_cluster,
unique_suffix,
remote_cluster_source_storage_class,
local_cluster_target_storage_class,
):
"""
Create a StorageMap resource for MTV migration.
Maps storage classes between source and destination clusters.
"""
mapping = [
{
"source": {"name": py_config["default_storage_class"]},
"destination": {"storageClass": py_config["default_storage_class"]},
"source": {"name": remote_cluster_source_storage_class},
"destination": {"storageClass": local_cluster_target_storage_class},
}
]
with StorageMap(
client=admin_client,
name="storage-map",
name=f"storage-map-{unique_suffix}",
namespace=local_cluster_mtv_provider_for_local_cluster.namespace,
source_provider_name=local_cluster_mtv_provider_for_remote_cluster.name,
source_provider_namespace=local_cluster_mtv_provider_for_remote_cluster.namespace,
Expand Down Expand Up @@ -428,7 +462,10 @@ def remote_cluster_rhel10_data_source(remote_admin_client, remote_cluster_golden

@pytest.fixture(scope="class")
def vm_for_cclm_from_template_with_data_source(
remote_admin_client, remote_cluster_source_test_namespace, remote_cluster_rhel10_data_source
remote_admin_client,
remote_cluster_source_test_namespace,
remote_cluster_rhel10_data_source,
remote_cluster_source_storage_class,
):
with VirtualMachineForTests(
name="vm-from-template-and-data-source",
Expand All @@ -437,7 +474,7 @@ def vm_for_cclm_from_template_with_data_source(
os_flavor=OS_FLAVOR_RHEL,
data_volume_template=data_volume_template_with_source_ref_dict(
data_source=remote_cluster_rhel10_data_source,
storage_class=py_config["default_storage_class"],
storage_class=remote_cluster_source_storage_class,
),
memory_guest=Images.Rhel.DEFAULT_MEMORY_SIZE,
) as vm:
Expand All @@ -447,7 +484,10 @@ def vm_for_cclm_from_template_with_data_source(

@pytest.fixture(scope="class")
def vm_for_cclm_with_instance_type(
remote_admin_client, remote_cluster_source_test_namespace, remote_cluster_rhel10_data_source
remote_admin_client,
remote_cluster_source_test_namespace,
remote_cluster_rhel10_data_source,
remote_cluster_source_storage_class,
):
with VirtualMachineForTests(
name="vm-with-instance-type",
Expand All @@ -458,7 +498,7 @@ def vm_for_cclm_with_instance_type(
vm_preference=VirtualMachineClusterPreference(name=RHEL10_PREFERENCE, client=remote_admin_client),
data_volume_template=data_volume_template_with_source_ref_dict(
data_source=remote_cluster_rhel10_data_source,
storage_class=py_config["default_storage_class"],
storage_class=remote_cluster_source_storage_class,
),
) as vm:
vm.start()
Expand Down Expand Up @@ -489,6 +529,7 @@ def remote_cluster_artifactory_config_map_scope_class(remote_admin_client, remot
def vm_for_cclm_windows_with_instance_type(
remote_admin_client,
remote_cluster_source_test_namespace,
remote_cluster_source_storage_class,
remote_cluster_artifactory_secret_scope_class,
remote_cluster_artifactory_config_map_scope_class,
):
Expand All @@ -499,7 +540,7 @@ def vm_for_cclm_windows_with_instance_type(
api_name="storage",
source="registry",
size=Images.Windows.CONTAINER_DISK_DV_SIZE,
storage_class=py_config["default_storage_class"],
storage_class=remote_cluster_source_storage_class,
url=f"{get_test_artifact_server_url(schema='registry')}/{WINDOWS_2022[CONTAINER_DISK_IMAGE_PATH_STR]}",
secret=remote_cluster_artifactory_secret_scope_class,
cert_configmap=remote_cluster_artifactory_config_map_scope_class.name,
Expand Down
99 changes: 95 additions & 4 deletions tests/storage/cross_cluster_live_migration/test_cclm.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
"""
Cross-cluster live migration tests.

Jira epic: https://redhat.atlassian.net/browse/CNV-50823 # <skip-jira-utils-check>
"""
Comment thread
coderabbitai[bot] marked this conversation as resolved.

import pytest
from pytest_testconfig import config as py_config
Comment thread
jpeimer marked this conversation as resolved.

from tests.storage.constants import TEST_FILE_CONTENT, TEST_FILE_NAME
from tests.storage.constants import STORAGE_CLASS_A, STORAGE_CLASS_B, TEST_FILE_CONTENT, TEST_FILE_NAME
from tests.storage.cross_cluster_live_migration.utils import (
assert_vms_are_stopped,
assert_vms_can_be_deleted,
Expand All @@ -13,6 +20,7 @@

TESTS_CLASS_NAME_SEVERAL_VMS = "TestCCLMSeveralVMs"
TESTS_CLASS_NAME_WINDOWS_VM = "TestCCLMWindowsWithVTPM"
TESTS_CLASS_NAME_STORAGE_A_TO_B = "TestCCLMFromStorageAtoB"
Comment thread
jpeimer marked this conversation as resolved.

pytestmark = [
pytest.mark.cclm,
Expand All @@ -25,9 +33,11 @@


@pytest.mark.parametrize(
"vms_for_cclm",
"remote_cluster_source_storage_class, local_cluster_target_storage_class, vms_for_cclm",
[
pytest.param(
{"source_storage_class": py_config[STORAGE_CLASS_B]},
{"target_storage_class": py_config[STORAGE_CLASS_B]},
{
"vms_fixtures": [
"vm_for_cclm_from_template_with_data_source",
Expand All @@ -38,6 +48,7 @@
],
indirect=True,
)
@pytest.mark.usefixtures("remote_cluster_source_storage_class", "local_cluster_target_storage_class")
class TestCCLMSeveralVMs:
@pytest.mark.polarion("CNV-11995")
@pytest.mark.dependency(name=f"{TESTS_CLASS_NAME_SEVERAL_VMS}::test_migrate_vm_from_remote_to_local_cluster")
Expand Down Expand Up @@ -93,16 +104,18 @@ def test_target_vms_can_be_deleted(self, local_vms_after_cclm_migration):


@pytest.mark.parametrize(
"dv_wait_timeout, vms_for_cclm",
"remote_cluster_source_storage_class, local_cluster_target_storage_class, dv_wait_timeout, vms_for_cclm",
[
pytest.param(
{"source_storage_class": py_config[STORAGE_CLASS_B]},
{"target_storage_class": py_config[STORAGE_CLASS_B]},
{"dv_wait_timeout": TIMEOUT_50MIN},
{"vms_fixtures": ["vm_for_cclm_windows_with_instance_type"]},
)
],
indirect=True,
)
@pytest.mark.usefixtures("dv_wait_timeout")
@pytest.mark.usefixtures("remote_cluster_source_storage_class", "local_cluster_target_storage_class", "dv_wait_timeout")
class TestCCLMWindowsWithVTPM:
@pytest.mark.dependency(name=f"{TESTS_CLASS_NAME_WINDOWS_VM}::test_migrate_windows_vm_from_remote_to_local_cluster")
@pytest.mark.polarion("CNV-11999")
Expand Down Expand Up @@ -139,3 +152,81 @@ def test_source_vms_can_be_deleted(self, vms_for_cclm):
@pytest.mark.polarion("CNV-15236")
def test_target_vms_can_be_deleted(self, local_vms_after_cclm_migration):
assert_vms_can_be_deleted(vms=local_vms_after_cclm_migration)


@pytest.mark.parametrize(
"remote_cluster_source_storage_class, local_cluster_target_storage_class, vms_for_cclm",
[
pytest.param(
{"source_storage_class": py_config[STORAGE_CLASS_A]},
{"target_storage_class": py_config[STORAGE_CLASS_B]},
{
"vms_fixtures": [
"vm_for_cclm_with_instance_type",
]
},
)
],
indirect=True,
)
@pytest.mark.usefixtures("remote_cluster_source_storage_class", "local_cluster_target_storage_class")
class TestCCLMFromStorageAtoB:
Comment thread
jpeimer marked this conversation as resolved.
@pytest.mark.polarion("CNV-15955")
Comment thread
jpeimer marked this conversation as resolved.
@pytest.mark.dependency(name=f"{TESTS_CLASS_NAME_STORAGE_A_TO_B}::test_migrate_vm_from_remote_to_local_cluster")
Comment thread
jpeimer marked this conversation as resolved.
def test_migrate_vm_from_remote_to_local_cluster(
self,
written_file_to_vms_before_cclm,
vms_boot_id_before_cclm,
mtv_migration,
):
mtv_migration.wait_for_condition(
condition=mtv_migration.Condition.Type.SUCCEEDED,
status=mtv_migration.Condition.Status.TRUE,
timeout=TIMEOUT_10MIN,
stop_condition=mtv_migration.Status.FAILED,
)

@pytest.mark.dependency(
depends=[f"{TESTS_CLASS_NAME_STORAGE_A_TO_B}::test_migrate_vm_from_remote_to_local_cluster"]
)
@pytest.mark.polarion("CNV-15956")
def test_verify_vms_not_rebooted_after_migration(self, local_vms_after_cclm_migration, vms_boot_id_before_cclm):
verify_vms_boot_id_after_cross_cluster_live_migration(
local_vms=local_vms_after_cclm_migration, initial_boot_id=vms_boot_id_before_cclm
)

@pytest.mark.dependency(
depends=[f"{TESTS_CLASS_NAME_STORAGE_A_TO_B}::test_migrate_vm_from_remote_to_local_cluster"]
)
@pytest.mark.polarion("CNV-15957")
def test_verify_file_persisted_after_migration(self, local_vms_after_cclm_migration):
for vm in local_vms_after_cclm_migration:
check_file_in_vm(
vm=vm,
file_name=TEST_FILE_NAME,
file_content=TEST_FILE_CONTENT,
username=vm.username,
password=vm.password,
)

@pytest.mark.dependency(
depends=[f"{TESTS_CLASS_NAME_STORAGE_A_TO_B}::test_migrate_vm_from_remote_to_local_cluster"]
)
@pytest.mark.polarion("CNV-15958")
def test_source_vms_are_stopped_after_cclm(self, vms_for_cclm):
assert_vms_are_stopped(vms=vms_for_cclm)

@pytest.mark.dependency(
depends=[f"{TESTS_CLASS_NAME_STORAGE_A_TO_B}::test_migrate_vm_from_remote_to_local_cluster"]
)
@pytest.mark.polarion("CNV-15954")
def test_compute_live_migrate_vms_after_cclm(self, local_vms_after_cclm_migration):
verify_compute_live_migration_after_cclm(local_vms=local_vms_after_cclm_migration)

@pytest.mark.polarion("CNV-15959")
def test_source_vms_can_be_deleted(self, vms_for_cclm):
Comment thread
jpeimer marked this conversation as resolved.
assert_vms_can_be_deleted(vms=vms_for_cclm)

@pytest.mark.polarion("CNV-15960")
def test_target_vms_can_be_deleted(self, local_vms_after_cclm_migration):
assert_vms_can_be_deleted(vms=local_vms_after_cclm_migration)
3 changes: 1 addition & 2 deletions tests/storage/storage_migration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,9 @@
)
from tests.storage.storage_migration.utils import (
build_namespaces_spec_for_storage_migration,
get_storage_class_for_storage_migration,
wait_for_storage_migration_completed,
)
from tests.storage.utils import create_windows_directory
from tests.storage.utils import create_windows_directory, get_storage_class_for_storage_migration
from utilities.artifactory import get_http_image_url
from utilities.constants import (
OS_FLAVOR_FEDORA,
Expand Down
12 changes: 0 additions & 12 deletions tests/storage/storage_migration/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,5 @@
WINDOWS_FILE_BEFORE_STORAGE_MIGRATION = f"{FILE_BEFORE_STORAGE_MIGRATION}.txt"
WINDOWS_FILE_WITH_PATH = f"{WINDOWS_TEST_DIRECTORY_PATH}\\{WINDOWS_FILE_BEFORE_STORAGE_MIGRATION}"

STORAGE_CLASS_A = "storage_class_a"
STORAGE_CLASS_B = "storage_class_b"

NO_STORAGE_CLASS_FAILURE_MESSAGE = (
f"Test failed: {'{storage_class}'} storage class is not deployed. "
f"Available storage classes: {'{cluster_storage_classes_names}'}. "
"Ensure the correct storage_class is set in the global_config, "
"or override it with the pytest params: "
f"--tc={STORAGE_CLASS_A}:<storage_class_name> "
f"--tc={STORAGE_CLASS_B}:<storage_class_name>"
)

HOTPLUGGED_DEVICE = "/dev/sda"
MOUNT_HOTPLUGGED_DEVICE_PATH = "/mnt/hotplug"
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@
from pytest_testconfig import config as py_config

from tests.os_params import FEDORA_LATEST, FEDORA_LATEST_LABELS
from tests.storage.constants import STORAGE_CLASS_A, STORAGE_CLASS_B
from tests.storage.storage_migration.constants import (
CONTENT,
FILE_BEFORE_STORAGE_MIGRATION,
STORAGE_CLASS_A,
STORAGE_CLASS_B,
WINDOWS_FILE_WITH_PATH,
)
from tests.storage.storage_migration.utils import (
Expand Down
13 changes: 0 additions & 13 deletions tests/storage/storage_migration/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import shlex

import pytest
from ocp_resources.multi_namespace_virtual_machine_storage_migration import MultiNamespaceVirtualMachineStorageMigration
from ocp_resources.persistent_volume_claim import PersistentVolumeClaim
from pyhelper_utils.shell import run_ssh_commands
Expand All @@ -10,7 +9,6 @@
CONTENT,
FILE_BEFORE_STORAGE_MIGRATION,
MOUNT_HOTPLUGGED_DEVICE_PATH,
NO_STORAGE_CLASS_FAILURE_MESSAGE,
)
from tests.storage.utils import check_file_in_vm
from utilities.constants import TIMEOUT_2MIN, TIMEOUT_5SEC, TIMEOUT_10MIN, TIMEOUT_10SEC
Expand Down Expand Up @@ -76,17 +74,6 @@ def verify_storage_migration_succeeded(
verify_vm_storage_class_updated(vm=vm, target_storage_class=target_storage_class)


def get_storage_class_for_storage_migration(storage_class: str, cluster_storage_classes_names: list[str]) -> str:
if storage_class in cluster_storage_classes_names:
return storage_class
else:
pytest.fail(
NO_STORAGE_CLASS_FAILURE_MESSAGE.format(
storage_class=storage_class, cluster_storage_classes_names=cluster_storage_classes_names
)
)


def verify_file_in_hotplugged_disk(vm: VirtualMachineForTests, file_name: str, file_content: str) -> None:
output = run_ssh_commands(
host=vm.ssh_exec,
Expand Down
Loading