diff --git a/neutron/cmd/eventlet/server/__init__.py b/neutron/cmd/eventlet/server/__init__.py index bfa2568628d..01c3b52c1ec 100644 --- a/neutron/cmd/eventlet/server/__init__.py +++ b/neutron/cmd/eventlet/server/__init__.py @@ -10,8 +10,14 @@ # License for the specific language governing permissions and limitations # under the License. -from neutron import server +from neutron.server import wsgi_eventlet +from neutron.server import wsgi_pecan -def main(): - server.main() +def main_wsgi_eventlet(): + # This also starts the RPC server + wsgi_eventlet.main() + + +def main_wsgi_pecan(): + wsgi_pecan.main() diff --git a/neutron/common/rpc.py b/neutron/common/rpc.py index 6fe39842b7e..3037f5342f0 100644 --- a/neutron/common/rpc.py +++ b/neutron/common/rpc.py @@ -47,6 +47,12 @@ TRANSPORT_ALIASES = { 'neutron.rpc.impl_zmq': 'zmq', } +# NOTE(salv-orlando): I am afraid this is a global variable. While not ideal, +# they're however widely used throughout the code base. It should be set to +# true if the RPC server is not running in the current process space. This +# will prevent get_connection from creating connections to the AMQP server +RPC_DISABLED = False + def init(conf): global TRANSPORT, NOTIFIER @@ -201,6 +207,25 @@ class Connection(object): server.wait() +class VoidConnection(object): + + def create_consumer(self, topic, endpoints, fanout=False): + pass + + def consume_in_threads(self): + pass + + def close(self): + pass + + # functions def create_connection(new=True): + # NOTE(salv-orlando): This is a clever interpreation of the factory design + # patter aimed at preventing plugins from initializing RPC servers upon + # initialization when they are running in the REST over HTTP API server. + # The educated reader will perfectly be able that this a fairly dirty hack + # to avoid having to change the initialization process of every plugin. + if RPC_DISABLED: + return VoidConnection() return Connection() diff --git a/neutron/server/__init__.py b/neutron/server/__init__.py index 6c5bd4fe600..108e9f4d4ed 100644 --- a/neutron/server/__init__.py +++ b/neutron/server/__init__.py @@ -20,43 +20,21 @@ import sys -import eventlet from oslo_config import cfg -from oslo_log import log as logging from neutron.common import config -from neutron.i18n import _LI -from neutron import service - -LOG = logging.getLogger(__name__) -def main(): +def boot_server(server_func): # the configuration will be read into the cfg.CONF global data structure config.init(sys.argv[1:]) + config.setup_logging() if not cfg.CONF.config_file: sys.exit(_("ERROR: Unable to find configuration file via the default" " search paths (~/.neutron/, ~/, /etc/neutron/, /etc/) and" " the '--config-file' option!")) try: - pool = eventlet.GreenPool() - - neutron_api = service.serve_wsgi(service.NeutronApiService) - api_thread = pool.spawn(neutron_api.wait) - - try: - neutron_rpc = service.serve_rpc() - except NotImplementedError: - LOG.info(_LI("RPC was already started in parent process by " - "plugin.")) - else: - rpc_thread = pool.spawn(neutron_rpc.wait) - - # api and rpc should die together. When one dies, kill the other. - rpc_thread.link(lambda gt: api_thread.kill()) - api_thread.link(lambda gt: rpc_thread.kill()) - - pool.waitall() + server_func() except KeyboardInterrupt: pass except RuntimeError as e: diff --git a/neutron/server/wsgi_eventlet.py b/neutron/server/wsgi_eventlet.py new file mode 100644 index 00000000000..ad89f3ea48a --- /dev/null +++ b/neutron/server/wsgi_eventlet.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +# 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 eventlet + +from oslo_log import log + +from neutron.i18n import _LI +from neutron import server +from neutron import service + +LOG = log.getLogger(__name__) + + +def _eventlet_wsgi_server(): + pool = eventlet.GreenPool() + + neutron_api = service.serve_wsgi(service.NeutronApiService) + api_thread = pool.spawn(neutron_api.wait) + + try: + neutron_rpc = service.serve_rpc() + except NotImplementedError: + LOG.info(_LI("RPC was already started in parent process by " + "plugin.")) + else: + rpc_thread = pool.spawn(neutron_rpc.wait) + + # api and rpc should die together. When one dies, kill the other. + rpc_thread.link(lambda gt: api_thread.kill()) + api_thread.link(lambda gt: rpc_thread.kill()) + + pool.waitall() + + +def main(): + server.boot_server(_eventlet_wsgi_server) diff --git a/neutron/server/wsgi_pecan.py b/neutron/server/wsgi_pecan.py new file mode 100755 index 00000000000..a9d62b69628 --- /dev/null +++ b/neutron/server/wsgi_pecan.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# 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 as std_logging +from wsgiref import simple_server + +from oslo_config import cfg +from oslo_log import log +from six.moves import socketserver + +from neutron.common import rpc as n_rpc +from neutron.i18n import _LI, _LW +from neutron.newapi import app as pecan_app +from neutron import server + +LOG = log.getLogger(__name__) + + +class ThreadedSimpleServer(socketserver.ThreadingMixIn, + simple_server.WSGIServer): + pass + + +def _pecan_wsgi_server(): + LOG.info(_LI("Pecan WSGI server starting...")) + # No AMQP connection should be created within this process + n_rpc.RPC_DISABLED = True + application = pecan_app.setup_app() + + host = cfg.CONF.bind_host + port = cfg.CONF.bind_port + + wsgi = simple_server.make_server( + host, + port, + application, + server_class=ThreadedSimpleServer + ) + # Log option values + cfg.CONF.log_opt_values(LOG, std_logging.DEBUG) + LOG.warning( + _LW("Development Server Serving on http://%(host)s:%(port)s"), + {'host': host, 'port': port} + ) + + wsgi.serve_forever() + + +def main(): + server.boot_server(_pecan_wsgi_server) diff --git a/setup.cfg b/setup.cfg index 8ecc3ce6c13..f1bcddaf9f8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -86,7 +86,6 @@ scripts = console_scripts = neutron-db-manage = neutron.db.migration.cli:main neutron-debug = neutron.debug.shell:main - neutron-dev-server = neutron.cmd.eventlet.api:main neutron-dhcp-agent = neutron.cmd.eventlet.agents.dhcp:main neutron-hyperv-agent = neutron.cmd.eventlet.plugins.hyperv_neutron_agent:main neutron-keepalived-state-change = neutron.cmd.keepalived_state_change:main @@ -104,7 +103,8 @@ console_scripts = neutron-openvswitch-agent = neutron.cmd.eventlet.plugins.ovs_neutron_agent:main neutron-ovs-cleanup = neutron.cmd.ovs_cleanup:main neutron-restproxy-agent = neutron.plugins.bigswitch.agent.restproxy_agent:main - neutron-server = neutron.cmd.eventlet.server:main + neutron-server = neutron.cmd.eventlet.server:main_wsgi_eventlet + neutron-dev-server = neutron.cmd.eventlet.server:main_wsgi_pecan neutron-rootwrap = oslo_rootwrap.cmd:main neutron-rootwrap-daemon = oslo_rootwrap.cmd:daemon neutron-usage-audit = neutron.cmd.usage_audit:main