From 5b8fbdcad25875491890cfce135d31671c4cabe0 Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Fri, 29 Nov 2013 21:55:45 +0000 Subject: [PATCH] Add transport aliases We need to support deprecated transport driver configurations like: rpc_backend = nova.rpc.impl_kombu i.e. 'nova.rpc.impl_kombu' is a deprecated alias for 'rabbit'. Initially, we supported this by adding the aliases to each project's setup.cfg: oslo.messaging.drivers = nova.rpc.impl_kombu = oslo.messaging._drivers.impl_rabbit:RabbitDriver However, this means that code like this: url = str(TransportURL(conf)) generates a bogus URL string like: nova.rpc.impl_kombu://... We need to apply these transport aliases when we load drivers, but also when we create transport URLs from configuration containing potentially deprecated aliases. To enable that, add an aliases parameter to TransportURL(), TransportURL.parse() and get_transport(). blueprint: transport-aliases Change-Id: Ifce68ff62746c2a363c719417a2bd0a78ee025dd --- oslo/messaging/transport.py | 28 ++++++++++++++------ tests/test_transport.py | 36 +++++++++++++++++++++---- tests/test_urls.py | 53 +++++++++++++++++++++++++++---------- 3 files changed, 90 insertions(+), 27 deletions(-) diff --git a/oslo/messaging/transport.py b/oslo/messaging/transport.py index a5e496672..e61c9c260 100644 --- a/oslo/messaging/transport.py +++ b/oslo/messaging/transport.py @@ -123,7 +123,7 @@ class DriverLoadFailure(exceptions.MessagingException): self.ex = ex -def get_transport(conf, url=None, allowed_remote_exmods=[]): +def get_transport(conf, url=None, allowed_remote_exmods=[], aliases=None): """A factory method for Transport objects. This method will construct a Transport object from transport configuration @@ -150,12 +150,14 @@ def get_transport(conf, url=None, allowed_remote_exmods=[]): transport will deserialize remote exceptions from :type allowed_remote_exmods: list + :param aliases: A map of transport alias to transport name + :type aliases: dict """ conf.register_opts(_transport_opts) if not isinstance(url, TransportURL): url = url or conf.transport_url - parsed = TransportURL.parse(conf, url) + parsed = TransportURL.parse(conf, url, aliases) if not parsed.transport: raise InvalidTransportURL(url, 'No scheme specified in "%s"' % url) url = parsed @@ -220,9 +222,12 @@ class TransportURL(object): :type virtual_host: str :param hosts: a list of TransportHost objects :type hosts: list + :param aliases: A map of transport alias to transport name + :type aliases: dict """ - def __init__(self, conf, transport=None, virtual_host=None, hosts=None): + def __init__(self, conf, transport=None, virtual_host=None, hosts=None, + aliases=None): self.conf = conf self.conf.register_opts(_transport_opts) self._transport = transport @@ -231,13 +236,18 @@ class TransportURL(object): self.hosts = [] else: self.hosts = hosts + if aliases is None: + self.aliases = {} + else: + self.aliases = aliases @property def transport(self): if self._transport is None: - return self.conf.rpc_backend + transport = self.conf.rpc_backend else: - return self._transport + transport = self._transport + return self.aliases.get(transport, transport) @transport.setter def transport(self, value): @@ -300,7 +310,7 @@ class TransportURL(object): return url @classmethod - def parse(cls, conf, url): + def parse(cls, conf, url, aliases=None): """Parse an url. Assuming a URL takes the form of: @@ -337,10 +347,12 @@ class TransportURL(object): :type conf: oslo.config.cfg.ConfigOpts :param url: The URL to parse :type url: str + :param aliases: A map of transport alias to transport name + :type aliases: dict :returns: A TransportURL """ if not url: - return cls(conf) + return cls(conf, aliases=aliases) # FIXME(flaper87): Not PY3K compliant if not isinstance(url, basestring): @@ -402,4 +414,4 @@ class TransportURL(object): username=username, password=password)) - return cls(conf, url.scheme, virtual_host, hosts) + return cls(conf, url.scheme, virtual_host, hosts, aliases) diff --git a/tests/test_transport.py b/tests/test_transport.py index dcf59555c..4fb24d22f 100644 --- a/tests/test_transport.py +++ b/tests/test_transport.py @@ -54,39 +54,63 @@ class GetTransportTestCase(test_utils.BaseTestCase): scenarios = [ ('rpc_backend', dict(url=None, transport_url=None, rpc_backend='testbackend', - control_exchange=None, allowed=None, + control_exchange=None, allowed=None, aliases=None, expect=dict(backend='testbackend', exchange=None, url='testbackend:', allowed=[]))), ('transport_url', dict(url=None, transport_url='testtransport:', rpc_backend=None, - control_exchange=None, allowed=None, + control_exchange=None, allowed=None, aliases=None, expect=dict(backend='testtransport', exchange=None, url='testtransport:', allowed=[]))), ('url_param', dict(url='testtransport:', transport_url=None, rpc_backend=None, - control_exchange=None, allowed=None, + control_exchange=None, allowed=None, aliases=None, expect=dict(backend='testtransport', exchange=None, url='testtransport:', allowed=[]))), ('control_exchange', dict(url=None, transport_url=None, rpc_backend='testbackend', - control_exchange='testexchange', allowed=None, + control_exchange='testexchange', allowed=None, aliases=None, expect=dict(backend='testbackend', exchange='testexchange', url='testbackend:', allowed=[]))), ('allowed_remote_exmods', dict(url=None, transport_url=None, rpc_backend='testbackend', - control_exchange=None, allowed=['foo', 'bar'], + control_exchange=None, allowed=['foo', 'bar'], aliases=None, expect=dict(backend='testbackend', exchange=None, url='testbackend:', allowed=['foo', 'bar']))), + ('rpc_backend_aliased', + dict(url=None, transport_url=None, rpc_backend='testfoo', + control_exchange=None, allowed=None, + aliases=dict(testfoo='testbackend'), + expect=dict(backend='testbackend', + exchange=None, + url='testbackend:', + allowed=[]))), + ('transport_url_aliased', + dict(url=None, transport_url='testfoo:', rpc_backend=None, + control_exchange=None, allowed=None, + aliases=dict(testfoo='testtransport'), + expect=dict(backend='testtransport', + exchange=None, + url='testtransport:', + allowed=[]))), + ('url_param_aliased', + dict(url='testfoo:', transport_url=None, rpc_backend=None, + control_exchange=None, allowed=None, + aliases=dict(testfoo='testtransport'), + expect=dict(backend='testtransport', + exchange=None, + url='testtransport:', + allowed=[]))), ] def setUp(self): @@ -119,6 +143,8 @@ class GetTransportTestCase(test_utils.BaseTestCase): kwargs = dict(url=self.url) if self.allowed is not None: kwargs['allowed_remote_exmods'] = self.allowed + if self.aliases is not None: + kwargs['aliases'] = self.aliases transport = messaging.get_transport(self.conf, **kwargs) self.assertIsNotNone(transport) diff --git a/tests/test_urls.py b/tests/test_urls.py index 84f5a177a..1f190adf9 100644 --- a/tests/test_urls.py +++ b/tests/test_urls.py @@ -27,51 +27,54 @@ class TestParseURL(test_utils.BaseTestCase): scenarios = [ ('transport', - dict(url='foo:', + dict(url='foo:', aliases=None, + expect=dict(transport='foo'))), + ('transport_aliased', + dict(url='bar:', aliases=dict(bar='foo'), expect=dict(transport='foo'))), ('virtual_host_slash', - dict(url='foo:////', + dict(url='foo:////', aliases=None, expect=dict(transport='foo', virtual_host='/'))), ('virtual_host', - dict(url='foo:///bar', + dict(url='foo:///bar', aliases=None, expect=dict(transport='foo', virtual_host='bar'))), ('host', - dict(url='foo://host/bar', + dict(url='foo://host/bar', aliases=None, expect=dict(transport='foo', virtual_host='bar', hosts=[ dict(host='host'), ]))), ('ipv6_host', - dict(url='foo://[ffff::1]/bar', + dict(url='foo://[ffff::1]/bar', aliases=None, expect=dict(transport='foo', virtual_host='bar', hosts=[ dict(host='ffff::1'), ]))), ('port', - dict(url='foo://host:1234/bar', + dict(url='foo://host:1234/bar', aliases=None, expect=dict(transport='foo', virtual_host='bar', hosts=[ dict(host='host', port=1234), ]))), ('ipv6_port', - dict(url='foo://[ffff::1]:1234/bar', + dict(url='foo://[ffff::1]:1234/bar', aliases=None, expect=dict(transport='foo', virtual_host='bar', hosts=[ dict(host='ffff::1', port=1234), ]))), ('username', - dict(url='foo://u@host:1234/bar', + dict(url='foo://u@host:1234/bar', aliases=None, expect=dict(transport='foo', virtual_host='bar', hosts=[ dict(host='host', port=1234, username='u'), ]))), ('password', - dict(url='foo://u:p@host:1234/bar', + dict(url='foo://u:p@host:1234/bar', aliases=None, expect=dict(transport='foo', virtual_host='bar', hosts=[ @@ -79,14 +82,14 @@ class TestParseURL(test_utils.BaseTestCase): username='u', password='p'), ]))), ('creds_no_host', - dict(url='foo://u:p@/bar', + dict(url='foo://u:p@/bar', aliases=None, expect=dict(transport='foo', virtual_host='bar', hosts=[ dict(username='u', password='p'), ]))), ('multi_host', - dict(url='foo://u:p@host1:1234,host2:4321/bar', + dict(url='foo://u:p@host1:1234,host2:4321/bar', aliases=None, expect=dict(transport='foo', virtual_host='bar', hosts=[ @@ -95,7 +98,7 @@ class TestParseURL(test_utils.BaseTestCase): dict(host='host2', port=4321), ]))), ('multi_creds', - dict(url='foo://u1:p1@host1:1234,u2:p2@host2:4321/bar', + dict(url='foo://u1:p1@host1:1234,u2:p2@host2:4321/bar', aliases=None, expect=dict(transport='foo', virtual_host='bar', hosts=[ @@ -106,6 +109,7 @@ class TestParseURL(test_utils.BaseTestCase): ]))), ('multi_creds_ipv6', dict(url='foo://u1:p1@[ffff::1]:1234,u2:p2@[ffff::2]:4321/bar', + aliases=None, expect=dict(transport='foo', virtual_host='bar', hosts=[ @@ -123,7 +127,7 @@ class TestParseURL(test_utils.BaseTestCase): def test_parse_url(self): self.config(rpc_backend=None) - url = messaging.TransportURL.parse(self.conf, self.url) + url = messaging.TransportURL.parse(self.conf, self.url, self.aliases) hosts = [] for host in self.expect.get('hosts', []): @@ -147,18 +151,35 @@ class TestFormatURL(test_utils.BaseTestCase): transport=None, virtual_host=None, hosts=[], + aliases=None, + expected='testbackend:///')), + ('rpc_backend_aliased', + dict(rpc_backend='testfoo', + transport=None, + virtual_host=None, + hosts=[], + aliases=dict(testfoo='testbackend'), expected='testbackend:///')), ('transport', dict(rpc_backend=None, transport='testtransport', virtual_host=None, hosts=[], + aliases=None, + expected='testtransport:///')), + ('transport_aliased', + dict(rpc_backend=None, + transport='testfoo', + virtual_host=None, + hosts=[], + aliases=dict(testfoo='testtransport'), expected='testtransport:///')), ('virtual_host', dict(rpc_backend=None, transport='testtransport', virtual_host='/vhost', hosts=[], + aliases=None, expected='testtransport:////vhost')), ('host', dict(rpc_backend=None, @@ -170,6 +191,7 @@ class TestFormatURL(test_utils.BaseTestCase): username='bob', password='secret'), ], + aliases=None, expected='testtransport://bob:secret@host:10//')), ('multi_host', dict(rpc_backend=None, @@ -185,6 +207,7 @@ class TestFormatURL(test_utils.BaseTestCase): username='b2', password='s2'), ], + aliases=None, expected='testtransport://b1:s1@h1:1000,b2:s2@h2:2000/')), ('quoting', dict(rpc_backend=None, @@ -196,6 +219,7 @@ class TestFormatURL(test_utils.BaseTestCase): username='b$', password='s&'), ], + aliases=None, expected='testtransport://b%24:s%26@host:10//%24')), ] @@ -216,6 +240,7 @@ class TestFormatURL(test_utils.BaseTestCase): url = messaging.TransportURL(self.conf, self.transport, self.virtual_host, - hosts) + hosts, + self.aliases) self.assertEqual(str(url), self.expected)