Add a TransportURL class to the public API
Nova's cells/rpc_driver.py has some code which allows user of the REST API to update elements of a cell's transport URL (say, the host name of the message broker) stored in the database. To achieve this, it has a parse_transport_url() method which breaks the URL into its constituent parts and an unparse_transport_url() which re-forms it again after updating some of its parts. This is all fine and, since it's fairly specialized, it wouldn't be a big deal to leave this code in Nova for now ... except the unparse method looks at CONF.rpc_backend to know what scheme to use in the returned URL if now backend was specified. oslo.messaging registers the rpc_backend option, but the ability to reference any option registered by the library should not be relied upon by users of the library. Imagine, for instance, if we renamed the option in future (with backwards compat for old configurations), then this would mean API breakage. So, long story short - an API along these lines makes some sense, but especially since not having it would mean we'd need to add some way to query the name of the transport driver. In this commit, we add a simple new TransportURL class: >>> url = messaging.TransportURL.parse(cfg.CONF, 'foo:///') >>> str(url), url ('foo:///', <TransportURL transport='foo'>) >>> url.hosts.append(messaging.TransportHost(hostname='localhost')) >>> str(url), url ('foo://localhost/', <TransportURL transport='foo', hosts=[<TransportHost hostname='localhost'>]>) >>> url.transport = None >>> str(url), url ('kombu://localhost/', <TransportURL transport='kombu', hosts=[<TransportHost hostname='localhost'>]>) >>> cfg.CONF.set_override('rpc_backend', 'bar') >>> str(url), url ('bar://localhost/', <TransportURL transport='bar', hosts=[<TransportHost hostname='localhost'>]>) The TransportURL.parse() method equates to parse_transport_url() and TransportURL.__str__() equates to unparse_transport(). The transport drivers are also updated to take a TransportURL as a required argument, which simplifies the handling of transport URLs in the drivers. Change-Id: Ic04173476329858e4a2c2d2707e9d4aeb212d127
This commit is contained in:
parent
5aa7c37144
commit
c846cf35b8
@ -8,4 +8,9 @@ Transport
|
||||
|
||||
.. autoclass:: Transport
|
||||
|
||||
.. autoclass:: TransportURL
|
||||
:members:
|
||||
|
||||
.. autoclass:: TransportHost
|
||||
|
||||
.. autofunction:: set_transport_defaults
|
||||
|
@ -24,7 +24,6 @@ from oslo import messaging
|
||||
from oslo.messaging._drivers import amqp as rpc_amqp
|
||||
from oslo.messaging._drivers import base
|
||||
from oslo.messaging._drivers import common as rpc_common
|
||||
from oslo.messaging import _urls as urls
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -241,12 +240,12 @@ class ReplyWaiter(object):
|
||||
|
||||
class AMQPDriverBase(base.BaseDriver):
|
||||
|
||||
def __init__(self, conf, connection_pool, url=None, default_exchange=None,
|
||||
allowed_remote_exmods=[]):
|
||||
def __init__(self, conf, url, connection_pool,
|
||||
default_exchange=None, allowed_remote_exmods=[]):
|
||||
super(AMQPDriverBase, self).__init__(conf, url, default_exchange,
|
||||
allowed_remote_exmods)
|
||||
|
||||
self._server_params = self._parse_url(self._url)
|
||||
self._server_params = self._server_params_from_url(self._url)
|
||||
|
||||
self._default_exchange = default_exchange
|
||||
|
||||
@ -261,38 +260,22 @@ class AMQPDriverBase(base.BaseDriver):
|
||||
self._reply_q_conn = None
|
||||
self._waiter = None
|
||||
|
||||
@staticmethod
|
||||
def _parse_url(url):
|
||||
if url is None:
|
||||
return None
|
||||
|
||||
parsed = urls.parse_url(url)
|
||||
|
||||
# Make sure there's not a query string; that could identify
|
||||
# requirements we can't comply with (e.g., ssl), so reject it if
|
||||
# it's present
|
||||
if parsed['parameters']:
|
||||
raise messaging.InvalidTransportURL(
|
||||
url, "Cannot comply with query string in transport URL")
|
||||
|
||||
if not parsed['hosts']:
|
||||
def _server_params_from_url(self, url):
|
||||
if not url.hosts:
|
||||
return None
|
||||
|
||||
sp = {
|
||||
'virtual_host': parsed['virtual_host'],
|
||||
'virtual_host': url.virtual_host,
|
||||
}
|
||||
|
||||
# FIXME(markmc): support multiple hosts
|
||||
host = parsed['hosts'][0]
|
||||
host = url.hosts[0]
|
||||
|
||||
if ':' in host['host']:
|
||||
(sp['hostname'], sp['port']) = host['host'].split(':', 1)
|
||||
sp['port'] = int(sp['port'])
|
||||
else:
|
||||
sp['hostname'] = host['host']
|
||||
|
||||
sp['username'] = host['username']
|
||||
sp['password'] = host['password']
|
||||
sp['hostname'] = host.hostname
|
||||
if host.port is not None:
|
||||
sp['port'] = host.port
|
||||
sp['username'] = host.username or ''
|
||||
sp['password'] = host.password or ''
|
||||
|
||||
return sp
|
||||
|
||||
|
@ -55,8 +55,8 @@ class BaseDriver(object):
|
||||
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
def __init__(self, conf, url=None, default_exchange=None,
|
||||
allowed_remote_exmods=[]):
|
||||
def __init__(self, conf, url,
|
||||
default_exchange=None, allowed_remote_exmods=[]):
|
||||
self.conf = conf
|
||||
self._url = url
|
||||
self._default_exchange = default_exchange
|
||||
|
@ -86,7 +86,7 @@ class FakeExchange(object):
|
||||
|
||||
class FakeDriver(base.BaseDriver):
|
||||
|
||||
def __init__(self, conf, url=None, default_exchange=None,
|
||||
def __init__(self, conf, url, default_exchange=None,
|
||||
allowed_remote_exmods=[]):
|
||||
super(FakeDriver, self).__init__(conf, url, default_exchange,
|
||||
allowed_remote_exmods=[])
|
||||
|
@ -742,13 +742,14 @@ def cleanup():
|
||||
|
||||
class QpidDriver(amqpdriver.AMQPDriverBase):
|
||||
|
||||
def __init__(self, conf, url=None, default_exchange=None,
|
||||
allowed_remote_exmods=[]):
|
||||
def __init__(self, conf, url,
|
||||
default_exchange=None, allowed_remote_exmods=[]):
|
||||
conf.register_opts(qpid_opts)
|
||||
conf.register_opts(rpc_amqp.amqp_opts)
|
||||
|
||||
connection_pool = rpc_amqp.get_connection_pool(conf, Connection)
|
||||
|
||||
super(QpidDriver, self).__init__(conf, connection_pool,
|
||||
url, default_exchange,
|
||||
super(QpidDriver, self).__init__(conf, url,
|
||||
connection_pool,
|
||||
default_exchange,
|
||||
allowed_remote_exmods)
|
||||
|
@ -873,13 +873,14 @@ def cleanup():
|
||||
|
||||
class RabbitDriver(amqpdriver.AMQPDriverBase):
|
||||
|
||||
def __init__(self, conf, url=None, default_exchange=None,
|
||||
def __init__(self, conf, url, default_exchange=None,
|
||||
allowed_remote_exmods=[]):
|
||||
conf.register_opts(rabbit_opts)
|
||||
conf.register_opts(rpc_amqp.amqp_opts)
|
||||
|
||||
connection_pool = rpc_amqp.get_connection_pool(conf, Connection)
|
||||
|
||||
super(RabbitDriver, self).__init__(conf, connection_pool,
|
||||
url, default_exchange,
|
||||
super(RabbitDriver, self).__init__(conf, url,
|
||||
connection_pool,
|
||||
default_exchange,
|
||||
allowed_remote_exmods)
|
||||
|
@ -1,111 +0,0 @@
|
||||
|
||||
# Copyright 2013 Red Hat, 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 urlparse
|
||||
|
||||
|
||||
def parse_url(url):
|
||||
"""Parse an url.
|
||||
|
||||
Assuming a URL takes the form of:
|
||||
|
||||
transport://user:pass@host1:port[,hostN:portN]/virtual_host[?opt=val]
|
||||
|
||||
then parse the URL and return a dictionary with the following structure:
|
||||
|
||||
{
|
||||
'virtual_host': 'virtual_host',
|
||||
'transport': 'transport',
|
||||
'hosts': [{'username': 'username',
|
||||
'password': 'password'
|
||||
'host': 'host1:port1'},
|
||||
...],
|
||||
'parameters': {'option': 'value'}
|
||||
}
|
||||
|
||||
Netloc is parsed following the sequence bellow:
|
||||
|
||||
* It is first splitted by ',' in order to support multiple hosts
|
||||
* The last parsed username and password will be propagated to the rest
|
||||
of hotsts specified:
|
||||
|
||||
user:passwd@host1:port1,host2:port2
|
||||
|
||||
[
|
||||
{"username": "user", "password": "passwd", "host": "host1:port1"},
|
||||
{"username": "user", "password": "passwd", "host": "host2:port2"}
|
||||
]
|
||||
|
||||
* In order to avoid the above propagation, it is possible to alter the
|
||||
order in which the hosts are specified or specify a set of fake credentials
|
||||
using ",:@host2:port2"
|
||||
|
||||
|
||||
user:passwd@host1:port1,:@host2:port2
|
||||
|
||||
[
|
||||
{"username": "user", "password": "passwd", "host": "host1:port1"},
|
||||
{"username": "", "password": "", "host": "host2:port2"}
|
||||
]
|
||||
|
||||
:param url: The URL to parse
|
||||
:type url: str
|
||||
:returns: A dictionary with the parsed data
|
||||
"""
|
||||
if not url:
|
||||
return None
|
||||
|
||||
# NOTE(flaper87): Not PY3K compliant
|
||||
if not isinstance(url, basestring):
|
||||
raise TypeError("Wrong URL type")
|
||||
|
||||
url = urlparse.urlparse(url)
|
||||
|
||||
parsed = dict(transport=url.scheme)
|
||||
|
||||
virtual_host = None
|
||||
if url.path.startswith('/'):
|
||||
virtual_host = url.path[1:]
|
||||
parsed["virtual_host"] = virtual_host
|
||||
|
||||
# NOTE(flaper87): Parse netloc.
|
||||
hosts = []
|
||||
username = password = ''
|
||||
for host in url.netloc.split(","):
|
||||
if not host:
|
||||
continue
|
||||
|
||||
if "@" in host:
|
||||
username, host = host.split("@", 1)
|
||||
if ":" in username:
|
||||
username, password = username.split(":", 1)
|
||||
|
||||
hosts.append({
|
||||
"host": host,
|
||||
"username": username,
|
||||
"password": password,
|
||||
})
|
||||
|
||||
parsed["hosts"] = hosts
|
||||
|
||||
parameters = {}
|
||||
if url.query:
|
||||
# NOTE(flaper87): This returns a dict with
|
||||
# key -> [value], those values need to be
|
||||
# normalized
|
||||
parameters = urlparse.parse_qs(url.query)
|
||||
parsed['parameters'] = parameters
|
||||
|
||||
return parsed
|
@ -3,6 +3,7 @@
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
# Copyright 2013 Red Hat, Inc.
|
||||
# Copyright (c) 2012 Rackspace Hosting
|
||||
#
|
||||
# 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
|
||||
@ -20,10 +21,13 @@ __all__ = [
|
||||
'DriverLoadFailure',
|
||||
'InvalidTransportURL',
|
||||
'Transport',
|
||||
'TransportHost',
|
||||
'TransportURL',
|
||||
'get_transport',
|
||||
'set_transport_defaults',
|
||||
]
|
||||
|
||||
import urllib
|
||||
import urlparse
|
||||
|
||||
from oslo.config import cfg
|
||||
@ -136,36 +140,258 @@ def get_transport(conf, url=None, allowed_remote_exmods=[]):
|
||||
|
||||
rabbit://me:passwd@host:5672/virtual_host
|
||||
|
||||
and can either be passed as a string or a TransportURL object.
|
||||
|
||||
:param conf: the user configuration
|
||||
:type conf: cfg.ConfigOpts
|
||||
:param url: a transport URL
|
||||
:type url: str
|
||||
:type url: str or TransportURL
|
||||
:param allowed_remote_exmods: a list of modules which a client using this
|
||||
transport will deserialize remote exceptions from
|
||||
transport will deserialize remote exceptions
|
||||
from
|
||||
:type allowed_remote_exmods: list
|
||||
"""
|
||||
conf.register_opts(_transport_opts)
|
||||
|
||||
if not isinstance(url, TransportURL):
|
||||
url = url or conf.transport_url
|
||||
if url is not None:
|
||||
rpc_backend = urlparse.urlparse(url).scheme
|
||||
if not rpc_backend:
|
||||
parsed = TransportURL.parse(conf, url)
|
||||
if not parsed.transport:
|
||||
raise InvalidTransportURL(url, 'No scheme specified in "%s"' % url)
|
||||
else:
|
||||
rpc_backend = conf.rpc_backend
|
||||
url = parsed
|
||||
|
||||
kwargs = dict(default_exchange=conf.control_exchange,
|
||||
allowed_remote_exmods=allowed_remote_exmods)
|
||||
if url is not None:
|
||||
kwargs['url'] = url
|
||||
|
||||
try:
|
||||
mgr = driver.DriverManager('oslo.messaging.drivers',
|
||||
rpc_backend,
|
||||
url.transport,
|
||||
invoke_on_load=True,
|
||||
invoke_args=[conf],
|
||||
invoke_args=[conf, url],
|
||||
invoke_kwds=kwargs)
|
||||
except RuntimeError as ex:
|
||||
raise DriverLoadFailure(rpc_backend, ex)
|
||||
raise DriverLoadFailure(url.transport, ex)
|
||||
|
||||
return Transport(mgr.driver)
|
||||
|
||||
|
||||
class TransportHost(object):
|
||||
|
||||
"""A host element of a parsed transport URL."""
|
||||
|
||||
def __init__(self, hostname=None, port=None, username=None, password=None):
|
||||
self.hostname = hostname
|
||||
self.port = port
|
||||
self.username = username
|
||||
self.password = password
|
||||
|
||||
def __eq__(self, other):
|
||||
return vars(self) == vars(other)
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __repr__(self):
|
||||
attrs = []
|
||||
for a in ['hostname', 'port', 'username', 'password']:
|
||||
v = getattr(self, a)
|
||||
if v:
|
||||
attrs.append((a, repr(v)))
|
||||
values = ', '.join(['%s=%s' % i for i in attrs])
|
||||
return '<TransportHost ' + values + '>'
|
||||
|
||||
|
||||
class TransportURL(object):
|
||||
|
||||
"""A parsed transport URL.
|
||||
|
||||
Transport URLs take the form::
|
||||
|
||||
transport://user:pass@host1:port[,hostN:portN]/virtual_host
|
||||
|
||||
i.e. the scheme selects the transport driver, you may include multiple
|
||||
hosts in netloc and the path part is a "virtual host" partition path.
|
||||
|
||||
:param conf: a ConfigOpts instance
|
||||
:type conf: oslo.config.cfg.ConfigOpts
|
||||
:param transport: a transport name e.g. 'rabbit' or 'qpid'
|
||||
:type transport: str
|
||||
:param virtual_host: a virtual host path e.g. '/'
|
||||
:type virtual_host: str
|
||||
:param hosts: a list of TransportHost objects
|
||||
:type hosts: list
|
||||
"""
|
||||
|
||||
def __init__(self, conf, transport=None, virtual_host=None, hosts=None):
|
||||
self.conf = conf
|
||||
self.conf.register_opts(_transport_opts)
|
||||
self._transport = transport
|
||||
self._virtual_host = virtual_host
|
||||
self._hosts = hosts
|
||||
if self._hosts is None:
|
||||
self._hosts = []
|
||||
|
||||
@property
|
||||
def transport(self):
|
||||
if self._transport is None:
|
||||
return self.conf.rpc_backend
|
||||
else:
|
||||
return self._transport
|
||||
|
||||
@transport.setter
|
||||
def transport(self, value):
|
||||
self._transport = value
|
||||
|
||||
@property
|
||||
def virtual_host(self):
|
||||
return self._virtual_host
|
||||
|
||||
@virtual_host.setter
|
||||
def virtual_host(self, value):
|
||||
self._virtual_host = value
|
||||
|
||||
@property
|
||||
def hosts(self):
|
||||
return self._hosts
|
||||
|
||||
def __eq__(self, other):
|
||||
return (self.transport == other.transport and
|
||||
self.virtual_host == other.virtual_host and
|
||||
self.hosts == other.hosts)
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __repr__(self):
|
||||
attrs = []
|
||||
for a in ['transport', 'virtual_host', 'hosts']:
|
||||
v = getattr(self, a)
|
||||
if v:
|
||||
attrs.append((a, repr(v)))
|
||||
values = ', '.join(['%s=%s' % i for i in attrs])
|
||||
return '<TransportURL ' + values + '>'
|
||||
|
||||
def __str__(self):
|
||||
netlocs = []
|
||||
|
||||
for host in self.hosts:
|
||||
username = host.username
|
||||
password = host.password
|
||||
hostname = host.hostname
|
||||
port = host.port
|
||||
|
||||
# Starting place for the network location
|
||||
netloc = ''
|
||||
|
||||
# Build the username and password portion of the transport URL
|
||||
if username is not None or password is not None:
|
||||
if username is not None:
|
||||
netloc += urllib.quote(username, '')
|
||||
if password is not None:
|
||||
netloc += ':%s' % urllib.quote(password, '')
|
||||
netloc += '@'
|
||||
|
||||
# Build the network location portion of the transport URL
|
||||
if hostname:
|
||||
if ':' in hostname:
|
||||
netloc += '[%s]' % hostname
|
||||
else:
|
||||
netloc += hostname
|
||||
if port is not None:
|
||||
netloc += ':%d' % port
|
||||
|
||||
netlocs.append(netloc)
|
||||
|
||||
# Assemble the transport URL
|
||||
url = '%s://%s/' % (self.transport, ','.join(netlocs))
|
||||
|
||||
if self.virtual_host:
|
||||
url += urllib.quote(self.virtual_host)
|
||||
|
||||
return url
|
||||
|
||||
@classmethod
|
||||
def parse(cls, conf, url):
|
||||
"""Parse an url.
|
||||
|
||||
Assuming a URL takes the form of:
|
||||
|
||||
transport://user:pass@host1:port[,hostN:portN]/virtual_host
|
||||
|
||||
then parse the URL and return a TransportURL object.
|
||||
|
||||
Netloc is parsed following the sequence bellow:
|
||||
|
||||
* It is first splitted by ',' in order to support multiple hosts
|
||||
* The last parsed username and password will be propagated to the rest
|
||||
of hotsts specified:
|
||||
|
||||
user:passwd@host1:port1,host2:port2
|
||||
|
||||
[
|
||||
{"username": "user", "password": "passwd", "host": "host1:port1"},
|
||||
{"username": "user", "password": "passwd", "host": "host2:port2"}
|
||||
]
|
||||
|
||||
* In order to avoid the above propagation, it is possible to alter the
|
||||
order in which the hosts are specified or specify a set of fake
|
||||
credentials using ",:@host2:port2"
|
||||
|
||||
user:passwd@host1:port1,:@host2:port2
|
||||
|
||||
[
|
||||
{"username": "user", "password": "passwd", "host": "host1:port1"},
|
||||
{"username": "", "password": "", "host": "host2:port2"}
|
||||
]
|
||||
|
||||
:param conf: a ConfigOpts instance
|
||||
:type conf: oslo.config.cfg.ConfigOpts
|
||||
:param url: The URL to parse
|
||||
:type url: str
|
||||
:returns: A TransportURL
|
||||
"""
|
||||
if not url:
|
||||
return cls(conf)
|
||||
|
||||
# FIXME(flaper87): Not PY3K compliant
|
||||
if not isinstance(url, basestring):
|
||||
raise InvalidTransportURL(url, 'Wrong URL type')
|
||||
|
||||
url = urlparse.urlparse(url)
|
||||
|
||||
# Make sure there's not a query string; that could identify
|
||||
# requirements we can't comply with (e.g., ssl), so reject it if
|
||||
# it's present
|
||||
if '?' in url.path or url.query:
|
||||
raise InvalidTransportURL(url.geturl(),
|
||||
"Cannot comply with query string in "
|
||||
"transport URL")
|
||||
|
||||
virtual_host = None
|
||||
if url.path.startswith('/'):
|
||||
virtual_host = url.path[1:]
|
||||
|
||||
hosts = []
|
||||
|
||||
username = password = ''
|
||||
for host in url.netloc.split(','):
|
||||
if not host:
|
||||
continue
|
||||
|
||||
hostname = host
|
||||
username = password = port = None
|
||||
|
||||
if '@' in host:
|
||||
username, hostname = host.split('@', 1)
|
||||
if ':' in username:
|
||||
username, password = username.split(':', 1)
|
||||
if ':' in hostname:
|
||||
hostname, port = hostname.split(':', 1)
|
||||
port = int(port)
|
||||
|
||||
hosts.append(TransportHost(hostname=hostname,
|
||||
port=port,
|
||||
username=username,
|
||||
password=password))
|
||||
|
||||
return cls(conf, url.scheme, virtual_host, hosts)
|
||||
|
@ -52,26 +52,12 @@ class _FakeManager(object):
|
||||
class GetTransportTestCase(test_utils.BaseTestCase):
|
||||
|
||||
scenarios = [
|
||||
('all_none',
|
||||
dict(url=None, transport_url=None, rpc_backend=None,
|
||||
control_exchange=None, allowed=None,
|
||||
expect=dict(backend=None,
|
||||
exchange=None,
|
||||
url=None,
|
||||
allowed=[]))),
|
||||
('rpc_backend',
|
||||
dict(url=None, transport_url=None, rpc_backend='testbackend',
|
||||
control_exchange=None, allowed=None,
|
||||
expect=dict(backend='testbackend',
|
||||
exchange=None,
|
||||
url=None,
|
||||
allowed=[]))),
|
||||
('control_exchange',
|
||||
dict(url=None, transport_url=None, rpc_backend=None,
|
||||
control_exchange='testexchange', allowed=None,
|
||||
expect=dict(backend=None,
|
||||
exchange='testexchange',
|
||||
url=None,
|
||||
url='testbackend:',
|
||||
allowed=[]))),
|
||||
('transport_url',
|
||||
dict(url=None, transport_url='testtransport:', rpc_backend=None,
|
||||
@ -87,12 +73,19 @@ class GetTransportTestCase(test_utils.BaseTestCase):
|
||||
exchange=None,
|
||||
url='testtransport:',
|
||||
allowed=[]))),
|
||||
('control_exchange',
|
||||
dict(url=None, transport_url=None, rpc_backend='testbackend',
|
||||
control_exchange='testexchange', allowed=None,
|
||||
expect=dict(backend='testbackend',
|
||||
exchange='testexchange',
|
||||
url='testbackend:',
|
||||
allowed=[]))),
|
||||
('allowed_remote_exmods',
|
||||
dict(url=None, transport_url=None, rpc_backend=None,
|
||||
dict(url=None, transport_url=None, rpc_backend='testbackend',
|
||||
control_exchange=None, allowed=['foo', 'bar'],
|
||||
expect=dict(backend=None,
|
||||
expect=dict(backend='testbackend',
|
||||
exchange=None,
|
||||
url=None,
|
||||
url='testbackend:',
|
||||
allowed=['foo', 'bar']))),
|
||||
]
|
||||
|
||||
@ -107,11 +100,11 @@ class GetTransportTestCase(test_utils.BaseTestCase):
|
||||
|
||||
self.mox.StubOutWithMock(driver, 'DriverManager')
|
||||
|
||||
invoke_args = [self.conf]
|
||||
invoke_args = [self.conf,
|
||||
messaging.TransportURL.parse(self.conf,
|
||||
self.expect['url'])]
|
||||
invoke_kwds = dict(default_exchange=self.expect['exchange'],
|
||||
allowed_remote_exmods=self.expect['allowed'])
|
||||
if self.expect['url']:
|
||||
invoke_kwds['url'] = self.expect['url']
|
||||
|
||||
drvr = _FakeDriver(self.conf)
|
||||
driver.DriverManager('oslo.messaging.drivers',
|
||||
@ -164,7 +157,9 @@ class GetTransportSadPathTestCase(test_utils.BaseTestCase):
|
||||
if self.rpc_backend:
|
||||
self.mox.StubOutWithMock(driver, 'DriverManager')
|
||||
|
||||
invoke_args = [self.conf]
|
||||
invoke_args = [self.conf,
|
||||
messaging.TransportURL.parse(self.conf,
|
||||
self.url)]
|
||||
invoke_kwds = dict(default_exchange='openstack',
|
||||
allowed_remote_exmods=[])
|
||||
|
||||
@ -191,7 +186,7 @@ class GetTransportSadPathTestCase(test_utils.BaseTestCase):
|
||||
|
||||
for k, v in self.ex.items():
|
||||
self.assertTrue(hasattr(ex, k))
|
||||
self.assertEqual(getattr(ex, k), v)
|
||||
self.assertEqual(str(getattr(ex, k)), v)
|
||||
|
||||
|
||||
# FIXME(markmc): this could be used elsewhere
|
||||
|
@ -13,12 +13,13 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo.config import cfg
|
||||
import testscenarios
|
||||
|
||||
from oslo.messaging import _urls as urls
|
||||
from oslo import messaging
|
||||
from oslo.messaging import transport
|
||||
from tests import utils as test_utils
|
||||
|
||||
|
||||
load_tests = testscenarios.load_tests_apply_scenarios
|
||||
|
||||
|
||||
@ -27,89 +28,163 @@ class TestParseURL(test_utils.BaseTestCase):
|
||||
scenarios = [
|
||||
('transport',
|
||||
dict(url='foo:',
|
||||
expect=dict(transport='foo',
|
||||
virtual_host=None,
|
||||
hosts=[],
|
||||
parameters={}))),
|
||||
expect=dict(transport='foo'))),
|
||||
('virtual_host_slash',
|
||||
dict(url='foo:////',
|
||||
expect=dict(transport='foo',
|
||||
virtual_host='/',
|
||||
hosts=[],
|
||||
parameters={}))),
|
||||
expect=dict(transport='foo', virtual_host='/'))),
|
||||
('virtual_host',
|
||||
dict(url='foo:///bar',
|
||||
expect=dict(transport='foo',
|
||||
virtual_host='bar',
|
||||
hosts=[],
|
||||
parameters={}))),
|
||||
expect=dict(transport='foo', virtual_host='bar'))),
|
||||
('host',
|
||||
dict(url='foo://host/bar',
|
||||
expect=dict(transport='foo',
|
||||
virtual_host='bar',
|
||||
hosts=[
|
||||
dict(host='host',
|
||||
username='',
|
||||
password=''),
|
||||
],
|
||||
parameters={}))),
|
||||
dict(host='host'),
|
||||
]))),
|
||||
('port',
|
||||
dict(url='foo://host:1234/bar',
|
||||
expect=dict(transport='foo',
|
||||
virtual_host='bar',
|
||||
hosts=[
|
||||
dict(host='host:1234',
|
||||
username='',
|
||||
password=''),
|
||||
],
|
||||
parameters={}))),
|
||||
dict(host='host', port=1234),
|
||||
]))),
|
||||
('username',
|
||||
dict(url='foo://u@host:1234/bar',
|
||||
expect=dict(transport='foo',
|
||||
virtual_host='bar',
|
||||
hosts=[
|
||||
dict(host='host:1234',
|
||||
username='u',
|
||||
password=''),
|
||||
],
|
||||
parameters={}))),
|
||||
dict(host='host', port=1234, username='u'),
|
||||
]))),
|
||||
('password',
|
||||
dict(url='foo://u:p@host:1234/bar',
|
||||
expect=dict(transport='foo',
|
||||
virtual_host='bar',
|
||||
hosts=[
|
||||
dict(host='host:1234',
|
||||
username='u',
|
||||
password='p'),
|
||||
],
|
||||
parameters={}))),
|
||||
dict(host='host', port=1234,
|
||||
username='u', password='p'),
|
||||
]))),
|
||||
('multi_host',
|
||||
dict(url='foo://u:p@host1:1234,host2:4321/bar',
|
||||
expect=dict(transport='foo',
|
||||
virtual_host='bar',
|
||||
hosts=[
|
||||
dict(host='host1:1234',
|
||||
username='u',
|
||||
password='p'),
|
||||
dict(host='host2:4321',
|
||||
username='u',
|
||||
password='p'),
|
||||
],
|
||||
parameters={}))),
|
||||
dict(host='host1', port=1234,
|
||||
username='u', password='p'),
|
||||
dict(host='host2', port=4321),
|
||||
]))),
|
||||
('multi_creds',
|
||||
dict(url='foo://u1:p1@host1:1234,u2:p2@host2:4321/bar',
|
||||
expect=dict(transport='foo',
|
||||
virtual_host='bar',
|
||||
hosts=[
|
||||
dict(host='host1:1234',
|
||||
username='u1',
|
||||
password='p1'),
|
||||
dict(host='host2:4321',
|
||||
username='u2',
|
||||
password='p2'),
|
||||
],
|
||||
parameters={}))),
|
||||
dict(host='host1', port=1234,
|
||||
username='u1', password='p1'),
|
||||
dict(host='host2', port=4321,
|
||||
username='u2', password='p2'),
|
||||
]))),
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
super(TestParseURL, self).setUp(conf=cfg.ConfigOpts())
|
||||
self.conf.register_opts(transport._transport_opts)
|
||||
|
||||
def test_parse_url(self):
|
||||
self.assertEqual(urls.parse_url(self.url), self.expect)
|
||||
self.config(rpc_backend=None)
|
||||
|
||||
url = messaging.TransportURL.parse(self.conf, self.url)
|
||||
|
||||
hosts = []
|
||||
for host in self.expect.get('hosts', []):
|
||||
hosts.append(messaging.TransportHost(host.get('host'),
|
||||
host.get('port'),
|
||||
host.get('username'),
|
||||
host.get('password')))
|
||||
expected = messaging.TransportURL(self.conf,
|
||||
self.expect.get('transport'),
|
||||
self.expect.get('virtual_host'),
|
||||
hosts)
|
||||
|
||||
self.assertEqual(url, expected)
|
||||
|
||||
|
||||
class TestFormatURL(test_utils.BaseTestCase):
|
||||
|
||||
scenarios = [
|
||||
('rpc_backend',
|
||||
dict(rpc_backend='testbackend',
|
||||
transport=None,
|
||||
virtual_host=None,
|
||||
hosts=[],
|
||||
expected='testbackend:///')),
|
||||
('transport',
|
||||
dict(rpc_backend=None,
|
||||
transport='testtransport',
|
||||
virtual_host=None,
|
||||
hosts=[],
|
||||
expected='testtransport:///')),
|
||||
('virtual_host',
|
||||
dict(rpc_backend=None,
|
||||
transport='testtransport',
|
||||
virtual_host='/vhost',
|
||||
hosts=[],
|
||||
expected='testtransport:////vhost')),
|
||||
('host',
|
||||
dict(rpc_backend=None,
|
||||
transport='testtransport',
|
||||
virtual_host='/',
|
||||
hosts=[
|
||||
dict(hostname='host',
|
||||
port=10,
|
||||
username='bob',
|
||||
password='secret'),
|
||||
],
|
||||
expected='testtransport://bob:secret@host:10//')),
|
||||
('multi_host',
|
||||
dict(rpc_backend=None,
|
||||
transport='testtransport',
|
||||
virtual_host='',
|
||||
hosts=[
|
||||
dict(hostname='h1',
|
||||
port=1000,
|
||||
username='b1',
|
||||
password='s1'),
|
||||
dict(hostname='h2',
|
||||
port=2000,
|
||||
username='b2',
|
||||
password='s2'),
|
||||
],
|
||||
expected='testtransport://b1:s1@h1:1000,b2:s2@h2:2000/')),
|
||||
('quoting',
|
||||
dict(rpc_backend=None,
|
||||
transport='testtransport',
|
||||
virtual_host='/$',
|
||||
hosts=[
|
||||
dict(hostname='host',
|
||||
port=10,
|
||||
username='b$',
|
||||
password='s&'),
|
||||
],
|
||||
expected='testtransport://b%24:s%26@host:10//%24')),
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
super(TestFormatURL, self).setUp(conf=cfg.ConfigOpts())
|
||||
self.conf.register_opts(transport._transport_opts)
|
||||
|
||||
def test_parse_url(self):
|
||||
self.config(rpc_backend=self.rpc_backend)
|
||||
|
||||
hosts = []
|
||||
for host in self.hosts:
|
||||
hosts.append(messaging.TransportHost(host.get('hostname'),
|
||||
host.get('port'),
|
||||
host.get('username'),
|
||||
host.get('password')))
|
||||
|
||||
url = messaging.TransportURL(self.conf,
|
||||
self.transport,
|
||||
self.virtual_host,
|
||||
hosts)
|
||||
|
||||
self.assertEqual(str(url), self.expected)
|
||||
|
Loading…
Reference in New Issue
Block a user