Add plugin base

We have various kinds of plugins. For now they are next:

1) Benchmark Engines
2) Server Provides
3) Scenarios
4) Scenario Runners
5) Context
6) SLA
And maybe in future other stuff....

The core thing is next:
1) All type of plugins have configuration
2) All plugins should be accessable via unique name
3) All plugins should have ability to validate their configuration
4) All plugins should be deprecateble in the same way

So it really makes sense to create single base that adds
abbility to do all this stuff

Change-Id: Ic11b299e0ce09b72718f5bb5504fd56218397fe1
This commit is contained in:
Boris Pavlovic 2015-01-28 04:18:27 +03:00
parent 40f463f781
commit a6166a5e9c
3 changed files with 158 additions and 0 deletions

74
rally/common/plugin.py Normal file
View File

@ -0,0 +1,74 @@
# Copyright 2015: Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from rally.common import utils
from rally import exceptions
def deprecated(reason, rally_version):
"""Put this decorator on function or class to mark plugin as deprecated.
:param reason: Message that describes why plugin was deprecated
:param rally_version: version of Rally when this plugin was deprecated
"""
def wrapper(plugin):
plugin._plugin_deprecated = {
"reason": reason,
"rally_version": rally_version
}
return plugin
return wrapper
def plugin(name):
"""Put this decorator on top of plugin to specify it's name.
This will be used for everything except Scenarios plugins. They have
different nature.
:param name: name of plugin that is used for searching purpose
"""
def wrapper(plugin):
plugin._plugin_name = name
return plugin
return wrapper
class Plugin(object):
"""Use this class as a base for all plugins in Rally."""
@classmethod
def get_name(cls):
return getattr(cls, "_plugin_name", None)
@classmethod
def get(cls, name):
for _plugin in cls.get_all():
if _plugin.get_name() == name:
return _plugin
raise exceptions.NoSuchPlugin(name=name)
@classmethod
def get_all(cls):
return list(utils.itersubclasses(cls))
@classmethod
def is_deprecated(cls):
"""Return deprecation details for deprecated plugins."""
return getattr(cls, "_plugin_deprecated", False)

View File

@ -115,6 +115,10 @@ class NotFoundException(RallyException):
msg_fmt = _("Not found.")
class NoSuchPlugin(NotFoundException):
msg_fmt = _("There is no plugin with name: `%(name)s`.")
class NoSuchEngine(NotFoundException):
msg_fmt = _("There is no engine with name `%(engine_name)s`.")

View File

@ -0,0 +1,80 @@
# Copyright 2015: Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from rally.common import plugin
from rally import exceptions
from tests.unit import test
@plugin.plugin("base_plugin")
class BasePlugin(plugin.Plugin):
pass
@plugin.plugin("some_plugin")
class SomePlugin(BasePlugin):
pass
@plugin.deprecated("some_reason", "0.1.1")
@plugin.plugin("deprecated_plugin")
class DeprecatedPlugin(BasePlugin):
pass
class PluginModuleTestCase(test.TestCase):
def test_deprecated(self):
@plugin.deprecated("some", "0.0.1")
def func():
return 42
self.assertEqual(func._plugin_deprecated,
{"reason": "some", "rally_version": "0.0.1"})
self.assertEqual(func(), 42)
def test_plugin(self):
@plugin.plugin(name="test")
def func():
return 42
self.assertEqual(func._plugin_name, "test")
self.assertEqual(func(), 42)
class PluginTestCase(test.TestCase):
def test_get_name(self):
self.assertEqual("some_plugin", SomePlugin.get_name())
def test_get(self):
self.assertEqual(SomePlugin,
BasePlugin.get("some_plugin"))
def test_get_not_found(self):
self.assertRaises(exceptions.NoSuchPlugin,
BasePlugin.get, "non_existing")
def test_get_all(self):
self.assertEqual([SomePlugin, DeprecatedPlugin], BasePlugin.get_all())
self.assertEqual([], SomePlugin.get_all())
def test_is_deprecated(self):
self.assertFalse(SomePlugin.is_deprecated())
self.assertEqual(DeprecatedPlugin.is_deprecated(),
{"reason": "some_reason", "rally_version": "0.1.1"})