diff --git a/changelogs/fragments/pkg_mgr_peek.yml b/changelogs/fragments/pkg_mgr_peek.yml new file mode 100644 index 00000000000..0a584b315e1 --- /dev/null +++ b/changelogs/fragments/pkg_mgr_peek.yml @@ -0,0 +1,2 @@ +minor_changes: + - package action now has a configuration that overrides the detected package manager, it is still overridden itself by the use option. diff --git a/lib/ansible/modules/package.py b/lib/ansible/modules/package.py index 545a394a43b..54d88999efa 100644 --- a/lib/ansible/modules/package.py +++ b/lib/ansible/modules/package.py @@ -28,7 +28,7 @@ options: description: - Package name, or package specifier with version. - Syntax varies with package manager. For example V(name-1.0) or V(name=1.0). - - Package names also vary with package manager; this module will not "translate" them per distro. For example V(libyaml-dev), V(libyaml-devel). + - Package names also vary with package manager; this module will not "translate" them per distribution. For example V(libyaml-dev), V(libyaml-devel). - To operate on several packages this can accept a comma separated string of packages or a list of packages, depending on the underlying package manager. required: true state: @@ -38,8 +38,9 @@ options: required: true use: description: - - 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. + - 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 auto-detect it. - You should only use this field if the automatic selection is not working for some reason. + - Since version 2.17 you can use the C(ansible_package_use) variable to override the automatic detection, but this option still takes precedence. default: auto requirements: - Whatever is required for the package plugins specific for each system. diff --git a/lib/ansible/plugins/action/package.py b/lib/ansible/plugins/action/package.py index e4f91fd8c52..6963b770c47 100644 --- a/lib/ansible/plugins/action/package.py +++ b/lib/ansible/plugins/action/package.py @@ -21,6 +21,7 @@ from ansible.executor.module_common import get_action_args_with_defaults from ansible.module_utils.facts.system.pkg_mgr import PKG_MGRS from ansible.plugins.action import ActionBase from ansible.utils.display import Display +from ansible.utils.vars import combine_vars display = Display() @@ -38,31 +39,46 @@ class ActionModule(ActionBase): self._supports_async = True result = super(ActionModule, self).run(tmp, task_vars) - del tmp # tmp no longer has any effect module = self._task.args.get('use', '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! - try: if module == 'auto': - 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 module != 'auto': + + if self._task.delegate_to: + hosts_vars = task_vars['hostvars'][self._task.delegate_to] + tvars = combine_vars(self._task.vars, task_vars.get('delegated_vars', {})) + else: + hosts_vars = task_vars + tvars = task_vars + + # use config + module = tvars.get('ansible_package_use', None) + + if not module: + # no use, no config, get from facts + if hosts_vars.get('ansible_facts', {}).get('pkg_mgr', False): + facts = hosts_vars + pmgr = 'pkg_mgr' + else: + # we had no facts, so generate them + # very expensive step, we actually run fact gathering because we don't have facts for this host. + facts = self._execute_module( + module_name='ansible.legacy.setup', + module_args=dict(filter='ansible_pkg_mgr', gather_subset='!all'), + task_vars=task_vars, + ) + pmgr = 'ansible_pkg_mgr' + + try: + # actually get from facts + module = facts['ansible_facts'][pmgr] + except KeyError: + raise AnsibleActionFail('Could not detect a package manager. Try using the "use" option.') + + if module and module != 'auto': if not self._shared_loader_obj.module_loader.has_plugin(module): - raise AnsibleActionFail('Could not find a module for %s.' % module) + raise AnsibleActionFail('Could not find a matching action for the "%s" package manager.' % module) else: # run the 'package' module new_module_args = self._task.args.copy() diff --git a/test/integration/targets/package/tasks/main.yml b/test/integration/targets/package/tasks/main.yml index d1934fc3701..119474afdf1 100644 --- a/test/integration/targets/package/tasks/main.yml +++ b/test/integration/targets/package/tasks/main.yml @@ -240,3 +240,19 @@ - "result is changed" when: ansible_distribution == "Fedora" + + +- name: test ansible_package_use + block: + - name: test with var + package: + name: doesnotmatter + vars: + ansible_package_use: lola + ignore_errors: true + register: use_to_use + + - assert: + that: + - use_to_use is failed + - "'Could not find a matching action' in use_to_use['msg']"