From 31330b9a3a71543f2b1568679e3be6fe881ecd32 Mon Sep 17 00:00:00 2001 From: Tim Serong Date: Wed, 6 Sep 2017 15:53:32 +1000 Subject: [PATCH] [RM-18164] Use /etc/os-release if platform.linux_distribution() empty On systems with only /etc/os-release (rather than distro-specific release files), platform.linux_distribution() returns ('', '', ''). In this case, fall back to parsing /etc/os-release instead. It would better to flip the ordering, and use /etc/os-release first, with platform.linux_distribution() as a fallback. There's tests for seven different distros' os-release files already, but we may want more before flipping this around. (It would be better still to just use the distro.linux_distribution() function, especially since platform.linux_distribution() is deprecated, but I don't know that there's a way of ensuring that module is present in advance on all the remote hosts.) Fixes: http://tracker.ceph.com/issues/18164 Signed-off-by: Tim Serong --- ceph_deploy/hosts/__init__.py | 2 +- ceph_deploy/hosts/remotes.py | 31 ++++++ ceph_deploy/tests/test_remotes.py | 153 +++++++++++++++++++++++++++++- 3 files changed, 184 insertions(+), 2 deletions(-) diff --git a/ceph_deploy/hosts/__init__.py b/ceph_deploy/hosts/__init__.py index a96e614..c8605d5 100644 --- a/ceph_deploy/hosts/__init__.py +++ b/ceph_deploy/hosts/__init__.py @@ -114,7 +114,7 @@ def _normalized_distro_name(distro): return 'scientific' elif distro.startswith('oracle'): return 'oracle' - elif distro.startswith(('suse', 'opensuse')): + elif distro.startswith(('suse', 'opensuse', 'sles')): return 'suse' elif distro.startswith('centos'): return 'centos' diff --git a/ceph_deploy/hosts/remotes.py b/ceph_deploy/hosts/remotes.py index e3aef4a..fec1da0 100644 --- a/ceph_deploy/hosts/remotes.py +++ b/ceph_deploy/hosts/remotes.py @@ -8,12 +8,15 @@ import os import shutil import tempfile import platform +import re def platform_information(_linux_distribution=None): """ detect platform information from remote host """ linux_distribution = _linux_distribution or platform.linux_distribution distro, release, codename = linux_distribution() + if not distro: + distro, release, codename = parse_os_release() if not codename and 'debian' in distro.lower(): # this could be an empty string in Debian debian_codenames = { '10': 'buster', @@ -45,6 +48,34 @@ def platform_information(_linux_distribution=None): ) +def parse_os_release(release_path='/etc/os-release'): + """ Extract (distro, release, codename) from /etc/os-release if present """ + release_info = {} + if os.path.isfile(release_path): + for line in open(release_path, 'r').readlines(): + line = line.strip() + if line.startswith('#'): + continue + parts = line.split('=') + if len(parts) != 2: + continue + release_info[parts[0].strip()] = parts[1].strip("\"'\n\t ") + # In theory, we want ID/NAME, VERSION_ID and VERSION_CODENAME (with a + # possible fallback to VERSION on the latter), based on information at: + # https://www.freedesktop.org/software/systemd/man/os-release.html + # However, after reviewing several distros /etc/os-release, getting + # the codename is a bit of a mess. It's usually in parentheses in + # VERSION, with some exceptions. + distro = release_info.get('ID', '') + release = release_info.get('VERSION_ID', '') + codename = release_info.get('UBUNTU_CODENAME', release_info.get('VERSION', '')) + match = re.match(r'^[^(]+ \(([^)]+)\)', codename) + if match: + codename = match.group(1).lower() + if not codename and release_info.get('NAME', '') == 'openSUSE Tumbleweed': + codename = 'tumbleweed' + return (distro, release, codename) + def machine_type(): """ detect machine type """ return platform.machine() diff --git a/ceph_deploy/tests/test_remotes.py b/ceph_deploy/tests/test_remotes.py index 7138429..4378ab7 100644 --- a/ceph_deploy/tests/test_remotes.py +++ b/ceph_deploy/tests/test_remotes.py @@ -1,6 +1,6 @@ from mock import patch from ceph_deploy.hosts import remotes -from ceph_deploy.hosts.remotes import platform_information +from ceph_deploy.hosts.remotes import platform_information, parse_os_release class FakeExists(object): @@ -83,3 +83,154 @@ class TestPlatformInformation(object): assert distro == 'Ubuntu' assert release == '12.04' assert codename == 'precise' + +class TestParseOsRelease(object): + """ test various forms of /etc/os-release """ + + def setup(self): + pass + + def test_handles_centos_7(self, tmpdir): + path = str(tmpdir.join('os_release')) + with open(path, 'w') as os_release: + os_release.write(""" +NAME="CentOS Linux" +VERSION="7 (Core)" +ID="centos" +ID_LIKE="rhel fedora" +VERSION_ID="7" +PRETTY_NAME="CentOS Linux 7 (Core)" +ANSI_COLOR="0;31" +CPE_NAME="cpe:/o:centos:centos:7" +HOME_URL="https://www.centos.org/" +BUG_REPORT_URL="https://bugs.centos.org/" + +CENTOS_MANTISBT_PROJECT="CentOS-7" +CENTOS_MANTISBT_PROJECT_VERSION="7" +REDHAT_SUPPORT_PRODUCT="centos" +REDHAT_SUPPORT_PRODUCT_VERSION="7" +""") + distro, release, codename = parse_os_release(path) + assert distro == 'centos' + assert release == '7' + assert codename == 'core' + + + def test_handles_debian_stretch(self, tmpdir): + path = str(tmpdir.join('os_release')) + with open(path, 'w') as os_release: + os_release.write(""" +PRETTY_NAME="Debian GNU/Linux 9 (stretch)" +NAME="Debian GNU/Linux" +VERSION_ID="9" +VERSION="9 (stretch)" +ID=debian +HOME_URL="https://www.debian.org/" +SUPPORT_URL="https://www.debian.org/support" +BUG_REPORT_URL="https://bugs.debian.org/" +""") + distro, release, codename = parse_os_release(path) + assert distro == 'debian' + assert release == '9' + assert codename == 'stretch' + + def test_handles_fedora_26(self, tmpdir): + path = str(tmpdir.join('os_release')) + with open(path, 'w') as os_release: + os_release.write(""" +NAME=Fedora +VERSION="26 (Twenty Six)" +ID=fedora +VERSION_ID=26 +PRETTY_NAME="Fedora 26 (Twenty Six)" +ANSI_COLOR="0;34" +CPE_NAME="cpe:/o:fedoraproject:fedora:26" +HOME_URL="https://fedoraproject.org/" +BUG_REPORT_URL="https://bugzilla.redhat.com/" +REDHAT_BUGZILLA_PRODUCT="Fedora" +REDHAT_BUGZILLA_PRODUCT_VERSION=26 +REDHAT_SUPPORT_PRODUCT="Fedora" +REDHAT_SUPPORT_PRODUCT_VERSION=26 +PRIVACY_POLICY_URL=https://fedoraproject.org/wiki/Legal:PrivacyPolicy +""") + distro, release, codename = parse_os_release(path) + assert distro == 'fedora' + assert release == '26' + assert codename == 'twenty six' + + def test_handles_opensuse_leap_42_2(self, tmpdir): + path = str(tmpdir.join('os_release')) + with open(path, 'w') as os_release: + os_release.write(""" +NAME="openSUSE Leap" +VERSION="42.2" +ID=opensuse +ID_LIKE="suse" +VERSION_ID="42.2" +PRETTY_NAME="openSUSE Leap 42.2" +ANSI_COLOR="0;32" +CPE_NAME="cpe:/o:opensuse:leap:42.2" +BUG_REPORT_URL="https://bugs.opensuse.org" +HOME_URL="https://www.opensuse.org/" +""") + distro, release, codename = parse_os_release(path) + assert distro == 'opensuse' + assert release == '42.2' + assert codename == '42.2' + + def test_handles_opensuse_tumbleweed(self, tmpdir): + path = str(tmpdir.join('os_release')) + with open(path, 'w') as os_release: + os_release.write(""" +NAME="openSUSE Tumbleweed" +# VERSION="20170502" +ID=opensuse +ID_LIKE="suse" +VERSION_ID="20170502" +PRETTY_NAME="openSUSE Tumbleweed" +ANSI_COLOR="0;32" +CPE_NAME="cpe:/o:opensuse:tumbleweed:20170502" +BUG_REPORT_URL="https://bugs.opensuse.org" +HOME_URL="https://www.opensuse.org/" +""") + distro, release, codename = parse_os_release(path) + assert distro == 'opensuse' + assert release == '20170502' + assert codename == 'tumbleweed' + + def test_handles_sles_12_sp3(self, tmpdir): + path = str(tmpdir.join('os_release')) + with open(path, 'w') as os_release: + os_release.write(""" +NAME="SLES" +VERSION="12-SP3" +VERSION_ID="12.3" +PRETTY_NAME="SUSE Linux Enterprise Server 12 SP3" +ID="sles" +ANSI_COLOR="0;32" +CPE_NAME="cpe:/o:suse:sles:12:sp3" +""") + distro, release, codename = parse_os_release(path) + assert distro == 'sles' + assert release == '12.3' + assert codename == '12-SP3' + + def test_handles_ubuntu_xenial(self, tmpdir): + path = str(tmpdir.join('os_release')) + with open(path, 'w') as os_release: + os_release.write(""" +NAME="Ubuntu" +VERSION="16.04 LTS (Xenial Xerus)" +ID=ubuntu +ID_LIKE=debian +PRETTY_NAME="Ubuntu 16.04 LTS" +VERSION_ID="16.04" +HOME_URL="http://www.ubuntu.com/" +SUPPORT_URL="http://help.ubuntu.com/" +BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/" +UBUNTU_CODENAME=xenial +""") + distro, release, codename = parse_os_release(path) + assert distro == 'ubuntu' + assert release == '16.04' + assert codename == 'xenial' -- 2.47.3