From 136a6eec9ede9e82fcc4d37821e84e653dcde29b Mon Sep 17 00:00:00 2001 From: Alberto Murillo Silva Date: Mon, 16 Jan 2017 20:42:23 -0600 Subject: [PATCH] dnf: Add autoremove option This allow users to write better playbooks by replacing - shell: dnf autoremove -y with - dnf: autoremove=yes Fixes #18815 Signed-off-by: Alberto Murillo Silva --- lib/ansible/modules/packaging/os/dnf.py | 59 ++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/lib/ansible/modules/packaging/os/dnf.py b/lib/ansible/modules/packaging/os/dnf.py index 797dc1c8f9..d49d7a6472 100644 --- a/lib/ansible/modules/packaging/os/dnf.py +++ b/lib/ansible/modules/packaging/os/dnf.py @@ -97,7 +97,16 @@ options: version_added: "2.3" default: "/" -notes: [] + autoremove: + description: + - If C(yes), 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 state is I(absent) + required: false + choices: [ "yes", "no" ] + version_added: "2.4" + +notes: ["autoremove requires dnf >= 2.0.1"] # informational: requirements for nodes requirements: - "python >= 2.6" @@ -144,11 +153,20 @@ EXAMPLES = ''' dnf: name: '@Development tools' state: present + +- name: Autoremove unneeded packages installed as dependencies + dnf: + autoremove: yes + +- name: Uninstall httpd but keep its dependencies + dnf: + name: httpd + state: absent + autoremove: no ''' import os try: - import dnf import dnf import dnf.cli import dnf.const @@ -161,6 +179,7 @@ except ImportError: from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.six import PY2 +from distutils.version import LooseVersion def _ensure_dnf(module): @@ -314,11 +333,18 @@ def _install_remote_rpms(base, filenames): base.package_install(pkg) -def ensure(module, base, state, names): +def ensure(module, base, state, names, autoremove): # Accumulate failures. Package management modules install what they can # and fail with a message about what they can't. failures = [] allow_erasing = False + + # Autoremove is called alone + # Jump to remove path where base.autoremove() is run + if not names and autoremove is not None: + names = [] + state = 'absent' + if names == ['*'] and state == 'latest': base.upgrade_all() else: @@ -398,6 +424,9 @@ def ensure(module, base, state, names): else: # state == absent + if autoremove is not None: + base.conf.clean_requirements_on_remove = autoremove + if filenames: module.fail_json( msg="Cannot remove paths -- please specify package name.") @@ -425,6 +454,9 @@ def ensure(module, base, state, names): # packages allow_erasing = True + if autoremove: + base.autoremove() + if not base.resolve(allow_erasing=allow_erasing): if failures: module.fail_json(msg='Failed to install some of the ' @@ -460,7 +492,6 @@ def main(): argument_spec=dict( name=dict(aliases=['pkg'], type='list'), state=dict( - default='installed', choices=[ 'absent', 'present', 'installed', 'removed', 'latest']), enablerepo=dict(type='list', default=[]), @@ -469,14 +500,28 @@ def main(): conf_file=dict(default=None, type='path'), disable_gpg_check=dict(default=False, type='bool'), installroot=dict(default='/', type='path'), + autoremove=dict(type='bool'), ), - required_one_of=[['name', 'list']], - mutually_exclusive=[['name', 'list']], + required_one_of=[['name', 'list', 'autoremove']], + mutually_exclusive=[['name', 'list'], ['autoremove', 'list']], supports_check_mode=True) params = module.params _ensure_dnf(module) + # Check if autoremove is called correctly + if params['autoremove'] is not None: + if LooseVersion(dnf.__version__) < LooseVersion('2.0.1'): + module.fail_json(msg="Autoremove requires dnf>=2.0.1. Current dnf version is %s" % dnf.__version__) + if params['state'] not in ["absent", None]: + module.fail_json(msg="Autoremove should be used alone or with state=absent") + + # Set state as installed by default + # This is not set in AnsibleModule() because the following shouldn't happend + # - dnf: autoremove=yes state=installed + if params['state'] is None: + params['state'] = 'installed' + if params['list']: base = _base( module, params['conf_file'], params['disable_gpg_check'], @@ -491,7 +536,7 @@ def main(): module, params['conf_file'], params['disable_gpg_check'], params['disablerepo'], params['enablerepo'], params['installroot']) - ensure(module, base, params['state'], params['name']) + ensure(module, base, params['state'], params['name'], params['autoremove']) if __name__ == '__main__':