From 192f93b4a6b8b0af4c598c0f13f4d6d1b5b263ab Mon Sep 17 00:00:00 2001 From: Tasos Papaioannou Date: Wed, 18 Dec 2024 13:59:12 -0500 Subject: [PATCH] Local Insights advisor testing --- pytest_fixtures/component/rh_cloud.py | 85 +++++++---- tests/foreman/ui/test_rhcloud_insights.py | 169 ++++++++++++--------- tests/foreman/ui/test_rhcloud_inventory.py | 27 ++++ 3 files changed, 180 insertions(+), 101 deletions(-) diff --git a/pytest_fixtures/component/rh_cloud.py b/pytest_fixtures/component/rh_cloud.py index 9116716d6ff..9494e9652a8 100644 --- a/pytest_fixtures/component/rh_cloud.py +++ b/pytest_fixtures/component/rh_cloud.py @@ -1,23 +1,41 @@ +from broker import Broker import pytest from robottelo.constants import CAPSULE_REGISTRATION_OPTS @pytest.fixture(scope='module') -def rhcloud_manifest_org(module_target_sat, module_sca_manifest): +def module_target_sat_insights(request, module_target_sat, satellite_factory): + hosted_insights = getattr(request, 'param', True) + + satellite = module_target_sat if hosted_insights else satellite_factory() + if not hosted_insights: + satellite.execute('satellite-installer --enable-local-advisor') + + yield satellite + + if not hosted_insights: + satellite.teardown() + Broker(hosts=[satellite]).checkin() + + +@pytest.fixture(scope='module') +def rhcloud_manifest_org(module_target_sat_insights, module_sca_manifest): """A module level fixture to get organization with manifest.""" - org = module_target_sat.api.Organization().create() - module_target_sat.upload_manifest(org.id, module_sca_manifest.content) + org = module_target_sat_insights.api.Organization().create() + module_target_sat_insights.upload_manifest(org.id, module_sca_manifest.content) return org @pytest.fixture(scope='module') -def rhcloud_activation_key(module_target_sat, rhcloud_manifest_org): +def rhcloud_activation_key(module_target_sat_insights, rhcloud_manifest_org): """A module-level fixture to create an Activation key in module_org""" - return module_target_sat.api.ActivationKey( + return module_target_sat_insights.api.ActivationKey( content_view=rhcloud_manifest_org.default_content_view, organization=rhcloud_manifest_org, - environment=module_target_sat.api.LifecycleEnvironment(id=rhcloud_manifest_org.library.id), + environment=module_target_sat_insights.api.LifecycleEnvironment( + id=rhcloud_manifest_org.library.id + ), service_level='Self-Support', purpose_usage='test-usage', purpose_role='test-role', @@ -27,12 +45,12 @@ def rhcloud_activation_key(module_target_sat, rhcloud_manifest_org): @pytest.fixture(scope='module') def rhcloud_registered_hosts( - rhcloud_activation_key, rhcloud_manifest_org, mod_content_hosts, module_target_sat + rhcloud_activation_key, rhcloud_manifest_org, mod_content_hosts, module_target_sat_insights ): """Fixture that registers content hosts to Satellite and Insights.""" for vm in mod_content_hosts: vm.configure_insights_client( - satellite=module_target_sat, + satellite=module_target_sat_insights, activation_key=rhcloud_activation_key, org=rhcloud_manifest_org, rhel_distro=f"rhel{vm.os_version.major}", @@ -43,44 +61,57 @@ def rhcloud_registered_hosts( @pytest.fixture def rhel_insights_vm( - module_target_sat, rhcloud_activation_key, rhcloud_manifest_org, rhel_contenthost + request, + module_target_sat_insights, + rhcloud_activation_key, + rhcloud_manifest_org, + rhel_contenthost, ): """A function-level fixture to create rhel content host registered with insights.""" rhel_contenthost.configure_rex( - satellite=module_target_sat, org=rhcloud_manifest_org, register=False + satellite=module_target_sat_insights, org=rhcloud_manifest_org, register=False ) rhel_contenthost.configure_insights_client( - satellite=module_target_sat, + satellite=module_target_sat_insights, activation_key=rhcloud_activation_key, org=rhcloud_manifest_org, rhel_distro=f"rhel{rhel_contenthost.os_version.major}", ) - # Generate report - module_target_sat.generate_inventory_report(rhcloud_manifest_org) - # Sync inventory status - module_target_sat.sync_inventory_status(rhcloud_manifest_org) + if not module_target_sat.api.RHCloud().advisor_engine_config()['use_local_advisor_engine']: + module_target_sat_insights.generate_inventory_report(rhcloud_manifest_org) + module_target_sat_insights.sync_inventory_status(rhcloud_manifest_org) return rhel_contenthost @pytest.fixture -def inventory_settings(module_target_sat): - hostnames_setting = module_target_sat.update_setting('obfuscate_inventory_hostnames', False) - ip_setting = module_target_sat.update_setting('obfuscate_inventory_ips', False) - packages_setting = module_target_sat.update_setting('exclude_installed_packages', False) - parameter_tags_setting = module_target_sat.update_setting('include_parameter_tags', False) +def inventory_settings(module_target_sat_insights): + hostnames_setting = module_target_sat_insights.update_setting( + 'obfuscate_inventory_hostnames', False + ) + ip_setting = module_target_sat_insights.update_setting('obfuscate_inventory_ips', False) + packages_setting = module_target_sat_insights.update_setting( + 'exclude_installed_packages', False + ) + parameter_tags_setting = module_target_sat_insights.update_setting( + 'include_parameter_tags', False + ) yield - module_target_sat.update_setting('obfuscate_inventory_hostnames', hostnames_setting) - module_target_sat.update_setting('obfuscate_inventory_ips', ip_setting) - module_target_sat.update_setting('exclude_installed_packages', packages_setting) - module_target_sat.update_setting('include_parameter_tags', parameter_tags_setting) + module_target_sat_insights.update_setting('obfuscate_inventory_hostnames', hostnames_setting) + module_target_sat_insights.update_setting('obfuscate_inventory_ips', ip_setting) + module_target_sat_insights.update_setting('exclude_installed_packages', packages_setting) + module_target_sat_insights.update_setting('include_parameter_tags', parameter_tags_setting) @pytest.fixture(scope='module') -def rhcloud_capsule(module_capsule_host, module_target_sat, rhcloud_manifest_org, default_location): +def rhcloud_capsule( + module_capsule_host, module_target_sat_insights, rhcloud_manifest_org, default_location +): """Configure the capsule instance with the satellite from settings.server.hostname""" org = rhcloud_manifest_org - module_capsule_host.capsule_setup(sat_host=module_target_sat, **CAPSULE_REGISTRATION_OPTS) - module_target_sat.cli.Capsule.update( + module_capsule_host.capsule_setup( + sat_host=module_target_sat_insights, **CAPSULE_REGISTRATION_OPTS + ) + module_target_sat_insights.cli.Capsule.update( { 'name': module_capsule_host.hostname, 'organization-ids': org.id, diff --git a/tests/foreman/ui/test_rhcloud_insights.py b/tests/foreman/ui/test_rhcloud_insights.py index 662f841d22e..af0af81ccc0 100644 --- a/tests/foreman/ui/test_rhcloud_insights.py +++ b/tests/foreman/ui/test_rhcloud_insights.py @@ -18,7 +18,11 @@ from wait_for import wait_for from robottelo.config import settings -from robottelo.constants import DEFAULT_LOC, DNF_RECOMMENDATION, OPENSSH_RECOMMENDATION +from robottelo.constants import DEFAULT_LOC, DEFAULT_ORG, DNF_RECOMMENDATION, OPENSSH_RECOMMENDATION + + +def _hosted_insights(satellite): + return not satellite.api.RHCloud().advisor_engine_config()['use_local_advisor_engine'] def create_insights_vulnerability(insights_vm): @@ -29,27 +33,50 @@ def create_insights_vulnerability(insights_vm): ) +def sync_recommendations(satellite, org_name=DEFAULT_ORG, loc_name=DEFAULT_LOC): + with satellite.ui_session() as session: + session.organization.select(org_name=org_name) + session.location.select(loc_name=DEFAULT_LOC) + + timestamp = datetime.utcnow().strftime('%Y-%m-%d %H:%M') + session.cloudinsights.sync_hits() + + wait_for( + lambda: satellite.api.ForemanTask() + .search(query={'search': f'Insights full sync and started_at >= "{timestamp}"'})[0] + .result + == 'success', + timeout=400, + delay=15, + silent_failure=True, + handle_exception=True, + ) + + @pytest.mark.e2e @pytest.mark.pit_server @pytest.mark.pit_client @pytest.mark.tier3 @pytest.mark.no_containers @pytest.mark.rhel_ver_list(r'^[\d]+$') +@pytest.mark.parametrize( + "module_target_sat_insights", [True, False], ids=["hosted", "local"], indirect=True +) def test_rhcloud_insights_e2e( rhel_insights_vm, rhcloud_manifest_org, - module_target_sat, + module_target_sat_insights, ): - """Synchronize hits data from cloud, verify it is displayed in Satellite and run remediation. + """Synchronize hits data from hosted or local Insights Advisor, verify results are displayed in Satellite, and run remediation. :id: d952e83c-3faf-4299-a048-2eb6ccb8c9c2 :steps: 1. Prepare misconfigured machine and upload its data to Insights. - 2. In Satellite UI, go to Configure -> Insights -> Sync recommendations. + 2. In Satellite UI, go to Configure -> Insights -> Sync recommendations (hosted Insights) or Insights -> XXX (local Insights). 3. Run remediation for "OpenSSH config permissions" recommendation against host. 4. Verify that the remediation job completed successfully. - 5. Sync Insights recommendations. + 5. Refresh Insights recommendations (re-sync if using hosted Insights). 6. Search for previously remediated issue. :expectedresults: @@ -71,62 +98,51 @@ def test_rhcloud_insights_e2e( job_query = ( f'Remote action: Insights remediations for selected issues on {rhel_insights_vm.hostname}' ) - org = rhcloud_manifest_org + org_name = rhcloud_manifest_org.name + # Prepare misconfigured machine and upload data to Insights create_insights_vulnerability(rhel_insights_vm) - with module_target_sat.ui_session() as session: - session.organization.select(org_name=org.name) + # Sync the recommendations (hosted Insights only). + hosted_insights = _hosted_insights(module_target_sat) + if hosted_insights: + sync_recommendations(module_target_sat_insights, org_name=org_name, loc_name=DEFAULT_LOC) + + with module_target_sat_insights.ui_session() as session: + session.organization.select(org_name=org_name) session.location.select(loc_name=DEFAULT_LOC) - timestamp = datetime.utcnow().strftime('%Y-%m-%d %H:%M') - # Sync Insights recommendations - session.cloudinsights.sync_hits() - wait_for( - lambda: module_target_sat.api.ForemanTask() - .search(query={'search': f'Insights full sync and started_at >= "{timestamp}"'})[0] - .result - == 'success', - timeout=400, - delay=15, - silent_failure=True, - handle_exception=True, - ) - # Workaround for alert message causing search to fail. See airgun issue 584. - session.browser.refresh() + # Search for Insights recommendation. result = session.cloudinsights.search( f'hostname= "{rhel_insights_vm.hostname}" and title = "{OPENSSH_RECOMMENDATION}"' )[0] assert result['Hostname'] == rhel_insights_vm.hostname assert result['Recommendation'] == OPENSSH_RECOMMENDATION + # Run remediation and verify job completion. timestamp = datetime.utcnow().strftime('%Y-%m-%d %H:%M') session.cloudinsights.remediate(OPENSSH_RECOMMENDATION) - wait_for( - lambda: module_target_sat.api.ForemanTask() - .search(query={'search': f'{job_query} and started_at >= "{timestamp}"'})[0] - .result - == 'success', - timeout=400, - delay=15, - silent_failure=True, - handle_exception=True, - ) - # Search for Insights recommendations again. - timestamp = datetime.utcnow().strftime('%Y-%m-%d %H:%M') - session.cloudinsights.sync_hits() - wait_for( - lambda: module_target_sat.api.ForemanTask() - .search(query={'search': f'Insights full sync and started_at >= "{timestamp}"'})[0] - .result - == 'success', - timeout=400, - delay=15, - silent_failure=True, - handle_exception=True, - ) - # Workaround for alert message causing search to fail. See airgun issue 584. - session.browser.refresh() + + # Wait for the remediation task to complete. + wait_for( + lambda: module_target_sat_insights.api.ForemanTask() + .search(query={'search': f'{job_query} and started_at >= "{timestamp}"'})[0] + .result + == 'success', + timeout=400, + delay=15, + silent_failure=True, + handle_exception=True, + ) + + # Re-sync the recommendations (hosted Insights only). + if hosted_insights: + sync_recommendations(module_target_sat_insights, org_name=org_name, loc_name=DEFAULT_LOC) + + with module_target_sat_insights.ui_session() as session: + session.organization.select(org_name=org_name) + session.location.select(loc_name=DEFAULT_LOC) + # Verify that the insights recommendation is not listed anymore. assert not session.cloudinsights.search( f'hostname= "{rhel_insights_vm.hostname}" and title = "{OPENSSH_RECOMMENDATION}"' @@ -214,10 +230,13 @@ def test_host_sorting_based_on_recommendation_count(): @pytest.mark.tier2 @pytest.mark.no_containers @pytest.mark.rhel_ver_list([7, 8, 9]) +@pytest.mark.parametrize( + "module_target_sat_insights", [True, False], ids=["hosted", "local"], indirect=True +) def test_host_details_page( rhel_insights_vm, rhcloud_manifest_org, - module_target_sat, + module_target_sat_insights, ): """Test host details page for host having insights recommendations. @@ -253,52 +272,54 @@ def test_host_details_page( :CaseAutomation: Automated """ - org = rhcloud_manifest_org + org_name = rhcloud_manifest_org.name + # Prepare misconfigured machine and upload data to Insights create_insights_vulnerability(rhel_insights_vm) - with module_target_sat.ui_session() as session: - session.organization.select(org_name=org.name) - session.location.select(loc_name=DEFAULT_LOC) + + hosted_insights = _hosted_insights(module_target_sat) + if hosted_insights: # Sync insights recommendations - timestamp = datetime.utcnow().strftime('%Y-%m-%d %H:%M') - session.cloudinsights.sync_hits() - wait_for( - lambda: module_target_sat.api.ForemanTask() - .search(query={'search': f'Insights full sync and started_at >= "{timestamp}"'})[0] - .result - == 'success', - timeout=400, - delay=15, - silent_failure=True, - handle_exception=True, - ) + sync_recommendations(module_target_sat_insights, org_name=org_name, loc_name=DEFAULT_LOC) + + with module_target_sat_insights.ui_session() as session: + session.organization.select(org_name=org_name) + session.location.select(loc_name=DEFAULT_LOC) + # Verify Insights status of host. result = session.host_new.get_host_statuses(rhel_insights_vm.hostname) assert result['Insights']['Status'] == 'Reporting' - assert result['Inventory']['Status'] == 'Successfully uploaded to your RH cloud inventory' + assert ( + result['Inventory']['Status'] == 'Successfully uploaded to your RH cloud inventory' + if hosted_insights + else 'N/A' + ) + result = session.host_new.search(rhel_insights_vm.hostname)[0] assert result['Name'] == rhel_insights_vm.hostname assert int(result['Recommendations']) > 0 - values = session.host_new.get_host_statuses(rhel_insights_vm.hostname) - assert values['Inventory']['Status'] == 'Successfully uploaded to your RH cloud inventory' + # Read the recommendations listed in Insights tab present on host details page insights_recommendations = session.host_new.get_insights(rhel_insights_vm.hostname)[ 'recommendations_table' ] + for recommendation in insights_recommendations: if recommendation['Recommendation'] == DNF_RECOMMENDATION: assert recommendation['Total risk'] == 'Moderate' assert DNF_RECOMMENDATION in recommendation['Recommendation'] assert len(insights_recommendations) == int(result['Recommendations']) + # Test Recommendation button present on host details page recommendations = session.host_new.get_insights(rhel_insights_vm.hostname)[ 'recommendations_table' ] assert len(recommendations), 'No recommendations were found' assert int(result['Recommendations']) == len(recommendations) - # Delete host - rhel_insights_vm.nailgun_host.delete() - assert not rhel_insights_vm.nailgun_host + + # Delete host + rhel_insights_vm.nailgun_host.delete() + assert not rhel_insights_vm.nailgun_host @pytest.mark.e2e @@ -309,7 +330,7 @@ def test_insights_registration_with_capsule( rhcloud_capsule, rhcloud_activation_key, rhcloud_manifest_org, - module_target_sat, + module_target_sat_insights, rhel_contenthost, default_os, ): @@ -348,7 +369,7 @@ def test_insights_registration_with_capsule( rhel_contenthost.create_custom_repos( **{f'rhel{rhelver}_os': settings.repos[f'rhel{rhelver}_os']} ) - with module_target_sat.ui_session() as session: + with module_target_sat_insights.ui_session() as session: session.organization.select(org_name=org.name) session.location.select(loc_name=DEFAULT_LOC) # Generate host registration command @@ -370,7 +391,7 @@ def test_insights_registration_with_capsule( values = session.host_new.get_host_statuses(rhel_contenthost.hostname) assert values['Insights']['Status'] == 'Reporting' # Clean insights status - result = module_target_sat.run( + result = module_target_sat_insights.run( f'foreman-rake rh_cloud_insights:clean_statuses SEARCH="{rhel_contenthost.hostname}"' ) assert 'Deleted 1 insights statuses' in result.stdout diff --git a/tests/foreman/ui/test_rhcloud_inventory.py b/tests/foreman/ui/test_rhcloud_inventory.py index 97bb661bf7b..4888f2be971 100644 --- a/tests/foreman/ui/test_rhcloud_inventory.py +++ b/tests/foreman/ui/test_rhcloud_inventory.py @@ -14,6 +14,7 @@ from datetime import datetime, timedelta +from navmazing import NavigationTriesExceeded import pytest from wait_for import wait_for @@ -354,3 +355,29 @@ def test_rhcloud_inventory_without_manifest(session, module_org, target_sat): f'Skipping organization {module_org.name}, no candlepin certificate defined.' in inventory_data['uploading']['terminal'] ) + + +@pytest.mark.parametrize("module_target_sat_insights", [False], ids=["local"], indirect=True) +def test_rhcloud_inventory_disabled_local_insights( + session, module_target_sat_insights, rhcloud_manifest_org +): + """Verify that the 'Configure > Insights > Inventory Upload' navigation item is not available + when the Satellite is configured to use a local advisor engine. + + :id: 84023ae9-7bc4-4332-9aaf-749d6c48c2d2 + + :steps: + 1. Configure Satellite to use local Insights advisor engine. + 3. Navigate to the Overview page. + + :expectedresults: + 1. "Configure > Insights > Inventory Upload" does not exist. + + :CaseImportance: Medium + + :CaseAutomation: Automated + """ + with session: + session.organization.select(org_name=rhcloud_manifest_org.name) + with pytest.raises(NavigationTriesExceeded): + session.cloudinventory.read()