From a1122b2dd263bba137290f6e02cb72e5fc9596cb Mon Sep 17 00:00:00 2001 From: David Moore Date: Tue, 9 Jan 2024 20:37:49 -0500 Subject: [PATCH] API-errata-CP, component-eval, updates to global registration and host content Please enter the commit message for your changes. Lines starting --- pytest_fixtures/component/repository.py | 22 + robottelo/constants/__init__.py | 4 + robottelo/host_helpers/api_factory.py | 32 +- robottelo/host_helpers/contenthost_mixins.py | 6 + tests/foreman/api/test_errata.py | 1545 ++++++++++++------ 5 files changed, 1120 insertions(+), 489 deletions(-) diff --git a/pytest_fixtures/component/repository.py b/pytest_fixtures/component/repository.py index b7a26560980..ce49a1d7f88 100644 --- a/pytest_fixtures/component/repository.py +++ b/pytest_fixtures/component/repository.py @@ -245,3 +245,25 @@ def module_repos_collection_with_manifest( ) _repos_collection.setup_content(module_entitlement_manifest_org.id, module_lce.id) return _repos_collection + + +@pytest.fixture +def function_repos_collection_with_manifest( + request, target_sat, function_sca_manifest_org, function_lce +): + """This fixture and its usage is very similar to repos_collection fixture above with extra + setup_content and uploaded manifest capabilities using function_lce and + function_sca_manifest_org fixtures + """ + repos = getattr(request, 'param', []) + repo_distro, repos = _simplify_repos(request, repos) + _repos_collection = target_sat.cli_factory.RepositoryCollection( + distro=repo_distro, + repositories=[ + getattr(target_sat.cli_factory, repo_name)(**repo_params) + for repo in repos + for repo_name, repo_params in repo.items() + ], + ) + _repos_collection.setup_content(function_sca_manifest_org.id, function_lce.id) + return _repos_collection diff --git a/robottelo/constants/__init__.py b/robottelo/constants/__init__.py index c45d7a38f1c..f9673326022 100644 --- a/robottelo/constants/__init__.py +++ b/robottelo/constants/__init__.py @@ -752,6 +752,9 @@ class Colored(Box): REAL_RHEL7_0_1_PACKAGE_FILENAME = 'python-pulp-common-2.21.0.2-1.el7sat.noarch.rpm' REAL_RHEL7_0_2_PACKAGE_NAME = 'python2-psutil' # for RHBA-2021:1314 REAL_RHEL7_0_2_PACKAGE_FILENAME = 'python2-psutil-5.7.2-2.el7sat.x86_64.rpm' +REAL_RHEL8_1_PACKAGE_NAME = 'puppet-agent' # for RHSA-2022:4867 +REAL_RHEL8_1_PACKAGE_FILENAME = 'puppet-agent-6.19.1-1.el8sat.x86_64' +REAL_RHEL8_2_PACKAGE_FILENAME = 'puppet-agent-6.26.0-1.el8sat.x86_64' FAKE_0_CUSTOM_PACKAGE_GROUP_NAME = 'birds' FAKE_3_YUM_OUTDATED_PACKAGES = [ 'acme-package-1.0.1-1.noarch', @@ -806,6 +809,7 @@ class Colored(Box): FAKE_2_ERRATA_ID = 'RHSA-2012:0055' # for FAKE_1_CUSTOM_PACKAGE REAL_RHEL7_0_ERRATA_ID = 'RHBA-2020:3615' # for REAL_RHEL7_0_0_PACKAGE REAL_RHEL7_1_ERRATA_ID = 'RHBA-2017:0395' # tcsh bug fix update +REAL_RHEL8_1_ERRATA_ID = 'RHSA-2022:4867' # for REAL_RHEL8_1_PACKAGE FAKE_1_YUM_REPOS_COUNT = 32 FAKE_3_YUM_REPOS_COUNT = 78 FAKE_9_YUM_SECURITY_ERRATUM = [ diff --git a/robottelo/host_helpers/api_factory.py b/robottelo/host_helpers/api_factory.py index da6a39ecd76..bae286dc8ac 100644 --- a/robottelo/host_helpers/api_factory.py +++ b/robottelo/host_helpers/api_factory.py @@ -3,6 +3,7 @@ example: my_satellite.api_factory.api_method() """ from contextlib import contextmanager +from datetime import datetime import time from fauxfactory import gen_ipaddr, gen_mac, gen_string @@ -647,12 +648,18 @@ def attach_custom_product_subscription(self, prod_name=None, host_name=None): ) def wait_for_errata_applicability_task( - self, host_id, from_when, search_rate=1, max_tries=10, poll_rate=None, poll_timeout=15 + self, + host_id, + from_when, + search_rate=1, + max_tries=10, + poll_rate=None, + poll_timeout=15, ): """Search the generate applicability task for given host and make sure it finishes :param int host_id: Content host ID of the host where we are regenerating applicability. - :param int from_when: Timestamp (in UTC) to limit number of returned tasks to investigate. + :param int from_when: Epoch Time (seconds in UTC) to limit number of returned tasks to investigate. :param int search_rate: Delay between searches. :param int max_tries: How many times search should be executed. :param int poll_rate: Delay between the end of one task check-up and @@ -666,23 +673,30 @@ def wait_for_errata_applicability_task( assert isinstance(host_id, int), 'Param host_id have to be int' assert isinstance(from_when, int), 'Param from_when have to be int' now = int(time.time()) - assert from_when <= now, 'Param from_when have to be timestamp in the past' + assert from_when <= now, 'Param from_when have to be epoch time in the past' for _ in range(max_tries): now = int(time.time()) - max_age = now - from_when + 1 + # Format epoch time for search, one second prior margin of safety + timestamp = datetime.fromtimestamp(from_when - 1).strftime('%m-%d-%Y %H:%M:%S') + # Long format to match search: ex. 'January 03, 2024 at 03:08:08 PM' + long_format = datetime.strptime(timestamp, '%m-%d-%Y %H:%M:%S').strftime( + '%B %d, %Y at %I:%M:%S %p' + ) search_query = ( - '( label = Actions::Katello::Host::GenerateApplicability OR label = ' - 'Actions::Katello::Host::UploadPackageProfile ) AND started_at > "%s seconds ago"' - % max_age + '( label = Actions::Katello::Applicability::Hosts::BulkGenerate OR' + ' label = Actions::Katello::Host::UploadPackageProfile ) AND' + f' started_at >= "{long_format}" ' ) tasks = self._satellite.api.ForemanTask().search(query={'search': search_query}) tasks_finished = 0 for task in tasks: if ( - task.label == 'Actions::Katello::Host::GenerateApplicability' + task.label == 'Actions::Katello::Applicability::Hosts::BulkGenerate' + and 'host_ids' in task.input and host_id in task.input['host_ids'] ) or ( task.label == 'Actions::Katello::Host::UploadPackageProfile' + and 'host' in task.input and host_id == task.input['host']['id'] ): task.poll(poll_rate=poll_rate, timeout=poll_timeout) @@ -692,7 +706,7 @@ def wait_for_errata_applicability_task( time.sleep(search_rate) else: raise AssertionError( - f"No task was found using query '{search_query}' for host '{host_id}'" + f'No task was found using query " {search_query} " for host id: {host_id}' ) def wait_for_syncplan_tasks(self, repo_backend_id=None, timeout=10, repo_name=None): diff --git a/robottelo/host_helpers/contenthost_mixins.py b/robottelo/host_helpers/contenthost_mixins.py index 96da029d148..feac3cc89ae 100644 --- a/robottelo/host_helpers/contenthost_mixins.py +++ b/robottelo/host_helpers/contenthost_mixins.py @@ -136,6 +136,12 @@ def applicable_errata_count(self): """return the applicable errata count for a host""" return self.nailgun_host.read().content_facet_attributes['errata_counts']['total'] + @property + def applicable_package_count(self): + """return the applicable package count for a host""" + self.run('subscription-manager repos') + return self.nailgun_host.read().content_facet_attributes['applicable_package_count'] + class SystemFacts: """Helpers mixin that enables getting/setting subscription-manager facts on a host""" diff --git a/tests/foreman/api/test_errata.py b/tests/foreman/api/test_errata.py index cb831425778..45ad3774a11 100644 --- a/tests/foreman/api/test_errata.py +++ b/tests/foreman/api/test_errata.py @@ -12,14 +12,28 @@ """ # For ease of use hc refers to host-collection throughout this document -from time import sleep +from time import sleep, time -from nailgun import entities import pytest -from robottelo import constants from robottelo.config import settings -from robottelo.constants import DEFAULT_SUBSCRIPTION_NAME +from robottelo.constants import ( + DEFAULT_ARCHITECTURE, + FAKE_1_CUSTOM_PACKAGE, + FAKE_2_CUSTOM_PACKAGE, + FAKE_2_CUSTOM_PACKAGE_NAME, + FAKE_4_CUSTOM_PACKAGE, + FAKE_4_CUSTOM_PACKAGE_NAME, + FAKE_5_CUSTOM_PACKAGE, + FAKE_9_YUM_OUTDATED_PACKAGES, + FAKE_9_YUM_SECURITY_ERRATUM, + FAKE_9_YUM_UPDATED_PACKAGES, + PRDS, + REAL_RHEL8_1_ERRATA_ID, + REAL_RHEL8_1_PACKAGE_FILENAME, + REPOS, + REPOSET, +) pytestmark = [ pytest.mark.run_in_one_thread, @@ -30,25 +44,53 @@ CUSTOM_REPO_URL = settings.repos.yum_9.url CUSTOM_REPO_ERRATA_ID = settings.repos.yum_6.errata[2] +ERRATA = [ + { + 'id': settings.repos.yum_6.errata[2], # security advisory + 'old_package': FAKE_1_CUSTOM_PACKAGE, + 'new_package': FAKE_2_CUSTOM_PACKAGE, + 'package_name': FAKE_2_CUSTOM_PACKAGE_NAME, + }, + { + 'id': settings.repos.yum_6.errata[0], # bugfix advisory + 'old_package': FAKE_4_CUSTOM_PACKAGE, + 'new_package': FAKE_5_CUSTOM_PACKAGE, + 'package_name': FAKE_4_CUSTOM_PACKAGE_NAME, + }, +] +REPO_WITH_ERRATA = { + 'url': settings.repos.yum_9.url, + 'errata': ERRATA, + 'errata_ids': settings.repos.yum_9.errata, +} @pytest.fixture(scope='module') -def activation_key(module_org, module_lce, module_target_sat): +def activation_key(module_sca_manifest_org, module_cv, module_lce, module_target_sat): + """A new Activation Key associated with published version + of module_cv, promoted to module_lce.""" + _cv = cv_publish_promote( + module_target_sat, + module_sca_manifest_org, + module_cv, + module_lce, + )['content-view'] return module_target_sat.api.ActivationKey( - environment=module_lce, organization=module_org + organization=module_sca_manifest_org, + environment=module_lce, + content_view=_cv, ).create() @pytest.fixture(scope='module') -def rh_repo( - module_entitlement_manifest_org, module_lce, module_cv, activation_key, module_target_sat -): +def rh_repo(module_sca_manifest_org, module_lce, module_cv, activation_key, module_target_sat): + "rhel8 rh repos with errata and outdated/updated packages" return module_target_sat.cli_factory.setup_org_for_a_rh_repo( { - 'product': constants.PRDS['rhel'], - 'repository-set': constants.REPOSET['rhst7'], - 'repository': constants.REPOS['rhst7']['name'], - 'organization-id': module_entitlement_manifest_org.id, + 'product': PRDS['rhel'], + 'repository-set': REPOSET['rhst8'], + 'repository': REPOS['rhst8']['name'], + 'organization-id': module_sca_manifest_org.id, 'content-view-id': module_cv.id, 'lifecycle-environment-id': module_lce.id, 'activationkey-id': activation_key.id, @@ -57,11 +99,12 @@ def rh_repo( @pytest.fixture(scope='module') -def custom_repo(module_org, module_lce, module_cv, activation_key, module_target_sat): +def custom_repo(module_sca_manifest_org, module_lce, module_cv, activation_key, module_target_sat): + "zoo repos with errata and outdated/updated packages" return module_target_sat.cli_factory.setup_org_for_a_custom_repo( { - 'url': settings.repos.yum_9.url, - 'organization-id': module_org.id, + 'url': CUSTOM_REPO_URL, + 'organization-id': module_sca_manifest_org.id, 'content-view-id': module_cv.id, 'lifecycle-environment-id': module_lce.id, 'activationkey-id': activation_key.id, @@ -69,54 +112,7 @@ def custom_repo(module_org, module_lce, module_cv, activation_key, module_target ) -def _install_package( - module_org, clients, host_ids, package_name, via_ssh=True, rpm_package_name=None -): - """Install package via SSH CLI if via_ssh is True, otherwise - install via http api: PUT /api/v2/hosts/bulk/install_content - """ - if via_ssh: - for client in clients: - result = client.run(f'yum install -y {package_name}') - assert result.status == 0 - result = client.run(f'rpm -q {package_name}') - assert result.status == 0 - else: - entities.Host().install_content( - data={ - 'organization_id': module_org.id, - 'included': {'ids': host_ids}, - 'content_type': 'package', - 'content': [package_name], - } - ) - _validate_package_installed(clients, rpm_package_name) - - -def _validate_package_installed(hosts, package_name, expected_installed=True, timeout=240): - """Check whether package was installed on the list of hosts.""" - for host in hosts: - for _ in range(timeout // 15): - result = host.run(f'rpm -q {package_name}') - if ( - result.status == 0 - and expected_installed - or result.status != 0 - and not expected_installed - ): - break - sleep(15) - else: - pytest.fail( - 'Package {} was not {} host {}'.format( - package_name, - 'installed on' if expected_installed else 'removed from', - host.hostname, - ) - ) - - -def _validate_errata_counts(module_org, host, errata_type, expected_value, timeout=120): +def _validate_errata_counts(host, errata_type, expected_value, timeout=120): """Check whether host contains expected errata counts.""" for _ in range(timeout // 5): host = host.read() @@ -135,113 +131,687 @@ def _validate_errata_counts(module_org, host, errata_type, expected_value, timeo ) -def _fetch_available_errata(module_org, host, expected_amount, timeout=120): +def _fetch_available_errata(host, expected_amount=None, timeout=120): """Fetch available errata for host.""" errata = host.errata() for _ in range(timeout // 5): - if len(errata['results']) == expected_amount: + if expected_amount is None or len(errata['results']) == expected_amount: return errata['results'] sleep(5) errata = host.errata() else: pytest.fail( 'Host {} contains {} available errata, but expected to ' - 'contain {} of them'.format(host.name, len(errata['results']), expected_amount) + 'contain {} of them'.format( + host.name, + len(errata['results']), + expected_amount if not None else 'No expected_amount provided', + ) + ) + + +def _fetch_available_errata_instances(sat, host, expected_amount=None, timeout=120): + """Fetch list of instances of avaliable errata for host.""" + _errata_dict = _fetch_available_errata(host.nailgun_host, expected_amount, timeout) + _errata_ids = [errata['id'] for errata in _errata_dict] + instances = [sat.api.Errata(id=_id).read() for _id in _errata_ids] + assert ( + len(instances) == len(_errata_dict) == host.applicable_errata_count + ), 'Length of errata instances list or api result differs from expected applicable count.' + return instances + + +def errata_id_set(erratum_list): + """Return a set of unique errata id's, passing list of errata instances, or dictionary. + :raise: `AssertionError`: if errata_id could not be found from a list entry. + :return: set{string} + """ + result = set() + try: + # erratum_list is a list of errata instances + result = set(e.errata_id for e in erratum_list) + except Exception: + try: + # erratum_list is a list of errata dictionary references + result = set(e['errata_id'] for e in erratum_list) + except Exception as err: + # some errata_id cannot be extracted from an entry in erratum_list + raise AssertionError( + 'Must take a dictionary ref or list of erratum instances, each entry needs attribute or key "errata_id".' + f' An entry in the given erratum_list had no discernible "errata_id". Errata(s): {erratum_list}.' + ) from err + return result + + +def package_applicability_changed_as_expected( + sat, + host, + package_filename, + prior_applicable_errata_list, + prior_applicable_errata_count, + prior_applicable_package_count, + return_applicables=False, +): + """Checks that after installing some package, updated any impacted errata(s) + status and host applicability count, and changed applicable package count by one. + + That one of the following occured: + - A non-applicable package was modified, or the same prior version was installed, + the amount of applicable errata and applicable packages remains the same. + Return False, as no applicability changes occured. + + - An Outdated applicable package was installed. Errata applicability increased + by the number of found applicable errata containing that package, + if the errata were not already applicable prior to install. + The number of applicable packages increased by one. + + - An Updated applicable package was installed. Errata applicability decreased + by the amount of found errata containing that package, if the errata are + no longer applicable, but they were prior to install, if any. + The number of applicable packages decreased by one. + + :param string: package_filename: + the full filename of the package version installed. + :param list: prior_applicable_errata_list: + list of all erratum instances from search, that were applicable before modifying package. + :param int prior_applicable_errata_count: + number of total applicable errata prior to modifying package. + :param int prior_applicable_package_count: + number of total applicable packages prior to modifying package. + :param boolean return_applicables (False): if set to True, and method's 'result' is not False: + return a dict containing result, and relevant package and errata information. + + :raise: `AssertionError` if: + Expected changes are not found. + Changes are made to unexpected errata or packages. + A non-readable prior list of erratum was passed. + :return: result(boolean), or relevant applicables(dict) + False if found that no applicable package was modified. + True if method finished executing, expected changes were found. + + :return_applicables: if True: return dict of relevant applicable and changed entities: + result boolean: True, method finished executing + errata_count int: current applicable errata count + package_count int: current applicable package count + current_package string: current version filename of package + prior_package string: previous version filename of package + change_in_errata int: positive, negative, or zero + changed_errata list[string]: of modified errata_ids + """ + assert ( + len(prior_applicable_errata_list) == prior_applicable_errata_count + ), 'Length of "prior_applicable_errata_list" passed, must equal "prior_applicable_errata_count" passed.' + if len(prior_applicable_errata_list) != 0: + try: + prior_applicable_errata_list[0].read() + except Exception as err: + raise AssertionError( + 'Exception on read of index zero in passed parameter "prior_applicable_errata_list".' + ' Must pass a list of readable erratum instances, or empty list.' + ) from err + # schedule errata applicability recalculate for most current status + task = None + epoch_timestamp = int(time() - 1) + result = host.execute('subscription-manager repos') + assert ( + result.status == 0 + ), f'Command "subscription-manager repos" failed to execute on host: {host.hostname},\n{result}' + + try: + task = sat.api_factory.wait_for_errata_applicability_task( + host_id=host.nailgun_host.id, + from_when=epoch_timestamp, + ) + except AssertionError: + # No task for forced applicability regenerate, + # applicability was already up to date + assert task is None + package_basename = str(package_filename.split("-", 1)[0]) # 'package-4.0-1.rpm' > 'package' + prior_unique_errata_ids = errata_id_set(prior_applicable_errata_list) + current_applicable_errata = _fetch_available_errata_instances(sat, host) + app_unique_errata_ids = errata_id_set(current_applicable_errata) + app_errata_with_package_diff = [] + app_errata_diff_ids = set() + + if prior_applicable_errata_count == host.applicable_errata_count: + # Applicable errata count had no change. + # we expect applicable errata id(s) from search also did not change. + assert ( + prior_unique_errata_ids == app_unique_errata_ids + ), 'Expected list of applicable erratum to remain the same.' + if prior_applicable_package_count == host.applicable_package_count: + # no applicable packages were modified + return False + + if prior_applicable_errata_count != host.applicable_errata_count: + # Modifying package changed errata applicability. + # we expect one or more errata id(s) from search to be added or removed. + difference = abs(prior_applicable_errata_count - host.applicable_errata_count) + # Check list of errata id(s) from search matches expected difference + assert ( + len(app_unique_errata_ids) == prior_applicable_errata_count + difference + ), 'Length of applicable errata found by search, does not match applicability count difference.' + # modifying package increased errata applicability count (outdated ver installed) + if prior_applicable_errata_count < host.applicable_errata_count: + # save the new errata(s) found, ones added since package modify + app_errata_with_package_diff = [ + errata + for errata in current_applicable_errata + if ( + any(package_basename in p for p in errata.packages) + and errata.errata_id not in prior_unique_errata_ids + ) + ] + # modifying package decreased errata applicability count (updated ver installed) + elif prior_applicable_errata_count > host.applicable_errata_count: + # save the old errata(s) found, ones removed since package modify + app_errata_with_package_diff = [ + errata + for errata in current_applicable_errata + if ( + not any(package_basename in p.filename for p in errata.packages) + and errata.errata_id in prior_unique_errata_ids + ) + ] + app_errata_diff_ids = errata_id_set(app_errata_with_package_diff) + assert len(app_errata_diff_ids) > 0, ( + f'Applicable errata count changed by {difference}, after modifying {package_filename},' + ' but could not find any affected errata(s) with packages list' + f' that contains a matching package_basename: {package_basename}.' ) + # Check that applicable_package_count changed, + # if not, an applicable package was not modified. + if prior_applicable_package_count == host.applicable_package_count: + # if applicable packages remains the same, errata should also be the same + assert prior_applicable_errata_count == host.applicable_errata_count + assert prior_unique_errata_ids == app_unique_errata_ids + # no applicable errata were impaced by package install + return False + # is current errata list different from one prior to package install ? + if app_unique_errata_ids != prior_unique_errata_ids: + difference = len(app_unique_errata_ids) - len(prior_unique_errata_ids) + # check diff in applicable counts, is equal to diff in length of errata search results. + assert prior_applicable_errata_count + difference == host.applicable_errata_count + + """ Check applicable_package count changed by one. + we expect applicable_errata_count increased/decrease, + only by number of 'new' or 'removed' applicable errata, if any. + """ + if app_errata_with_package_diff: + if host.applicable_errata_count > prior_applicable_errata_count: + """Current applicable errata count is higher than before install, + An outdated package is expected to have been installed. + Check applicable package count increased by one. + Check applicable errata count increased by number + of newly applicable errata. + """ + assert prior_applicable_package_count + 1 == host.applicable_package_count + expected_increase = 0 + if app_unique_errata_ids != prior_unique_errata_ids: + difference = len(app_unique_errata_ids) - prior_applicable_errata_count + assert prior_applicable_errata_count + difference == host.applicable_errata_count + expected_increase = len(app_errata_diff_ids) + assert prior_applicable_errata_count + expected_increase == host.applicable_errata_count + + elif host.applicable_errata_count < prior_applicable_errata_count: + """Current applicable errata count is lower than before install, + An updated package is expected to have been installed. + Check applicable package count decreased by one. + Check applicable errata count decreased by number of + prior applicable errata, that are no longer found. + """ + if host.applicable_errata_count < prior_applicable_errata_count: + assert host.applicable_package_count == prior_applicable_package_count - 1 + expected_decrease = 0 + if app_unique_errata_ids != prior_unique_errata_ids: + difference = len(app_unique_errata_ids) - len(prior_applicable_errata_count) + assert prior_applicable_errata_count + difference == host.applicable_errata_count + expected_decrease = len(app_errata_diff_ids) + assert prior_applicable_errata_count - expected_decrease == host.applicable_errata_count + else: + # We found by search an errata that was added or removed compared to prior install, + # But we also found that applicable_errata_count had not changed. + raise AssertionError( + f'Found one or more different errata: {app_errata_diff_ids},' + ' from those present prior to install, but applicable count did not change,' + f' {host.applicable_errata_count} were found, but expected {host.applicable_errata_count + len(app_errata_diff_ids)}.' + ) + else: + # already checked that applicable package count changed, + # but found applicable erratum list should not change, + # check the errata count and list remained the same. + assert ( + host.applicable_errata_count == prior_applicable_errata_count + ), 'Expected current applicable errata count, to equal prior applicable errata count.' + assert ( + len(current_applicable_errata) == prior_applicable_errata_count + ), 'Expected current applicable errata list length, to equal to prior applicable count.' + assert prior_unique_errata_ids == app_unique_errata_ids, ( + f'Expected set of prior applicable errata_ids: {prior_unique_errata_ids},' + f' to be equivalent to set of current applicable errata_ids: {app_unique_errata_ids}.' + ) + if return_applicables is True: + change_in_errata = len(app_unique_errata_ids) - prior_applicable_errata_count + output = host.execute(f'rpm -q {package_basename}').stdout + current_package = output[:-1] + assert package_basename in current_package + if current_package == package_filename: + # we have already checked if applicable package count changed, + # in case the same version as prior was installed and present. + prior_package = None # package must not have been present before this modification + else: + prior_package = package_filename + return { + 'result': True, + 'errata_count': host.applicable_errata_count, + 'package_count': host.applicable_package_count, + 'current_package': current_package, + 'prior_package': prior_package, + 'change_in_errata': change_in_errata, + 'changed_errata': list(app_errata_diff_ids), + } + return True + + +def cv_publish_promote(sat, org, cv, lce=None, needs_publish=True): + """Publish & promote Content View Version with all content visible in org. + + :param lce: if None, default to 'Library', + pass a single instance of lce, or list of instances. + :param bool needs_publish: if False, skip publish of a new version + :return dictionary: + 'content-view': instance of updated cv + 'content-view-version': instance of newest cv version + """ + # Default to 'Library' lce, if None passed + # Take a single instance of lce, or list of instances + lce_ids = 'Library' + if lce is not None: + lce_ids = [lce.id] if not isinstance(lce, list) else [_lce.id for _lce in lce] + + if needs_publish is True: + _publish_and_wait(sat, org, cv) + # Content-view must have at least one published version + cv = sat.api.ContentView(id=cv.id).read() + assert cv.version, f'No version(s) are published to the Content-View: {cv.id}' + # Find highest version id, will be the latest + cvv_id = max(cvv.id for cvv in cv.version) + # Promote to lifecycle-environment(s) + if lce_ids == 'Library': + library_lce = cv.environment[0].read() + sat.api.ContentViewVersion(id=cvv_id).promote( + data={'environment_ids': library_lce.id, 'force': 'True'} + ) + else: + sat.api.ContentViewVersion(id=cvv_id).promote(data={'environment_ids': lce_ids}) + _result = { + 'content-view': sat.api.ContentView(id=cv.id).read(), + 'content-view-version': sat.api.ContentViewVersion(id=cvv_id).read(), + } + assert all( + entry for entry in _result.values() + ), f'One or more necessary components are missing: {_result}' + return _result + + +def _publish_and_wait(sat, org, cv): + """Publish a new version of content-view to organization, wait for task(s) completion.""" + 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).' + ) @pytest.mark.upgrade @pytest.mark.tier3 -@pytest.mark.rhel_ver_list([7, 8, 9]) +@pytest.mark.rhel_ver_match('[^6]') @pytest.mark.no_containers -def test_positive_install_in_hc(module_org, activation_key, custom_repo, target_sat, content_hosts): +@pytest.mark.e2e +def test_positive_install_in_hc( + module_sca_manifest_org, + activation_key, + module_cv, + module_lce, + custom_repo, + target_sat, + content_hosts, +): """Install errata in a host-collection :id: 6f0242df-6511-4c0f-95fc-3fa32c63a064 - :Setup: Errata synced on satellite server. + :Setup: + 1. Some Unregistered hosts. + 2. Errata synced on satellite server. - :steps: PUT /api/v2/hosts/bulk/update_content + :Steps: + 1. Setup custom repo for each client, publish & promote content-view. + 2. Register clients as content hosts, install one outdated custom package on each client. + 3. Create Host Collection from clients, install errata to clients by Host Collection. + 4. PUT /api/v2/hosts/bulk/update_content + + :expectedresults: + 1. package install invokes errata applicability recalculate + 2. errata is installed in the host-collection + 3. errata installation invokes applicability recalculate + 4. updated custom package is found on the contained hosts + + :CaseImportance: Medium - :expectedresults: errata is installed in the host-collection. :BZ: 1983043 """ + # custom_repo already in published a module_cv version + repo_id = custom_repo['repository-id'] + # just promote to lce, do not publish + cv_publish_promote( + target_sat, module_sca_manifest_org, module_cv, module_lce, needs_publish=False + ) + # Each client: create custom repo, register as content host to cv, install outdated package for client in content_hosts: - client.install_katello_ca(target_sat) - client.register_contenthost(module_org.label, activation_key.name) - assert client.subscribed + _repo = target_sat.api.Repository(id=repo_id).read() + client.create_custom_repos(**{f'{_repo.name}': _repo.url}) + result = client.register( + org=module_sca_manifest_org, + activation_keys=activation_key.name, + target=target_sat, + loc=None, + ) + assert ( + result.status == 0 + ), f'Failed to register the host - {client.hostname}: {result.stderr}' client.add_rex_key(satellite=target_sat) - host_ids = [client.nailgun_host.id for client in content_hosts] - _install_package( - module_org, - clients=content_hosts, - host_ids=host_ids, - package_name=constants.FAKE_1_CUSTOM_PACKAGE, - ) - host_collection = target_sat.api.HostCollection(organization=module_org).create() + assert client.subscribed + client.run(r'subscription-manager repos --enable \*') + # Remove custom package by name + client.run(f'yum remove -y {FAKE_2_CUSTOM_PACKAGE_NAME}') + # No applicable errata or packages to start + assert (pre_errata_count := client.applicable_errata_count) == 0 + assert (pre_package_count := client.applicable_package_count) == 0 + prior_app_errata = _fetch_available_errata_instances(target_sat, client, expected_amount=0) + # 1s margin of safety for rounding + epoch_timestamp = int(time() - 1) + # install outdated version + assert client.run(f'yum install -y {FAKE_1_CUSTOM_PACKAGE}').status == 0 + target_sat.api_factory.wait_for_errata_applicability_task( + host_id=client.nailgun_host.id, + from_when=epoch_timestamp, + ) + assert client.run(f'rpm -q {FAKE_1_CUSTOM_PACKAGE}').status == 0 + # One errata now applicable on client + assert client.applicable_errata_count == 1 + # One package now has an applicable errata + assert client.applicable_package_count == 1 + # Fetch the new errata instance(s), expecting only one + _fetch_available_errata_instances(target_sat, client, expected_amount=1) + + """ Did installing outdated package, update applicability as expected? + * Call method package_applicability_changed_as_expected * + returns: False if no applicability change occured or expected (package not applicable). + True if applicability changes were expected and occured (package is applicable). + raises: `AssertionError` if any expected changes did not occur, or unexpected changes were found. + + Expected: that each outdated package install: updated one or more errata to applicable, + if those now applicable errata(s) were not already applicable to some package prior. + """ + passed_checks = package_applicability_changed_as_expected( + target_sat, + client, + FAKE_1_CUSTOM_PACKAGE, + prior_app_errata, + pre_errata_count, + pre_package_count, + ) + assert ( + passed_checks is True + ), f'The package: {FAKE_1_CUSTOM_PACKAGE}, was not applicable to any erratum present on host: {client.hostname}.' + # Setup host collection using client ids + host_collection = target_sat.api.HostCollection(organization=module_sca_manifest_org).create() host_ids = [client.nailgun_host.id for client in content_hosts] host_collection.host_ids = host_ids host_collection = host_collection.update(['host_ids']) + # Install erratum to host collection task_id = target_sat.api.JobInvocation().run( data={ 'feature': 'katello_errata_install', 'inputs': {'errata': str(CUSTOM_REPO_ERRATA_ID)}, 'targeting_type': 'static_query', 'search_query': f'host_collection_id = {host_collection.id}', - 'organization_id': module_org.id, + 'organization_id': module_sca_manifest_org.id, }, )['id'] target_sat.wait_for_tasks( search_query=(f'label = Actions::RemoteExecution::RunHostsJob and id = {task_id}'), search_rate=15, max_tries=10, + ), ( + f'Could not install erratum: {CUSTOM_REPO_ERRATA_ID}, to Host-Collection.' + f' Task: {task_id} failed, or timed out.' ) for client in content_hosts: - result = client.run(f'rpm -q {constants.FAKE_2_CUSTOM_PACKAGE}') - assert result.status == 0 + # No applicable errata after install on all clients + assert ( + client.applicable_errata_count == 0 + ), f'A client in Host-Collection: {client.hostname}, had {client.applicable_errata_count} ' + 'applicable errata, expected 0.' + # Updated package is present on all clients + result = client.run(f'rpm -q {FAKE_2_CUSTOM_PACKAGE}') + assert result.status == 0, ( + f'The client in Host-Collection: {client.hostname},' + f' could not find the updated package: {FAKE_2_CUSTOM_PACKAGE}' + ) + # No applicable packages on client + assert client.applicable_package_count == 0, ( + f'A client in Host-Collection: {client.hostname}, had {client.applicable_package_count} ' + f'applicable package(s) after installing erratum: {CUSTOM_REPO_ERRATA_ID}, but expected 0.' + ) @pytest.mark.tier3 -@pytest.mark.rhel_ver_list([7, 8, 9]) +@pytest.mark.rhel_ver_match('[^6]') @pytest.mark.no_containers @pytest.mark.e2e def test_positive_install_multiple_in_host( - module_org, activation_key, custom_repo, rhel_contenthost, target_sat + target_sat, rhel_contenthost, module_org, activation_key, module_lce ): """For a host with multiple applicable errata install one and ensure - the rest of errata is still available + the rest of errata is still available, repeat for some list of errata. + After each package or errata install, check applicability updates + as expected. :id: 67b7e95b-9809-455a-a74e-f1815cc537fc + :setup: + 1. An Unregistered host. + 2. Errata synced on satellite server. + + :steps: + 1. Setup content for a content host (repos, cv, etc) + 2. Register vm as a content host + 3. Remove any impacted custom packages present + - no applicable errata to start + 4. Install outdated versions of the custom packages + - some expected applicable errata + 5. Install any applicable security errata + - errata applicability drops after each install + - applicable packages drops by amount updated + - impacted package(s) updated and found + + :expectedresults: + 1. Package installation succeeded, if the package makes a + new errata applicable; available errata counter + increased by one. + 2. Errata apply task succeeded, available errata + counter decreased by one; it is possible to schedule + another errata installation. + 3. Applicable package counter decreased by number + of updated packages. Updated package(s) found. + 4. Errata recalculate applicability task is invoked + automatically, after install command of applicable package, + and errata apply task. Task(s) found and finish successfully. + :customerscenario: true :BZ: 1469800, 1528275, 1983043, 1905560 - :expectedresults: errata installation task succeeded, available errata - counter decreased by one; it's possible to schedule another errata - installation - :CaseImportance: Medium :parametrized: yes + """ - rhel_contenthost.install_katello_ca(target_sat) - rhel_contenthost.register_contenthost(module_org.label, activation_key.name) + # Associate custom repos with org, lce, ak: + custom_repo_id = target_sat.cli_factory.setup_org_for_a_custom_repo( + { + 'url': settings.repos.yum_9.url, + 'organization-id': module_org.id, + 'lifecycle-environment-id': module_lce.id, + 'activationkey-id': activation_key.id, + } + )['repository-id'] + rhel_contenthost.register( + activation_keys=activation_key.name, + target=target_sat, + org=module_org, + loc=None, + ) assert rhel_contenthost.subscribed - host = rhel_contenthost.nailgun_host - for package in constants.FAKE_9_YUM_OUTDATED_PACKAGES: - _install_package( - module_org, clients=[rhel_contenthost], host_ids=[host.id], package_name=package + # 1s margin of safety for rounding + epoch_timestamp = int(time() - 1) + # Remove any packages errata could apply to, verify none are present on host + for package in FAKE_9_YUM_OUTDATED_PACKAGES: + pkg_name = str(package.split("-", 1)[0]) # 'bear-4.0-1.noarch' > 'bear' + result = rhel_contenthost.run(f'yum remove -y {pkg_name}') + assert rhel_contenthost.run(f'rpm -q {pkg_name}').status == 1 + + # Wait for any recalculate task(s), possibly invoked by yum remove, + # catch AssertionError raised if no task was generated + try: + target_sat.api_factory.wait_for_errata_applicability_task( + host_id=rhel_contenthost.nailgun_host.id, + from_when=epoch_timestamp, ) - applicable_errata_count = rhel_contenthost.applicable_errata_count - assert applicable_errata_count > 1 - rhel_contenthost.add_rex_key(satellite=target_sat) - for errata in settings.repos.yum_9.errata[1:4]: + except AssertionError: + # Yum remove did not trigger any errata recalculate task, + # assert any YUM_9 packages were/are not present, then continue + present_packages = set( + [ + package.filename + for package in target_sat.api.Package(repository=custom_repo_id).search() + ] + ) + assert not set(FAKE_9_YUM_OUTDATED_PACKAGES).intersection(present_packages) + assert not set(FAKE_9_YUM_UPDATED_PACKAGES).intersection(present_packages) + + # No applicable errata to start + assert rhel_contenthost.applicable_errata_count == 0 + present_applicable_packages = [] + # Installing all YUM_9 outdated custom packages + for i in range(len(FAKE_9_YUM_OUTDATED_PACKAGES)): + # record params prior to install, for post-install checks + package_filename = FAKE_9_YUM_OUTDATED_PACKAGES[i] + FAKE_9_YUM_UPDATED_PACKAGES[i] + pre_errata_count = rhel_contenthost.applicable_errata_count + pre_package_count = rhel_contenthost.applicable_package_count + prior_app_errata = _fetch_available_errata_instances(target_sat, rhel_contenthost) + # 1s margin of safety for rounding + epoch_timestamp = int(time() - 1) + assert rhel_contenthost.run(f'yum install -y {package_filename}').status == 0 + # Wait for async errata recalculate task(s), invoked by yum install, + # searching back 1s prior to install. + target_sat.api_factory.wait_for_errata_applicability_task( + host_id=rhel_contenthost.nailgun_host.id, + from_when=epoch_timestamp, + ) + # outdated package found on host + assert rhel_contenthost.run(f'rpm -q {package_filename}').status == 0 + """ + Modifying the applicable package did all: + 1. changed package applicability count by one and only one. + 2. changed errata applicability count by number of affected errata, whose + applicability status changed after package was modified. + 3. changed lists of applicable packages and applicable errata accordingly. + - otherwise raise `AssertionError` in below method; + """ + passed_checks = package_applicability_changed_as_expected( + target_sat, + rhel_contenthost, + package_filename, + prior_app_errata, + pre_errata_count, + pre_package_count, + ) + # If passed_checks is False, this package was not applicable, continue to next. + if passed_checks is True: + present_applicable_packages.append(package_filename) + + # Some applicable errata(s) now expected for outdated packages + assert rhel_contenthost.applicable_errata_count > 0 + # Expected applicable package(s) now for the applicable errata + assert rhel_contenthost.applicable_package_count == len(present_applicable_packages) + post_app_errata = _fetch_available_errata_instances(target_sat, rhel_contenthost) + """Installing all YUM_9 security errata sequentially, if applicable. + after each install, applicable-errata-count should drop by one, + one or more of the erratum's listed packages should be updated. + """ + installed_errata = [] + updated_packages = [] + expected_errata_to_install = [ + errata.errata_id + for errata in post_app_errata + if errata.errata_id in FAKE_9_YUM_SECURITY_ERRATUM + ] + all_applicable_packages = set( + package for errata in post_app_errata for package in errata.packages + ) + security_packages_to_install = set() + for errata_id in FAKE_9_YUM_SECURITY_ERRATUM: + errata_instance = ( + target_sat.api.Errata().search(query={'search': f'errata_id="{errata_id}"'})[0].read() + ) + present_packages_impacted_by_errata = [ + package + for package in errata_instance.packages + if package in FAKE_9_YUM_UPDATED_PACKAGES + ] + security_packages_to_install.update(present_packages_impacted_by_errata) + # Are expected security errata packages found in all applicable packages ? + assert security_packages_to_install.issubset(all_applicable_packages) + # Try to install each ERRATUM in FAKE_9_YUM_SECURITY_ERRATUM list, + # Each time, check lists of applicable erratum and packages, and counts + for ERRATUM in FAKE_9_YUM_SECURITY_ERRATUM: + pre_errata_count = rhel_contenthost.applicable_errata_count + ERRATUM_instance = ( + target_sat.api.Errata().search(query={'search': f'errata_id="{ERRATUM}"'})[0].read() + ) + # Check each time before each install + applicable_errata = _fetch_available_errata_instances(target_sat, rhel_contenthost) + # If this ERRATUM is not applicable, continue to next + if (len(applicable_errata) == 0) or ( + ERRATUM not in [_errata.errata_id for _errata in applicable_errata] + ): + continue + assert pre_errata_count >= 1 + errata_packages = [] + pre_package_count = rhel_contenthost.applicable_package_count + # From search result, find this ERRATUM by erratum_id, + # save the relevant list of package(s) + for _errata in applicable_errata: + if _errata.errata_id == ERRATUM: + errata_packages = _errata.packages + assert len(errata_packages) >= 1 + epoch_timestamp = int(time() - 1) + # Install this ERRATUM to host, wait for REX task task_id = target_sat.api.JobInvocation().run( data={ 'feature': 'katello_errata_install', - 'inputs': {'errata': str(errata)}, + 'inputs': {'errata': str(ERRATUM)}, 'targeting_type': 'static_query', 'search_query': f'name = {rhel_contenthost.hostname}', 'organization_id': module_org.id, @@ -252,23 +822,102 @@ def test_positive_install_multiple_in_host( search_rate=20, max_tries=15, ) - applicable_errata_count -= 1 - assert rhel_contenthost.applicable_errata_count == applicable_errata_count + # Wait for async errata recalculate task(s), invoked by REX task + target_sat.api_factory.wait_for_errata_applicability_task( + host_id=rhel_contenthost.nailgun_host.id, + from_when=epoch_timestamp, + ) + # Host Applicable Errata count decreased by one + assert ( + rhel_contenthost.applicable_errata_count == pre_errata_count - 1 + ), f'Host applicable errata did not decrease by one, after installation of {ERRATUM}' + # Applying this ERRATUM updated one or more of the erratum's listed packages + found_updated_packages = [] + for package in errata_packages: + result = rhel_contenthost.run(f'rpm -q {package}') + if result.status == 0: + assert ( + package in FAKE_9_YUM_UPDATED_PACKAGES + ), f'An unexpected package: "{package}", was updated by this errata: {ERRATUM}.' + if package in ERRATUM_instance.packages: + found_updated_packages.append(package) + + assert len(found_updated_packages) > 0, ( + f'None of the expected errata.packages: {errata_packages}, were found on host: "{rhel_contenthost.hostname}",' + f' after installing the applicable errata: {ERRATUM}.' + ) + # Host Applicable Packages count dropped by number of packages updated + assert rhel_contenthost.applicable_package_count == pre_package_count - len( + found_updated_packages + ), ( + f'Host: "{rhel_contenthost.hostname}" applicable package count did not decrease by {len(found_updated_packages)},' + f' after errata: {ERRATUM} installed updated packages: {found_updated_packages}' + ) + installed_errata.append(ERRATUM) + updated_packages.extend(found_updated_packages) + + # In case no ERRATUM in list are applicable: + # Lack of any package or errata install will raise `AssertionError`. + assert ( + len(installed_errata) > 0 + ), f'No applicable errata were found or installed from list: {FAKE_9_YUM_SECURITY_ERRATUM}.' + assert ( + len(updated_packages) > 0 + ), f'No applicable packages were found or installed from list: {FAKE_9_YUM_UPDATED_PACKAGES}.' + # Each expected erratum and packages installed only once + pkg_set = set(updated_packages) + errata_set = set(installed_errata) + assert len(pkg_set) == len( + updated_packages + ), f'Expect no repeat packages in install list: {updated_packages}.' + assert len(errata_set) == len( + installed_errata + ), f'Expected no repeat errata in install list: {installed_errata}.' + # Only the expected YUM_9 packages were installed + assert set(updated_packages).issubset(set(FAKE_9_YUM_UPDATED_PACKAGES)) + # Only the expected YUM_9 errata were updated + assert set(installed_errata).issubset(set(FAKE_9_YUM_SECURITY_ERRATUM)) + # Check number of installed errata id(s) matches expected + assert len(installed_errata) == len(expected_errata_to_install), ( + f'Expected to install {len(expected_errata_to_install)} errata from list: {FAKE_9_YUM_SECURITY_ERRATUM},' + f' but installed: {len(installed_errata)}.' + ) + # Check sets of installed errata id(s) strings, matches expected + assert set(installed_errata) == set( + expected_errata_to_install + ), 'Expected errata id(s) and installed errata id(s) are not the same.' + # Check number of updated package version filename(s) matches expected + assert len(updated_packages) == len(security_packages_to_install), ( + f'Expected to install {len(security_packages_to_install)} packages from list: {FAKE_9_YUM_UPDATED_PACKAGES},' + f' but installed {len(updated_packages)}.' + ) + # Check sets of installed package filename(s) strings, matches expected + assert set(updated_packages) == set( + security_packages_to_install + ), 'Expected package version filename(s) and installed package version filenam(s) are not the same.' @pytest.mark.tier3 @pytest.mark.skipif((not settings.robottelo.REPOS_HOSTING_URL), reason='Missing repos_hosting_url') -def test_positive_list(module_org, custom_repo, target_sat): - """View all errata specific to repository +def test_positive_list_sorted_filtered(custom_repo, target_sat): + """View, sort, and filter all errata specific to repository. :id: 1efceabf-9821-4804-bacf-2213ac0c7550 :Setup: Errata synced on satellite server. - :steps: Create two repositories each synced and containing errata + :Steps: + + 1. Create two repositories each synced and containing errata + 2. GET /katello/api/errata + + :expectedresults: + + 1. Check that the errata belonging to one repo is not + showing in the other. + 2. Check that the errata can be sorted by updated date, + issued date, and filtered by CVE. - :expectedresults: Check that the errata belonging to one repo is not - showing in the other. """ repo1 = target_sat.api.Repository(id=custom_repo['repository-id']).read() repo2 = target_sat.api.Repository( @@ -281,6 +930,7 @@ def test_positive_list(module_org, custom_repo, target_sat): repo2_errata_ids = [ errata['errata_id'] for errata in repo2.errata(data={'per_page': '1000'})['results'] ] + # Check errata are viewable, errata for one repo is not showing in the other assert len(repo1_errata_ids) == len(settings.repos.yum_9.errata) assert len(repo2_errata_ids) == len(settings.repos.yum_3.errata) assert CUSTOM_REPO_ERRATA_ID in repo1_errata_ids @@ -288,19 +938,7 @@ def test_positive_list(module_org, custom_repo, target_sat): assert settings.repos.yum_3.errata[5] in repo2_errata_ids assert settings.repos.yum_3.errata[5] not in repo1_errata_ids - -@pytest.mark.tier3 -def test_positive_list_updated(module_org, custom_repo, target_sat): - """View all errata in an Org sorted by Updated - - :id: 560d6584-70bd-4d1b-993a-cc7665a9e600 - - :Setup: Errata synced on satellite server. - - :steps: GET /katello/api/errata - - :expectedresults: Errata is filtered by Org and sorted by Updated date. - """ + # View all errata in Org sorted by Updated repo = target_sat.api.Repository(id=custom_repo['repository-id']).read() assert repo.sync()['result'] == 'success' erratum_list = target_sat.api.Errata(repository=repo).search( @@ -309,31 +947,17 @@ def test_positive_list_updated(module_org, custom_repo, target_sat): updated = [errata.updated for errata in erratum_list] assert updated == sorted(updated) - -@pytest.mark.tier3 -def test_positive_sorted_issue_date_and_filter_by_cve(module_org, custom_repo, target_sat): - """Sort by issued date and filter errata by CVE - - :id: a921d4c2-8d3d-4462-ba6c-fbd4b898a3f2 - - :Setup: Errata synced on satellite server. - - :steps: GET /katello/api/errata - - :expectedresults: Errata is sorted by issued date and filtered by CVE. - """ # Errata is sorted by issued date. erratum_list = target_sat.api.Errata(repository=custom_repo['repository-id']).search( query={'order': 'issued ASC', 'per_page': '1000'} ) issued = [errata.issued for errata in erratum_list] assert issued == sorted(issued) - # Errata is filtered by CVE erratum_list = target_sat.api.Errata(repository=custom_repo['repository-id']).search( query={'order': 'cve DESC', 'per_page': '1000'} ) - # Most of Errata don't have any CVEs. Removing empty CVEs from results + # Most Errata won't have any CVEs. Removing empty CVEs from results erratum_cves = [errata.cves for errata in erratum_list if errata.cves] # Verifying each errata have its CVEs sorted in DESC order for errata_cves in erratum_cves: @@ -342,66 +966,67 @@ def test_positive_sorted_issue_date_and_filter_by_cve(module_org, custom_repo, t @pytest.fixture(scope='module') -def setup_content_rhel6(module_entitlement_manifest_org, module_target_sat): - """Setup content fot rhel6 content host - Using `Red Hat Enterprise Virtualization Agents for RHEL 6 Server (RPMs)` - from manifest, SATTOOLS_REPO for host-tools and yum_9 repo as custom repo. - - :return: Activation Key, Organization, subscription list - """ - org = module_entitlement_manifest_org - rh_repo_id_rhva = module_target_sat.api_factory.enable_rhrepo_and_fetchid( - basearch='x86_64', - org_id=org.id, - product=constants.PRDS['rhel'], - repo=constants.REPOS['rhva6']['name'], - reposet=constants.REPOSET['rhva6'], - releasever=constants.DEFAULT_RELEASE_VERSION, - ) - rh_repo = module_target_sat.api.Repository(id=rh_repo_id_rhva).read() - rh_repo.sync() +def setup_content_rhel8( + module_sca_manifest_org, + rh_repo_module_manifest, + activation_key, + module_lce, + module_cv, + module_target_sat, + return_result=True, +): + """Setup content for rhel8 content host + Using RH SAT-TOOLS RHEL8 for sat-tools, and FAKE_YUM_9 as custom-repo. + Published to content-view and promoted to lifecycle-environment. - host_tools_product = module_target_sat.api.Product(organization=org).create() - host_tools_repo = module_target_sat.api.Repository( - product=host_tools_product, - ).create() - host_tools_repo.url = settings.repos.SATCLIENT_REPO.RHEL6 - host_tools_repo = host_tools_repo.update(['url']) - host_tools_repo.sync() + Raises `AssertionError` if one or more of the setup components read are empty. - custom_product = module_target_sat.api.Product(organization=org).create() - custom_repo = module_target_sat.api.Repository( - product=custom_product, - ).create() - custom_repo.url = CUSTOM_REPO_URL - custom_repo = custom_repo.update(['url']) + :return: if return_result is True: otherwise None + A dictionary (_result) with the satellite instances of activaton-key, organization, + content-view, lifecycle-environment, rh_repo, custom_repo. + """ + org = module_sca_manifest_org + # Setup Custom and RH repos + custom_repo_id = module_target_sat.cli_factory.setup_org_for_a_custom_repo( + { + 'url': CUSTOM_REPO_URL, + 'organization-id': org.id, + 'lifecycle-environment-id': module_lce.id, + 'activationkey-id': activation_key.id, + 'content-view-id': module_cv.id, + } + )['repository-id'] + custom_repo = module_target_sat.api.Repository(id=custom_repo_id).read() custom_repo.sync() - - lce = module_target_sat.api.LifecycleEnvironment(organization=org).create() - - cv = module_target_sat.api.ContentView( - organization=org, - repository=[rh_repo_id_rhva, host_tools_repo.id, custom_repo.id], - ).create() - cv.publish() - cvv = cv.read().version[0].read() - cvv.promote(data={'environment_ids': lce.id, 'force': False}) - - ak = module_target_sat.api.ActivationKey( - content_view=cv, organization=org, environment=lce - ).create() - - sub_list = [DEFAULT_SUBSCRIPTION_NAME, host_tools_product.name, custom_product.name] - for sub_name in sub_list: - subscription = module_target_sat.api.Subscription(organization=org).search( - query={'search': f'name="{sub_name}"'} - )[0] - ak.add_subscriptions(data={'subscription_id': subscription.id}) - return ak, org, sub_list + # Sync and add RH repo + rh_repo = module_target_sat.api.Repository(id=rh_repo_module_manifest.id).read() + rh_repo.sync() + module_target_sat.cli.ContentView.add_repository( + {'id': module_cv.id, 'organization-id': org.id, 'repository-id': rh_repo.id} + ) + _cv = cv_publish_promote(module_target_sat, org, module_cv, module_lce) + module_cv = _cv['content-view'] + latest_cvv = _cv['content-view-version'] + + _result = { + 'activation-key': activation_key.read(), + 'organization': org.read(), + 'content-view': module_cv.read(), + 'content-view-version': latest_cvv.read(), + 'lifecycle-environment': module_lce.read(), + 'rh_repo': rh_repo.read(), + 'custom_repo': custom_repo.read(), + } + assert all( + entry for entry in _result.values() + ), f'One or more necessary components are not present: {_result}' + return _result if return_result else None -@pytest.mark.tier3 -def test_positive_get_count_for_host(setup_content_rhel6, rhel6_contenthost, target_sat): +@pytest.mark.tier2 +def test_positive_get_count_for_host( + setup_content_rhel8, activation_key, rhel8_contenthost, module_target_sat +): """Available errata count when retrieving Host :id: 2f35933f-8026-414e-8f75-7f4ec048faae @@ -409,49 +1034,82 @@ def test_positive_get_count_for_host(setup_content_rhel6, rhel6_contenthost, tar :Setup: 1. Errata synced on satellite server. - 2. Some Content hosts present. + 2. Some client host present. + 3. Some rh repo and custom repo, added to content-view. + + :Steps: - :steps: GET /api/v2/hosts + 1. Register content host + 2. Install some outdated packages + 3. GET /api/v2/hosts + + :expectedresults: The applicable errata count is retrieved. - :expectedresults: The available errata count is retrieved. :parametrized: yes :CaseImportance: Medium """ - ak_name = setup_content_rhel6[0].name - org_label = setup_content_rhel6[1].label - org_id = setup_content_rhel6[1].id - sub_list = setup_content_rhel6[2] - rhel6_contenthost.install_katello_ca(target_sat) - rhel6_contenthost.register_contenthost(org_label, ak_name) - assert rhel6_contenthost.subscribed - pool_id = rhel6_contenthost.subscription_manager_get_pool(sub_list=sub_list) - pool_list = [pool_id[0][0]] - rhel6_contenthost.subscription_manager_attach_pool(pool_list=pool_list) - rhel6_contenthost.install_katello_host_tools() - rhel6_contenthost.enable_repo(constants.REPOS['rhva6']['id']) - host = rhel6_contenthost.nailgun_host + org = setup_content_rhel8['organization'] + custom_repo = setup_content_rhel8['rh_repo'] + rhel8_contenthost.create_custom_repos(**{f'{custom_repo.name}': custom_repo.url}) + result = rhel8_contenthost.register( + org=org, + activation_keys=activation_key.name, + target=module_target_sat, + loc=None, + ) + assert ( + result.status == 0 + ), f'Failed to register the host - {rhel8_contenthost.hostname}: {result.stderr}' + assert rhel8_contenthost.subscribed + rhel8_contenthost.execute(r'subscription-manager repos --enable \*') + host = rhel8_contenthost.nailgun_host.read() + # No applicable errata to start + assert rhel8_contenthost.applicable_errata_count == 0 for errata in ('security', 'bugfix', 'enhancement'): - _validate_errata_counts(org_id, host, errata_type=errata, expected_value=0) - rhel6_contenthost.run(f'yum install -y {constants.FAKE_1_CUSTOM_PACKAGE}') - _validate_errata_counts(org_id, host, errata_type='security', expected_value=1) - rhel6_contenthost.run(f'yum install -y {constants.REAL_0_RH_PACKAGE}') - _validate_errata_counts(org_id, host, errata_type='bugfix', expected_value=2) + _validate_errata_counts(host, errata_type=errata, expected_value=0) + # One bugfix errata after installing outdated Kangaroo + result = rhel8_contenthost.execute(f'yum install -y {FAKE_9_YUM_OUTDATED_PACKAGES[7]}') + assert result.status == 0, f'Failed to install package {FAKE_9_YUM_OUTDATED_PACKAGES[7]}' + _validate_errata_counts(host, errata_type='bugfix', expected_value=1) + # One enhancement errata after installing outdated Gorilla + result = rhel8_contenthost.execute(f'yum install -y {FAKE_9_YUM_OUTDATED_PACKAGES[3]}') + assert result.status == 0, f'Failed to install package {FAKE_9_YUM_OUTDATED_PACKAGES[3]}' + _validate_errata_counts(host, errata_type='enhancement', expected_value=1) + # Install and check two outdated packages, with applicable security erratum + # custom_repo outdated Walrus + result = rhel8_contenthost.execute(f'yum install -y {FAKE_1_CUSTOM_PACKAGE}') + assert result.status == 0, f'Failed to install package {FAKE_1_CUSTOM_PACKAGE}' + _validate_errata_counts(host, errata_type='security', expected_value=1) + # rh_repo outdated Puppet-agent + result = rhel8_contenthost.execute(f'yum install -y {REAL_RHEL8_1_PACKAGE_FILENAME}') + assert result.status == 0, f'Failed to install package {REAL_RHEL8_1_PACKAGE_FILENAME}' + _validate_errata_counts(host, errata_type='security', expected_value=2) + # All avaliable errata present + assert rhel8_contenthost.applicable_errata_count == 4 @pytest.mark.upgrade @pytest.mark.tier3 -def test_positive_get_applicable_for_host(setup_content_rhel6, rhel6_contenthost, target_sat): +def test_positive_get_applicable_for_host( + setup_content_rhel8, activation_key, rhel8_contenthost, target_sat +): """Get applicable errata ids for a host :id: 51d44d51-eb3f-4ee4-a1df-869629d427ac :Setup: + 1. Errata synced on satellite server. - 2. Some Content hosts present. + 2. Some client hosts present. + 3. Some rh repo and custom repo, added to content-view. - :steps: GET /api/v2/hosts/:id/errata + :Steps: + + 1. Register vm as a content host + 2. Install some outdated packages + 3. GET /api/v2/hosts/:id/errata :expectedresults: The available errata is retrieved. @@ -459,30 +1117,44 @@ def test_positive_get_applicable_for_host(setup_content_rhel6, rhel6_contenthost :CaseImportance: Medium """ - ak_name = setup_content_rhel6[0].name - org_label = setup_content_rhel6[1].label - org_id = setup_content_rhel6[1].id - rhel6_contenthost.install_katello_ca(target_sat) - rhel6_contenthost.register_contenthost(org_label, ak_name) - assert rhel6_contenthost.subscribed - pool_id = rhel6_contenthost.subscription_manager_get_pool(sub_list=setup_content_rhel6[2]) - pool_list = [pool_id[0][0]] - rhel6_contenthost.subscription_manager_attach_pool(pool_list=pool_list) - rhel6_contenthost.install_katello_host_tools() - rhel6_contenthost.enable_repo(constants.REPOS['rhva6']['id']) - host = rhel6_contenthost.nailgun_host - erratum = _fetch_available_errata(org_id, host, expected_amount=0) + org = setup_content_rhel8['organization'] + custom_repo = setup_content_rhel8['rh_repo'] + + rhel8_contenthost.create_custom_repos(**{f'{custom_repo.name}': custom_repo.url}) + result = rhel8_contenthost.register( + activation_keys=activation_key.name, + target=target_sat, + org=org, + loc=None, + ) + assert ( + result.status == 0 + ), f'Failed to register the host - {rhel8_contenthost.hostname}: {result.stderr}' + assert rhel8_contenthost.subscribed + rhel8_contenthost.execute(r'subscription-manager repos --enable \*') + for errata in REPO_WITH_ERRATA['errata']: + # Remove custom package if present, old or new. + package_name = errata['package_name'] + result = rhel8_contenthost.execute(f'yum erase -y {package_name}') + if result.status != 0: + pytest.fail(f'Failed to remove {package_name}: {result.stdout} {result.stderr}') + + rhel8_contenthost.execute('subscription-manager repos') + assert rhel8_contenthost.applicable_errata_count == 0 + host = rhel8_contenthost.nailgun_host.read() + # Check no applicable errata to start + erratum = _fetch_available_errata(host, expected_amount=0) assert len(erratum) == 0 - rhel6_contenthost.run(f'yum install -y {constants.FAKE_1_CUSTOM_PACKAGE}') - erratum = _fetch_available_errata(org_id, host, 1) + # Install outdated applicable custom package + rhel8_contenthost.run(f'yum install -y {FAKE_1_CUSTOM_PACKAGE}') + erratum = _fetch_available_errata(host, 1) assert len(erratum) == 1 assert CUSTOM_REPO_ERRATA_ID in [errata['errata_id'] for errata in erratum] - rhel6_contenthost.run(f'yum install -y {constants.REAL_0_RH_PACKAGE}') - erratum = _fetch_available_errata(org_id, host, 3) - assert len(erratum) == 3 - assert {constants.REAL_1_ERRATA_ID, constants.REAL_2_ERRATA_ID}.issubset( - {errata['errata_id'] for errata in erratum} - ) + # Install outdated applicable real package (from RH repo) + rhel8_contenthost.run(f'yum install -y {REAL_RHEL8_1_PACKAGE_FILENAME}') + erratum = _fetch_available_errata(host, 2) + assert len(erratum) == 2 + assert REAL_RHEL8_1_ERRATA_ID in [errata['errata_id'] for errata in erratum] @pytest.mark.tier3 @@ -497,15 +1169,17 @@ def test_positive_get_diff_for_cv_envs(target_sat): 1. Errata synced on satellite server. 2. Multiple environments present. - :steps: GET /katello/api/compare + :Steps: GET /katello/api/compare :expectedresults: Difference in errata between a set of environments for a content view is retrieved. + """ org = target_sat.api.Organization().create() env = target_sat.api.LifecycleEnvironment(organization=org).create() content_view = target_sat.api.ContentView(organization=org).create() activation_key = target_sat.api.ActivationKey(environment=env, organization=org).create() + # Published content-view-version with repos will be created for repo_url in [settings.repos.yum_9.url, CUSTOM_REPO_URL]: target_sat.cli_factory.setup_org_for_a_custom_repo( { @@ -517,32 +1191,34 @@ def test_positive_get_diff_for_cv_envs(target_sat): } ) new_env = target_sat.api.LifecycleEnvironment(organization=org, prior=env).create() - cvvs = content_view.read().version[-2:] - cvvs[-1].promote(data={'environment_ids': new_env.id, 'force': False}) + # no need to publish a new version, just promote newest + cv_publish_promote( + sat=target_sat, org=org, cv=content_view, lce=[env, new_env], needs_publish=False + ) + content_view = target_sat.api.ContentView(id=content_view.id).read() + # Get last two versions by id to compare + cvv_ids = sorted(cvv.id for cvv in content_view.version)[-2:] result = target_sat.api.Errata().compare( - data={'content_view_version_ids': [cvv.id for cvv in cvvs], 'per_page': '9999'} + data={'content_view_version_ids': [cvv_id for cvv_id in cvv_ids], 'per_page': '9999'} ) cvv2_only_errata = next( errata for errata in result['results'] if errata['errata_id'] == CUSTOM_REPO_ERRATA_ID ) - assert cvvs[-1].id in cvv2_only_errata['comparison'] + assert cvv_ids[-1] in cvv2_only_errata['comparison'] both_cvvs_errata = next( - errata - for errata in result['results'] - if errata['errata_id'] in constants.FAKE_9_YUM_SECURITY_ERRATUM + errata for errata in result['results'] if errata['errata_id'] in FAKE_9_YUM_SECURITY_ERRATUM ) - assert {cvv.id for cvv in cvvs} == set(both_cvvs_errata['comparison']) + assert {cvv_id for cvv_id in cvv_ids} == set(both_cvvs_errata['comparison']) @pytest.mark.tier3 def test_positive_incremental_update_required( - module_org, + module_sca_manifest_org, module_lce, activation_key, module_cv, - custom_repo, - rh_repo, - rhel7_contenthost, + rh_repo_module_manifest, + rhel8_contenthost, target_sat, ): """Given a set of hosts and errata, check for content view version @@ -553,7 +1229,7 @@ def test_positive_incremental_update_required( :Setup: 1. Errata synced on satellite server - :steps: + :Steps: 1. Create VM as Content Host, registering to CV with custom errata 2. Install package in VM so it needs one erratum 3. Check if incremental_updates required: @@ -573,27 +1249,38 @@ def test_positive_incremental_update_required( :BZ: 2013093 """ - rhel7_contenthost.install_katello_ca(target_sat) - rhel7_contenthost.register_contenthost(module_org.label, activation_key.name) - assert rhel7_contenthost.subscribed - rhel7_contenthost.enable_repo(constants.REPOS['rhst7']['id']) - rhel7_contenthost.install_katello_agent() - host = rhel7_contenthost.nailgun_host - # install package to create demand for an Erratum - _install_package( - module_org, - [rhel7_contenthost], - [host.id], - constants.FAKE_1_CUSTOM_PACKAGE, - via_ssh=True, - rpm_package_name=constants.FAKE_1_CUSTOM_PACKAGE, + org = module_sca_manifest_org + rh_repo = target_sat.api.Repository( + id=rh_repo_module_manifest.id, + ).read() + rh_repo.sync() + # Add RH repo to content-view + target_sat.cli.ContentView.add_repository( + {'id': module_cv.id, 'organization-id': org.id, 'repository-id': rh_repo.id} ) + module_cv = target_sat.api.ContentView(id=module_cv.id).read() + _cv = cv_publish_promote(target_sat, org, module_cv, module_lce) + module_cv = _cv['content-view'] + + result = rhel8_contenthost.register( + org=org, + activation_keys=activation_key.name, + target=target_sat, + loc=None, + ) + assert result.status == 0, f'Failed to register the host: {rhel8_contenthost.hostname}' + assert rhel8_contenthost.subscribed + rhel8_contenthost.execute(r'subscription-manager repos --enable \*') + host = rhel8_contenthost.nailgun_host.read() + # install package to create demand for an Erratum + result = rhel8_contenthost.run(f'yum install -y {REAL_RHEL8_1_PACKAGE_FILENAME}') + assert result.status == 0, f'Failed to install package: {REAL_RHEL8_1_PACKAGE_FILENAME}' # Call nailgun to make the API POST to see if any incremental updates are required response = target_sat.api.Host().bulk_available_incremental_updates( data={ - 'organization_id': module_org.id, + 'organization_id': org.id, 'included': {'ids': [host.id]}, - 'errata_ids': [settings.repos.yum_6.errata[2]], + 'errata_ids': [REAL_RHEL8_1_ERRATA_ID], }, ) assert not response, 'Incremental update should not be required at this point' @@ -602,25 +1289,22 @@ def test_positive_incremental_update_required( target_sat.api.RPMContentViewFilter( content_view=module_cv, inclusion=True, name='Include Nothing' ).create() - module_cv.publish() - module_cv = module_cv.read() - CV1V = module_cv.version[-1].read() - # Must promote a CV version into a new Environment before we can add errata - CV1V.promote(data={'environment_ids': module_lce.id, 'force': False}) - module_cv = module_cv.read() + module_cv = target_sat.api.ContentView(id=module_cv.id).read() + module_cv = cv_publish_promote(target_sat, org, module_cv, module_lce)['content-view'] + rhel8_contenthost.execute('subscription-manager repos') # Call nailgun to make the API POST to ensure an incremental update is required response = target_sat.api.Host().bulk_available_incremental_updates( data={ - 'organization_id': module_org.id, + 'organization_id': org.id, 'included': {'ids': [host.id]}, - 'errata_ids': [settings.repos.yum_6.errata[2]], + 'errata_ids': [REAL_RHEL8_1_ERRATA_ID], }, ) - assert 'next_version' in response[0], 'Incremental update should be suggested' - 'at this point' + assert response, 'Nailgun response for host(s) with avaliable incremental update was None' + assert 'next_version' in response[0], 'Incremental update should be suggested at this point' -def _run_remote_command_on_content_host(module_org, command, vm, return_result=False): +def _run_remote_command_on_content_host(command, vm, return_result=False): result = vm.run(command) assert result.status == 0 if return_result: @@ -628,18 +1312,18 @@ def _run_remote_command_on_content_host(module_org, command, vm, return_result=F return None -def _set_prerequisites_for_swid_repos(module_org, vm): +def _set_prerequisites_for_swid_repos(vm): _run_remote_command_on_content_host( - module_org, f'curl --insecure --remote-name {settings.repos.swid_tools_repo}', vm + f'curl --insecure --remote-name {settings.repos.swid_tools_repo}', vm ) - _run_remote_command_on_content_host(module_org, "mv *swid*.repo /etc/yum.repos.d", vm) - _run_remote_command_on_content_host(module_org, "yum install -y swid-tools", vm) - _run_remote_command_on_content_host(module_org, "dnf install -y dnf-plugin-swidtags", vm) + _run_remote_command_on_content_host('mv *swid*.repo /etc/yum.repos.d', vm) + _run_remote_command_on_content_host('yum install -y swid-tools', vm) + _run_remote_command_on_content_host('yum install -y dnf-plugin-swidtags', vm) -def _validate_swid_tags_installed(module_org, vm, module_name): +def _validate_swid_tags_installed(vm, module_name): result = _run_remote_command_on_content_host( - module_org, f"swidq -i -n {module_name} | grep 'Name'", vm, return_result=True + f"swidq -i -n {module_name} | grep 'Name'", vm, return_result=True ) assert module_name in result @@ -647,14 +1331,13 @@ def _validate_swid_tags_installed(module_org, vm, module_name): @pytest.mark.tier3 @pytest.mark.upgrade @pytest.mark.pit_client -@pytest.mark.parametrize( - 'module_repos_collection_with_manifest', - [{'YumRepository': {'url': settings.repos.swid_tag.url, 'distro': 'rhel8'}}], - indirect=True, -) @pytest.mark.no_containers def test_errata_installation_with_swidtags( - module_org, module_lce, module_repos_collection_with_manifest, rhel8_contenthost, target_sat + module_sca_manifest_org, + module_lce, + module_cv, + rhel8_contenthost, + target_sat, ): """Verify errata installation with swid_tags and swid tags get updated after module stream update. @@ -663,15 +1346,18 @@ def test_errata_installation_with_swidtags( :steps: - 1. create product and repository having swid tags - 2. create content view and published it with repository - 3. create activation key and register content host - 4. create rhel8, swid repos on content host - 5. install swid-tools, dnf-plugin-swidtags packages on content host - 6. install older module stream and generate errata, swid tag - 7. assert errata count, swid tags are generated - 8. install errata vis updating module stream - 9. assert errata count and swid tag after module update + 1. promote empty content view and create activation key + 2. create product and repository having swid tags + 3. create rhel8, swid repos on content host + 4. create custom repo with applicable module stream packages + 5. associate repositories and cv / ak to contenthost, sync all content + 6. publish & promote content view version with all content + 7. register host using cv's activation key + 8. install swid-tools, dnf-plugin-swidtags packages on content host + 9. install older module stream and generate errata, swid tag + 10. assert errata count, swid tags are generated + 11. install errata via updating module stream + 12. assert errata count and swid tag changed after module update :expectedresults: swid tags should get updated after errata installation via module stream update @@ -681,215 +1367,114 @@ def test_errata_installation_with_swidtags( :parametrized: yes :CaseImportance: Critical + """ module_name = 'kangaroo' version = '20180704111719' - # setup rhel8 and sat_tools_repos - rhel8_contenthost.create_custom_repos( - **{ - 'baseos': settings.repos.rhel8_os.baseos, - 'appstream': settings.repos.rhel8_os.appstream, - } + # new cv version for ak + cv_publish_promote( + target_sat, + module_sca_manifest_org, + module_cv, + module_lce, ) - module_repos_collection_with_manifest.setup_virtual_machine( - rhel8_contenthost, install_katello_agent=False + _ak = target_sat.api.ActivationKey( + organization=module_sca_manifest_org, + environment=module_lce, + content_view=module_cv, + ).create() + # needed repos for module stream, swid tags, prereqs + _repos = { + 'base_os': settings.repos.rhel8_os.baseos, + 'sat_tools': settings.repos.rhel8_os.appstream, + 'swid_tags': settings.repos.swid_tag.url, + } + # associate repos with host, org, lce, and sync + rhel8_contenthost.create_custom_repos(**_repos) + for _key in _repos: + target_sat.cli_factory.setup_org_for_a_custom_repo( + { + 'url': _repos[_key], + 'name': _key, + 'organization-id': module_sca_manifest_org.id, + 'lifecycle-environment-id': module_lce.id, + 'activationkey-id': _ak.id, + }, + ) + # promote new version with all repos/content + cv_publish_promote( + target_sat, + module_sca_manifest_org, + module_cv, + module_lce, ) - - # install older module stream - rhel8_contenthost.add_rex_key(satellite=target_sat) - _set_prerequisites_for_swid_repos(module_org, vm=rhel8_contenthost) - _run_remote_command_on_content_host( - module_org, f'dnf -y module install {module_name}:0:{version}', rhel8_contenthost + # register host with ak, succeeds + result = rhel8_contenthost.register( + activation_keys=_ak.name, + target=target_sat, + org=module_sca_manifest_org, + loc=None, ) - target_sat.cli.Host.errata_recalculate({'host-id': rhel8_contenthost.nailgun_host.id}) + assert result.status == 0, f'Failed to register the host {target_sat.hostname},\n{result}' + rhel8_contenthost.add_rex_key(satellite=target_sat) + assert ( + rhel8_contenthost.subscribed + ), f'Failed to subscribe the host {target_sat.hostname}, to content.' + rhel8_contenthost.install_katello_ca(target_sat) + result = rhel8_contenthost.execute(r'subscription-manager repos --enable \*') + assert result.status == 0, f'Failed to enable repositories with subscription-manager,\n{result}' + + # install outdated module stream package + _set_prerequisites_for_swid_repos(rhel8_contenthost) + result = rhel8_contenthost.execute(f'dnf -y module install {module_name}:0:{version}') + assert ( + result.status == 0 + ), f'Failed to install module stream package: {module_name}:0:{version}.\n{result.stdout}' + # recalculate errata after install of old module stream + rhel8_contenthost.execute('subscription-manager repos') + # validate swid tags Installed before_errata_apply_result = _run_remote_command_on_content_host( - module_org, f"swidq -i -n {module_name} | grep 'File' | grep -o 'rpm-.*.swidtag'", rhel8_contenthost, return_result=True, ) assert before_errata_apply_result != '' - applicable_errata_count = rhel8_contenthost.applicable_errata_count - assert applicable_errata_count == 1 + assert (applicable_errata_count := rhel8_contenthost.applicable_errata_count) == 1 # apply modular errata - _run_remote_command_on_content_host( - module_org, f'dnf -y module update {module_name}', rhel8_contenthost - ) - _run_remote_command_on_content_host(module_org, 'dnf -y upload-profile', rhel8_contenthost) - target_sat.cli.Host.errata_recalculate({'host-id': rhel8_contenthost.nailgun_host.id}) + result = rhel8_contenthost.execute(f'dnf -y module update {module_name}') + assert ( + result.status == 0 + ), f'Failed to updated module stream package: {module_name}.\n{result.stdout}' + assert rhel8_contenthost.execute('dnf -y upload-profile').status == 0 + + # recalculate and check errata after modular update + rhel8_contenthost.execute('subscription-manager repos') applicable_errata_count -= 1 assert rhel8_contenthost.applicable_errata_count == applicable_errata_count + # swidtags were updated based on package version after_errata_apply_result = _run_remote_command_on_content_host( - module_org, - f"swidq -i -n {module_name} | grep 'File'| grep -o 'rpm-.*.swidtag'", + f"swidq -i -n {module_name} | grep 'File' | grep -o 'rpm-.*.swidtag'", rhel8_contenthost, return_result=True, ) - - # swidtags get updated based on package version assert before_errata_apply_result != after_errata_apply_result -"""Section for tests using RHEL8 Content Host. - The applicability tests using Default Content View are related to the introduction of Pulp3. - """ - - @pytest.fixture(scope='module') -def rh_repo_module_manifest(module_entitlement_manifest_org, module_target_sat): +def rh_repo_module_manifest(module_sca_manifest_org, module_target_sat): """Use module manifest org, creates tools repo, syncs and returns RH repo.""" # enable rhel repo and return its ID rh_repo_id = module_target_sat.api_factory.enable_rhrepo_and_fetchid( - basearch=constants.DEFAULT_ARCHITECTURE, - org_id=module_entitlement_manifest_org.id, - product=constants.PRDS['rhel8'], - repo=constants.REPOS['rhst8']['name'], - reposet=constants.REPOSET['rhst8'], + basearch=DEFAULT_ARCHITECTURE, + org_id=module_sca_manifest_org.id, + product=PRDS['rhel8'], + repo=REPOS['rhst8']['name'], + reposet=REPOSET['rhst8'], releasever='None', ) # Sync step because repo is not synced by default rh_repo = module_target_sat.api.Repository(id=rh_repo_id).read() rh_repo.sync() return rh_repo - - -@pytest.fixture(scope='module') -def rhel8_custom_repo_cv(module_entitlement_manifest_org, module_target_sat): - """Create repo and publish CV so that packages are in Library""" - return module_target_sat.cli_factory.setup_org_for_a_custom_repo( - { - 'url': settings.repos.module_stream_1.url, - 'organization-id': module_entitlement_manifest_org.id, - } - ) - - -@pytest.fixture(scope='module') -def rhel8_module_ak( - module_entitlement_manifest_org, - default_lce, - rh_repo_module_manifest, - rhel8_custom_repo_cv, - module_target_sat, -): - rhel8_module_ak = module_target_sat.api.ActivationKey( - content_view=module_entitlement_manifest_org.default_content_view, - environment=module_target_sat.api.LifecycleEnvironment( - id=module_entitlement_manifest_org.library.id - ), - organization=module_entitlement_manifest_org, - ).create() - # Ensure tools repo is enabled in the activation key - rhel8_module_ak.content_override( - data={ - 'content_overrides': [{'content_label': constants.REPOS['rhst8']['id'], 'value': '1'}] - } - ) - # Fetch available subscriptions - subs = module_target_sat.api.Subscription(organization=module_entitlement_manifest_org).search( - query={'search': f'{constants.DEFAULT_SUBSCRIPTION_NAME}'} - ) - assert subs - # Add default subscription to activation key - rhel8_module_ak.add_subscriptions(data={'subscription_id': subs[0].id}) - # Add custom subscription to activation key - product = module_target_sat.api.Product(organization=module_entitlement_manifest_org).search( - query={'search': 'redhat=false'} - ) - custom_sub = module_target_sat.api.Subscription( - organization=module_entitlement_manifest_org - ).search(query={'search': f'name={product[0].name}'}) - rhel8_module_ak.add_subscriptions(data={'subscription_id': custom_sub[0].id}) - return rhel8_module_ak - - -@pytest.mark.tier2 -def test_apply_modular_errata_using_default_content_view( - module_entitlement_manifest_org, - default_lce, - rhel8_contenthost, - rhel8_module_ak, - rhel8_custom_repo_cv, - target_sat, -): - """ - Registering a RHEL8 system to the default content view with no modules enabled results in - no modular errata or packages showing as applicable or installable - - Enabling a module on a RHEL8 system assigned to the default content view and installing an - older package should result in the modular errata and package showing as applicable and - installable - - :id: 030981dd-19ba-4f8b-9c24-0aee90aaa4c4 - - Steps: - 1. Register host with AK, install tools - 2. Assert no errata indicated - 3. Install older version of stream - 4. Assert errata is applicable - 5. Update module stream - 6. Assert errata is no longer applicable - - :expectedresults: Errata enumeration works with module streams when using default Content View - - :CaseAutomation: Automated - - :parametrized: yes - """ - module_name = 'duck' - stream = '0' - version = '20180704244205' - - rhel8_contenthost.install_katello_ca(target_sat) - rhel8_contenthost.register_contenthost( - module_entitlement_manifest_org.label, rhel8_module_ak.name - ) - assert rhel8_contenthost.subscribed - host = rhel8_contenthost.nailgun_host - host = host.read() - # Assert no errata on host, no packages applicable or installable - errata = _fetch_available_errata(module_entitlement_manifest_org, host, expected_amount=0) - assert len(errata) == 0 - rhel8_contenthost.install_katello_host_tools() - # Install older version of module stream to generate the errata - result = rhel8_contenthost.execute( - f'yum -y module install {module_name}:{stream}:{version}', - ) - assert result.status == 0 - # Check that there is now two errata applicable - errata = _fetch_available_errata(module_entitlement_manifest_org, host, 2) - target_sat.cli.Host.errata_recalculate({'host-id': rhel8_contenthost.nailgun_host.id}) - assert len(errata) == 2 - # Assert that errata package is required - assert constants.FAKE_3_CUSTOM_PACKAGE in errata[0]['module_streams'][0]['packages'] - # Update module - result = rhel8_contenthost.execute( - f'yum -y module update {module_name}:{stream}:{version}', - ) - assert result.status == 0 - # Check that there is now no errata applicable - errata = _fetch_available_errata(module_entitlement_manifest_org, host, 0) - assert len(errata) == 0 - - @pytest.mark.tier2 - @pytest.mark.skip("Uses old large_errata repo from repos.fedorapeople") - def test_positive_sync_repos_with_large_errata(target_sat): - """Attempt to synchronize 2 repositories containing large (or lots of) - errata. - - :id: d6680b9f-4c88-40b4-8b96-3d170664cb28 - - :customerscenario: true - - :BZ: 1463811 - - :expectedresults: both repositories were successfully synchronized - """ - org = target_sat.api.Organization().create() - for _ in range(2): - product = target_sat.api.Product(organization=org).create() - repo = target_sat.api.Repository(product=product, url=settings.repos.yum_7.url).create() - response = repo.sync() - assert response, f"Repository {repo} failed to sync."