From 2b9cf8b0746c0125d4e4516b03d3add39d0adaad Mon Sep 17 00:00:00 2001 From: "Bryn M. Reeves" Date: Tue, 16 Sep 2014 11:07:44 +0100 Subject: [sosreport] initial profiles support Add simple profile support. Plugins can define a 'profiles' attribute that contains a list of profile names that this plugin belongs to. If a profile is given on the command line only plugins belonging to that profile will run. Two new command-line options are introduced in thie commit: --list-profiles --profile=NAME Profiles can be combined with -l/--list-plugins in order to see the effect of the profile on the set of enabled plugins. Fixes #247. Signed-off-by: Bryn M. Reeves --- man/en/sosreport.1 | 24 +++++++++++---- sos/plugins/__init__.py | 5 ++++ sos/sosreport.py | 80 ++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 102 insertions(+), 7 deletions(-) diff --git a/man/en/sosreport.1 b/man/en/sosreport.1 index df4f1cd3..5cc0298c 100644 --- a/man/en/sosreport.1 +++ b/man/en/sosreport.1 @@ -12,15 +12,16 @@ sosreport \- Collect and package diagnostic and support data [--batch] [--build] [--debug]\fR [--name name] [--case-id id] [--ticket-number nr] [--tmp-dir directory]\fR + [-p|--profile profile-name]\fR + [--list-profiles]\fR [-z|--compression-type method]\fR [--help]\fR .SH DESCRIPTION -\fBsosreport\fR generates a compressed tar archive of diagnostic -information from the running system. The archive may be stored -locally or centrally for recording or tracking purposes or may -be sent to technical support representatives, developers or -system administrators to assist with technical fault-finding and -debugging. +\fBsosreport\fR generates an archive of configuration and diagnostic +information from the running system. The archive may be stored locally +or centrally for recording or tracking purposes or may be sent to +technical support representatives, developers or system administrators +to assist with technical fault-finding and debugging. .LP Sos is modular in design and is able to collect data from a wide range of subsystems and packages that may be installed. An @@ -69,6 +70,17 @@ Specify alternate configuration file. Specify alternate temporary directory to copy data as well as the compressed report. .TP +.B \--list-profiles +Display a list of available profiles and the plugins that they enable. +.TP +.B \-p, \--profile NAME +Only run plugins that correspond to the given profile. Multple profiles +may be specified as a comma-separated list; the set of plugins executed +is the union of each of the profile's plugin sets. Currently defined +profiles include: boot, cluster, desktop, debug, hardware, identity, +network, openstack, packagemanager, security, services, storage, +sysmgmt, system, performance, virt, and webserver. +.TP .B \-z, \--compression-type METHOD Override the default compression type specified by the active policy. .TP diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py index 3e8d9f0b..9ad079d2 100644 --- a/sos/plugins/__init__.py +++ b/sos/plugins/__init__.py @@ -88,6 +88,10 @@ class Plugin(object): of files) to check for before running this plugin. If any of these packages or files is found on the system, the default implementation of check_enabled will return True. + + profiles is an iterable of profile names that this plugin belongs to. + Whenever any of the profiles is selected on the command line the plugin + will be enabled (subject to normal check_enabled tests). """ plugin_name = None @@ -96,6 +100,7 @@ class Plugin(object): packages = () files = () archive = None + profiles = () def __init__(self, commons): if not getattr(self, "option_list", False): diff --git a/sos/sosreport.py b/sos/sosreport.py index bf9adafe..4e2f2685 100644 --- a/sos/sosreport.py +++ b/sos/sosreport.py @@ -219,6 +219,8 @@ class SoSOptions(object): _debug = False _case_id = "" _customer_name = "" + _profiles = deque() + _list_profiles = False _config_file = "" _tmp_dir = "" _report = True @@ -436,6 +438,28 @@ class SoSOptions(object): self._check_options_initialized() self._customer_name = value + @property + def profiles(self): + if self._options is not None: + return self._options.profiles + return self._profiles + + @profiles.setter + def profiles(self, value): + self._check_options_initialized() + self._profiles = value + + @property + def list_profiles(self): + if self._options is not None: + return self._options.list_profiles + return self._list_profiles + + @list_profiles.setter + def list_profiles(self, value): + self._check_options_initialized() + self._list_profiles = value + @property def config_file(self): if self._options is not None: @@ -538,6 +562,11 @@ class SoSOptions(object): parser.add_option("--case-id", action="store", dest="case_id", help="specify case identifier") + parser.add_option("-p", "--profile", action="extend", + dest="profiles", type="string", default=deque(), + help="enable plugins selected by the given profiles") + parser.add_option("--list-profiles", action="store_true", + dest="list_profiles", default=False) parser.add_option("--name", action="store", dest="customer_name", help="specify report name") @@ -750,6 +779,13 @@ class SoSReport(object): self.config.get("plugins", "disable").split(',')] return disabled + def _is_in_profile(self, plugin_class, profiles): + if not len(profiles): + return True + if not hasattr(plugin_class, "profiles"): + return False + return any([p in profiles for p in plugin_class.profiles]) + def _is_skipped(self, plugin_name): return (plugin_name in self.opts.noplugins or plugin_name in self._get_disabled_plugins()) @@ -787,7 +823,7 @@ class SoSReport(object): helper = ImporterHelper(sos.plugins) plugins = helper.get_modules() self.plugin_names = deque() - + self.profiles = set() # validate and load plugins for plug in plugins: plugbase, ext = os.path.splitext(plug) @@ -814,6 +850,12 @@ class SoSReport(object): # plug-in is valid, let's decide whether run it or not self.plugin_names.append(plugbase) + if hasattr(plugin_class, "profiles"): + self.profiles.update(plugin_class.profiles) + + if not self._is_in_profile(plugin_class, self.opts.profiles): + self._skip(plugin_class, _("excluded")) + continue if self._is_skipped(plugbase): self._skip(plugin_class, _("skipped")) @@ -960,6 +1002,39 @@ class SoSReport(object): self.ui_log.info("") + def list_profiles(self): + if not self.profiles: + self.soslog.fatal(_("no valid profiles found")) + return + self.ui_log.info(_("The following profiles are available:")) + self.ui_log.info("") + + def _has_prof(c): + return hasattr(c, "profiles") + + profiles = list(self.profiles) + profiles.sort() + for profile in profiles: + plugins = [] + for name, plugin in self.loaded_plugins: + if _has_prof(plugin) and profile in plugin.profiles: + plugins.append(name) + lines = [] + line = "" + for name in plugins: + if len(line) + len(name) + 2 > 62: + lines.append(line) + line = "" + line = line + name + ', ' + if line[-2:] == ', ': + line = line[:-2] + lines.append(line) + self.ui_log.info(" %-15s %s" % (profile, lines[0])) + lines.pop(0) + for line in lines: + self.ui_log.info(" %-15s %s" % ("", line)) + self.ui_log.info("") + def batch(self): if self.opts.batch: self.ui_log.info(self.policy.get_msg()) @@ -1273,6 +1348,9 @@ class SoSReport(object): if self.opts.list_plugins: self.list_plugins() return True + if self.opts.list_profiles: + self.list_profiles() + return True # verify that at least one plug-in is enabled if not self.verify_plugins(): -- cgit