Merge "Handle driver initialization errors to avoid service crash"

This commit is contained in:
Zuul 2019-11-18 17:24:16 +00:00 committed by Gerrit Code Review
commit e022fdaa8e
6 changed files with 45 additions and 21 deletions

View File

@ -36,9 +36,9 @@ class ElasticsearchDriver(base.Driver):
from elasticsearch import Elasticsearch from elasticsearch import Elasticsearch
except ImportError: except ImportError:
raise exc.CommandError( raise exc.CommandError(
"To use this command, you should install " "To use OSProfiler with ElasticSearch driver, "
"'elasticsearch' manually. Use command:\n " "please install `elasticsearch` library. "
"'pip install elasticsearch'.") "To install with pip:\n `pip install elasticsearch`.")
client_url = parser.urlunparse(parser.urlparse(self.connection_str) client_url = parser.urlunparse(parser.urlparse(self.connection_str)
._replace(scheme="http")) ._replace(scheme="http"))

View File

@ -40,8 +40,8 @@ class Jaeger(base.Driver):
except ImportError: except ImportError:
raise exc.CommandError( raise exc.CommandError(
"To use OSProfiler with Uber Jaeger tracer, " "To use OSProfiler with Uber Jaeger tracer, "
"you have to install `jaeger-client` manually. " "please install `jaeger-client` library. "
"Install with pip:\n `pip install jaeger-client`." "To install with pip:\n `pip install jaeger-client`."
) )
parsed_url = parser.urlparse(connection_str) parsed_url = parser.urlparse(connection_str)

View File

@ -28,9 +28,9 @@ class MongoDB(base.Driver):
from pymongo import MongoClient from pymongo import MongoClient
except ImportError: except ImportError:
raise exc.CommandError( raise exc.CommandError(
"To use this command, you should install " "To use OSProfiler with MongoDB driver, "
"'pymongo' manually. Use command:\n " "please install `pymongo` library. "
"'pip install pymongo'.") "To install with pip:\n `pip install pymongo`.")
client = MongoClient(self.connection_str, connect=False) client = MongoClient(self.connection_str, connect=False)
self.db = client[db_name] self.db = client[db_name]

View File

@ -40,9 +40,9 @@ class Redis(base.Driver):
from redis import StrictRedis from redis import StrictRedis
except ImportError: except ImportError:
raise exc.CommandError( raise exc.CommandError(
"To use this command, you should install " "To use OSProfiler with Redis driver, "
"'redis' manually. Use command:\n " "please install `redis` library. "
"'pip install redis'.") "To install with pip:\n `pip install redis`.")
# only connection over network is supported with schema # only connection over network is supported with schema
# redis://[:password]@host[:port][/db] # redis://[:password]@host[:port][/db]

View File

@ -13,16 +13,21 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import logging
from osprofiler.drivers import base from osprofiler.drivers import base
LOG = logging.getLogger(__name__)
def _noop_notifier(info, context=None): def _noop_notifier(info, context=None):
"""Do nothing on notify().""" """Do nothing on notify()."""
# NOTE(boris-42): By default we are using noop notifier. # NOTE(boris-42): By default we are using noop notifier.
__notifier = _noop_notifier __notifier = _noop_notifier
__driver_cache = {} __notifier_cache = {} # map: connection-string -> notifier
def notify(info): def notify(info):
@ -54,14 +59,24 @@ def create(connection_string, *args, **kwargs):
:param connection_string: connection string which specifies the storage :param connection_string: connection string which specifies the storage
driver for notifier driver for notifier
:param *args: args that will be passed to the driver's __init__ method :param args: args that will be passed to the driver's __init__ method
:param **kwargs: kwargs that will be passed to the driver's __init__ method :param kwargs: kwargs that will be passed to the driver's __init__ method
:returns: Callable notifier method :returns: Callable notifier method
:raises TypeError: In case of invalid name of plugin raises TypeError
""" """
global __driver_cache global __notifier_cache
if connection_string not in __driver_cache: if connection_string not in __notifier_cache:
__driver_cache[connection_string] = base.get_driver(connection_string, try:
*args, driver = base.get_driver(connection_string, *args, **kwargs)
**kwargs).notify __notifier_cache[connection_string] = driver.notify
return __driver_cache[connection_string] LOG.info("osprofiler is enabled with connection string: %s",
connection_string)
except Exception:
LOG.exception("Could not initialize driver for connection string "
"%s, osprofiler is disabled", connection_string)
__notifier_cache[connection_string] = _noop_notifier
return __notifier_cache[connection_string]
def clear_notifier_cache():
__notifier_cache.clear()

View File

@ -23,6 +23,7 @@ class NotifierTestCase(test.TestCase):
def tearDown(self): def tearDown(self):
notifier.set(notifier._noop_notifier) # restore defaults notifier.set(notifier._noop_notifier) # restore defaults
notifier.clear_notifier_cache()
super(NotifierTestCase, self).tearDown() super(NotifierTestCase, self).tearDown()
def test_set(self): def test_set(self):
@ -49,3 +50,11 @@ class NotifierTestCase(test.TestCase):
result = notifier.create("test", 10, b=20) result = notifier.create("test", 10, b=20)
mock_factory.assert_called_once_with("test", 10, b=20) mock_factory.assert_called_once_with("test", 10, b=20)
self.assertEqual(mock_factory.return_value.notify, result) self.assertEqual(mock_factory.return_value.notify, result)
@mock.patch("osprofiler.notifier.base.get_driver")
def test_create_driver_init_failure(self, mock_get_driver):
mock_get_driver.side_effect = Exception()
result = notifier.create("test", 10, b=20)
mock_get_driver.assert_called_once_with("test", 10, b=20)
self.assertEqual(notifier._noop_notifier, result)