Skip to content
Draft
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
1 change: 1 addition & 0 deletions tests/storage/windows/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Windows golden image tests for self-validation
120 changes: 120 additions & 0 deletions tests/storage/windows/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
"""
Fixtures for Windows storage tests using self-validation golden image.

These tests require a Windows 11 golden image DataSource to be created beforehand
via the self-validation setup script (setup-golden-image.sh).
"""

import os

import pytest
from ocp_resources.data_source import DataSource
from ocp_resources.datavolume import DataVolume
from ocp_resources.virtual_machine_cluster_instancetype import VirtualMachineClusterInstancetype
from ocp_resources.virtual_machine_cluster_preference import VirtualMachineClusterPreference
from ocp_resources.virtual_machine_instance import VirtualMachineInstance

from utilities.constants import TIMEOUT_5SEC, TIMEOUT_10MIN, U1_LARGE
from utilities.storage import add_dv_to_vm, data_volume_template_with_source_ref_dict
from utilities.virt import VirtualMachineForTests, running_vm

WINDOWS_GOLDEN_IMAGE_NAME = "windows11-golden-image"
WINDOWS_GOLDEN_IMAGE_NAMESPACE = "openshift-virtualization-os-images"
BLANK_DATA_DISK_SIZE = "1Gi"
WINDOWS_11_PREFERENCE = "windows.11"


@pytest.fixture(scope="session")
def skip_if_windows_eula_not_accepted():
"""Skip Windows tests if ACCEPT_WINDOWS_EULA is not set to true."""
if os.environ.get("ACCEPT_WINDOWS_EULA", "").lower() != "true":
pytest.skip(
"Windows tests require ACCEPT_WINDOWS_EULA=true. "
"Set this environment variable to accept Microsoft EULA and enable Windows testing."
)


@pytest.fixture(scope="module")
def windows11_golden_image_data_source(unprivileged_client, golden_images_namespace):
"""Get the Windows 11 golden image DataSource created by self-validation setup."""
data_source = DataSource(
client=unprivileged_client,
name=WINDOWS_GOLDEN_IMAGE_NAME,
namespace=golden_images_namespace.name,
)
if not data_source.exists:
pytest.skip(
f"Windows golden image DataSource '{WINDOWS_GOLDEN_IMAGE_NAME}' not found in "
f"'{golden_images_namespace.name}'. Run self-validation with ACCEPT_WINDOWS_EULA=true to create it."
)
data_source.wait_for_condition(
condition=data_source.Condition.READY,
status=data_source.Condition.Status.TRUE,
timeout=TIMEOUT_5SEC,
)
return data_source


@pytest.fixture(scope="class")
def windows_vm_from_golden_image(
unprivileged_client,
namespace,
windows11_golden_image_data_source,
):
"""Create a Windows VM from the self-validation golden image DataSource."""
with VirtualMachineForTests(
client=unprivileged_client,
name=f"{windows11_golden_image_data_source.name}-test-vm",
namespace=namespace.name,
vm_instance_type=VirtualMachineClusterInstancetype(client=unprivileged_client, name=U1_LARGE),
vm_preference=VirtualMachineClusterPreference(client=unprivileged_client, name=WINDOWS_11_PREFERENCE),
data_volume_template=data_volume_template_with_source_ref_dict(
data_source=windows11_golden_image_data_source,
),
) as vm:
yield vm


@pytest.fixture(scope="class")
def blank_data_disk_template(namespace, snapshot_storage_class_name_scope_module):
"""Create a blank DataVolume template dict for use as a second disk."""
dv = DataVolume(
name="windows-data-disk",
namespace=namespace.name,
source="blank",
size=BLANK_DATA_DISK_SIZE,
storage_class=snapshot_storage_class_name_scope_module,
api_name="storage",
)
dv.to_dict()
return dv.res


@pytest.fixture(scope="class")
def windows_vm_with_data_disk(
unprivileged_client,
namespace,
windows11_golden_image_data_source,
snapshot_storage_class_name_scope_module,
blank_data_disk_template,
):
"""Create a running Windows VM with boot disk + blank data disk, guest agent connected."""
with VirtualMachineForTests(
client=unprivileged_client,
name="windows-snapshot-test-vm",
namespace=namespace.name,
vm_instance_type=VirtualMachineClusterInstancetype(client=unprivileged_client, name=U1_LARGE),
vm_preference=VirtualMachineClusterPreference(client=unprivileged_client, name=WINDOWS_11_PREFERENCE),
data_volume_template=data_volume_template_with_source_ref_dict(
data_source=windows11_golden_image_data_source,
storage_class=snapshot_storage_class_name_scope_module,
),
) as vm:
add_dv_to_vm(vm=vm, template_dv=blank_data_disk_template)
running_vm(vm=vm, wait_for_interfaces=True, check_ssh_connectivity=False)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

HIGH: Keep SSH validation enabled for the Windows snapshot fixture.

This fixture is the readiness gate for the snapshot scenario. With check_ssh_connectivity=False, a Windows image can pass setup even when SSH/WSL2 is broken, so the snapshot test stops validating the expected Windows image contract.

Suggested fix
-        running_vm(vm=vm, wait_for_interfaces=True, check_ssh_connectivity=False)
+        running_vm(vm=vm, wait_for_interfaces=True)

Based on learnings: In tests/infrastructure/instance_types/supported_os/test_windows_os.py, Windows guest images are configured with SSH support, so running_vm() should keep the default check_ssh_connectivity=True to verify SSH connectivity is working properly in the Windows guest images.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/storage/windows/conftest.py` at line 114, The Windows snapshot fixture
is disabling SSH validation by calling running_vm(vm=vm,
wait_for_interfaces=True, check_ssh_connectivity=False); revert this so SSH
connectivity is validated: remove the check_ssh_connectivity=False override (or
set it to True) in the running_vm call inside the Windows snapshot fixture in
tests/storage/windows/conftest.py to ensure the fixture verifies SSH/WSL2
connectivity as expected.

vm.vmi.wait_for_condition(
condition=VirtualMachineInstance.Condition.Type.AGENT_CONNECTED,
status=VirtualMachineInstance.Condition.Status.TRUE,
timeout=TIMEOUT_10MIN,
)
yield vm
80 changes: 80 additions & 0 deletions tests/storage/windows/test_windows_app_consistent_snapshot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"""
Windows app-consistent snapshot test.

Verifies that an online VirtualMachineSnapshot of a multi-disk Windows VM
(boot + data disk) freezes for less than 10 seconds, proving guest agent
freeze/thaw works correctly on the storage provider.
Comment on lines +4 to +6

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

LOW: Align the documented freeze limit with the enforced threshold.

The module docstring says < 10s, but the code enforces < 20s. That mismatch will mislead anyone triaging runs around the 10-20 second range.

Suggested fix
- Verifies that an online VirtualMachineSnapshot of a multi-disk Windows VM
- (boot + data disk) freezes for less than 10 seconds, proving guest agent
+ Verifies that an online VirtualMachineSnapshot of a multi-disk Windows VM
+ (boot + data disk) freezes for less than 20 seconds, proving guest agent
  freeze/thaw works correctly on the storage provider.

Also applies to: 21-21

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/storage/windows/test_windows_app_consistent_snapshot.py` around lines 4
- 6, The module docstring stating the freeze limit as "< 10s" is inconsistent
with the test assertion that enforces "< 20s"; update the module-level docstring
at the top of tests/storage/windows/test_windows_app_consistent_snapshot.py to
state "< 20s" so it matches the enforced threshold (or, if policy is to enforce
10s, instead change the assertion that checks the freeze duration to use 10
seconds). Ensure you edit the module docstring and confirm consistency with the
assertion/threshold used in the test (the line that asserts the measured freeze
duration is less than 20 seconds).

"""

import logging

import pytest
from dateutil import parser as date_parser
from ocp_resources.virtual_machine_snapshot import VirtualMachineSnapshot
from timeout_sampler import TimeoutSampler

from tests.storage.utils import check_snapshot_indication
from utilities.constants import TIMEOUT_10MIN

LOGGER = logging.getLogger(__name__)

FREEZE_THRESHOLD_SECONDS = 20


pytestmark = [
pytest.mark.windows,
pytest.mark.conformance,
pytest.mark.storage,
pytest.mark.high_resource_vm,
pytest.mark.usefixtures("skip_if_windows_eula_not_accepted"),
]


class TestWindowsAppConsistentSnapshot:
"""Test app-consistent online snapshot of a multi-disk Windows VM."""

@pytest.mark.polarion("CNV-16100")
def test_windows_multi_disk_snapshot_freeze_within_threshold(
self,
windows_vm_with_data_disk,
):
"""
Test that the freeze window of an online snapshot of a 2-disk Windows VM
completes within 20 seconds.

Measures the time between snapshot creation and status.creationTime
(point-in-time capture), which represents the guest agent freeze duration.
The backend may take longer to finalize, but the VM is already unfrozen.
"""
vm = windows_vm_with_data_disk

LOGGER.info(f"Creating online snapshot of Windows VM {vm.name} with 2 disks...")
with VirtualMachineSnapshot(
name=f"snapshot-{vm.name}",
namespace=vm.namespace,
vm_name=vm.name,
) as snapshot:
for creation_time in TimeoutSampler(
wait_timeout=TIMEOUT_10MIN,
sleep=1,
func=lambda: snapshot.instance.get("status", {}).get("creationTime"),
):
if creation_time:
break

snapshot_created = date_parser.parse(timestr=snapshot.instance.metadata.creationTimestamp)
snapshot_captured = date_parser.parse(timestr=creation_time)
freeze_seconds = (snapshot_captured - snapshot_created).total_seconds()

LOGGER.info(
f"Freeze window: {freeze_seconds:.1f}s (created={snapshot_created}, captured={snapshot_captured})"
)

snapshot.wait_snapshot_done(timeout=TIMEOUT_10MIN)

check_snapshot_indication(snapshot=snapshot, is_online=True)
LOGGER.info("Online indication confirmed - app-consistent snapshot verified")

assert freeze_seconds < FREEZE_THRESHOLD_SECONDS, (
f"Freeze took {freeze_seconds:.1f}s, expected < {FREEZE_THRESHOLD_SECONDS}s"
)
68 changes: 68 additions & 0 deletions tests/storage/windows/test_windows_golden_image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
"""
Windows golden image storage tests.

These tests verify that Windows VMs can be created from the golden image
DataSource created by the self-validation setup (setup-golden-image.sh).

Requirements:
- Windows golden image DataSource must exist (created when ACCEPT_WINDOWS_EULA=true)
- ACCEPT_WINDOWS_EULA=true environment variable must be set
"""

import logging

import pytest
from ocp_resources.virtual_machine_instance import VirtualMachineInstance

from utilities.constants import TIMEOUT_10MIN
from utilities.virt import get_guest_os_info, running_vm

LOGGER = logging.getLogger(__name__)

pytestmark = [
pytest.mark.windows,
pytest.mark.conformance,
pytest.mark.storage,
pytest.mark.high_resource_vm,
pytest.mark.usefixtures("skip_if_windows_eula_not_accepted"),
]


class TestWindowsGoldenImage:
"""Test Windows VM creation from self-validation golden image DataSource."""

@pytest.mark.polarion("CNV-16101")
def test_windows_vm_boots_from_golden_image(
self,
windows_vm_from_golden_image,
):
"""
Test that a Windows VM can boot from the golden image.

This test verifies:
1. VM can be created from the Windows golden image DataSource
2. VM boots successfully
3. Guest agent connects (indicates Windows is running properly)
4. Guest agent reports Windows OS info
"""
vm = windows_vm_from_golden_image

LOGGER.info(f"Starting Windows VM {vm.name} from golden image...")
running_vm(vm=vm, wait_for_interfaces=True, check_ssh_connectivity=False)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

HIGH: Re-enable SSH checks in the golden-image validation path.

This is the test that proves the golden image is usable. Disabling SSH verification means the test can still pass when the Windows image boots and reports guest-agent info but its SSH/WSL2 path is broken.

Suggested fix
-        running_vm(vm=vm, wait_for_interfaces=True, check_ssh_connectivity=False)
+        running_vm(vm=vm, wait_for_interfaces=True)

Based on learnings: In tests/infrastructure/instance_types/supported_os/test_windows_os.py, Windows guest images are configured with SSH support, so running_vm() should keep the default check_ssh_connectivity=True to verify SSH connectivity is working properly in the Windows guest images.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/storage/windows/test_windows_golden_image.py` at line 51, The test
disables SSH verification for the golden-image path by calling running_vm(vm=vm,
wait_for_interfaces=True, check_ssh_connectivity=False); restore SSH checks by
removing the override or setting check_ssh_connectivity=True so running_vm uses
the Windows SSH validation (matching behavior in
tests/infrastructure/instance_types/supported_os/test_windows_os.py). Update the
call in tests/storage/windows/test_windows_golden_image.py to call running_vm
with the default (or explicit True) for check_ssh_connectivity to ensure
SSH/WSL2 paths are validated.


LOGGER.info("Waiting for Windows guest agent to connect...")
vm.vmi.wait_for_condition(
condition=VirtualMachineInstance.Condition.Type.AGENT_CONNECTED,
status=VirtualMachineInstance.Condition.Status.TRUE,
timeout=TIMEOUT_10MIN,
)

LOGGER.info("Validating Windows OS info from guest agent...")
os_info = get_guest_os_info(vmi=vm.vmi)
assert os_info, "VMI doesn't have guest agent data"

os_name = os_info.get("name", "").lower()
LOGGER.info(f"Guest agent reports OS: {os_name}")
assert "windows" in os_name, f"Expected Windows OS, but got: {os_name}"

LOGGER.info("Windows VM booted successfully from golden image!")