Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sos/policies/distros/debian.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ def __init__(self, sysroot=None, init=None, probe_runtime=True,
self.package_manager = DpkgPackageManager(chroot=self.sysroot,
remote_exec=remote_exec)
self.valid_subclasses += [DebianPlugin]
self.load_presets()

def _get_pkg_name_for_binary(self, binary):
# for binary not specified inside {..}, return binary itself
Expand Down
8 changes: 8 additions & 0 deletions sos/policies/distros/ubuntu.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
from sos.policies.package_managers.dpkg import DpkgPackageManager
from sos.policies.package_managers import MultiPackageManager

from sos.presets.ubuntu import SUNBEAM, UBUNTU_PRESETS, UBUNTU


class UbuntuPolicy(DebianPolicy):
vendor = "Canonical"
Expand Down Expand Up @@ -51,6 +53,12 @@ def __init__(self, sysroot=None, init=None, probe_runtime=True,
pass

self.valid_subclasses += [UbuntuPlugin]
self.register_presets(UBUNTU_PRESETS)

def probe_preset(self):
if self.pkg_by_name("sunbeam") is not None:
return self.find_preset(SUNBEAM)
return self.find_preset(UBUNTU)

def dist_version(self):
""" Returns the version stated in DISTRIB_RELEASE
Expand Down
36 changes: 36 additions & 0 deletions sos/presets/ubuntu/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright (C) 2026 Canonical Ltd., Arif Ali <arif-ali@ubuntu.com>

# This file is part of the sos project: https://github.com/sosreport/sos
#
# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions of
# version 2 of the GNU General Public License.
#
# See the LICENSE file in the source distribution for further information.

from sos.options import SoSOptions
from sos.presets import PresetDefaults


UBUNTU = "ubuntu"
UBUNTU_DESC = "Ubuntu"

SUNBEAM = "sunbeam"
SUNBEAM_DESC = "Canonical OpenStack"
SUNBEAM_OPTS = SoSOptions(
all_logs=True,
plugopts=[
'sunbeam.juju-allow-login = True',
'kubernetes.all = True',
'kubernetes.describe = True',
'kubernetes.kubelogs = True',
'kubernetes.podlogs = True',
Comment on lines +24 to +27

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the spaces around = allowed? When I mimic the same in Red Hat Satellite preset:

SAT_OPTS = SoSOptions(log_size=100, plugopts=['apache.log = on', 'networking.traceroute = True'])

I get error:

# sos report -l

sos report (version 4.11.0)

no such option "log " for plugin (apache)
#

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought I tested it, obviously not :)

agreed, it's an issue, but maybe that's an issue in itself, and we should solve that. I tested the below patch, which works for me now. So this way it truly behaves like a variable, and we can have spaces.

diff --git a/sos/report/__init__.py b/sos/report/__init__.py
index 20f314c0..05073d1b 100644
--- a/sos/report/__init__.py
+++ b/sos/report/__init__.py
@@ -960,15 +960,17 @@ class SoSReport(SoSComponent):
             for plugname, plug in self.loaded_plugins:
                 if plugname in opts:
                     for opt in opts[plugname]:
+                        opt = opt.strip()
                         if opt not in plug.options:
                             self.soslog.error(f'no such option "{opt}" for '
                                               f'plugin ({plugname})')
                             self._exit(1)
                         try:
-                            plug.options[opt].set_value(opts[plugname][opt])
+                            plug.options[opt].set_value(
+                                opts[plugname][opt].strip())
                             self.soslog.debug(
                                 f"Set {plugname} plugin option to "
-                                f"{plug.options[opt]}")
+                                f"{plug.options[opt].strip()}")
                         except Exception as err:
                             self.soslog.error(err)
                             self._exit(1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wont work on cmdline args (obviusly), will work in presets and I guess also in config file - sounds good to me!

Let add a commit for that.

])

UBUNTU_PRESETS = {
SUNBEAM: PresetDefaults(name=SUNBEAM, desc=SUNBEAM_DESC,
opts=SUNBEAM_OPTS),
UBUNTU: PresetDefaults(name=UBUNTU, desc=UBUNTU_DESC),
}

# vim: set et ts=4 sw=4 :
4 changes: 3 additions & 1 deletion sos/report/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -927,11 +927,13 @@ def _set_tunables(self):
opts = {}
for opt in self.opts.plugopts:
try:
opt, val = opt.split("=")
opt, val = opt.split("=", 1)
except ValueError:
val = True

opt = opt.strip()
if isinstance(val, str):
val = val.strip()
arg = val.lower()
if arg in ["on", "enable", "enabled", "true", "yes"]:
val = True
Expand Down
72 changes: 72 additions & 0 deletions tests/unittests/report_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,20 @@
# version 2 of the GNU General Public License.
#
# See the LICENSE file in the source distribution for further information.
import logging
import unittest

try:
import json
except ImportError:
import simplejson as json

from sos.report import SoSReport
from sos.report.plugins import Plugin, PluginOpt
from sos.report.reporting import (Report, Section, Command, CopiedFile,
CreatedFile, Alert, PlainTextReport)
from sos.policies.distros import LinuxPolicy
from sos.policies.init_systems import InitSystem


class ReportTest(unittest.TestCase):
Expand Down Expand Up @@ -149,6 +154,73 @@ def test_alert(self):
PlainTextReport(self.report).unicode())


class MockOptions:
all_logs = False
dry_run = False
log_size = 25
allow_system_changes = False
skip_commands = []
skip_files = []
plugopts = []


class MockPlugin(Plugin):

option_list = [
PluginOpt('baz', default=False),
PluginOpt('empty', default=None),
PluginOpt('test_option', default='foobar', val_type=str)
]

def __init__(self, commons):
super().__init__(commons=commons)


class SetTunablesPresetSpacesTest(unittest.TestCase):

def setUp(self):
self.commons = {
'sysroot': '/',
'policy': LinuxPolicy(init=InitSystem()),
'cmdlineopts': MockOptions(),
'devices': {}
}
self.plugin = MockPlugin(self.commons)

self.report = SoSReport.__new__(SoSReport)
self.report.opts = MockOptions()
self.report.soslog = logging.getLogger('sos_test_set_tunables')
self.report.loaded_plugins = [('mock', self.plugin)]

def set_tunables(self, plugopts):
"""Run _set_tunables with the given preset-style plugopts list."""
self.report.opts.plugopts = plugopts
self.report._set_tunables()

def test_plugopt_without_spaces_still_parses(self):
self.set_tunables(['mock.baz=True'])
self.assertIs(self.plugin.options['baz'].value, True)

def test_plugopt_with_spaces_around_equals(self):
self.set_tunables(['mock.baz = True'])
self.assertIs(self.plugin.options['baz'].value, True)

def test_plugopt_with_spaces_disables_option(self):
self.set_tunables(['mock.baz = False'])
self.assertIs(self.plugin.options['baz'].value, False)

def test_plugopt_string_value_with_spaces_around_equals(self):
self.set_tunables(['mock.test_option = bar'])
self.assertEqual(self.plugin.options['test_option'].value,
'bar')

def test_unknown_plugopt_exits_even_with_spaces(self):
# An unknown option name (with surrounding whitespace) should still
# be reported as unknown rather than silently mis-stripped.
with self.assertRaises(SystemExit):
self.set_tunables(['mock.bogus = True'])


if __name__ == "__main__":
unittest.main()

Expand Down
Loading