Skip to content
Open
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
35 changes: 34 additions & 1 deletion pytest_fixtures/component/provision_libvirt.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
# Compute resource - Libvirt entities
from fauxfactory import gen_string
import pytest

from robottelo.config import settings
from robottelo.constants import COMPUTE_PROFILE_SMALL, FOREMAN_PROVIDERS, LIBVIRT_RESOURCE_URL

LIBVIRT_URL = LIBVIRT_RESOURCE_URL % settings.libvirt.libvirt_hostname


@pytest.fixture(scope='module')
def module_cr_libvirt(module_target_sat, module_org, module_location):
"""Create a Libvirt compute resource for the module."""
return module_target_sat.api.LibvirtComputeResource(
organization=[module_org], location=[module_location]
name=gen_string('alpha'),
provider=FOREMAN_PROVIDERS['libvirt'],
display_type='VNC',
organization=[module_org],
location=[module_location],
url=LIBVIRT_URL,
).create()


Expand All @@ -19,3 +31,24 @@ def module_libvirt_provisioning_sat(module_provisioning_sat):
# Configure Libvirt CR for provisioning
module_provisioning_sat.sat.configure_libvirt_cr()
return module_provisioning_sat


@pytest.fixture(scope='module')
def module_libvirt_compute_profile(module_target_sat, module_cr_libvirt):
"""Create compute attributes for Libvirt compute profile."""
return module_target_sat.api.ComputeAttribute(
compute_profile=COMPUTE_PROFILE_SMALL,
compute_resource=module_cr_libvirt,
vm_attrs={
'cpus': 1,
'memory': 6442450944,
'firmware': 'Automatic',
'volumes_attributes': {'0': {'capacity': '10G', 'format_type': 'qcow2'}},
'nics_attributes': {
'0': {
'type': 'bridge',
'bridge': f'br-{settings.provisioning.vlan_id}',
},
},
},
).create()
134 changes: 134 additions & 0 deletions tests/foreman/ui/test_computeresource_libvirt.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,3 +211,137 @@ def test_positive_provision_end_to_end(

session.host.delete(name)
assert not sat.api.Host().search(query={'search': f'name="{name}"'})


@pytest.mark.e2e
@pytest.mark.on_premises_provisioning
@pytest.mark.parametrize('setting_update', ['destroy_vm_on_host_delete=True'], indirect=True)
@pytest.mark.parametrize('pxe_loader', ['bios', 'uefi'], indirect=True)
@pytest.mark.rhel_ver_list('[10]')
def test_positive_image_provision_end_to_end(
request,
session,
setting_update,
module_provisioning_rhel_content,
module_libvirt_provisioning_sat,
configure_secureboot_provisioning,
module_sca_manifest_org,
module_location,
module_ssh_key_file,
pxe_loader,
module_cr_libvirt,
default_architecture,
module_libvirt_compute_profile,
):
"""Perform end to end testing for image-based provisioning on Libvirt compute resource.

:id: cd9bc749-4e4f-4639-bc9b-d9fc2a2d98e9

:steps:
1. Configure provisioning setup with required content and networking.
2. Create a Libvirt compute resource image with credentials and OS details.
3. Create a host via UI using image-based provisioning method.
4. Verify the VM is running on the Libvirt hypervisor.
5. Monitor and verify the build status transitions to 'Installed'.
6. If SecureBoot is configured, validate it's enabled on the provisioned host.

:expectedresults:
1. Image is successfully created on the compute resource.
2. Host is created via UI without errors.
3. VM appears in the running state on the Libvirt hypervisor.
4. Build status progresses from 'Pending installation' to 'Installed'.
5. SecureBoot is enabled when UEFI SecureBoot firmware is configured.

:CaseImportance: Critical
"""
# Initialize satellite and network configuration objects
sat = module_libvirt_provisioning_sat.sat
subnet = module_libvirt_provisioning_sat.subnet

# Generate a unique hostname for the test VM
hostname = gen_string('alpha').lower()

# Create an image resource on the Libvirt compute resource
# This image will be used as the base for provisioning the host
image = sat.api.Image(
compute_resource=module_cr_libvirt,
name=gen_string('alpha'),
operatingsystem=module_provisioning_rhel_content.os,
architecture=default_architecture,
username=settings.libvirt.image_username,
password=settings.libvirt.image_password,
uuid=settings.libvirt.libvirt_image_path,
).create()

@request.addfinalizer
def _finalize():
# Cleanup: Delete the image and compute resource after test execution.
sat.provisioning_cleanup(host_fqdn)
cr = sat.api.LibvirtComputeResource().search(
query={'search': f'name={module_cr_libvirt.id}'}
)
sat.api.Image(id=image.id, compute_resource=module_cr_libvirt.id).delete()
if cr:
Comment on lines +280 to +284
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion (testing): Per-test finalizer deletes a module-scoped compute resource fixture, which can interfere with other tests using the same fixture.

This test’s finalizer deletes the Libvirt compute resource provided by module_cr_libvirt (a module-scoped fixture). That means one test can tear down a resource that other tests in the module still need, leading to order-dependent or flaky failures.

Please move the compute resource cleanup into module_cr_libvirt itself (e.g., convert it to a yield fixture and delete the resource there), or otherwise ensure per-test cleanup does not remove shared module-scoped resources so fixture lifetimes stay consistent with their scope.

Suggested implementation:

    @request.addfinalizer
    def _finalize():
        # Cleanup: Delete the image and host after test execution.
        sat.provisioning_cleanup(host_fqdn)

        # Delete the test-scoped image created for this test run.
        sat.api.Image(id=image.id, compute_resource=module_cr_libvirt.id).delete()

        # Delete the host and ensure it is gone.
        session.host.delete(name)
        assert not sat.api.Host().search(query={'search': f'name="{name}"'})

To fully implement the review comment and align resource lifetimes with fixture scopes, you should also:

  1. Locate the module_cr_libvirt fixture (likely in this file or a nearby conftest.py).

  2. Convert it into a yield fixture if it is not already, for example:

    @pytest.fixture(scope="module")
    def module_cr_libvirt():
        cr = sat.api.LibvirtComputeResource(...).create()
        try:
            yield cr
        finally:
            # Module-scoped cleanup: delete the compute resource here
            sat.api.LibvirtComputeResource(id=cr.id).delete()
  3. Remove any other per-test code paths that delete LibvirtComputeResource instances created by module_cr_libvirt, to avoid one test tearing down resources needed by others in the module.

These changes ensure that only module-level teardown deletes the module-scoped Libvirt compute resource, while per-test finalizers remain limited to test-scoped entities like hosts and images.

sat.api.LibvirtComputeResource(id=cr[0].id).delete()

# Begin UI session to create and manage the host
with sat.ui_session() as session:
session.organization.select(org_name=module_sca_manifest_org.name)
session.location.select(loc_name=module_location.name)

# Create a new host via the UI with image-based provisioning
session.host.create(
{
'host.name': hostname,
'host.organization': module_sca_manifest_org.name,
'host.location': module_location.name,
'host.deploy': f'{module_cr_libvirt.name} (Libvirt)',
'host.compute_profile': COMPUTE_PROFILE_SMALL,
'host.content_source': sat.hostname,
'operating_system.architecture': 'x86_64',
'operating_system.operating_system': module_provisioning_rhel_content.os.title,
'provider_content.operating_system.provision_method': 'image',
'operating_system.root_password': gen_string('alpha').lower(),
'interfaces.interface.domain': module_libvirt_provisioning_sat.domain.name,
'interfaces.interface.subnet': f'{subnet.name} ({subnet.network}/{subnet.cidr})',
}
)

# Verify the VM was successfully created and is running on the Libvirt hypervisor
result = sat.execute(
f'su foreman -s /bin/bash -c "virsh -c {LIBVIRT_URL} list --state-running"'
)
assert hostname in result.stdout, f"VM '{hostname}' not found in running VMs on hypervisor"

# Monitor the build status until provisioning completes
# The host should transition from 'Pending installation' to 'Installed'
host_fqdn = f'{hostname}.{module_libvirt_provisioning_sat.domain.name}'
wait_for(
lambda: session.host_new.get_host_statuses(host_fqdn)['Build']['Status']
!= 'Pending installation',
timeout=1800,
delay=30,
fail_func=session.browser.refresh,
silent_failure=True,
handle_exception=True,
)

# Verify the final build status is 'Installed'
values = session.host_new.get_host_statuses(host_fqdn)
assert values['Build']['Status'] == 'Installed'

# If UEFI SecureBoot firmware is configured, verify SecureBoot is enabled on the host
if pxe_loader.vm_firmware == 'uefi_secure_boot':
# Search for the provisioned host to get its details
host_info = sat.api.Host().search(query={'search': f'name="{host_fqdn}"'})
for host in host_info:
# Establish SSH connection to the provisioned host
provisioning_host = ContentHost(host.ip, auth=module_ssh_key_file)

# Wait for the host to complete reboot and SSH service to be available
provisioning_host.wait_for_connection()

# Verify SecureBoot is enabled using mokutil command
assert (
'SecureBoot enabled' in provisioning_host.execute('mokutil --sb-state').stdout
), "SecureBoot is not enabled on the provisioned host"