From 6eb93e844d5634285f3ac86a0e00011b98ceb2c7 Mon Sep 17 00:00:00 2001 From: joehuang Date: Thu, 2 Mar 2017 02:39:34 -0500 Subject: [PATCH] Support WSGI deployment for Tricircle Admin API(part1) 1. What is the problem Currently Tricircle Admin API is running through python command line and will start oslo_service wsgi server directly. The community has set a community wide goal in Pike cycle: "Control Plane API endpoints deployment via WSGI" https://governance.openstack.org/tc/goals/pike/deploy-api-in-wsgi.html Completion Criteria a). Provide WSGI application script file(s) (e.g. to be used by web server). There shouldn't be any web server restriction and the application could be deploying to any web server that support WSGI applications. b). Switch devstack jobs to deploy control-plane API services in WSGI with Apache. Usage of Apache is already the default in Devstack, let's keep using it for consistency unless there is some efforts to support another web server but this is not the case at this time. 2. What is the solution for the problem The first step is to finish these two goals: a). Provide WSGI application script file b). Update devstack related script in Tricircle to use Apache as the web server. The second step will clean and update other documentation accordingly 3. What the features need to be implemented to the Tricircle to realize the solution No new feature delivered to end user. Change-Id: I828f2d846725d18bb4a66a5d357c717e6b7d28bb Signed-off-by: joehuang --- devstack/apache-tricircle-api.template | 39 ++++++++ devstack/plugin.sh | 95 +++++++++++++++++-- devstack/settings | 3 + devstack/verify_cross_pod_install.sh | 6 +- devstack/verify_top_install.sh | 4 +- doc/source/api_v1.rst | 4 +- .../multi-pod-installation-devstack.rst | 6 +- .../single-pod-installation-devstack.rst | 4 +- ...port-wsgi-deployment-21eb19bcb04932f0.yaml | 6 ++ setup.cfg | 4 +- tricircle/api/wsgi.py | 57 +++++++++++ tricircle/tempestplugin/post_test_hook.sh | 4 +- 12 files changed, 209 insertions(+), 23 deletions(-) create mode 100644 devstack/apache-tricircle-api.template create mode 100644 releasenotes/notes/support-wsgi-deployment-21eb19bcb04932f0.yaml create mode 100644 tricircle/api/wsgi.py diff --git a/devstack/apache-tricircle-api.template b/devstack/apache-tricircle-api.template new file mode 100644 index 00000000..10723045 --- /dev/null +++ b/devstack/apache-tricircle-api.template @@ -0,0 +1,39 @@ +# apache configuration template for tricircle-api + +Listen %PUBLICPORT% +LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\" %D(us)" tricircle_combined + + + Require all granted + + + WSGIDaemonProcess tricircle-api processes=%APIWORKERS% threads=1 user=%USER% display-name=%{GROUP} %VIRTUALENV% + WSGIProcessGroup tricircle-api + WSGIScriptAlias / %PUBLICWSGI% + WSGIApplicationGroup %{GLOBAL} + WSGIPassAuthorization On + = 2.4> + ErrorLogFormat "%M" + + ErrorLog /var/log/%APACHE_NAME%/tricircle-api.log + CustomLog /var/log/%APACHE_NAME%/tricircle_access.log tricircle_combined + %SSLENGINE% + %SSLCERTFILE% + %SSLKEYFILE% + + +%SSLLISTEN% +%SSLLISTEN% %SSLENGINE% +%SSLLISTEN% %SSLCERTFILE% +%SSLLISTEN% %SSLKEYFILE% +%SSLLISTEN% + +Alias /tricircle %PUBLICWSGI% + + SetHandler wsgi-script + Options +ExecCGI + + WSGIProcessGroup tricircle-api + WSGIApplicationGroup %{GLOBAL} + WSGIPassAuthorization On + diff --git a/devstack/plugin.sh b/devstack/plugin.sh index a9c76e32..88026c76 100644 --- a/devstack/plugin.sh +++ b/devstack/plugin.sh @@ -15,14 +15,20 @@ function is_tricircle_enabled { function create_tricircle_accounts { if [[ "$ENABLED_SERVICES" =~ "t-api" ]]; then - create_service_user "tricircle" + create_service_user "tricircle" "admin" local tricircle_api=$(get_or_create_service "tricircle" \ "tricircle" "Cross Neutron Networking Automation Service") + + local tricircle_api_url="$SERVICE_PROTOCOL://$TRICIRCLE_API_HOST/tricircle" + if [[ "$TRICIRCLE_DEPLOY_WITH_WSGI" == "False" ]]; then + tricircle_api_url="$SERVICE_PROTOCOL://$TRICIRCLE_API_HOST:$TRICIRCLE_API_PORT/" + fi + get_or_create_endpoint $tricircle_api \ "$CENTRAL_REGION_NAME" \ - "$SERVICE_PROTOCOL://$TRICIRCLE_API_HOST:$TRICIRCLE_API_PORT/v1.0" \ - "$SERVICE_PROTOCOL://$TRICIRCLE_API_HOST:$TRICIRCLE_API_PORT/v1.0" \ - "$SERVICE_PROTOCOL://$TRICIRCLE_API_HOST:$TRICIRCLE_API_PORT/v1.0" + "$tricircle_api_url" \ + "$tricircle_api_url" \ + "$tricircle_api_url" fi } @@ -130,6 +136,68 @@ function configure_tricircle_api { fi } +# configure_tricircle_api_wsgi() - Set WSGI config files +function configure_tricircle_api_wsgi { + local tricircle_api_apache_conf + local venv_path="" + local tricircle_bin_dir="" + local tricircle_ssl_listen="#" + + tricircle_bin_dir=$(get_python_exec_prefix) + tricircle_api_apache_conf=$(apache_site_config_for tricircle-api) + + if is_ssl_enabled_service "tricircle-api"; then + tricircle_ssl_listen="" + tricircle_ssl="SSLEngine On" + tricircle_certfile="SSLCertificateFile $TRICIRCLE_SSL_CERT" + tricircle_keyfile="SSLCertificateKeyFile $TRICIRCLE_SSL_KEY" + fi + + # configure venv bin if VENV is used + if [[ ${USE_VENV} = True ]]; then + venv_path="python-path=${PROJECT_VENV["tricircle"]}/lib/$(python_version)/site-packages" + tricircle_bin_dir=${PROJECT_VENV["tricircle"]}/bin + fi + + sudo cp $TRICIRCLE_API_APACHE_TEMPLATE $tricircle_api_apache_conf + sudo sed -e " + s|%PUBLICPORT%|$TRICIRCLE_API_PORT|g; + s|%APACHE_NAME%|$APACHE_NAME|g; + s|%PUBLICWSGI%|$tricircle_bin_dir/tricircle-api-wsgi|g; + s|%SSLENGINE%|$tricircle_ssl|g; + s|%SSLCERTFILE%|$tricircle_certfile|g; + s|%SSLKEYFILE%|$tricircle_keyfile|g; + s|%SSLLISTEN%|$tricircle_ssl_listen|g; + s|%USER%|$STACK_USER|g; + s|%VIRTUALENV%|$venv_path|g + s|%APIWORKERS%|$API_WORKERS|g + " -i $tricircle_api_apache_conf +} + +# start_tricircle_api_wsgi() - Start the API processes ahead of other things +function start_tricircle_api_wsgi { + enable_apache_site tricircle-api + restart_apache_server + tail_log tricircle-api /var/log/$APACHE_NAME/tricircle-api.log + + echo "Waiting for tricircle-api to start..." + if ! wait_for_service $SERVICE_TIMEOUT $TRICIRCLE_API_PROTOCOL://$TRICIRCLE_API_HOST/tricircle; then + die $LINENO "tricircle-api did not start" + fi +} + +# stop_tricircle_api_wsgi() - Disable the api service and stop it. +function stop_tricircle_api_wsgi { + disable_apache_site tricircle-api + restart_apache_server +} + +# cleanup_tricircle_api_wsgi() - Remove residual data files, anything left over from previous +# runs that a clean run would need to clean up +function cleanup_tricircle_api_wsgi { + sudo rm -f $(apache_site_config_for tricircle-api) +} + function configure_tricircle_xjob { if is_service_enabled t-job ; then echo "Configuring Tricircle xjob" @@ -214,6 +282,10 @@ elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then enable_service t-api t-job configure_tricircle_api configure_tricircle_xjob + + if [[ "$TRICIRCLE_DEPLOY_WITH_WSGI" == "True" ]]; then + configure_tricircle_api_wsgi + fi fi echo export PYTHONPATH=\$PYTHONPATH:$TRICIRCLE_DIR >> $RC_DIR/.localrc.auto @@ -243,12 +315,14 @@ elif [[ "$1" == "stack" && "$2" == "extra" ]]; then create_tricircle_accounts - run_process t-api "tricircle-api --config-file $TRICIRCLE_API_CONF" - + if [[ "$TRICIRCLE_DEPLOY_WITH_WSGI" == "True" ]]; then + start_tricircle_api_wsgi + else + run_process t-api "tricircle-api --config-file $TRICIRCLE_API_CONF" + fi fi if is_service_enabled t-job; then - run_process t-job "tricircle-xjob --config-file $TRICIRCLE_XJOB_CONF" fi fi @@ -256,7 +330,12 @@ fi if [[ "$1" == "unstack" ]]; then if is_service_enabled t-api; then - stop_process t-api + if [[ "$TRICIRCLE_DEPLOY_WITH_WSGI" == "True" ]]; then + stop_tricircle_api_wsgi + clean_tricircle_api_wsgi + else + stop_process t-api + fi fi if is_service_enabled t-job; then diff --git a/devstack/settings b/devstack/settings index 9c3b8242..b5c723ef 100644 --- a/devstack/settings +++ b/devstack/settings @@ -7,6 +7,7 @@ TRICIRCLE_BRANCH=${TRICIRCLE_BRANCH:-master} CENTRAL_REGION_NAME=${CENTRAL_REGION_NAME:-CentralRegion} TRICIRCLE_NEUTRON_PORT=${TRICIRCLE_NEUTRON_PORT:-20001} TRICIRCLE_START_SERVICES=${TRICIRCLE_START_SERVICES:-True} +TRICIRCLE_DEPLOY_WITH_WSGI=${TRICIRCLE_DEPLOY_WITH_WSGI:-True} # these default settings are used for devstack based gate/check jobs TRICIRCLE_DEFAULT_VLAN_BRIDGE=${TRICIRCLE_DEFAULT_VLAN_BRIDGE:-br-vlan} @@ -20,7 +21,9 @@ TRICIRCLE_CONF_DIR=${TRICIRCLE_CONF_DIR:-/etc/tricircle} TRICIRCLE_STATE_PATH=${TRICIRCLE_STATE_PATH:-/var/lib/tricircle} # tricircle rest admin api +TRICIRCLE_API=$TRICIRCLE_DIR/tricircle/cmd/api.py TRICIRCLE_API_CONF=$TRICIRCLE_CONF_DIR/api.conf +TRICIRCLE_API_APACHE_TEMPLATE=$TRICIRCLE_DIR/devstack/apache-tricircle-api.template TRICIRCLE_API_LISTEN_ADDRESS=${TRICIRCLE_API_LISTEN_ADDRESS:-0.0.0.0} TRICIRCLE_API_HOST=${TRICIRCLE_API_HOST:-$SERVICE_HOST} diff --git a/devstack/verify_cross_pod_install.sh b/devstack/verify_cross_pod_install.sh index bbd22646..0b5e127d 100755 --- a/devstack/verify_cross_pod_install.sh +++ b/devstack/verify_cross_pod_install.sh @@ -47,13 +47,13 @@ token=$(openstack token issue | awk 'NR==5 {print $4}') echo $token -curl -X POST http://127.0.0.1:19999/v1.0/pods -H "Content-Type: application/json" \ +curl -X POST http://127.0.0.1/tricircle/v1.0/pods -H "Content-Type: application/json" \ -H "X-Auth-Token: $token" -d '{"pod": {"region_name": "RegionOne"}}' -curl -X POST http://127.0.0.1:19999/v1.0/pods -H "Content-Type: application/json" \ +curl -X POST http://127.0.0.1/tricircle/v1.0/pods -H "Content-Type: application/json" \ -H "X-Auth-Token: $token" -d '{"pod": {"region_name": "Pod1", "az_name": "az1"}}' -curl -X POST http://127.0.0.1:19999/v1.0/pods -H "Content-Type: application/json" \ +curl -X POST http://127.0.0.1/tricircle/v1.0/pods -H "Content-Type: application/json" \ -H "X-Auth-Token: $token" -d '{"pod": {"region_name": "Pod2", "az_name": "az2"}}' echo "******************************" diff --git a/devstack/verify_top_install.sh b/devstack/verify_top_install.sh index aec9d9b0..f385d509 100755 --- a/devstack/verify_top_install.sh +++ b/devstack/verify_top_install.sh @@ -43,10 +43,10 @@ token=$(openstack token issue | awk 'NR==5 {print $4}') echo $token -curl -X POST http://127.0.0.1:19999/v1.0/pods -H "Content-Type: application/json" \ +curl -X POST http://127.0.0.1/tricircle/v1.0/pods -H "Content-Type: application/json" \ -H "X-Auth-Token: $token" -d '{"pod": {"region_name": "RegionOne"}}' -curl -X POST http://127.0.0.1:19999/v1.0/pods -H "Content-Type: application/json" \ +curl -X POST http://127.0.0.1/tricircle/v1.0/pods -H "Content-Type: application/json" \ -H "X-Auth-Token: $token" -d '{"pod": {"region_name": "Pod1", "az_name": "az1"}}' echo "******************************" diff --git a/doc/source/api_v1.rst b/doc/source/api_v1.rst index 10513e76..d426ed5a 100644 --- a/doc/source/api_v1.rst +++ b/doc/source/api_v1.rst @@ -25,8 +25,8 @@ the OpenStack Identity service. They also require a base service url that can be got from the OpenStack Tricircle endpoint. This will be the root url that every call below will be added to build a full path. -For instance, if the Tricircle service url is http://127.0.0.1:19999/v1.0 then -the full API call for /pods is http://127.0.0.1:19999/v1.0/pods. +For instance, if the Tricircle service url is http://127.0.0.1/tricircle/v1.0 +then the full API call for /pods is http://127.0.0.1/tricircle/v1.0/pods. As such, for the rest of this document we will leave out the root url where GET /pods really means GET {tricircle_service_url}/pods. diff --git a/doc/source/multi-pod-installation-devstack.rst b/doc/source/multi-pod-installation-devstack.rst index 6899c0f0..8c72ca90 100644 --- a/doc/source/multi-pod-installation-devstack.rst +++ b/doc/source/multi-pod-installation-devstack.rst @@ -265,13 +265,13 @@ How to play - 5 Create pod instances for the Tricircle to manage the mapping between availability zones and OpenStack instances, "$token" is obtained in step 4 :: - curl -X POST http://127.0.0.1:19999/v1.0/pods -H "Content-Type: application/json" \ + curl -X POST http://127.0.0.1/tricircle/v1.0/pods -H "Content-Type: application/json" \ -H "X-Auth-Token: $token" -d '{"pod": {"region_name": "CentralRegion"}}' - curl -X POST http://127.0.0.1:19999/v1.0/pods -H "Content-Type: application/json" \ + curl -X POST http://127.0.0.1/tricircle/v1.0/pods -H "Content-Type: application/json" \ -H "X-Auth-Token: $token" -d '{"pod": {"region_name": "RegionOne", "az_name": "az1"}}' - curl -X POST http://127.0.0.1:19999/v1.0/pods -H "Content-Type: application/json" \ + curl -X POST http://127.0.0.1/tricircle/v1.0/pods -H "Content-Type: application/json" \ -H "X-Auth-Token: $token" -d '{"pod": {"region_name": "RegionTwo", "az_name": "az2"}}' Pay attention to "region_name" parameter we specify when creating pod. Pod name diff --git a/doc/source/single-pod-installation-devstack.rst b/doc/source/single-pod-installation-devstack.rst index a3560fac..4a77cb9f 100644 --- a/doc/source/single-pod-installation-devstack.rst +++ b/doc/source/single-pod-installation-devstack.rst @@ -67,10 +67,10 @@ installing DevStack in virtual machine. availability zone and OpenStack instances, the "$token" is obtained in the step 7:: - curl -X POST http://127.0.0.1:19999/v1.0/pods -H "Content-Type: application/json" \ + curl -X POST http://127.0.0.1/tricircle/v1.0/pods -H "Content-Type: application/json" \ -H "X-Auth-Token: $token" -d '{"pod": {"region_name": "CentralRegion"}}' - curl -X POST http://127.0.0.1:19999/v1.0/pods -H "Content-Type: application/json" \ + curl -X POST http://127.0.0.1/tricircle/v1.0/pods -H "Content-Type: application/json" \ -H "X-Auth-Token: $token" -d '{"pod": {"region_name": "RegionOne", "az_name": "az1"}}' Pay attention to "region_name" parameter we specify when creating pod. Pod name diff --git a/releasenotes/notes/support-wsgi-deployment-21eb19bcb04932f0.yaml b/releasenotes/notes/support-wsgi-deployment-21eb19bcb04932f0.yaml new file mode 100644 index 00000000..fa390352 --- /dev/null +++ b/releasenotes/notes/support-wsgi-deployment-21eb19bcb04932f0.yaml @@ -0,0 +1,6 @@ +--- +prelude: > + Tricircle Admin API now supports WSGI deployment. The endpoint of + Tricircle Admin API could be accessed via the format of + http://host/tricircle, and no need to expose special port, thus + reduce the risk of port management. diff --git a/setup.cfg b/setup.cfg index 057e9a9e..595e8b7b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -45,9 +45,11 @@ output_file = tricircle/locale/tricircle.pot [entry_points] console_scripts = - tricircle-db-manage = tricircle.cmd.manage:main tricircle-api = tricircle.cmd.api:main + tricircle-db-manage = tricircle.cmd.manage:main tricircle-xjob = tricircle.cmd.xjob:main +wsgi_scripts = + tricircle-api-wsgi = tricircle.api.wsgi:init_application oslo.config.opts = tricircle.api = tricircle.api.opts:list_opts tricircle.common = tricircle.common.opts:list_opts diff --git a/tricircle/api/wsgi.py b/tricircle/api/wsgi.py new file mode 100644 index 00000000..21896717 --- /dev/null +++ b/tricircle/api/wsgi.py @@ -0,0 +1,57 @@ +# Copyright (c) 2017 Huawei Tech. Co,. 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. + +"""WSGI script for Tricircle API +WSGI handler for running Tricircle API under Apache2, nginx, gunicorn etc. + +Community wide goal in Pike: + https://governance.openstack.org/tc/goals/pike/deploy-api-in-wsgi.html +""" + +import os +import os.path + +from oslo_config import cfg +from oslo_log import log as logging + +from tricircle.api import app +from tricircle.common import config +from tricircle.common.i18n import _LI + +CONFIG_FILE = 'api.conf' + +CONF = cfg.CONF +LOG = logging.getLogger(__name__) + + +def _get_config_file(env=None): + if env is None: + env = os.environ + + dir_name = env.get('TRICIRCLE_CONF_DIR', '/etc/tricircle').strip() + return os.path.join(dir_name, CONFIG_FILE) + + +def init_application(): + + # initialize the config system + conf_file = _get_config_file() + config.init(app.common_opts, ['--config-file', conf_file]) + + LOG.info(_LI("Configuration:")) + CONF.log_opt_values(LOG, logging.INFO) + + # return WSGI app + return app.setup_app() diff --git a/tricircle/tempestplugin/post_test_hook.sh b/tricircle/tempestplugin/post_test_hook.sh index 1376abd0..8ceb072c 100755 --- a/tricircle/tempestplugin/post_test_hook.sh +++ b/tricircle/tempestplugin/post_test_hook.sh @@ -28,11 +28,11 @@ source $DEVSTACK_DIR/openrc admin admin token=$(openstack token issue | awk 'NR==5 {print $4}') echo $token -curl -X POST http://127.0.0.1:19999/v1.0/pods \ +curl -X POST http://127.0.0.1/tricircle/v1.0/pods \ -H "Content-Type: application/json" \ -H "X-Auth-Token: $token" -d '{"pod": {"region_name": "RegionOne"}}' -curl -X POST http://127.0.0.1:19999/v1.0/pods \ +curl -X POST http://127.0.0.1/tricircle/v1.0/pods \ -H "Content-Type: application/json" \ -H "X-Auth-Token: $token" \ -d '{"pod": {"region_name": "Pod1", "az_name": "az1"}}'