summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Loos <cloos@netsandbox.de>2022-11-23 20:44:15 +0100
committerGitHub <noreply@github.com>2022-11-23 14:44:15 -0500
commitf79a54ae22b59d4c9bab0fb71d95c63b2e4b834b (patch)
tree4138ee8e2d5c1175daadfbe794dc2da859d0ce36
parent505b29b2a981eabb2dd84bc66d37704bab91c3f9 (diff)
downloadansible-f79a54ae22b59d4c9bab0fb71d95c63b2e4b834b.tar.gz
Update vendored distro (#79227)
Commit bb35d41 in branch python2.7-support from 2022-10-10: https://github.com/python-distro/distro/commit/bb35d41
-rw-r--r--changelogs/fragments/79227-update-vendored-distro.yaml2
-rw-r--r--lib/ansible/module_utils/distro/_distro.py151
-rw-r--r--test/sanity/ignore.txt4
3 files changed, 76 insertions, 81 deletions
diff --git a/changelogs/fragments/79227-update-vendored-distro.yaml b/changelogs/fragments/79227-update-vendored-distro.yaml
new file mode 100644
index 0000000000..186f725977
--- /dev/null
+++ b/changelogs/fragments/79227-update-vendored-distro.yaml
@@ -0,0 +1,2 @@
+minor_changes:
+ - updated the vendored distro library to upstream version (https://github.com/ansible/ansible/pull/79227)
diff --git a/lib/ansible/module_utils/distro/_distro.py b/lib/ansible/module_utils/distro/_distro.py
index 58e41d4eb7..19262a41db 100644
--- a/lib/ansible/module_utils/distro/_distro.py
+++ b/lib/ansible/module_utils/distro/_distro.py
@@ -31,6 +31,8 @@ access to OS distribution information is needed. See `Python issue 1322
<https://bugs.python.org/issue1322>`_ for more information.
"""
+import argparse
+import json
import logging
import os
import re
@@ -136,56 +138,6 @@ _DISTRO_RELEASE_IGNORE_BASENAMES = (
)
-#
-# Python 2.6 does not have subprocess.check_output so replicate it here
-#
-def _my_check_output(*popenargs, **kwargs):
- r"""Run command with arguments and return its output as a byte string.
-
- If the exit code was non-zero it raises a CalledProcessError. The
- CalledProcessError object will have the return code in the returncode
- attribute and output in the output attribute.
-
- The arguments are the same as for the Popen constructor. Example:
-
- >>> check_output(["ls", "-l", "/dev/null"])
- 'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n'
-
- The stdout argument is not allowed as it is used internally.
- To capture standard error in the result, use stderr=STDOUT.
-
- >>> check_output(["/bin/sh", "-c",
- ... "ls -l non_existent_file ; exit 0"],
- ... stderr=STDOUT)
- 'ls: non_existent_file: No such file or directory\n'
-
- This is a backport of Python-2.7's check output to Python-2.6
- """
- if 'stdout' in kwargs:
- raise ValueError(
- 'stdout argument not allowed, it will be overridden.'
- )
- process = subprocess.Popen(
- stdout=subprocess.PIPE, *popenargs, **kwargs
- )
- output, unused_err = process.communicate()
- retcode = process.poll()
- if retcode:
- cmd = kwargs.get("args")
- if cmd is None:
- cmd = popenargs[0]
- # Deviation from Python-2.7: Python-2.6's CalledProcessError does not
- # have an argument for the stdout so simply omit it.
- raise subprocess.CalledProcessError(retcode, cmd)
- return output
-
-
-try:
- _check_output = subprocess.check_output
-except AttributeError:
- _check_output = _my_check_output
-
-
def linux_distribution(full_distribution_name=True):
# type: (bool) -> Tuple[str, str, str]
"""
@@ -204,7 +156,8 @@ def linux_distribution(full_distribution_name=True):
* ``version``: The result of :func:`distro.version`.
- * ``codename``: The result of :func:`distro.codename`.
+ * ``codename``: The extra item (usually in parentheses) after the
+ os-release version number, or the result of :func:`distro.codename`.
The interface of this function is compatible with the original
:py:func:`platform.linux_distribution` function, supporting a subset of
@@ -251,8 +204,9 @@ def id():
"fedora" Fedora
"sles" SUSE Linux Enterprise Server
"opensuse" openSUSE
- "amazon" Amazon Linux
+ "amzn" Amazon Linux
"arch" Arch Linux
+ "buildroot" Buildroot
"cloudlinux" CloudLinux OS
"exherbo" Exherbo Linux
"gentoo" GenToo Linux
@@ -272,6 +226,8 @@ def id():
"netbsd" NetBSD
"freebsd" FreeBSD
"midnightbsd" MidnightBSD
+ "rocky" Rocky Linux
+ "guix" Guix System
============== =========================================
If you have a need to get distros for reliable IDs added into this set,
@@ -366,6 +322,10 @@ def version(pretty=False, best=False):
sources in a fixed priority order does not always yield the most precise
version (e.g. for Debian 8.2, or CentOS 7.1).
+ Some other distributions may not provide this kind of information. In these
+ cases, an empty string would be returned. This behavior can be observed
+ with rolling releases distributions (e.g. Arch Linux).
+
The *best* parameter can be used to control the approach for the returned
version:
@@ -681,7 +641,7 @@ except ImportError:
def __get__(self, obj, owner):
# type: (Any, Type[Any]) -> Any
- assert obj is not None, "call {0} on an instance".format(self._fname)
+ assert obj is not None, "call {} on an instance".format(self._fname)
ret = obj.__dict__[self._fname] = self._f(obj)
return ret
@@ -776,10 +736,6 @@ class LinuxDistribution(object):
* :py:exc:`IOError`: Some I/O issue with an os-release file or distro
release file.
- * :py:exc:`subprocess.CalledProcessError`: The lsb_release command had
- some issue (other than not being available in the program execution
- path).
-
* :py:exc:`UnicodeError`: A data source has unexpected characters or
uses an unexpected encoding.
"""
@@ -837,7 +793,7 @@ class LinuxDistribution(object):
return (
self.name() if full_distribution_name else self.id(),
self.version(),
- self.codename(),
+ self._os_release_info.get("release_codename") or self.codename(),
)
def id(self):
@@ -913,6 +869,9 @@ class LinuxDistribution(object):
).get("version_id", ""),
self.uname_attr("release"),
]
+ if self.id() == "debian" or "debian" in self.like().split():
+ # On Debian-like, add debian_version file content to candidates list.
+ versions.append(self._debian_version)
version = ""
if best:
# This algorithm uses the last version in priority order that has
@@ -1155,12 +1114,17 @@ class LinuxDistribution(object):
# stripped, etc.), so the tokens are now either:
# * variable assignments: var=value
# * commands or their arguments (not allowed in os-release)
+ # Ignore any tokens that are not variable assignments
if "=" in token:
k, v = token.split("=", 1)
props[k.lower()] = v
- else:
- # Ignore any tokens that are not variable assignments
- pass
+
+ if "version" in props:
+ # extract release codename (if any) from version attribute
+ match = re.search(r"\((\D+)\)|,\s*(\D+)", props["version"])
+ if match:
+ release_codename = match.group(1) or match.group(2)
+ props["codename"] = props["release_codename"] = release_codename
if "version_codename" in props:
# os-release added a version_codename field. Use that in
@@ -1171,16 +1135,6 @@ class LinuxDistribution(object):
elif "ubuntu_codename" in props:
# Same as above but a non-standard field name used on older Ubuntus
props["codename"] = props["ubuntu_codename"]
- elif "version" in props:
- # If there is no version_codename, parse it from the version
- match = re.search(r"(\(\D+\))|,(\s+)?\D+", props["version"])
- if match:
- codename = match.group()
- codename = codename.strip("()")
- codename = codename.strip(",")
- codename = codename.strip()
- # codename appears within paranthese.
- props["codename"] = codename
return props
@@ -1198,7 +1152,7 @@ class LinuxDistribution(object):
with open(os.devnull, "wb") as devnull:
try:
cmd = ("lsb_release", "-a")
- stdout = _check_output(cmd, stderr=devnull)
+ stdout = subprocess.check_output(cmd, stderr=devnull)
# Command not found or lsb_release returned error
except (OSError, subprocess.CalledProcessError):
return {}
@@ -1233,18 +1187,31 @@ class LinuxDistribution(object):
@cached_property
def _uname_info(self):
# type: () -> Dict[str, str]
+ if not self.include_uname:
+ return {}
with open(os.devnull, "wb") as devnull:
try:
cmd = ("uname", "-rs")
- stdout = _check_output(cmd, stderr=devnull)
+ stdout = subprocess.check_output(cmd, stderr=devnull)
except OSError:
return {}
content = self._to_str(stdout).splitlines()
return self._parse_uname_content(content)
+ @cached_property
+ def _debian_version(self):
+ # type: () -> str
+ try:
+ with open(os.path.join(self.etc_dir, "debian_version")) as fp:
+ return fp.readline().rstrip()
+ except (OSError, IOError):
+ return ""
+
@staticmethod
def _parse_uname_content(lines):
# type: (Sequence[str]) -> Dict[str, str]
+ if not lines:
+ return {}
props = {}
match = re.search(r"^([^\s]+)\s+([\d\.]+)", lines[0].strip())
if match:
@@ -1270,7 +1237,7 @@ class LinuxDistribution(object):
if isinstance(text, bytes):
return text.decode(encoding)
else:
- if isinstance(text, unicode): # noqa pylint: disable=undefined-variable
+ if isinstance(text, unicode): # noqa
return text.encode(encoding)
return text
@@ -1325,6 +1292,7 @@ class LinuxDistribution(object):
"manjaro-release",
"oracle-release",
"redhat-release",
+ "rocky-release",
"sl-release",
"slackware-version",
]
@@ -1403,13 +1371,36 @@ def main():
logger.setLevel(logging.DEBUG)
logger.addHandler(logging.StreamHandler(sys.stdout))
- dist = _distro
+ parser = argparse.ArgumentParser(description="OS distro info tool")
+ parser.add_argument(
+ "--json", "-j", help="Output in machine readable format", action="store_true"
+ )
+
+ parser.add_argument(
+ "--root-dir",
+ "-r",
+ type=str,
+ dest="root_dir",
+ help="Path to the root filesystem directory (defaults to /)",
+ )
+
+ args = parser.parse_args()
- logger.info("Name: %s", dist.name(pretty=True))
- distribution_version = dist.version(pretty=True)
- logger.info("Version: %s", distribution_version)
- distribution_codename = dist.codename()
- logger.info("Codename: %s", distribution_codename)
+ if args.root_dir:
+ dist = LinuxDistribution(
+ include_lsb=False, include_uname=False, root_dir=args.root_dir
+ )
+ else:
+ dist = _distro
+
+ if args.json:
+ logger.info(json.dumps(dist.info(), indent=4, sort_keys=True))
+ else:
+ logger.info("Name: %s", dist.name(pretty=True))
+ distribution_version = dist.version(pretty=True)
+ logger.info("Version: %s", distribution_version)
+ distribution_codename = dist.codename()
+ logger.info("Codename: %s", distribution_codename)
if __name__ == "__main__":
diff --git a/test/sanity/ignore.txt b/test/sanity/ignore.txt
index 5e5b5308e8..36afe13857 100644
--- a/test/sanity/ignore.txt
+++ b/test/sanity/ignore.txt
@@ -72,8 +72,10 @@ lib/ansible/module_utils/compat/selinux.py import-3.9!skip # pass/fail depends o
lib/ansible/module_utils/distro/_distro.py future-import-boilerplate # ignore bundled
lib/ansible/module_utils/distro/_distro.py metaclass-boilerplate # ignore bundled
lib/ansible/module_utils/distro/_distro.py no-assert
-lib/ansible/module_utils/distro/_distro.py pylint:using-constant-test # bundled code we don't want to modify
lib/ansible/module_utils/distro/_distro.py pep8!skip # bundled code we don't want to modify
+lib/ansible/module_utils/distro/_distro.py pylint:ansible-format-automatic-specification # ignore bundled
+lib/ansible/module_utils/distro/_distro.py pylint:undefined-variable # ignore bundled
+lib/ansible/module_utils/distro/_distro.py pylint:using-constant-test # bundled code we don't want to modify
lib/ansible/module_utils/distro/__init__.py empty-init # breaks namespacing, bundled, do not override
lib/ansible/module_utils/facts/__init__.py empty-init # breaks namespacing, deprecate and eventually remove
lib/ansible/module_utils/facts/network/linux.py pylint:disallowed-name