From b70248eb2207d53f371d6ef4bafebd1637fd1f82 Mon Sep 17 00:00:00 2001 From: Abhijeet Kasurde Date: Wed, 15 May 2024 19:32:22 -0700 Subject: [PATCH] Warn if the binary is unavailable using get_bin_path (#83258) Signed-off-by: Abhijeet Kasurde --- lib/ansible/module_utils/basic.py | 2 +- .../module_utils/facts/hardware/linux.py | 41 +++++++++++-------- .../module_utils/basic/test_get_bin_path.py | 19 +++++++++ .../facts/hardware/test_linux_get_cpu_info.py | 2 +- 4 files changed, 44 insertions(+), 20 deletions(-) create mode 100644 test/units/module_utils/basic/test_get_bin_path.py diff --git a/lib/ansible/module_utils/basic.py b/lib/ansible/module_utils/basic.py index 83cbf9e5861..bed2aaef260 100644 --- a/lib/ansible/module_utils/basic.py +++ b/lib/ansible/module_utils/basic.py @@ -1367,7 +1367,7 @@ class AnsibleModule(object): if required: self.fail_json(msg=to_text(e)) elif warning is not None: - self.module.warn("Unable to find %s, %s" % (arg, warning)) + self.warn("Unable to find %s, %s" % (arg, warning)) return bin_path diff --git a/lib/ansible/module_utils/facts/hardware/linux.py b/lib/ansible/module_utils/facts/hardware/linux.py index a499cdee5c4..55d48c224f0 100644 --- a/lib/ansible/module_utils/facts/hardware/linux.py +++ b/lib/ansible/module_utils/facts/hardware/linux.py @@ -29,7 +29,6 @@ from multiprocessing.pool import ThreadPool from ansible.module_utils.common.text.converters import to_text from ansible.module_utils.common.locale import get_best_parsable_locale -from ansible.module_utils.common.process import get_bin_path from ansible.module_utils.common.text.formatters import bytes_to_human from ansible.module_utils.facts.hardware.base import Hardware, HardwareCollector from ansible.module_utils.facts.utils import get_file_content, get_file_lines, get_mount_size @@ -302,12 +301,9 @@ class LinuxHardware(Hardware): ) except AttributeError: # In Python < 3.3, os.sched_getaffinity() is not available - try: - cmd = get_bin_path('nproc') - except ValueError: - pass - else: - rc, out, _err = self.module.run_command(cmd) + nproc_cmd = self.module.get_bin_path('nproc', warning="skipping processor_nproc") + if nproc_cmd is not None: + rc, out, _err = self.module.run_command(nproc_cmd) if rc == 0: cpu_facts['processor_nproc'] = int(out) @@ -372,7 +368,6 @@ class LinuxHardware(Hardware): else: # Fall back to using dmidecode, if available - dmi_bin = self.module.get_bin_path('dmidecode') DMI_DICT = { 'bios_date': 'bios-release-date', 'bios_vendor': 'bios-vendor', @@ -393,6 +388,10 @@ class LinuxHardware(Hardware): 'product_version': 'system-version', 'system_vendor': 'system-manufacturer', } + dmi_bin = self.module.get_bin_path( + 'dmidecode', + warning="skipping dmi facts" + ) if dmi_bin is None: dmi_facts = dict.fromkeys( DMI_DICT.keys(), @@ -863,21 +862,27 @@ class LinuxHardware(Hardware): """ Get LVM Facts if running as root and lvm utils are available """ lvm_facts = {'lvm': 'N/A'} + vgs_cmd = self.module.get_bin_path( + 'vgs', + warning="skipping LVM facts" + ) + if vgs_cmd is None: + return lvm_facts - if os.getuid() == 0 and self.module.get_bin_path('vgs'): + if os.getuid() == 0: lvm_util_options = '--noheadings --nosuffix --units g --separator ,' - vgs_path = self.module.get_bin_path('vgs') # vgs fields: VG #PV #LV #SN Attr VSize VFree vgs = {} - if vgs_path: - rc, vg_lines, err = self.module.run_command('%s %s' % (vgs_path, lvm_util_options)) - for vg_line in vg_lines.splitlines(): - items = vg_line.strip().split(',') - vgs[items[0]] = {'size_g': items[-2], - 'free_g': items[-1], - 'num_lvs': items[2], - 'num_pvs': items[1]} + rc, vg_lines, err = self.module.run_command('%s %s' % (vgs_cmd, lvm_util_options)) + for vg_line in vg_lines.splitlines(): + items = vg_line.strip().split(',') + vgs[items[0]] = { + 'size_g': items[-2], + 'free_g': items[-1], + 'num_lvs': items[2], + 'num_pvs': items[1] + } lvs_path = self.module.get_bin_path('lvs') # lvs fields: diff --git a/test/units/module_utils/basic/test_get_bin_path.py b/test/units/module_utils/basic/test_get_bin_path.py new file mode 100644 index 00000000000..bac656ea9c0 --- /dev/null +++ b/test/units/module_utils/basic/test_get_bin_path.py @@ -0,0 +1,19 @@ +# Copyright: Contributors to the Ansible project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import annotations + +import json + +import pytest + + +@pytest.mark.parametrize("stdin", [{}], indirect=["stdin"]) +def test_get_bin_path_warning(am, capfd): + am.get_bin_path("non_existent_cmd", warning="skipping non_existent_cmd") + + with pytest.raises(SystemExit): + am.exit_json() + out, dummy = capfd.readouterr() + expected_warning = ["Unable to find non_existent_cmd, skipping non_existent_cmd"] + assert json.loads(out)["warnings"] == expected_warning diff --git a/test/units/module_utils/facts/hardware/test_linux_get_cpu_info.py b/test/units/module_utils/facts/hardware/test_linux_get_cpu_info.py index e0d350b65e4..204e3fa2374 100644 --- a/test/units/module_utils/facts/hardware/test_linux_get_cpu_info.py +++ b/test/units/module_utils/facts/hardware/test_linux_get_cpu_info.py @@ -33,7 +33,7 @@ def test_get_cpu_info_nproc(mocker): for test in CPU_INFO_TEST_SCENARIOS: mocker.patch('ansible.module_utils.facts.hardware.linux.get_file_lines', side_effect=[[], test['cpuinfo']]) mocker.patch('os.sched_getaffinity', create=True, side_effect=AttributeError) - mocker.patch('ansible.module_utils.facts.hardware.linux.get_bin_path', return_value='/usr/bin/nproc') + mocker.patch.object(module, 'get_bin_path', return_value='/usr/bin/nproc') module.run_command.return_value = (0, test['nproc_out'], '') collected_facts = {'ansible_architecture': test['architecture']}