diff options
author | Jake Hunsaker <jhunsake@redhat.com> | 2020-03-26 17:04:28 -0400 |
---|---|---|
committer | Jake Hunsaker <jhunsake@redhat.com> | 2020-04-08 09:27:16 -0400 |
commit | 6a3b296de560e6b4abc1ec56cf34b0028a04a878 (patch) | |
tree | 3f91e9184bccbdc55c7075c4867e60c968f3d25f | |
parent | 5b7717775ca8b26a1fde76a163d604f40d724721 (diff) | |
download | sos-6a3b296de560e6b4abc1ec56cf34b0028a04a878.tar.gz |
[component] Add Policy loading and move to new component.py
Adds Policy loading into `SoSComponent()` so that any subcommand can, if
it needs it, have policies loaded consistenly. This is controlled via
the `load_policy` class attr, which defaults to True. Future subcommands
that do not need to have a policy loaded (E.G. the planned `info`
command), can simply set this to False in order to skip this step.
Moves `SoSComponent()` out of sos/__init__.py and into sos/component.py
to ease import conflicts arising from policy loading.
Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
-rw-r--r-- | sos/__init__.py | 145 | ||||
-rw-r--r-- | sos/component.py | 171 |
2 files changed, 172 insertions, 144 deletions
diff --git a/sos/__init__.py b/sos/__init__.py index 10f67b93..e367d6a7 100644 --- a/sos/__init__.py +++ b/sos/__init__.py @@ -22,150 +22,7 @@ import sys import tempfile from argparse import ArgumentParser -from sos.options import SoSOptions, SosListOption -from sos.utilities import TempFileUtil - - -class SoSComponent(): - """Any sub-command that sos supports needs to subclass SoSComponent in - order to be properly supported by the sos binary. - - This class contains the standardized entrypoint for subcommands, as well as - building out supported options from both globally shared option lists, and - options supported by that specific subcommand. - - When sos initializes, it will load an unintialized instance of each class - found within one recursion of the module root directory that subclasses - SoSComponent. - - If sos is able to match the user-specified subcommand to one that exists - locally, then that SoSComponent is initialized, logging is setup, and a - policy is loaded. From there, the component's execute() method takes over. - - Added in 4.0 - """ - - desc = 'unset' - - arg_defaults = {} - configure_logging = True - - _arg_defaults = { - "config_file": '/etc/sos.conf', - "quiet": False, - "tmp_dir": '', - "sysroot": None, - "verbosity": 0 - } - - def __init__(self, parser, parsed_args, cmdline_args): - self.parser = parser - self.args = parsed_args - self.cmdline = cmdline_args - self.exit_process = False - - try: - import signal - signal.signal(signal.SIGTERM, self.get_exit_handler()) - except Exception: - pass - - # update args from component's arg_defaults defintion - self._arg_defaults.update(self.arg_defaults) - self.opts = self.load_options() - if self.configure_logging: - tmpdir = self.opts.tmp_dir or tempfile.gettempdir() - self.tmpdir = tempfile.mkdtemp(prefix="sos.", dir=tmpdir) - self.tempfile_util = TempFileUtil(self.tmpdir) - self._setup_logging() - - def get_exit_handler(self): - def exit_handler(signum, frame): - self.exit_process = True - self._exit() - return exit_handler - - def _exit(self, error=0): - raise SystemExit(error) - - @classmethod - def add_parser_options(cls, parser): - """This should be overridden by each subcommand to add its own unique - options to the parser - """ - pass - - def load_options(self): - """Compile arguments loaded from defaults, config files, and the command - line into a usable set of options - """ - # load the defaults defined by the component and the shared options - opts = SoSOptions(arg_defaults=self._arg_defaults) - - for option in self.parser._actions: - if option.default != '==SUPPRESS==': - option.default = None - - # load values from cmdline - cmdopts = SoSOptions().from_args(self.parser.parse_args()) - opts.merge(cmdopts) - - # load values from config file - opts.update_from_conf(opts.config_file) - - return opts - - def _setup_logging(self): - """Creates the log handler that shall be used by all components and any - and all related bits to those components that need to log either to the - console or to the log file for that run of sos. - """ - # main soslog - self.soslog = logging.getLogger('sos') - self.soslog.setLevel(logging.DEBUG) - self.sos_log_file = self.get_temp_file() - flog = logging.StreamHandler(self.sos_log_file) - flog.setFormatter(logging.Formatter( - '%(asctime)s %(levelname)s: %(message)s')) - flog.setLevel(logging.INFO) - self.soslog.addHandler(flog) - - if not self.opts.quiet: - console = logging.StreamHandler(sys.stdout) - console.setFormatter(logging.Formatter('%(message)s')) - if self.opts.verbosity and self.opts.verbosity > 1: - console.setLevel(logging.DEBUG) - flog.setLevel(logging.DEBUG) - elif self.opts.verbosity and self.opts.verbosity > 0: - console.setLevel(logging.INFO) - flog.setLevel(logging.DEBUG) - else: - console.setLevel(logging.WARNING) - self.soslog.addHandler(console) - # log ERROR or higher logs to stderr instead - console_err = logging.StreamHandler(sys.stderr) - console_err.setFormatter(logging.Formatter('%(message)s')) - console_err.setLevel(logging.ERROR) - self.soslog.addHandler(console_err) - - # ui log - self.ui_log = logging.getLogger('sos_ui') - self.ui_log.setLevel(logging.INFO) - self.sos_ui_log_file = self.get_temp_file() - ui_fhandler = logging.StreamHandler(self.sos_ui_log_file) - ui_fhandler.setFormatter(logging.Formatter( - '%(asctime)s %(levelname)s: %(message)s')) - - self.ui_log.addHandler(ui_fhandler) - - if not self.opts.quiet: - ui_console = logging.StreamHandler(sys.stdout) - ui_console.setFormatter(logging.Formatter('%(message)s')) - ui_console.setLevel(logging.INFO) - self.ui_log.addHandler(ui_console) - - def get_temp_file(self): - return self.tempfile_util.new() +from sos.options import SosListOption class SoS(): diff --git a/sos/component.py b/sos/component.py new file mode 100644 index 00000000..16f77479 --- /dev/null +++ b/sos/component.py @@ -0,0 +1,171 @@ +# Copyright 2020 Red Hat, Inc. +# Author: Jake Hunsaker <jhunsake@redhat.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. + +import logging +import tempfile +import sys +import sos.policies + +from argparse import SUPPRESS +from sos.options import SoSOptions +from sos.utilities import TempFileUtil + + +class SoSComponent(): + """Any sub-command that sos supports needs to subclass SoSComponent in + order to be properly supported by the sos binary. + + This class contains the standardized entrypoint for subcommands, as well as + building out supported options from both globally shared option lists, and + options supported by that specific subcommand. + + When sos initializes, it will load an unintialized instance of each class + found within one recursion of the module root directory that subclasses + SoSComponent. + + If sos is able to match the user-specified subcommand to one that exists + locally, then that SoSComponent is initialized, logging is setup, and a + policy is loaded. From there, the component's execute() method takes over. + + Added in 4.0 + """ + + desc = 'unset' + + arg_defaults = {} + configure_logging = True + load_policy = True + + _arg_defaults = { + "config_file": '/etc/sos.conf', + "quiet": False, + "tmp_dir": '', + "sysroot": None, + "verbosity": 0 + } + + def __init__(self, parser, parsed_args, cmdline_args): + self.parser = parser + self.args = parsed_args + self.cmdline = cmdline_args + self.exit_process = False + + try: + import signal + signal.signal(signal.SIGTERM, self.get_exit_handler()) + except Exception: + pass + + # update args from component's arg_defaults defintion + self._arg_defaults.update(self.arg_defaults) + self.opts = self.load_options() + if self.configure_logging: + tmpdir = self.opts.tmp_dir or tempfile.gettempdir() + self.tmpdir = tempfile.mkdtemp(prefix="sos.", dir=tmpdir) + self.tempfile_util = TempFileUtil(self.tmpdir) + self._setup_logging() + + if self.load_policy: + try: + self.policy = sos.policies.load(sysroot=self.opts.sysroot) + except KeyboardInterrupt: + self._exit(0) + self._is_root = self.policy.is_root() + + def get_exit_handler(self): + def exit_handler(signum, frame): + self.exit_process = True + self._exit() + return exit_handler + + def _exit(self, error=0): + raise SystemExit(error) + + @classmethod + def add_parser_options(cls, parser): + """This should be overridden by each subcommand to add its own unique + options to the parser + """ + pass + + def load_options(self): + """Compile arguments loaded from defaults, config files, and the command + line into a usable set of options + """ + # load the defaults defined by the component and the shared options + opts = SoSOptions(arg_defaults=self._arg_defaults) + + for option in self.parser._actions: + if option.default != SUPPRESS: + option.default = None + + # load values from cmdline + cmdopts = SoSOptions().from_args(self.parser.parse_args()) + opts.merge(cmdopts) + + # load values from config file + opts.update_from_conf(opts.config_file) + + return opts + + def _setup_logging(self): + """Creates the log handler that shall be used by all components and any + and all related bits to those components that need to log either to the + console or to the log file for that run of sos. + """ + # main soslog + self.soslog = logging.getLogger('sos') + self.soslog.setLevel(logging.DEBUG) + self.sos_log_file = self.get_temp_file() + flog = logging.StreamHandler(self.sos_log_file) + flog.setFormatter(logging.Formatter( + '%(asctime)s %(levelname)s: %(message)s')) + flog.setLevel(logging.INFO) + self.soslog.addHandler(flog) + + if not self.opts.quiet: + console = logging.StreamHandler(sys.stdout) + console.setFormatter(logging.Formatter('%(message)s')) + if self.opts.verbosity and self.opts.verbosity > 1: + console.setLevel(logging.DEBUG) + flog.setLevel(logging.DEBUG) + elif self.opts.verbosity and self.opts.verbosity > 0: + console.setLevel(logging.INFO) + flog.setLevel(logging.DEBUG) + else: + console.setLevel(logging.WARNING) + self.soslog.addHandler(console) + # log ERROR or higher logs to stderr instead + console_err = logging.StreamHandler(sys.stderr) + console_err.setFormatter(logging.Formatter('%(message)s')) + console_err.setLevel(logging.ERROR) + self.soslog.addHandler(console_err) + + # ui log + self.ui_log = logging.getLogger('sos_ui') + self.ui_log.setLevel(logging.INFO) + self.sos_ui_log_file = self.get_temp_file() + ui_fhandler = logging.StreamHandler(self.sos_ui_log_file) + ui_fhandler.setFormatter(logging.Formatter( + '%(asctime)s %(levelname)s: %(message)s')) + + self.ui_log.addHandler(ui_fhandler) + + if not self.opts.quiet: + ui_console = logging.StreamHandler(sys.stdout) + ui_console.setFormatter(logging.Formatter('%(message)s')) + ui_console.setLevel(logging.INFO) + self.ui_log.addHandler(ui_console) + + def get_temp_file(self): + return self.tempfile_util.new() + +# vim: set et ts=4 sw=4 : |