aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sos/plugins/__init__.py151
1 files changed, 83 insertions, 68 deletions
diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py
index 142301fd..85f72cbd 100644
--- a/sos/plugins/__init__.py
+++ b/sos/plugins/__init__.py
@@ -1,30 +1,31 @@
-## This exports methods available for use by plugins for sos
+# Copyright (C) 2006 Steve Conklin <sconklin@redhat.com>
-## Copyright (C) 2006 Steve Conklin <sconklin@redhat.com>
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
-### This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-## GNU General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+""" This exports methods available for use by plugins for sos """
from __future__ import with_statement
-from sos.utilities import sos_get_command_output, import_module, grep, fileobj, tail
+from sos.utilities import (sos_get_command_output, import_module, grep,
+ fileobj, tail)
import os
import glob
import re
-import traceback
import stat
from time import time
+# FIXME: Is itertools still used here?
from itertools import *
import logging
import fnmatch
@@ -33,10 +34,6 @@ import fnmatch
import six
from six.moves import zip, filter
-try:
- import json
-except ImportError:
- import simplejson as json
def regex_findall(regex, fname):
'''Return a list of all non overlapping matches in the string(s)'''
@@ -46,6 +43,7 @@ def regex_findall(regex, fname):
except AttributeError:
return []
+
def mangle_command(command):
# FIXME: this can be improved
mangledname = re.sub(r"^/(usr/|)(bin|sbin)/", "", command)
@@ -53,23 +51,26 @@ def mangle_command(command):
mangledname = re.sub(r"/", ".", mangledname).strip(" ._-")[0:64]
return mangledname
+
def _path_in_path_list(path, path_list):
return any(p in path for p in path_list)
+
def _node_type(st):
""" return a string indicating the type of special node represented by
the stat buffer st (block, character, fifo, socket).
"""
_types = [
- ( stat.S_ISBLK, "block device" ),
- ( stat.S_ISCHR, "character device" ),
- ( stat.S_ISFIFO, "named pipe" ),
- ( stat.S_ISSOCK, "socket" )
+ (stat.S_ISBLK, "block device"),
+ (stat.S_ISCHR, "character device"),
+ (stat.S_ISFIFO, "named pipe"),
+ (stat.S_ISSOCK, "socket")
]
for t in _types:
if t[0](st.st_mode):
return t[1]
-
+
+
class Plugin(object):
""" This is the base class for sosreport plugins. Plugins should subclass
this and set the class variables where applicable.
@@ -87,8 +88,8 @@ class Plugin(object):
packages (files) is an iterable of the names of packages (the paths
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.
+ or files is found on the system, the default implementation of
+ check_enabled will return True.
"""
plugin_name = None
@@ -113,12 +114,14 @@ class Plugin(object):
self.copy_strings = []
self.collect_cmds = []
- self.soslog = self.commons['soslog'] if 'soslog' in self.commons else logging.getLogger('sos')
+ self.soslog = self.commons['soslog'] if 'soslog' in self.commons \
+ else logging.getLogger('sos')
# get the option list into a dictionary
for opt in self.option_list:
self.opt_names.append(opt[0])
- self.opt_parms.append({'desc':opt[1], 'speed':opt[2], 'enabled':opt[3]})
+ self.opt_parms.append({'desc': opt[1], 'speed': opt[2],
+ 'enabled': opt[3]})
@classmethod
def name(class_):
@@ -129,13 +132,13 @@ class Plugin(object):
return class_.plugin_name
return class_.__name__.lower()
- def _format_msg(self,msg):
+ def _format_msg(self, msg):
return "[plugin:%s] %s" % (self.name(), msg)
def log_error(self, msg):
self.soslog.error(self._format_msg(msg))
- def log_warn(self,msg):
+ def log_warn(self, msg):
self.soslog.warning(self._format_msg(msg))
def log_info(self, msg):
@@ -164,7 +167,7 @@ class Plugin(object):
'''
globstr = '*' + cmd + '*'
self.log_debug("substituting '%s' for '%s' in commands matching '%s'"
- % (subst, regexp, globstr))
+ % (subst, regexp, globstr))
if not self.executed_commands:
return 0
@@ -173,14 +176,14 @@ class Plugin(object):
try:
for called in self.executed_commands:
# was anything collected?
- if called['file'] == None:
+ if called['file'] is None:
continue
if fnmatch.fnmatch(called['exe'], globstr):
path = os.path.join(self.commons['cmddir'], called['file'])
self.log_debug("applying substitution to '%s'" % path)
readable = self.archive.open_file(path)
result, replacements = re.subn(
- regexp, subst, readable.read())
+ regexp, subst, readable.read())
if replacements:
self.archive.add_string(result, path)
@@ -201,7 +204,7 @@ class Plugin(object):
try:
path = self._get_dest_for_srcpath(srcpath)
self.log_debug("substituting '%s' for '%s' in '%s'"
- % (subst, regexp, path))
+ % (subst, regexp, path))
if not path:
return 0
readable = self.archive.open_file(path)
@@ -242,9 +245,9 @@ class Plugin(object):
# adjust the target used inside the report to always be relative
if os.path.isabs(linkdest):
reldest = os.path.relpath(linkdest,
- os.path.dirname(srcpath))
+ os.path.dirname(srcpath))
self.log_debug("made link target '%s' relative as '%s'"
- % (linkdest, reldest))
+ % (linkdest, reldest))
else:
reldest = linkdest
@@ -252,7 +255,7 @@ class Plugin(object):
% (srcpath, linkdest, os.path.isdir(absdest)))
# use the relative target path in the tarball
- self.archive.add_link(reldest,srcpath)
+ self.archive.add_link(reldest, srcpath)
if os.path.isdir(absdest):
self.log_debug("link '%s' is a directory, skipping..." % linkdest)
@@ -264,14 +267,15 @@ class Plugin(object):
% (linkdest, absdest))
self.do_copy_path(absdest)
- self.copied_files.append({'srcpath':srcpath,
- 'dstpath':srcpath,
- 'symlink':"yes",
- 'pointsto':linkdest})
+ self.copied_files.append({'srcpath': srcpath,
+ 'dstpath': srcpath,
+ 'symlink': "yes",
+ 'pointsto': linkdest})
def copy_dir(self, srcpath):
for afile in os.listdir(srcpath):
- self.log_debug("recursively adding '%s' from '%s'" % (afile, srcpath))
+ self.log_debug("recursively adding '%s' from '%s'" % (afile,
+ srcpath))
self.do_copy_path(os.path.join(srcpath, afile), dest=None)
def _get_dest_for_srcpath(self, srcpath):
@@ -319,12 +323,13 @@ class Plugin(object):
# handle special nodes (block, char, fifo, socket)
if not (stat.S_ISREG(st.st_mode) or stat.S_ISDIR(st.st_mode)):
ntype = _node_type(st)
- self.log_debug("creating %s node at archive:'%s'" % (ntype, srcpath))
+ self.log_debug("creating %s node at archive:'%s'" % (ntype,
+ srcpath))
self.copy_node(srcpath, st)
return
# if we get here, it's definitely a regular file (not a symlink or dir)
- self.log_debug("copying path '%s' to archive:'%s'" % (srcpath,dest))
+ self.log_debug("copying path '%s' to archive:'%s'" % (srcpath, dest))
# if not readable(srcpath)
if not (st.st_mode & 0o444):
@@ -333,9 +338,9 @@ class Plugin(object):
else:
self.archive.add_file(srcpath, dest)
- self.copied_files.append({'srcpath':srcpath,
- 'dstpath':dest,
- 'symlink':"no"})
+ self.copied_files.append({'srcpath': srcpath,
+ 'dstpath': dest,
+ 'symlink': "no"})
def add_forbidden_path(self, forbiddenPath):
"""Specify a path to not copy, even if it's part of a copy_specs[]
@@ -381,7 +386,7 @@ class Plugin(object):
for name, parms in zip(self.opt_names, self.opt_parms):
if _check(name):
val = parms['enabled']
- if val != None:
+ if val is not None:
return val
items = six.iteritems(self.commons.get('global_plugin_options', {}))
@@ -416,7 +421,7 @@ class Plugin(object):
return
current_size = 0
limit_reached = False
- sizelimit *= 1024 * 1024 # in MB
+ sizelimit *= 1024 * 1024 # in MB
_file = None
for _file in files:
@@ -459,7 +464,7 @@ class Plugin(object):
result = sos_get_command_output(prog, timeout=timeout, runat=runat)
if result['status'] == 124:
self.log_warn("command '%s' timed out after %ds"
- % (prog, timeout))
+ % (prog, timeout))
if result['status'] == 127:
self.log_debug("could not run '%s': command not found" % prog)
return result
@@ -499,7 +504,7 @@ class Plugin(object):
command output
"""
cmd_output_path = os.path.join(self.archive.get_tmp_dir(),
- 'sos_commands', self.name())
+ 'sos_commands', self.name())
if name:
cmd_output_path = os.path.join(cmd_output_path, name)
if make:
@@ -520,17 +525,18 @@ class Plugin(object):
def make_command_filename(self, exe):
"""The internal function to build up a filename based on a command."""
- outfn = os.path.join(self.commons['cmddir'], self.name(), self.mangle_command(exe))
+ outfn = os.path.join(self.commons['cmddir'], self.name(),
+ self.mangle_command(exe))
# check for collisions
if os.path.exists(outfn):
inc = 2
while True:
- newfn = "%s_%d" % (outfn, inc)
- if not os.path.exists(newfn):
- outfn = newfn
- break
- inc +=1
+ newfn = "%s_%d" % (outfn, inc)
+ if not os.path.exists(newfn):
+ outfn = newfn
+ break
+ inc += 1
return outfn
@@ -538,7 +544,7 @@ class Plugin(object):
"""Add a string to the archive as a file named `filename`"""
self.copy_strings.append((content, filename))
content = "..." + (content.splitlines()[0]).decode('utf8')
- self.log_debug("added string '%s' as '%s'" % (content,filename))
+ self.log_debug("added string '%s' as '%s'" % (content, filename))
def get_cmd_output_now(self, exe, suggest_filename=None,
root_symlink=False, timeout=300,
@@ -550,7 +556,8 @@ class Plugin(object):
result = self.get_command_output(exe, timeout=timeout, runat=runat)
if (result['status'] == 127):
return None
- self.log_debug("collected output of '%s' in %s" % (exe.split()[0], time() - start))
+ self.log_debug("collected output of '%s' in %s" % (exe.split()[0],
+ time() - start))
if suggest_filename:
outfn = self.make_command_filename(suggest_filename)
@@ -563,7 +570,8 @@ class Plugin(object):
self.archive.add_link(outfn, root_symlink)
# save info for later
- self.executed_commands.append({'exe': exe, 'file':outfn_strip}) # save in our list
+ # save in our list
+ self.executed_commands.append({'exe': exe, 'file': outfn_strip})
self.commons['xmlreport'].add_command(cmdline=exe,
exitcode=result['status'],
f_stdout=outfn_strip)
@@ -604,10 +612,13 @@ class Plugin(object):
def collect_strings(self):
for string, file_name in self.copy_strings:
content = "..." + (string.splitlines()[0]).decode('utf8')
- self.log_info("collecting string '%s' as '%s'" % (content, file_name))
+ self.log_info("collecting string '%s' as '%s'" % (content,
+ file_name))
try:
self.archive.add_string(string,
- os.path.join('sos_strings', self.name(), file_name))
+ os.path.join('sos_strings',
+ self.name(),
+ file_name))
except Exception as e:
self.log_debug("could not add string '%s': %s"
% (file_name, e))
@@ -669,8 +680,8 @@ class Plugin(object):
pass
def report(self):
- """ Present all information that was gathered in an html file that allows browsing
- the results.
+ """ Present all information that was gathered in an html file that
+ allows browsing the results.
"""
# make this prettier
html = '<hr/><a name="%s"></a>\n' % self.name()
@@ -683,7 +694,7 @@ class Plugin(object):
html = html + "<p>Files copied:<br><ul>\n"
for afile in self.copied_files:
html = html + '<li><a href="%s">%s</a>' % \
- (".." + afile['dstpath'], afile['srcpath'])
+ (".." + afile['dstpath'], afile['srcpath'])
if (afile['symlink'] == "yes"):
html = html + " (symlink to %s)" % afile['pointsto']
html = html + '</li>\n'
@@ -696,10 +707,10 @@ class Plugin(object):
# don't use relpath - these are HTML paths not OS paths.
for cmd in self.executed_commands:
if cmd["file"] and len(cmd["file"]):
- cmd_rel_path = "../" + self.commons['cmddir'] \
- + "/" + cmd['file']
+ cmd_rel_path = "../" + self.commons['cmddir'] \
+ + "/" + cmd['file']
html = html + '<li><a href="%s">%s</a></li>\n' % \
- (cmd_rel_path, cmd['exe'])
+ (cmd_rel_path, cmd['exe'])
else:
html = html + '<li>%s</li>\n' % (cmd['exe'])
html = html + "</ul></p>\n"
@@ -723,18 +734,22 @@ class RedHatPlugin(object):
"""Tagging class to indicate that this plugin works with Red Hat Linux"""
pass
+
class UbuntuPlugin(object):
"""Tagging class to indicate that this plugin works with Ubuntu Linux"""
pass
+
class DebianPlugin(object):
"""Tagging class to indicate that this plugin works with Debian Linux"""
pass
+
class IndependentPlugin(object):
"""Tagging class that indicates this plugin can run on any platform"""
pass
+
def import_plugin(name, superclasses=None):
"""Import name as a module and return a list of all classes defined in that
module. superclasses should be a tuple of valid superclasses to import,