diff --git a/pytest_fixtures/component/provision_pxe.py b/pytest_fixtures/component/provision_pxe.py index 90e7c1798ff..f39701f408c 100644 --- a/pytest_fixtures/component/provision_pxe.py +++ b/pytest_fixtures/component/provision_pxe.py @@ -313,6 +313,7 @@ def pxe_loader(request): 'bios': {'vm_firmware': 'bios', 'pxe_loader': 'PXELinux BIOS'}, 'uefi': {'vm_firmware': 'uefi', 'pxe_loader': 'Grub2 UEFI'}, 'ipxe': {'vm_firmware': 'bios', 'pxe_loader': 'iPXE Embedded'}, + 'http_uefi': {'vm_firmware': 'uefi', 'pxe_loader': 'Grub2 UEFI HTTP'}, } return Box(PXE_LOADER_MAP[getattr(request, 'param', 'bios')]) diff --git a/tests/foreman/api/test_provisioning.py b/tests/foreman/api/test_provisioning.py index 3b4ded29dc8..17ebe267b0a 100644 --- a/tests/foreman/api/test_provisioning.py +++ b/tests/foreman/api/test_provisioning.py @@ -24,6 +24,41 @@ from robottelo.utils.installer import InstallerCommand +def _read_log(ch, pattern): + """Read the first line from the given channel buffer and return the matching line""" + # read lines until the buffer is empty + for log_line in ch.stdout().splitlines(): + logger.debug(f'foreman-tail: {log_line}') + if re.search(pattern, log_line): + return log_line + else: + return None + + +def _wait_for_log(channel, pattern, timeout=5, delay=0.2): + """_read_log method enclosed in wait_for method""" + matching_log = wait_for( + _read_log, + func_args=( + channel, + pattern, + ), + fail_condition=None, + timeout=timeout, + delay=delay, + logger=logger, + ) + return matching_log.out + + +def assert_host_logs(channel, pattern): + """Reads foreman logs until given pattern found""" + try: + log = _wait_for_log(channel, pattern, timeout=300, delay=10) + assert pattern in log + except TimedOutError: + raise AssertionError(f'Timed out waiting for {pattern} from VM') + @pytest.mark.e2e @pytest.mark.parametrize('pxe_loader', ['bios', 'uefi'], indirect=True) @pytest.mark.on_premises_provisioning @@ -276,3 +311,129 @@ def test_rhel_ipxe_provisioning( # assert that the host is subscribed and consumes # subsctiption provided by the activation key assert provisioning_host.subscribed, 'Host is not subscribed' + + +@pytest.mark.skip_if_open("BZ:2242925") +@pytest.mark.e2e +@pytest.mark.parametrize('pxe_loader', ['http_uefi'], indirect=True) +@pytest.mark.on_premises_provisioning +@pytest.mark.rhel_ver_match('[^6]') +def test_rhel_httpboot_provisioning( + request, + module_provisioning_sat, + module_sca_manifest_org, + module_location, + provisioning_host, + pxe_loader, + module_provisioning_rhel_content, + provisioning_hostgroup, + module_lce_library, + module_default_org_view, +): + """Provision a host using httpboot workflow + + :id: 98c2865e-5d21-402e-ad01-c474b7fc4eee + + :steps: + 1. Configure satellite for provisioning + 2. provision a host using pxe loader as Grub2 UEFI HTTP + 3. Check that resulting host is registered to Satellite + 4. Check host is subscribed to Satellite + + :expectedresults: + 1. Provisioning via HTTP is successful + 2. Host installs right version of RHEL + 3. Satellite is able to run REX job on the host + 4. Host is registered to Satellite and subscription status is 'Success' + + :parametrized: yes + + :BZ: 2242925 + """ + sat = module_provisioning_sat.sat + # update grub2-efi package + sat.cli.Packages.update(packages='grub2-efi', options={'assumeyes': True}) + + host_mac_addr = provisioning_host._broker_args['provisioning_nic_mac_addr'] + host = sat.api.Host( + hostgroup=provisioning_hostgroup, + organization=module_sca_manifest_org, + location=module_location, + name=gen_string('alpha').lower(), + mac=host_mac_addr, + operatingsystem=module_provisioning_rhel_content.os, + subnet=module_provisioning_sat.subnet, + host_parameters_attributes=[ + {'name': 'remote_execution_connect_by_ip', 'value': 'true', 'parameter_type': 'boolean'} + ], + build=True, # put the host in build mode + ).create(create_missing=False) + # Clean up the host to free IP leases on Satellite. + # broker should do that as a part of the teardown, putting here just to make sure. + request.addfinalizer(host.delete) + # Start the VM, do not ensure that we can connect to SSHD + provisioning_host.power_control(ensure=False) + # check for proper HTTP requests + shell = module_provisioning_sat.session.shell() + shell.send('foreman-tail') + assert_host_logs(shell, f'GET /httpboot/grub2/grub.cfg-{host_mac_addr} with 200') + # Host should do call back to the Satellite reporting + # the result of the installation. Wait until Satellite reports that the host is installed. + wait_for( + lambda: host.read().build_status_label != 'Pending installation', + timeout=1500, + delay=10, + ) + host = host.read() + assert host.build_status_label == 'Installed' + + # Change the hostname of the host as we know it already. + # In the current infra environment we do not support + # addressing hosts using FQDNs, falling back to IP. + provisioning_host.hostname = host.ip + # Host is not blank anymore + provisioning_host.blank = False + + # Wait for the host to be rebooted and SSH daemon to be started. + provisioning_host.wait_for_connection() + + # Perform version check and check if root password is properly updated + host_os = host.operatingsystem.read() + expected_rhel_version = f'{host_os.major}.{host_os.minor}' + + if int(host_os.major) >= 9: + assert ( + provisioning_host.execute( + 'echo -e "\nPermitRootLogin yes" >> /etc/ssh/sshd_config; systemctl restart sshd' + ).status + == 0 + ) + host_ssh_os = sat.execute( + f'sshpass -p {settings.provisioning.host_root_password} ' + 'ssh -o StrictHostKeyChecking=no -o PubkeyAuthentication=no -o PasswordAuthentication=yes ' + f'-o UserKnownHostsFile=/dev/null root@{provisioning_host.hostname} cat /etc/redhat-release' + ) + assert host_ssh_os.status == 0 + assert ( + expected_rhel_version in host_ssh_os.stdout + ), f'The installed OS version differs from the expected version {expected_rhel_version}' + + # Run a command on the host using REX to verify that Satellite's SSH key is present on the host + template_id = ( + sat.api.JobTemplate().search(query={'search': 'name="Run Command - Script Default"'})[0].id + ) + job = sat.api.JobInvocation().run( + data={ + 'job_template_id': template_id, + 'inputs': { + 'command': f'subscription-manager config | grep "hostname = {sat.hostname}"' + }, + 'search_query': f"name = {host.name}", + 'targeting_type': 'static_query', + }, + ) + assert job['result'] == 'success', 'Job invocation failed' + + # assert that the host is subscribed and consumes + # subsctiption provided by the activation key + assert provisioning_host.subscribed, 'Host is not subscribed'