diff --git a/robottelo/host_helpers/api_factory.py b/robottelo/host_helpers/api_factory.py index 1ec747e1b6c..eb70ea5f5f4 100644 --- a/robottelo/host_helpers/api_factory.py +++ b/robottelo/host_helpers/api_factory.py @@ -719,7 +719,6 @@ def register_host_and_needed_setup( environment, content_view, enable_repos=False, - unregister=False, rex_key=False, force=False, ): @@ -735,17 +734,15 @@ def register_host_and_needed_setup( * The host will be registered to location: None (visible to all locations). param satellite: sat instance where needed entities exist. - param client: instance of a rhel contenthost to register. - param unregister (bool): unregister the host at the start, in case it is already registered - default: False, a reused fixture contenthost may fail if registered prior, - unregister before calling, or ensure it is a new host. + param client: instance of a rhel contenthost to register. param enable_repos (bool): enable all available repos on the client, after registration? default: False, be sure to enable any repo(s) for client, after calling this method. param rex_key (bool): add a Remote Execution Key to client for satellite? default: False + param force (bool): force registration of the client to bypass? default: False, a reused fixture contenthost will fail if already registered. @@ -808,17 +805,6 @@ def register_host_and_needed_setup( 'Argument "client" must be instance, with attribute "hostname".' ) return method_error - if not force and client.subscribed: - if unregister: - client.unregister() - else: - method_error['message'] = ( - 'Passed client is already registered.' - ' Unregister the host prior to calling this method.' - ' Or, pass unregister=True, to unregister within this method first,' - ' Or, pass force=True, to bypass during registration.' - ) - return method_error # for entity arguments matched to above params: # fetch entity instance on satellite, # from given id or name, else read passed argument as an instance. @@ -830,18 +816,15 @@ def register_host_and_needed_setup( param = getattr(self._satellite.api, entity)(id=value).read() # passed str, search for entity by name elif isinstance(value, str): - # search for org name itself, will be just scoped to satellite + search_query = f'name="{value}"' if entity == 'Organization': - search_query = f'name="{value}"' + # search for org name itself, will be just scoped to satellite # equivalent: _satellite_.api.{KEY}().search(...name={VALUE}) result = getattr(self._satellite.api, entity)().search( query={'search': search_query} ) - # search of non-org entity by name, will be scoped to organization else: - search_query = ( - f"name='{value}' and organization_id={entities['Organization'].id}" - ) + # search of non-org entity by name, will be scoped to organization result = getattr(self._satellite.api, entity)( organization=entities['Organization'] ).search(query={'search': search_query}) @@ -945,7 +928,6 @@ def register_host_and_needed_setup( f' For client: {client.hostname}.\n{output.stdout}' ) return method_error - entities = {k: v.read() for k, v in entities.items()} return ( # dict containing registered host client, and updated entities { diff --git a/tests/foreman/api/test_reporttemplates.py b/tests/foreman/api/test_reporttemplates.py index 05c65d74831..c1e9e89006b 100644 --- a/tests/foreman/api/test_reporttemplates.py +++ b/tests/foreman/api/test_reporttemplates.py @@ -13,6 +13,7 @@ """ import re +from time import time from broker import Broker from fauxfactory import gen_string @@ -341,6 +342,81 @@ def test_positive_export_report(): """ +@pytest.mark.tier2 +@pytest.mark.rhel_ver_match('[^6]') +@pytest.mark.no_containers +def test_positive_applied_errata_by_search( + function_org, function_lce, rhel_contenthost, target_sat +): + """Generate an Applied Errata report + :id: 0f7d2772-47a4-4215-b555-dd8ee675372f + :setup: A Host with some applied errata. + :steps: + 1. Generate an Applied Errata report + :expectedresults: A report is generated with all applied errata listed + :CaseImportance: Medium + """ + activation_key = target_sat.api.ActivationKey( + environment=function_lce, organization=function_org + ).create() + cv = target_sat.api.ContentView(organization=function_org).create() + ERRATUM_ID = str(settings.repos.yum_6.errata[2]) + target_sat.cli_factory.setup_org_for_a_custom_repo( + { + 'url': settings.repos.yum_9.url, + 'organization-id': function_org.id, + 'content-view-id': cv.id, + 'lifecycle-environment-id': function_lce.id, + 'activationkey-id': activation_key.id, + } + ) + errata_name = ( + target_sat.api.Errata() + .search(query={'search': f'errata_id="{ERRATUM_ID}"'})[0] + .read() + .description + ) + result = rhel_contenthost.register(function_org, None, activation_key.name, target_sat) + assert f'The registered system name is: {rhel_contenthost.hostname}' in result.stdout + assert rhel_contenthost.subscribed + epoch_timestamp = int(time() - 1) + rhel_contenthost.execute(r'subscription-manager repos --enable \*') + assert rhel_contenthost.execute(f'yum install -y {FAKE_1_CUSTOM_PACKAGE}').status == 0 + assert rhel_contenthost.execute(f'rpm -q {FAKE_1_CUSTOM_PACKAGE}').status == 0 + rhel_contenthost.execute('subscription-manager repos') + task_id = target_sat.api.JobInvocation().run( + data={ + 'feature': 'katello_errata_install_by_search', + 'inputs': {'Errata search query': errata_name}, + 'targeting_type': 'static_query', + 'search_query': f'name = {rhel_contenthost.hostname}', + 'organization_id': function_org.id, + }, + )['id'] + target_sat.wait_for_tasks( + search_query=(f'label = Actions::RemoteExecution::RunHostsJob and id = {task_id}'), + search_rate=15, + ) + rt = ( + target_sat.api.ReportTemplate() + .search(query={'search': 'name="Host - Applied Errata"'})[0] + .read() + ) + res = rt.generate( + data={ + 'organization_id': function_org.id, + 'report_format': 'json', + 'input_values': { + 'Filter Errata Type': 'all', + 'Include Last Reboot': 'no', + 'Status': 'all', + }, + } + ) + assert res[0]['erratum_id'] == ERRATUM_ID + assert res[0]['issued'] + + @pytest.mark.tier2 @pytest.mark.stubbed def test_positive_generate_report_sanitized(): diff --git a/tests/foreman/ui/test_errata.py b/tests/foreman/ui/test_errata.py index a7e47bdef96..485a9b10fb8 100644 --- a/tests/foreman/ui/test_errata.py +++ b/tests/foreman/ui/test_errata.py @@ -13,11 +13,11 @@ """ from datetime import datetime, timedelta +import re from broker import Broker from dateutil.parser import parse from fauxfactory import gen_string -from manifester import Manifester import pytest from selenium.common.exceptions import NoSuchElementException from wait_for import wait_for @@ -79,56 +79,70 @@ def _set_setting_value(setting_entity, value): setting_entity.update(['value']) -@pytest.fixture(scope='module') -def module_manifest(): - with Manifester(manifest_category=settings.manifest.entitlement) as manifest: - yield manifest +def checkout_clients(distro, count): + """ + Checkout given number of Broker hosts by distro. + yield: list of unique contenthost object(s) of same nick. + """ + with Broker( + nick=distro, + workflow='deploy-rhel', + host_class=ContentHost, + _count=count, + ) as clients: + if not isinstance(clients, list) or len(clients) != count: + if not (isinstance(clients, ContentHost) and count == 1): + pytest.fail('Failed to provision the expected number of hosts.') + yield clients @pytest.fixture -def function_manifest(): - with Manifester(manifest_category=settings.manifest.entitlement) as manifest: - yield manifest - - -@pytest.fixture(scope='module') -def module_org_with_parameter(module_target_sat, module_manifest): - org = module_target_sat.api.Organization(simple_content_access=False).create() - # org.sca_disable() - module_target_sat.api.Parameter( - name='remote_execution_connect_by_ip', - parameter_type='boolean', - value='Yes', - organization=org.id, - ).create() - module_target_sat.upload_manifest(org.id, module_manifest.content) - return org +def multiple_hosts(request): + """Checkout number of desired Broker ContentHost(s) by nicks, count. + Return all host clients as list. + default: Three clients, one of each distro: rhel7, rhel8, rhel9. -@pytest.fixture -def function_org_with_parameter(target_sat, function_manifest): - org = target_sat.api.Organization(simple_content_access=False).create() - target_sat.api.Parameter( - name='remote_execution_connect_by_ip', - parameter_type='boolean', - value='Yes', - organization=org.id, - ).create() - target_sat.upload_manifest(org.id, function_manifest.content) - return org + parametrize: by providing dictionary {'distro': count, 'other_distro': count,}: + keys (str): being desired rhel distributions, + values (int): being desired count of each distro. + example: to parametrize a test case: + @pytest.mark.parametrize( + 'multiple_hosts', + [{'rhel7': 6, 'rhel9': 3,}], + indirect=True, + ) + Would yield clients list; contaning six rhel7 hosts, and three rhel9 hosts. + Note: you can also provide `@pytest.mark.no_containers` from test, if desired. + """ + default_params = { + 'rhel7': 1, + 'rhel8': 1, + 'rhel9': 1, + } + params = getattr(request, 'param', default_params) + clients = [] + for distro in params.keys(): + # checkout desired number for each distro + chosts = checkout_clients( + distro=distro, + count=params[f'{distro}'], + ) + if not isinstance(chosts, list): + chosts = list(chosts) + clients.extend(chosts) -@pytest.fixture -def lce(target_sat, function_org_with_parameter): - return target_sat.api.LifecycleEnvironment(organization=function_org_with_parameter).create() - + yield clients -@pytest.fixture -def erratatype_vm(module_repos_collection_with_setup, target_sat): - """Virtual machine client using module_repos_collection_with_setup for subscription""" - with Broker(nick=module_repos_collection_with_setup.distro, host_class=ContentHost) as client: - module_repos_collection_with_setup.setup_virtual_machine(client) - yield client + @request.addfinalizer + # For in-between parametrized sessions, + # unregister the hosts if they're still subscribed to content. + def cleanup(): + nonlocal clients + for cl in clients: + if cl.subscribed: + cl.unregister() @pytest.fixture @@ -142,14 +156,6 @@ def errata_status_installable(module_target_sat): _set_setting_value(errata_status_installable, original_value) -@pytest.fixture -def vm(module_repos_collection_with_setup, rhel7_contenthost, target_sat): - """Virtual machine registered in satellite""" - module_repos_collection_with_setup.setup_virtual_machine(rhel7_contenthost) - rhel7_contenthost.add_rex_key(satellite=target_sat) - return rhel7_contenthost - - def cv_publish_promote(sat, org, cv, lce=None, needs_publish=True): """Publish & promote Content View Version with all content visible in org. @@ -191,22 +197,21 @@ def cv_publish_promote(sat, org, cv, lce=None, needs_publish=True): return _result -def _publish_and_wait(sat, org, cv): - """Publish a new version of content-view to organization, wait for task(s) completion.""" +def _publish_and_wait(sat, org, cv, timeout=60): + """Synchrnous publish of a new version of content-view to organization, + wait for task completion. + + return: the polled task, if success or failed. + """ task_id = sat.api.ContentView(id=cv.id).publish({'id': cv.id, 'organization': org})['id'] assert task_id, f'No task was invoked to publish the Content-View: {cv.id}.' # Should take < 1 minute, check in 5s intervals - ( - sat.wait_for_tasks( - search_query=(f'label = Actions::Katello::ContentView::Publish and id = {task_id}'), - search_rate=5, - max_tries=12, - ), - ( - f'Failed to publish the Content-View: {cv.id}, in time.' - f'Task: {task_id} failed, or timed out (60s).' - ), + sat.wait_for_tasks( + search_query=(f'label = Actions::Katello::ContentView::Publish and id = {task_id}'), + search_rate=5, + max_tries=round(timeout / 5), ) + return sat.api.ForemanTask(id=task_id).poll(must_succeed=False) @pytest.fixture @@ -224,8 +229,9 @@ def registered_contenthost( module_sca_manifest_org, module_target_sat, rhel_contenthost, - errata_host_ak, + module_product, module_lce, + module_ak, module_cv, request, repos=None, @@ -233,119 +239,117 @@ def registered_contenthost( """RHEL ContentHost registered in satellite, Using SCA and global registration. - :note: rhel_contenthost will be parameterized by rhel6 to 9, also -fips for all distros. - to use specific rhel version parameterized contenthost; + :note: rhel_contenthost will be parametrized by rhel6 to 9, also -fips for all distros. + to use specific rhel version parametrized contenthost; use `pytest.mark.rhel_ver_match()` to mark contenthost version(s) for tests using this fixture. - :repos: pass as a parameterized request + :environment: Defaults to module_lce. + To use Library environment for activation key / content-view: + pass the string 'library' (not case sensative) in the list of params. + + :repos: pass as a parametrized request list of upstream URLs for custom repositories. default: None; when None set to [CUSTOM_REPO_URL,] example: - @pytest.mark.parametrize("registered_contenthost", + @pytest.mark.parametrize('registered_contenthost', [[repo1_url, repo2_url,]], indirect=True, ) + for Library env: + @pytest.mark.parametrize('registered_contenthost', + [['library', repo1_url, repo2_url,]], + indirect=True, + ) """ - try: - repos = getattr(request, 'param', repos).copy() - except AttributeError: - # Default, no parameterized request passed - repos = [CUSTOM_REPO_URL] - assert isinstance(repos, list), 'Passed request must be a list of upstream url strings.' - assert len(repos) > 0, 'Passed request must be a non-empty list.' - - custom_products = [] + params = getattr(request, 'param', None) + environment = module_lce + if params is None: + repos = [] + else: + if any(p.lower() == 'library' for p in params): + environment = 'Library' + repos = [p for p in params if str(p).lower() != 'library'] + custom_repos = [] for repo_url in repos: - # Publishes a new cvv, associates org, ak, cv, with custom repo: - custom_repo_info = module_target_sat.cli_factory.setup_org_for_a_custom_repo( - { - 'url': repo_url, - 'organization-id': module_sca_manifest_org.id, - 'lifecycle-environment-id': module_lce.id, - 'activationkey-id': errata_host_ak.id, - 'content-view-id': module_cv.id, - } - ) - custom_products.append(custom_repo_info['product-id']) - custom_repos.append(custom_repo_info['repository-id']) - - # Publish new version and promote with all content - cv_publish_promote(module_target_sat, module_sca_manifest_org, module_cv, module_lce) - result = rhel_contenthost.register( - activation_keys=errata_host_ak.name, - target=module_target_sat, - org=module_sca_manifest_org, - loc=None, - ) - assert result.status == 0, f'Failed to register host:\n{result.stderr}' - assert rhel_contenthost.subscribed, ( - f'Failed to subscribe host to content, host: {rhel_contenthost.hostname}' - f' Attempting to subscribe to content-view id: {module_cv.id}' - f' Using activation-key id: {errata_host_ak.id}' + new_repo = module_target_sat.api.Repository(url=repo_url, product=module_product).create() + new_repo.sync() + custom_repos.append(new_repo) + if len(custom_repos) > 0: + module_cv.repository = custom_repos + module_cv.update(['repository']) + if rhel_contenthost.subscribed: + rhel_contenthost.unregister() + # Publish/promote CV if needed, associate entities, register client: + setup = module_target_sat.api_factory.register_host_and_needed_setup( + organization=module_sca_manifest_org, + client=rhel_contenthost, + activation_key=module_ak, + environment=environment, + content_view=module_cv, ) + assert setup['result'] != 'error', f'{setup["message"]}' + assert (client := setup['client']) + assert client.subscribed + # nothing applicable to start + result = client.execute('subscription-manager repos') + assert client.applicable_errata_count == 0 + assert client.applicable_packages_count == 0 + # no repos given/present, subscription-manager should report error + if len(repos) == 0: + assert client.execute(r'subscription-manager repos --enable \*').status == 1 + # any custom repos in host are setup, and can be synced again, + # we can also invoke/enable each repo with subscription-manager: + else: + # list all repos available to sub-manager: + sub_manager_repos = client.subscription_manager_list_repos() + repo_ids_names = {'ids': [], 'names': []} + for line in sub_manager_repos.stdout.splitlines(): + # in each output line, check for Name: and ID: of repos listed + if search := re.search(r'ID: (.*)', line): + id_found = search.group(1).strip() + repo_ids_names['ids'].append(id_found) + if search := re.search(r'Name: (.*)', line): + name_found = search.group(1).strip() + repo_ids_names['names'].append(name_found) + # every repo id found, has a corresponding name we will match to satellite repo + assert len(repo_ids_names['ids']) == len(repo_ids_names['names']), ( + f"Failed to extract a given repository's name, and or id, from subscription-manager list." + f" {sub_manager_repos}" + ) - for custom_repo_id in custom_repos: - # custom repos setup and successfully sync - custom_repo = module_target_sat.api.Repository(id=custom_repo_id).read() - assert custom_repo - result = custom_repo.sync()['humanized'] - assert ( - len(result['errors']) == 0 - ), f'Failed to sync custom repository [id: {custom_repo_id}]:\n{str(result["errors"])}' - - yield rhel_contenthost + for repo in custom_repos: + # sync repository to satellite + assert module_target_sat.api.Repository(id=repo.id).read() + result = repo.sync()['humanized'] + assert ( + len(result['errors']) == 0 + ), f'Failed to sync custom repository [id: {repo.id}]:\n{str(result["errors"])}' + # found index (repo) with matching name, grab sub-manager repo-id: + assert repo.name in repo_ids_names['names'] + sub_man_repo_id = repo_ids_names['ids'][repo_ids_names['names'].index(repo.name)] + # repo present, and can be enabled without error + enable_repo = client.execute(f'subscription-manager repos --enable {sub_man_repo_id}') + assert enable_repo.status == 0, ( + f'Failed to enable a repository with subscription-manager, on client: {client.hostname}.' + f' {enable_repo.stderr}' + ) + assert len(custom_repos) == len(repo_ids_names['ids']) + assert all(name in repo_ids_names['names'] for r in custom_repos for name in [r.name]) @request.addfinalizer - # Cleanup for in-between parameterized sessions + # Cleanup for in-between parametrized sessions, + # unregister the host if it's still subscribed to content. def cleanup(): - nonlocal \ - rhel_contenthost, \ - module_cv, \ - module_lce, \ - custom_repos, \ - custom_products, \ - errata_host_ak, \ - module_sca_manifest_org - rhel_contenthost.unregister() - errata_host_ak.delete() - # find any other aks and delete them - other_aks = module_target_sat.api.ActivationKey( - organization=module_sca_manifest_org, - environment=module_lce, - ).search() - for ak in other_aks: - ak.read().delete() - # Remove CV from all lifecycle-environments - module_target_sat.cli.ContentView.remove_from_environment( - { - 'id': module_cv.id, - 'organization-id': module_sca_manifest_org.id, - 'lifecycle-environment-id': module_lce.id, - } + nonlocal client + if client.subscribed: + client.unregister() + assert not client.subscribed, ( + f'Failed to unregister the host client: {client.hostname}, was unable to fully teardown host.' + ' Client retains some content association.' ) - module_target_sat.cli.ContentView.remove_from_environment( - { - 'id': module_cv.id, - 'organization-id': module_sca_manifest_org.id, - 'lifecycle-environment': 'Library', - } - ) - # Delete all CV versions - module_cv = module_cv.read() - for version in module_cv.version: - version.delete() - # Remove repos from CV, delete all custom repos and products - for repo_id in custom_repos: - module_target_sat.cli.ContentView.remove_repository( - { - 'id': module_cv.id, - 'repository-id': repo_id, - } - ) - module_target_sat.api.Repository(id=repo_id).delete() - for product_id in custom_products: - module_target_sat.api.Product(id=product_id).delete() + + return client @pytest.mark.e2e @@ -375,7 +379,7 @@ def test_end_to_end( :parametrized: yes - :BZ: 2029192 + :BZ: 2029192, 2265095 :customerscenario: true """ @@ -410,7 +414,7 @@ def test_end_to_end( module_cv = module_cv.read() # associate needed entities, prepare content view, register the host client: - # we will also enable the custom repo we added to module_cv + # we will also enable the custom repo we added to module_cv. setup_result = module_target_sat.api_factory.register_host_and_needed_setup( organization=module_sca_manifest_org, client=rhel_contenthost, @@ -419,7 +423,7 @@ def test_end_to_end( content_view=module_cv, enable_repos=True, ) - # above method encountered no errors, received a registered client + # above setup encountered no error, received a registered client assert setup_result['result'] != 'error', f'{setup_result["message"]}' assert (client := setup_result['client']) assert client.subscribed @@ -442,6 +446,7 @@ def test_end_to_end( assert ( applicable_errata == 1 ), f'Expected 1 applicable errata: {CUSTOM_REPO_ERRATA_ID}, after setup. Got {applicable_errata}' + with session: datetime_utc_start = datetime.utcnow().replace(microsecond=0) # Check selection box function for BZ#1688636 @@ -452,7 +457,13 @@ def test_end_to_end( environment=module_lce.name, ) assert len(results) == 1 + # BZ 2265095: Check all columns in table of applicable host: + # from ContentTypes > Errata > Details > Content Hosts tab assert results[0]['Name'] == client.hostname + #assert str(client.deploy_rhel_version) in results[0]['OS'] + #assert results[0]['Environment'] == module_lce.name + #assert results[0]['Content View'] == module_cv.name + # Check errata details errata = session.errata.read(CUSTOM_REPO_ERRATA_ID) assert errata['repositories']['table'], ( f'There are no repositories listed for errata ({CUSTOM_REPO_ERRATA_ID}),', @@ -711,6 +722,7 @@ def test_host_content_errata_tab_pagination( # wait for the tab to load with updated pagination, sat may be slow, timeout 30s. # lambda: read is not the same as prior pagination read, and is also not empty {}. _invalid_pagination = ({}, _prior_pagination) + session.browser.refresh() wait_for( lambda: session.host_new.get_errata_pagination(_chost_name).read() not in _invalid_pagination, @@ -760,6 +772,7 @@ def test_host_content_errata_tab_pagination( # timeout is 30s, for tab loading with no pagination {}, on read. _items = -1 _ex_raised = False + session.browser.refresh() try: wait_for( lambda: not session.host_new.get_errata_pagination(_chost_name).read(), @@ -862,19 +875,9 @@ def test_positive_list( @pytest.mark.tier2 -@pytest.mark.parametrize( - 'module_repos_collection_with_setup', - [ - { - 'distro': 'rhel6', - 'VirtualizationAgentsRepository': {'cdn': True}, - 'YumRepository': {'url': CUSTOM_REPO_URL}, - } - ], - indirect=True, -) def test_positive_list_permission( - test_name, module_org_with_parameter, module_repos_collection_with_setup, module_target_sat + test_name, + module_target_sat, ): """Show errata only if the User has permissions to view them @@ -891,7 +894,7 @@ def test_positive_list_permission( :expectedresults: Check that the new user is able to see errata for one product only. """ - module_org = module_org_with_parameter + module_org = 0 role = module_target_sat.api.Role().create() module_target_sat.api.Filter( organization=[module_org], @@ -920,20 +923,26 @@ def test_positive_list_permission( @pytest.mark.tier3 @pytest.mark.upgrade +@pytest.mark.no_containers @pytest.mark.parametrize( - 'module_repos_collection_with_setup', + 'multiple_hosts', [ { - 'distro': 'rhel7', - 'SatelliteToolsRepository': {}, - 'RHELAnsibleEngineRepository': {'cdn': True}, - 'YumRepository': {'url': CUSTOM_REPO_URL}, + 'rhel8': 1, + 'rhel9': 3, } ], indirect=True, ) def test_positive_apply_for_all_hosts( - session, module_org_with_parameter, module_repos_collection_with_setup, target_sat + module_sca_manifest_org, + module_product, + multiple_hosts, + target_sat, + module_lce, + module_cv, + module_ak, + session, ): """Apply an erratum for all content hosts @@ -943,50 +952,54 @@ def test_positive_apply_for_all_hosts( :customerscenario: true - :steps: + :setup: + 1. Checkout 10 contenthosts, rhel7, 8 and 9. + 2. Register all of the hosts to the same AK, CV, and custom repo. + :steps: 1. Go to Content -> Errata. Select an erratum -> Content Hosts tab. 2. Select all Content Hosts and apply the erratum. :expectedresults: Check that the erratum is applied in all the content hosts. """ - with Broker( - nick=module_repos_collection_with_setup.distro, host_class=ContentHost, _count=2 - ) as clients: - for client in clients: - module_repos_collection_with_setup.setup_virtual_machine(client) - client.add_rex_key(satellite=target_sat) - assert client.execute(f'yum install -y {FAKE_1_CUSTOM_PACKAGE}').status == 0 - with session: - session.location.select(loc_name=DEFAULT_LOC) - for client in clients: - client.add_rex_key(satellite=target_sat) - status = session.contenthost.install_errata( - client.hostname, CUSTOM_REPO_ERRATA_ID, install_via='rex' - ) - assert status['overview']['job_status'] == 'Success' - assert status['overview']['job_status_progress'] == '100%' - packages_rows = session.contenthost.search_package( - client.hostname, FAKE_2_CUSTOM_PACKAGE - ) - assert packages_rows[0]['Installed Package'] == FAKE_2_CUSTOM_PACKAGE + # one custom repo on satellite, for all hosts to use + custom_repo = target_sat.api.Repository(url=CUSTOM_REPO_URL, product=module_product).create() + custom_repo.sync() + module_cv.repository = [custom_repo] + module_cv.update(['repository']) + for client in multiple_hosts: + # setup all hosts to same ak, content-view, and custom repo + setup = target_sat.api_factory.register_host_and_needed_setup( + organization=module_sca_manifest_org, + client=client, + activation_key=module_ak, + environment=module_lce, + content_view=module_cv, + enable_repos=True, + ) + assert setup['result'] != 'error', f'{setup["message"]}' + assert (client := setup['client']) + assert client.subscribed + assert client.execute(f'yum install -y {FAKE_1_CUSTOM_PACKAGE}').status == 0 + + with session: + session.location.select(loc_name=DEFAULT_LOC) + for client in multiple_hosts: + status = session.contenthost.install_errata( + client.hostname, CUSTOM_REPO_ERRATA_ID, install_via='rex' + ) + assert status['overview']['job_status'] == 'Success' + assert status['overview']['job_status_progress'] == '100%' + packages_rows = session.contenthost.search_package( + client.hostname, FAKE_2_CUSTOM_PACKAGE + ) + assert packages_rows[0]['Installed Package'] == FAKE_2_CUSTOM_PACKAGE @pytest.mark.tier2 @pytest.mark.upgrade -@pytest.mark.parametrize( - 'module_repos_collection_with_setup', - [ - { - 'distro': 'rhel6', - 'VirtualizationAgentsRepository': {'cdn': True, 'distro': 'rhel6'}, - 'YumRepository': {'url': CUSTOM_REPO_URL}, - } - ], - indirect=True, -) -def test_positive_view_cve(session, module_repos_collection_with_setup): +def test_positive_view_cve(session): """View CVE number(s) in Errata Details page :id: e1c2de13-fed8-448e-b618-c2adb6e82a35 @@ -1012,20 +1025,14 @@ def test_positive_view_cve(session, module_repos_collection_with_setup): @pytest.mark.tier3 @pytest.mark.upgrade -@pytest.mark.parametrize( - 'module_repos_collection_with_setup', - [ - { - 'distro': 'rhel7', - 'SatelliteToolsRepository': {}, - 'RHELAnsibleEngineRepository': {'cdn': True}, - 'YumRepository': {'url': CUSTOM_REPO_URL}, - } - ], - indirect=True, -) def test_positive_filter_by_environment( - session, module_org_with_parameter, module_repos_collection_with_setup, target_sat + module_sca_manifest_org, + module_product, + target_sat, + module_lce, + module_cv, + module_ak, + session, ): """Filter Content hosts by environment @@ -1042,20 +1049,35 @@ def test_positive_filter_by_environment( :expectedresults: Content hosts can be filtered by Environment. """ - module_org = module_org_with_parameter - with Broker( - nick=module_repos_collection_with_setup.distro, host_class=ContentHost, _count=2 - ) as clients: + org = module_sca_manifest_org + # one custom repo on satellite, for all hosts to use + custom_repo = target_sat.api.Repository(url=CUSTOM_REPO_URL, product=module_product).create() + custom_repo.sync() + module_cv.repository = [custom_repo] + module_cv.update(['repository']) + + with Broker(nick='rhel8', host_class=ContentHost, _count=3) as clients: for client in clients: - module_repos_collection_with_setup.setup_virtual_machine(client) + # register all hosts to the same AK, CV: + setup = target_sat.api_factory.register_host_and_needed_setup( + organization=module_sca_manifest_org, + client=client, + activation_key=module_ak, + environment=module_lce, + content_view=module_cv, + enable_repos=True, + ) + assert setup['result'] != 'error', f'{setup["message"]}' + assert (client := setup['client']) + assert client.subscribed assert client.execute(f'yum install -y {FAKE_1_CUSTOM_PACKAGE}').status == 0 + # Promote the latest content view version to a new lifecycle environment - content_view = target_sat.api.ContentView( - id=module_repos_collection_with_setup.setup_content_data['content_view']['id'] - ).read() + content_view = setup['content_view'].read() + content_view.version.sort(key=lambda version: version.id) content_view_version = content_view.version[-1].read() lce = content_view_version.environment[-1].read() - new_lce = target_sat.api.LifecycleEnvironment(organization=module_org, prior=lce).create() + new_lce = target_sat.api.LifecycleEnvironment(organization=org, prior=lce).create() content_view_version.promote(data={'environment_ids': new_lce.id}) host = ( target_sat.api.Host().search(query={'search': f'name={clients[0].hostname}'})[0].read() @@ -1065,6 +1087,7 @@ def test_positive_filter_by_environment( 'lifecycle_environment_id': new_lce.id, } host.update(['content_facet_attributes']) + with session: session.location.select(loc_name=DEFAULT_LOC) # search in new_lce @@ -1075,7 +1098,7 @@ def test_positive_filter_by_environment( assert not session.errata.search_content_hosts( CUSTOM_REPO_ERRATA_ID, clients[1].hostname, environment=new_lce.name ) - # search in lce + # search in module_lce values = session.errata.search_content_hosts( CUSTOM_REPO_ERRATA_ID, clients[1].hostname, environment=lce.name ) @@ -1088,19 +1111,18 @@ def test_positive_filter_by_environment( @pytest.mark.tier3 @pytest.mark.upgrade @pytest.mark.parametrize( - 'module_repos_collection_with_setup', - [ - { - 'distro': 'rhel7', - 'SatelliteToolsRepository': {}, - 'RHELAnsibleEngineRepository': {'cdn': True}, - 'YumRepository': {'url': CUSTOM_REPO_URL}, - } - ], + 'registered_contenthost', + [[CUSTOM_REPO_URL]], indirect=True, ) +@pytest.mark.rhel_ver_match('8') def test_positive_content_host_previous_env( - session, module_org_with_parameter, module_repos_collection_with_setup, vm, module_target_sat + session, + module_cv, + module_lce, + module_org, + module_target_sat, + registered_contenthost, ): """Check if the applicable errata are available from the content host's previous environment @@ -1108,7 +1130,6 @@ def test_positive_content_host_previous_env( :id: 78110ba8-3942-46dd-8c14-bffa1dbd5195 :Setup: - 1. Make sure multiple environments are present. 2. Content host's previous environments have additional errata. @@ -1119,30 +1140,29 @@ def test_positive_content_host_previous_env( :parametrized: yes """ - module_org = module_org_with_parameter + vm = registered_contenthost hostname = vm.hostname assert vm.execute(f'yum install -y {FAKE_1_CUSTOM_PACKAGE}').status == 0 # Promote the latest content view version to a new lifecycle environment - content_view = module_target_sat.api.ContentView( - id=module_repos_collection_with_setup.setup_content_data['content_view']['id'] - ).read() - content_view_version = content_view.version[-1].read() - lce = content_view_version.environment[-1].read() + content_view = module_cv.read() + # lce = content_view_version.environment[-1].read() new_lce = module_target_sat.api.LifecycleEnvironment( - organization=module_org, prior=lce + organization=module_org, + prior=module_lce, ).create() + content_view = content_view.read() + content_view.version.sort(key=lambda version: version.id) + content_view_version = content_view.version[-1].read() content_view_version.promote(data={'environment_ids': new_lce.id}) - host = module_target_sat.api.Host().search(query={'search': f'name={hostname}'})[0].read() - host.content_facet_attributes = { - 'content_view_id': content_view.id, - 'lifecycle_environment_id': new_lce.id, - } - host.update(['content_facet_attributes']) + with session: session.location.select(loc_name=DEFAULT_LOC) - environment = f'Previous Lifecycle Environment ({lce.name}/{content_view.name})' + # can view errata from previous env, dropdown option is correct + environment = f'Previous Lifecycle Environment ({module_lce.name}/{content_view.name})' content_host_erratum = session.contenthost.search_errata( - hostname, CUSTOM_REPO_ERRATA_ID, environment=environment + hostname, + CUSTOM_REPO_ERRATA_ID, + environment=environment, ) assert content_host_erratum[0]['Id'] == CUSTOM_REPO_ERRATA_ID @@ -1154,7 +1174,7 @@ def test_positive_content_host_previous_env( [[CUSTOM_REPO_URL]], indirect=True, ) -def test_positive_check_errata(session, module_org_with_parameter, registered_contenthost): +def test_positive_check_errata(session, registered_contenthost): """Check if the applicable errata is available from the host page :id: 81f3c5bf-5317-40d6-ab3a-2b1a2c5fcbdd @@ -1181,6 +1201,11 @@ def test_positive_check_errata(session, module_org_with_parameter, registered_co @pytest.mark.tier3 @pytest.mark.rhel_ver_match('[8, 9]') +@pytest.mark.parametrize( + 'registered_contenthost', + [['Library', CUSTOM_REPO_URL]], + indirect=True, +) def test_positive_host_content_library( registered_contenthost, function_lce, @@ -1491,12 +1516,20 @@ def test_positive_check_errata_counts_by_type_on_host_details_page( @pytest.mark.upgrade @pytest.mark.skipif((not settings.robottelo.REPOS_HOSTING_URL), reason='Missing repos_hosting_url') @pytest.mark.parametrize('setting_update', ['errata_status_installable'], indirect=True) +@pytest.mark.rhel_ver_match('8') +@pytest.mark.parametrize( + 'registered_contenthost', + [[CUSTOM_REPO_URL]], + indirect=True, +) def test_positive_filtered_errata_status_installable_param( - session, - function_entitlement_manifest_org, errata_status_installable, - target_sat, + registered_contenthost, setting_update, + target_sat, + module_lce, + module_cv, + session, ): """Filter errata for specific content view and verify that host that was registered using that content view has different states in @@ -1505,16 +1538,16 @@ def test_positive_filtered_errata_status_installable_param( :id: ed94cf34-b8b9-4411-8edc-5e210ea6af4f - :steps: + :setup: + 1. Fixture entities and registered contenthost setup. + 2. Install an outdated package, making an erratum applicable. - 1. Prepare setup: Create Lifecycle Environment, Content View, - Activation Key and all necessary repos - 2. Register Content Host using created data - 3. Create necessary Content View Filter and Rule for repository errata - 4. Publish and Promote Content View to a new version. - 5. Go to created Host page and check its properties - 6. Change 'errata status installable' flag in the settings and - check host properties once more + :steps: + 1. Create necessary Content View Filter and Rule for repository errata. + 2. Publish and Promote Content View to a new version containing Filter. + 3. Go to created Host page and check its properties. + 4. Change 'errata status installable' flag in the settings, and + check host properties once more. :expectedresults: Check that 'errata status installable' flag works as intended @@ -1522,91 +1555,71 @@ def test_positive_filtered_errata_status_installable_param( :CaseImportance: Medium """ - org = function_entitlement_manifest_org - lce = target_sat.api.LifecycleEnvironment(organization=org).create() - repos_collection = target_sat.cli_factory.RepositoryCollection( - distro='rhel7', - repositories=[ - target_sat.cli_factory.SatelliteToolsRepository(), - # As Satellite Tools may be added as custom repo and to have a "Fully entitled" host, - # force the host to consume an RH product with adding a cdn repo. - target_sat.cli_factory.RHELAnsibleEngineRepository(cdn=True), - target_sat.cli_factory.YumRepository(url=CUSTOM_REPO_URL), - ], - ) - repos_collection.setup_content(org.id, lce.id) - with Broker(nick=repos_collection.distro, host_class=ContentHost) as client: - repos_collection.setup_virtual_machine(client) - assert client.execute(f'yum install -y {FAKE_1_CUSTOM_PACKAGE}').status == 0 - # Adding content view filter and content view filter rule to exclude errata for the - # installed package. - content_view = target_sat.api.ContentView( - id=repos_collection.setup_content_data['content_view']['id'] - ).read() - cv_filter = target_sat.api.ErratumContentViewFilter( - content_view=content_view, inclusion=False - ).create() - errata = target_sat.api.Errata(content_view_version=content_view.version[-1]).search( - query=dict(search=f'errata_id="{CUSTOM_REPO_ERRATA_ID}"') - )[0] - target_sat.api.ContentViewFilterRule(content_view_filter=cv_filter, errata=errata).create() - content_view.publish() - content_view = content_view.read() - content_view_version = content_view.version[-1] - content_view_version.promote(data={'environment_ids': lce.id}) - with session: - session.organization.select(org_name=org.name) - session.location.select(loc_name=DEFAULT_LOC) - property_value = 'Yes' - session.settings.update(f'name = {setting_update.name}', property_value) - expected_values = { - 'Status': 'OK', - 'Errata': 'All errata applied', - 'Subscription': 'Fully entitled', - } - host_details_values = session.host.get_details(client.hostname) - actual_values = { - key: value - for key, value in host_details_values['properties']['properties_table'].items() - if key in expected_values - } - for key in actual_values: - assert expected_values[key] in actual_values[key], 'Expected text not found' - property_value = 'Yes' - session.settings.update(f'name = {setting_update.name}', property_value) - assert client.execute(f'yum install -y {FAKE_9_YUM_OUTDATED_PACKAGES[1]}').status == 0 - expected_values = { - 'Status': 'Error', - 'Errata': 'Security errata installable', - 'Subscription': 'Fully entitled', - } - # Refresh the host page to get the new details - session.browser.refresh() - host_details_values = session.host.get_details(client.hostname) - actual_values = { - key: value - for key, value in host_details_values['properties']['properties_table'].items() - if key in expected_values - } - for key in actual_values: - assert expected_values[key] in actual_values[key], 'Expected text not found' + client = registered_contenthost + assert client.execute(f'yum install -y {FAKE_1_CUSTOM_PACKAGE}').status == 0 + # Adding content view filter and content view filter rule to exclude errata, + # for the installed package above. + cv_filter = target_sat.api.ErratumContentViewFilter( + content_view=module_cv, inclusion=False + ).create() + module_cv = module_cv.read() + errata = target_sat.api.Errata(content_view_version=module_cv.version[-1]).search( + query={'search': f'errata_id="{CUSTOM_REPO_ERRATA_ID}"'} + )[0] + target_sat.api.ContentViewFilterRule(content_view_filter=cv_filter, errata=errata).create() + module_cv = module_cv.read() + # publish and promote the new version with Filter + module_cv.publish() + module_cv = module_cv.read() + module_cv.version.sort(key=lambda version: version.id) + content_view_version = module_cv.version[-1].read() + content_view_version.promote(data={'environment_ids': module_lce.id}) + + with session: + session.location.select(loc_name=DEFAULT_LOC) + property_value = 'Yes' + session.settings.update(f'name = {setting_update.name}', property_value) + expected_values = { + 'Status': 'OK', + 'Errata': 'All errata applied', + 'Subscription': 'Fully entitled', + } + host_details_values = session.host.get_details(client.hostname) + actual_values = { + key: value + for key, value in host_details_values['properties']['properties_table'].items() + if key in expected_values + } + for key in actual_values: + assert expected_values[key] in actual_values[key], 'Expected text not found' + property_value = 'Yes' + session.settings.update(f'name = {setting_update.name}', property_value) + assert client.execute(f'yum install -y {FAKE_9_YUM_OUTDATED_PACKAGES[1]}').status == 0 + expected_values = { + 'Status': 'Error', + 'Errata': 'Security errata installable', + 'Subscription': 'Fully entitled', + } + # Refresh the host page to get the new details + session.browser.refresh() + host_details_values = session.host.get_details(client.hostname) + actual_values = { + key: value + for key, value in host_details_values['properties']['properties_table'].items() + if key in expected_values + } + for key in actual_values: + assert expected_values[key] in actual_values[key], 'Expected text not found' @pytest.mark.tier3 -@pytest.mark.parametrize( - 'module_repos_collection_with_setup', - [ - { - 'distro': 'rhel7', - 'SatelliteToolsRepository': {}, - 'RHELAnsibleEngineRepository': {'cdn': True}, - 'YumRepository': {'url': CUSTOM_REPO_URL}, - } - ], - indirect=True, -) def test_content_host_errata_search_commands( - session, module_org_with_parameter, module_repos_collection_with_setup, target_sat + session, + target_sat, + module_product, + module_org, + module_ak, + module_cv, ): """View a list of affected content hosts for security (RHSA) and bugfix (RHBA) errata, filtered with errata status and applicable flags. Applicability is calculated using the @@ -1633,11 +1646,25 @@ def test_content_host_errata_search_commands( :BZ: 1707335 """ - with Broker( - nick=module_repos_collection_with_setup.distro, host_class=ContentHost, _count=2 - ) as clients: + custom_repo = target_sat.api.Repository(url=CUSTOM_REPO_URL, product=module_product).create() + custom_repo.sync() + module_cv.repository = [custom_repo] + module_cv.update(['repository']) + + with Broker(nick='rhel8', host_class=ContentHost, _count=2) as clients: for client in clients: - module_repos_collection_with_setup.setup_virtual_machine(client) + # register all hosts to the same AK, CV: + setup = target_sat.api_factory.register_host_and_needed_setup( + organization=module_org, + client=client, + activation_key=module_ak, + environment='Library', + content_view=module_cv, + enable_repos=True, + ) + assert setup['result'] != 'error', f'{setup["message"]}' + assert (client := setup['client']) + assert client.subscribed # Install pkg walrus-0.71-1.noarch to create need for RHSA on client 1 assert clients[0].execute(f'yum install -y {FAKE_1_CUSTOM_PACKAGE}').status == 0 # Install pkg kangaroo-0.1-1.noarch to create need for RHBA on client 2