# 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. from oslo.config import cfg import six from stevedore import driver from marconi.common import errors from marconi.common import utils from marconi.openstack.common import log LOG = log.getLogger(__name__) def dynamic_conf(uri, options): """Given metadata, yields a dynamic configuration. :param uri: shard location :type uri: six.text_type :param options: additional shard metadata :type options: dict :returns: Configuration object suitable for constructing storage drivers :rtype: oslo.config.cfg.ConfigOpts """ # NOTE(cpp-cabrera): make it *very* clear to data storage # drivers that we are operating in a dynamic mode. general_opts = utils.dict_to_conf({'dynamic': True}) # NOTE(cpp-cabrera): parse general opts: 'drivers' storage_type = six.moves.urllib_parse.urlparse(uri).scheme driver_opts = utils.dict_to_conf({'storage': storage_type}) # NOTE(cpp-cabrera): parse storage-specific opts: # 'drivers:storage:{type}' storage_opts = utils.dict_to_conf({'uri': uri, 'options': options}) storage_group = u'drivers:storage:%s' % storage_type # NOTE(cpp-cabrera): register those options! conf = cfg.ConfigOpts() conf.register_opts(general_opts) conf.register_opts(driver_opts, group=u'drivers') conf.register_opts(storage_opts, group=storage_group) return conf def load_storage_driver(conf, cache, control_mode=False): """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 control_mode: (Default False). Determines which driver type to load; if False, the data driver is loaded. If True, the control driver is loaded. """ mode = 'control' if control_mode else 'data' driver_type = 'marconi.queues.{0}.storage'.format(mode) try: mgr = driver.DriverManager(driver_type, conf['drivers'].storage, invoke_on_load=True, invoke_args=[conf, cache]) return mgr.driver except RuntimeError as exc: 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 __cmp__(self, other): return cmp(self.obj[key], other.obj[key]) # TODO(zyuan): define magic operators to make py3 work # http://code.activestate.com/recipes/576653/ for item in iterable: yield Keyed(item) def can_connect(uri): """Given a URI, verifies whether its 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 """ driver_type = 'marconi.queues.data.storage' 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. conf = dynamic_conf(uri, {}) mgr = driver.DriverManager(driver_type, storage_type, invoke_on_load=True, invoke_args=[conf, None]) return mgr.driver.is_alive() except RuntimeError: return False