diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 1757adc413a..3159784d158 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -87,7 +87,7 @@ body: [collections org]: /ansible-collections - placeholder: dnf, apt, yum, pip, user etc. + placeholder: dnf, apt, pip, user etc. validations: required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 5a954614e4b..dd39c40de1c 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -139,7 +139,7 @@ body: [collections org]: /ansible-collections - placeholder: dnf, apt, yum, pip, user etc. + placeholder: dnf, apt, pip, user etc. validations: required: true diff --git a/changelogs/fragments/yum-removal.yml b/changelogs/fragments/yum-removal.yml new file mode 100644 index 00000000000..d9f9dc3ff4a --- /dev/null +++ b/changelogs/fragments/yum-removal.yml @@ -0,0 +1,2 @@ +removed_features: + - "With the removal of Python 2 support, the yum module and yum action plugin are removed and redirected to ``dnf``." diff --git a/hacking/azp/README.md b/hacking/azp/README.md index 6e833fefafa..fbb531844f5 100644 --- a/hacking/azp/README.md +++ b/hacking/azp/README.md @@ -15,7 +15,7 @@ This directory contains the following scripts: Incidental testing and code coverage occurs when a test covers one or more portions of code as an unintentional side-effect of testing another portion of code. -For example, the ``yum`` integration test intentionally tests the ``yum`` Ansible module. +For example, the ``dnf`` integration test intentionally tests the ``dnf`` Ansible module. However, in doing so it also uses, and unintentionally tests the ``file`` module as well. As part of the process of migrating modules and plugins into collections, integration tests were identified that provided exclusive incidental code coverage. diff --git a/lib/ansible/config/ansible_builtin_runtime.yml b/lib/ansible/config/ansible_builtin_runtime.yml index 570ccb051cf..3630e76ca43 100644 --- a/lib/ansible/config/ansible_builtin_runtime.yml +++ b/lib/ansible/config/ansible_builtin_runtime.yml @@ -9088,6 +9088,8 @@ plugin_routing: tombstone: removal_date: "2023-05-16" warning_text: Use include_tasks or import_tasks instead. + yum: + redirect: ansible.builtin.dnf become: doas: redirect: community.general.doas diff --git a/lib/ansible/executor/task_result.py b/lib/ansible/executor/task_result.py index 0c85130c41a..2690f3a52bb 100644 --- a/lib/ansible/executor/task_result.py +++ b/lib/ansible/executor/task_result.py @@ -54,7 +54,7 @@ class TaskResult: if 'results' in self._result: results = self._result['results'] # Loop tasks are only considered skipped if all items were skipped. - # some squashed results (eg, yum) are not dicts and can't be skipped individually + # some squashed results (eg, dnf) are not dicts and can't be skipped individually if results and all(isinstance(res, dict) and res.get('skipped', False) for res in results): return True diff --git a/lib/ansible/module_utils/common/respawn.py b/lib/ansible/module_utils/common/respawn.py index e2cc7db2595..0f57c154576 100644 --- a/lib/ansible/module_utils/common/respawn.py +++ b/lib/ansible/module_utils/common/respawn.py @@ -19,7 +19,7 @@ def respawn_module(interpreter_path): Respawn the currently-running Ansible Python module under the specified Python interpreter. Ansible modules that require libraries that are typically available only under well-known interpreters - (eg, ``yum``, ``apt``, ``dnf``) can use bespoke logic to determine the libraries they need are not + (eg, ``apt``, ``dnf``) can use bespoke logic to determine the libraries they need are not available, then call `respawn_module` to re-execute the current module under a different interpreter and exit the current process when the new subprocess has completed. The respawned process inherits only stdout/stderr from the current process. diff --git a/lib/ansible/module_utils/facts/system/pkg_mgr.py b/lib/ansible/module_utils/facts/system/pkg_mgr.py index 5b7c3119712..e9da18647b8 100644 --- a/lib/ansible/module_utils/facts/system/pkg_mgr.py +++ b/lib/ansible/module_utils/facts/system/pkg_mgr.py @@ -15,11 +15,11 @@ from ansible.module_utils.facts.collector import BaseFactCollector # package manager, put the preferred one last. If there is an # ansible module, use that as the value for the 'name' key. PKG_MGRS = [{'path': '/usr/bin/rpm-ostree', 'name': 'atomic_container'}, - {'path': '/usr/bin/yum', 'name': 'yum'}, # NOTE the `path` key for dnf/dnf5 is effectively discarded when matched for Red Hat OS family, # special logic to infer the default `pkg_mgr` is used in `PkgMgrFactCollector._check_rh_versions()` # leaving them here so a list of package modules can be constructed by iterating over `name` keys + {'path': '/usr/bin/yum', 'name': 'dnf'}, {'path': '/usr/bin/dnf-3', 'name': 'dnf'}, {'path': '/usr/bin/dnf5', 'name': 'dnf5'}, @@ -45,7 +45,6 @@ PKG_MGRS = [{'path': '/usr/bin/rpm-ostree', 'name': 'atomic_container'}, {'path': '/usr/bin/swupd', 'name': 'swupd'}, {'path': '/usr/sbin/sorcery', 'name': 'sorcery'}, {'path': '/usr/bin/installp', 'name': 'installp'}, - {'path': '/QOpenSys/pkgs/bin/yum', 'name': 'yum'}, ] @@ -69,39 +68,18 @@ class PkgMgrFactCollector(BaseFactCollector): super(PkgMgrFactCollector, self).__init__(*args, **kwargs) self._default_unknown_pkg_mgr = 'unknown' - def _check_rh_versions(self, pkg_mgr_name, collected_facts): + def _check_rh_versions(self): if os.path.exists('/run/ostree-booted'): return "atomic_container" - # Reset whatever was matched from PKG_MGRS, infer the default pkg_mgr below - pkg_mgr_name = self._default_unknown_pkg_mgr # Since /usr/bin/dnf and /usr/bin/microdnf can point to different versions of dnf in different distributions # the only way to infer the default package manager is to look at the binary they are pointing to. # /usr/bin/microdnf is likely used only in fedora minimal container so /usr/bin/dnf takes precedence for bin_path in ('/usr/bin/dnf', '/usr/bin/microdnf'): if os.path.exists(bin_path): - pkg_mgr_name = 'dnf5' if os.path.realpath(bin_path) == '/usr/bin/dnf5' else 'dnf' - break - - try: - major_version = collected_facts['ansible_distribution_major_version'] - if collected_facts['ansible_distribution'] == 'Kylin Linux Advanced Server': - major_version = major_version.lstrip('V') - distro_major_ver = int(major_version) - except ValueError: - # a non integer magical future version - return self._default_unknown_pkg_mgr - - if ( - (collected_facts['ansible_distribution'] == 'Fedora' and distro_major_ver < 23) - or (collected_facts['ansible_distribution'] == 'Kylin Linux Advanced Server' and distro_major_ver < 10) - or (collected_facts['ansible_distribution'] == 'Amazon' and distro_major_ver < 2022) - or (collected_facts['ansible_distribution'] == 'TencentOS' and distro_major_ver < 3) - or distro_major_ver < 8 # assume RHEL or a clone - ) and any(pm for pm in PKG_MGRS if pm['name'] == 'yum' and os.path.exists(pm['path'])): - pkg_mgr_name = 'yum' + return 'dnf5' if os.path.realpath(bin_path) == '/usr/bin/dnf5' else 'dnf' - return pkg_mgr_name + return self._default_unknown_pkg_mgr def _check_apt_flavor(self, pkg_mgr_name): # Check if '/usr/bin/apt' is APT-RPM or an ordinary (dpkg-based) APT. @@ -142,9 +120,9 @@ class PkgMgrFactCollector(BaseFactCollector): # installed or available to the distro, the ansible_fact entry should be # the default package manager officially supported by the distro. if collected_facts['ansible_os_family'] == "RedHat": - pkg_mgr_name = self._check_rh_versions(pkg_mgr_name, collected_facts) + pkg_mgr_name = self._check_rh_versions() elif collected_facts['ansible_os_family'] == 'Debian' and pkg_mgr_name != 'apt': - # It's possible to install yum, dnf, zypper, rpm, etc inside of + # It's possible to install dnf, zypper, rpm, etc inside of # Debian. Doing so does not mean the system wants to use them. pkg_mgr_name = 'apt' elif collected_facts['ansible_os_family'] == 'Altlinux': diff --git a/lib/ansible/module_utils/yumdnf.py b/lib/ansible/module_utils/yumdnf.py index 9aaba00de6f..4a07c4fac6a 100644 --- a/lib/ansible/module_utils/yumdnf.py +++ b/lib/ansible/module_utils/yumdnf.py @@ -11,16 +11,12 @@ from __future__ import annotations -import os -import time -import glob from abc import ABCMeta, abstractmethod -from ansible.module_utils.six import with_metaclass - yumdnf_argument_spec = dict( argument_spec=dict( allow_downgrade=dict(type='bool', default=False), + allowerasing=dict(default=False, type="bool"), autoremove=dict(type='bool', default=False), bugfix=dict(required=False, type='bool', default=False), cacheonly=dict(type='bool', default=False), @@ -35,10 +31,14 @@ yumdnf_argument_spec = dict( enablerepo=dict(type='list', elements='str', default=[]), exclude=dict(type='list', elements='str', default=[]), installroot=dict(type='str', default="/"), - install_repoquery=dict(type='bool', default=True), + install_repoquery=dict( + type='bool', default=True, + removed_in_version='2.20', removed_from_collection='ansible.builtin', + ), install_weak_deps=dict(type='bool', default=True), list=dict(type='str'), name=dict(type='list', elements='str', aliases=['pkg'], default=[]), + nobest=dict(default=False, type="bool"), releasever=dict(default=None), security=dict(type='bool', default=False), skip_broken=dict(type='bool', default=False), @@ -56,7 +56,7 @@ yumdnf_argument_spec = dict( ) -class YumDnf(with_metaclass(ABCMeta, object)): # type: ignore[misc] +class YumDnf(metaclass=ABCMeta): """ Abstract class that handles the population of instance variables that should be identical between both YUM and DNF modules because of the feature parity @@ -68,6 +68,7 @@ class YumDnf(with_metaclass(ABCMeta, object)): # type: ignore[misc] self.module = module self.allow_downgrade = self.module.params['allow_downgrade'] + self.allowerasing = self.module.params['allowerasing'] self.autoremove = self.module.params['autoremove'] self.bugfix = self.module.params['bugfix'] self.cacheonly = self.module.params['cacheonly'] @@ -86,6 +87,7 @@ class YumDnf(with_metaclass(ABCMeta, object)): # type: ignore[misc] self.install_weak_deps = self.module.params['install_weak_deps'] self.list = self.module.params['list'] self.names = [p.strip() for p in self.module.params['name']] + self.nobest = self.module.params['nobest'] self.releasever = self.module.params['releasever'] self.security = self.module.params['security'] self.skip_broken = self.module.params['skip_broken'] @@ -126,31 +128,6 @@ class YumDnf(with_metaclass(ABCMeta, object)): # type: ignore[misc] results=[], ) - # This should really be redefined by both the yum and dnf module but a - # default isn't a bad idea - self.lockfile = '/var/run/yum.pid' - - @abstractmethod - def is_lockfile_pid_valid(self): - return - - def _is_lockfile_present(self): - return (os.path.isfile(self.lockfile) or glob.glob(self.lockfile)) and self.is_lockfile_pid_valid() - - def wait_for_lock(self): - '''Poll until the lock is removed if timeout is a positive number''' - - if not self._is_lockfile_present(): - return - - if self.lock_timeout > 0: - for iteration in range(0, self.lock_timeout): - time.sleep(1) - if not self._is_lockfile_present(): - return - - self.module.fail_json(msg='{0} lockfile is held by another process'.format(self.pkg_mgr_name)) - def listify_comma_sep_strings_in_list(self, some_list): """ method to accept a list of strings as the parameter, find any strings diff --git a/lib/ansible/modules/async_status.py b/lib/ansible/modules/async_status.py index ee584e3e02b..9bcc3ef1255 100644 --- a/lib/ansible/modules/async_status.py +++ b/lib/ansible/modules/async_status.py @@ -54,17 +54,17 @@ author: EXAMPLES = r''' --- -- name: Asynchronous yum task - ansible.builtin.yum: +- name: Asynchronous dnf task + ansible.builtin.dnf: name: docker-io state: present async: 1000 poll: 0 - register: yum_sleeper + register: dnf_sleeper - name: Wait for asynchronous job to end ansible.builtin.async_status: - jid: '{{ yum_sleeper.ansible_job_id }}' + jid: '{{ dnf_sleeper.ansible_job_id }}' register: job_result until: job_result.finished retries: 100 @@ -72,7 +72,7 @@ EXAMPLES = r''' - name: Clean up async file ansible.builtin.async_status: - jid: '{{ yum_sleeper.ansible_job_id }}' + jid: '{{ dnf_sleeper.ansible_job_id }}' mode: cleanup ''' diff --git a/lib/ansible/modules/dnf.py b/lib/ansible/modules/dnf.py index e03661f78f3..aa949879155 100644 --- a/lib/ansible/modules/dnf.py +++ b/lib/ansible/modules/dnf.py @@ -21,7 +21,7 @@ options: description: - By default, this module will select the backend based on the C(ansible_pkg_mgr) fact. default: "auto" - choices: [ auto, dnf4, dnf5 ] + choices: [ auto, yum, yum4, dnf4, dnf5 ] type: str version_added: 2.15 name: @@ -206,8 +206,8 @@ options: version_added: "2.7" install_repoquery: description: - - This is effectively a no-op in DNF as it is not needed with DNF, but is an accepted parameter for feature - parity/compatibility with the M(ansible.builtin.yum) module. + - This is effectively a no-op in DNF as it is not needed with DNF. + - This option is deprecated and will be removed in ansible-core 2.20. type: bool default: "yes" version_added: "2.7" @@ -261,7 +261,7 @@ extends_documentation_fragment: - action_common_attributes.flow attributes: action: - details: In the case of dnf, it has 2 action plugins that use it under the hood, M(ansible.builtin.yum) and M(ansible.builtin.package). + details: dnf has 2 action plugins that use it under the hood, M(ansible.builtin.dnf) and M(ansible.builtin.package). support: partial async: support: none @@ -409,7 +409,6 @@ class DnfModule(YumDnf): super(DnfModule, self).__init__(module) self._ensure_dnf() - self.lockfile = "/var/cache/dnf/*_lock.pid" self.pkg_mgr_name = "dnf" try: @@ -417,15 +416,6 @@ class DnfModule(YumDnf): except AttributeError: self.with_modules = False - # DNF specific args that are not part of YumDnf - self.allowerasing = self.module.params['allowerasing'] - self.nobest = self.module.params['nobest'] - - def is_lockfile_pid_valid(self): - # FIXME? it looks like DNF takes care of invalid lock files itself? - # https://github.com/ansible/ansible/issues/57189 - return True - def _sanitize_dnf_error_msg_install(self, spec, error): """ For unhandled dnf.exceptions.Error scenarios, there are certain error @@ -467,7 +457,7 @@ class DnfModule(YumDnf): 'version': package.version, 'repo': package.repoid} - # envra format for alignment with the yum module + # envra format for backwards compat result['envra'] = '{epoch}:{name}-{version}-{release}.{arch}'.format(**result) # keep nevra key for backwards compat as it was previously @@ -1461,11 +1451,7 @@ def main(): # list=repos # list=pkgspec - # Extend yumdnf_argument_spec with dnf-specific features that will never be - # backported to yum because yum is now in "maintenance mode" upstream - yumdnf_argument_spec['argument_spec']['allowerasing'] = dict(default=False, type='bool') - yumdnf_argument_spec['argument_spec']['nobest'] = dict(default=False, type='bool') - yumdnf_argument_spec['argument_spec']['use_backend'] = dict(default='auto', choices=['auto', 'dnf4', 'dnf5']) + yumdnf_argument_spec['argument_spec']['use_backend'] = dict(default='auto', choices=['auto', 'yum', 'yum4', 'dnf4', 'dnf5']) module = AnsibleModule( **yumdnf_argument_spec diff --git a/lib/ansible/modules/dnf5.py b/lib/ansible/modules/dnf5.py index facc2e37514..2a4ed660deb 100644 --- a/lib/ansible/modules/dnf5.py +++ b/lib/ansible/modules/dnf5.py @@ -151,7 +151,7 @@ options: validate_certs: description: - This is effectively a no-op in the dnf5 module as dnf5 itself handles downloading a https url as the source of the rpm, - but is an accepted parameter for feature parity/compatibility with the M(ansible.builtin.yum) module. + but is an accepted parameter for feature parity/compatibility with the M(ansible.builtin.dnf) module. type: bool default: "yes" sslverify: @@ -174,8 +174,8 @@ options: default: "no" install_repoquery: description: - - This is effectively a no-op in DNF as it is not needed with DNF, but is an accepted parameter for feature - parity/compatibility with the M(ansible.builtin.yum) module. + - This is effectively a no-op in DNF as it is not needed with DNF. + - This option is deprecated and will be removed in ansible-core 2.20. type: bool default: "yes" download_only: @@ -222,7 +222,7 @@ extends_documentation_fragment: - action_common_attributes.flow attributes: action: - details: In the case of dnf, it has 2 action plugins that use it under the hood, M(ansible.builtin.yum) and M(ansible.builtin.package). + details: dnf5 has 2 action plugins that use it under the hood, M(ansible.builtin.dnf) and M(ansible.builtin.package). support: partial async: support: none @@ -408,14 +408,8 @@ class Dnf5Module(YumDnf): super(Dnf5Module, self).__init__(module) self._ensure_dnf() - # FIXME https://github.com/rpm-software-management/dnf5/issues/402 - self.lockfile = "" self.pkg_mgr_name = "dnf5" - # DNF specific args that are not part of YumDnf - self.allowerasing = self.module.params["allowerasing"] - self.nobest = self.module.params["nobest"] - def _ensure_dnf(self): locale = get_best_parsable_locale(self.module) os.environ["LC_ALL"] = os.environ["LC_MESSAGES"] = locale @@ -457,10 +451,6 @@ class Dnf5Module(YumDnf): failures=[], ) - def is_lockfile_pid_valid(self): - # FIXME https://github.com/rpm-software-management/dnf5/issues/402 - return True - def run(self): if sys.version_info.major < 3: self.module.fail_json( @@ -702,10 +692,6 @@ class Dnf5Module(YumDnf): def main(): - # Extend yumdnf_argument_spec with dnf-specific features that will never be - # backported to yum because yum is now in "maintenance mode" upstream - yumdnf_argument_spec["argument_spec"]["allowerasing"] = dict(default=False, type="bool") - yumdnf_argument_spec["argument_spec"]["nobest"] = dict(default=False, type="bool") Dnf5Module(AnsibleModule(**yumdnf_argument_spec)).run() diff --git a/lib/ansible/modules/package.py b/lib/ansible/modules/package.py index a8404769d1f..545a394a43b 100644 --- a/lib/ansible/modules/package.py +++ b/lib/ansible/modules/package.py @@ -15,7 +15,7 @@ author: - Ansible Core Team short_description: Generic OS package manager description: - - This modules manages packages on a target without specifying a package manager module (like M(ansible.builtin.yum), M(ansible.builtin.apt), ...). + - This modules manages packages on a target without specifying a package manager module (like M(ansible.builtin.dnf), M(ansible.builtin.apt), ...). It is convenient to use in an heterogeneous environment of machines without having to create a specific task for each package manager. M(ansible.builtin.package) calls behind the module for the package manager used by the operating system discovered by the module M(ansible.builtin.setup). If M(ansible.builtin.setup) was not yet run, M(ansible.builtin.package) will run it. @@ -38,7 +38,7 @@ options: required: true use: description: - - The required package manager module to use (V(yum), V(apt), and so on). The default V(auto) will use existing facts or try to autodetect it. + - The required package manager module to use (V(dnf), V(apt), and so on). The default V(auto) will use existing facts or try to autodetect it. - You should only use this field if the automatic selection is not working for some reason. default: auto requirements: diff --git a/lib/ansible/modules/yum.py b/lib/ansible/modules/yum.py deleted file mode 100644 index cbcf4b954f2..00000000000 --- a/lib/ansible/modules/yum.py +++ /dev/null @@ -1,1820 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright: (c) 2012, Red Hat, Inc -# Written by Seth Vidal -# Copyright: (c) 2014, Epic Games, Inc. - -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import annotations - - -DOCUMENTATION = ''' ---- -module: yum -version_added: historical -short_description: Manages packages with the I(yum) package manager -description: - - Installs, upgrade, downgrades, removes, and lists packages and groups with the I(yum) package manager. - - This module only works on Python 2. If you require Python 3 support see the M(ansible.builtin.dnf) module. -options: - use_backend: - description: - - This module supports V(yum) (as it always has), this is known as C(yum3)/C(YUM3)/C(yum-deprecated) by - upstream yum developers. As of Ansible 2.7+, this module also supports C(YUM4), which is the - "new yum" and it has an V(dnf) backend. As of ansible-core 2.15+, this module will auto select the backend - based on the C(ansible_pkg_mgr) fact. - - By default, this module will select the backend based on the C(ansible_pkg_mgr) fact. - default: "auto" - choices: [ auto, yum, yum4, dnf, dnf4, dnf5 ] - type: str - version_added: "2.7" - name: - description: - - A package name or package specifier with version, like V(name-1.0). - - Comparison operators for package version are valid here C(>), C(<), C(>=), C(<=). Example - V(name>=1.0) - - If a previous version is specified, the task also needs to turn O(allow_downgrade) on. - See the O(allow_downgrade) documentation for caveats with downgrading packages. - - When using O(state=latest), this can be V('*') which means run C(yum -y update). - - You can also pass a url or a local path to an rpm file (using O(state=present)). - To operate on several packages this can accept a comma separated string of packages or (as of 2.0) a list of packages. - aliases: [ pkg ] - type: list - elements: str - default: [] - exclude: - description: - - Package name(s) to exclude when state=present, or latest - type: list - elements: str - default: [] - version_added: "2.0" - list: - description: - - "Package name to run the equivalent of C(yum list --show-duplicates ) against. In addition to listing packages, - use can also list the following: V(installed), V(updates), V(available) and V(repos)." - - This parameter is mutually exclusive with O(name). - type: str - state: - description: - - Whether to install (V(present) or V(installed), V(latest)), or remove (V(absent) or V(removed)) a package. - - V(present) and V(installed) will simply ensure that a desired package is installed. - - V(latest) will update the specified package if it's not of the latest available version. - - V(absent) and V(removed) will remove the specified package. - - Default is V(None), however in effect the default action is V(present) unless the O(autoremove) option is - enabled for this module, then V(absent) is inferred. - type: str - choices: [ absent, installed, latest, present, removed ] - enablerepo: - description: - - I(Repoid) of repositories to enable for the install/update operation. - These repos will not persist beyond the transaction. - When specifying multiple repos, separate them with a C(","). - - As of Ansible 2.7, this can alternatively be a list instead of C(",") - separated string - type: list - elements: str - default: [] - version_added: "0.9" - disablerepo: - description: - - I(Repoid) of repositories to disable for the install/update operation. - These repos will not persist beyond the transaction. - When specifying multiple repos, separate them with a C(","). - - As of Ansible 2.7, this can alternatively be a list instead of C(",") - separated string - type: list - elements: str - default: [] - version_added: "0.9" - conf_file: - description: - - The remote yum configuration file to use for the transaction. - type: str - version_added: "0.6" - disable_gpg_check: - description: - - Whether to disable the GPG checking of signatures of packages being - installed. Has an effect only if O(state) is V(present) or V(latest). - type: bool - default: "no" - version_added: "1.2" - skip_broken: - description: - - Skip all unavailable packages or packages with broken dependencies - without raising an error. Equivalent to passing the --skip-broken option. - type: bool - default: "no" - version_added: "2.3" - update_cache: - description: - - Force yum to check if cache is out of date and redownload if needed. - Has an effect only if O(state) is V(present) or V(latest). - type: bool - default: "no" - aliases: [ expire-cache ] - version_added: "1.9" - validate_certs: - description: - - This only applies if using a https url as the source of the rpm. e.g. for localinstall. If set to V(false), the SSL certificates will not be validated. - - This should only set to V(false) used on personally controlled sites using self-signed certificates as it avoids verifying the source site. - - Prior to 2.1 the code worked as if this was set to V(true). - type: bool - default: "yes" - version_added: "2.1" - sslverify: - description: - - Disables SSL validation of the repository server for this transaction. - - This should be set to V(false) if one of the configured repositories is using an untrusted or self-signed certificate. - type: bool - default: "yes" - version_added: "2.13" - update_only: - description: - - When using latest, only update installed packages. Do not install packages. - - Has an effect only if O(state) is V(latest) - default: "no" - type: bool - version_added: "2.5" - - installroot: - description: - - Specifies an alternative installroot, relative to which all packages - will be installed. - default: "/" - type: str - version_added: "2.3" - security: - description: - - If set to V(true), and O(state=latest) then only installs updates that have been marked security related. - type: bool - default: "no" - version_added: "2.4" - bugfix: - description: - - If set to V(true), and O(state=latest) then only installs updates that have been marked bugfix related. - default: "no" - type: bool - version_added: "2.6" - allow_downgrade: - description: - - Specify if the named package and version is allowed to downgrade - a maybe already installed higher version of that package. - Note that setting allow_downgrade=True can make this module - behave in a non-idempotent way. The task could end up with a set - of packages that does not match the complete list of specified - packages to install (because dependencies between the downgraded - package and others can cause changes to the packages which were - in the earlier transaction). - type: bool - default: "no" - version_added: "2.4" - enable_plugin: - description: - - I(Plugin) name to enable for the install/update operation. - The enabled plugin will not persist beyond the transaction. - type: list - elements: str - default: [] - version_added: "2.5" - disable_plugin: - description: - - I(Plugin) name to disable for the install/update operation. - The disabled plugins will not persist beyond the transaction. - type: list - elements: str - default: [] - version_added: "2.5" - releasever: - description: - - Specifies an alternative release from which all packages will be - installed. - type: str - version_added: "2.7" - autoremove: - description: - - If V(true), removes all "leaf" packages from the system that were originally - installed as dependencies of user-installed packages but which are no longer - required by any such package. Should be used alone or when O(state) is V(absent) - - "NOTE: This feature requires yum >= 3.4.3 (RHEL/CentOS 7+)" - type: bool - default: "no" - version_added: "2.7" - disable_excludes: - description: - - Disable the excludes defined in YUM config files. - - If set to V(all), disables all excludes. - - If set to V(main), disable excludes defined in [main] in yum.conf. - - If set to V(repoid), disable excludes defined for given repo id. - type: str - version_added: "2.7" - download_only: - description: - - Only download the packages, do not install them. - default: "no" - type: bool - version_added: "2.7" - lock_timeout: - description: - - Amount of time to wait for the yum lockfile to be freed. - required: false - default: 30 - type: int - version_added: "2.8" - install_weak_deps: - description: - - Will also install all packages linked by a weak dependency relation. - - "NOTE: This feature requires yum >= 4 (RHEL/CentOS 8+)" - type: bool - default: "yes" - version_added: "2.8" - download_dir: - description: - - Specifies an alternate directory to store packages. - - Has an effect only if O(download_only) is specified. - type: str - version_added: "2.8" - install_repoquery: - description: - - If repoquery is not available, install yum-utils. If the system is - registered to RHN or an RHN Satellite, repoquery allows for querying - all channels assigned to the system. It is also required to use the - 'list' parameter. - - "NOTE: This will run and be logged as a separate yum transaction which - takes place before any other installation or removal." - - "NOTE: This will use the system's default enabled repositories without - regard for disablerepo/enablerepo given to the module." - required: false - version_added: "1.5" - default: "yes" - type: bool - cacheonly: - description: - - Tells yum to run entirely from system cache; does not download or update metadata. - default: "no" - type: bool - version_added: "2.12" -extends_documentation_fragment: -- action_common_attributes -- action_common_attributes.flow -attributes: - action: - details: In the case of yum, it has 2 action plugins that use it under the hood, M(ansible.builtin.yum) and M(ansible.builtin.package). - support: partial - async: - support: none - bypass_host_loop: - support: none - check_mode: - support: full - diff_mode: - support: full - platform: - platforms: rhel -notes: - - When used with a C(loop:) each package will be processed individually, - it is much more efficient to pass the list directly to the O(name) option. - - In versions prior to 1.9.2 this module installed and removed each package - given to the yum module separately. This caused problems when packages - specified by filename or url had to be installed or removed together. In - 1.9.2 this was fixed so that packages are installed in one yum - transaction. However, if one of the packages adds a new yum repository - that the other packages come from (such as epel-release) then that package - needs to be installed in a separate task. This mimics yum's command line - behaviour. - - 'Yum itself has two types of groups. "Package groups" are specified in the - rpm itself while "environment groups" are specified in a separate file - (usually by the distribution). Unfortunately, this division becomes - apparent to ansible users because ansible needs to operate on the group - of packages in a single transaction and yum requires groups to be specified - in different ways when used in that way. Package groups are specified as - "@development-tools" and environment groups are "@^gnome-desktop-environment". - Use the "yum group list hidden ids" command to see which category of group the group - you want to install falls into.' - - 'The yum module does not support clearing yum cache in an idempotent way, so it - was decided not to implement it, the only method is to use command and call the yum - command directly, namely "command: yum clean all" - https://github.com/ansible/ansible/pull/31450#issuecomment-352889579' -# informational: requirements for nodes -requirements: -- yum -author: - - Ansible Core Team - - Seth Vidal (@skvidal) - - Eduard Snesarev (@verm666) - - Berend De Schouwer (@berenddeschouwer) - - Abhijeet Kasurde (@Akasurde) - - Adam Miller (@maxamillion) -''' - -EXAMPLES = ''' -- name: Install the latest version of Apache - ansible.builtin.yum: - name: httpd - state: latest - -- name: Install Apache >= 2.4 - ansible.builtin.yum: - name: httpd>=2.4 - state: present - -- name: Install a list of packages (suitable replacement for 2.11 loop deprecation warning) - ansible.builtin.yum: - name: - - nginx - - postgresql - - postgresql-server - state: present - -- name: Install a list of packages with a list variable - ansible.builtin.yum: - name: "{{ packages }}" - vars: - packages: - - httpd - - httpd-tools - -- name: Remove the Apache package - ansible.builtin.yum: - name: httpd - state: absent - -- name: Install the latest version of Apache from the testing repo - ansible.builtin.yum: - name: httpd - enablerepo: testing - state: present - -- name: Install one specific version of Apache - ansible.builtin.yum: - name: httpd-2.2.29-1.4.amzn1 - state: present - -- name: Upgrade all packages - ansible.builtin.yum: - name: '*' - state: latest - -- name: Upgrade all packages, excluding kernel & foo related packages - ansible.builtin.yum: - name: '*' - state: latest - exclude: kernel*,foo* - -- name: Install the nginx rpm from a remote repo - ansible.builtin.yum: - name: http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm - state: present - -- name: Install nginx rpm from a local file - ansible.builtin.yum: - name: /usr/local/src/nginx-release-centos-6-0.el6.ngx.noarch.rpm - state: present - -- name: Install the 'Development tools' package group - ansible.builtin.yum: - name: "@Development tools" - state: present - -- name: Install the 'Gnome desktop' environment group - ansible.builtin.yum: - name: "@^gnome-desktop-environment" - state: present - -- name: List ansible packages and register result to print with debug later - ansible.builtin.yum: - list: ansible - register: result - -- name: Install package with multiple repos enabled - ansible.builtin.yum: - name: sos - enablerepo: "epel,ol7_latest" - -- name: Install package with multiple repos disabled - ansible.builtin.yum: - name: sos - disablerepo: "epel,ol7_latest" - -- name: Download the nginx package but do not install it - ansible.builtin.yum: - name: - - nginx - state: latest - download_only: true -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.common.locale import get_best_parsable_locale -from ansible.module_utils.common.respawn import has_respawned, respawn_module -from ansible.module_utils.common.text.converters import to_native, to_text -from ansible.module_utils.yumdnf import YumDnf, yumdnf_argument_spec - -import errno -import os -import re -import sys -import tempfile - -try: - import rpm - HAS_RPM_PYTHON = True -except ImportError: - HAS_RPM_PYTHON = False - -try: - import yum - HAS_YUM_PYTHON = True -except ImportError: - HAS_YUM_PYTHON = False - -try: - from yum.misc import find_unfinished_transactions, find_ts_remaining - from rpmUtils.miscutils import splitFilename, compareEVR - transaction_helpers = True -except ImportError: - transaction_helpers = False - -from contextlib import contextmanager -from ansible.module_utils.urls import fetch_file - -def_qf = "%{epoch}:%{name}-%{version}-%{release}.%{arch}" -rpmbin = None - - -class YumModule(YumDnf): - """ - Yum Ansible module back-end implementation - """ - - def __init__(self, module): - - # state=installed name=pkgspec - # state=removed name=pkgspec - # state=latest name=pkgspec - # - # informational commands: - # list=installed - # list=updates - # list=available - # list=repos - # list=pkgspec - - # This populates instance vars for all argument spec params - super(YumModule, self).__init__(module) - - self.pkg_mgr_name = "yum" - self.lockfile = '/var/run/yum.pid' - self._yum_base = None - - def _enablerepos_with_error_checking(self): - # NOTE: This seems unintuitive, but it mirrors yum's CLI behavior - if len(self.enablerepo) == 1: - try: - self.yum_base.repos.enableRepo(self.enablerepo[0]) - except yum.Errors.YumBaseError as e: - if u'repository not found' in to_text(e): - self.module.fail_json(msg="Repository %s not found." % self.enablerepo[0]) - else: - raise e - else: - for rid in self.enablerepo: - try: - self.yum_base.repos.enableRepo(rid) - except yum.Errors.YumBaseError as e: - if u'repository not found' in to_text(e): - self.module.warn("Repository %s not found." % rid) - else: - raise e - - def is_lockfile_pid_valid(self): - try: - try: - with open(self.lockfile, 'r') as f: - oldpid = int(f.readline()) - except ValueError: - # invalid data - os.unlink(self.lockfile) - return False - - if oldpid == os.getpid(): - # that's us? - os.unlink(self.lockfile) - return False - - try: - with open("/proc/%d/stat" % oldpid, 'r') as f: - stat = f.readline() - - if stat.split()[2] == 'Z': - # Zombie - os.unlink(self.lockfile) - return False - except IOError: - # either /proc is not mounted or the process is already dead - try: - # check the state of the process - os.kill(oldpid, 0) - except OSError as e: - if e.errno == errno.ESRCH: - # No such process - os.unlink(self.lockfile) - return False - - self.module.fail_json(msg="Unable to check PID %s in %s: %s" % (oldpid, self.lockfile, to_native(e))) - except (IOError, OSError) as e: - # lockfile disappeared? - return False - - # another copy seems to be running - return True - - @property - def yum_base(self): - if self._yum_base: - return self._yum_base - else: - # Only init once - self._yum_base = yum.YumBase() - self._yum_base.preconf.debuglevel = 0 - self._yum_base.preconf.errorlevel = 0 - self._yum_base.preconf.plugins = True - self._yum_base.preconf.enabled_plugins = self.enable_plugin - self._yum_base.preconf.disabled_plugins = self.disable_plugin - if self.releasever: - self._yum_base.preconf.releasever = self.releasever - if self.installroot != '/': - # do not setup installroot by default, because of error - # CRITICAL:yum.cli:Config Error: Error accessing file for config file:////etc/yum.conf - # in old yum version (like in CentOS 6.6) - self._yum_base.preconf.root = self.installroot - self._yum_base.conf.installroot = self.installroot - if self.conf_file and os.path.exists(self.conf_file): - self._yum_base.preconf.fn = self.conf_file - if os.geteuid() != 0: - if hasattr(self._yum_base, 'setCacheDir'): - self._yum_base.setCacheDir() - else: - cachedir = yum.misc.getCacheDir() - self._yum_base.repos.setCacheDir(cachedir) - self._yum_base.conf.cache = 0 - if self.disable_excludes: - self._yum_base.conf.disable_excludes = self.disable_excludes - - # setting conf.sslverify allows retrieving the repo's metadata - # without validating the certificate, but that does not allow - # package installation from a bad-ssl repo. - self._yum_base.conf.sslverify = self.sslverify - - # A sideeffect of accessing conf is that the configuration is - # loaded and plugins are discovered - self.yum_base.conf # pylint: disable=pointless-statement - - try: - for rid in self.disablerepo: - self.yum_base.repos.disableRepo(rid) - - self._enablerepos_with_error_checking() - - except Exception as e: - self.module.fail_json(msg="Failure talking to yum: %s" % to_native(e)) - - return self._yum_base - - def po_to_envra(self, po): - if hasattr(po, 'ui_envra'): - return po.ui_envra - - return '%s:%s-%s-%s.%s' % (po.epoch, po.name, po.version, po.release, po.arch) - - def is_group_env_installed(self, name): - name_lower = name.lower() - - if yum.__version_info__ >= (3, 4): - groups_list = self.yum_base.doGroupLists(return_evgrps=True) - else: - groups_list = self.yum_base.doGroupLists() - - # list of the installed groups on the first index - groups = groups_list[0] - for group in groups: - if name_lower.endswith(group.name.lower()) or name_lower.endswith(group.groupid.lower()): - return True - - if yum.__version_info__ >= (3, 4): - # list of the installed env_groups on the third index - envs = groups_list[2] - for env in envs: - if name_lower.endswith(env.name.lower()) or name_lower.endswith(env.environmentid.lower()): - return True - - return False - - def is_installed(self, repoq, pkgspec, qf=None, is_pkg=False): - if qf is None: - qf = "%{epoch}:%{name}-%{version}-%{release}.%{arch}\n" - - if not repoq: - pkgs = [] - try: - e, m, dummy = self.yum_base.rpmdb.matchPackageNames([pkgspec]) - pkgs = e + m - if not pkgs and not is_pkg: - pkgs.extend(self.yum_base.returnInstalledPackagesByDep(pkgspec)) - except Exception as e: - self.module.fail_json(msg="Failure talking to yum: %s" % to_native(e)) - - return [self.po_to_envra(p) for p in pkgs] - - else: - global rpmbin - if not rpmbin: - rpmbin = self.module.get_bin_path('rpm', required=True) - - cmd = [rpmbin, '-q', '--qf', qf, pkgspec] - if '*' in pkgspec: - cmd.append('-a') - if self.installroot != '/': - cmd.extend(['--root', self.installroot]) - # rpm localizes messages and we're screen scraping so make sure we use - # an appropriate locale - locale = get_best_parsable_locale(self.module) - lang_env = dict(LANG=locale, LC_ALL=locale, LC_MESSAGES=locale) - rc, out, err = self.module.run_command(cmd, environ_update=lang_env) - if rc != 0 and 'is not installed' not in out: - self.module.fail_json(msg='Error from rpm: %s: %s' % (cmd, err)) - if 'is not installed' in out: - out = '' - - pkgs = [p for p in out.replace('(none)', '0').split('\n') if p.strip()] - if not pkgs and not is_pkg: - cmd = [rpmbin, '-q', '--qf', qf, '--whatprovides', pkgspec] - if self.installroot != '/': - cmd.extend(['--root', self.installroot]) - rc2, out2, err2 = self.module.run_command(cmd, environ_update=lang_env) - else: - rc2, out2, err2 = (0, '', '') - - if rc2 != 0 and 'no package provides' not in out2: - self.module.fail_json(msg='Error from rpm: %s: %s' % (cmd, err + err2)) - if 'no package provides' in out2: - out2 = '' - pkgs += [p for p in out2.replace('(none)', '0').split('\n') if p.strip()] - return pkgs - - return [] - - def is_available(self, repoq, pkgspec, qf=def_qf): - if not repoq: - - pkgs = [] - try: - e, m, dummy = self.yum_base.pkgSack.matchPackageNames([pkgspec]) - pkgs = e + m - if not pkgs: - pkgs.extend(self.yum_base.returnPackagesByDep(pkgspec)) - except Exception as e: - self.module.fail_json(msg="Failure talking to yum: %s" % to_native(e)) - - return [self.po_to_envra(p) for p in pkgs] - - else: - myrepoq = list(repoq) - - r_cmd = ['--disablerepo', ','.join(self.disablerepo)] - myrepoq.extend(r_cmd) - - r_cmd = ['--enablerepo', ','.join(self.enablerepo)] - myrepoq.extend(r_cmd) - - if self.releasever: - myrepoq.extend('--releasever=%s' % self.releasever) - - cmd = myrepoq + ["--qf", qf, pkgspec] - rc, out, err = self.module.run_command(cmd) - if rc == 0: - return [p for p in out.split('\n') if p.strip()] - else: - self.module.fail_json(msg='Error from repoquery: %s: %s' % (cmd, err)) - - return [] - - def is_update(self, repoq, pkgspec, qf=def_qf): - if not repoq: - - pkgs = [] - updates = [] - - try: - pkgs = self.yum_base.returnPackagesByDep(pkgspec) + \ - self.yum_base.returnInstalledPackagesByDep(pkgspec) - if not pkgs: - e, m, dummy = self.yum_base.pkgSack.matchPackageNames([pkgspec]) - pkgs = e + m - updates = self.yum_base.doPackageLists(pkgnarrow='updates').updates - except Exception as e: - self.module.fail_json(msg="Failure talking to yum: %s" % to_native(e)) - - retpkgs = (pkg for pkg in pkgs if pkg in updates) - - return set(self.po_to_envra(p) for p in retpkgs) - - else: - myrepoq = list(repoq) - r_cmd = ['--disablerepo', ','.join(self.disablerepo)] - myrepoq.extend(r_cmd) - - r_cmd = ['--enablerepo', ','.join(self.enablerepo)] - myrepoq.extend(r_cmd) - - if self.releasever: - myrepoq.extend('--releasever=%s' % self.releasever) - - cmd = myrepoq + ["--pkgnarrow=updates", "--qf", qf, pkgspec] - rc, out, err = self.module.run_command(cmd) - - if rc == 0: - return set(p for p in out.split('\n') if p.strip()) - else: - self.module.fail_json(msg='Error from repoquery: %s: %s' % (cmd, err)) - - return set() - - def what_provides(self, repoq, req_spec, qf=def_qf): - if not repoq: - - pkgs = [] - try: - try: - pkgs = self.yum_base.returnPackagesByDep(req_spec) + \ - self.yum_base.returnInstalledPackagesByDep(req_spec) - except Exception as e: - # If a repo with `repo_gpgcheck=1` is added and the repo GPG - # key was never accepted, querying this repo will throw an - # error: 'repomd.xml signature could not be verified'. In that - # situation we need to run `yum -y makecache fast` which will accept - # the key and try again. - if 'repomd.xml signature could not be verified' in to_native(e): - if self.releasever: - self.module.run_command(self.yum_basecmd + ['makecache', 'fast', '--releasever=%s' % self.releasever]) - else: - self.module.run_command(self.yum_basecmd + ['makecache', 'fast']) - pkgs = self.yum_base.returnPackagesByDep(req_spec) + \ - self.yum_base.returnInstalledPackagesByDep(req_spec) - else: - raise - if not pkgs: - exact_matches, glob_matches = self.yum_base.pkgSack.matchPackageNames([req_spec])[0:2] - pkgs.extend(exact_matches) - pkgs.extend(glob_matches) - exact_matches, glob_matches = self.yum_base.rpmdb.matchPackageNames([req_spec])[0:2] - pkgs.extend(exact_matches) - pkgs.extend(glob_matches) - except Exception as e: - self.module.fail_json(msg="Failure talking to yum: %s" % to_native(e)) - - return set(self.po_to_envra(p) for p in pkgs) - - else: - myrepoq = list(repoq) - r_cmd = ['--disablerepo', ','.join(self.disablerepo)] - myrepoq.extend(r_cmd) - - r_cmd = ['--enablerepo', ','.join(self.enablerepo)] - myrepoq.extend(r_cmd) - - if self.releasever: - myrepoq.extend('--releasever=%s' % self.releasever) - - cmd = myrepoq + ["--qf", qf, "--whatprovides", req_spec] - rc, out, err = self.module.run_command(cmd) - cmd = myrepoq + ["--qf", qf, req_spec] - rc2, out2, err2 = self.module.run_command(cmd) - if rc == 0 and rc2 == 0: - out += out2 - pkgs = {p for p in out.split('\n') if p.strip()} - if not pkgs: - pkgs = self.is_installed(repoq, req_spec, qf=qf) - return pkgs - else: - self.module.fail_json(msg='Error from repoquery: %s: %s' % (cmd, err + err2)) - - return set() - - def transaction_exists(self, pkglist): - """ - checks the package list to see if any packages are - involved in an incomplete transaction - """ - - conflicts = [] - if not transaction_helpers: - return conflicts - - # first, we create a list of the package 'nvreas' - # so we can compare the pieces later more easily - pkglist_nvreas = (splitFilename(pkg) for pkg in pkglist) - - # next, we build the list of packages that are - # contained within an unfinished transaction - unfinished_transactions = find_unfinished_transactions() - for trans in unfinished_transactions: - steps = find_ts_remaining(trans) - for step in steps: - # the action is install/erase/etc., but we only - # care about the package spec contained in the step - (action, step_spec) = step - (n, v, r, e, a) = splitFilename(step_spec) - # and see if that spec is in the list of packages - # requested for installation/updating - for pkg in pkglist_nvreas: - # if the name and arch match, we're going to assume - # this package is part of a pending transaction - # the label is just for display purposes - label = "%s-%s" % (n, a) - if n == pkg[0] and a == pkg[4]: - if label not in conflicts: - conflicts.append("%s-%s" % (n, a)) - break - return conflicts - - def local_envra(self, path): - """return envra of a local rpm passed in""" - - ts = rpm.TransactionSet() - ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES) - fd = os.open(path, os.O_RDONLY) - try: - header = ts.hdrFromFdno(fd) - except rpm.error as e: - return None - finally: - os.close(fd) - - return '%s:%s-%s-%s.%s' % ( - header[rpm.RPMTAG_EPOCH] or '0', - header[rpm.RPMTAG_NAME], - header[rpm.RPMTAG_VERSION], - header[rpm.RPMTAG_RELEASE], - header[rpm.RPMTAG_ARCH] - ) - - @contextmanager - def set_env_proxy(self): - # setting system proxy environment and saving old, if exists - namepass = "" - scheme = ["http", "https"] - old_proxy_env = [os.getenv("http_proxy"), os.getenv("https_proxy")] - try: - # "_none_" is a special value to disable proxy in yum.conf/*.repo - if self.yum_base.conf.proxy and self.yum_base.conf.proxy not in ("_none_",): - if self.yum_base.conf.proxy_username: - namepass = namepass + self.yum_base.conf.proxy_username - proxy_url = self.yum_base.conf.proxy - if self.yum_base.conf.proxy_password: - namepass = namepass + ":" + self.yum_base.conf.proxy_password - elif '@' in self.yum_base.conf.proxy: - namepass = self.yum_base.conf.proxy.split('@')[0].split('//')[-1] - proxy_url = self.yum_base.conf.proxy.replace("{0}@".format(namepass), "") - - if namepass: - namepass = namepass + '@' - for item in scheme: - os.environ[item + "_proxy"] = re.sub( - r"(http://)", - r"\g<1>" + namepass, proxy_url - ) - else: - for item in scheme: - os.environ[item + "_proxy"] = self.yum_base.conf.proxy - yield - except yum.Errors.YumBaseError: - raise - finally: - # revert back to previously system configuration - for item in scheme: - if os.getenv("{0}_proxy".format(item)): - del os.environ["{0}_proxy".format(item)] - if old_proxy_env[0]: - os.environ["http_proxy"] = old_proxy_env[0] - if old_proxy_env[1]: - os.environ["https_proxy"] = old_proxy_env[1] - - def pkg_to_dict(self, pkgstr): - if pkgstr.strip() and pkgstr.count('|') == 5: - n, e, v, r, a, repo = pkgstr.split('|') - else: - return {'error_parsing': pkgstr} - - d = { - 'name': n, - 'arch': a, - 'epoch': e, - 'release': r, - 'version': v, - 'repo': repo, - 'envra': '%s:%s-%s-%s.%s' % (e, n, v, r, a) - } - - if repo == 'installed': - d['yumstate'] = 'installed' - else: - d['yumstate'] = 'available' - - return d - - def repolist(self, repoq, qf="%{repoid}"): - cmd = repoq + ["--qf", qf, "-a"] - if self.releasever: - cmd.extend(['--releasever=%s' % self.releasever]) - rc, out, err = self.module.run_command(cmd) - if rc == 0: - return set(p for p in out.split('\n') if p.strip()) - else: - return [] - - def list_stuff(self, repoquerybin, stuff): - - qf = "%{name}|%{epoch}|%{version}|%{release}|%{arch}|%{repoid}" - # is_installed goes through rpm instead of repoquery so it needs a slightly different format - is_installed_qf = "%{name}|%{epoch}|%{version}|%{release}|%{arch}|installed\n" - repoq = [repoquerybin, '--show-duplicates', '--plugins', '--quiet'] - if self.disablerepo: - repoq.extend(['--disablerepo', ','.join(self.disablerepo)]) - if self.enablerepo: - repoq.extend(['--enablerepo', ','.join(self.enablerepo)]) - if self.installroot != '/': - repoq.extend(['--installroot', self.installroot]) - if self.conf_file and os.path.exists(self.conf_file): - repoq += ['-c', self.conf_file] - - if stuff == 'installed': - return [self.pkg_to_dict(p) for p in sorted(self.is_installed(repoq, '-a', qf=is_installed_qf)) if p.strip()] - - if stuff == 'updates': - return [self.pkg_to_dict(p) for p in sorted(self.is_update(repoq, '-a', qf=qf)) if p.strip()] - - if stuff == 'available': - return [self.pkg_to_dict(p) for p in sorted(self.is_available(repoq, '-a', qf=qf)) if p.strip()] - - if stuff == 'repos': - return [dict(repoid=name, state='enabled') for name in sorted(self.repolist(repoq)) if name.strip()] - - return [ - self.pkg_to_dict(p) for p in - sorted(self.is_installed(repoq, stuff, qf=is_installed_qf) + self.is_available(repoq, stuff, qf=qf)) - if p.strip() - ] - - def exec_install(self, items, action, pkgs, res): - cmd = self.yum_basecmd + [action] + pkgs - if self.releasever: - cmd.extend(['--releasever=%s' % self.releasever]) - - # setting sslverify using --setopt is required as conf.sslverify only - # affects the metadata retrieval. - if not self.sslverify: - cmd.extend(['--setopt', 'sslverify=0']) - - if self.module.check_mode: - self.module.exit_json(changed=True, results=res['results'], changes=dict(installed=pkgs)) - else: - res['changes'] = dict(installed=pkgs) - - locale = get_best_parsable_locale(self.module) - lang_env = dict(LANG=locale, LC_ALL=locale, LC_MESSAGES=locale) - rc, out, err = self.module.run_command(cmd, environ_update=lang_env) - - if rc == 1: - for spec in items: - # Fail on invalid urls: - if ('://' in spec and ('No package %s available.' % spec in out or 'Cannot open: %s. Skipping.' % spec in err)): - err = 'Package at %s could not be installed' % spec - self.module.fail_json(changed=False, msg=err, rc=rc) - - res['rc'] = rc - res['results'].append(out) - res['msg'] += err - res['changed'] = True - - if ('Nothing to do' in out and rc == 0) or ('does not have any packages' in err): - res['changed'] = False - - if rc != 0: - res['changed'] = False - self.module.fail_json(**res) - - # Fail if yum prints 'No space left on device' because that means some - # packages failed executing their post install scripts because of lack of - # free space (e.g. kernel package couldn't generate initramfs). Note that - # yum can still exit with rc=0 even if some post scripts didn't execute - # correctly. - if 'No space left on device' in (out or err): - res['changed'] = False - res['msg'] = 'No space left on device' - self.module.fail_json(**res) - - # FIXME - if we did an install - go and check the rpmdb to see if it actually installed - # look for each pkg in rpmdb - # look for each pkg via obsoletes - - return res - - def install(self, items, repoq): - - pkgs = [] - downgrade_pkgs = [] - res = {} - res['results'] = [] - res['msg'] = '' - res['rc'] = 0 - res['changed'] = False - - for spec in items: - pkg = None - downgrade_candidate = False - - # check if pkgspec is installed (if possible for idempotence) - if spec.endswith('.rpm') or '://' in spec: - if '://' not in spec and not os.path.exists(spec): - res['msg'] += "No RPM file matching '%s' found on system" % spec - res['results'].append("No RPM file matching '%s' found on system" % spec) - res['rc'] = 127 # Ensure the task fails in with-loop - self.module.fail_json(**res) - - if '://' in spec: - with self.set_env_proxy(): - package = fetch_file(self.module, spec) - if not package.endswith('.rpm'): - # yum requires a local file to have the extension of .rpm and we - # can not guarantee that from an URL (redirects, proxies, etc) - new_package_path = '%s.rpm' % package - os.rename(package, new_package_path) - package = new_package_path - else: - package = spec - - # most common case is the pkg is already installed - envra = self.local_envra(package) - if envra is None: - self.module.fail_json(msg="Failed to get envra information from RPM package: %s" % spec) - installed_pkgs = self.is_installed(repoq, envra) - if installed_pkgs: - res['results'].append('%s providing %s is already installed' % (installed_pkgs[0], package)) - continue - - (name, ver, rel, epoch, arch) = splitFilename(envra) - installed_pkgs = self.is_installed(repoq, name) - - # case for two same envr but different archs like x86_64 and i686 - if len(installed_pkgs) == 2: - (cur_name0, cur_ver0, cur_rel0, cur_epoch0, cur_arch0) = splitFilename(installed_pkgs[0]) - (cur_name1, cur_ver1, cur_rel1, cur_epoch1, cur_arch1) = splitFilename(installed_pkgs[1]) - cur_epoch0 = cur_epoch0 or '0' - cur_epoch1 = cur_epoch1 or '0' - compare = compareEVR((cur_epoch0, cur_ver0, cur_rel0), (cur_epoch1, cur_ver1, cur_rel1)) - if compare == 0 and cur_arch0 != cur_arch1: - for installed_pkg in installed_pkgs: - if installed_pkg.endswith(arch): - installed_pkgs = [installed_pkg] - - if len(installed_pkgs) == 1: - installed_pkg = installed_pkgs[0] - (cur_name, cur_ver, cur_rel, cur_epoch, cur_arch) = splitFilename(installed_pkg) - cur_epoch = cur_epoch or '0' - compare = compareEVR((cur_epoch, cur_ver, cur_rel), (epoch, ver, rel)) - - # compare > 0 -> higher version is installed - # compare == 0 -> exact version is installed - # compare < 0 -> lower version is installed - if compare > 0 and self.allow_downgrade: - downgrade_candidate = True - elif compare >= 0: - continue - - # else: if there are more installed packages with the same name, that would mean - # kernel, gpg-pubkey or like, so just let yum deal with it and try to install it - - pkg = package - - # groups - elif spec.startswith('@'): - if self.is_group_env_installed(spec): - continue - - pkg = spec - - # range requires or file-requires or pkgname :( - else: - # most common case is the pkg is already installed and done - # short circuit all the bs - and search for it as a pkg in is_installed - # if you find it then we're done - if not set(['*', '?']).intersection(set(spec)): - installed_pkgs = self.is_installed(repoq, spec, is_pkg=True) - if installed_pkgs: - res['results'].append('%s providing %s is already installed' % (installed_pkgs[0], spec)) - continue - - # look up what pkgs provide this - pkglist = self.what_provides(repoq, spec) - if not pkglist: - res['msg'] += "No package matching '%s' found available, installed or updated" % spec - res['results'].append("No package matching '%s' found available, installed or updated" % spec) - res['rc'] = 126 # Ensure the task fails in with-loop - self.module.fail_json(**res) - - # if any of the packages are involved in a transaction, fail now - # so that we don't hang on the yum operation later - conflicts = self.transaction_exists(pkglist) - if conflicts: - res['msg'] += "The following packages have pending transactions: %s" % ", ".join(conflicts) - res['rc'] = 125 # Ensure the task fails in with-loop - self.module.fail_json(**res) - - # if any of them are installed - # then nothing to do - - found = False - for this in pkglist: - if self.is_installed(repoq, this, is_pkg=True): - found = True - res['results'].append('%s providing %s is already installed' % (this, spec)) - break - - # if the version of the pkg you have installed is not in ANY repo, but there are - # other versions in the repos (both higher and lower) then the previous checks won't work. - # so we check one more time. This really only works for pkgname - not for file provides or virt provides - # but virt provides should be all caught in what_provides on its own. - # highly irritating - if not found: - if self.is_installed(repoq, spec): - found = True - res['results'].append('package providing %s is already installed' % (spec)) - - if found: - continue - - # Downgrade - The yum install command will only install or upgrade to a spec version, it will - # not install an older version of an RPM even if specified by the install spec. So we need to - # determine if this is a downgrade, and then use the yum downgrade command to install the RPM. - if self.allow_downgrade: - for package in pkglist: - # Get the NEVRA of the requested package using pkglist instead of spec because pkglist - # contains consistently-formatted package names returned by yum, rather than user input - # that is often not parsed correctly by splitFilename(). - (name, ver, rel, epoch, arch) = splitFilename(package) - - # Check if any version of the requested package is installed - inst_pkgs = self.is_installed(repoq, name, is_pkg=True) - if inst_pkgs: - (cur_name, cur_ver, cur_rel, cur_epoch, cur_arch) = splitFilename(inst_pkgs[0]) - compare = compareEVR((cur_epoch, cur_ver, cur_rel), (epoch, ver, rel)) - if compare > 0: - downgrade_candidate = True - else: - downgrade_candidate = False - break - - # If package needs to be installed/upgraded/downgraded, then pass in the spec - # we could get here if nothing provides it but that's not - # the error we're catching here - pkg = spec - - if downgrade_candidate and self.allow_downgrade: - downgrade_pkgs.append(pkg) - else: - pkgs.append(pkg) - - if downgrade_pkgs: - res = self.exec_install(items, 'downgrade', downgrade_pkgs, res) - - if pkgs: - res = self.exec_install(items, 'install', pkgs, res) - - return res - - def remove(self, items, repoq): - - pkgs = [] - res = {} - res['results'] = [] - res['msg'] = '' - res['changed'] = False - res['rc'] = 0 - - for pkg in items: - if pkg.startswith('@'): - installed = self.is_group_env_installed(pkg) - else: - installed = self.is_installed(repoq, pkg) - - if installed: - pkgs.append(pkg) - else: - res['results'].append('%s is not installed' % pkg) - - if pkgs: - if self.module.check_mode: - self.module.exit_json(changed=True, results=res['results'], changes=dict(removed=pkgs)) - else: - res['changes'] = dict(removed=pkgs) - - # run an actual yum transaction - if self.autoremove: - cmd = self.yum_basecmd + ["autoremove"] + pkgs - else: - cmd = self.yum_basecmd + ["remove"] + pkgs - rc, out, err = self.module.run_command(cmd) - - res['rc'] = rc - res['results'].append(out) - res['msg'] = err - - if rc != 0: - if self.autoremove and 'No such command' in out: - self.module.fail_json(msg='Version of YUM too old for autoremove: Requires yum 3.4.3 (RHEL/CentOS 7+)') - else: - self.module.fail_json(**res) - - # compile the results into one batch. If anything is changed - # then mark changed - # at the end - if we've end up failed then fail out of the rest - # of the process - - # at this point we check to see if the pkg is no longer present - self._yum_base = None # previous YumBase package index is now invalid - for pkg in pkgs: - if pkg.startswith('@'): - installed = self.is_group_env_installed(pkg) - else: - installed = self.is_installed(repoq, pkg, is_pkg=True) - - if installed: - # Return a message so it's obvious to the user why yum failed - # and which package couldn't be removed. More details: - # https://github.com/ansible/ansible/issues/35672 - res['msg'] = "Package '%s' couldn't be removed!" % pkg - self.module.fail_json(**res) - - res['changed'] = True - - return res - - def run_check_update(self): - # run check-update to see if we have packages pending - if self.releasever: - rc, out, err = self.module.run_command(self.yum_basecmd + ['check-update'] + ['--releasever=%s' % self.releasever]) - else: - rc, out, err = self.module.run_command(self.yum_basecmd + ['check-update']) - return rc, out, err - - @staticmethod - def parse_check_update(check_update_output): - # preprocess string and filter out empty lines so the regex below works - out = '\n'.join((l for l in check_update_output.splitlines() if l)) - - # Remove incorrect new lines in longer columns in output from yum check-update - # yum line wrapping can move the repo to the next line: - # some_looooooooooooooooooooooooooooooooooooong_package_name 1:1.2.3-1.el7 - # some-repo-label - out = re.sub(r'\n\W+(.*)', r' \1', out) - - updates = {} - obsoletes = {} - for line in out.split('\n'): - line = line.split() - # Ignore irrelevant lines: - # - '*' in line matches lines like mirror lists: "* base: mirror.corbina.net" - # - len(line) != 3 or 6 could be strings like: - # "This system is not registered with an entitlement server..." - # - len(line) = 6 is package obsoletes - # - checking for '.' in line[0] (package name) likely ensures that it is of format: - # "package_name.arch" (coreutils.x86_64) - if '*' in line or len(line) not in [3, 6] or '.' not in line[0]: - continue - - pkg, version, repo = line[0], line[1], line[2] - name, dist = pkg.rsplit('.', 1) - - if name not in updates: - updates[name] = [] - - updates[name].append({'version': version, 'dist': dist, 'repo': repo}) - - if len(line) == 6: - obsolete_pkg, obsolete_version, obsolete_repo = line[3], line[4], line[5] - obsolete_name, obsolete_dist = obsolete_pkg.rsplit('.', 1) - - if obsolete_name not in obsoletes: - obsoletes[obsolete_name] = [] - - obsoletes[obsolete_name].append({'version': obsolete_version, 'dist': obsolete_dist, 'repo': obsolete_repo}) - - return updates, obsoletes - - def latest(self, items, repoq): - - res = {} - res['results'] = [] - res['msg'] = '' - res['changed'] = False - res['rc'] = 0 - pkgs = {} - pkgs['update'] = [] - pkgs['install'] = [] - updates = {} - obsoletes = {} - update_all = False - cmd = self.yum_basecmd[:] - - # determine if we're doing an update all - if '*' in items: - update_all = True - - rc, out, err = self.run_check_update() - - if rc == 0 and update_all: - res['results'].append('Nothing to do here, all packages are up to date') - return res - elif rc == 100: - updates, obsoletes = self.parse_check_update(out) - elif rc == 1: - res['msg'] = err - res['rc'] = rc - self.module.fail_json(**res) - - if update_all: - cmd.append('update') - will_update = set(updates.keys()) - will_update_from_other_package = dict() - else: - will_update = set() - will_update_from_other_package = dict() - for spec in items: - # some guess work involved with groups. update @ will install the group if missing - if spec.startswith('@'): - pkgs['update'].append(spec) - will_update.add(spec) - continue - - # check if pkgspec is installed (if possible for idempotence) - # localpkg - if spec.endswith('.rpm') and '://' not in spec: - if not os.path.exists(spec): - res['msg'] += "No RPM file matching '%s' found on system" % spec - res['results'].append("No RPM file matching '%s' found on system" % spec) - res['rc'] = 127 # Ensure the task fails in with-loop - self.module.fail_json(**res) - - # get the pkg e:name-v-r.arch - envra = self.local_envra(spec) - - if envra is None: - self.module.fail_json(msg="Failed to get envra information from RPM package: %s" % spec) - - # local rpm files can't be updated - if self.is_installed(repoq, envra): - pkgs['update'].append(spec) - else: - pkgs['install'].append(spec) - continue - - # URL - if '://' in spec: - # download package so that we can check if it's already installed - with self.set_env_proxy(): - package = fetch_file(self.module, spec) - envra = self.local_envra(package) - - if envra is None: - self.module.fail_json(msg="Failed to get envra information from RPM package: %s" % spec) - - # local rpm files can't be updated - if self.is_installed(repoq, envra): - pkgs['update'].append(spec) - else: - pkgs['install'].append(spec) - continue - - # dep/pkgname - find it - if self.is_installed(repoq, spec): - pkgs['update'].append(spec) - else: - pkgs['install'].append(spec) - pkglist = self.what_provides(repoq, spec) - # FIXME..? may not be desirable to throw an exception here if a single package is missing - if not pkglist: - res['msg'] += "No package matching '%s' found available, installed or updated" % spec - res['results'].append("No package matching '%s' found available, installed or updated" % spec) - res['rc'] = 126 # Ensure the task fails in with-loop - self.module.fail_json(**res) - - nothing_to_do = True - for pkg in pkglist: - if spec in pkgs['install'] and self.is_available(repoq, pkg): - nothing_to_do = False - break - - # this contains the full NVR and spec could contain wildcards - # or virtual provides (like "python-*" or "smtp-daemon") while - # updates contains name only. - (pkgname, ver, rel, epoch, arch) = splitFilename(pkg) - if spec in pkgs['update'] and pkgname in updates: - nothing_to_do = False - will_update.add(spec) - # Massage the updates list - if spec != pkgname: - # For reporting what packages would be updated more - # succinctly - will_update_from_other_package[spec] = pkgname - break - - if not self.is_installed(repoq, spec) and self.update_only: - res['results'].append("Packages providing %s not installed due to update_only specified" % spec) - continue - if nothing_to_do: - res['results'].append("All packages providing %s are up to date" % spec) - continue - - # if any of the packages are involved in a transaction, fail now - # so that we don't hang on the yum operation later - conflicts = self.transaction_exists(pkglist) - if conflicts: - res['msg'] += "The following packages have pending transactions: %s" % ", ".join(conflicts) - res['results'].append("The following packages have pending transactions: %s" % ", ".join(conflicts)) - res['rc'] = 128 # Ensure the task fails in with-loop - self.module.fail_json(**res) - - # check_mode output - to_update = [] - for w in will_update: - if w.startswith('@'): - # yum groups - to_update.append((w, None)) - elif w not in updates: - # There are (at least, probably more) 2 ways we can get here: - # - # * A virtual provides (our user specifies "webserver", but - # "httpd" is the key in 'updates'). - # - # * A wildcard. emac* will get us here if there's a package - # called 'emacs' in the pending updates list. 'updates' will - # of course key on 'emacs' in that case. - - other_pkg = will_update_from_other_package[w] - - # We are guaranteed that: other_pkg in updates - # ...based on the logic above. But we only want to show one - # update in this case (given the wording of "at least") below. - # As an example, consider a package installed twice: - # foobar.x86_64, foobar.i686 - # We want to avoid having both: - # ('foo*', 'because of (at least) foobar-1.x86_64 from repo') - # ('foo*', 'because of (at least) foobar-1.i686 from repo') - # We just pick the first one. - # - # TODO: This is something that might be nice to change, but it - # would be a module UI change. But without it, we're - # dropping potentially important information about what - # was updated. Instead of (given_spec, random_matching_package) - # it'd be nice if we appended (given_spec, [all_matching_packages]) - # - # ... But then, we also drop information if multiple - # different (distinct) packages match the given spec and - # we should probably fix that too. - pkg = updates[other_pkg][0] - to_update.append( - ( - w, - 'because of (at least) %s-%s.%s from %s' % ( - other_pkg, - pkg['version'], - pkg['dist'], - pkg['repo'] - ) - ) - ) - else: - # Otherwise the spec is an exact match - for pkg in updates[w]: - to_update.append( - ( - w, - '%s.%s from %s' % ( - pkg['version'], - pkg['dist'], - pkg['repo'] - ) - ) - ) - - if self.update_only: - res['changes'] = dict(installed=[], updated=to_update) - else: - res['changes'] = dict(installed=pkgs['install'], updated=to_update) - - if obsoletes: - res['obsoletes'] = obsoletes - - # return results before we actually execute stuff - if self.module.check_mode: - if will_update or pkgs['install']: - res['changed'] = True - return res - - if self.releasever: - cmd.extend(['--releasever=%s' % self.releasever]) - - # run commands - if update_all: - rc, out, err = self.module.run_command(cmd) - res['changed'] = True - elif self.update_only: - if pkgs['update']: - cmd += ['update'] + pkgs['update'] - locale = get_best_parsable_locale(self.module) - lang_env = dict(LANG=locale, LC_ALL=locale, LC_MESSAGES=locale) - rc, out, err = self.module.run_command(cmd, environ_update=lang_env) - out_lower = out.strip().lower() - if not out_lower.endswith("no packages marked for update") and \ - not out_lower.endswith("nothing to do"): - res['changed'] = True - else: - rc, out, err = [0, '', ''] - elif pkgs['install'] or will_update and not self.update_only: - cmd += ['install'] + pkgs['install'] + pkgs['update'] - locale = get_best_parsable_locale(self.module) - lang_env = dict(LANG=locale, LC_ALL=locale, LC_MESSAGES=locale) - rc, out, err = self.module.run_command(cmd, environ_update=lang_env) - out_lower = out.strip().lower() - if not out_lower.endswith("no packages marked for update") and \ - not out_lower.endswith("nothing to do"): - res['changed'] = True - else: - rc, out, err = [0, '', ''] - - res['rc'] = rc - res['msg'] += err - res['results'].append(out) - - if rc: - res['failed'] = True - - return res - - def ensure(self, repoq): - pkgs = self.names - - # autoremove was provided without `name` - if not self.names and self.autoremove: - pkgs = [] - self.state = 'absent' - - if self.conf_file and os.path.exists(self.conf_file): - self.yum_basecmd += ['-c', self.conf_file] - - if repoq: - repoq += ['-c', self.conf_file] - - if self.skip_broken: - self.yum_basecmd.extend(['--skip-broken']) - - if self.disablerepo: - self.yum_basecmd.extend(['--disablerepo=%s' % ','.join(self.disablerepo)]) - - if self.enablerepo: - self.yum_basecmd.extend(['--enablerepo=%s' % ','.join(self.enablerepo)]) - - if self.enable_plugin: - self.yum_basecmd.extend(['--enableplugin', ','.join(self.enable_plugin)]) - - if self.disable_plugin: - self.yum_basecmd.extend(['--disableplugin', ','.join(self.disable_plugin)]) - - if self.exclude: - e_cmd = ['--exclude=%s' % ','.join(self.exclude)] - self.yum_basecmd.extend(e_cmd) - - if self.disable_excludes: - self.yum_basecmd.extend(['--disableexcludes=%s' % self.disable_excludes]) - - if self.cacheonly: - self.yum_basecmd.extend(['--cacheonly']) - - if self.download_only: - self.yum_basecmd.extend(['--downloadonly']) - - if self.download_dir: - self.yum_basecmd.extend(['--downloaddir=%s' % self.download_dir]) - - if self.releasever: - self.yum_basecmd.extend(['--releasever=%s' % self.releasever]) - - if self.installroot != '/': - # do not setup installroot by default, because of error - # CRITICAL:yum.cli:Config Error: Error accessing file for config file:////etc/yum.conf - # in old yum version (like in CentOS 6.6) - e_cmd = ['--installroot=%s' % self.installroot] - self.yum_basecmd.extend(e_cmd) - - if self.state in ('installed', 'present', 'latest'): - # The need of this entire if conditional has to be changed - # this function is the ensure function that is called - # in the main section. - # - # This conditional tends to disable/enable repo for - # install present latest action, same actually - # can be done for remove and absent action - # - # As solution I would advice to cal - # try: self.yum_base.repos.disableRepo(disablerepo) - # and - # try: self.yum_base.repos.enableRepo(enablerepo) - # right before any yum_cmd is actually called regardless - # of yum action. - # - # Please note that enable/disablerepo options are general - # options, this means that we can call those with any action - # option. https://linux.die.net/man/8/yum - # - # This docstring will be removed together when issue: #21619 - # will be solved. - # - # This has been triggered by: #19587 - - if self.update_cache: - self.module.run_command(self.yum_basecmd + ['clean', 'expire-cache']) - - try: - current_repos = self.yum_base.repos.repos.keys() - if self.enablerepo: - try: - new_repos = self.yum_base.repos.repos.keys() - for i in new_repos: - if i not in current_repos: - rid = self.yum_base.repos.getRepo(i) - a = rid.repoXML.repoid # nopep8 - https://github.com/ansible/ansible/pull/21475#pullrequestreview-22404868 - current_repos = new_repos - except yum.Errors.YumBaseError as e: - self.module.fail_json(msg="Error setting/accessing repos: %s" % to_native(e)) - except yum.Errors.YumBaseError as e: - self.module.fail_json(msg="Error accessing repos: %s" % to_native(e)) - if self.state == 'latest' or self.update_only: - if self.disable_gpg_check: - self.yum_basecmd.append('--nogpgcheck') - if self.security: - self.yum_basecmd.append('--security') - if self.bugfix: - self.yum_basecmd.append('--bugfix') - res = self.latest(pkgs, repoq) - elif self.state in ('installed', 'present'): - if self.disable_gpg_check: - self.yum_basecmd.append('--nogpgcheck') - res = self.install(pkgs, repoq) - elif self.state in ('removed', 'absent'): - res = self.remove(pkgs, repoq) - else: - # should be caught by AnsibleModule argument_spec - self.module.fail_json( - msg="we should never get here unless this all failed", - changed=False, - results='', - errors='unexpected state' - ) - return res - - @staticmethod - def has_yum(): - return HAS_YUM_PYTHON - - def run(self): - """ - actually execute the module code backend - """ - - if (not HAS_RPM_PYTHON or not HAS_YUM_PYTHON) and sys.executable != '/usr/bin/python' and not has_respawned(): - respawn_module('/usr/bin/python') - # end of the line for this process; we'll exit here once the respawned module has completed - - error_msgs = [] - if not HAS_RPM_PYTHON: - error_msgs.append('The Python 2 bindings for rpm are needed for this module. If you require Python 3 support use the `dnf` Ansible module instead.') - if not HAS_YUM_PYTHON: - error_msgs.append('The Python 2 yum module is needed for this module. If you require Python 3 support use the `dnf` Ansible module instead.') - - self.wait_for_lock() - - if error_msgs: - self.module.fail_json(msg='. '.join(error_msgs)) - - # fedora will redirect yum to dnf, which has incompatibilities - # with how this module expects yum to operate. If yum-deprecated - # is available, use that instead to emulate the old behaviors. - if self.module.get_bin_path('yum-deprecated'): - yumbin = self.module.get_bin_path('yum-deprecated') - else: - yumbin = self.module.get_bin_path('yum') - - # need debug level 2 to get 'Nothing to do' for groupinstall. - self.yum_basecmd = [yumbin, '-d', '2', '-y'] - - if self.update_cache and not self.names and not self.list: - rc, stdout, stderr = self.module.run_command(self.yum_basecmd + ['clean', 'expire-cache']) - if rc == 0: - self.module.exit_json( - changed=False, - msg="Cache updated", - rc=rc, - results=[] - ) - else: - self.module.exit_json( - changed=False, - msg="Failed to update cache", - rc=rc, - results=[stderr], - ) - - repoquerybin = self.module.get_bin_path('repoquery', required=False) - - if self.install_repoquery and not repoquerybin and not self.module.check_mode: - yum_path = self.module.get_bin_path('yum') - if yum_path: - if self.releasever: - self.module.run_command('%s -y install yum-utils --releasever %s' % (yum_path, self.releasever)) - else: - self.module.run_command('%s -y install yum-utils' % yum_path) - repoquerybin = self.module.get_bin_path('repoquery', required=False) - - if self.list: - if not repoquerybin: - self.module.fail_json(msg="repoquery is required to use list= with this module. Please install the yum-utils package.") - results = {'results': self.list_stuff(repoquerybin, self.list)} - else: - # If rhn-plugin is installed and no rhn-certificate is available on - # the system then users will see an error message using the yum API. - # Use repoquery in those cases. - - repoquery = None - try: - yum_plugins = self.yum_base.plugins._plugins - except AttributeError: - pass - else: - if 'rhnplugin' in yum_plugins: - if repoquerybin: - repoquery = [repoquerybin, '--show-duplicates', '--plugins', '--quiet'] - if self.installroot != '/': - repoquery.extend(['--installroot', self.installroot]) - - if self.disable_excludes: - # repoquery does not support --disableexcludes, - # so make a temp copy of yum.conf and get rid of the 'exclude=' line there - try: - with open('/etc/yum.conf', 'r') as f: - content = f.readlines() - - tmp_conf_file = tempfile.NamedTemporaryFile(dir=self.module.tmpdir, delete=False) - self.module.add_cleanup_file(tmp_conf_file.name) - - tmp_conf_file.writelines([c for c in content if not c.startswith("exclude=")]) - tmp_conf_file.close() - except Exception as e: - self.module.fail_json(msg="Failure setting up repoquery: %s" % to_native(e)) - - repoquery.extend(['-c', tmp_conf_file.name]) - - results = self.ensure(repoquery) - if repoquery: - results['msg'] = '%s %s' % ( - results.get('msg', ''), - 'Warning: Due to potential bad behaviour with rhnplugin and certificates, used slower repoquery calls instead of Yum API.' - ) - - self.module.exit_json(**results) - - -def main(): - # state=installed name=pkgspec - # state=removed name=pkgspec - # state=latest name=pkgspec - # - # informational commands: - # list=installed - # list=updates - # list=available - # list=repos - # list=pkgspec - - yumdnf_argument_spec['argument_spec']['use_backend'] = dict(default='auto', choices=['auto', 'yum', 'yum4', 'dnf', 'dnf4', 'dnf5']) - - module = AnsibleModule( - **yumdnf_argument_spec - ) - - module_implementation = YumModule(module) - module_implementation.run() - - -if __name__ == '__main__': - main() diff --git a/lib/ansible/plugins/action/__init__.py b/lib/ansible/plugins/action/__init__.py index 5979337f175..5517d10a638 100644 --- a/lib/ansible/plugins/action/__init__.py +++ b/lib/ansible/plugins/action/__init__.py @@ -1165,7 +1165,7 @@ class ActionBase(ABC): if data.pop("_ansible_suppress_tmpdir_delete", False): self._cleanup_remote_tmp = False - # NOTE: yum returns results .. but that made it 'compatible' with squashing, so we allow mappings, for now + # NOTE: dnf returns results .. but that made it 'compatible' with squashing, so we allow mappings, for now if 'results' in data and (not isinstance(data['results'], Sequence) or isinstance(data['results'], string_types)): data['ansible_module_results'] = data['results'] del data['results'] diff --git a/lib/ansible/plugins/action/dnf.py b/lib/ansible/plugins/action/dnf.py index 7658a0e6d62..52391a4c827 100644 --- a/lib/ansible/plugins/action/dnf.py +++ b/lib/ansible/plugins/action/dnf.py @@ -8,10 +8,9 @@ from ansible.utils.display import Display display = Display() -VALID_BACKENDS = frozenset(("dnf", "dnf4", "dnf5")) +VALID_BACKENDS = frozenset(("yum", "yum4", "dnf", "dnf4", "dnf5")) -# FIXME mostly duplicate of the yum action plugin class ActionModule(ActionBase): TRANSFERS_FILES = False @@ -29,7 +28,7 @@ class ActionModule(ActionBase): module = self._task.args.get('use', self._task.args.get('use_backend', 'auto')) - if module == 'auto': + if module in {'yum', 'auto'}: try: if self._task.delegate_to: # if we delegate, we should use delegated host's facts module = self._templar.template("{{hostvars['%s']['ansible_facts']['pkg_mgr']}}" % self._task.delegate_to) @@ -57,7 +56,7 @@ class ActionModule(ActionBase): ) else: - if module == "dnf4": + if module in {"yum4", "dnf4"}: module = "dnf" # eliminate collisions with collections search while still allowing local override diff --git a/lib/ansible/plugins/action/yum.py b/lib/ansible/plugins/action/yum.py deleted file mode 100644 index d49b9c98f96..00000000000 --- a/lib/ansible/plugins/action/yum.py +++ /dev/null @@ -1,110 +0,0 @@ -# (c) 2018, Ansible Project -# -# This file is part of Ansible -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . -from __future__ import annotations - -from ansible.errors import AnsibleActionFail -from ansible.plugins.action import ActionBase -from ansible.utils.display import Display - -display = Display() - -VALID_BACKENDS = frozenset(('yum', 'yum4', 'dnf', 'dnf4', 'dnf5')) - - -class ActionModule(ActionBase): - - TRANSFERS_FILES = False - - def run(self, tmp=None, task_vars=None): - ''' - Action plugin handler for yum3 vs yum4(dnf) operations. - - Enables the yum module to use yum3 and/or yum4. Yum4 is a yum - command-line compatibility layer on top of dnf. Since the Ansible - modules for yum(aka yum3) and dnf(aka yum4) call each of yum3 and yum4's - python APIs natively on the backend, we need to handle this here and - pass off to the correct Ansible module to execute on the remote system. - ''' - - self._supports_check_mode = True - self._supports_async = True - - result = super(ActionModule, self).run(tmp, task_vars) - del tmp # tmp no longer has any effect - - # Carry-over concept from the package action plugin - if 'use' in self._task.args and 'use_backend' in self._task.args: - raise AnsibleActionFail("parameters are mutually exclusive: ('use', 'use_backend')") - - module = self._task.args.get('use', self._task.args.get('use_backend', 'auto')) - - if module == 'dnf': - module = 'auto' - - if module == 'auto': - try: - if self._task.delegate_to: # if we delegate, we should use delegated host's facts - module = self._templar.template("{{hostvars['%s']['ansible_facts']['pkg_mgr']}}" % self._task.delegate_to) - else: - module = self._templar.template("{{ansible_facts.pkg_mgr}}") - except Exception: - pass # could not get it from template! - - if module not in VALID_BACKENDS: - facts = self._execute_module( - module_name="ansible.legacy.setup", module_args=dict(filter="ansible_pkg_mgr", gather_subset="!all"), - task_vars=task_vars) - display.debug("Facts %s" % facts) - module = facts.get("ansible_facts", {}).get("ansible_pkg_mgr", "auto") - if (not self._task.delegate_to or self._task.delegate_facts) and module != 'auto': - result['ansible_facts'] = {'pkg_mgr': module} - - if module not in VALID_BACKENDS: - result.update( - { - 'failed': True, - 'msg': ("Could not detect which major revision of yum is in use, which is required to determine module backend.", - "You should manually specify use_backend to tell the module whether to use the yum (yum3) or dnf (yum4) backend})"), - } - ) - - else: - if module in {"yum4", "dnf4"}: - module = "dnf" - - # eliminate collisions with collections search while still allowing local override - module = 'ansible.legacy.' + module - - if not self._shared_loader_obj.module_loader.has_plugin(module): - result.update({'failed': True, 'msg': "Could not find a yum module backend for %s." % module}) - else: - new_module_args = self._task.args.copy() - if 'use_backend' in new_module_args: - del new_module_args['use_backend'] - if 'use' in new_module_args: - del new_module_args['use'] - - display.vvvv("Running %s as the backend for the yum action plugin" % module) - result.update(self._execute_module( - module_name=module, module_args=new_module_args, task_vars=task_vars, wrap_async=self._task.async_val)) - - # Cleanup - if not self._task.async_val: - # remove a temporary path we created - self._remove_tmp_path(self._connection._shell.tmpdir) - - return result diff --git a/test/integration/targets/copy/tasks/selinux.yml b/test/integration/targets/copy/tasks/selinux.yml index dddee6f933a..4a5870680a3 100644 --- a/test/integration/targets/copy/tasks/selinux.yml +++ b/test/integration/targets/copy/tasks/selinux.yml @@ -2,7 +2,7 @@ # https://github.com/ansible/ansible/issues/70244 - block: - name: Install dosfstools - yum: + dnf: name: dosfstools state: present @@ -31,6 +31,6 @@ state: absent - name: Uninstall dosfstools - yum: + dnf: name: dosfstools state: absent diff --git a/test/integration/targets/dnf/tasks/skip_broken_and_nobest.yml b/test/integration/targets/dnf/tasks/skip_broken_and_nobest.yml index 95c875a2f0d..46350f556a5 100644 --- a/test/integration/targets/dnf/tasks/skip_broken_and_nobest.yml +++ b/test/integration/targets/dnf/tasks/skip_broken_and_nobest.yml @@ -332,7 +332,7 @@ state: absent - name: Remove all test packages installed - yum: + dnf: name: - broken-* - dinginessentail diff --git a/test/integration/targets/package/tasks/main.yml b/test/integration/targets/package/tasks/main.yml index 37267aa63a5..d1934fc3701 100644 --- a/test/integration/targets/package/tasks/main.yml +++ b/test/integration/targets/package/tasks/main.yml @@ -149,17 +149,17 @@ when: ansible_distribution in package_distros ## -## yum +## dnf ## -#Validation for new parameter 'use' in yum action plugin which aliases to 'use_backend' +#Validation for new parameter 'use' in dnf action plugin which aliases to 'use_backend' #Issue: https://github.com/ansible/ansible/issues/70774 - block: - name: verify if using both the parameters 'use' and 'use_backend' throw error - yum: + dnf: name: at state: present - use_backend: yum - use: yum + use_backend: dnf + use: dnf ignore_errors: yes register: result @@ -170,7 +170,7 @@ - "not result is changed" - name: verify if package installation is successful using 'use' parameter - yum: + dnf: name: at state: present use: dnf @@ -182,7 +182,7 @@ - "result is changed" - name: remove at package - yum: + dnf: name: at state: absent use: dnf @@ -194,7 +194,7 @@ - "result is changed" - name: verify if package installation is successful using 'use_backend' parameter - yum: + dnf: name: at state: present use_backend: dnf @@ -206,7 +206,7 @@ - "result is changed" - name: remove at package - yum: + dnf: name: at state: absent use_backend: dnf @@ -218,7 +218,7 @@ - "result is changed" - name: verify if package installation is successful without using 'use_backend' and 'use' parameters - yum: + dnf: name: at state: present register: result @@ -229,7 +229,7 @@ - "result is changed" - name: remove at package - yum: + dnf: name: at state: absent register: result diff --git a/test/integration/targets/setup_epel/tasks/main.yml b/test/integration/targets/setup_epel/tasks/main.yml deleted file mode 100644 index a8593bb4fe8..00000000000 --- a/test/integration/targets/setup_epel/tasks/main.yml +++ /dev/null @@ -1,10 +0,0 @@ -- name: Enable RHEL7 extras - # EPEL 7 depends on RHEL 7 extras, which is not enabled by default on RHEL. - # See: https://docs.fedoraproject.org/en-US/epel/epel-policy/#_policy - command: yum-config-manager --enable rhel-7-server-rhui-extras-rpms - when: ansible_facts.distribution == 'RedHat' and ansible_facts.distribution_major_version == '7' -- name: Install EPEL - yum: - name: https://ci-files.testing.ansible.com/test/integration/targets/setup_epel/epel-release-latest-{{ ansible_distribution_major_version }}.noarch.rpm - disable_gpg_check: true - when: ansible_facts.distribution in ['RedHat', 'CentOS'] diff --git a/test/integration/targets/setup_rpm_repo/aliases b/test/integration/targets/setup_rpm_repo/aliases deleted file mode 100644 index 65e831523cc..00000000000 --- a/test/integration/targets/setup_rpm_repo/aliases +++ /dev/null @@ -1 +0,0 @@ -needs/target/setup_epel diff --git a/test/integration/targets/setup_rpm_repo/tasks/main.yml b/test/integration/targets/setup_rpm_repo/tasks/main.yml index bf5af101e0e..810419089fe 100644 --- a/test/integration/targets/setup_rpm_repo/tasks/main.yml +++ b/test/integration/targets/setup_rpm_repo/tasks/main.yml @@ -1,11 +1,4 @@ - block: - - name: Install epel repo which is missing on rhel-7 and is needed for rpmfluff - include_role: - name: setup_epel - when: - - ansible_distribution in ['RedHat', 'CentOS'] - - ansible_distribution_major_version is version('7', '==') - - name: Include distribution specific variables include_vars: "{{ lookup('first_found', params) }}" vars: diff --git a/test/integration/targets/unarchive/tasks/prepare_tests.yml b/test/integration/targets/unarchive/tasks/prepare_tests.yml index 98a8ba1118b..fa8de52d7d5 100644 --- a/test/integration/targets/unarchive/tasks/prepare_tests.yml +++ b/test/integration/targets/unarchive/tasks/prepare_tests.yml @@ -5,7 +5,7 @@ - name: Ensure required binaries are present package: name: "{{ unarchive_packages }}" - when: ansible_pkg_mgr in ('yum', 'dnf', 'apt', 'pkgng') + when: ansible_pkg_mgr in ('dnf', 'apt', 'pkgng') - name: prep our file copy: diff --git a/test/integration/targets/unarchive/tasks/test_missing_binaries.yml b/test/integration/targets/unarchive/tasks/test_missing_binaries.yml index 58d38f4f91e..04254ad6289 100644 --- a/test/integration/targets/unarchive/tasks/test_missing_binaries.yml +++ b/test/integration/targets/unarchive/tasks/test_missing_binaries.yml @@ -1,5 +1,5 @@ - name: Test missing binaries - when: ansible_pkg_mgr in ('yum', 'dnf', 'apt', 'pkgng') + when: ansible_pkg_mgr in ('dnf', 'apt', 'pkgng') block: - name: Remove zip binaries package: diff --git a/test/integration/targets/yum/aliases b/test/integration/targets/yum/aliases deleted file mode 100644 index b12f3547433..00000000000 --- a/test/integration/targets/yum/aliases +++ /dev/null @@ -1,4 +0,0 @@ -destructive -shippable/posix/group1 -skip/freebsd -skip/macos diff --git a/test/integration/targets/yum/files/yum.conf b/test/integration/targets/yum/files/yum.conf deleted file mode 100644 index 5a5fca608df..00000000000 --- a/test/integration/targets/yum/files/yum.conf +++ /dev/null @@ -1,5 +0,0 @@ -[main] -gpgcheck=1 -installonly_limit=3 -clean_requirements_on_remove=True -tsflags=nodocs diff --git a/test/integration/targets/yum/filter_plugins/filter_list_of_tuples_by_first_param.py b/test/integration/targets/yum/filter_plugins/filter_list_of_tuples_by_first_param.py deleted file mode 100644 index 86d6885a335..00000000000 --- a/test/integration/targets/yum/filter_plugins/filter_list_of_tuples_by_first_param.py +++ /dev/null @@ -1,22 +0,0 @@ -from __future__ import annotations - - -def filter_list_of_tuples_by_first_param(lst, search, startswith=False): - out = [] - for element in lst: - if startswith: - if element[0].startswith(search): - out.append(element) - else: - if search in element[0]: - out.append(element) - return out - - -class FilterModule(object): - ''' filter ''' - - def filters(self): - return { - 'filter_list_of_tuples_by_first_param': filter_list_of_tuples_by_first_param, - } diff --git a/test/integration/targets/yum/meta/main.yml b/test/integration/targets/yum/meta/main.yml deleted file mode 100644 index 34d812616b6..00000000000 --- a/test/integration/targets/yum/meta/main.yml +++ /dev/null @@ -1,4 +0,0 @@ -dependencies: - - prepare_tests - - setup_rpm_repo - - setup_remote_tmp_dir diff --git a/test/integration/targets/yum/tasks/cacheonly.yml b/test/integration/targets/yum/tasks/cacheonly.yml deleted file mode 100644 index 03cbd0e9c6c..00000000000 --- a/test/integration/targets/yum/tasks/cacheonly.yml +++ /dev/null @@ -1,16 +0,0 @@ ---- -- name: Test cacheonly (clean before testing) - command: yum clean all - -- name: Try installing from cache where it has been cleaned - yum: - name: sos - state: latest - cacheonly: true - register: yum_result - ignore_errors: true - -- name: Verify yum failure - assert: - that: - - "yum_result is failed" diff --git a/test/integration/targets/yum/tasks/check_mode_consistency.yml b/test/integration/targets/yum/tasks/check_mode_consistency.yml deleted file mode 100644 index e2a99d95bd7..00000000000 --- a/test/integration/targets/yum/tasks/check_mode_consistency.yml +++ /dev/null @@ -1,61 +0,0 @@ -- name: install htop in check mode to verify changes dict returned - yum: - name: htop - state: present - check_mode: yes - register: yum_changes_check_mode_result - -- name: install verify changes dict returned in check mode - assert: - that: - - "yum_changes_check_mode_result is success" - - "yum_changes_check_mode_result is changed" - - "'changes' in yum_changes_check_mode_result" - - "'installed' in yum_changes_check_mode_result['changes']" - - "'htop' in yum_changes_check_mode_result['changes']['installed']" - -- name: install htop to verify changes dict returned - yum: - name: htop - state: present - register: yum_changes_result - -- name: install verify changes dict returned - assert: - that: - - "yum_changes_result is success" - - "yum_changes_result is changed" - - "'changes' in yum_changes_result" - - "'installed' in yum_changes_result['changes']" - - "'htop' in yum_changes_result['changes']['installed']" - -- name: remove htop in check mode to verify changes dict returned - yum: - name: htop - state: absent - check_mode: yes - register: yum_changes_check_mode_result - -- name: remove verify changes dict returned in check mode - assert: - that: - - "yum_changes_check_mode_result is success" - - "yum_changes_check_mode_result is changed" - - "'changes' in yum_changes_check_mode_result" - - "'removed' in yum_changes_check_mode_result['changes']" - - "'htop' in yum_changes_check_mode_result['changes']['removed']" - -- name: remove htop to verify changes dict returned - yum: - name: htop - state: absent - register: yum_changes_result - -- name: remove verify changes dict returned - assert: - that: - - "yum_changes_result is success" - - "yum_changes_result is changed" - - "'changes' in yum_changes_result" - - "'removed' in yum_changes_result['changes']" - - "'htop' in yum_changes_result['changes']['removed']" diff --git a/test/integration/targets/yum/tasks/lock.yml b/test/integration/targets/yum/tasks/lock.yml deleted file mode 100644 index 3f585c1daae..00000000000 --- a/test/integration/targets/yum/tasks/lock.yml +++ /dev/null @@ -1,28 +0,0 @@ -- block: - - name: Make sure testing package is not installed - yum: - name: sos - state: absent - - - name: Create bogus lock file - copy: - content: bogus content for this lock file - dest: /var/run/yum.pid - - - name: Install a package, lock file should be deleted by the module - yum: - name: sos - state: present - register: yum_result - - - assert: - that: - - yum_result is success - - always: - - name: Clean up - yum: - name: sos - state: absent - - when: ansible_pkg_mgr == 'yum' diff --git a/test/integration/targets/yum/tasks/main.yml b/test/integration/targets/yum/tasks/main.yml deleted file mode 100644 index 157124a99cf..00000000000 --- a/test/integration/targets/yum/tasks/main.yml +++ /dev/null @@ -1,82 +0,0 @@ -# (c) 2014, James Tanner -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# Note: We install the yum package onto Fedora so that this will work on dnf systems -# We want to test that for people who don't want to upgrade their systems. - -- block: - - name: ensure test packages are removed before starting - yum: - name: - - sos - state: absent - - - import_tasks: yum.yml - always: - - name: remove installed packages - yum: - name: - - sos - state: absent - - - name: remove installed group - yum: - name: "@Custom Group" - state: absent - - - name: On Fedora 28 the above won't remove the group which results in a failure in repo.yml below - yum: - name: dinginessentail - state: absent - when: - - ansible_distribution in ['Fedora'] - - when: - - ansible_distribution in ['RedHat', 'CentOS', 'ScientificLinux', 'Fedora'] - - -- block: - - import_tasks: repo.yml - - import_tasks: yum_group_remove.yml - when: - - ansible_distribution in ['RedHat', 'CentOS', 'ScientificLinux'] - always: - - yum_repository: - name: "{{ item }}" - state: absent - loop: "{{ repos }}" - - - command: yum clean metadata - when: - - ansible_distribution in ['RedHat', 'CentOS', 'ScientificLinux', 'Fedora'] - - -- import_tasks: yuminstallroot.yml - when: - - ansible_distribution in ['RedHat', 'CentOS', 'ScientificLinux', 'Fedora'] - - -- import_tasks: proxy.yml - when: - - ansible_distribution in ['RedHat', 'CentOS', 'ScientificLinux', 'Fedora'] - - -- import_tasks: check_mode_consistency.yml - when: - - (ansible_distribution in ['RedHat', 'CentOS', 'ScientificLinux'] and ansible_distribution_major_version|int == 7) - - -- import_tasks: lock.yml - when: - - ansible_distribution in ['RedHat', 'CentOS', 'ScientificLinux'] - -- import_tasks: multiarch.yml - when: - - ansible_distribution in ['RedHat', 'CentOS', 'ScientificLinux'] - - ansible_architecture == 'x86_64' - # Our output parsing expects us to be on yum, not dnf - - ansible_distribution_major_version is version('7', '<=') - -- import_tasks: cacheonly.yml - when: - - ansible_distribution in ['RedHat', 'CentOS', 'ScientificLinux', 'Fedora'] diff --git a/test/integration/targets/yum/tasks/multiarch.yml b/test/integration/targets/yum/tasks/multiarch.yml deleted file mode 100644 index bced634c4fa..00000000000 --- a/test/integration/targets/yum/tasks/multiarch.yml +++ /dev/null @@ -1,154 +0,0 @@ -- block: - - name: Set up test yum repo - yum_repository: - name: multiarch-test-repo - description: ansible-test multiarch test repo - baseurl: "{{ multiarch_repo_baseurl }}" - gpgcheck: no - repo_gpgcheck: no - - - name: Install two out of date packages from the repo - yum: - name: - - multiarch-a-1.0 - - multiarch-b-1.0 - register: outdated - - - name: See what we installed - command: rpm -q multiarch-a multiarch-b - register: rpm_q - - # Here we assume we're running on x86_64 (and limit to this in main.yml) - # (avoid comparing ansible_architecture because we only have test RPMs - # for i686 and x86_64 and ansible_architecture could be other things.) - - name: Assert that we got the right architecture - assert: - that: - - outdated is changed - - outdated.changes.installed | length == 2 - - rpm_q.stdout_lines | length == 2 - - rpm_q.stdout_lines[0].endswith('x86_64') - - rpm_q.stdout_lines[1].endswith('x86_64') - - - name: Install the same versions, but i686 instead - yum: - name: - - multiarch-a-1.0*.i686 - - multiarch-b-1.0*.i686 - register: outdated_i686 - - - name: See what we installed - command: rpm -q multiarch-a multiarch-b - register: rpm_q - - - name: Assert that all four are installed - assert: - that: - - outdated_i686 is changed - - outdated.changes.installed | length == 2 - - rpm_q.stdout_lines | length == 4 - - - name: Update them all to 2.0 - yum: - name: multiarch-* - state: latest - update_only: true - register: yum_latest - - - name: Assert that all were updated and shown in results - assert: - that: - - yum_latest is changed - # This is just testing UI stability. The behavior is arguably not - # correct, because multiple packages are being updated. But the - # "because of (at least)..." wording kinda locks us in to only - # showing one update in this case. :( - - yum_latest.changes.updated | length == 1 - - - name: Downgrade them so we can upgrade them a different way - yum: - name: - - multiarch-a-1.0* - - multiarch-b-1.0* - allow_downgrade: true - register: downgrade - - - name: See what we installed - command: rpm -q multiarch-a multiarch-b --queryformat '%{name}-%{version}.%{arch}\n' - register: rpm_q - - - name: Ensure downgrade worked - assert: - that: - - downgrade is changed - - rpm_q.stdout_lines | sort == ['multiarch-a-1.0.i686', 'multiarch-a-1.0.x86_64', 'multiarch-b-1.0.i686', 'multiarch-b-1.0.x86_64'] - - # This triggers a different branch of logic that the partial wildcard - # above, but we're limited to check_mode here since it's '*'. - - name: Upgrade with full wildcard - yum: - name: '*' - state: latest - update_only: true - update_cache: true - check_mode: true - register: full_wildcard - - # https://github.com/ansible/ansible/issues/73284 - - name: Ensure we report things correctly (both arches) - assert: - that: - - full_wildcard is changed - - full_wildcard.changes.updated | filter_list_of_tuples_by_first_param('multiarch', startswith=True) | length == 4 - - - name: Downgrade them so we can upgrade them a different way - yum: - name: - - multiarch-a-1.0* - - multiarch-b-1.0* - allow_downgrade: true - register: downgrade - - - name: Try to install again via virtual provides, should be unchanged - yum: - name: - - virtual-provides-multiarch-a - - virtual-provides-multiarch-b - state: present - register: install_vp - - - name: Ensure the above did not change - assert: - that: - - install_vp is not changed - - - name: Try to upgrade via virtual provides - yum: - name: - - virtual-provides-multiarch-a - - virtual-provides-multiarch-b - state: latest - update_only: true - register: upgrade_vp - - - name: Ensure we report things correctly (both arches) - assert: - that: - - upgrade_vp is changed - # This is just testing UI stability, like above. - # We'll only have one package in "updated" per spec, even though - # (in this case) two are getting updated per spec. - - upgrade_vp.changes.updated | length == 2 - - always: - - name: Remove test yum repo - yum_repository: - name: multiarch-test-repo - state: absent - - - name: Remove all test packages installed - yum: - name: - - multiarch-* - - virtual-provides-multiarch-* - state: absent diff --git a/test/integration/targets/yum/tasks/proxy.yml b/test/integration/targets/yum/tasks/proxy.yml deleted file mode 100644 index b011d11b31e..00000000000 --- a/test/integration/targets/yum/tasks/proxy.yml +++ /dev/null @@ -1,186 +0,0 @@ -- name: test yum proxy settings - block: - - name: install tinyproxy - yum: - name: 'https://ci-files.testing.ansible.com/test/integration/targets/yum/tinyproxy-1.10.0-3.el7.x86_64.rpm' - state: installed - - # systemd doesn't play nice with this in a container for some reason - - name: start tinyproxy (systemd with tiny proxy does not work in container) - shell: tinyproxy - changed_when: false - - # test proxy without auth - - name: set unauthenticated proxy in yum.conf - lineinfile: - path: /etc/yum.conf - line: "proxy=http://127.0.0.1:8888" - state: present - - - name: clear proxy logs - shell: ': > /var/log/tinyproxy/tinyproxy.log' - changed_when: false - args: - executable: /usr/bin/bash - - - name: install ninvaders with unauthenticated proxy - yum: - name: 'https://ci-files.testing.ansible.com/test/integration/targets/yum/ninvaders-0.1.1-18.el7.x86_64.rpm' - state: installed - register: yum_proxy_result - - - assert: - that: - - "yum_proxy_result.changed" - - "'msg' in yum_proxy_result" - - "'rc' in yum_proxy_result" - - - name: check that it install via unauthenticated proxy - command: grep -q Request /var/log/tinyproxy/tinyproxy.log - - - name: uninstall ninvaders with unauthenticated proxy - yum: - name: ninvaders - state: absent - register: yum_proxy_result - - - assert: - that: - - "yum_proxy_result.changed" - - "'msg' in yum_proxy_result" - - "'rc' in yum_proxy_result" - - - name: unset unauthenticated proxy in yum.conf - lineinfile: - path: /etc/yum.conf - line: "proxy=http://127.0.0.1:8888" - state: absent - - # test proxy with auth - - name: set authenticated proxy config in tinyproxy.conf - lineinfile: - path: /etc/tinyproxy/tinyproxy.conf - line: "BasicAuth 1testuser 1testpassword" - state: present - - # systemd doesn't play nice with this in a container for some reason - - name: SIGHUP tinyproxy to reload config (workaround because of systemd+tinyproxy in container) - shell: kill -HUP $(ps -ef | grep tinyproxy | grep -v grep | awk '{print $2}') - changed_when: false - args: - executable: /usr/bin/bash - - - name: set authenticated proxy config in yum.conf - lineinfile: - path: /etc/yum.conf - line: "proxy=http://1testuser:1testpassword@127.0.0.1:8888" - state: present - - - name: clear proxy logs - shell: ': > /var/log/tinyproxy/tinyproxy.log' - changed_when: false - args: - executable: /usr/bin/bash - - - name: install ninvaders with authenticated proxy - yum: - name: 'https://ci-files.testing.ansible.com/test/integration/targets/yum/ninvaders-0.1.1-18.el7.x86_64.rpm' - state: installed - register: yum_proxy_result - - - assert: - that: - - "yum_proxy_result.changed" - - "'msg' in yum_proxy_result" - - "'rc' in yum_proxy_result" - - - name: check that it install via authenticated proxy - command: grep -q Request /var/log/tinyproxy/tinyproxy.log - - - name: uninstall ninvaders with authenticated proxy - yum: - name: ninvaders - state: absent - - - name: unset authenticated proxy config in yum.conf - lineinfile: - path: /etc/yum.conf - line: "proxy=http://1testuser:1testpassword@127.0.0.1:8888" - state: absent - - - name: set proxy config in yum.conf - lineinfile: - path: /etc/yum.conf - line: "proxy=http://127.0.0.1:8888" - state: present - - - name: set proxy_username config in yum.conf - lineinfile: - path: /etc/yum.conf - line: "proxy_username=1testuser" - state: present - - - name: set proxy_password config in yum.conf - lineinfile: - path: /etc/yum.conf - line: "proxy_password=1testpassword" - state: present - - - name: clear proxy logs - shell: ': > /var/log/tinyproxy/tinyproxy.log' - changed_when: false - args: - executable: /usr/bin/bash - - - name: install ninvaders with proxy, proxy_username, and proxy_password config in yum.conf - yum: - name: 'https://ci-files.testing.ansible.com/test/integration/targets/yum/ninvaders-0.1.1-18.el7.x86_64.rpm' - state: installed - register: yum_proxy_result - - - assert: - that: - - "yum_proxy_result.changed" - - "'msg' in yum_proxy_result" - - "'rc' in yum_proxy_result" - - - name: check that it install via proxy with proxy_username, proxy_password config in yum.conf - command: grep -q Request /var/log/tinyproxy/tinyproxy.log - - always: - #cleanup - - name: uninstall tinyproxy - yum: - name: tinyproxy - state: absent - - - name: uninstall ninvaders - yum: - name: ninvaders - state: absent - - - name: ensure unset authenticated proxy - lineinfile: - path: /etc/yum.conf - line: "proxy=http://1testuser:1testpassword@127.0.0.1:8888" - state: absent - - - name: ensure unset proxy - lineinfile: - path: /etc/yum.conf - line: "proxy=http://127.0.0.1:8888" - state: absent - - - name: ensure unset proxy_username - lineinfile: - path: /etc/yum.conf - line: "proxy_username=1testuser" - state: absent - - - name: ensure unset proxy_password - lineinfile: - path: /etc/yum.conf - line: "proxy_password=1testpassword" - state: absent - when: - - (ansible_distribution in ['RedHat', 'CentOS', 'ScientificLinux'] and ansible_distribution_major_version|int == 7 and ansible_architecture in ['x86_64']) diff --git a/test/integration/targets/yum/tasks/repo.yml b/test/integration/targets/yum/tasks/repo.yml deleted file mode 100644 index f312b1ca3de..00000000000 --- a/test/integration/targets/yum/tasks/repo.yml +++ /dev/null @@ -1,729 +0,0 @@ -- block: - - name: Install dinginessentail-1.0-1 - yum: - name: dinginessentail-1.0-1 - state: present - register: yum_result - - - name: Check dinginessentail with rpm - shell: rpm -q dinginessentail - register: rpm_result - - - name: Verify installation - assert: - that: - - "yum_result.changed" - - "rpm_result.stdout.startswith('dinginessentail-1.0-1')" - - - name: Verify yum module outputs - assert: - that: - - "'msg' in yum_result" - - "'rc' in yum_result" - - "'results' in yum_result" - # ============================================================================ - - name: Install dinginessentail-1.0-1 again - yum: - name: dinginessentail-1.0-1 - state: present - register: yum_result - - - name: Check dinginessentail with rpm - shell: rpm -q dinginessentail - register: rpm_result - - - name: Verify installation - assert: - that: - - "not yum_result.changed" - - "rpm_result.stdout.startswith('dinginessentail-1.0-1')" - - - name: Verify yum module outputs - assert: - that: - - "'msg' in yum_result" - - "'rc' in yum_result" - - "'results' in yum_result" - # ============================================================================ - - name: Install dinginessentail-1:1.0-2 - yum: - name: "dinginessentail-1:1.0-2.{{ ansible_architecture }}" - state: present - register: yum_result - - - name: Check dinginessentail with rpm - shell: rpm -q dinginessentail - register: rpm_result - - - name: Verify installation - assert: - that: - - "yum_result.changed" - - "rpm_result.stdout.startswith('dinginessentail-1.0-2')" - - - name: Verify yum module outputs - assert: - that: - - "'msg' in yum_result" - - "'rc' in yum_result" - - "'results' in yum_result" - - - name: Remove dinginessentail - yum: - name: dinginessentail - state: absent - # ============================================================================ - - name: Downgrade dinginessentail - yum: - name: dinginessentail-1.0-1 - state: present - allow_downgrade: yes - register: yum_result - - - name: Check dinginessentail with rpm - shell: rpm -q dinginessentail - register: rpm_result - - - name: Verify installation - assert: - that: - - "yum_result.changed" - - "rpm_result.stdout.startswith('dinginessentail-1.0-1')" - - - name: Verify yum module outputs - assert: - that: - - "'msg' in yum_result" - - "'rc' in yum_result" - - "'results' in yum_result" - # ============================================================================ - - name: Update to the latest dinginessentail - yum: - name: dinginessentail - state: latest - register: yum_result - - - name: Check dinginessentail with rpm - shell: rpm -q dinginessentail - register: rpm_result - - - name: Verify installation - assert: - that: - - "yum_result.changed" - - "rpm_result.stdout.startswith('dinginessentail-1.1-1')" - - - name: Verify yum module outputs - assert: - that: - - "'msg' in yum_result" - - "'rc' in yum_result" - - "'results' in yum_result" - # ============================================================================ - - name: Install dinginessentail-1.0-1 from a file (higher version is already installed) - yum: - name: "{{ repodir }}/dinginessentail-1.0-1.{{ ansible_architecture }}.rpm" - state: present - register: yum_result - - - name: Check dinginessentail with rpm - shell: rpm -q dinginessentail - register: rpm_result - - - name: Verify installation - assert: - that: - - "not yum_result.changed" - - "rpm_result.stdout.startswith('dinginessentail-1.1-1')" - - - name: Verify yum module outputs - assert: - that: - - "'msg' in yum_result" - - "'rc' in yum_result" - - "'results' in yum_result" - - - name: Remove dinginessentail - yum: - name: dinginessentail - state: absent - # ============================================================================ - - name: Install dinginessentail-1.0-1 from a file - yum: - name: "{{ repodir }}/dinginessentail-1.0-1.{{ ansible_architecture }}.rpm" - state: present - disable_gpg_check: true - register: yum_result - - - name: Check dinginessentail with rpm - shell: rpm -q dinginessentail - register: rpm_result - - - name: Verify installation - assert: - that: - - "yum_result.changed" - - "rpm_result.stdout.startswith('dinginessentail-1.0-1')" - - - name: Verify yum module outputs - assert: - that: - - "'msg' in yum_result" - - "'rc' in yum_result" - - "'results' in yum_result" - # ============================================================================ - - name: Install dinginessentail-1.0-1 from a file again - yum: - name: "{{ repodir }}/dinginessentail-1.0-1.{{ ansible_architecture }}.rpm" - state: present - disable_gpg_check: true - register: yum_result - - - name: Check dinginessentail with rpm - shell: rpm -q dinginessentail - register: rpm_result - - - name: Verify installation - assert: - that: - - "not yum_result.changed" - - "rpm_result.stdout.startswith('dinginessentail-1.0-1')" - - - name: Verify yum module outputs - assert: - that: - - "'msg' in yum_result" - - "'rc' in yum_result" - - "'results' in yum_result" - # ============================================================================ - - name: Install dinginessentail-1.0-2 from a file - yum: - name: "{{ repodir }}/dinginessentail-1.0-2.{{ ansible_architecture }}.rpm" - state: present - disable_gpg_check: true - register: yum_result - - - name: Check dinginessentail with rpm - shell: rpm -q dinginessentail - register: rpm_result - - - name: Verify installation - assert: - that: - - "yum_result.changed" - - "rpm_result.stdout.startswith('dinginessentail-1.0-2')" - - - name: Verify yum module outputs - assert: - that: - - "'msg' in yum_result" - - "'rc' in yum_result" - - "'results' in yum_result" - # ============================================================================ - - name: Install dinginessentail-1.0-2 from a file again - yum: - name: "{{ repodir }}/dinginessentail-1.0-2.{{ ansible_architecture }}.rpm" - state: present - disable_gpg_check: true - register: yum_result - - - name: Check dinginessentail with rpm - shell: rpm -q dinginessentail - register: rpm_result - - - name: Verify installation - assert: - that: - - "not yum_result.changed" - - "rpm_result.stdout.startswith('dinginessentail-1.0-2')" - - - name: Verify yum module outputs - assert: - that: - - "'msg' in yum_result" - - "'rc' in yum_result" - - "'results' in yum_result" - # ============================================================================ - - name: Try to downgrade dinginessentail without allow_downgrade being set - yum: - name: dinginessentail-1.0-1 - state: present - allow_downgrade: no - register: yum_result - - - name: Check dinginessentail with rpm - shell: rpm -q dinginessentail - register: rpm_result - - - name: Verify installation - assert: - that: - - "not yum_result.changed" - - "rpm_result.stdout.startswith('dinginessentail-1.0-2')" - - - name: Verify yum module outputs - assert: - that: - - "'msg' in yum_result" - - "'rc' in yum_result" - - "'results' in yum_result" - # ============================================================================ - - name: Update dinginessentail with update_only set - yum: - name: dinginessentail - state: latest - update_only: yes - register: yum_result - - - name: Check dinginessentail with rpm - shell: rpm -q dinginessentail - register: rpm_result - - - name: Verify installation - assert: - that: - - "yum_result.changed" - - "rpm_result.stdout.startswith('dinginessentail-1.1-1')" - - - name: Verify yum module outputs - assert: - that: - - "'msg' in yum_result" - - "'rc' in yum_result" - - "'results' in yum_result" - - - name: Remove dinginessentail - yum: - name: dinginessentail - state: absent - # ============================================================================ - - name: Try to update dinginessentail which is not installed, update_only is set - yum: - name: dinginessentail - state: latest - update_only: yes - register: yum_result - ignore_errors: yes - - - name: Check dinginessentail with rpm - shell: rpm -q dinginessentail - register: rpm_result - ignore_errors: yes - - - name: Verify installation - assert: - that: - - "rpm_result.rc == 1" - - "yum_result.rc == 0" - - "not yum_result.changed" - - "not yum_result is failed" - - - name: Verify yum module outputs - assert: - that: - - "'msg' in yum_result" - - "'rc' in yum_result" - - "'results' in yum_result" - # ============================================================================ - - name: Try to install incompatible arch - yum: - name: "{{ repodir_ppc64 }}/dinginessentail-1.0-1.ppc64.rpm" - state: present - register: yum_result - ignore_errors: yes - - - name: Check dinginessentail with rpm - shell: rpm -q dinginessentail - register: rpm_result - ignore_errors: yes - - - name: Verify installation - assert: - that: - - "rpm_result.rc == 1" - - "yum_result.rc == 1" - - "not yum_result.changed" - - "yum_result is failed" - - - name: Verify yum module outputs - assert: - that: - - "'msg' in yum_result" - - "'rc' in yum_result" - - "'results' in yum_result" - # ============================================================================ - - name: Make sure latest dinginessentail is installed - yum: - name: dinginessentail - state: latest - - - name: Downgrade dinginessentail using rpm file - yum: - name: "{{ repodir }}/dinginessentail-1.0-1.{{ ansible_architecture }}.rpm" - state: present - allow_downgrade: yes - disable_gpg_check: yes - register: yum_result - - - name: Check dinginessentail with rpm - shell: rpm -q dinginessentail - register: rpm_result - - - name: Verify installation - assert: - that: - - "yum_result.changed" - - "rpm_result.stdout.startswith('dinginessentail-1.0-1')" - - - name: Verify yum module outputs - assert: - that: - - "'msg' in yum_result" - - "'rc' in yum_result" - - "'results' in yum_result" - # ============================================================================ - - block: - - name: make sure dinginessentail is not installed - yum: - name: dinginessentail - state: absent - - - name: install dinginessentail both archs - yum: - name: "{{ pkgs }}" - state: present - disable_gpg_check: true - vars: - pkgs: - - "{{ repodir }}/dinginessentail-1.1-1.x86_64.rpm" - - "{{ repodir_i686 }}/dinginessentail-1.1-1.i686.rpm" - - - name: try to install lower version of dinginessentail from rpm file, without allow_downgrade, just one arch - yum: - name: "{{ repodir_i686 }}/dinginessentail-1.0-1.i686.rpm" - state: present - register: yum_result - - - name: check dinginessentail with rpm - shell: rpm -q dinginessentail - register: rpm_result - - - name: verify installation - assert: - that: - - "not yum_result.changed" - - "rpm_result.stdout_lines[0].startswith('dinginessentail-1.1-1')" - - "rpm_result.stdout_lines[1].startswith('dinginessentail-1.1-1')" - - - name: verify yum module outputs - assert: - that: - - "'msg' in yum_result" - - "'rc' in yum_result" - - "'results' in yum_result" - when: ansible_architecture == "x86_64" - # ============================================================================ - - block: - - name: make sure dinginessentail is not installed - yum: - name: dinginessentail - state: absent - - - name: install dinginessentail both archs - yum: - name: "{{ pkgs }}" - state: present - disable_gpg_check: true - vars: - pkgs: - - "{{ repodir }}/dinginessentail-1.0-1.x86_64.rpm" - - "{{ repodir_i686 }}/dinginessentail-1.0-1.i686.rpm" - - - name: Update both arch in one task using rpm files - yum: - name: "{{ repodir }}/dinginessentail-1.1-1.x86_64.rpm,{{ repodir_i686 }}/dinginessentail-1.1-1.i686.rpm" - state: present - disable_gpg_check: yes - register: yum_result - - - name: check dinginessentail with rpm - shell: rpm -q dinginessentail - register: rpm_result - - - name: verify installation - assert: - that: - - "yum_result.changed" - - "rpm_result.stdout_lines[0].startswith('dinginessentail-1.1-1')" - - "rpm_result.stdout_lines[1].startswith('dinginessentail-1.1-1')" - - - name: verify yum module outputs - assert: - that: - - "'msg' in yum_result" - - "'rc' in yum_result" - - "'results' in yum_result" - when: ansible_architecture == "x86_64" - # ============================================================================ - always: - - name: Clean up - yum: - name: dinginessentail - state: absent - -# FIXME: dnf currently doesn't support epoch as part of it's pkg_spec for -# finding install candidates -# https://bugzilla.redhat.com/show_bug.cgi?id=1619687 -- block: - - name: Install 1:dinginessentail-1.0-2 - yum: - name: "1:dinginessentail-1.0-2.{{ ansible_architecture }}" - state: present - disable_gpg_check: true - register: yum_result - - - name: Check dinginessentail with rpm - shell: rpm -q dinginessentail - register: rpm_result - - - name: Verify installation - assert: - that: - - "yum_result.changed" - - "rpm_result.stdout.startswith('dinginessentail-1.0-2')" - - - name: Verify yum module outputs - assert: - that: - - "'msg' in yum_result" - - "'rc' in yum_result" - - "'results' in yum_result" - always: - - name: Clean up - yum: - name: dinginessentail - state: absent - - when: ansible_pkg_mgr == 'yum' - -# DNF1 (Fedora < 26) had some issues: -# - did not accept architecture tag as valid component of a package spec unless -# installing a file (i.e. can't search the repo) -# - doesn't handle downgrade transactions via the API properly, marks it as a -# conflict -# -# NOTE: Both DNF1 and Fedora < 26 have long been EOL'd by their respective -# upstreams -- block: - # ============================================================================ - - name: Install dinginessentail-1.0-2 - yum: - name: "dinginessentail-1.0-2.{{ ansible_architecture }}" - state: present - disable_gpg_check: true - register: yum_result - - - name: Check dinginessentail with rpm - shell: rpm -q dinginessentail - register: rpm_result - - - name: Verify installation - assert: - that: - - "yum_result.changed" - - "rpm_result.stdout.startswith('dinginessentail-1.0-2')" - - - name: Verify yum module outputs - assert: - that: - - "'msg' in yum_result" - - "'rc' in yum_result" - - "'results' in yum_result" - - - name: Install dinginessentail-1.0-2 again - yum: - name: dinginessentail-1.0-2 - state: present - register: yum_result - - - name: Check dinginessentail with rpm - shell: rpm -q dinginessentail - register: rpm_result - - - name: Verify installation - assert: - that: - - "not yum_result.changed" - - "rpm_result.stdout.startswith('dinginessentail-1.0-2')" - - - name: Verify yum module outputs - assert: - that: - - "'msg' in yum_result" - - "'rc' in yum_result" - - "'results' in yum_result" - always: - - name: Clean up - yum: - name: dinginessentail - state: absent - when: not (ansible_distribution == "Fedora" and ansible_distribution_major_version|int < 26) - -# https://github.com/ansible/ansible/issues/47689 -- block: - - name: Install dinginessentail == 1.0 - yum: - name: "dinginessentail == 1.0" - state: present - register: yum_result - - - name: Check dinginessentail with rpm - shell: rpm -q dinginessentail - register: rpm_result - - - name: Verify installation - assert: - that: - - "yum_result.changed" - - "rpm_result.stdout.startswith('dinginessentail-1.0-1')" - - - name: Verify yum module outputs - assert: - that: - - "'msg' in yum_result" - - "'rc' in yum_result" - - "'results' in yum_result" - always: - - name: Clean up - yum: - name: dinginessentail - state: absent - - when: ansible_pkg_mgr == 'yum' - - -# https://github.com/ansible/ansible/pull/54603 -- block: - - name: Install dinginessentail < 1.1 - yum: - name: "dinginessentail < 1.1" - state: present - register: yum_result - - - name: Check dinginessentail with rpm - shell: rpm -q dinginessentail - register: rpm_result - - - name: Verify installation - assert: - that: - - "yum_result.changed" - - "rpm_result.stdout.startswith('dinginessentail-1.0')" - - - name: Install dinginessentail >= 1.1 - yum: - name: "dinginessentail >= 1.1" - state: present - register: yum_result - - - name: Check dinginessentail with rpm - shell: rpm -q dinginessentail - register: rpm_result - - - name: Verify installation - assert: - that: - - "yum_result.changed" - - "rpm_result.stdout.startswith('dinginessentail-1.1')" - - - name: Verify yum module outputs - assert: - that: - - "'msg' in yum_result" - - "'rc' in yum_result" - - "'results' in yum_result" - - always: - - name: Clean up - yum: - name: dinginessentail - state: absent - - when: ansible_pkg_mgr == 'yum' - -# https://github.com/ansible/ansible/issues/45250 -- block: - - name: Install dinginessentail-1.0, dinginessentail-olive-1.0, landsidescalping-1.0 - yum: - name: "dinginessentail-1.0,dinginessentail-olive-1.0,landsidescalping-1.0" - state: present - - - name: Upgrade dinginessentail* - yum: - name: dinginessentail* - state: latest - register: yum_result - - - name: Check dinginessentail with rpm - shell: rpm -q dinginessentail - register: rpm_result - - - name: Verify update of dinginessentail - assert: - that: - - "rpm_result.stdout.startswith('dinginessentail-1.1-1')" - - - name: Check dinginessentail-olive with rpm - shell: rpm -q dinginessentail-olive - register: rpm_result - - - name: Verify update of dinginessentail-olive - assert: - that: - - "rpm_result.stdout.startswith('dinginessentail-olive-1.1-1')" - - - name: Check landsidescalping with rpm - shell: rpm -q landsidescalping - register: rpm_result - - - name: Verify landsidescalping did NOT get updated - assert: - that: - - "rpm_result.stdout.startswith('landsidescalping-1.0-1')" - - - name: Verify yum module outputs - assert: - that: - - "yum_result is changed" - - "'msg' in yum_result" - - "'rc' in yum_result" - - "'results' in yum_result" - always: - - name: Clean up - yum: - name: dinginessentail,dinginessentail-olive,landsidescalping - state: absent - -- block: - - yum: - name: dinginessentail - state: present - - - yum: - list: dinginessentail* - register: list_out - - - set_fact: - passed: true - loop: "{{ list_out.results }}" - when: item.yumstate == 'installed' - - - name: Test that there is yumstate=installed in the result - assert: - that: - - passed is defined - always: - - name: Clean up - yum: - name: dinginessentail - state: absent diff --git a/test/integration/targets/yum/tasks/yum.yml b/test/integration/targets/yum/tasks/yum.yml deleted file mode 100644 index 511c57760a8..00000000000 --- a/test/integration/targets/yum/tasks/yum.yml +++ /dev/null @@ -1,884 +0,0 @@ -# Setup by setup_rpm_repo -- set_fact: - package1: dinginessentail - package2: dinginessentail-olive - -# UNINSTALL -- name: uninstall {{ package1 }} - yum: name={{ package1 }} state=removed - register: yum_result - -- name: check {{ package1 }} with rpm - shell: rpm -q {{ package1 }} - ignore_errors: True - register: rpm_result - -- name: verify uninstallation of {{ package1 }} - assert: - that: - - "yum_result is success" - - "rpm_result is failed" - -# UNINSTALL AGAIN -- name: uninstall {{ package1 }} again in check mode - yum: name={{ package1 }} state=removed - check_mode: true - register: yum_result - -- name: verify no change on re-uninstall in check mode - assert: - that: - - "not yum_result is changed" - -- name: uninstall {{ package1 }} again - yum: name={{ package1 }} state=removed - register: yum_result - -- name: verify no change on re-uninstall - assert: - that: - - "not yum_result is changed" - -# INSTALL -- name: install {{ package1 }} in check mode - yum: name={{ package1 }} state=present - check_mode: true - register: yum_result - -- name: verify installation of {{ package1 }} in check mode - assert: - that: - - "yum_result is changed" - -- name: install {{ package1 }} - yum: name={{ package1 }} state=present - register: yum_result - -- name: verify installation of {{ package1 }} - assert: - that: - - "yum_result is success" - - "yum_result is changed" - -- name: verify yum module outputs - assert: - that: - - "'changed' in yum_result" - - "'msg' in yum_result" - - "'results' in yum_result" - -- name: check {{ package1 }} with rpm - shell: rpm -q {{ package1 }} - -# INSTALL AGAIN -- name: install {{ package1 }} again in check mode - yum: name={{ package1 }} state=present - check_mode: true - register: yum_result -- name: verify no change on second install in check mode - assert: - that: - - "not yum_result is changed" - -- name: install {{ package1 }} again - yum: name={{ package1 }} state=present - register: yum_result -- name: verify no change on second install - assert: - that: - - "not yum_result is changed" - -- name: install {{ package1 }} again with empty string enablerepo - yum: name={{ package1 }} state=present enablerepo="" - register: yum_result -- name: verify no change on third install with empty string enablerepo - assert: - that: - - "yum_result is success" - - "not yum_result is changed" - -# This test case is unfortunately distro specific because we have to specify -# repo names which are not the same across Fedora/RHEL/CentOS for base/updates -- name: install {{ package1 }} again with missing repo enablerepo - yum: - name: '{{ package1 }}' - state: present - enablerepo: '{{ repos + ["thisrepodoesnotexist"] }}' - disablerepo: "*" - register: yum_result - when: ansible_distribution == 'CentOS' -- name: verify no change on fourth install with missing repo enablerepo (yum) - assert: - that: - - "yum_result is success" - - "yum_result is not changed" - when: ansible_distribution == 'CentOS' - -# This test case is unfortunately distro specific because we have to specify -# repo names which are not the same across Fedora/RHEL/CentOS for base/updates -- name: install repos again with disable all and enable select repo(s) - yum: - name: '{{ package1 }}' - state: present - enablerepo: '{{ repos }}' - disablerepo: "*" - register: yum_result - when: ansible_distribution == 'CentOS' -- name: verify no change on fourth install with missing repo enablerepo (yum) - assert: - that: - - "yum_result is success" - - "yum_result is not changed" - when: ansible_distribution == 'CentOS' - -- name: install {{ package1 }} again with only missing repo enablerepo - yum: - name: '{{ package1 }}' - state: present - enablerepo: "thisrepodoesnotexist" - ignore_errors: true - register: yum_result -- name: verify no change on fifth install with only missing repo enablerepo (yum) - assert: - that: - - "yum_result is not success" - when: ansible_pkg_mgr == 'yum' -- name: verify no change on fifth install with only missing repo enablerepo (dnf) - assert: - that: - - "yum_result is success" - when: ansible_pkg_mgr == 'dnf' - -# INSTALL AGAIN WITH LATEST -- name: install {{ package1 }} again with state latest in check mode - yum: name={{ package1 }} state=latest - check_mode: true - register: yum_result -- name: verify install {{ package1 }} again with state latest in check mode - assert: - that: - - "not yum_result is changed" - -- name: install {{ package1 }} again with state latest idempotence - yum: name={{ package1 }} state=latest - register: yum_result -- name: verify install {{ package1 }} again with state latest idempotence - assert: - that: - - "not yum_result is changed" - -# INSTALL WITH LATEST -- name: uninstall {{ package1 }} - yum: name={{ package1 }} state=removed - register: yum_result -- name: verify uninstall {{ package1 }} - assert: - that: - - "yum_result is successful" - -- name: copy yum.conf file in case it is missing - copy: - src: yum.conf - dest: /etc/yum.conf - force: False - register: yum_conf_copy - -- block: - - name: install {{ package1 }} with state latest in check mode with config file param - yum: name={{ package1 }} state=latest conf_file=/etc/yum.conf - check_mode: true - register: yum_result - - name: verify install {{ package1 }} with state latest in check mode with config file param - assert: - that: - - "yum_result is changed" - - always: - - name: remove tmp yum.conf file if we created it - file: - path: /etc/yum.conf - state: absent - when: yum_conf_copy is changed - -- name: install {{ package1 }} with state latest in check mode - yum: name={{ package1 }} state=latest - check_mode: true - register: yum_result -- name: verify install {{ package1 }} with state latest in check mode - assert: - that: - - "yum_result is changed" - -- name: install {{ package1 }} with state latest - yum: name={{ package1 }} state=latest - register: yum_result -- name: verify install {{ package1 }} with state latest - assert: - that: - - "yum_result is changed" - -- name: install {{ package1 }} with state latest idempotence - yum: name={{ package1 }} state=latest - register: yum_result -- name: verify install {{ package1 }} with state latest idempotence - assert: - that: - - "not yum_result is changed" - -- name: install {{ package1 }} with state latest idempotence with config file param - yum: name={{ package1 }} state=latest - register: yum_result -- name: verify install {{ package1 }} with state latest idempotence with config file param - assert: - that: - - "not yum_result is changed" - - -# Multiple packages -- name: uninstall {{ package1 }} and {{ package2 }} - yum: name={{ package1 }},{{ package2 }} state=removed - -- name: check {{ package1 }} with rpm - shell: rpm -q {{ package1 }} - ignore_errors: True - register: rpm_package1_result - -- name: check {{ package2 }} with rpm - shell: rpm -q {{ package2 }} - ignore_errors: True - register: rpm_package2_result - -- name: verify packages installed - assert: - that: - - "rpm_package1_result is failed" - - "rpm_package2_result is failed" - -- name: install {{ package1 }} and {{ package2 }} as comma separated - yum: name={{ package1 }},{{ package2 }} state=present - register: yum_result - -- name: verify packages installed - assert: - that: - - "yum_result is success" - - "yum_result is changed" - -- name: check {{ package1 }} with rpm - shell: rpm -q {{ package1 }} - -- name: check {{ package2 }} with rpm - shell: rpm -q {{ package2 }} - -- name: uninstall {{ package1 }} and {{ package2 }} - yum: name={{ package1 }},{{ package2 }} state=removed - register: yum_result - -- name: install {{ package1 }} and {{ package2 }} as list - yum: - name: - - '{{ package1 }}' - - '{{ package2 }}' - state: present - register: yum_result - -- name: verify packages installed - assert: - that: - - "yum_result is success" - - "yum_result is changed" - -- name: check {{ package1 }} with rpm - shell: rpm -q {{ package1 }} - -- name: check {{ package2 }} with rpm - shell: rpm -q {{ package2 }} - -- name: uninstall {{ package1 }} and {{ package2 }} - yum: name={{ package1 }},{{ package2 }} state=removed - register: yum_result - -- name: install {{ package1 }} and {{ package2 }} as comma separated with spaces - yum: - name: "{{ package1 }}, {{ package2 }}" - state: present - register: yum_result - -- name: verify packages installed - assert: - that: - - "yum_result is success" - - "yum_result is changed" - -- name: check {{ package1 }} with rpm - shell: rpm -q {{ package1 }} - -- name: check {{ package2 }} with rpm - shell: rpm -q {{ package2 }} - -- name: uninstall {{ package1 }} and {{ package2 }} - yum: name={{ package1 }},{{ package2 }} state=removed - -- name: install non-existent rpm - yum: - name: does-not-exist - register: non_existent_rpm - ignore_errors: True - -- name: check non-existent rpm install failed - assert: - that: - - non_existent_rpm is failed - -# Install in installroot='/' -- name: install {{ package1 }} - yum: name={{ package1 }} state=present installroot='/' - register: yum_result - -- name: verify installation of {{ package1 }} - assert: - that: - - "yum_result is success" - - "yum_result is changed" - -- name: verify yum module outputs - assert: - that: - - "'changed' in yum_result" - - "'msg' in yum_result" - - "'results' in yum_result" - -- name: check {{ package1 }} with rpm - shell: rpm -q {{ package1 }} --root=/ - -- name: uninstall {{ package1 }} - yum: - name: '{{ package1 }}' - installroot: '/' - state: removed - register: yum_result - -# Seems like some yum versions won't download a package from local file repository, continue to use sos for this test. -# https://stackoverflow.com/questions/58295660/yum-downloadonly-ignores-packages-in-local-repo -- name: Test download_only - yum: - name: sos - state: latest - download_only: true - register: yum_result - -- name: verify download of sos (part 1 -- yum "install" succeeded) - assert: - that: - - "yum_result is success" - - "yum_result is changed" - -- name: uninstall sos (noop) - yum: - name: sos - state: removed - register: yum_result - -- name: verify download of sos (part 2 -- nothing removed during uninstall) - assert: - that: - - "yum_result is success" - - "not yum_result is changed" - -- name: uninstall sos for downloadonly/downloaddir test - yum: - name: sos - state: absent - -- name: Test download_only/download_dir - yum: - name: sos - state: latest - download_only: true - download_dir: "/var/tmp/packages" - register: yum_result - -- name: verify yum output - assert: - that: - - "yum_result is success" - - "yum_result is changed" - -- command: "ls /var/tmp/packages" - register: ls_out - -- name: Verify specified download_dir was used - assert: - that: - - "'sos' in ls_out.stdout" - -- name: install group - yum: - name: "@Custom Group" - state: present - register: yum_result - -- name: verify installation of the group - assert: - that: - - "yum_result is success" - - "yum_result is changed" - -- name: verify yum module outputs - assert: - that: - - "'changed' in yum_result" - - "'msg' in yum_result" - - "'results' in yum_result" - -- name: install the group again - yum: - name: "@Custom Group" - state: present - register: yum_result - -- name: verify nothing changed - assert: - that: - - "yum_result is success" - - "not yum_result is changed" - -- name: verify yum module outputs - assert: - that: - - "'changed' in yum_result" - - "'msg' in yum_result" - - "'results' in yum_result" - -- name: install the group again but also with a package that is not yet installed - yum: - name: - - "@Custom Group" - - '{{ package2 }}' - state: present - register: yum_result - -- name: verify {{ package3 }} is installed - assert: - that: - - "yum_result is success" - - "yum_result is changed" - -- name: verify yum module outputs - assert: - that: - - "'changed' in yum_result" - - "'msg' in yum_result" - - "'results' in yum_result" - -- name: try to install the group again, with --check to check 'changed' - yum: - name: "@Custom Group" - state: present - check_mode: yes - register: yum_result - -- name: verify nothing changed - assert: - that: - - "not yum_result is changed" - -- name: verify yum module outputs - assert: - that: - - "'changed' in yum_result" - - "'msg' in yum_result" - - "'results' in yum_result" - -- name: try to install non existing group - yum: - name: "@non-existing-group" - state: present - register: yum_result - ignore_errors: True - -- name: verify installation of the non existing group failed - assert: - that: - - "yum_result is failed" - - "not yum_result is changed" - - "yum_result is failed" - -- name: verify yum module outputs - assert: - that: - - "'changed' in yum_result" - - "'msg' in yum_result" - - "'results' in yum_result" - -- name: try to install non existing file - yum: - name: /tmp/non-existing-1.0.0.fc26.noarch.rpm - state: present - register: yum_result - ignore_errors: yes - -- name: verify installation failed - assert: - that: - - "yum_result is failed" - - "not yum_result is changed" - -- name: verify yum module outputs - assert: - that: - - "'changed' in yum_result" - - "'msg' in yum_result" - -- name: try to install from non existing url - yum: - name: https://ci-files.testing.ansible.com/test/integration/targets/yum/non-existing-1.0.0.fc26.noarch.rpm - state: present - register: yum_result - ignore_errors: yes - -- name: verify installation failed - assert: - that: - - "yum_result is failed" - - "not yum_result is changed" - -- name: verify yum module outputs - assert: - that: - - "'changed' in yum_result" - - "'msg' in yum_result" - -- name: use latest to install httpd - yum: - name: httpd - state: latest - register: yum_result - -- name: verify httpd was installed - assert: - that: - - "'changed' in yum_result" - -- name: uninstall httpd - yum: - name: httpd - state: removed - -- name: update httpd only if it exists - yum: - name: httpd - state: latest - update_only: yes - register: yum_result - -- name: verify httpd not installed - assert: - that: - - "not yum_result is changed" - - "'Packages providing httpd not installed due to update_only specified' in yum_result.results" - -- name: try to install uncompatible arch rpm on non-ppc64le, should fail - yum: - name: https://ci-files.testing.ansible.com/test/integration/targets/yum/banner-1.3.4-3.el7.ppc64le.rpm - state: present - register: yum_result - ignore_errors: True - when: - - ansible_architecture not in ['ppc64le'] - -- name: verify that yum failed on non-ppc64le - assert: - that: - - "not yum_result is changed" - - "yum_result is failed" - when: - - ansible_architecture not in ['ppc64le'] - -- name: try to install uncompatible arch rpm on ppc64le, should fail - yum: - name: https://ci-files.testing.ansible.com/test/integration/targets/yum/tinyproxy-1.10.0-3.el7.x86_64.rpm - state: present - register: yum_result - ignore_errors: True - when: - - ansible_architecture in ['ppc64le'] - -- name: verify that yum failed on ppc64le - assert: - that: - - "not yum_result is changed" - - "yum_result is failed" - when: - - ansible_architecture in ['ppc64le'] - -# setup for testing installing an RPM from url - -- set_fact: - pkg_name: noarchfake - pkg_path: '{{ repodir }}/noarchfake-1.0-1.noarch.rpm' - -- name: cleanup - yum: - name: "{{ pkg_name }}" - state: absent - -# setup end - -- name: install a local noarch rpm from file - yum: - name: "{{ pkg_path }}" - state: present - disable_gpg_check: true - register: yum_result - -- name: verify installation - assert: - that: - - "yum_result is success" - - "yum_result is changed" - - "yum_result is not failed" - -- name: verify yum module outputs - assert: - that: - - "'changed' in yum_result" - - "'msg' in yum_result" - - "'results' in yum_result" - -- name: install the downloaded rpm again - yum: - name: "{{ pkg_path }}" - state: present - register: yum_result - -- name: verify installation - assert: - that: - - "yum_result is success" - - "not yum_result is changed" - - "yum_result is not failed" - -- name: verify yum module outputs - assert: - that: - - "'changed' in yum_result" - - "'msg' in yum_result" - - "'results' in yum_result" - -- name: clean up - yum: - name: "{{ pkg_name }}" - state: absent - -- name: install from url - yum: - name: "file://{{ pkg_path }}" - state: present - disable_gpg_check: true - register: yum_result - -- name: verify installation - assert: - that: - - "yum_result is success" - - "yum_result is changed" - - "yum_result is not failed" - -- name: verify yum module outputs - assert: - that: - - "'changed' in yum_result" - - "'msg' in yum_result" - - "'results' in yum_result" - -- name: Create a temp RPM file which does not contain nevra information - file: - name: "/tmp/non_existent_pkg.rpm" - state: touch - -- name: Try installing RPM file which does not contain nevra information - yum: - name: "/tmp/non_existent_pkg.rpm" - state: present - register: no_nevra_info_result - ignore_errors: yes - -- name: Verify RPM failed to install - assert: - that: - - "'changed' in no_nevra_info_result" - - "'msg' in no_nevra_info_result" - -- name: Delete a temp RPM file - file: - name: "/tmp/non_existent_pkg.rpm" - state: absent - -- name: get yum version - yum: - list: yum - register: yum_version - -- name: set yum_version of installed version - set_fact: - yum_version: "{%- if item.yumstate == 'installed' -%}{{ item.version }}{%- else -%}{{ yum_version }}{%- endif -%}" - with_items: "{{ yum_version.results }}" - -- name: Ensure double uninstall of wildcard globs works - block: - - name: "Install lohit-*-fonts" - yum: - name: "lohit-*-fonts" - state: present - - - name: "Remove lohit-*-fonts (1st time)" - yum: - name: "lohit-*-fonts" - state: absent - register: remove_lohit_fonts_1 - - - name: "Verify lohit-*-fonts (1st time)" - assert: - that: - - "remove_lohit_fonts_1 is changed" - - "'msg' in remove_lohit_fonts_1" - - "'results' in remove_lohit_fonts_1" - - - name: "Remove lohit-*-fonts (2nd time)" - yum: - name: "lohit-*-fonts" - state: absent - register: remove_lohit_fonts_2 - - - name: "Verify lohit-*-fonts (2nd time)" - assert: - that: - - "remove_lohit_fonts_2 is not changed" - - "'msg' in remove_lohit_fonts_2" - - "'results' in remove_lohit_fonts_2" - - "'lohit-*-fonts is not installed' in remove_lohit_fonts_2['results']" - -- block: - - name: uninstall {{ package2 }} - yum: name={{ package2 }} state=removed - - - name: check {{ package2 }} with rpm - shell: rpm -q {{ package2 }} - ignore_errors: True - register: rpm_package2_result - - - name: verify {{ package2 }} is uninstalled - assert: - that: - - "rpm_package2_result is failed" - - - name: exclude {{ package2 }} (yum backend) - lineinfile: - dest: /etc/yum.conf - regexp: (^exclude=)(.)* - line: "exclude={{ package2 }}*" - state: present - when: ansible_pkg_mgr == 'yum' - - - name: exclude {{ package2 }} (dnf backend) - lineinfile: - dest: /etc/dnf/dnf.conf - regexp: (^excludepkgs=)(.)* - line: "excludepkgs={{ package2 }}*" - state: present - when: ansible_pkg_mgr == 'dnf' - - # begin test case where disable_excludes is supported - - name: Try install {{ package2 }} without disable_excludes - yum: name={{ package2 }} state=latest - register: yum_package2_result - ignore_errors: True - - - name: verify {{ package2 }} did not install because it is in exclude list - assert: - that: - - "yum_package2_result is failed" - - - name: install {{ package2 }} with disable_excludes - yum: name={{ package2 }} state=latest disable_excludes=all - register: yum_package2_result_using_excludes - - - name: verify {{ package2 }} did install using disable_excludes=all - assert: - that: - - "yum_package2_result_using_excludes is success" - - "yum_package2_result_using_excludes is changed" - - "yum_package2_result_using_excludes is not failed" - - - name: remove exclude {{ package2 }} (cleanup yum.conf) - lineinfile: - dest: /etc/yum.conf - regexp: (^exclude={{ package2 }}*) - line: "exclude=" - state: present - when: ansible_pkg_mgr == 'yum' - - - name: remove exclude {{ package2 }} (cleanup dnf.conf) - lineinfile: - dest: /etc/dnf/dnf.conf - regexp: (^excludepkgs={{ package2 }}*) - line: "excludepkgs=" - state: present - when: ansible_pkg_mgr == 'dnf' - - # Fedora < 26 has a bug in dnf where package excludes in dnf.conf aren't - # actually honored and those releases are EOL'd so we have no expectation they - # will ever be fixed - when: not ((ansible_distribution == "Fedora") and (ansible_distribution_major_version|int < 26)) - -- name: Check that packages with Provides are handled correctly in state=absent - block: - - name: Install test packages - yum: - name: - - https://ci-files.testing.ansible.com/test/integration/targets/yum/test-package-that-provides-toaster-1.3.3.7-1.el7.noarch.rpm - - https://ci-files.testing.ansible.com/test/integration/targets/yum/toaster-1.2.3.4-1.el7.noarch.rpm - disable_gpg_check: true - register: install - - - name: Remove toaster - yum: - name: toaster - state: absent - register: remove - - - name: rpm -qa - command: rpm -qa - register: rpmqa - - - assert: - that: - - install is successful - - install is changed - - remove is successful - - remove is changed - - "'toaster-1.2.3.4' not in rpmqa.stdout" - - "'test-package-that-provides-toaster' in rpmqa.stdout" - always: - - name: Remove test packages - yum: - name: - - test-package-that-provides-toaster - - toaster - state: absent - -- yum: - list: "{{ package1 }}" - register: list_out - -- name: check that both yum and dnf return envra - assert: - that: - - '"envra" in list_out["results"][0]' - -- name: check that dnf returns nevra for backwards compat - assert: - that: - - '"nevra" in list_out["results"][0]' - when: ansible_pkg_mgr == 'dnf' diff --git a/test/integration/targets/yum/tasks/yum_group_remove.yml b/test/integration/targets/yum/tasks/yum_group_remove.yml deleted file mode 100644 index 22c6dcb11c9..00000000000 --- a/test/integration/targets/yum/tasks/yum_group_remove.yml +++ /dev/null @@ -1,152 +0,0 @@ -- name: install a group to test and yum-utils - yum: - name: "{{ pkgs }}" - state: present - vars: - pkgs: - - "@Custom Group" - - yum-utils - when: ansible_pkg_mgr == "yum" - -- name: install a group to test and dnf-utils - yum: - name: "{{ pkgs }}" - state: present - vars: - pkgs: - - "@Custom Group" - - dnf-utils - when: ansible_pkg_mgr == "dnf" - -- name: check mode remove the group - yum: - name: "@Custom Group" - state: absent - check_mode: yes - register: yum_result - -- name: verify changed - assert: - that: - - "yum_result.changed" - -- name: verify yum module outputs - assert: - that: - - "'changed' in yum_result" - - "'results' in yum_result" - -- name: remove the group - yum: - name: "@Custom Group" - state: absent - register: yum_result - -- name: verify changed - assert: - that: - - "yum_result.rc == 0" - - "yum_result.changed" - -- name: verify yum module outputs - assert: - that: - - "'changed' in yum_result" - - "'msg' in yum_result" - - "'results' in yum_result" - -- name: remove the group again - yum: - name: "@Custom Group" - state: absent - register: yum_result - -- name: verify changed - assert: - that: - - "not yum_result.changed" - -- name: verify yum module outputs - assert: - that: - - "'changed' in yum_result" - - "'msg' in yum_result" - - "'results' in yum_result" - -- name: check mode remove the group again - yum: - name: "@Custom Group" - state: absent - check_mode: yes - register: yum_result - -- name: verify changed - assert: - that: - - "not yum_result.changed" - -- name: verify yum module outputs - assert: - that: - - "'changed' in yum_result" - - "'results' in yum_result" - -- name: install a group and a package to test - yum: - name: "@Custom Group,sos" - state: present - register: yum_output - -- name: check mode remove the group along with the package - yum: - name: "@Custom Group,sos" - state: absent - register: yum_result - check_mode: yes - -- name: verify changed - assert: - that: - - "yum_result.changed" - -- name: verify yum module outputs - assert: - that: - - "'changed' in yum_result" - - "'results' in yum_result" - -- name: remove the group along with the package - yum: - name: "@Custom Group,sos" - state: absent - register: yum_result - -- name: verify changed - assert: - that: - - "yum_result.changed" - -- name: verify yum module outputs - assert: - that: - - "'changed' in yum_result" - - "'msg' in yum_result" - - "'results' in yum_result" - -- name: check mode remove the group along with the package - yum: - name: "@Custom Group,sos" - state: absent - register: yum_result - check_mode: yes - -- name: verify not changed - assert: - that: - - "not yum_result.changed" - -- name: verify yum module outputs - assert: - that: - - "'changed' in yum_result" - - "'results' in yum_result" diff --git a/test/integration/targets/yum/tasks/yuminstallroot.yml b/test/integration/targets/yum/tasks/yuminstallroot.yml deleted file mode 100644 index 028e8059478..00000000000 --- a/test/integration/targets/yum/tasks/yuminstallroot.yml +++ /dev/null @@ -1,132 +0,0 @@ -# make a installroot -- name: Create installroot - command: mktemp -d "{{ remote_tmp_dir }}/ansible.test.XXXXXX" - register: yumroot - -#- name: Populate directory -# file: -# path: "/{{ yumroot.stdout }}/etc/" -# state: directory -# mode: 0755 -# -#- name: Populate directory2 -# copy: -# content: "[main]\ndistropkgver={{ ansible_distribution_version }}\n" -# dest: "/{{ yumroot.stdout }}/etc/yum.conf" - -- name: Make a necessary directory - file: - path: "{{ yumroot.stdout }}/etc/yum/vars/" - state: directory - mode: 0755 - -- name: get yum releasever - command: "{{ ansible_python_interpreter }} -c 'import yum; yb = yum.YumBase(); print(yb.conf.yumvar[\"releasever\"])'" - register: releasever - ignore_errors: yes - -- name: Populate directory - copy: - content: "{{ releasever.stdout_lines[-1] }}\n" - dest: "/{{ yumroot.stdout }}/etc/yum/vars/releasever" - when: releasever is successful - -# This will drag in > 200 MB. -- name: attempt installroot - yum: name=zlib installroot="{{ yumroot.stdout }}/" disable_gpg_check=yes - register: yum_result - -- name: check sos with rpm in installroot - shell: rpm -q zlib --root="{{ yumroot.stdout }}/" - failed_when: False - register: rpm_result - -- name: verify installation of sos - assert: - that: - - "yum_result.rc == 0" - - "yum_result.changed" - - "rpm_result.rc == 0" - -- name: verify yum module outputs - assert: - that: - - "'changed' in yum_result" - - "'msg' in yum_result" - - "'rc' in yum_result" - - "'results' in yum_result" - -- name: cleanup installroot - file: - path: "{{ yumroot.stdout }}/" - state: absent - -# Test for releasever working correctly -# -# Bugfix: https://github.com/ansible/ansible/issues/67050 -# -# This test case is based on a reproducer originally reported on Reddit: -# https://www.reddit.com/r/ansible/comments/g2ps32/ansible_yum_module_throws_up_an_error_when/ -# -# NOTE: For the Ansible upstream CI we can only run this for RHEL7 because the -# containerized runtimes in shippable don't allow the nested mounting of -# buildah container volumes. -- name: perform yuminstallroot in a buildah mount with releasever - when: - - ansible_facts["distribution_major_version"] == "7" - - ansible_facts["distribution"] == "RedHat" - block: - - name: install required packages for buildah test - yum: - state: present - name: - - buildah - - name: create buildah container from scratch - command: "buildah --name yum_installroot_releasever_test from scratch" - - name: mount the buildah container - command: "buildah mount yum_installroot_releasever_test" - register: buildah_mount - - name: figure out yum value of $releasever - shell: python -c 'import yum; yb = yum.YumBase(); print(yb.conf.yumvar["releasever"])' | tail -1 - register: buildah_host_releasever - - name: test yum install of python using releasever - yum: - name: 'python' - state: present - installroot: "{{ buildah_mount.stdout }}" - releasever: "{{ buildah_host_releasever.stdout }}" - register: yum_result - - name: verify installation of python - assert: - that: - - "yum_result.rc == 0" - - "yum_result.changed" - - "rpm_result.rc == 0" - - name: remove python before another test - yum: - name: 'python' - state: absent - installroot: "{{ buildah_mount.stdout }}" - releasever: "{{ buildah_host_releasever.stdout }}" - - name: test yum install of python using releasever with latest - yum: - name: 'python' - state: latest - installroot: "{{ buildah_mount.stdout }}" - releasever: "{{ buildah_host_releasever.stdout }}" - register: yum_result - - name: verify installation of python - assert: - that: - - "yum_result.rc == 0" - - "yum_result.changed" - - "rpm_result.rc == 0" - always: - - name: remove buildah container - command: "buildah rm yum_installroot_releasever_test" - ignore_errors: yes - - name: remove buildah from CI system - yum: - state: absent - name: - - buildah diff --git a/test/integration/targets/yum/vars/main.yml b/test/integration/targets/yum/vars/main.yml deleted file mode 100644 index a2a073f297e..00000000000 --- a/test/integration/targets/yum/vars/main.yml +++ /dev/null @@ -1 +0,0 @@ -multiarch_repo_baseurl: https://ci-files.testing.ansible.com/test/integration/targets/yum/multiarch-test-repo/RPMS/ diff --git a/test/lib/ansible_test/_util/controller/sanity/mypy/ansible-core.ini b/test/lib/ansible_test/_util/controller/sanity/mypy/ansible-core.ini index 0c10e68ae0b..0251f674b51 100644 --- a/test/lib/ansible_test/_util/controller/sanity/mypy/ansible-core.ini +++ b/test/lib/ansible_test/_util/controller/sanity/mypy/ansible-core.ini @@ -46,9 +46,6 @@ ignore_missing_imports = True [mypy-lxml.*] ignore_missing_imports = True -[mypy-yum.*] -ignore_missing_imports = True - [mypy-rpmUtils.*] ignore_missing_imports = True diff --git a/test/lib/ansible_test/_util/controller/sanity/mypy/modules.ini b/test/lib/ansible_test/_util/controller/sanity/mypy/modules.ini index fb3da5c26d4..b4e7b05eb9f 100644 --- a/test/lib/ansible_test/_util/controller/sanity/mypy/modules.ini +++ b/test/lib/ansible_test/_util/controller/sanity/mypy/modules.ini @@ -13,9 +13,6 @@ ignore_missing_imports = True [mypy-md5.*] ignore_missing_imports = True -[mypy-yum.*] -ignore_missing_imports = True - [mypy-rpmUtils.*] ignore_missing_imports = True diff --git a/test/sanity/ignore.txt b/test/sanity/ignore.txt index a8dbf19e82d..30fd4687f16 100644 --- a/test/sanity/ignore.txt +++ b/test/sanity/ignore.txt @@ -47,7 +47,6 @@ lib/ansible/modules/systemd_service.py validate-modules:parameter-invalid lib/ansible/modules/uri.py validate-modules:doc-required-mismatch lib/ansible/modules/user.py validate-modules:doc-default-does-not-match-spec lib/ansible/modules/user.py validate-modules:use-run-command-not-popen -lib/ansible/modules/yum.py validate-modules:parameter-invalid lib/ansible/module_utils/basic.py no-get-exception # only referenced in deprecation code lib/ansible/module_utils/basic.py pylint:unused-import # deferring resolution to allow enabling the rule now lib/ansible/module_utils/compat/selinux.py import-3.7!skip # pass/fail depends on presence of libselinux.so diff --git a/test/units/module_utils/facts/system/test_pkg_mgr.py b/test/units/module_utils/facts/system/test_pkg_mgr.py index bc7da5e4a98..2053d2722f9 100644 --- a/test/units/module_utils/facts/system/test_pkg_mgr.py +++ b/test/units/module_utils/facts/system/test_pkg_mgr.py @@ -7,43 +7,28 @@ from __future__ import annotations from ansible.module_utils.facts.system.pkg_mgr import PkgMgrFactCollector -_FEDORA_FACTS = { - "ansible_distribution": "Fedora", - "ansible_distribution_major_version": 38, # any version where yum isn't default - "ansible_os_family": "RedHat" -} - -_KYLIN_FACTS = { - "ansible_distribution": "Kylin Linux Advanced Server", - "ansible_distribution_major_version": "V10", - "ansible_os_family": "RedHat" -} - -# NOTE pkg_mgr == "dnf" means the dnf module for the dnf 4 or below +_FACTS = {"ansible_os_family": "RedHat"} -def test_default_dnf_version_detection_kylin_dnf4(mocker): - mocker.patch("os.path.exists", lambda p: p in ("/usr/bin/dnf", "/usr/bin/dnf-3")) - mocker.patch("os.path.realpath", lambda p: {"/usr/bin/dnf": "/usr/bin/dnf-3"}.get(p, p)) - assert PkgMgrFactCollector().collect(collected_facts=_KYLIN_FACTS).get("pkg_mgr") == "dnf" +# NOTE pkg_mgr == "dnf" means the dnf module for the dnf 4 or below def test_default_dnf_version_detection_fedora_dnf4(mocker): mocker.patch("os.path.exists", lambda p: p in ("/usr/bin/dnf", "/usr/bin/dnf-3")) mocker.patch("os.path.realpath", lambda p: {"/usr/bin/dnf": "/usr/bin/dnf-3"}.get(p, p)) - assert PkgMgrFactCollector().collect(collected_facts=_FEDORA_FACTS).get("pkg_mgr") == "dnf" + assert PkgMgrFactCollector().collect(collected_facts=_FACTS).get("pkg_mgr") == "dnf" def test_default_dnf_version_detection_fedora_dnf5(mocker): mocker.patch("os.path.exists", lambda p: p in ("/usr/bin/dnf", "/usr/bin/dnf5")) mocker.patch("os.path.realpath", lambda p: {"/usr/bin/dnf": "/usr/bin/dnf5"}.get(p, p)) - assert PkgMgrFactCollector().collect(collected_facts=_FEDORA_FACTS).get("pkg_mgr") == "dnf5" + assert PkgMgrFactCollector().collect(collected_facts=_FACTS).get("pkg_mgr") == "dnf5" def test_default_dnf_version_detection_fedora_dnf4_both_installed(mocker): mocker.patch("os.path.exists", lambda p: p in ("/usr/bin/dnf", "/usr/bin/dnf-3", "/usr/bin/dnf5")) mocker.patch("os.path.realpath", lambda p: {"/usr/bin/dnf": "/usr/bin/dnf-3"}.get(p, p)) - assert PkgMgrFactCollector().collect(collected_facts=_FEDORA_FACTS).get("pkg_mgr") == "dnf" + assert PkgMgrFactCollector().collect(collected_facts=_FACTS).get("pkg_mgr") == "dnf" def test_default_dnf_version_detection_fedora_dnf4_microdnf5_installed(mocker): @@ -55,20 +40,20 @@ def test_default_dnf_version_detection_fedora_dnf4_microdnf5_installed(mocker): "os.path.realpath", lambda p: {"/usr/bin/dnf": "/usr/bin/dnf-3", "/usr/bin/microdnf": "/usr/bin/dnf5"}.get(p, p) ) - assert PkgMgrFactCollector().collect(collected_facts=_FEDORA_FACTS).get("pkg_mgr") == "dnf" + assert PkgMgrFactCollector().collect(collected_facts=_FACTS).get("pkg_mgr") == "dnf" def test_default_dnf_version_detection_fedora_dnf4_microdnf(mocker): mocker.patch("os.path.exists", lambda p: p == "/usr/bin/microdnf") - assert PkgMgrFactCollector().collect(collected_facts=_FEDORA_FACTS).get("pkg_mgr") == "dnf" + assert PkgMgrFactCollector().collect(collected_facts=_FACTS).get("pkg_mgr") == "dnf" def test_default_dnf_version_detection_fedora_dnf5_microdnf(mocker): mocker.patch("os.path.exists", lambda p: p in ("/usr/bin/microdnf", "/usr/bin/dnf5")) mocker.patch("os.path.realpath", lambda p: {"/usr/bin/microdnf": "/usr/bin/dnf5"}.get(p, p)) - assert PkgMgrFactCollector().collect(collected_facts=_FEDORA_FACTS).get("pkg_mgr") == "dnf5" + assert PkgMgrFactCollector().collect(collected_facts=_FACTS).get("pkg_mgr") == "dnf5" def test_default_dnf_version_detection_fedora_no_default(mocker): mocker.patch("os.path.exists", lambda p: p in ("/usr/bin/dnf-3", "/usr/bin/dnf5")) - assert PkgMgrFactCollector().collect(collected_facts=_FEDORA_FACTS).get("pkg_mgr") == "unknown" + assert PkgMgrFactCollector().collect(collected_facts=_FACTS).get("pkg_mgr") == "unknown" diff --git a/test/units/modules/test_yum.py b/test/units/modules/test_yum.py deleted file mode 100644 index 21a7edcdb55..00000000000 --- a/test/units/modules/test_yum.py +++ /dev/null @@ -1,221 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import annotations - -import unittest - -from ansible.modules.yum import YumModule - - -yum_plugin_load_error = """ -Plugin "product-id" can't be imported -Plugin "search-disabled-repos" can't be imported -Plugin "subscription-manager" can't be imported -Plugin "product-id" can't be imported -Plugin "search-disabled-repos" can't be imported -Plugin "subscription-manager" can't be imported -""" - -# from https://github.com/ansible/ansible/issues/20608#issuecomment-276106505 -wrapped_output_1 = """ -Загружены модули: fastestmirror -Loading mirror speeds from cached hostfile - * base: mirror.h1host.ru - * extras: mirror.h1host.ru - * updates: mirror.h1host.ru - -vms-agent.x86_64 0.0-9 dev -""" - -# from https://github.com/ansible/ansible/issues/20608#issuecomment-276971275 -wrapped_output_2 = """ -Загружены модули: fastestmirror -Loading mirror speeds from cached hostfile - * base: mirror.corbina.net - * extras: mirror.corbina.net - * updates: mirror.corbina.net - -empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty.x86_64 - 0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1-0 - addons -libtiff.x86_64 4.0.3-27.el7_3 updates -""" - -# From https://github.com/ansible/ansible/issues/20608#issuecomment-276698431 -wrapped_output_3 = """ -Loaded plugins: fastestmirror, langpacks -Loading mirror speeds from cached hostfile - -ceph.x86_64 1:11.2.0-0.el7 ceph -ceph-base.x86_64 1:11.2.0-0.el7 ceph -ceph-common.x86_64 1:11.2.0-0.el7 ceph -ceph-mds.x86_64 1:11.2.0-0.el7 ceph -ceph-mon.x86_64 1:11.2.0-0.el7 ceph -ceph-osd.x86_64 1:11.2.0-0.el7 ceph -ceph-selinux.x86_64 1:11.2.0-0.el7 ceph -libcephfs1.x86_64 1:11.0.2-0.el7 ceph -librados2.x86_64 1:11.2.0-0.el7 ceph -libradosstriper1.x86_64 1:11.2.0-0.el7 ceph -librbd1.x86_64 1:11.2.0-0.el7 ceph -librgw2.x86_64 1:11.2.0-0.el7 ceph -python-cephfs.x86_64 1:11.2.0-0.el7 ceph -python-rados.x86_64 1:11.2.0-0.el7 ceph -python-rbd.x86_64 1:11.2.0-0.el7 ceph -""" - -# from https://github.com/ansible/ansible-modules-core/issues/4318#issuecomment-251416661 -wrapped_output_4 = """ -ipxe-roms-qemu.noarch 20160127-1.git6366fa7a.el7 - rhelosp-9.0-director-puddle -quota.x86_64 1:4.01-11.el7_2.1 rhelosp-rhel-7.2-z -quota-nls.noarch 1:4.01-11.el7_2.1 rhelosp-rhel-7.2-z -rdma.noarch 7.2_4.1_rc6-2.el7 rhelosp-rhel-7.2-z -screen.x86_64 4.1.0-0.23.20120314git3c2946.el7_2 - rhelosp-rhel-7.2-z -sos.noarch 3.2-36.el7ost.2 rhelosp-9.0-puddle -sssd-client.x86_64 1.13.0-40.el7_2.12 rhelosp-rhel-7.2-z -""" - - -# A 'normal-ish' yum check-update output, without any wrapped lines -unwrapped_output_rhel7 = """ - -Loaded plugins: etckeeper, product-id, search-disabled-repos, subscription- - : manager -This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register. - -NetworkManager-openvpn.x86_64 1:1.2.6-1.el7 epel -NetworkManager-openvpn-gnome.x86_64 1:1.2.6-1.el7 epel -cabal-install.x86_64 1.16.1.0-2.el7 epel -cgit.x86_64 1.1-1.el7 epel -python34-libs.x86_64 3.4.5-3.el7 epel -python34-test.x86_64 3.4.5-3.el7 epel -python34-tkinter.x86_64 3.4.5-3.el7 epel -python34-tools.x86_64 3.4.5-3.el7 epel -qgit.x86_64 2.6-4.el7 epel -rdiff-backup.x86_64 1.2.8-12.el7 epel -stoken-libs.x86_64 0.91-1.el7 epel -xlockmore.x86_64 5.49-2.el7 epel -""" - -# Some wrapped obsoletes for prepending to output for testing both -wrapped_output_rhel7_obsoletes_postfix = """ -Obsoleting Packages -ddashboard.x86_64 0.2.0.1-1.el7_3 mhlavink-developerdashboard - developerdashboard.x86_64 0.1.12.2-1.el7_2 @mhlavink-developerdashboard -python-bugzilla.noarch 1.2.2-3.el7_2.1 mhlavink-developerdashboard - python-bugzilla-develdashboardfixes.noarch - 1.2.2-3.el7 @mhlavink-developerdashboard -python2-futures.noarch 3.0.5-1.el7 epel - python-futures.noarch 3.0.3-1.el7 @epel -python2-pip.noarch 8.1.2-5.el7 epel - python-pip.noarch 7.1.0-1.el7 @epel -python2-pyxdg.noarch 0.25-6.el7 epel - pyxdg.noarch 0.25-5.el7 @epel -python2-simplejson.x86_64 3.10.0-1.el7 epel - python-simplejson.x86_64 3.3.3-1.el7 @epel -Security: kernel-3.10.0-327.28.2.el7.x86_64 is an installed security update -Security: kernel-3.10.0-327.22.2.el7.x86_64 is the currently running version -""" - -wrapped_output_multiple_empty_lines = """ -Loaded plugins: langpacks, product-id, search-disabled-repos, subscription-manager - -This system is not registered with an entitlement server. You can use subscription-manager to register. - - -screen.x86_64 4.1.0-0.23.20120314git3c2946.el7_2 - rhelosp-rhel-7.2-z -sos.noarch 3.2-36.el7ost.2 rhelosp-9.0-puddle -""" - -longname = """ -Loaded plugins: fastestmirror, priorities, rhnplugin -This system is receiving updates from RHN Classic or Red Hat Satellite. -Loading mirror speeds from cached hostfile - -xxxxxxxxxxxxxxxxxxxxxxxxxx.noarch - 1.16-1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -glibc.x86_64 2.17-157.el7_3.1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx""" - - -unwrapped_output_rhel7_obsoletes = unwrapped_output_rhel7 + wrapped_output_rhel7_obsoletes_postfix -unwrapped_output_rhel7_expected_new_obsoletes_pkgs = [ - "ddashboard", "python-bugzilla", "python2-futures", "python2-pip", - "python2-pyxdg", "python2-simplejson" -] -unwrapped_output_rhel7_expected_old_obsoletes_pkgs = [ - "developerdashboard", "python-bugzilla-develdashboardfixes", - "python-futures", "python-pip", "pyxdg", "python-simplejson" -] -unwrapped_output_rhel7_expected_updated_pkgs = [ - "NetworkManager-openvpn", "NetworkManager-openvpn-gnome", "cabal-install", - "cgit", "python34-libs", "python34-test", "python34-tkinter", - "python34-tools", "qgit", "rdiff-backup", "stoken-libs", "xlockmore" -] - - -class TestYumUpdateCheckParse(unittest.TestCase): - def _assert_expected(self, expected_pkgs, result): - - for expected_pkg in expected_pkgs: - self.assertIn(expected_pkg, result) - self.assertEqual(len(result), len(expected_pkgs)) - self.assertIsInstance(result, dict) - - def test_empty_output(self): - res, obs = YumModule.parse_check_update("") - expected_pkgs = [] - self._assert_expected(expected_pkgs, res) - - def test_longname(self): - res, obs = YumModule.parse_check_update(longname) - expected_pkgs = ['xxxxxxxxxxxxxxxxxxxxxxxxxx', 'glibc'] - self._assert_expected(expected_pkgs, res) - - def test_plugin_load_error(self): - res, obs = YumModule.parse_check_update(yum_plugin_load_error) - expected_pkgs = [] - self._assert_expected(expected_pkgs, res) - - def test_wrapped_output_1(self): - res, obs = YumModule.parse_check_update(wrapped_output_1) - expected_pkgs = ["vms-agent"] - self._assert_expected(expected_pkgs, res) - - def test_wrapped_output_2(self): - res, obs = YumModule.parse_check_update(wrapped_output_2) - expected_pkgs = ["empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty-empty", - "libtiff"] - - self._assert_expected(expected_pkgs, res) - - def test_wrapped_output_3(self): - res, obs = YumModule.parse_check_update(wrapped_output_3) - expected_pkgs = ["ceph", "ceph-base", "ceph-common", "ceph-mds", - "ceph-mon", "ceph-osd", "ceph-selinux", "libcephfs1", - "librados2", "libradosstriper1", "librbd1", "librgw2", - "python-cephfs", "python-rados", "python-rbd"] - self._assert_expected(expected_pkgs, res) - - def test_wrapped_output_4(self): - res, obs = YumModule.parse_check_update(wrapped_output_4) - - expected_pkgs = ["ipxe-roms-qemu", "quota", "quota-nls", "rdma", "screen", - "sos", "sssd-client"] - self._assert_expected(expected_pkgs, res) - - def test_wrapped_output_rhel7(self): - res, obs = YumModule.parse_check_update(unwrapped_output_rhel7) - self._assert_expected(unwrapped_output_rhel7_expected_updated_pkgs, res) - - def test_wrapped_output_rhel7_obsoletes(self): - res, obs = YumModule.parse_check_update(unwrapped_output_rhel7_obsoletes) - self._assert_expected( - unwrapped_output_rhel7_expected_updated_pkgs + unwrapped_output_rhel7_expected_new_obsoletes_pkgs, - res - ) - self._assert_expected(unwrapped_output_rhel7_expected_old_obsoletes_pkgs, obs) - - def test_wrapped_output_multiple_empty_lines(self): - res, obs = YumModule.parse_check_update(wrapped_output_multiple_empty_lines) - self._assert_expected(['screen', 'sos'], res)