diff --git a/changelog/66856.fixed.md b/changelog/66856.fixed.md new file mode 100644 index 000000000000..22800e3b4fe4 --- /dev/null +++ b/changelog/66856.fixed.md @@ -0,0 +1 @@ +Better handling output of `systemctl --version` with salt.grains.core._systemd diff --git a/salt/grains/core.py b/salt/grains/core.py index 119e78c8e9bb..b7f99c0fa526 100644 --- a/salt/grains/core.py +++ b/salt/grains/core.py @@ -2517,10 +2517,31 @@ def _systemd(): """ Return the systemd grain """ - systemd_info = __salt__["cmd.run"]("systemctl --version").splitlines() + systemd_version = "UNDEFINED" + systemd_features = "" + try: + systemd_output = __salt__["cmd.run_all"]("systemctl --version") + except Exception: # pylint: disable=broad-except + log.error("Exception while executing `systemctl --version`", exc_info=True) + return { + "version": systemd_version, + "features": systemd_features, + } + if systemd_output.get("retcode") == 0: + systemd_info = systemd_output.get("stdout", "").splitlines() + try: + if systemd_info[0].startswith("systemd "): + systemd_version = systemd_info[0].split()[1] + systemd_features = systemd_info[1] + except IndexError: + pass + if systemd_version == "UNDEFINED" or systemd_features == "": + log.error( + "Unexpected output returned by `systemctl --version`: %s", systemd_output + ) return { - "version": systemd_info[0].split()[1], - "features": systemd_info[1], + "version": systemd_version, + "features": systemd_features, } diff --git a/tests/pytests/unit/grains/test_core.py b/tests/pytests/unit/grains/test_core.py index 03d55ede45e5..73574bbd0e56 100644 --- a/tests/pytests/unit/grains/test_core.py +++ b/tests/pytests/unit/grains/test_core.py @@ -4188,34 +4188,93 @@ def test__selinux(): assert ret == {"enabled": True, "enforced": "Disabled"} -def test__systemd(): +@pytest.mark.parametrize( + "systemd_data,expected", + ( + ( + { + "pid": 1234, + "retcode": 0, + "stdout": "systemd 254 (254.3-1)\n+PAM +AUDIT -SELINUX -APPARMOR -IMA +SMACK " + "+SECCOMP +GCRYPT +GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS " + "+FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 " + "-PWQUALITY +P11KIT -QRENCODE +TPM2 +BZIP2 +LZ4 +XZ +ZLIB +ZSTD " + "+BPF_FRAMEWORK +XKBCOMMON +UTMP -SYSVINIT default-hierarchy=unified", + "stderr": "", + }, + { + "version": "254", + "features": "+PAM +AUDIT -SELINUX -APPARMOR -IMA +SMACK +SECCOMP +GCRYPT +GNUTLS +OPENSSL " + "+ACL +BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP " + "+LIBFDISK +PCRE2 -PWQUALITY +P11KIT -QRENCODE +TPM2 +BZIP2 +LZ4 +XZ " + "+ZLIB +ZSTD +BPF_FRAMEWORK +XKBCOMMON +UTMP -SYSVINIT default-hierarchy=unified", + }, + ), + ( + { + "pid": 2345, + "retcode": 1, + "stdout": "", + "stderr": "some garbage in the output", + }, + { + "version": "UNDEFINED", + "features": "", + }, + ), + ( + { + "pid": 3456, + "retcode": 0, + "stdout": "unexpected stdout\none more line", + "stderr": "", + }, + { + "version": "UNDEFINED", + "features": "", + }, + ), + ( + { + "pid": 4567, + "retcode": 0, + "stdout": "", + "stderr": "", + }, + { + "version": "UNDEFINED", + "features": "", + }, + ), + ( + Exception("Some exception on calling `systemctl --version`"), + { + "version": "UNDEFINED", + "features": "", + }, + ), + ), +) +def test__systemd(systemd_data, expected): """ test _systemd """ + + def mock_run_all_systemd(_): + if isinstance(systemd_data, Exception): + raise systemd_data + return systemd_data + with patch.dict( core.__salt__, { - "cmd.run": MagicMock( - return_value=( - "systemd 254 (254.3-1)\n+PAM +AUDIT -SELINUX -APPARMOR -IMA +SMACK " - "+SECCOMP +GCRYPT +GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS " - "+FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 " - "-PWQUALITY +P11KIT -QRENCODE +TPM2 +BZIP2 +LZ4 +XZ +ZLIB +ZSTD " - "+BPF_FRAMEWORK +XKBCOMMON +UTMP -SYSVINIT default-hierarchy=unified" - ) - ), + "cmd.run_all": mock_run_all_systemd, }, ): ret = core._systemd() assert "version" in ret assert "features" in ret - assert ret["version"] == "254" - assert ret["features"] == ( - "+PAM +AUDIT -SELINUX -APPARMOR -IMA +SMACK +SECCOMP +GCRYPT +GNUTLS +OPENSSL " - "+ACL +BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP " - "+LIBFDISK +PCRE2 -PWQUALITY +P11KIT -QRENCODE +TPM2 +BZIP2 +LZ4 +XZ " - "+ZLIB +ZSTD +BPF_FRAMEWORK +XKBCOMMON +UTMP -SYSVINIT default-hierarchy=unified" - ) + assert ret == expected def test__clean_value_uuid(caplog):