173 lines
6.5 KiB
Python
173 lines
6.5 KiB
Python
#! /usr/bin/env python
|
|
#
|
|
# Copyright (c) 2014 VMware, Inc. 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.
|
|
#
|
|
|
|
from __future__ import print_function
|
|
from __future__ import division
|
|
from __future__ import absolute_import
|
|
import socket
|
|
import sys
|
|
|
|
from oslo_config import cfg
|
|
from oslo_log import log as logging
|
|
from oslo_service import service
|
|
|
|
from congress.common import config
|
|
from congress.common import eventlet_server
|
|
from congress.db import api as db_api
|
|
from congress import encryption
|
|
from congress import harness
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class ServerWrapper(object):
|
|
"""Wraps an eventlet_server with some launching info & capabilities."""
|
|
|
|
def __init__(self, server, workers):
|
|
self.server = server
|
|
self.workers = workers
|
|
|
|
def launch_with(self, launcher):
|
|
if hasattr(self.server, 'listen'):
|
|
self.server.listen()
|
|
if self.workers > 1:
|
|
# Use multi-process launcher
|
|
launcher.launch_service(self.server, self.workers)
|
|
else:
|
|
# Use single process launcher
|
|
launcher.launch_service(self.server)
|
|
|
|
|
|
def serve(*servers):
|
|
if max([server[1].workers for server in servers]) > 1:
|
|
# TODO(arosen) - need to provide way to communicate with DSE services
|
|
launcher = service.ProcessLauncher(cfg.CONF, restart_method='mutate')
|
|
else:
|
|
launcher = service.ServiceLauncher(cfg.CONF, restart_method='mutate')
|
|
|
|
for name, server in servers:
|
|
try:
|
|
server.launch_with(launcher)
|
|
except socket.error:
|
|
LOG.exception(_('Failed to start the %s server'), name)
|
|
raise
|
|
|
|
try:
|
|
launcher.wait()
|
|
except KeyboardInterrupt:
|
|
LOG.info("Congress server stopped by interrupt.")
|
|
|
|
|
|
def create_api_server(conf_path, node_id, host, port, workers, policy_engine,
|
|
datasources):
|
|
congress_api_server = eventlet_server.APIServer(
|
|
conf_path, node_id, host=host, port=port,
|
|
keepalive=cfg.CONF.tcp_keepalive,
|
|
keepidle=cfg.CONF.tcp_keepidle,
|
|
policy_engine=policy_engine,
|
|
api=True,
|
|
datasources=datasources,
|
|
bus_id=cfg.CONF.dse.bus_id)
|
|
# TODO(thinrichs): there's some sort of magic happening for the api
|
|
# server. We call eventlet_server, which on start() calls
|
|
# service.congress_app_factory, which uses harness to create the
|
|
# API service, which the magic seems to need to do the right thing.
|
|
# That's why we're not just calling harness.create2 here; instead,
|
|
# it's buried inside the congress_app_factory.
|
|
return node_id, ServerWrapper(congress_api_server, workers)
|
|
|
|
|
|
def create_nonapi_server(node_id, policy_engine, datasources, workers):
|
|
congress_server = eventlet_server.Server(
|
|
node_id, bus_id=cfg.CONF.dse.bus_id)
|
|
harness.create2(existing_node=congress_server.node, api=False,
|
|
policy_engine=policy_engine,
|
|
datasources=datasources)
|
|
return node_id, ServerWrapper(congress_server, workers)
|
|
|
|
|
|
def launch_servers(node_id, api, policy, data):
|
|
servers = []
|
|
if api:
|
|
LOG.info("Starting congress API server on port %d", cfg.CONF.bind_port)
|
|
# API resource runtime encapsulation:
|
|
# event loop -> wsgi server -> webapp -> resource manager
|
|
paste_config = config.find_paste_config()
|
|
config.set_config_defaults()
|
|
servers.append(create_api_server(paste_config,
|
|
node_id,
|
|
cfg.CONF.bind_host,
|
|
cfg.CONF.bind_port,
|
|
cfg.CONF.api_workers,
|
|
policy_engine=policy,
|
|
datasources=data))
|
|
else:
|
|
LOG.info("Starting congress server on node %s", node_id)
|
|
servers.append(create_nonapi_server(node_id, policy, data,
|
|
cfg.CONF.api_workers))
|
|
|
|
return servers
|
|
|
|
|
|
def main():
|
|
args = sys.argv[1:]
|
|
|
|
# TODO(thinrichs): find the right way to do deployment configuration.
|
|
# For some reason we need to config.init(args) in 2 places; here and
|
|
# at the top of the file (now moved to bin/congress-server).
|
|
# Remove either one, and things break.
|
|
# Note: config.init() will delete the deploy args, so grab them before.
|
|
config.init(args)
|
|
if not cfg.CONF.config_file:
|
|
sys.exit("ERROR: Unable to find configuration file via default "
|
|
"search paths ~/.congress/, ~/, /etc/congress/, /etc/) and "
|
|
"the '--config-file' option!")
|
|
if cfg.CONF.replicated_policy_engine and not (
|
|
db_api.is_mysql() or db_api.is_postgres()):
|
|
if db_api.is_sqlite():
|
|
LOG.warning("Deploying replicated policy engine with SQLite "
|
|
"backend is not officially supported. Unexpected "
|
|
"behavior may occur. Officially supported backends "
|
|
"are MySQL and PostgresSQL.")
|
|
else:
|
|
sys.exit("ERROR: replicated_policy_engine option can be used only "
|
|
"with MySQL or PostgreSQL database backends. Please set "
|
|
"the connection option in [database] section of "
|
|
"congress.conf to use a supported backend.")
|
|
config.setup_logging()
|
|
|
|
if not (cfg.CONF.api or cfg.CONF.policy_engine or cfg.CONF.datasources):
|
|
# No flags provided, start all services
|
|
cfg.CONF.api = True
|
|
cfg.CONF.policy_engine = True
|
|
cfg.CONF.datasources = True
|
|
|
|
# initialize encryption key if datasource services enabled in this instance
|
|
if cfg.CONF.datasources:
|
|
encryption.initialize_key()
|
|
LOG.debug("Initialized encryption key on datasource node")
|
|
|
|
# Construct requested deployment
|
|
servers = launch_servers(cfg.CONF.node_id, cfg.CONF.api,
|
|
cfg.CONF.policy_engine, cfg.CONF.datasources)
|
|
|
|
serve(*servers)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|