Integrating new RPC layer with Mistral
At this point all tests pass, but there is no test for kombu driver. Komu driver must be consider as expermiental before new tests are introduced. TODO (next commits): - tests Partially implements blueprint mistral-alternative-rpc Co-Authored-By: Dawid Deja <dawid.deja@intel.com> Change-Id: I1f5ca1f1e8c741efcb61480ccbec8e50ad993cba
This commit is contained in:
parent
fc6d712840
commit
95e6b34b17
@ -25,9 +25,10 @@ eventlet.monkey_patch(
|
||||
thread=False if '--use-debugger' in sys.argv else True,
|
||||
time=True)
|
||||
|
||||
|
||||
import os
|
||||
import six
|
||||
import time
|
||||
|
||||
|
||||
# If ../mistral/__init__.py exists, add ../ to Python search path, so that
|
||||
# it will override what happens to be installed in /usr/(local/)lib/python...
|
||||
@ -39,7 +40,6 @@ if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'mistral', '__init__.py')):
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import oslo_messaging as messaging
|
||||
|
||||
if six.PY3 is True:
|
||||
import socketserver
|
||||
@ -51,7 +51,6 @@ from wsgiref.simple_server import WSGIServer
|
||||
|
||||
from mistral.api import app
|
||||
from mistral import config
|
||||
from mistral import context as ctx
|
||||
from mistral.db.v2 import api as db_api
|
||||
from mistral.engine import default_engine as def_eng
|
||||
from mistral.engine import default_executor as def_executor
|
||||
@ -59,6 +58,7 @@ from mistral.engine.rpc import rpc
|
||||
from mistral.services import expiration_policy
|
||||
from mistral.services import scheduler
|
||||
from mistral.utils import profiler
|
||||
from mistral.utils import rpc_utils
|
||||
from mistral import version
|
||||
|
||||
|
||||
@ -67,51 +67,33 @@ CONF = cfg.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def launch_executor(transport):
|
||||
def launch_executor():
|
||||
profiler.setup('mistral-executor', cfg.CONF.executor.host)
|
||||
|
||||
target = messaging.Target(
|
||||
topic=cfg.CONF.executor.topic,
|
||||
server=cfg.CONF.executor.host
|
||||
)
|
||||
|
||||
executor_v2 = def_executor.DefaultExecutor(rpc.get_engine_client())
|
||||
executor_endpoint = rpc.ExecutorServer(executor_v2)
|
||||
|
||||
endpoints = [rpc.ExecutorServer(executor_v2)]
|
||||
|
||||
server = messaging.get_rpc_server(
|
||||
transport,
|
||||
target,
|
||||
endpoints,
|
||||
executor='eventlet',
|
||||
serializer=ctx.RpcContextSerializer(ctx.JsonPayloadSerializer())
|
||||
executor_server = rpc.get_rpc_server_driver()(
|
||||
rpc_utils.get_rpc_info_from_oslo(CONF.executor)
|
||||
)
|
||||
executor_server.register_endpoint(executor_endpoint)
|
||||
|
||||
executor_v2.register_membership()
|
||||
|
||||
try:
|
||||
server.start()
|
||||
while True:
|
||||
time.sleep(604800)
|
||||
executor_server.run()
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
pass
|
||||
finally:
|
||||
print("Stopping executor service...")
|
||||
server.stop()
|
||||
server.wait()
|
||||
|
||||
|
||||
def launch_engine(transport):
|
||||
def launch_engine():
|
||||
profiler.setup('mistral-engine', cfg.CONF.engine.host)
|
||||
|
||||
target = messaging.Target(
|
||||
topic=cfg.CONF.engine.topic,
|
||||
server=cfg.CONF.engine.host
|
||||
)
|
||||
|
||||
engine_v2 = def_eng.DefaultEngine(rpc.get_engine_client())
|
||||
|
||||
endpoints = [rpc.EngineServer(engine_v2)]
|
||||
engine_endpoint = rpc.EngineServer(engine_v2)
|
||||
|
||||
# Setup scheduler in engine.
|
||||
db_api.setup_db()
|
||||
@ -120,37 +102,30 @@ def launch_engine(transport):
|
||||
# Setup expiration policy
|
||||
expiration_policy.setup()
|
||||
|
||||
server = messaging.get_rpc_server(
|
||||
transport,
|
||||
target,
|
||||
endpoints,
|
||||
executor='eventlet',
|
||||
serializer=ctx.RpcContextSerializer(ctx.JsonPayloadSerializer())
|
||||
engine_server = rpc.get_rpc_server_driver()(
|
||||
rpc_utils.get_rpc_info_from_oslo(CONF.engine)
|
||||
)
|
||||
engine_server.register_endpoint(engine_endpoint)
|
||||
|
||||
engine_v2.register_membership()
|
||||
|
||||
try:
|
||||
server.start()
|
||||
while True:
|
||||
time.sleep(604800)
|
||||
engine_server.run()
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
pass
|
||||
finally:
|
||||
print("Stopping engine service...")
|
||||
server.stop()
|
||||
server.wait()
|
||||
|
||||
|
||||
class ThreadingWSGIServer(socketserver.ThreadingMixIn, WSGIServer):
|
||||
pass
|
||||
|
||||
|
||||
def launch_api(transport):
|
||||
def launch_api():
|
||||
host = cfg.CONF.api.host
|
||||
port = cfg.CONF.api.port
|
||||
|
||||
server = simple_server.make_server(
|
||||
api_server = simple_server.make_server(
|
||||
host,
|
||||
port,
|
||||
app.setup_app(),
|
||||
@ -160,12 +135,12 @@ def launch_api(transport):
|
||||
LOG.info("Mistral API is serving on http://%s:%s (PID=%s)" %
|
||||
(host, port, os.getpid()))
|
||||
|
||||
server.serve_forever()
|
||||
api_server.serve_forever()
|
||||
|
||||
|
||||
def launch_any(transport, options):
|
||||
def launch_any(options):
|
||||
# Launch the servers on different threads.
|
||||
threads = [eventlet.spawn(LAUNCH_OPTIONS[option], transport)
|
||||
threads = [eventlet.spawn(LAUNCH_OPTIONS[option])
|
||||
for option in options]
|
||||
|
||||
print('Server started.')
|
||||
@ -250,11 +225,11 @@ def main():
|
||||
# servers are launched on the same process. Otherwise, messages do not
|
||||
# get delivered if the Mistral servers are launched on different
|
||||
# processes because the "fake" transport is using an in process queue.
|
||||
transport = rpc.get_transport()
|
||||
rpc.get_transport()
|
||||
|
||||
if cfg.CONF.server == ['all']:
|
||||
# Launch all servers.
|
||||
launch_any(transport, LAUNCH_OPTIONS.keys())
|
||||
launch_any(LAUNCH_OPTIONS.keys())
|
||||
else:
|
||||
# Validate launch option.
|
||||
if set(cfg.CONF.server) - set(LAUNCH_OPTIONS.keys()):
|
||||
@ -262,7 +237,7 @@ def main():
|
||||
'api, engine, and executor.')
|
||||
|
||||
# Launch distinct set of server(s).
|
||||
launch_any(transport, set(cfg.CONF.server))
|
||||
launch_any(set(cfg.CONF.server))
|
||||
|
||||
except RuntimeError as excp:
|
||||
sys.stderr.write("ERROR: %s\n" % excp)
|
||||
|
@ -50,7 +50,8 @@ rpc_impl_opt = cfg.StrOpt(
|
||||
'rpc_implementation',
|
||||
default='oslo',
|
||||
choices=['oslo', 'kombu'],
|
||||
help='Specifies RPC implementation for RPC client and server.'
|
||||
help='Specifies RPC implementation for RPC client and server. Support of '
|
||||
'kombu driver is experimental.'
|
||||
)
|
||||
|
||||
pecan_opts = [
|
||||
|
@ -17,13 +17,12 @@ from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import oslo_messaging as messaging
|
||||
from oslo_messaging.rpc import client
|
||||
from oslo_messaging.rpc import dispatcher
|
||||
from oslo_messaging.rpc import server
|
||||
from stevedore import driver
|
||||
|
||||
from mistral import context as auth_ctx
|
||||
from mistral.engine import base
|
||||
from mistral import exceptions as exc
|
||||
from mistral.utils import rpc_utils
|
||||
from mistral.workflow import utils as wf_utils
|
||||
|
||||
|
||||
@ -38,16 +37,6 @@ _ENGINE_CLIENT = None
|
||||
_EXECUTOR_CLIENT = None
|
||||
|
||||
|
||||
def get_rpc_server(transport, target, endpoints, executor='blocking',
|
||||
serializer=None):
|
||||
return server.RPCServer(
|
||||
transport,
|
||||
target,
|
||||
dispatcher.RPCDispatcher(endpoints, serializer),
|
||||
executor
|
||||
)
|
||||
|
||||
|
||||
def cleanup():
|
||||
"""Intended to be used by tests to recreate all RPC related objects."""
|
||||
|
||||
@ -73,7 +62,9 @@ def get_engine_client():
|
||||
global _ENGINE_CLIENT
|
||||
|
||||
if not _ENGINE_CLIENT:
|
||||
_ENGINE_CLIENT = EngineClient(get_transport())
|
||||
_ENGINE_CLIENT = EngineClient(
|
||||
rpc_utils.get_rpc_info_from_oslo(cfg.CONF.engine)
|
||||
)
|
||||
|
||||
return _ENGINE_CLIENT
|
||||
|
||||
@ -82,7 +73,9 @@ def get_executor_client():
|
||||
global _EXECUTOR_CLIENT
|
||||
|
||||
if not _EXECUTOR_CLIENT:
|
||||
_EXECUTOR_CLIENT = ExecutorClient(get_transport())
|
||||
_EXECUTOR_CLIENT = ExecutorClient(
|
||||
rpc_utils.get_rpc_info_from_oslo(cfg.CONF.executor)
|
||||
)
|
||||
|
||||
return _EXECUTOR_CLIENT
|
||||
|
||||
@ -316,19 +309,12 @@ def wrap_messaging_exception(method):
|
||||
class EngineClient(base.Engine):
|
||||
"""RPC Engine client."""
|
||||
|
||||
def __init__(self, transport):
|
||||
def __init__(self, rpc_conf_dict):
|
||||
"""Constructs an RPC client for engine.
|
||||
|
||||
:param transport: Messaging transport.
|
||||
:param rpc_conf_dict: Dict containing RPC configuration.
|
||||
"""
|
||||
serializer = auth_ctx.RpcContextSerializer(
|
||||
auth_ctx.JsonPayloadSerializer())
|
||||
|
||||
self._client = messaging.RPCClient(
|
||||
transport,
|
||||
messaging.Target(topic=cfg.CONF.engine.topic),
|
||||
serializer=serializer
|
||||
)
|
||||
self._client = get_rpc_client_driver()(rpc_conf_dict)
|
||||
|
||||
@wrap_messaging_exception
|
||||
def start_workflow(self, wf_identifier, wf_input, description='',
|
||||
@ -337,7 +323,7 @@ class EngineClient(base.Engine):
|
||||
|
||||
:return: Workflow execution.
|
||||
"""
|
||||
return self._client.call(
|
||||
return self._client.sync_call(
|
||||
auth_ctx.ctx(),
|
||||
'start_workflow',
|
||||
workflow_identifier=wf_identifier,
|
||||
@ -353,7 +339,7 @@ class EngineClient(base.Engine):
|
||||
|
||||
:return: Action execution.
|
||||
"""
|
||||
return self._client.call(
|
||||
return self._client.sync_call(
|
||||
auth_ctx.ctx(),
|
||||
'start_action',
|
||||
action_name=action_name,
|
||||
@ -363,7 +349,7 @@ class EngineClient(base.Engine):
|
||||
)
|
||||
|
||||
def on_task_state_change(self, task_ex_id, state, state_info=None):
|
||||
return self._client.call(
|
||||
return self._client.sync_call(
|
||||
auth_ctx.ctx(),
|
||||
'on_task_state_change',
|
||||
task_ex_id=task_ex_id,
|
||||
@ -389,7 +375,7 @@ class EngineClient(base.Engine):
|
||||
:return: Task.
|
||||
"""
|
||||
|
||||
return self._client.call(
|
||||
return self._client.sync_call(
|
||||
auth_ctx.ctx(),
|
||||
'on_action_complete',
|
||||
action_ex_id=action_ex_id,
|
||||
@ -405,7 +391,7 @@ class EngineClient(base.Engine):
|
||||
:return: Workflow execution.
|
||||
"""
|
||||
|
||||
return self._client.call(
|
||||
return self._client.sync_call(
|
||||
auth_ctx.ctx(),
|
||||
'pause_workflow',
|
||||
execution_id=wf_ex_id
|
||||
@ -442,7 +428,7 @@ class EngineClient(base.Engine):
|
||||
:return: Workflow execution.
|
||||
"""
|
||||
|
||||
return self._client.call(
|
||||
return self._client.sync_call(
|
||||
auth_ctx.ctx(),
|
||||
'resume_workflow',
|
||||
wf_ex_id=wf_ex_id,
|
||||
@ -463,7 +449,7 @@ class EngineClient(base.Engine):
|
||||
:return: Workflow execution, model.Execution
|
||||
"""
|
||||
|
||||
return self._client.call(
|
||||
return self._client.sync_call(
|
||||
auth_ctx.ctx(),
|
||||
'stop_workflow',
|
||||
execution_id=wf_ex_id,
|
||||
@ -480,7 +466,7 @@ class EngineClient(base.Engine):
|
||||
:return: Workflow execution.
|
||||
"""
|
||||
|
||||
return self._client.call(
|
||||
return self._client.sync_call(
|
||||
auth_ctx.ctx(),
|
||||
'rollback_workflow',
|
||||
execution_id=wf_ex_id
|
||||
@ -522,23 +508,14 @@ class ExecutorServer(object):
|
||||
class ExecutorClient(base.Executor):
|
||||
"""RPC Executor client."""
|
||||
|
||||
def __init__(self, transport):
|
||||
def __init__(self, rpc_conf_dict):
|
||||
"""Constructs an RPC client for the Executor.
|
||||
|
||||
:param transport: Messaging transport.
|
||||
:type transport: Transport.
|
||||
:param rpc_conf_dict: Dict containing RPC configuration.
|
||||
"""
|
||||
|
||||
self.topic = cfg.CONF.executor.topic
|
||||
|
||||
serializer = auth_ctx.RpcContextSerializer(
|
||||
auth_ctx.JsonPayloadSerializer()
|
||||
)
|
||||
|
||||
self._client = messaging.RPCClient(
|
||||
transport,
|
||||
messaging.Target(),
|
||||
serializer=serializer
|
||||
)
|
||||
self._client = get_rpc_client_driver()(rpc_conf_dict)
|
||||
|
||||
def run_action(self, action_ex_id, action_class_str, attributes,
|
||||
action_params, target=None, async=True):
|
||||
@ -561,9 +538,8 @@ class ExecutorClient(base.Executor):
|
||||
'params': action_params
|
||||
}
|
||||
|
||||
call_ctx = self._client.prepare(topic=self.topic, server=target)
|
||||
|
||||
rpc_client_method = call_ctx.cast if async else call_ctx.call
|
||||
rpc_client_method = (self._client.async_call
|
||||
if async else self._client.sync_call)
|
||||
|
||||
res = rpc_client_method(auth_ctx.ctx(), 'run_action', **kwargs)
|
||||
|
||||
|
@ -19,7 +19,9 @@ import datetime
|
||||
import json
|
||||
|
||||
import mock
|
||||
|
||||
from oslo_config import cfg
|
||||
import oslo_messaging
|
||||
|
||||
from mistral.db.v2 import api as db_api
|
||||
from mistral.db.v2.sqlalchemy import models
|
||||
@ -29,6 +31,9 @@ from mistral.tests.unit.api import base
|
||||
from mistral.workflow import states
|
||||
from mistral.workflow import utils as wf_utils
|
||||
|
||||
# This line is needed for correct initialization of messaging config.
|
||||
oslo_messaging.get_transport(cfg.CONF)
|
||||
|
||||
|
||||
ACTION_EX_DB = models.ActionExecution(
|
||||
id='123',
|
||||
@ -146,6 +151,7 @@ MOCK_NOT_FOUND = mock.MagicMock(side_effect=exc.DBEntityNotFoundError())
|
||||
MOCK_DELETE = mock.MagicMock(return_value=None)
|
||||
|
||||
|
||||
@mock.patch.object(rpc, '_IMPL_CLIENT', mock.Mock())
|
||||
class TestActionExecutionsController(base.APITest):
|
||||
def setUp(self):
|
||||
super(TestActionExecutionsController, self).setUp()
|
||||
|
@ -19,6 +19,8 @@ import datetime
|
||||
import json
|
||||
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
import oslo_messaging
|
||||
import uuid
|
||||
from webtest import app as webtest_app
|
||||
|
||||
@ -31,6 +33,9 @@ from mistral.tests.unit.api import base
|
||||
from mistral import utils
|
||||
from mistral.workflow import states
|
||||
|
||||
# This line is needed for correct initialization of messaging config.
|
||||
oslo_messaging.get_transport(cfg.CONF)
|
||||
|
||||
|
||||
WF_EX = models.WorkflowExecution(
|
||||
id='123e4567-e89b-12d3-a456-426655440000',
|
||||
@ -117,6 +122,7 @@ MOCK_NOT_FOUND = mock.MagicMock(side_effect=exc.DBEntityNotFoundError())
|
||||
MOCK_ACTION_EXC = mock.MagicMock(side_effect=exc.ActionException())
|
||||
|
||||
|
||||
@mock.patch.object(rpc, '_IMPL_CLIENT', mock.Mock())
|
||||
class TestExecutionsController(base.APITest):
|
||||
@mock.patch.object(db_api, 'get_workflow_execution', MOCK_WF_EX)
|
||||
def test_get(self):
|
||||
|
@ -99,11 +99,10 @@ class EngineTestCase(base.DbTestCase):
|
||||
|
||||
# Drop all RPC objects (transport, clients).
|
||||
rpc.cleanup()
|
||||
|
||||
transport = rpc.get_transport()
|
||||
|
||||
self.engine_client = rpc.EngineClient(transport)
|
||||
self.executor_client = rpc.ExecutorClient(transport)
|
||||
self.engine_client = rpc.get_engine_client()
|
||||
self.executor_client = rpc.get_executor_client()
|
||||
|
||||
self.engine = def_eng.DefaultEngine(self.engine_client)
|
||||
self.executor = def_exec.DefaultExecutor(self.engine_client)
|
||||
|
@ -19,10 +19,13 @@ from mistral.utils import rpc_utils
|
||||
|
||||
|
||||
class RPCTest(base.EngineTestCase):
|
||||
def setUp(self):
|
||||
super(RPCTest, self).setUp()
|
||||
|
||||
def test_get_rabbit_config(self):
|
||||
conf = cfg.CONF
|
||||
|
||||
rpc_info = rpc_utils.get_rabbit_info_from_oslo(conf.engine)
|
||||
rpc_info = rpc_utils._get_rabbit_info_from_oslo(conf.engine)
|
||||
|
||||
self.assertDictEqual(
|
||||
{
|
||||
@ -34,8 +37,8 @@ class RPCTest(base.EngineTestCase):
|
||||
'host': 'localhost',
|
||||
'exchange': 'openstack',
|
||||
'password': 'guest',
|
||||
'auto_delete': False,
|
||||
'durable_queues': False,
|
||||
'auto_delete': False
|
||||
},
|
||||
rpc_info
|
||||
)
|
@ -452,7 +452,7 @@ class DefaultEngineTest(base.DbTestCase):
|
||||
class DefaultEngineWithTransportTest(eng_test_base.EngineTestCase):
|
||||
def test_engine_client_remote_error(self):
|
||||
mocked = mock.Mock()
|
||||
mocked.call.side_effect = rpc_client.RemoteError(
|
||||
mocked.sync_call.side_effect = rpc_client.RemoteError(
|
||||
'InputException',
|
||||
'Input is wrong'
|
||||
)
|
||||
@ -468,7 +468,7 @@ class DefaultEngineWithTransportTest(eng_test_base.EngineTestCase):
|
||||
|
||||
def test_engine_client_remote_error_arbitrary(self):
|
||||
mocked = mock.Mock()
|
||||
mocked.call.side_effect = KeyError('wrong key')
|
||||
mocked.sync_call.side_effect = KeyError('wrong key')
|
||||
self.engine_client._client = mocked
|
||||
|
||||
exception = self.assertRaises(
|
||||
|
@ -19,7 +19,15 @@ from oslo_config import cfg
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
def get_rabbit_info_from_oslo(additional_conf):
|
||||
def get_rpc_info_from_oslo(additional_conf=None):
|
||||
if CONF.rpc_backend in ['rabbit', 'fake']:
|
||||
return _get_rabbit_info_from_oslo(additional_conf)
|
||||
else:
|
||||
# TODO(nmakhotkin) Implement.
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def _get_rabbit_info_from_oslo(additional_conf):
|
||||
return {
|
||||
'user_id': CONF.oslo_messaging_rabbit.rabbit_userid,
|
||||
'password': CONF.oslo_messaging_rabbit.rabbit_password,
|
||||
|
Loading…
Reference in New Issue
Block a user