diff --git a/avocado/core/dispatcher.py b/avocado/core/dispatcher.py index a98a9ee4919ef89ffed360b233a85147d83bc42a..11494db41d7366f1f23577979ef707d7999c5821 100644 --- a/avocado/core/dispatcher.py +++ b/avocado/core/dispatcher.py @@ -16,10 +16,12 @@ import sys -from stevedore import ExtensionManager +from stevedore import EnabledExtensionManager +from .settings import settings -class Dispatcher(ExtensionManager): + +class Dispatcher(EnabledExtensionManager): """ Base dispatcher for various extension types @@ -28,10 +30,21 @@ class Dispatcher(ExtensionManager): def __init__(self, namespace): self.load_failures = [] super(Dispatcher, self).__init__(namespace=namespace, + check_func=self.enabled, invoke_on_load=True, on_load_failure_callback=self.store_load_failure, propagate_map_exceptions=True) + def enabled(self, extension): + namespace_prefix = 'avocado.plugins.' + if self.namespace.startswith(namespace_prefix): + namespace = self.namespace[len(namespace_prefix):] + else: + namespace = self.namespace + disabled = settings.get_value('plugins', 'disable', key_type=list) + fqn = "%s.%s" % (namespace, extension.entry_point.name) + return fqn not in disabled + @staticmethod def store_load_failure(manager, entrypoint, exception): manager.load_failures.append((entrypoint, exception)) diff --git a/docs/source/Plugins.rst b/docs/source/Plugins.rst index 031c511cd3216a9acc71fdbe502f06f49d00355a..411d878d061eb54846d958792ce7f081f6e4624c 100644 --- a/docs/source/Plugins.rst +++ b/docs/source/Plugins.rst @@ -81,6 +81,54 @@ to add to your setuptools based `setup.py` file something like:: Then, by running either ``$ python setup.py install`` or ``$ python setup.py develop`` your plugin should be visible to Avocado. +Fully qualified named for a plugin +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The plugin registry mentioned earlier, (`setuptools`_ and its `entry +points`_) is global to a given Python installation. Avocado uses the +namespace prefix ``avocado.plugins.`` to avoid name clashes with other +software. Now, inside Avocado itself, there's no need keep using the +``avocado.plugins.`` prefix. + +Take for instance, the Job Pre/Post plugins are defined on +``setup.py``:: + + 'avocado.plugins.job.prepost': [ + 'jobscripts = avocado.plugins.jobscripts:JobScripts' + ] + +The setuptools entry point namespace is composed of the mentioned +prefix ``avocado.plugins.``, which is is then followed by the Avocado +plugin type, in this case, ``job.prepost``. + +Inside avocado itself, the fully qualified name for a plugin is the +plugin type, such as ``job.prepost`` concatenated to the name used in +the entry point definition itself, in this case, ``jobscripts``. + +To summarize, still using the same example, the fully qualified +Avocado plugin name is going to be ``job.prepost.jobscripts``. + +Disabling a plugin +~~~~~~~~~~~~~~~~~~ + +Even though a plugin can be installed and registered under +`setuptools`_ `entry points`_, it can be explicitly disabled in +Avocado. + +The mechanism available to do so is to add entries to the ``disable`` +key under the ``plugins`` section of the Avocado configuration file. +Example:: + + [plugins] + disable = ['cli.hello', 'job.prepost.jobscripts'] + +The exact effect on Avocado when a plugin is disabled depends on the +plugin type. For instance, by disabling plugins of type ``cli.cmd``, +the command implemented by the plugin should no longer be available on +the Avocado command line application. Now, by disabling a +``job.prepost`` plugin, those won't be executed before/after the +execution of the jobs. + Wrap Up ~~~~~~~ diff --git a/etc/avocado/avocado.conf b/etc/avocado/avocado.conf index 5570fc2d2bf6fffc5e7bb4b05c7958208ed112a1..c6ec581e00c90ff8bf411ea6b70e541550927d7b 100644 --- a/etc/avocado/avocado.conf +++ b/etc/avocado/avocado.conf @@ -69,6 +69,9 @@ username = password = [plugins] +# Disable listed plugins completely. Use the fully qualified plugin +# name, as described in the Avocado documentation "Plugins" section. +disable = [] # Suppress notification about broken plugins in the app standard error. # Add the name of each broken plugin you want to suppress the notification # in the list. The names can be easily seen from the stderr messages. Example: diff --git a/selftests/functional/test_basic.py b/selftests/functional/test_basic.py index ddd0297712ffc4fb429c9c5050376173056cbe2e..72aa9683c5a7b32fe9fdf71cb53d14c800ce34e1 100644 --- a/selftests/functional/test_basic.py +++ b/selftests/functional/test_basic.py @@ -865,6 +865,28 @@ class PluginsTest(AbsPluginsTest, unittest.TestCase): (expected_rc, result)) self.assertNotIn('Disabled', output) + def test_disable_plugin(self): + os.chdir(basedir) + cmd_line = './scripts/avocado plugins' + result = process.run(cmd_line, ignore_status=True) + expected_rc = exit_codes.AVOCADO_ALL_OK + self.assertEqual(result.exit_status, expected_rc, + "Avocado did not return rc %d:\n%s" % + (expected_rc, result)) + self.assertIn("Collect system information", result.stdout) + + config_content = "[plugins]\ndisable=['cli.cmd.sysinfo',]" + config = script.TemporaryScript("disable_sysinfo_cmd.conf", + config_content) + with config: + cmd_line = './scripts/avocado --config %s plugins' % config + result = process.run(cmd_line, ignore_status=True) + expected_rc = exit_codes.AVOCADO_ALL_OK + self.assertEqual(result.exit_status, expected_rc, + "Avocado did not return rc %d:\n%s" % + (expected_rc, result)) + self.assertNotIn("Collect system information", result.stdout) + def test_Namespace_object_has_no_attribute(self): os.chdir(basedir) cmd_line = './scripts/avocado plugins'