-
Notifications
You must be signed in to change notification settings - Fork 545
How to Write a Plugin
Plugins live in the sos.plugins package. This is the first place that a proper python package exists on the python path (e.g. ./sos/plugins)
Essentially you need to create a class that inherits from Plugin
and at least one of the provided tagging superclasses.
from sos.plugins import Plugin, RedHatPlugin
class MyPlugin(Plugin, RedHatPlugin):
pass
In order for a plugin to actually do any work one of the hook methods must be defined. The setup
hook is called during normal collection on every enabled plugin.
from sos.plugins import Plugin, RedHatPlugin
class CopyTest(Plugin, RedHatPlugin):
"""This plugin copies some stuff"""
def setup(self):
self.add_copy_specs([
"/path/to/something/interesting",
"/path/to/something/else/interesting",
])
The above will copy the two files specified in the add_copy_specs
method to the final archive.
If you only need to add a single file you can call the add_copy_spec
method instead:
def setup(self):
self.add_copy_spec("/path/to/something/interesting")
A final version allows optional size-limiting and 'tailing' of files to limit the final report size. Currently this method should only be used for single files or globs that expand to a list of files; size-limiting is not applied to directories added recursively via this method.
def add_copy_spec_limit(self, copyspec, sizelimit=None, tailit=True):
"""Add a file or glob but limit it to sizelimit megabytes. If fname is
a single file the file will be tailed to meet sizelimit. If the first
file in a glob is too large it will be tailed to meet the sizelimit.
"""
If you wish to collect the output of a program as part of your collection process then you can call the add_cmd_output
method:
from sos.plugins import Plugin, RedHatPlugin
class CopyTest(Plugin, RedHatPlugin):
"""This plugin copies some stuff"""
def setup(self):
self.add_cmd_output("bin -v")
The add_cmd_output
method will execute it's argument without a shell using the PATH specified by the active policy. There is normally no need to specify an absolute path. If you need to use shell syntax this can be done by calling sh -c "<command string>"
.
The output of the command will be added to the report archive under sos_commands/plugin_name/mangled_command_name
. Mangling converts spaces to underscores and removes other characters that are illegal or problematic in path names.
Additionally, the command will be added to the report index automatically.
The add_cmd_output
method also accepts several keyword parameters to control the collection process:
def add_cmd_outputs(self, cmds, timeout=300, runat=None):
"""Run a list of programs and collect the output"""
The timeout
parameter sets a maximum time (in seconds) to wait for the child process to exit. After this time sos will abandon the child and continue with report generation. A directory may be specified via the runat
program. The child will switch to this directory before executing the command.
You can inform sosreport that your plugin should only run when certain conditions are met. The default behavior checks for the presence of files or packages specified in the plugin class. More complex checks can be implemented by overriding the checkenabled
method of the base Plugin
class.
from sos.plugins import Plugin, RedHatPlugin
class DepTest(Plugin, RedHatPlugin):
"""This plugin depends on a file or package"""
files = ('/path/to/file/I/need',)
packages = ('name-of-package',)
def setup(self):
self.add_copy_specs([
"/path/to/something",
"/path/to/something/else",
])
Note: if you use a tuple for files
or packages
be sure that you place the trailing comma in the case of a 1-tuple. ('some string')
does not create a 1-tuple, but ('some string',)
does.
Be aware that if any of the files or packages are found then the plugin will attempt to run. If you need to ensure that multiple files are in place or multiple packages are in place then you will want to implement your own checkenabled
method.
from sos.plugins import Plugin, RedHatPlugin
from os.path import exists
class DepTest(Plugin, RedHatPlugin):
"""This plugin depends on something"""
def checkenabled(self):
return exists('/path/to/thing/i/need') and exists('/path/to/other/thing/i/need')
def setup(self):
self.addCopySpecs([
"/path/to/something",
"/path/to/something/else",
])
Most plugins require super user access to perform their tasks. However, certain plugins may not have this requirement. If your plugin does not need root access then you can set a class-level variable requires_root
to False
.
from sos.plugins import Plugin, IndependentPlugin
class MyPlugin(Plugin, IndependentPlugin):
requires_root = False
def setup(self):
pass
If your plugin needs to run on multiple platforms you can do that a couple ways. If the rules for collection are the same on every platform (like for say JBOSS) then subclassing IndependentPlugin
is all you really need to do:
from sos.plugins import Plugin, IndependentPlugin
class MyPlugin(Plugin, IndependentPlugin):
def setup(self):
pass # do stuff
However, if you need to do different things on different platforms you need to define one plugin per platform, like so:
from sos.plugins import Plugin, RedHatPlugin, DebianPlugin
class MyRedHatPlugin(Plugin, RedHatPlugin):
name = "myplugin"
def setup(self):
pass # do red hat specific stuff
class MyDebianPlugin(Plugin, DebianPlugin):
name = "myplugin"
def setup(self):
pass # do debian specific stuff
Notice how both plugins have a class-level name
attribute. This should be the same for all platform-specific implementations of your plugin. The name attribute determines the name presented to the user for plugin selection as well as option definition.
In some cases you may wish to share certain bits between platform-specific plugins, in this case you can make a common shared superclass:
from sos.plugins import Plugin, RedHatPlugin, DebianPlugin
class MyPlugin(Plugin):
name = "myplugin"
def setup(self):
pass # do common things here
class MyRedHatPlugin(MyPlugin, RedHatPlugin):
def setup(self):
super(MyRedHatPlugin, self).setup()
pass # do red hat specific stuff
class MyDebianPlugin(MyPlugin, DebianPlugin):
def setup(self):
super(MyDebianPlugin, self).setup()
pass # do debian specific stuff
Note how the leaf classes are still the only ones that subclass things like RedHatPlugin
and DebianPlugin
. This ensures that your shared plugin class does not get run as a plugin on its own.