diff --git a/tests/foreman/ui/test_rhcloud_insights_vulnerability.py b/tests/foreman/ui/test_rhcloud_insights_vulnerability.py index bd69d97e4dd..cd3d4673903 100644 --- a/tests/foreman/ui/test_rhcloud_insights_vulnerability.py +++ b/tests/foreman/ui/test_rhcloud_insights_vulnerability.py @@ -22,6 +22,7 @@ from robottelo import constants from robottelo.constants import OPENSSH_RECOMMENDATION +from robottelo.utils.datafactory import gen_string def _safe_ui_call(fn, *fn_args): @@ -1342,3 +1343,268 @@ def _has_restored_vulnerabilities(): assert status['Red Hat Lightspeed']['Status'] == 'Reporting', ( 'Host should be reporting to Red Hat Lightspeed after re-registration' ) + + +@pytest.mark.no_containers +@pytest.mark.rhel_ver_match('10') +@pytest.mark.parametrize('module_target_sat_insights', [False], ids=['local'], indirect=True) +def test_positive_iop_services_with_alternate_hostname_fact( + rhel_contenthost, + rhcloud_manifest_org, + module_target_sat_insights, + rhcloud_activation_key, + setup_content_for_iop, +): + """Verify IoP Advisor and Vulnerability services work correctly when using alternate hostname fact. + + This test validates that when Satellite and Insights are configured to use + the same alternate display name (via custom RHSM fact), the host registration, + IoP services, and re-registration all function correctly without creating + duplicate host records. + + :id: a1b2c3d4-e5f6-7890-abcd-ef1234567890 + + :steps: + 1. Configure Satellite to use alternate hostname fact (network.hostname-override) + 2. Create custom RHSM fact on the client with alternate hostname + 3. Configure Insights to use the same alternate display name + 4. Verify the alternate hostname is resolvable from Satellite + 5. Register the host using global registration with setup_insights=true + 6. Verify the host appears in Satellite using the alternate display name + 7. Verify the host appears in Insights using the alternate display name + 8. Verify successful IoP registration + 9. Create vulnerabilities and recommendations on the host + 10. Verify Vulnerability service displays data correctly + 11. Verify Advisor service shows recommendations correctly + 12. Force re-register using global registration with force=true + 13. Verify the host continues to use the alternate display name + 14. Verify successful IoP registration after re-registration + 15. Re-verify Vulnerability and Advisor services + 16. Verify no duplicate host records were created + 17. Verify no stale IoP entries exist + + :expectedresults: + 1. Satellite accepts and uses the alternate hostname fact + 2. Host registers successfully with alternate display name + 3. IoP services (Vulnerability and Advisor) work correctly with alternate hostname + 4. Re-registration maintains the alternate hostname without duplicates + 5. No stale IoP entries are left after re-registration + + :customerscenario: true + + :Verifies: SAT-44011 + """ + satellite = module_target_sat_insights + client = rhel_contenthost + org = rhcloud_manifest_org + + # Define the alternate hostname with a meaningful prefix and random suffix + hostname_prefix = f'alt-insights-{gen_string("alpha", 6).lower()}' + alternate_hostname = f'{hostname_prefix}.{client.hostname.split(".", 1)[-1]}' + + # Step 1: Configure Satellite to use alternate hostname fact + result = satellite.execute( + 'hammer settings set --name register_hostname_fact --value network.hostname-override' + ) + assert result.status == 0, f'Failed to configure Satellite hostname fact: {result.stderr}' + + # Step 2: Create custom RHSM fact on the client with alternate hostname + rhsm_fact_content = f'{{"network.hostname-override":"{alternate_hostname}"}}' + assert client.execute('mkdir -p /etc/rhsm/facts').status == 0 + result = client.execute(f'echo \'{rhsm_fact_content}\' > /etc/rhsm/facts/hostname.facts') + assert result.status == 0, f'Failed to create RHSM fact file: {result.stderr}' + + # Step 3: Configure Insights to use the same alternate display name + result = client.execute( + f'echo "display_name={alternate_hostname}" >> /etc/insights-client/insights-client.conf' + ) + assert result.status == 0, f'Failed to configure Insights display name: {result.stderr}' + + # Step 4: Verify the alternate hostname is resolvable from Satellite + # Add entry to /etc/hosts for resolution + client_ip = client.execute('hostname -I | awk \'{print $1}\'').stdout.strip() + result = satellite.execute(f'echo "{client_ip} {alternate_hostname}" >> /etc/hosts') + assert result.status == 0, f'Failed to add hosts entry on Satellite: {result.stderr}' + + # Verify resolution + result = satellite.execute(f'ping -c 1 {alternate_hostname}') + assert result.status == 0, ( + f'Alternate hostname {alternate_hostname} is not resolvable from Satellite' + ) + + # Step 5: Register the host using global registration with setup_insights=true + result = client.register( + org=org, + loc=None, + activation_keys=[rhcloud_activation_key.name], + target=satellite, + setup_insights=True, + ) + assert result.status == 0, f'Failed to register host with global registration: {result.stderr}' + + # Step 6-7: Verify the host appears in Satellite and Insights using alternate display name + with satellite.ui_session() as session: + session.organization.select(org_name=org.name) + + # Search for host by alternate hostname + search_result = session.host_new.search(alternate_hostname) + assert search_result, f'Host not found with alternate hostname {alternate_hostname}' + assert len(search_result) > 0, ( + f'Search returned empty results for alternate hostname {alternate_hostname}' + ) + assert alternate_hostname in search_result[0]['Name'], ( + f'Host name does not match alternate hostname. ' + f'Expected: {alternate_hostname}, Got: {search_result[0]["Name"]}' + ) + + # Step 8: Verify successful IoP registration + status = session.host_new.get_host_statuses(alternate_hostname) + assert status['Red Hat Lightspeed']['Status'] == 'Reporting', ( + 'IoP registration failed - Red Hat Lightspeed status is not Reporting' + ) + + # Step 9: Create recommendations on the host (lightweight, no package operations) + # Add permission misconfiguration to trigger recommendation + assert client.execute('chmod 777 /etc/shadow').status == 0 + + # Run insights-client to report recommendations + result = client.execute('insights-client') + assert result.status == 0, f'insights-client failed: {result.stdout}' + + # Step 10: Verify Vulnerability service displays data correctly + # Check for any vulnerabilities (natural CVEs from base system packages) + def _has_vulnerabilities(): + session.browser.refresh() + vulns = session.host_new.get_vulnerabilities(alternate_hostname) + return bool(vulns) + + has_vulns, _ = wait_for( + _has_vulnerabilities, + timeout=600, + delay=30, + handle_exception=True, + ) + assert has_vulns, ( + f'No vulnerabilities found for host with alternate hostname {alternate_hostname}' + ) + + vulnerabilities = session.host_new.get_vulnerabilities(alternate_hostname) + # Verify we have vulnerability data (checking for any CVE, not a specific one) + assert len(vulnerabilities) > 0, 'Vulnerabilities list should not be empty' + assert any(vuln.get('CVE ID') for vuln in vulnerabilities), ( + 'No CVE IDs found in vulnerabilities' + ) + + # Step 11: Verify Advisor service shows recommendations correctly + def _has_recommendations(): + session.browser.refresh() + recs = session.host_new.get_recommendations(alternate_hostname) + return bool(recs) + + has_recs, _ = wait_for( + _has_recommendations, + timeout=600, + delay=30, + handle_exception=True, + ) + assert has_recs, ( + f'No recommendations found for host with alternate hostname {alternate_hostname}' + ) + + # Step 12: Force re-register using global registration with force=true + result = client.register( + org=org, + loc=None, + activation_keys=[rhcloud_activation_key.name], + target=satellite, + setup_insights=True, + force=True, + ) + assert result.status == 0, f'Failed to force re-register host: {result.stderr}' + + # Step 13: Verify the host continues to use the alternate display name + session.browser.refresh() + search_result = session.host_new.search(alternate_hostname) + assert search_result, ( + f'Host not found with alternate hostname {alternate_hostname} after re-registration' + ) + assert len(search_result) > 0, ( + f'Search returned empty results for alternate hostname {alternate_hostname} after re-registration' + ) + assert alternate_hostname in search_result[0]['Name'], ( + f'Host name changed after re-registration. ' + f'Expected: {alternate_hostname}, Got: {search_result[0]["Name"]}' + ) + + # Step 14: Verify successful IoP registration after re-registration + status = session.host_new.get_host_statuses(alternate_hostname) + assert status['Red Hat Lightspeed']['Status'] == 'Reporting', ( + 'IoP registration failed after re-registration' + ) + + # Step 15: Re-verify Vulnerability and Advisor services + # Run insights-client to refresh data + result = client.execute('insights-client') + assert result.status == 0, f'insights-client failed after re-registration: {result.stdout}' + + # Verify vulnerabilities still show + def _has_vulnerabilities_after_rereg(): + session.browser.refresh() + vulns = session.host_new.get_vulnerabilities(alternate_hostname) + return bool(vulns) + + has_vulns_after, _ = wait_for( + _has_vulnerabilities_after_rereg, + timeout=600, + delay=30, + handle_exception=True, + ) + assert has_vulns_after, ( + 'Vulnerabilities missing after re-registration with alternate hostname' + ) + + # Verify recommendations still show + def _has_recommendations_after_rereg(): + session.browser.refresh() + recs = session.host_new.get_recommendations(alternate_hostname) + return bool(recs) + + has_recs_after, _ = wait_for( + _has_recommendations_after_rereg, + timeout=600, + delay=30, + handle_exception=True, + ) + assert has_recs_after, ( + 'Recommendations missing after re-registration with alternate hostname' + ) + + # Step 16: Verify no duplicate host records were created + # Search by domain to find all hosts that might match (original or alternate) + domain = client.hostname.split('.', 1)[-1] if '.' in client.hostname else '' + all_hosts_with_domain = session.host_new.search(domain) if domain else [] + + # Filter for hosts that match either original or alternate hostname + matching_hosts = [ + host + for host in all_hosts_with_domain + if client.hostname in host.get('Name', '') or alternate_hostname in host.get('Name', '') + ] + + # Should only find one host (with the alternate hostname) + assert len(matching_hosts) == 1, ( + f'Expected 1 host, found {len(matching_hosts)}. ' + f'Possible duplicate or missing host. Matching hosts: {[h["Name"] for h in matching_hosts]}' + ) + + assert alternate_hostname in matching_hosts[0]['Name'], ( + f'Host should use alternate hostname {alternate_hostname}, ' + f'but found {matching_hosts[0]["Name"]}' + ) + + # Step 17: Verify no stale IoP entries exist + # Verify that vulnerabilities are still associated with the alternate hostname + vulnerabilities_final = session.host_new.get_vulnerabilities(alternate_hostname) + assert len(vulnerabilities_final) > 0, ( + 'Vulnerabilities should still be present after re-registration with alternate hostname' + )