1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
|
#
# Copyright (C) 2018 FreeIPA Contributors see COPYING for license
#
"""Distribution information
Known Linux distros with /etc/os-release
----------------------------------------
- alpine
- centos (like rhel, fedora)
- debian
- fedora
- rhel
- ubuntu (like debian)
The platform ids for ipaplatform providers are based on:
1) IPAPLATFORM_OVERRIDE env var
2) ipaplatform.override.OVERRIDE value
3) ID field of /etc/os-release (Linux)
4) ID_LIKE fields of /etc/os-release (Linux)
"""
from __future__ import absolute_import
from collections.abc import Mapping
import importlib
import re
import os
import sys
import warnings
import ipaplatform
try:
from ipaplatform.override import OVERRIDE
except ImportError:
OVERRIDE = None
_osrelease_line = re.compile(
u"^(?!#)(?P<name>[a-zA-Z0-9_]+)="
u"(?P<quote>[\"\']?)(?P<value>.+)(?P=quote)$"
)
def _parse_osrelease(filename='/etc/os-release'):
"""Parser for /etc/os-release for Linux distributions
https://www.freedesktop.org/software/systemd/man/os-release.html
"""
release = {}
with open(filename) as f:
for line in f:
mo = _osrelease_line.match(line)
if mo is not None:
release[mo.group('name')] = mo.group('value')
if 'ID_LIKE' in release:
release['ID_LIKE'] = tuple(
v.strip()
for v in release['ID_LIKE'].split(' ')
if v.strip()
)
else:
release["ID_LIKE"] = ()
# defaults
release.setdefault('NAME', 'Linux')
release.setdefault('ID', 'linux')
release.setdefault('VERSION', '')
release.setdefault('VERSION_ID', '')
return release
class OSInfo(Mapping):
__slots__ = ('_info', '_platform', '_container')
bsd_family = (
'freebsd',
'openbsd',
'netbsd',
'dragonfly',
'gnukfreebsd'
)
def __init__(self):
if sys.platform.startswith('linux'):
# Linux, get distribution from /etc/os-release
info = self._handle_linux()
elif sys.platform == 'win32':
info = self._handle_win32()
elif sys.platform == 'darwin':
info = self._handle_darwin()
elif sys.platform.startswith(self.bsd_family):
info = self._handle_bsd()
else:
raise ValueError("Unsupported platform: {}".format(sys.platform))
self._info = info
self._platform = None
self._container = None
def _handle_linux(self):
"""Detect Linux distribution from /etc/os-release
"""
try:
return _parse_osrelease()
except Exception as e:
warnings.warn("Failed to read /etc/os-release: {}".format(e))
return {
'NAME': 'Linux',
'ID': 'linux',
}
def _handle_win32(self):
"""Windows 32 or 64bit platform
"""
return {
'NAME': 'Windows',
'ID': 'win32',
}
def _handle_darwin(self):
"""Handle macOS / Darwin platform
"""
return {
'NAME': 'macOS',
'ID': 'macos',
}
def _handle_bsd(self):
"""Handle BSD-like platforms
"""
platform = sys.platform
simple = platform.rstrip('0123456789')
id_like = []
if simple != platform:
id_like.append(simple)
return {
'NAME': platform,
'ID': platform,
'ID_LIKE': tuple(id_like),
}
def __getitem__(self, item):
return self._info[item]
def __iter__(self):
return iter(self._info)
def __len__(self):
return len(self._info)
@property
def name(self):
"""OS name (user)
"""
return self._info['NAME']
@property
def id(self):
"""Lower case OS identifier
"""
return self._info['ID']
@property
def id_like(self):
"""Related / similar OS
"""
return self._info.get('ID_LIKE', ())
@property
def version(self):
"""Version number and name of OS (for user)
"""
return self._info.get('VERSION')
@property
def version_number(self):
"""Version number tuple based on version_id
"""
version_id = self._info.get('VERSION_ID')
if not version_id:
return ()
return tuple(int(p) for p in version_id.split('.'))
@property
def platform_ids(self):
"""Ordered tuple of detected platforms (including override)
"""
platforms = []
# env var first
env = os.environ.get("IPAPLATFORM_OVERRIDE")
if env:
platforms.append(env)
# override from package definition
if OVERRIDE is not None and OVERRIDE not in platforms:
# allow RPM and Debian packages to override platform
platforms.append(OVERRIDE)
if self.id not in platforms:
platforms.append(self.id)
platforms.extend(self.id_like)
return tuple(platforms)
@property
def platform(self):
if self._platform is not None:
return self._platform
for platform in self.platform_ids:
try:
importlib.import_module('ipaplatform.{}'.format(platform))
except ImportError:
pass
else:
self._platform = platform
return platform
raise ImportError('No ipaplatform available for "{}"'.format(
', '.join(self.platform_ids)))
@property
def container(self):
if self._container is not None:
return self._container
from ipaplatform.tasks import tasks # pylint: disable=cyclic-import
try:
self._container = tasks.detect_container()
except NotImplementedError:
raise NotImplementedError(
'Platform does not support detecting containers')
return self._container
osinfo = OSInfo()
ipaplatform.NAME = osinfo.platform
if __name__ == '__main__':
import pprint
pprint.pprint(dict(osinfo))
|