From 5bd6638d4e8f98ec3b476e5a3933af750841ab85 Mon Sep 17 00:00:00 2001 From: jnagare Date: Tue, 7 Jul 2026 19:02:01 +0530 Subject: [PATCH 1/4] Add test for IoP services with alternate hostname fact 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. This test covers: - Configuring Satellite to use network.hostname-override fact - Creating custom RHSM fact with alternate hostname using gen_string - Configuring Insights with same alternate display name - Verifying hostname resolution from Satellite - Host registration with global registration - IoP Vulnerability and Advisor services functionality - Force re-registration maintaining alternate hostname - Verification of no duplicate host records or stale IoP entries Implementation notes: - Uses lightweight recommendation (chmod 777) instead of package operations - Verifies natural vulnerabilities from base system packages - Avoids time-consuming dnf update and mariadb installation - Focus is on hostname functionality, not specific CVE testing Changes: - Added gen_string import for random hostname generation - Use meaningful prefix 'alt-insights-' with random suffix - Removed e2e marker as requested - Updated Verifies tag to SAT-44011 Co-Authored-By: Claude Sonnet 4.5 --- .../ui/test_rhcloud_insights_vulnerability.py | 265 ++++++++++++++++++ 1 file changed, 265 insertions(+) diff --git a/tests/foreman/ui/test_rhcloud_insights_vulnerability.py b/tests/foreman/ui/test_rhcloud_insights_vulnerability.py index bd69d97e4dd..d557702055a 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,267 @@ 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 and len(search_result) > 0, ( + f'Host not found with 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 and len(search_result) > 0, ( + f'Host not found with 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 + all_hosts_search = session.host_new.search(client.hostname.split('.')[0]) + # Should only find one host (the alternate hostname one) + assert len(all_hosts_search) == 1, ( + f'Duplicate host records found. Expected 1, got {len(all_hosts_search)}. ' + f'Hosts: {[h["Name"] for h in all_hosts_search]}' + ) + + # 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' + ) + + # Verify host uses alternate hostname in all IoP data (no stale original hostname entries) + assert alternate_hostname in all_hosts_search[0]['Name'], ( + f'Host should use alternate hostname {alternate_hostname}, ' + f'but found {all_hosts_search[0]["Name"]}' + ) From 30d8db101e952a2d159bfff71a9597df8b0e678b Mon Sep 17 00:00:00 2001 From: jnagare Date: Tue, 7 Jul 2026 20:38:35 +0530 Subject: [PATCH 2/4] Fix duplicate host detection logic in alternate hostname test The test was searching for hosts using the original hostname prefix, but since the host is registered with an alternate hostname (alt-insights-*), the search returned no results. Fixed by: - Search by domain name instead of hostname prefix - Filter results to match either original or alternate hostname - Verify only one host exists and it uses the alternate hostname This ensures we properly detect duplicates while accounting for the fact that the host is registered with a different name. --- .../ui/test_rhcloud_insights_vulnerability.py | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/tests/foreman/ui/test_rhcloud_insights_vulnerability.py b/tests/foreman/ui/test_rhcloud_insights_vulnerability.py index d557702055a..bec3edfff94 100644 --- a/tests/foreman/ui/test_rhcloud_insights_vulnerability.py +++ b/tests/foreman/ui/test_rhcloud_insights_vulnerability.py @@ -1588,11 +1588,25 @@ def _has_recommendations_after_rereg(): ) # Step 16: Verify no duplicate host records were created - all_hosts_search = session.host_new.search(client.hostname.split('.')[0]) - # Should only find one host (the alternate hostname one) - assert len(all_hosts_search) == 1, ( - f'Duplicate host records found. Expected 1, got {len(all_hosts_search)}. ' - f'Hosts: {[h["Name"] for h in all_hosts_search]}' + # 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 @@ -1601,9 +1615,3 @@ def _has_recommendations_after_rereg(): assert len(vulnerabilities_final) > 0, ( 'Vulnerabilities should still be present after re-registration with alternate hostname' ) - - # Verify host uses alternate hostname in all IoP data (no stale original hostname entries) - assert alternate_hostname in all_hosts_search[0]['Name'], ( - f'Host should use alternate hostname {alternate_hostname}, ' - f'but found {all_hosts_search[0]["Name"]}' - ) From b416ce612039c96ca0aca7ff6d5c8e88868cf8fc Mon Sep 17 00:00:00 2001 From: jnagare Date: Tue, 7 Jul 2026 22:05:07 +0530 Subject: [PATCH 3/4] Fix PT018 linting errors: break down compound assertions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split compound assertions 'assert X and Y' into separate assertions to comply with PT018 linting rule. Changed: - assert search_result and len(search_result) > 0 → assert search_result → assert len(search_result) > 0 This provides clearer error messages and follows pytest best practices. --- .../foreman/ui/test_rhcloud_insights_vulnerability.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/foreman/ui/test_rhcloud_insights_vulnerability.py b/tests/foreman/ui/test_rhcloud_insights_vulnerability.py index bec3edfff94..88cbdd02f21 100644 --- a/tests/foreman/ui/test_rhcloud_insights_vulnerability.py +++ b/tests/foreman/ui/test_rhcloud_insights_vulnerability.py @@ -1458,8 +1458,9 @@ def test_positive_iop_services_with_alternate_hostname_fact( # Search for host by alternate hostname search_result = session.host_new.search(alternate_hostname) - assert search_result and len(search_result) > 0, ( - f'Host not found with alternate hostname {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. ' @@ -1536,9 +1537,12 @@ def _has_recommendations(): # 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 and len(search_result) > 0, ( + 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"]}' From 3451a4f4c2485b97d4fa7beaa041e41ae25a6036 Mon Sep 17 00:00:00 2001 From: jnagare Date: Tue, 7 Jul 2026 22:07:38 +0530 Subject: [PATCH 4/4] Apply ruff formatting Automatic formatting applied by ruff: - Collapsed multi-line assert statements to single lines where appropriate - Reformatted list comprehension for better readability - Code style improvements for consistency --- .../ui/test_rhcloud_insights_vulnerability.py | 27 ++++++------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/tests/foreman/ui/test_rhcloud_insights_vulnerability.py b/tests/foreman/ui/test_rhcloud_insights_vulnerability.py index 88cbdd02f21..cd3d4673903 100644 --- a/tests/foreman/ui/test_rhcloud_insights_vulnerability.py +++ b/tests/foreman/ui/test_rhcloud_insights_vulnerability.py @@ -1406,32 +1406,24 @@ def test_positive_iop_services_with_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}' - ) + 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' - ) + 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}' - ) + 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' - ) + 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 @@ -1448,9 +1440,7 @@ def test_positive_iop_services_with_alternate_hostname_fact( target=satellite, setup_insights=True, ) - assert result.status == 0, ( - f'Failed to register host with global registration: {result.stderr}' - ) + 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: @@ -1530,9 +1520,7 @@ def _has_recommendations(): setup_insights=True, force=True, ) - assert result.status == 0, ( - f'Failed to force re-register host: {result.stderr}' - ) + 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() @@ -1598,7 +1586,8 @@ def _has_recommendations_after_rereg(): # Filter for hosts that match either original or alternate hostname matching_hosts = [ - host for host in all_hosts_with_domain + host + for host in all_hosts_with_domain if client.hostname in host.get('Name', '') or alternate_hostname in host.get('Name', '') ]