diff --git a/manifests/api.pp b/manifests/api.pp index 776d8015..cd9263d5 100644 --- a/manifests/api.pp +++ b/manifests/api.pp @@ -141,6 +141,15 @@ # returns in a single response (integer value) # Defaults to $::os_service_default # +# [*service_name*] +# (optional) Name of the service that will be providing the +# server functionality of cinder-api. +# If the value is 'httpd', this means cinder-api will be a web +# service, and you must use another class to configure that +# web service. For example, use class { 'cinder::wsgi::apache'...} +# to make cinder-api be a web app using apache mod_wsgi. +# Defaults to '$::cinder::params::api_service' +# class cinder::api ( $keystone_password, $keystone_enabled = true, @@ -171,20 +180,21 @@ class cinder::api ( $public_endpoint = $::os_service_default, $osapi_volume_base_url = $::os_service_default, $osapi_max_limit = $::os_service_default, + $service_name = $::cinder::params::api_service, # DEPRECATED PARAMETERS $validation_options = {}, -) { +) inherits cinder::params { include ::cinder::params include ::cinder::policy - Cinder_config<||> ~> Service['cinder-api'] - Cinder_api_paste_ini<||> ~> Service['cinder-api'] - Class['cinder::policy'] ~> Service['cinder-api'] + Cinder_config<||> ~> Service[$service_name] + Cinder_api_paste_ini<||> ~> Service[$service_name] + Class['cinder::policy'] ~> Service[$service_name] if $::cinder::params::api_package { Package['cinder-api'] -> Class['cinder::policy'] - Package['cinder-api'] -> Service['cinder-api'] + Package['cinder-api'] -> Service[$service_name] Package['cinder-api'] ~> Exec<| title == 'cinder-manage db_sync' |> package { 'cinder-api': ensure => $package_ensure, @@ -207,13 +217,29 @@ class cinder::api ( } } - service { 'cinder-api': - ensure => $ensure, - name => $::cinder::params::api_service, - enable => $enabled, - hasstatus => true, - require => Package['cinder'], - tag => 'cinder-service', + if $service_name == $::cinder::params::api_service { + service { 'cinder-api': + ensure => $ensure, + name => $::cinder::params::api_service, + enable => $enabled, + hasstatus => true, + require => Package['cinder'], + tag => 'cinder-service', + } + + } elsif $service_name == 'httpd' { + include ::apache::params + service { 'cinder-api': + ensure => 'stopped', + name => $::cinder::params::api_service, + enable => false, + tag => ['cinder-service'], + } + + # we need to make sure cinder-api/eventlet is stopped before trying to start apache + Service['cinder-api'] -> Service[$service_name] + } else { + fail('Invalid service_name. Either cinder-api/openstack-cinder-api for running as a standalone service, or httpd for being run by a httpd server') } cinder_config { diff --git a/manifests/keystone/auth.pp b/manifests/keystone/auth.pp index e321abd0..62bc7c6a 100644 --- a/manifests/keystone/auth.pp +++ b/manifests/keystone/auth.pp @@ -267,8 +267,7 @@ class cinder::keystone::auth ( } if $configure_user_role { - Keystone_user_role["${auth_name}@${tenant}"] ~> Service <| name == 'cinder-api' |> - Keystone_user_role["${auth_name}@${tenant}"] -> Cinder_type <| |> + Keystone_user_role["${auth_name}@${tenant}"] -> Cinder_type<||> } } diff --git a/manifests/params.pp b/manifests/params.pp index 1965df4d..d97697d8 100644 --- a/manifests/params.pp +++ b/manifests/params.pp @@ -3,42 +3,46 @@ class cinder::params { if $::osfamily == 'Debian' { - $package_name = 'cinder-common' - $client_package = 'python-cinderclient' - $api_package = 'cinder-api' - $api_service = 'cinder-api' - $backup_package = 'cinder-backup' - $backup_service = 'cinder-backup' - $scheduler_package = 'cinder-scheduler' - $scheduler_service = 'cinder-scheduler' - $volume_package = 'cinder-volume' - $volume_service = 'cinder-volume' - $db_sync_command = 'cinder-manage db sync' - $tgt_package_name = 'tgt' - $tgt_service_name = 'tgt' - $ceph_init_override = '/etc/init/cinder-volume.override' - $iscsi_helper = 'tgtadm' - $lio_package_name = 'targetcli' - $lock_path = '/var/lock/cinder' + $package_name = 'cinder-common' + $client_package = 'python-cinderclient' + $api_package = 'cinder-api' + $api_service = 'cinder-api' + $backup_package = 'cinder-backup' + $backup_service = 'cinder-backup' + $scheduler_package = 'cinder-scheduler' + $scheduler_service = 'cinder-scheduler' + $volume_package = 'cinder-volume' + $volume_service = 'cinder-volume' + $db_sync_command = 'cinder-manage db sync' + $tgt_package_name = 'tgt' + $tgt_service_name = 'tgt' + $ceph_init_override = '/etc/init/cinder-volume.override' + $iscsi_helper = 'tgtadm' + $lio_package_name = 'targetcli' + $lock_path = '/var/lock/cinder' + $cinder_wsgi_script_path = '/usr/lib/cgi-bin/cinder' + $cinder_wsgi_script_source = '/usr/bin/cinder-wsgi' } elsif($::osfamily == 'RedHat') { - $package_name = 'openstack-cinder' - $client_package = 'python-cinderclient' - $api_package = false - $api_service = 'openstack-cinder-api' - $backup_package = false - $backup_service = 'openstack-cinder-backup' - $scheduler_package = false - $scheduler_service = 'openstack-cinder-scheduler' - $volume_package = false - $volume_service = 'openstack-cinder-volume' - $db_sync_command = 'cinder-manage db sync' - $tgt_package_name = 'scsi-target-utils' - $tgt_service_name = 'tgtd' - $ceph_init_override = '/etc/sysconfig/openstack-cinder-volume' - $lio_package_name = 'targetcli' - $lock_path = '/var/lib/cinder/tmp' + $package_name = 'openstack-cinder' + $client_package = 'python-cinderclient' + $api_package = false + $api_service = 'openstack-cinder-api' + $backup_package = false + $backup_service = 'openstack-cinder-backup' + $scheduler_package = false + $scheduler_service = 'openstack-cinder-scheduler' + $volume_package = false + $volume_service = 'openstack-cinder-volume' + $db_sync_command = 'cinder-manage db sync' + $tgt_package_name = 'scsi-target-utils' + $tgt_service_name = 'tgtd' + $ceph_init_override = '/etc/sysconfig/openstack-cinder-volume' + $lio_package_name = 'targetcli' + $lock_path = '/var/lib/cinder/tmp' + $cinder_wsgi_script_path = '/var/www/cgi-bin/cinder' + $cinder_wsgi_script_source = '/usr/bin/cinder-wsgi' case $::operatingsystem { 'RedHat', 'CentOS', 'Scientific', 'OracleLinux': { diff --git a/manifests/wsgi/apache.pp b/manifests/wsgi/apache.pp new file mode 100644 index 00000000..c38a9173 --- /dev/null +++ b/manifests/wsgi/apache.pp @@ -0,0 +1,124 @@ +# +# 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 Cinder API with apache mod_wsgi in place of cinder-api service. +# +# Serving Cinder 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 cinder-api service. +# +# == Parameters +# +# [*servername*] +# The servername for the virtualhost. +# Optional. Defaults to $::fqdn +# +# [*port*] +# The port. +# Optional. Defaults to 8776 +# +# [*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 $::processorcount +# +# [*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['cinder'] +# +# == Examples +# +# include apache +# +# class { 'cinder::wsgi::apache': } +# +class cinder::wsgi::apache ( + $servername = $::fqdn, + $port = 8776, + $bind_host = undef, + $path = '/', + $ssl = true, + $workers = 1, + $ssl_cert = undef, + $ssl_key = undef, + $ssl_chain = undef, + $ssl_ca = undef, + $ssl_crl_path = undef, + $ssl_crl = undef, + $ssl_certs_dir = undef, + $threads = $::processorcount, + $priority = '10', +) { + + include ::cinder::params + include ::apache + include ::apache::mod::wsgi + if $ssl { + include ::apache::mod::ssl + } + + ::openstacklib::wsgi::apache { 'cinder_wsgi': + bind_host => $bind_host, + bind_port => $port, + group => 'cinder', + 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 => 'cinder', + workers => $workers, + wsgi_daemon_process => 'cinder-api', + wsgi_process_group => 'cinder-api', + wsgi_script_dir => $::cinder::params::cinder_wsgi_script_path, + wsgi_script_file => 'cinder-api', + wsgi_script_source => $::cinder::params::cinder_wsgi_script_source, + } +} diff --git a/metadata.json b/metadata.json index c4d74fe1..e221d119 100644 --- a/metadata.json +++ b/metadata.json @@ -37,6 +37,7 @@ { "name": "puppetlabs/rabbitmq", "version_requirement": ">=2.0.2 <6.0.0" }, { "name": "puppetlabs/stdlib", "version_requirement": ">=4.0.0 <5.0.0" }, { "name": "openstack/openstacklib", "version_requirement": ">=8.0.0 <9.0.0" }, - { "name": "openstack/oslo", "version_requirement": "<9.0.0" } + { "name": "openstack/oslo", "version_requirement": "<9.0.0" }, + { "name": "puppetlabs/apache", "version_requirement": ">=1.0.0 <2.0.0" } ] } diff --git a/releasenotes/notes/cinder_api_wsgi-e51e79ab412fc764.yaml b/releasenotes/notes/cinder_api_wsgi-e51e79ab412fc764.yaml new file mode 100644 index 00000000..b2f86f2f --- /dev/null +++ b/releasenotes/notes/cinder_api_wsgi-e51e79ab412fc764.yaml @@ -0,0 +1,3 @@ +--- +features: + - Add WSGI support for Cinder API with Apache. diff --git a/spec/acceptance/basic_cinder_spec.rb b/spec/acceptance/basic_cinder_spec.rb index eeaf6ebd..3e2cb184 100644 --- a/spec/acceptance/basic_cinder_spec.rb +++ b/spec/acceptance/basic_cinder_spec.rb @@ -46,6 +46,11 @@ describe 'basic cinder' do keystone_password => 'a_big_secret', identity_uri => 'http://127.0.0.1:35357/', default_volume_type => 'iscsi_backend', + service_name => 'httpd', + } + include ::apache + class { '::cinder::wsgi::apache': + ssl => false, } class { '::cinder::backup': } class { '::cinder::ceilometer': } @@ -65,7 +70,7 @@ describe 'basic cinder' do end describe port(8776) do - it { is_expected.to be_listening.with('tcp') } + it { is_expected.to be_listening } end describe cron do diff --git a/spec/classes/cinder_api_spec.rb b/spec/classes/cinder_api_spec.rb index c0d13fe2..13f6c4a0 100644 --- a/spec/classes/cinder_api_spec.rb +++ b/spec/classes/cinder_api_spec.rb @@ -303,6 +303,37 @@ describe 'cinder::api' do ) end end + describe 'when running cinder-api in wsgi' do + let :params do + req_params.merge!({ :service_name => 'httpd' }) + end + + let :pre_condition do + "include ::apache + class { 'cinder': rabbit_password => 'secret' }" + end + + it 'configures cinder-api service with Apache' do + is_expected.to contain_service('cinder-api').with( + :ensure => 'stopped', + :enable => false, + :tag => ['cinder-service'], + ) + end + end + + describe 'when service_name is not valid' do + let :params do + req_params.merge!({ :service_name => 'foobar' }) + end + + let :pre_condition do + "include ::apache + class { 'cinder': rabbit_password => 'secret' }" + end + + it_raises 'a Puppet::Error', /Invalid service_name/ + end end on_supported_os({ @@ -310,7 +341,11 @@ describe 'cinder::api' do }).each do |os,facts| context "on #{os}" do let (:facts) do - facts.merge(OSDefaults.get_facts({:processorcount => 8})) + facts.merge(OSDefaults.get_facts({ + :processorcount => 8, + :fqdn => 'some.host.tld', + :concat_basedir => '/var/lib/puppet/concat', + })) end it_configures 'cinder api' diff --git a/spec/classes/cinder_wsgi_apache_spec.rb b/spec/classes/cinder_wsgi_apache_spec.rb new file mode 100644 index 00000000..5f144d1d --- /dev/null +++ b/spec/classes/cinder_wsgi_apache_spec.rb @@ -0,0 +1,110 @@ +require 'spec_helper' + +describe 'cinder::wsgi::apache' do + + shared_examples_for 'apache serving cinder with mod_wsgi' do + it { is_expected.to contain_service('httpd').with_name(platform_params[:httpd_service_name]) } + it { is_expected.to contain_class('cinder::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_params[:wsgi_script_path]}").with( + 'ensure' => 'directory', + 'owner' => 'cinder', + 'group' => 'cinder', + 'require' => 'Package[httpd]' + )} + + + it { is_expected.to contain_file('cinder_wsgi').with( + 'ensure' => 'file', + 'path' => "#{platform_params[:wsgi_script_path]}/cinder-api", + 'source' => platform_params[:wsgi_script_source], + 'owner' => 'cinder', + 'group' => 'cinder', + 'mode' => '0644' + )} + it { is_expected.to contain_file('cinder_wsgi').that_requires("File[#{platform_params[:wsgi_script_path]}]") } + + it { is_expected.to contain_apache__vhost('cinder_wsgi').with( + 'servername' => 'some.host.tld', + 'ip' => nil, + 'port' => '8776', + 'docroot' => "#{platform_params[:wsgi_script_path]}", + 'docroot_owner' => 'cinder', + 'docroot_group' => 'cinder', + 'ssl' => 'true', + 'wsgi_daemon_process' => 'cinder-api', + 'wsgi_process_group' => 'cinder-api', + 'wsgi_script_aliases' => { '/' => "#{platform_params[:wsgi_script_path]}/cinder-api" }, + 'require' => 'File[cinder_wsgi]' + )} + it { is_expected.to contain_file("#{platform_params[: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, + :workers => 37, + } + end + + it { is_expected.to contain_apache__vhost('cinder_wsgi').with( + 'servername' => 'dummy.host', + 'ip' => '10.42.51.1', + 'port' => '12345', + 'docroot' => "#{platform_params[:wsgi_script_path]}", + 'docroot_owner' => 'cinder', + 'docroot_group' => 'cinder', + 'ssl' => 'false', + 'wsgi_daemon_process' => 'cinder-api', + 'wsgi_process_group' => 'cinder-api', + 'wsgi_script_aliases' => { '/' => "#{platform_params[:wsgi_script_path]}/cinder-api" }, + 'require' => 'File[cinder_wsgi]' + )} + + it { is_expected.to contain_file("#{platform_params[: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({ + :processorcount => 42, + :concat_basedir => '/var/lib/puppet/concat', + :fqdn => 'some.host.tld', + })) + end + + let(:platform_params) do + case facts[:osfamily] + when 'Debian' + { + :httpd_service_name => 'apache2', + :httpd_ports_file => '/etc/apache2/ports.conf', + :wsgi_script_path => '/usr/lib/cgi-bin/cinder', + :wsgi_script_source => '/usr/bin/cinder-wsgi' + } + when 'RedHat' + { + :httpd_service_name => 'httpd', + :httpd_ports_file => '/etc/httpd/conf/ports.conf', + :wsgi_script_path => '/var/www/cgi-bin/cinder', + :wsgi_script_source => '/usr/bin/cinder-wsgi' + } + end + end + + it_behaves_like 'apache serving cinder with mod_wsgi' + end + end +end