Skip to content

Commit

Permalink
[Policy] Consolidate check() across policies
Browse files Browse the repository at this point in the history
There is a common theme for new policies to subclass an existing one and
just update the `check()` method. In almost all instances, the `check()`
method remains almost entirely the same, simply changing a release file
name or string.

Reduce repeating ourselves by consolidating this into the base
`LinuxPolicy.check()`, and leverage per-policy class attributes
`os_release_file`, `os_release_name`, and `os_release_id`. The first of
which will enable a policy if the specified file is found. Failing that,
`os_release_name` is used to check the `NAME` field in
`/etc/os-release`, and `os_release_id` is used to check the `ID` field
in the same file if the name pattern does not match (or is not set).

Note that we do _not_ check the `ID_LIKE` field.

This allows a policy to be defined with as little as something like the
following:

```
class MyPolicy(LinuxPolicy):
    distro = 'My Awesome Linux'
    vendor = 'Myself'
    os_release_file = '/etc/mylinux-release'
    os_release_name = 'MyLinux'
    os_release_id ='mynix'
```

Resolves: #1934

Signed-off-by: Jake Hunsaker <[email protected]>
  • Loading branch information
TurboTurtle committed Aug 28, 2024
1 parent 3886534 commit 9b6bff7
Show file tree
Hide file tree
Showing 17 changed files with 76 additions and 304 deletions.
1 change: 1 addition & 0 deletions sos/policies/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def load(cache={}, sysroot=None, init=None, probe_runtime=True,
cache['policy'] = policy(sysroot=sysroot, init=init,
probe_runtime=probe_runtime,
remote_exec=remote_exec)
break

if sys.platform != 'linux':
raise Exception("SoS is not supported on this platform")
Expand Down
30 changes: 29 additions & 1 deletion sos/policies/distros/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
except ImportError:
BOTO3_LOADED = False

OS_RELEASE = "/etc/os-release"
# Container environment variables for detecting if we're in a container
ENV_CONTAINER = 'container'
ENV_HOST_SYSROOT = 'HOST'
Expand All @@ -53,6 +54,11 @@ class LinuxPolicy(Policy):
vendor = "None"
PATH = "/bin:/sbin:/usr/bin:/usr/sbin"
init = None
# the following will be used, in order, as part of check() to validate that
# we are running on a particular distro
os_release_file = ''
os_release_name = ''
os_release_id = ''
# _ prefixed class attrs are used for storing any vendor-defined defaults
# the non-prefixed attrs are used by the upload methods, and will be set
# to the cmdline/config file values, if provided. If not provided, then
Expand Down Expand Up @@ -145,7 +151,29 @@ def check(cls, remote=''):
This function is responsible for determining if the underlying system
is supported by this policy.
"""
raise NotImplementedError
def _check_release(content):
_matches = [cls.distro]
if isinstance(cls.os_release_name, list):
_matches.extend(cls.os_release_name)
else:
_matches.append(cls.os_release_name)
if cls.os_release_id:
_matches.append(cls.os_release_id)
for line in content.splitlines():
if line.startswith(('NAME=', 'ID=')):
_distro = line.split('=')[1:][0].strip("\"'")
if _distro in _matches:
return True
return False

if remote:
return _check_release(remote)
# use the os-specific file primarily
if os.path.isfile(cls.os_release_file):
return True
# next check os-release for a NAME or ID value we expect
with open(OS_RELEASE, "r", encoding='utf-8') as f:
return _check_release(f.read())

def kernel_version(self):
return self.release
Expand Down
23 changes: 4 additions & 19 deletions sos/policies/distros/almalinux.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
#
# See the LICENSE file in the source distribution for further information.

import os
from sos.policies.distros.redhat import RedHatPolicy, OS_RELEASE
from sos.policies.distros.redhat import RedHatPolicy


class AlmaLinuxPolicy(RedHatPolicy):
distro = "AlmaLinux"
vendor = "AlmaLinux OS Foundation"
os_release_file = '/etc/almalinux-release'
os_release_name = 'AlmaLinux'

vendor_urls = [
('Distribution Website', 'https://www.almalinux.org/'),
('Commercial Support', 'https://tuxcare.com/linux-support-services/')
Expand All @@ -26,21 +28,4 @@ def __init__(self, sysroot=None, init=None, probe_runtime=True,
probe_runtime=probe_runtime,
remote_exec=remote_exec)

@classmethod
def check(cls, remote=''):
if remote:
return cls.distro in remote

if not os.path.isfile('/etc/almalinux-release'):
return False

if os.path.exists(OS_RELEASE):
with open(OS_RELEASE, 'r', encoding='utf-8') as f:
for line in f:
if line.startswith('NAME'):
if 'AlmaLinux' in line:
return True

return False

# vim: set et ts=4 sw=4 :
21 changes: 3 additions & 18 deletions sos/policies/distros/amazon.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,21 @@
#
# See the LICENSE file in the source distribution for further information.

import os
from sos.policies.distros.redhat import RedHatPolicy, OS_RELEASE
from sos.policies.distros.redhat import RedHatPolicy


class AmazonPolicy(RedHatPolicy):

distro = "Amazon Linux"
vendor = "Amazon"
vendor_urls = [('Distribution Website', 'https://aws.amazon.com')]
os_release_file = ''
os_release_name = 'Amazon Linux'

def __init__(self, sysroot=None, init=None, probe_runtime=True,
remote_exec=None):
super().__init__(sysroot=sysroot, init=init,
probe_runtime=probe_runtime,
remote_exec=remote_exec)

@classmethod
def check(cls, remote=''):

if remote:
return cls.distro in remote

if not os.path.exists(OS_RELEASE):
return False

with open(OS_RELEASE, 'r', encoding='utf-8') as f:
for line in f:
if line.startswith('NAME'):
if 'Amazon Linux' in line:
return True
return False

# vim: set et ts=4 sw=4 :
26 changes: 3 additions & 23 deletions sos/policies/distros/anolis.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,21 @@
#
# See the LICENSE file in the source distribution for further information.

import os
from sos.policies.distros.redhat import RedHatPolicy, OS_RELEASE
from sos.policies.distros.redhat import RedHatPolicy


class AnolisPolicy(RedHatPolicy):

distro = "Anolis OS"
vendor = "The OpenAnolis Project"
vendor_urls = [('Distribution Website', 'https://openanolis.org/')]
os_release_file = '/etc/anolis-release'
os_release_name = 'Anolis OS'

def __init__(self, sysroot=None, init=None, probe_runtime=True,
remote_exec=None):
super().__init__(sysroot=sysroot, init=init,
probe_runtime=probe_runtime,
remote_exec=remote_exec)

@classmethod
def check(cls, remote=''):

if remote:
return cls.distro in remote

# Return False if /etc/os-release is missing
if not os.path.exists(OS_RELEASE):
return False

# Return False if /etc/anolis-release is missing
if not os.path.isfile('/etc/anolis-release'):
return False

with open(OS_RELEASE, 'r', encoding='utf-8') as f:
for line in f:
if line.startswith('NAME'):
if 'Anolis OS' in line:
return True
return False

# vim: set et ts=4 sw=4 :
25 changes: 5 additions & 20 deletions sos/policies/distros/azure.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
#
# See the LICENSE file in the source distribution for further information.

import os
from sos.report.plugins import AzurePlugin
from sos.policies.distros.redhat import RedHatPolicy, OS_RELEASE
from sos.policies.distros.redhat import RedHatPolicy


class AzurePolicy(RedHatPolicy):
Expand All @@ -20,6 +19,10 @@ class AzurePolicy(RedHatPolicy):
vendor_urls = [
('Distribution Website', 'https://github.com/microsoft/azurelinux')
]
os_release_name = [
'Common Base Linux Mariner',
'Microsoft Azure Linux'
]

def __init__(self, sysroot=None, init=None, probe_runtime=True,
remote_exec=None):
Expand All @@ -28,22 +31,4 @@ def __init__(self, sysroot=None, init=None, probe_runtime=True,
remote_exec=remote_exec)
self.valid_subclasses += [AzurePlugin]

@classmethod
def check(cls, remote=''):

if remote:
return cls.distro in remote

if not os.path.exists(OS_RELEASE):
return False

with open(OS_RELEASE, 'r', encoding='utf-8') as f:
for line in f:
if line.startswith('NAME'):
if 'Common Base Linux Mariner' in line:
return True
if 'Microsoft Azure Linux' in line:
return True
return False

# vim: set et ts=4 sw=4 :
27 changes: 3 additions & 24 deletions sos/policies/distros/circle.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,21 @@
#
# See the LICENSE file in the source distribution for further information.

import os
from sos.policies.distros.redhat import RedHatPolicy, OS_RELEASE
from sos.policies.distros.redhat import RedHatPolicy


class CirclePolicy(RedHatPolicy):

distro = "Circle Linux"
vendor = "The Circle Linux Project"
vendor_urls = [('Distribution Website', 'https://cclinux.org')]
os_release_file = '/etc/circle-release'
os_release_name = 'Circle Linux'

def __init__(self, sysroot=None, init=None, probe_runtime=True,
remote_exec=None):
super().__init__(sysroot=sysroot, init=init,
probe_runtime=probe_runtime,
remote_exec=remote_exec)

@classmethod
def check(cls, remote=''):

if remote:
return cls.distro in remote

# Return False if /etc/os-release is missing
if not os.path.exists(OS_RELEASE):
return False

# Return False if /etc/circle-release is missing
if not os.path.isfile('/etc/circle-release'):
return False

with open(OS_RELEASE, 'r', encoding='utf-8') as f:
for line in f:
if line.startswith('NAME'):
if 'Circle Linux' in line:
return True

return False

# vim: set et ts=4 sw=4 :
22 changes: 3 additions & 19 deletions sos/policies/distros/cloudlinux.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
#
# See the LICENSE file in the source distribution for further information.

import os
from sos.policies.distros.redhat import RedHatPolicy, OS_RELEASE
from sos.policies.distros.redhat import RedHatPolicy


class CloudLinuxPolicy(RedHatPolicy):
Expand All @@ -19,28 +18,13 @@ class CloudLinuxPolicy(RedHatPolicy):
('Distribution Website', 'https://www.cloudlinux.com/'),
('Commercial Support', 'https://www.cloudlinux.com/')
]
os_release_file = '/etc/cloudlinux-release'
os_release_name = 'CloudLinux'

def __init__(self, sysroot=None, init=None, probe_runtime=True,
remote_exec=None):
super().__init__(sysroot=sysroot, init=init,
probe_runtime=probe_runtime,
remote_exec=remote_exec)

@classmethod
def check(cls, remote=''):
if remote:
return cls.distro in remote

if not os.path.isfile('/etc/cloudlinux-release'):
return False

if os.path.exists(OS_RELEASE):
with open(OS_RELEASE, 'r', encoding='utf-8') as f:
for line in f:
if line.startswith('NAME'):
if 'CloudLinux' in line:
return True

return False

# vim: set et ts=4 sw=4 :
15 changes: 2 additions & 13 deletions sos/policies/distros/cos.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class CosPolicy(LinuxPolicy):
('Distribution Website',
'https://cloud.google.com/container-optimized-os/')
]
os_release_name = 'Container-Optimized OS'
os_release_id = 'cos'
valid_subclasses = [CosPlugin, IndependentPlugin]
PATH = "/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin"

Expand All @@ -43,17 +45,4 @@ def __init__(self, sysroot=None, init=None, probe_runtime=True,
remote_exec=remote_exec)
self.valid_subclasses += [CosPolicy]

@classmethod
def check(cls, remote=''):
if remote:
return cls.distro in remote

try:
with open('/etc/os-release', 'r', encoding='utf-8') as fp:
os_release = dict(line.strip().split('=') for line in fp
if not _blank_or_comment(line))
return os_release['ID'] == 'cos'
except (IOError, KeyError):
return False

# vim: set et ts=4 sw=4 :
13 changes: 1 addition & 12 deletions sos/policies/distros/debian.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
#
# See the LICENSE file in the source distribution for further information.

import os

from sos.report.plugins import DebianPlugin
from sos.policies.distros import LinuxPolicy
from sos.policies.package_managers.dpkg import DpkgPackageManager
Expand All @@ -17,6 +15,7 @@ class DebianPolicy(LinuxPolicy):
distro = "Debian"
vendor = "the Debian project"
vendor_urls = [('Community Website', 'https://www.debian.org/')]
os_release_file = '/etc/debian_version'
name_pattern = 'friendly'
valid_subclasses = [DebianPlugin]
PATH = "/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games" \
Expand Down Expand Up @@ -50,16 +49,6 @@ def _get_pkg_name_for_binary(self, binary):
"xz": "xz-utils"
}.get(binary, binary)

@classmethod
def check(cls, remote=''):
"""This method checks to see if we are running on Debian.
It returns True or False."""

if remote:
return cls.distro in remote

return os.path.isfile('/etc/debian_version')

def dist_version(self):
try:
with open('/etc/os-release', 'r', encoding='utf-8') as fp:
Expand Down
Loading

0 comments on commit 9b6bff7

Please sign in to comment.