Add base for notifier plugins

OSprofiler will support notifers for different collectors.

So add base for plugins.

In next patch we will add osprofiler oslo.messaging based notifier

Change-Id: Iad14c67f1e7cdbd0f4bcc64200fecf048316e385
This commit is contained in:
Boris Pavlovic 2014-07-02 23:43:15 +04:00
parent 33fdfd7b83
commit 090e577244
9 changed files with 163 additions and 1 deletions

View File

View File

@ -0,0 +1,34 @@
# Copyright 2014 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 osprofiler import _utils as utils
class Notifier(object):
def notify(self, info):
"""This method will be called on each notifier.notify() call.
To add new drivers you should, create new subclass of this class and
implement notify method.
"""
@staticmethod
def factory(name, *args, **kwargs):
for driver in utils.itersubclasses(Notifier):
if name == driver.__name__:
return driver(*args, **kwargs).notify
raise TypeError("There is no driver, with name: %s" % name)

View File

@ -0,0 +1,14 @@
# Copyright 2014 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.

View File

@ -89,3 +89,24 @@ def signed_unpack(data, hmac_data, hmac_key):
return json.loads(binary_decode(base64.urlsafe_b64decode(data)))
except Exception:
return None
def itersubclasses(cls, _seen=None):
"""Generator over all subclasses of a given class in depth first order."""
if not isinstance(cls, type):
raise TypeError("itersubclasses must be called with "
"new-style classes, not %.100r" % cls)
_seen = _seen or set()
try:
subs = cls.__subclasses__()
except TypeError: # fails only when cls is type
subs = cls.__subclasses__(cls)
subs = cls.__subclasses__()
for sub in subs:
if sub not in _seen:
_seen.add(sub)
yield sub
for sub in itersubclasses(sub, _seen):
yield sub

View File

@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
from osprofiler._notifiers import base
def _noop_notifier(info):
"""Do nothing on notify()."""
@ -45,3 +47,15 @@ def set(notifier):
"""
global __notifier
__notifier = notifier
def create(plugin_name, *args, **kwargs):
"""Create notifier based on specified plugin_name
:param plugin_name: Name of plugin that creates notifier
:param *args: args that will be passed to plugin init method
:param **kwargs: kwargs that will be passed to plugin init method
:returns: Callable notifier method
:raise TypeError: In case of invalid name of plugin raises TypeError
"""
return base.Notifier.factory(plugin_name, *args, **kwargs)

View File

View File

@ -0,0 +1,48 @@
# Copyright 2014 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 osprofiler._notifiers import base
from tests import test
class NotifierBaseTestCase(test.TestCase):
def test_factory(self):
class A(base.Notifier):
def notify(self, a):
return a
self.assertEqual(base.Notifier.factory("A")(10), 10)
def test_factory_with_args(self):
class B(base.Notifier):
def __init__(self, a, b=10):
self.a = a
self.b = b
def notify(self, c):
return self.a + self.b + c
self.assertEqual(base.Notifier.factory("B", 5, b=7)(10), 22)
def test_factory_not_found(self):
self.assertRaises(TypeError, base.Notifier.factory, "non existing")
def test_notify(self):
base.Notifier().notify("")

View File

@ -13,8 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
from osprofiler import notifier
import mock
from osprofiler import notifier
from tests import test
@ -34,3 +35,17 @@ class NotifierTestCase(test.TestCase):
def test_get_default_notifier(self):
self.assertEqual(notifier.get(), notifier._noop_notifier)
def test_notify(self):
m = mock.MagicMock()
notifier.set(m)
notifier.notify(10)
m.assert_called_once_with(10)
@mock.patch("osprofiler.notifier.base.Notifier.factory")
def test_create(self, mock_factory):
result = notifier.create("test", 10, b=20)
mock_factory.assert_called_once_with("test", 10, b=20)
self.assertEqual(mock_factory.return_value, result)

View File

@ -76,3 +76,19 @@ class UtilsTestCase(test.TestCase):
hmac_data = utils.generate_hmac(data, hmac)
self.assertIsNone(utils.signed_unpack(data, hmac_data, hmac))
def test_itersubclasses(self):
class A(object):
pass
class B(A):
pass
class C(A):
pass
class D(C):
pass
self.assertEqual([B, C, D], list(utils.itersubclasses(A)))