diff --git a/manifests/api.pp b/manifests/api.pp index 0ec46aa..a977cd7 100644 --- a/manifests/api.pp +++ b/manifests/api.pp @@ -31,6 +31,15 @@ # (optional) Run cloudkitty-dbsync command on api nodes after installing the package. # Defaults to true. # +# [*service_name*] +# (optional) Name of the service that will be providing the +# server functionality of cloudkitty-api. +# If the value is 'httpd', this means cloudkitty-api will be a web +# service, and you must use another class to configure that +# web service. For example, use class { 'cloudkitty::wsgi::apache'...} +# to make cloudkitty-api be a web app using apache mod_wsgi. +# Defaults to 'httpd' +# class cloudkitty::api ( $package_ensure = 'present', $manage_service = true, @@ -39,6 +48,7 @@ class cloudkitty::api ( $port = $::os_service_default, $pecan_debug = $::os_service_default, $sync_db = true, + $service_name = 'httpd', ) { include ::cloudkitty @@ -64,13 +74,16 @@ class cloudkitty::api ( include ::cloudkitty::db::sync } - service { 'cloudkitty-api': - ensure => $service_ensure, - name => $::cloudkitty::params::api_service_name, - enable => $enabled, - hasstatus => true, - hasrestart => true, - tag => 'cloudkitty-service', + if $service_name == 'httpd' { + include ::apache::params + service { 'cloudkitty-api': + ensure => 'stopped', + name => $::cloudkitty::params::api_service_name, + enable => false, + tag => 'cloudkitty-service', + } + } else { + fail('Invalid service_name. Only httpd for being run by a httpd server') } cloudkitty_config { @@ -78,4 +91,5 @@ class cloudkitty::api ( 'api/port': value => $port; 'api/pecan_debug': value => $pecan_debug; } + } diff --git a/manifests/init.pp b/manifests/init.pp index 8e62d14..3a29de8 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -8,10 +8,6 @@ # (Optional) Ensure state for package. # Defaults to 'present' # -# [*rpc_backend*] -# (Optional) Use these options to configure the message system. -# Defaults to $::os_service_default. -# # [*rpc_response_timeout*] # (Optional) Configure the timeout (in seconds) for rpc responses # Defaults to $::os_service_default. @@ -242,9 +238,14 @@ # (Optional) Keystone version to use. # Defaults to '3' # +# DEPRECATED PARAMETERS +# +# [*rpc_backend*] +# (Optional) Use these options to configure the message system. +# Defaults to $::os_service_default. +# class cloudkitty( $package_ensure = 'present', - $rpc_backend = $::os_service_default, $rabbit_use_ssl = $::os_service_default, $rabbit_heartbeat_timeout_threshold = $::os_service_default, $rabbit_heartbeat_rate = $::os_service_default, @@ -292,6 +293,8 @@ class cloudkitty( $tenant_fetcher_backend = $::os_service_default, $auth_section = 'keystone_authtoken', $keystone_version = '3', + # DEPRECATED PARAMETERS + $rpc_backend = $::os_service_default, ) { include ::cloudkitty::params @@ -310,41 +313,42 @@ class cloudkitty( purge => $purge_config, } - if $rpc_backend == 'rabbit' or is_service_default($rpc_backend) { - oslo::messaging::rabbit { 'cloudkitty_config': - rabbit_ha_queues => $rabbit_ha_queues, - rabbit_use_ssl => $rabbit_use_ssl, - amqp_durable_queues => $amqp_durable_queues, - heartbeat_timeout_threshold => $rabbit_heartbeat_timeout_threshold, - heartbeat_rate => $rabbit_heartbeat_rate, - kombu_ssl_version => $kombu_ssl_version, - kombu_ssl_keyfile => $kombu_ssl_keyfile, - kombu_ssl_certfile => $kombu_ssl_certfile, - kombu_ssl_ca_certs => $kombu_ssl_ca_certs, - kombu_reconnect_delay => $kombu_reconnect_delay, - kombu_failover_strategy => $kombu_failover_strategy, - kombu_compression => $kombu_compression, - } + if $rpc_backend { + warning('The rpc_backend parameter has been deprecated, please use default_transport_url instead.') } - elsif $rpc_backend == 'amqp' { - oslo::messaging::amqp { 'cloudkitty_config': - server_request_prefix => $amqp_server_request_prefix, - broadcast_prefix => $amqp_broadcast_prefix, - group_request_prefix => $amqp_group_request_prefix, - container_name => $amqp_container_name, - idle_timeout => $amqp_idle_timeout, - trace => $amqp_trace, - ssl_ca_file => $amqp_ssl_ca_file, - ssl_cert_file => $amqp_ssl_cert_file, - ssl_key_file => $amqp_ssl_key_file, - ssl_key_password => $amqp_ssl_key_password, - allow_insecure_clients => $amqp_allow_insecure_clients, - sasl_mechanisms => $amqp_sasl_mechanisms, - sasl_config_dir => $amqp_sasl_config_dir, - sasl_config_name => $amqp_sasl_config_name, - username => $amqp_username, - password => $amqp_password, - } + + oslo::messaging::rabbit { 'cloudkitty_config': + rabbit_ha_queues => $rabbit_ha_queues, + rabbit_use_ssl => $rabbit_use_ssl, + amqp_durable_queues => $amqp_durable_queues, + heartbeat_timeout_threshold => $rabbit_heartbeat_timeout_threshold, + heartbeat_rate => $rabbit_heartbeat_rate, + kombu_ssl_version => $kombu_ssl_version, + kombu_ssl_keyfile => $kombu_ssl_keyfile, + kombu_ssl_certfile => $kombu_ssl_certfile, + kombu_ssl_ca_certs => $kombu_ssl_ca_certs, + kombu_reconnect_delay => $kombu_reconnect_delay, + kombu_failover_strategy => $kombu_failover_strategy, + kombu_compression => $kombu_compression, + } + + oslo::messaging::amqp { 'cloudkitty_config': + server_request_prefix => $amqp_server_request_prefix, + broadcast_prefix => $amqp_broadcast_prefix, + group_request_prefix => $amqp_group_request_prefix, + container_name => $amqp_container_name, + idle_timeout => $amqp_idle_timeout, + trace => $amqp_trace, + ssl_ca_file => $amqp_ssl_ca_file, + ssl_cert_file => $amqp_ssl_cert_file, + ssl_key_file => $amqp_ssl_key_file, + ssl_key_password => $amqp_ssl_key_password, + allow_insecure_clients => $amqp_allow_insecure_clients, + sasl_mechanisms => $amqp_sasl_mechanisms, + sasl_config_dir => $amqp_sasl_config_dir, + sasl_config_name => $amqp_sasl_config_name, + username => $amqp_username, + password => $amqp_password, } oslo::messaging::default { 'cloudkitty_config': diff --git a/manifests/params.pp b/manifests/params.pp index 11cea65..83649c7 100644 --- a/manifests/params.pp +++ b/manifests/params.pp @@ -12,17 +12,21 @@ class cloudkitty::params { case $::osfamily { 'RedHat': { # package names - $api_package_name = 'openstack-cloudkitty-api' - $processor_package_name = 'openstack-cloudkitty-processor' - $ui_package_name = 'openstack-cloudkitty-ui' - $common_package_name = 'openstack-cloudkitty-common' + $api_package_name = 'openstack-cloudkitty-api' + $processor_package_name = 'openstack-cloudkitty-processor' + $ui_package_name = 'openstack-cloudkitty-ui' + $common_package_name = 'openstack-cloudkitty-common' + $cloudkitty_wsgi_script_source = '/usr/lib/python2.7/site-packages/cloudkitty/api/app.wsgi' + $cloudkitty_wsgi_script_path = '/var/www/cgi-bin/cloudkitty' } 'Debian': { # package names - $api_package_name = 'cloudkitty-api' - $processor_package_name = 'cloudkitty-processor' - $ui_package_name = 'cloudkitty-dashboard' - $common_package_name = 'cloudkitty-common' + $api_package_name = 'cloudkitty-api' + $processor_package_name = 'cloudkitty-processor' + $ui_package_name = 'cloudkitty-dashboard' + $common_package_name = 'cloudkitty-common' + $cloudkitty_wsgi_script_source = '/usr/lib/python2.7/dist-packages/cloudkitty/api/app.wsgi' + $cloudkitty_wsgi_script_path = '/usr/lib/cgi-bin/cloudkitty' } default: { fail("Unsupported osfamily: ${::osfamily} operatingsystem") diff --git a/manifests/wsgi/apache.pp b/manifests/wsgi/apache.pp new file mode 100644 index 0000000..997c1da --- /dev/null +++ b/manifests/wsgi/apache.pp @@ -0,0 +1,132 @@ +# +# 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 cloudkitty 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 cloudkitty service. +# +# == Parameters +# +# [*servername*] +# The servername for the virtualhost. +# Optional. Defaults to $::fqdn +# +# [*port*] +# The port. +# Optional. Defaults to 8889 +# +# [*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['cloudkitty'] +# +# == Examples +# +# include apache +# +# class { 'cloudkitty::wsgi::apache': } +# +class cloudkitty::wsgi::apache ( + $servername = $::fqdn, + $port = 8889, + $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, + $wsgi_process_display_name = undef, + $threads = $::os_workers, + $priority = '10', +) { + + include ::cloudkitty::deps + include ::cloudkitty::params + include ::apache + include ::apache::mod::wsgi + if $ssl { + include ::apache::mod::ssl + } + + ::openstacklib::wsgi::apache { 'cloudkitty_wsgi': + bind_host => $bind_host, + bind_port => $port, + group => 'cloudkitty', + 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 => 'cloudkitty', + workers => $workers, + wsgi_daemon_process => 'cloudkitty', + wsgi_process_display_name => $wsgi_process_display_name, + wsgi_process_group => 'cloudkitty', + wsgi_script_dir => $::cloudkitty::params::cloudkitty_wsgi_script_path, + wsgi_script_file => 'app', + wsgi_script_source => $::cloudkitty::params::cloudkitty_wsgi_script_source, + require => Anchor['cloudkitty::install::end'], + } +} diff --git a/spec/acceptance/basic_cloudkitty_spec.rb b/spec/acceptance/cloudkitty_wsgi_apache_spec.rb similarity index 91% rename from spec/acceptance/basic_cloudkitty_spec.rb rename to spec/acceptance/cloudkitty_wsgi_apache_spec.rb index 316eca8..e6cb0ff 100644 --- a/spec/acceptance/basic_cloudkitty_spec.rb +++ b/spec/acceptance/cloudkitty_wsgi_apache_spec.rb @@ -51,7 +51,13 @@ describe 'basic cloudkitty' do class { '::cloudkitty::db::mysql': password => 'a_big_secret', } - class { '::cloudkitty::api': } + class { '::cloudkitty::api': + service_name => 'httpd', + } + include ::apache + class { '::cloudkitty::wsgi::apache': + ssl => false, + } class { '::cloudkitty::processor': } class { '::cloudkitty::client': } } diff --git a/spec/classes/cloudkitty_api_spec.rb b/spec/classes/cloudkitty_api_spec.rb index 30de461..fbad957 100644 --- a/spec/classes/cloudkitty_api_spec.rb +++ b/spec/classes/cloudkitty_api_spec.rb @@ -38,47 +38,45 @@ describe 'cloudkitty::api' do ) end - [{:enabled => true}, {:enabled => false}].each do |param_hash| - context "when service should be #{param_hash[:enabled] ? 'enabled' : 'disabled'}" do - before do - params.merge!(param_hash) - end + context 'when running cloudkitty-api in wsgi' do + before do + params.merge!({ :service_name => 'httpd' }) + end - it 'configures cloudkitty-api service' do + let :pre_condition do + "include ::apache + include ::cloudkitty::db + class { 'cloudkitty': } + class { '::cloudkitty::keystone::authtoken': + password => 'a_big_secret', + }" + end - is_expected.to contain_service('cloudkitty-api').with( - :ensure => (params[:manage_service] && params[:enabled]) ? 'running' : 'stopped', - :name => platform_params[:api_service_name], - :enable => params[:enabled], - :hasstatus => true, - :hasrestart => true, - :tag => 'cloudkitty-service', - ) - is_expected.to contain_service('cloudkitty-api').that_subscribes_to('Anchor[cloudkitty::service::begin]') - is_expected.to contain_service('cloudkitty-api').that_notifies('Anchor[cloudkitty::service::end]') - end + it 'configures cloudkitty-api service with Apache' do + is_expected.to contain_service('cloudkitty-api').with( + :ensure => 'stopped', + :name => platform_params[:api_service_name], + :enable => false, + :tag => 'cloudkitty-service', + ) end end - context 'with disabled service managing' do + context 'when service_name is not valid' do before do - params.merge!({ - :manage_service => false, - :enabled => false }) + params.merge!({ :service_name => 'foobar' }) end - it 'configures cloudkitty-api service' do - - is_expected.to contain_service('cloudkitty-api').with( - :ensure => nil, - :name => platform_params[:api_service_name], - :enable => false, - :hasstatus => true, - :hasrestart => true, - :tag => 'cloudkitty-service', - ) - is_expected.to contain_service('cloudkitty-api').that_subscribes_to(nil) + let :pre_condition do + "include ::apache + include ::cloudkitty::db + class { 'cloudkitty': } + class { '::cloudkitty::keystone::authtoken': + password => 'a_big_secret', + }" end + + it_raises 'a Puppet::Error', /Invalid service_name/ end context 'with $sync_db set to false in ::cloudkitty' do diff --git a/spec/classes/cloudkitty_init_spec.rb b/spec/classes/cloudkitty_init_spec.rb index 6307504..836e99d 100644 --- a/spec/classes/cloudkitty_init_spec.rb +++ b/spec/classes/cloudkitty_init_spec.rb @@ -45,7 +45,6 @@ describe 'cloudkitty' do context 'with overridden parameters' do let :params do { - :rpc_backend => 'rabbit', :rabbit_ha_queues => 'undef', :rabbit_heartbeat_timeout_threshold => '60', :rabbit_heartbeat_rate => '10', @@ -62,7 +61,6 @@ describe 'cloudkitty' do end it 'configures rabbit' do - is_expected.to contain_cloudkitty_config('DEFAULT/rpc_backend').with_value('rabbit') is_expected.to contain_cloudkitty_config('DEFAULT/transport_url').with_value('rabbit://rabbit_user:password@localhost:5673') is_expected.to contain_cloudkitty_config('DEFAULT/rpc_response_timeout').with_value('120') is_expected.to contain_cloudkitty_config('DEFAULT/control_exchange').with_value('cloudkitty') @@ -150,37 +148,30 @@ describe 'cloudkitty' do end end - context 'with amqp rpc_backend' do - let :params do - { :rpc_backend => 'amqp' } - end - - context 'with default parameters' do - it 'configures amqp' do - is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/server_request_prefix').with_value('') - is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/broadcast_prefix').with_value('') - is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/group_request_prefix').with_value('') - is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/container_name').with_value('') - is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/idle_timeout').with_value('') - is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/trace').with_value('') - is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/ssl_ca_file').with_value('') - is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/ssl_cert_file').with_value('') - is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/ssl_key_file').with_value('') - is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/ssl_key_password').with_value('') - is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/allow_insecure_clients').with_value('') - is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/sasl_mechanisms').with_value('') - is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/sasl_config_dir').with_value('') - is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/sasl_config_name').with_value('') - is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/username').with_value('') - is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/password').with_value('') - end + context 'with amqp default parameters' do + it 'configures amqp' do + is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/server_request_prefix').with_value('') + is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/broadcast_prefix').with_value('') + is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/group_request_prefix').with_value('') + is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/container_name').with_value('') + is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/idle_timeout').with_value('') + is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/trace').with_value('') + is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/ssl_ca_file').with_value('') + is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/ssl_cert_file').with_value('') + is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/ssl_key_file').with_value('') + is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/ssl_key_password').with_value('') + is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/allow_insecure_clients').with_value('') + is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/sasl_mechanisms').with_value('') + is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/sasl_config_dir').with_value('') + is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/sasl_config_name').with_value('') + is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/username').with_value('') + is_expected.to contain_cloudkitty_config('oslo_messaging_amqp/password').with_value('') end end context 'with overriden amqp parameters' do let :params do - { :rpc_backend => 'amqp', - :amqp_idle_timeout => '60', + { :amqp_idle_timeout => '60', :amqp_trace => true, :amqp_ssl_ca_file => '/etc/ca.cert', :amqp_ssl_cert_file => '/etc/certfile', diff --git a/spec/classes/cloudkitty_wsgi_apache_spec.rb b/spec/classes/cloudkitty_wsgi_apache_spec.rb new file mode 100644 index 0000000..624461f --- /dev/null +++ b/spec/classes/cloudkitty_wsgi_apache_spec.rb @@ -0,0 +1,93 @@ +require 'spec_helper' + +describe 'cloudkitty::wsgi::apache' do + + shared_examples_for 'apache serving cloudkitty with mod_wsgi' do + context 'with default parameters' do + it { is_expected.to contain_class('cloudkitty::params') } + it { is_expected.to contain_class('apache') } + it { is_expected.to contain_class('apache::mod::wsgi') } + it { is_expected.to contain_class('apache::mod::ssl') } + it { is_expected.to contain_openstacklib__wsgi__apache('cloudkitty_wsgi').with( + :bind_port => 8889, + :group => 'cloudkitty', + :path => '/', + :servername => facts[:fqdn], + :ssl => true, + :threads => facts[:os_workers], + :user => 'cloudkitty', + :workers => 1, + :wsgi_daemon_process => 'cloudkitty', + :wsgi_process_group => 'cloudkitty', + :wsgi_script_dir => platform_params[:wsgi_script_path], + :wsgi_script_file => 'app', + :wsgi_script_source => platform_params[:wsgi_script_source], + )} + end + + context '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 => 'cloudkitty', + :workers => 37, + } + end + it { is_expected.to contain_class('cloudkitty::params') } + it { is_expected.to contain_class('apache') } + it { is_expected.to contain_class('apache::mod::wsgi') } + it { is_expected.to_not contain_class('apache::mod::ssl') } + it { is_expected.to contain_openstacklib__wsgi__apache('cloudkitty_wsgi').with( + :bind_host => '10.42.51.1', + :bind_port => 12345, + :group => 'cloudkitty', + :path => '/', + :servername => 'dummy.host', + :ssl => false, + :threads => facts[:os_workers], + :user => 'cloudkitty', + :workers => 37, + :wsgi_daemon_process => 'cloudkitty', + :wsgi_process_display_name => 'cloudkitty', + :wsgi_process_group => 'cloudkitty', + :wsgi_script_dir => platform_params[:wsgi_script_path], + :wsgi_script_file => 'app', + :wsgi_script_source => platform_params[:wsgi_script_source], + )} + 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 => 42, + :concat_basedir => '/var/lib/puppet/concat', + :fqdn => 'some.host.tld', + })) + end + + let(:platform_params) do + case facts[:osfamily] + when 'Debian' + { + :wsgi_script_path => '/usr/lib/cgi-bin/cloudkitty', + :wsgi_script_source => '/usr/lib/python2.7/dist-packages/cloudkitty/api/app.wsgi' + } + when 'RedHat' + { + :wsgi_script_path => '/var/www/cgi-bin/cloudkitty', + :wsgi_script_source => '/usr/lib/python2.7/site-packages/cloudkitty/api/app.wsgi' + } + end + end + + it_behaves_like 'apache serving cloudkitty with mod_wsgi' + end + end +end