3b6ca5b6de
The Oslo libraries have moved all of their code out of the 'oslo' namespace package into per-library packages. The namespace package was retained during kilo for backwards compatibility, but will be removed by the liberty-2 milestone. This change removes the use of the namespace package, replacing it with the new package names. The patches in the libraries will be put on hold until application patches have landed, or L2, whichever comes first. At that point, new versions of the libraries without namespace packages will be released as a major version update. Please merge this patch, or an equivalent, before L2 to avoid problems with those library releases. Blueprint: remove-namespace-packages https://blueprints.launchpad.net/oslo-incubator/+spec/remove-namespace-packages Change-Id: I05a408b76d4f31b803769a27759e91df770511bb
441 lines
17 KiB
Python
441 lines
17 KiB
Python
# Copyright 2014 Canonical, Ltd.
|
|
# All Rights Reserved.
|
|
#
|
|
# 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 logging
|
|
import socket
|
|
|
|
import fixtures
|
|
import testtools
|
|
|
|
from six.moves import mock
|
|
|
|
try:
|
|
import zmq
|
|
except ImportError:
|
|
zmq = None
|
|
|
|
from oslo import messaging
|
|
from oslo_messaging.tests import utils as test_utils
|
|
from oslo_utils import importutils
|
|
|
|
# eventlet is not yet py3 compatible, so skip if not installed
|
|
eventlet = importutils.try_import('eventlet')
|
|
|
|
impl_zmq = importutils.try_import('oslo_messaging._drivers.impl_zmq')
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
def get_unused_port():
|
|
"""Returns an unused port on localhost."""
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
s.bind(('localhost', 0))
|
|
port = s.getsockname()[1]
|
|
s.close()
|
|
return port
|
|
|
|
|
|
class ZmqBaseTestCase(test_utils.BaseTestCase):
|
|
"""Base test case for all ZMQ tests that make use of the ZMQ Proxy"""
|
|
|
|
@testtools.skipIf(zmq is None, "zmq not available")
|
|
def setUp(self):
|
|
super(ZmqBaseTestCase, self).setUp()
|
|
self.messaging_conf.transport_driver = 'zmq'
|
|
# Get driver
|
|
transport = messaging.get_transport(self.conf)
|
|
self.driver = transport._driver
|
|
|
|
# Set config values
|
|
self.internal_ipc_dir = self.useFixture(fixtures.TempDir()).path
|
|
kwargs = {'rpc_zmq_bind_address': '127.0.0.1',
|
|
'rpc_zmq_host': '127.0.0.1',
|
|
'rpc_response_timeout': 5,
|
|
'rpc_zmq_port': get_unused_port(),
|
|
'rpc_zmq_ipc_dir': self.internal_ipc_dir}
|
|
self.config(**kwargs)
|
|
|
|
# Start RPC
|
|
LOG.info("Running internal zmq receiver.")
|
|
self.reactor = impl_zmq.ZmqProxy(self.conf)
|
|
self.reactor.consume_in_thread()
|
|
|
|
self.matchmaker = impl_zmq._get_matchmaker(host='127.0.0.1')
|
|
self.addCleanup(stopRpc(self.__dict__))
|
|
|
|
|
|
class TestConfZmqDriverLoad(test_utils.BaseTestCase):
|
|
|
|
@testtools.skipIf(zmq is None, "zmq not available")
|
|
def setUp(self):
|
|
super(TestConfZmqDriverLoad, self).setUp()
|
|
self.messaging_conf.transport_driver = 'zmq'
|
|
|
|
def test_driver_load(self):
|
|
transport = messaging.get_transport(self.conf)
|
|
self.assertIsInstance(transport._driver, impl_zmq.ZmqDriver)
|
|
|
|
|
|
class stopRpc(object):
|
|
def __init__(self, attrs):
|
|
self.attrs = attrs
|
|
|
|
def __call__(self):
|
|
if self.attrs['reactor']:
|
|
self.attrs['reactor'].close()
|
|
if self.attrs['driver']:
|
|
self.attrs['driver'].cleanup()
|
|
|
|
|
|
class TestZmqBasics(ZmqBaseTestCase):
|
|
|
|
def test_start_stop_listener(self):
|
|
target = messaging.Target(topic='testtopic')
|
|
listener = self.driver.listen(target)
|
|
result = listener.poll(0.01)
|
|
self.assertEqual(result, None)
|
|
|
|
def test_send_receive_raises(self):
|
|
"""Call() without method."""
|
|
target = messaging.Target(topic='testtopic')
|
|
self.driver.listen(target)
|
|
self.assertRaises(
|
|
KeyError,
|
|
self.driver.send,
|
|
target, {}, {'tx_id': 1}, wait_for_reply=True)
|
|
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq.ZmqIncomingMessage')
|
|
def test_send_receive_topic(self, mock_msg):
|
|
"""Call() with method."""
|
|
mock_msg.return_value = msg = mock.MagicMock()
|
|
msg.received = received = mock.MagicMock()
|
|
received.failure = False
|
|
received.reply = True
|
|
msg.condition = condition = mock.MagicMock()
|
|
condition.wait.return_value = True
|
|
|
|
target = messaging.Target(topic='testtopic')
|
|
self.driver.listen(target)
|
|
result = self.driver.send(
|
|
target, {},
|
|
{'method': 'hello-world', 'tx_id': 1},
|
|
wait_for_reply=True)
|
|
self.assertEqual(result, True)
|
|
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq._call', autospec=True)
|
|
def test_send_receive_fanout(self, mock_call):
|
|
target = messaging.Target(topic='testtopic', fanout=True)
|
|
self.driver.listen(target)
|
|
|
|
mock_call.__name__ = '_call'
|
|
mock_call.return_value = [True]
|
|
|
|
result = self.driver.send(
|
|
target, {},
|
|
{'method': 'hello-world', 'tx_id': 1},
|
|
wait_for_reply=True)
|
|
|
|
self.assertEqual(result, True)
|
|
mock_call.assert_called_once_with(
|
|
self.driver,
|
|
'tcp://127.0.0.1:%s' % self.conf['rpc_zmq_port'],
|
|
{}, 'fanout~testtopic.127.0.0.1',
|
|
{'tx_id': 1, 'method': 'hello-world'},
|
|
None, False, [], True)
|
|
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq._call', autospec=True)
|
|
def test_send_receive_direct(self, mock_call):
|
|
# Also verifies fix for bug http://pad.lv/1301723
|
|
target = messaging.Target(topic='testtopic', server='localhost')
|
|
self.driver.listen(target)
|
|
|
|
mock_call.__name__ = '_call'
|
|
mock_call.return_value = [True]
|
|
|
|
result = self.driver.send(
|
|
target, {},
|
|
{'method': 'hello-world', 'tx_id': 1},
|
|
wait_for_reply=True)
|
|
|
|
self.assertEqual(result, True)
|
|
mock_call.assert_called_once_with(
|
|
self.driver,
|
|
'tcp://localhost:%s' % self.conf['rpc_zmq_port'],
|
|
{}, 'testtopic.localhost',
|
|
{'tx_id': 1, 'method': 'hello-world'},
|
|
None, False, [], True)
|
|
|
|
|
|
class TestZmqSocket(test_utils.BaseTestCase):
|
|
|
|
@testtools.skipIf(zmq is None, "zmq not available")
|
|
def setUp(self):
|
|
super(TestZmqSocket, self).setUp()
|
|
self.messaging_conf.transport_driver = 'zmq'
|
|
# Get driver
|
|
transport = messaging.get_transport(self.conf)
|
|
self.driver = transport._driver
|
|
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq.ZmqSocket.subscribe')
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq.zmq.Context')
|
|
def test_zmqsocket_init_type_pull(self, mock_context, mock_subscribe):
|
|
mock_ctxt = mock.Mock()
|
|
mock_context.return_value = mock_ctxt
|
|
mock_sock = mock.Mock()
|
|
mock_ctxt.socket = mock.Mock(return_value=mock_sock)
|
|
mock_sock.connect = mock.Mock()
|
|
mock_sock.bind = mock.Mock()
|
|
addr = '127.0.0.1'
|
|
|
|
sock = impl_zmq.ZmqSocket(addr, impl_zmq.zmq.PULL, bind=False,
|
|
subscribe=None)
|
|
self.assertTrue(sock.can_recv)
|
|
self.assertFalse(sock.can_send)
|
|
self.assertFalse(sock.can_sub)
|
|
self.assertTrue(mock_sock.connect.called)
|
|
self.assertFalse(mock_sock.bind.called)
|
|
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq.ZmqSocket.subscribe')
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq.zmq.Context')
|
|
def test_zmqsocket_init_type_sub(self, mock_context, mock_subscribe):
|
|
mock_ctxt = mock.Mock()
|
|
mock_context.return_value = mock_ctxt
|
|
mock_sock = mock.Mock()
|
|
mock_ctxt.socket = mock.Mock(return_value=mock_sock)
|
|
mock_sock.connect = mock.Mock()
|
|
mock_sock.bind = mock.Mock()
|
|
addr = '127.0.0.1'
|
|
|
|
sock = impl_zmq.ZmqSocket(addr, impl_zmq.zmq.SUB, bind=False,
|
|
subscribe=None)
|
|
self.assertTrue(sock.can_recv)
|
|
self.assertFalse(sock.can_send)
|
|
self.assertTrue(sock.can_sub)
|
|
self.assertTrue(mock_sock.connect.called)
|
|
self.assertFalse(mock_sock.bind.called)
|
|
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq.ZmqSocket.subscribe')
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq.zmq.Context')
|
|
def test_zmqsocket_init_type_push(self, mock_context, mock_subscribe):
|
|
mock_ctxt = mock.Mock()
|
|
mock_context.return_value = mock_ctxt
|
|
mock_sock = mock.Mock()
|
|
mock_ctxt.socket = mock.Mock(return_value=mock_sock)
|
|
mock_sock.connect = mock.Mock()
|
|
mock_sock.bind = mock.Mock()
|
|
addr = '127.0.0.1'
|
|
|
|
sock = impl_zmq.ZmqSocket(addr, impl_zmq.zmq.PUSH, bind=False,
|
|
subscribe=None)
|
|
self.assertFalse(sock.can_recv)
|
|
self.assertTrue(sock.can_send)
|
|
self.assertFalse(sock.can_sub)
|
|
self.assertTrue(mock_sock.connect.called)
|
|
self.assertFalse(mock_sock.bind.called)
|
|
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq.ZmqSocket.subscribe')
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq.zmq.Context')
|
|
def test_zmqsocket_init_type_pub(self, mock_context, mock_subscribe):
|
|
mock_ctxt = mock.Mock()
|
|
mock_context.return_value = mock_ctxt
|
|
mock_sock = mock.Mock()
|
|
mock_ctxt.socket = mock.Mock(return_value=mock_sock)
|
|
mock_sock.connect = mock.Mock()
|
|
mock_sock.bind = mock.Mock()
|
|
addr = '127.0.0.1'
|
|
|
|
sock = impl_zmq.ZmqSocket(addr, impl_zmq.zmq.PUB, bind=False,
|
|
subscribe=None)
|
|
self.assertFalse(sock.can_recv)
|
|
self.assertTrue(sock.can_send)
|
|
self.assertFalse(sock.can_sub)
|
|
self.assertTrue(mock_sock.connect.called)
|
|
self.assertFalse(mock_sock.bind.called)
|
|
|
|
|
|
class TestZmqIncomingMessage(test_utils.BaseTestCase):
|
|
|
|
@testtools.skipIf(zmq is None, "zmq not available")
|
|
def setUp(self):
|
|
super(TestZmqIncomingMessage, self).setUp()
|
|
self.messaging_conf.transport_driver = 'zmq'
|
|
# Get driver
|
|
transport = messaging.get_transport(self.conf)
|
|
self.driver = transport._driver
|
|
|
|
def test_zmqincomingmessage(self):
|
|
msg = impl_zmq.ZmqIncomingMessage(mock.Mock(), None, 'msg.foo')
|
|
msg.reply("abc")
|
|
self.assertIsInstance(
|
|
msg.received, impl_zmq.ZmqIncomingMessage.ReceivedReply)
|
|
self.assertIsInstance(
|
|
msg.received, impl_zmq.ZmqIncomingMessage.ReceivedReply)
|
|
self.assertEqual(msg.received.reply, "abc")
|
|
msg.requeue()
|
|
|
|
|
|
class TestZmqConnection(ZmqBaseTestCase):
|
|
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq.ZmqReactor', autospec=True)
|
|
def test_zmqconnection_create_consumer(self, mock_reactor):
|
|
|
|
mock_reactor.register = mock.Mock()
|
|
conn = impl_zmq.Connection(self.driver.conf, self.driver)
|
|
topic = 'topic.foo'
|
|
context = mock.Mock()
|
|
inaddr = ('ipc://%s/zmq_topic_topic.127.0.0.1' %
|
|
(self.internal_ipc_dir))
|
|
# No Fanout
|
|
conn.create_consumer(topic, context)
|
|
conn.reactor.register.assert_called_with(context, inaddr,
|
|
impl_zmq.zmq.PULL,
|
|
subscribe=None, in_bind=False)
|
|
|
|
# Reset for next bunch of checks
|
|
conn.reactor.register.reset_mock()
|
|
|
|
# Fanout
|
|
inaddr = ('ipc://%s/zmq_topic_fanout~topic' %
|
|
(self.internal_ipc_dir))
|
|
conn.create_consumer(topic, context, fanout='subscriber.foo')
|
|
conn.reactor.register.assert_called_with(context, inaddr,
|
|
impl_zmq.zmq.SUB,
|
|
subscribe='subscriber.foo',
|
|
in_bind=False)
|
|
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq.ZmqReactor', autospec=True)
|
|
def test_zmqconnection_create_consumer_topic_exists(self, mock_reactor):
|
|
mock_reactor.register = mock.Mock()
|
|
conn = impl_zmq.Connection(self.driver.conf, self.driver)
|
|
topic = 'topic.foo'
|
|
context = mock.Mock()
|
|
inaddr = ('ipc://%s/zmq_topic_topic.127.0.0.1' %
|
|
(self.internal_ipc_dir))
|
|
|
|
conn.create_consumer(topic, context)
|
|
conn.reactor.register.assert_called_with(
|
|
context, inaddr, impl_zmq.zmq.PULL, subscribe=None, in_bind=False)
|
|
conn.reactor.register.reset_mock()
|
|
# Call again with same topic
|
|
conn.create_consumer(topic, context)
|
|
self.assertFalse(conn.reactor.register.called)
|
|
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq._get_matchmaker',
|
|
autospec=True)
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq.ZmqReactor', autospec=True)
|
|
def test_zmqconnection_close(self, mock_reactor, mock_getmatchmaker):
|
|
conn = impl_zmq.Connection(self.driver.conf, self.driver)
|
|
conn.reactor.close = mock.Mock()
|
|
mock_getmatchmaker.return_value.stop_heartbeat = mock.Mock()
|
|
conn.close()
|
|
self.assertTrue(mock_getmatchmaker.return_value.stop_heartbeat.called)
|
|
self.assertTrue(conn.reactor.close.called)
|
|
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq.ZmqReactor', autospec=True)
|
|
def test_zmqconnection_wait(self, mock_reactor):
|
|
conn = impl_zmq.Connection(self.driver.conf, self.driver)
|
|
conn.reactor.wait = mock.Mock()
|
|
conn.wait()
|
|
self.assertTrue(conn.reactor.wait.called)
|
|
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq._get_matchmaker',
|
|
autospec=True)
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq.ZmqReactor', autospec=True)
|
|
def test_zmqconnection_consume_in_thread(self, mock_reactor,
|
|
mock_getmatchmaker):
|
|
mock_getmatchmaker.return_value.start_heartbeat = mock.Mock()
|
|
conn = impl_zmq.Connection(self.driver.conf, self.driver)
|
|
conn.reactor.consume_in_thread = mock.Mock()
|
|
conn.consume_in_thread()
|
|
self.assertTrue(mock_getmatchmaker.return_value.start_heartbeat.called)
|
|
self.assertTrue(conn.reactor.consume_in_thread.called)
|
|
|
|
|
|
class TestZmqListener(ZmqBaseTestCase):
|
|
|
|
def test_zmqlistener_no_msg(self):
|
|
listener = impl_zmq.ZmqListener(self.driver)
|
|
# Timeout = 0 should return straight away since the queue is empty
|
|
listener.poll(timeout=0)
|
|
|
|
def test_zmqlistener_w_msg(self):
|
|
listener = impl_zmq.ZmqListener(self.driver)
|
|
kwargs = {'a': 1, 'b': 2}
|
|
m = mock.Mock()
|
|
ctxt = mock.Mock(autospec=impl_zmq.RpcContext)
|
|
message = {'namespace': 'name.space', 'method': m.fake_method,
|
|
'args': kwargs}
|
|
eventlet.spawn_n(listener.dispatch, ctxt, message)
|
|
resp = listener.poll(timeout=10)
|
|
msg = {'method': m.fake_method, 'namespace': 'name.space',
|
|
'args': kwargs}
|
|
self.assertEqual(resp.message, msg)
|
|
|
|
|
|
class TestZmqDriver(ZmqBaseTestCase):
|
|
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq._cast', autospec=True)
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq._multi_send', autospec=True)
|
|
def test_zmqdriver_send(self, mock_multi_send, mock_cast):
|
|
context = mock.Mock(autospec=impl_zmq.RpcContext)
|
|
topic = 'testtopic'
|
|
msg = 'jeronimo'
|
|
self.driver.send(messaging.Target(topic=topic), context, msg,
|
|
False, 0, False)
|
|
mock_multi_send.assert_called_with(self.driver, mock_cast, context,
|
|
topic, msg,
|
|
allowed_remote_exmods=[],
|
|
envelope=False, pooled=True)
|
|
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq._cast', autospec=True)
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq._multi_send', autospec=True)
|
|
def test_zmqdriver_send_notification(self, mock_multi_send, mock_cast):
|
|
context = mock.Mock(autospec=impl_zmq.RpcContext)
|
|
topic = 'testtopic.foo'
|
|
topic_reformat = 'testtopic-foo'
|
|
msg = 'jeronimo'
|
|
self.driver.send_notification(messaging.Target(topic=topic), context,
|
|
msg, False, False)
|
|
mock_multi_send.assert_called_with(self.driver, mock_cast, context,
|
|
topic_reformat, msg,
|
|
allowed_remote_exmods=[],
|
|
envelope=False, pooled=True)
|
|
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq.ZmqListener', autospec=True)
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq.Connection', autospec=True)
|
|
def test_zmqdriver_listen(self, mock_connection, mock_listener):
|
|
mock_listener.return_value = listener = mock.Mock()
|
|
mock_connection.return_value = conn = mock.Mock()
|
|
conn.create_consumer = mock.Mock()
|
|
conn.consume_in_thread = mock.Mock()
|
|
topic = 'testtopic.foo'
|
|
self.driver.listen(messaging.Target(topic=topic))
|
|
conn.create_consumer.assert_called_with(topic, listener, fanout=True)
|
|
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq.ZmqListener', autospec=True)
|
|
@mock.patch('oslo_messaging._drivers.impl_zmq.Connection', autospec=True)
|
|
def test_zmqdriver_listen_for_notification(self, mock_connection,
|
|
mock_listener):
|
|
mock_listener.return_value = listener = mock.Mock()
|
|
mock_connection.return_value = conn = mock.Mock()
|
|
conn.create_consumer = mock.Mock()
|
|
conn.consume_in_thread = mock.Mock()
|
|
topic = 'testtopic.foo'
|
|
data = [(messaging.Target(topic=topic), 0)]
|
|
# NOTE(jamespage): Pooling not supported, just pass None for now.
|
|
self.driver.listen_for_notifications(data, None)
|
|
conn.create_consumer.assert_called_with("%s-%s" % (topic, 0), listener)
|