1685f5853a
In zaqar, there are some improperly log using in code tree. Like in some place, should use LOG.exception not LOG.error, repeat log calling, etc. Those could be optimized for better code. Change-Id: I99b830c4db4f2b9449cad713f37474f5ecbce05e Closes-Bug: #1543563
208 lines
6.8 KiB
Python
208 lines
6.8 KiB
Python
# Copyright (c) 2013 Rackspace, Inc.
|
|
#
|
|
# 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.
|
|
|
|
import copy
|
|
|
|
from oslo_config import cfg
|
|
from oslo_log import log
|
|
import six
|
|
from stevedore import driver
|
|
|
|
from zaqar.common import errors
|
|
from zaqar.common import utils
|
|
from zaqar.i18n import _LE
|
|
|
|
LOG = log.getLogger(__name__)
|
|
|
|
|
|
def dynamic_conf(uri, options, conf=None):
|
|
"""Given metadata, yields a dynamic configuration.
|
|
|
|
:param uri: pool location
|
|
:type uri: six.text_type
|
|
:param options: additional pool metadata
|
|
:type options: dict
|
|
:param conf: Optional conf object to copy
|
|
:type conf: `oslo_config.cfg.ConfigOpts`
|
|
:returns: Configuration object suitable for constructing storage
|
|
drivers
|
|
:rtype: oslo_config.cfg.ConfigOpts
|
|
"""
|
|
storage_type = six.moves.urllib_parse.urlparse(uri).scheme
|
|
|
|
# NOTE(cpp-cabrera): parse storage-specific opts:
|
|
# 'drivers:storage:{type}'
|
|
options['uri'] = uri
|
|
storage_opts = utils.dict_to_conf(options)
|
|
storage_group = u'drivers:message_store:%s' % storage_type
|
|
|
|
# NOTE(cpp-cabrera): register those options!
|
|
if conf is None:
|
|
conf = cfg.ConfigOpts()
|
|
else:
|
|
conf = copy.copy(conf)
|
|
|
|
if storage_group not in conf:
|
|
conf.register_opts(storage_opts,
|
|
group=storage_group)
|
|
|
|
if 'drivers' not in conf:
|
|
# NOTE(cpp-cabrera): parse general opts: 'drivers'
|
|
driver_opts = utils.dict_to_conf({'message_store': storage_type})
|
|
conf.register_opts(driver_opts, group=u'drivers')
|
|
|
|
conf.set_override('message_store', storage_type, 'drivers',
|
|
enforce_type=True)
|
|
|
|
for opt in options:
|
|
if opt in conf[storage_group]:
|
|
conf.set_override(opt, options[opt], group=storage_group,
|
|
enforce_type=True)
|
|
return conf
|
|
|
|
|
|
def load_storage_impl(uri, control_mode=False, default_store=None):
|
|
"""Loads a storage driver implementation and returns it.
|
|
|
|
:param uri: The connection uri to parse and load a driver for.
|
|
:param control_mode: (Default False). Determines which
|
|
driver type to load; if False, the data driver is
|
|
loaded. If True, the control driver is loaded.
|
|
:param default_store: The default store to load if no scheme
|
|
is parsed.
|
|
"""
|
|
|
|
mode = 'control' if control_mode else 'data'
|
|
driver_type = 'zaqar.{0}.storage'.format(mode)
|
|
storage_type = six.moves.urllib_parse.urlparse(uri).scheme or default_store
|
|
|
|
try:
|
|
mgr = driver.DriverManager(driver_type, storage_type,
|
|
invoke_on_load=False)
|
|
|
|
return mgr.driver
|
|
|
|
except Exception as exc:
|
|
LOG.exception(exc)
|
|
raise errors.InvalidDriver(exc)
|
|
|
|
|
|
def load_storage_driver(conf, cache, storage_type=None,
|
|
control_mode=False, control_driver=None):
|
|
"""Loads a storage driver and returns it.
|
|
|
|
The driver's initializer will be passed conf and cache as
|
|
its positional args.
|
|
|
|
:param conf: Configuration instance to use for loading the
|
|
driver. Must include a 'drivers' group.
|
|
:param cache: Cache instance that the driver can (optionally)
|
|
use to reduce latency for some operations.
|
|
:param storage_type: The storage_type to load. If None, then
|
|
the `drivers` option will be used.
|
|
:param control_mode: (Default False). Determines which
|
|
driver type to load; if False, the data driver is
|
|
loaded. If True, the control driver is loaded.
|
|
:param control_driver: (Default None). The control driver
|
|
instance to pass to the storage driver. Needed to access
|
|
the queue controller, mainly.
|
|
"""
|
|
|
|
if control_mode:
|
|
mode = 'control'
|
|
storage_type = storage_type or conf['drivers'].management_store
|
|
else:
|
|
mode = 'data'
|
|
storage_type = storage_type or conf['drivers'].message_store
|
|
|
|
driver_type = 'zaqar.{0}.storage'.format(mode)
|
|
|
|
_invoke_args = [conf, cache]
|
|
if control_driver is not None:
|
|
_invoke_args.append(control_driver)
|
|
|
|
try:
|
|
mgr = driver.DriverManager(driver_type,
|
|
storage_type,
|
|
invoke_on_load=True,
|
|
invoke_args=_invoke_args)
|
|
|
|
return mgr.driver
|
|
|
|
except Exception as exc:
|
|
LOG.error(_LE('Failed to load "{}" driver for "{}"').format(
|
|
driver_type, storage_type))
|
|
LOG.exception(exc)
|
|
raise errors.InvalidDriver(exc)
|
|
|
|
|
|
def keyify(key, iterable):
|
|
"""Make an iterator from an iterable of dicts compared with a key.
|
|
|
|
:param key: A key exists for all dict inside the iterable object
|
|
:param iterable: The input iterable object
|
|
"""
|
|
|
|
class Keyed(object):
|
|
def __init__(self, obj):
|
|
self.obj = obj
|
|
|
|
def __eq__(self, other):
|
|
return self.obj[key] == other.obj[key]
|
|
|
|
def __ne__(self, other):
|
|
return self.obj[key] != other.obj[key]
|
|
|
|
def __lt__(self, other):
|
|
return self.obj[key] < other.obj[key]
|
|
|
|
def __le__(self, other):
|
|
return self.obj[key] <= other.obj[key]
|
|
|
|
def __gt__(self, other):
|
|
return self.obj[key] > other.obj[key]
|
|
|
|
def __ge__(self, other):
|
|
return self.obj[key] >= other.obj[key]
|
|
|
|
for item in iterable:
|
|
yield Keyed(item)
|
|
|
|
|
|
def can_connect(uri, conf=None):
|
|
"""Given a URI, verifies whether it's possible to connect to it.
|
|
|
|
:param uri: connection string to a storage endpoint
|
|
:type uri: six.text_type
|
|
:returns: True if can connect else False
|
|
:rtype: bool
|
|
"""
|
|
conf = dynamic_conf(uri, {}, conf=conf)
|
|
storage_type = six.moves.urllib_parse.urlparse(uri).scheme
|
|
|
|
try:
|
|
# NOTE(cabrera): create a mock configuration containing only
|
|
# the URI field. This should be sufficient to initialize a
|
|
# storage driver.
|
|
driver = load_storage_driver(conf, None,
|
|
storage_type=storage_type,
|
|
control_driver=load_storage_driver
|
|
(conf, None,
|
|
storage_type=storage_type,
|
|
control_mode=True))
|
|
return driver.is_alive()
|
|
except Exception as exc:
|
|
LOG.debug('Can\'t connect to: %s \n%s' % (uri, exc))
|
|
return False
|