Support Neutron API in WSGI with Apache

Allow to deploy Neutron API in WSGI with Apache.

Note: the feature is not tested yet in our functional testing jobs
because it doesn't work in devstack yet. This code will maybe need
some change but that's fine, it's for Pike cycle, nothing needs to be
backported, so we can easily iterate later.

Also remove dependencies in keystone auth manifest where keystone
resources had to be created before we start neutron-server. This is not
possible anymore since neutron-server is run in httpd.

Change-Id: I27bdd8d011097c5fd49277578404b4facafaca25
This commit is contained in:
Emilien Macchi 2017-04-21 11:34:46 -04:00
parent 4950563c8d
commit c1f019f8e1
10 changed files with 386 additions and 26 deletions

17
examples/neutron_wsgi.pp Normal file
View File

@ -0,0 +1,17 @@
# Example of manifest to deploy Neutron API in WSGI with Apache
class { '::neutron':
allow_overlapping_ips => true,
rabbit_password => 'password',
rabbit_user => 'guest',
rabbit_host => 'localhost',
}
class { '::neutron::server':
auth_password => 'password',
database_connection => 'mysql://neutron:password@192.168.1.1/neutron',
service_name => 'httpd',
}
include ::apache
class { '::neutron::wsgi::apache':
ssl => false,
}

View File

@ -80,11 +80,11 @@ class neutron::keystone::auth (
include ::neutron::deps
if $configure_endpoint {
Keystone_endpoint["${region}/${service_name}::${service_type}"] ~> Service <| title == 'neutron-server' |>
Keystone_endpoint["${region}/${service_name}::${service_type}"] ~> Service <| tag == 'neutron-server-eventlet' |>
}
if $configure_user_role {
Keystone_user_role["${auth_name}@${tenant}"] ~> Service <| title == 'neutron-server' |>
Keystone_user_role["${auth_name}@${tenant}"] ~> Service <| tag == 'neutron-server-eventlet' |>
}
keystone::resource::service_identity { 'neutron':

View File

@ -71,6 +71,8 @@ class neutron::params {
$libreswan_package = 'libreswan'
$l3_agent_package = false
$fwaas_package = 'openstack-neutron-fwaas'
$neutron_wsgi_script_path = '/var/www/cgi-bin/neutron'
$neutron_wsgi_script_source = '/usr/bin/neutron-api'
} elsif($::osfamily == 'Debian') {
$nobody_user_group = 'nogroup'
$package_name = 'neutron-common'
@ -102,6 +104,8 @@ class neutron::params {
$fwaas_package = 'python-neutron-fwaas'
$l2gw_agent_package = 'neutron-l2gateway-agent'
$l2gw_package = 'python-networking-l2gw'
$neutron_wsgi_script_path = '/usr/lib/cgi-bin/neutron'
$neutron_wsgi_script_source = '/usr/bin/neutron-api'
} else {
fail("Unsupported osfamily ${::osfamily}")
}

View File

@ -17,8 +17,13 @@
# Defaults to true
#
# [*service_name*]
# (optional) The name of the neutron-server service
# Defaults to $::neutron::params::server_service
# (optional) Name of the service that will be providing the
# server functionality of neutron-api.
# If the value is 'httpd', this means neutron-api will be a web
# service, and you must use another class to configure that
# web service. For example, use class { 'neutron::wsgi::apache'...}
# to make neutron-api be a web app using apache mod_wsgi.
# Defaults to '$::neutron::params::server_service'
#
# [*log_file*]
# REMOVED: Use log_file of neutron class instead.
@ -409,13 +414,37 @@ class neutron::server (
$service_ensure = 'stopped'
}
}
service { 'neutron-server':
ensure => $service_ensure,
name => $service_name,
enable => $enabled,
hasstatus => true,
hasrestart => true,
tag => ['neutron-service', 'neutron-db-sync-service'],
if $service_name == $::neutron::params::server_service {
service { 'neutron-server':
ensure => $service_ensure,
name => $::neutron::params::server_service,
enable => $enabled,
hasstatus => true,
hasrestart => true,
tag => ['neutron-service', 'neutron-db-sync-service', 'neutron-server-eventlet'],
}
} elsif $service_name == 'httpd' {
include ::apache::params
service { 'neutron-server':
ensure => 'stopped',
name => $::neutron::params::server_service,
enable => false,
hasstatus => true,
hasrestart => true,
tag => ['neutron-service', 'neutron-db-sync-service'],
}
Service <<| title == 'httpd' |>> { tag +> 'neutron-service' }
# we need to make sure neutron-server is stopped before trying to start apache
Service[$::neutron::params::server_service] -> Service[$service_name]
} else {
# backward compatibility so operators can customize the service name.
service { 'neutron-server':
ensure => $service_ensure,
name => $service_name,
enable => $enabled,
hasstatus => true,
hasrestart => true,
tag => ['neutron-service', 'neutron-db-sync-service'],
}
}
}

View File

@ -111,9 +111,6 @@ class neutron::server::notifications (
include ::neutron::deps
# Depend on the specified keystone_user resource, if it exists.
Keystone_user <| title == 'nova' |> -> Class[neutron::server::notifications]
if is_service_default($tenant_id) and (! $tenant_name) {
fail('You must provide either tenant_name or tenant_id.')
}

153
manifests/wsgi/apache.pp Normal file
View File

@ -0,0 +1,153 @@
#
# Copyright (C) 2017 Red Hat Inc.
#
# 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.
#
# Class to serve neutron API with apache mod_wsgi in place of neutron-api service.
#
# Serving neutron API from apache is the recommended way to go for production
# because of limited performance for concurrent accesses when running eventlet.
#
# When using this class you should disable your neutron-api service.
#
# == Parameters
#
# [*servername*]
# The servername for the virtualhost.
# Optional. Defaults to $::fqdn
#
# [*port*]
# The port.
# Optional. Defaults to 9696
#
# [*bind_host*]
# The host/ip address Apache will listen on.
# Optional. Defaults to undef (listen on all ip addresses).
#
# [*path*]
# The prefix for the endpoint.
# Optional. Defaults to '/'
#
# [*ssl*]
# Use ssl ? (boolean)
# Optional. Defaults to true
#
# [*workers*]
# Number of WSGI workers to spawn.
# Optional. Defaults to 1
#
# [*priority*]
# (optional) The priority for the vhost.
# Defaults to '10'
#
# [*threads*]
# (optional) The number of threads for the vhost.
# Defaults to $::os_workers
#
# [*wsgi_process_display_name*]
# (optional) Name of the WSGI process display-name.
# Defaults to undef
#
# [*ssl_cert*]
# [*ssl_key*]
# [*ssl_chain*]
# [*ssl_ca*]
# [*ssl_crl_path*]
# [*ssl_crl*]
# [*ssl_certs_dir*]
# apache::vhost ssl parameters.
# Optional. Default to apache::vhost 'ssl_*' defaults.
#
# == Dependencies
#
# requires Class['apache'] & Class['neutron']
#
# == Examples
#
# include apache
#
# class { 'neutron::wsgi::apache': }
#
class neutron::wsgi::apache (
$servername = $::fqdn,
$port = 9696,
$bind_host = undef,
$path = '/',
$ssl = true,
$workers = $::os_workers,
$ssl_cert = undef,
$ssl_key = undef,
$ssl_chain = undef,
$ssl_ca = undef,
$ssl_crl_path = undef,
$ssl_crl = undef,
$ssl_certs_dir = undef,
$wsgi_process_display_name = undef,
$threads = 1,
$priority = '10',
) {
include ::neutron::deps
include ::neutron::params
include ::apache
include ::apache::mod::wsgi
if $ssl {
include ::apache::mod::ssl
}
# The httpd package is untagged, but needs to have ordering enforced,
# so handle it here rather than in the deps class.
Anchor['neutron::install::begin']
-> Package['httpd']
-> Anchor['neutron::install::end']
# Configure apache during the config phase
Anchor['neutron::config::begin']
-> Apache::Vhost<||>
~> Anchor['neutron::config::end']
# Start the service during the service phase
Anchor['neutron::service::begin']
-> Service['httpd']
-> Anchor['neutron::service::end']
# Notify the service when config changes
Anchor['neutron::config::end']
~> Service['httpd']
::openstacklib::wsgi::apache { 'neutron_wsgi':
bind_host => $bind_host,
bind_port => $port,
group => 'neutron',
path => $path,
priority => $priority,
servername => $servername,
ssl => $ssl,
ssl_ca => $ssl_ca,
ssl_cert => $ssl_cert,
ssl_certs_dir => $ssl_certs_dir,
ssl_chain => $ssl_chain,
ssl_crl => $ssl_crl,
ssl_crl_path => $ssl_crl_path,
ssl_key => $ssl_key,
threads => $threads,
user => 'neutron',
workers => $workers,
wsgi_daemon_process => 'neutron',
wsgi_process_display_name => $wsgi_process_display_name,
wsgi_process_group => 'neutron',
wsgi_script_dir => $::neutron::params::neutron_wsgi_script_path,
wsgi_script_file => 'app',
wsgi_script_source => $::neutron::params::neutron_wsgi_script_source,
}
}

View File

@ -0,0 +1,6 @@
---
features:
- |
Neutron API can now be deployed in WSGI with Apache, like we support it
for other modules. Switch neutron::server::service_name to 'httpd' and use
neutron::wsgi::apache class to deploy it.

View File

@ -61,7 +61,6 @@ describe 'neutron::keystone::auth' do
}
end
it { is_expected.to contain_keystone_endpoint('RegionOne/neutron::network').with_notify(['Service[neutron-server]']) }
end
describe 'with endpoint URL parameters' do

View File

@ -66,7 +66,7 @@ describe 'neutron::server' do
:name => platform_params[:server_service],
:enable => true,
:ensure => 'running',
:tag => ['neutron-service', 'neutron-db-sync-service'],
:tag => ['neutron-service', 'neutron-db-sync-service', 'neutron-server-eventlet'],
)
is_expected.to contain_service('neutron-server').that_subscribes_to('Anchor[neutron::service::begin]')
is_expected.to contain_service('neutron-server').that_notifies('Anchor[neutron::service::end]')
@ -140,15 +140,6 @@ describe 'neutron::server' do
end
end
context 'with custom service name' do
before :each do
params.merge!(:service_name => 'custom-service-name')
end
it 'should configure proper service name' do
is_expected.to contain_service('neutron-server').with_name('custom-service-name')
end
end
context 'with state_path and lock_path parameters' do
before :each do
params.merge!(:state_path => 'state_path',
@ -234,6 +225,44 @@ describe 'neutron::server' do
it { is_expected.to contain_neutron_config('oslo_middleware/enable_proxy_headers_parsing').with_value(true) }
end
context 'when running neutron-api in wsgi' do
before :each do
params.merge!({ :service_name => 'httpd' })
end
let :pre_condition do
"class { 'neutron': rabbit_password => 'passw0rd' }
include ::apache
class { '::neutron::keystone::authtoken':
password => 'passw0rd',
}"
end
it 'configures neutron-api service with Apache' do
is_expected.to contain_service('neutron-server').with(
:ensure => 'stopped',
:name => platform_params[:server_service],
:enable => false,
:tag => ['neutron-service', 'neutron-db-sync-service'],
)
end
end
context 'when service_name is customized' do
before :each do
params.merge!({ :service_name => 'foobar' })
end
it 'configures neutron-api service with custom name' do
is_expected.to contain_service('neutron-server').with(
:name => 'foobar',
:enable => true,
:ensure => 'running',
:tag => ['neutron-service', 'neutron-db-sync-service'],
)
end
end
end
shared_examples_for 'VPNaaS, FWaaS and LBaaS package installation' do

View File

@ -0,0 +1,126 @@
require 'spec_helper'
describe 'neutron::wsgi::apache' do
shared_examples_for 'apache serving neutron with mod_wsgi' do
it { is_expected.to contain_service('httpd').with_name(platform_parameters[:httpd_service_name]) }
it { is_expected.to contain_class('neutron::deps') }
it { is_expected.to contain_class('neutron::params') }
it { is_expected.to contain_class('apache') }
it { is_expected.to contain_class('apache::mod::wsgi') }
describe 'with default parameters' do
it { is_expected.to contain_file("#{platform_parameters[:wsgi_script_path]}").with(
'ensure' => 'directory',
'owner' => 'neutron',
'group' => 'neutron',
'require' => 'Package[httpd]'
)}
it { is_expected.to contain_file('neutron_wsgi').with(
'ensure' => 'file',
'path' => "#{platform_parameters[:wsgi_script_path]}/app",
'source' => platform_parameters[:wsgi_script_source],
'owner' => 'neutron',
'group' => 'neutron',
'mode' => '0644'
)}
it { is_expected.to contain_file('neutron_wsgi').that_requires("File[#{platform_parameters[:wsgi_script_path]}]") }
it { is_expected.to contain_apache__vhost('neutron_wsgi').with(
'servername' => 'some.host.tld',
'ip' => nil,
'port' => '9696',
'docroot' => "#{platform_parameters[:wsgi_script_path]}",
'docroot_owner' => 'neutron',
'docroot_group' => 'neutron',
'ssl' => 'true',
'wsgi_daemon_process' => 'neutron',
'wsgi_daemon_process_options' => {
'user' => 'neutron',
'group' => 'neutron',
'processes' => '8',
'threads' => '1',
'display-name' => 'neutron_wsgi',
},
'wsgi_process_group' => 'neutron',
'wsgi_script_aliases' => { '/' => "#{platform_parameters[:wsgi_script_path]}/app" },
'require' => 'File[neutron_wsgi]'
)}
it { is_expected.to contain_concat("#{platform_parameters[:httpd_ports_file]}") }
end
describe 'when overriding parameters using different ports' do
let :params do
{
:servername => 'dummy.host',
:bind_host => '10.42.51.1',
:port => 12345,
:ssl => false,
:wsgi_process_display_name => 'neutron',
:workers => 37,
}
end
it { is_expected.to contain_apache__vhost('neutron_wsgi').with(
'servername' => 'dummy.host',
'ip' => '10.42.51.1',
'port' => '12345',
'docroot' => "#{platform_parameters[:wsgi_script_path]}",
'docroot_owner' => 'neutron',
'docroot_group' => 'neutron',
'ssl' => 'false',
'wsgi_daemon_process' => 'neutron',
'wsgi_daemon_process_options' => {
'user' => 'neutron',
'group' => 'neutron',
'processes' => '37',
'threads' => '1',
'display-name' => 'neutron',
},
'wsgi_process_group' => 'neutron',
'wsgi_script_aliases' => { '/' => "#{platform_parameters[:wsgi_script_path]}/app" },
'require' => 'File[neutron_wsgi]'
)}
it { is_expected.to contain_concat("#{platform_parameters[:httpd_ports_file]}") }
end
end
on_supported_os({
:supported_os => OSDefaults.get_supported_os
}).each do |os,facts|
context "on #{os}" do
let (:facts) do
facts.merge!(OSDefaults.get_facts({
:os_workers => 8,
:concat_basedir => '/var/lib/puppet/concat',
:fqdn => 'some.host.tld'
}))
end
let(:platform_parameters) do
case facts[:osfamily]
when 'Debian'
{
:httpd_service_name => 'apache2',
:httpd_ports_file => '/etc/apache2/ports.conf',
:wsgi_script_path => '/usr/lib/cgi-bin/neutron',
:wsgi_script_source => '/usr/bin/neutron-api'
}
when 'RedHat'
{
:httpd_service_name => 'httpd',
:httpd_ports_file => '/etc/httpd/conf/ports.conf',
:wsgi_script_path => '/var/www/cgi-bin/neutron',
:wsgi_script_source => '/usr/bin/neutron-api'
}
end
end
it_configures 'apache serving neutron with mod_wsgi'
end
end
end