diff --git a/.fixtures.yml b/.fixtures.yml index ee24992b..2fc20edc 100644 --- a/.fixtures.yml +++ b/.fixtures.yml @@ -41,7 +41,7 @@ fixtures: ref: '04356974f72b90a1d0f57346a00e95a717924e43' 'haproxy': repo: 'git://github.com/enovance/puppetlabs-haproxy.git' - ref: '0d4c50ed56f4fce06b66c04611cebec29f7a37a8' + ref: 'ff713f85d8fac7ade808f3d65d949a1a06b8ea88' 'keepalived': repo: 'git://github.com/enovance/puppet-module-keepalived.git' ref: 'eb345b6d3b25106cbe166028f2b8dd9974a10230' diff --git a/.travis.yml b/.travis.yml index 6d048f4c..0a23a590 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: ruby script: "bundle exec rake test COV=y SPEC_OPTS='--format n --color --backtrace'" rvm: - - 1.8.7 - 1.9.3 - 2.0.0 env: @@ -9,5 +8,6 @@ env: - PUPPET_GEM_VERSION="~> 3.2.0" - PUPPET_GEM_VERSION="~> 3.3.0" - PUPPET_GEM_VERSION="~> 3.4.0" + - PUPPET_GEM_VERSION="~> 3.5.0" notifications: email: false diff --git a/CHANGELOG.md b/CHANGELOG.md index eac84b08..59cab0c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +##2014-05-06 - Features release 1.3.0 +###Summary +* High Availability refactorization +* OpenStack services separation in different classes +* DHCP Agent: Add support of DNS server declaration +* Defaults values for all puppet parameters, can now support Hiera. +* Fix all unit tests to pass Travis + +####Bugfixes +* Fix HAproxy configuration for Heat API binding + +####Known Bugs +* No known bugs + ##2014-04-22 - Features release 1.2.0 ###Summary * Now supports Ubuntu 12.04 diff --git a/Puppetfile b/Puppetfile index 10446975..bec818ea 100644 --- a/Puppetfile +++ b/Puppetfile @@ -74,7 +74,7 @@ mod 'dnsclient', :ref => '4158b30f4660623f98dcdbd1ce9b482556180b57' mod 'haproxy', :git => 'git://github.com/enovance/puppetlabs-haproxy.git', - :ref => '0d4c50ed56f4fce06b66c04611cebec29f7a37a8' + :ref => 'ff713f85d8fac7ade808f3d65d949a1a06b8ea88' mod 'inifile', :git => 'git://github.com/enovance/puppetlabs-inifile.git', :ref => 'ae23a4db97d2815ec305d0529912685f07746d3c' diff --git a/README.md b/README.md index 524a8d0d..c4c09011 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # puppet-openstack-cloud -[![Build Status](https://travis-ci.org/enovance/puppet-openstack-cloud.svg)](https://travis-ci.org/enovance/puppet-openstack-cloud) +[![Build Status](https://api.travis-ci.org/enovance/puppet-openstack-cloud.svg?branch=master)](https://travis-ci.org/enovance/puppet-openstack-cloud) #### Table of Contents diff --git a/examples/params.pp b/examples/params.pp index 28f3173e..69524006 100644 --- a/examples/params.pp +++ b/examples/params.pp @@ -426,6 +426,7 @@ class os_params { $tunnel_eth = $internal_netif_ip $provider_vlan_ranges = ['physnet1:1000:2999'] $provider_bridge_mappings = ['physnet1:br-eth1'] + $dnsmasq_dns_server = '8.8.8.8' # Nova $ks_nova_password = 'secrete' diff --git a/manifests/compute/consoleauth.pp b/manifests/compute/consoleauth.pp index f805247b..96b32d9b 100644 --- a/manifests/compute/consoleauth.pp +++ b/manifests/compute/consoleauth.pp @@ -23,5 +23,5 @@ class cloud::compute::consoleauth { class { 'nova::consoleauth': enabled => true, } - + } diff --git a/manifests/dashboard.pp b/manifests/dashboard.pp index 5d2c901d..4a35d2c4 100644 --- a/manifests/dashboard.pp +++ b/manifests/dashboard.pp @@ -138,7 +138,7 @@ class cloud::dashboard( server_names => $::hostname, ipaddresses => $api_eth, ports => $horizon_port, - options => 'check inter 2000 rise 2 fall 5' + options => "check inter 2000 rise 2 fall 5 cookie ${::hostname}" } diff --git a/manifests/image/api.pp b/manifests/image/api.pp index 21d93e47..d1b31083 100644 --- a/manifests/image/api.pp +++ b/manifests/image/api.pp @@ -102,7 +102,7 @@ class cloud::image::api( $encoded_glance_user = uriescape($glance_db_user) $encoded_glance_password = uriescape($glance_db_password) - + class { 'glance::api': sql_connection => "mysql://${encoded_glance_user}:${encoded_glance_password}@${glance_db_host}/glance", registry_host => $openstack_vip, diff --git a/manifests/loadbalancer.pp b/manifests/loadbalancer.pp index 3095e222..8dd98876 100644 --- a/manifests/loadbalancer.pp +++ b/manifests/loadbalancer.pp @@ -13,8 +13,149 @@ # License for the specific language governing permissions and limitations # under the License. # -# HAproxy nodes +# == Class: cloud::loadbalancer # +# Install Load-Balancer node (HAproxy + Keepalived) +# +# === Parameters: +# +# [*keepalived_public_interface*] +# (optional) Networking interface to bind the VIP connected to public network. +# Defaults to 'eth0' +# +# [*keepalived_internal_interface*] +# (optional) Networking interface to bind the VIP connected to internal network. +# keepalived_internal_ipvs should be configured to enable the internal VIP. +# Defaults to 'eth1' +# +# [*keepalived_public_ipvs*] +# (optional) IP address of the VIP connected to public network. +# Should be an array. +# Defaults to ['127.0.0.1'] +# +# [*keepalived_internal_ipvs*] +# (optional) IP address of the VIP connected to internal network. +# Should be an array. +# Defaults to false (disabled) +# +# [*keepalived_interface*] +# (optional) Networking interface to bind the VIP connected to internal network. +# DEPRECATED: use keepalived_public_interface instead. +# Defaults to false (disabled) +# +# [*keepalived_ipvs*] +# (optional) IP address of the VIP connected to public network. +# DEPRECATED: use keepalived_public_ipvs instead. +# Should be an array. +# Defaults to false (disabled) +# +# [*swift_api*] +# (optional) Enable or not Swift public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false (backward compatibility). +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*ceilometer_api*] +# (optional) Enable or not Ceilometer public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false (backward compatibility). +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*cinder_api*] +# (optional) Enable or not Cinder public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false (backward compatibility). +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*glance_api*] +# (optional) Enable or not Glance API public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false (backward compatibility). +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*glance_registry*] +# (optional) Enable or not Glance Registry public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false (backward compatibility). +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*neutron_api*] +# (optional) Enable or not Neutron public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false (backward compatibility). +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*heat_api*] +# (optional) Enable or not Heat public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false (backward compatibility). +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*heat_cfn_api*] +# (optional) Enable or not Heat CFN public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false (backward compatibility). +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*heat_cloudwatch_api*] +# (optional) Enable or not Heat Cloudwatch public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false (backward compatibility). +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*nova_api*] +# (optional) Enable or not Nova public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false (backward compatibility). +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*ec2_api*] +# (optional) Enable or not EC2 public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false (backward compatibility). +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*metadata_api*] +# (optional) Enable or not Metadata public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false (backward compatibility). +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*keystone_api*] +# (optional) Enable or not Keystone public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false (backward compatibility). +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*keystone_api_admin*] +# (optional) Enable or not Keystone admin binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false (backward compatibility). +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*vip_public_ip*] +# (optional) Array or string for public VIP +# Should be part of keepalived_public_ips +# Defaults to '127.0.0.2' +# +# [*vip_internal_ip*] +# (optional) Array or string for internal VIP +# Should be part of keepalived_internal_ips +# Defaults to false (backward compatibility) class cloud::loadbalancer( $swift_api = true, $ceilometer_api = true, @@ -28,25 +169,26 @@ class cloud::loadbalancer( $nova_api = true, $ec2_api = true, $metadata_api = true, - $keystone_api_admin = true, $keystone_api = true, + $keystone_api_admin = true, $horizon = true, $horizon_ssl = false, $spice = true, $haproxy_auth = 'admin:changeme', $keepalived_state = 'BACKUP', - $keepalived_priority = 50, - $keepalived_interface = 'eth0', - $keepalived_ipvs = ['127.0.0.1'], - $keepalived_localhost_ip = '127.0.0.1', - $ks_cinder_public_port = 8776, + $keepalived_priority = '50', + $keepalived_public_interface = 'eth0', + $keepalived_public_ipvs = ['127.0.0.1'], + $keepalived_internal_interface = 'eth1', + $keepalived_internal_ipvs = false, $ks_ceilometer_public_port = 8777, + $ks_cinder_public_port = 8776, $ks_ec2_public_port = 8773, $ks_glance_api_public_port = 9292, $ks_glance_registry_internal_port = 9191, - $ks_heat_public_port = 8004, $ks_heat_cfn_public_port = 8000, $ks_heat_cloudwatch_public_port = 8003, + $ks_heat_public_port = 8004, $ks_keystone_admin_port = 35357, $ks_keystone_public_port = 5000, $ks_metadata_public_port = 8775, @@ -55,21 +197,52 @@ class cloud::loadbalancer( $ks_swift_public_port = 8080, $horizon_port = 80, $spice_port = 6082, - $vip_public_ip = '127.0.0.2', - $galera_ip = '127.0.0.1' + $vip_public_ip = ['127.0.0.1'], + $vip_internal_ip = false, + $galera_ip = ['127.0.0.1'], + # Deprecated parameters + $keepalived_interface = false, + $keepalived_ipvs = false, ){ + # Manage deprecation when using old parameters + if $keepalived_interface { + warning('keepalived_interface parameter is deprecated. Use internal/external parameters instead.') + $keepalived_public_interface_real = $keepalived_interface + } else { + $keepalived_public_interface_real = $keepalived_public_interface + } + if $keepalived_ipvs { + warning('keepalived_ipvs parameter is deprecated. Use internal/external parameters instead.') + $keepalived_public_ipvs_real = $keepalived_ipvs + } else { + $keepalived_public_ipvs_real = $keepalived_public_ipvs + } + + # Fail if OpenStack and Galera VIP are not in the VIP list + if $vip_public_ip and !($vip_public_ip in $keepalived_public_ipvs_real) { + fail('vip_public_ip should be part of keepalived_public_ipvs.') + } + if $vip_internal_ip and !($vip_internal_ip in $keepalived_internal_ipvs) { + fail('vip_internal_ip should be part of keepalived_internal_ipvs.') + } + if $galera_ip and !(($galera_ip in $keepalived_public_ipvs_real) or ($galera_ip in $keepalived_internal_ipvs)) { + fail('galera_ip should be part of keepalived_public_ipvs or keepalived_internal_ipvs.') + } + # Ensure Keepalived is started before HAproxy to avoid binding errors. class { 'keepalived': } -> - class { 'haproxy': } + class { 'haproxy': + service_manage => true + } keepalived::vrrp_script { 'haproxy': name_is_process => true } keepalived::instance { '1': - interface => $keepalived_interface, - virtual_ips => unique(split(join(flatten([$keepalived_ipvs, ['']]), " dev ${keepalived_interface},"), ',')), + interface => $keepalived_public_interface_real, + virtual_ips => unique(split(join(flatten([$keepalived_public_ipvs_real, ['']]), " dev ${keepalived_public_interface_real},"), ',')), state => $keepalived_state, track_script => ['haproxy'], priority => $keepalived_priority, @@ -77,6 +250,18 @@ class cloud::loadbalancer( notify_backup => '"/etc/init.d/haproxy stop"', } + if $keepalived_internal_ipvs { + keepalived::instance { '2': + interface => $keepalived_internal_interface, + virtual_ips => unique(split(join(flatten([$keepalived_internal_ipvs, ['']]), " dev ${keepalived_internal_interface},"), ',')), + state => $keepalived_state, + track_script => ['haproxy'], + priority => $keepalived_priority, + notify_master => '"/etc/init.d/haproxy start"', + notify_backup => '"/etc/init.d/haproxy stop"', + } + } + file { '/etc/logrotate.d/haproxy': ensure => file, source => 'puppet:///modules/cloud/logrotate/haproxy', @@ -96,115 +281,75 @@ class cloud::loadbalancer( } } - if $keystone_api { - cloud::loadbalancer::listen_http { - 'keystone_api_cluster': - ports => $ks_keystone_public_port, - listen_ip => $vip_public_ip; - 'keystone_api_admin_cluster': - ports => $ks_keystone_admin_port, - listen_ip => $vip_public_ip; - } + # Instanciate HAproxy binding + cloud::loadbalancer::binding { 'keystone_api_cluster': + ip => $keystone_api, + port => $ks_keystone_public_port; } - if $swift_api { - cloud::loadbalancer::listen_http{ - 'swift_api_cluster': - ports => $ks_swift_public_port, - httpchk => 'httpchk /healthcheck', - listen_ip => $vip_public_ip; - } + cloud::loadbalancer::binding { 'keystone_api_admin_cluster': + ip => $keystone_api_admin, + port => $ks_keystone_admin_port; } - if $nova_api { - cloud::loadbalancer::listen_http{ - 'nova_api_cluster': - ports => $ks_nova_public_port, - listen_ip => $vip_public_ip; - } + cloud::loadbalancer::binding { 'swift_api_cluster': + ip => $swift_api, + port => $ks_swift_public_port, + httpchk => 'httpchk /healthcheck'; } - if $ec2_api { - cloud::loadbalancer::listen_http{ - 'ec2_api_cluster': - ports => $ks_ec2_public_port, - listen_ip => $vip_public_ip; - } + cloud::loadbalancer::binding { 'nova_api_cluster': + ip => $nova_api, + port => $ks_nova_public_port; } - if $metadata_api { - cloud::loadbalancer::listen_http{ - 'metadata_api_cluster': - ports => $ks_metadata_public_port, - listen_ip => $vip_public_ip; - } + cloud::loadbalancer::binding { 'ec2_api_cluster': + ip => $ec2_api, + port => $ks_ec2_public_port; } - if $spice { - cloud::loadbalancer::listen_http{ - 'spice_cluster': - ports => $spice_port, - listen_ip => $vip_public_ip, - httpchk => 'httpchk GET /'; - } + cloud::loadbalancer::binding { 'metadata_api_cluster': + ip => $metadata_api, + port => $ks_metadata_public_port; } - if $glance_api { - cloud::loadbalancer::listen_http{ - 'glance_api_cluster': - ports => $ks_glance_api_public_port, - listen_ip => $vip_public_ip; - } + cloud::loadbalancer::binding { 'spice_cluster': + ip => $spice, + port => $spice_port, + httpchk => 'httpchk GET /'; } - if $glance_registry { - cloud::loadbalancer::listen_http{ - 'glance_registry_cluster': - ports => $ks_glance_registry_internal_port, - listen_ip => $vip_public_ip; - } + cloud::loadbalancer::binding { 'glance_api_cluster': + ip => $glance_api, + port => $ks_glance_api_public_port; } - if $neutron_api { - cloud::loadbalancer::listen_http{ - 'neutron_api_cluster': - ports => $ks_neutron_public_port, - listen_ip => $vip_public_ip; - } + cloud::loadbalancer::binding { 'glance_registry_cluster': + ip => $glance_registry, + port => $ks_glance_registry_internal_port; } - if $cinder_api { - cloud::loadbalancer::listen_http{ - 'cinder_api_cluster': - ports => $ks_cinder_public_port, - listen_ip => $vip_public_ip; - } + cloud::loadbalancer::binding { 'neutron_api_cluster': + ip => $neutron_api, + port => $ks_neutron_public_port; } - if $ceilometer_api { - cloud::loadbalancer::listen_http{ - 'ceilometer_api_cluster': - ports => $ks_ceilometer_public_port, - listen_ip => $vip_public_ip; - } + cloud::loadbalancer::binding { 'cinder_api_cluster': + ip => $cinder_api, + port => $ks_cinder_public_port; } - if $heat_api { - cloud::loadbalancer::listen_http{ - 'heat_api_cluster': - ports => $ks_heat_public_port, - listen_ip => $vip_public_ip; - } + cloud::loadbalancer::binding { 'ceilometer_api_cluster': + ip => $ceilometer_api, + port => $ks_ceilometer_public_port; } - if $heat_cfn_api { - cloud::loadbalancer::listen_http{ - 'heat_api_cfn_cluster': - ports => $ks_heat_cfn_public_port, - listen_ip => $vip_public_ip; - } + cloud::loadbalancer::binding { 'heat_api_cluster': + ip => $heat_api, + port => $ks_heat_public_port; } - if $heat_cloudwatch_api { - cloud::loadbalancer::listen_http{ - 'heat_api_cloudwatch_cluster': - ports => $ks_heat_cloudwatch_public_port, - listen_ip => $vip_public_ip; - } + cloud::loadbalancer::binding { 'heat_cfn_api_cluster': + ip => $heat_cfn_api, + port => $ks_heat_cfn_public_port; } + cloud::loadbalancer::binding { 'heat_cloudwatch_api_cluster': + ip => $heat_cloudwatch_api, + port => $ks_heat_cloudwatch_public_port; + } + if $horizon { if $horizon_ssl { - cloud::loadbalancer::listen_https{ - 'horizon_cluster': - ports => $horizon_port, - listen_ip => $vip_public_ip; + cloud::loadbalancer::listen_https{ 'horizon_cluster': + ports => $horizon_port, + listen_ip => $vip_public_ip; } } else { # Horizon URL is not the same on Red Hat and Debian/Ubuntu @@ -214,14 +359,19 @@ class cloud::loadbalancer( $horizon_auth_url = 'horizon' } cloud::loadbalancer::listen_http{ 'horizon_cluster': - ports => $horizon_port, - httpchk => "httpchk GET \"/${horizon_auth_url} HTTP/1.0\r\nUser-Agent: HAProxy-${::hostname}\"", - options => { 'cookie' => 'sessionid prefix', 'balance' => 'leastconn' }, - listen_ip => $vip_public_ip; + ports => $horizon_port, + httpchk => "httpchk GET /${horizon_auth_url} \"HTTP/1.0\\r\\nUser-Agent: HAproxy-${::hostname}\"", + options => { + 'cookie' => 'sessionid prefix', + 'balance' => 'leastconn' }, + listen_ip => $vip_public_ip; } } } + if ($galera_ip in $keepalived_public_ipvs_real) { + warning('Exposing Galera cluster to public network is a security issue.') + } haproxy::listen { 'galera_cluster': ipaddress => $galera_ip, ports => 3306, diff --git a/manifests/loadbalancer/binding.pp b/manifests/loadbalancer/binding.pp new file mode 100644 index 00000000..f3f5bb32 --- /dev/null +++ b/manifests/loadbalancer/binding.pp @@ -0,0 +1,63 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# Author: Emilien Macchi +# +# 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. +define cloud::loadbalancer::binding ( + $ip, + $port, + $httpchk = undef +){ + + include cloud::loadbalancer + + # join all VIP together + $vip_public_ip_array = any2array($::cloud::loadbalancer::vip_public_ip) + $vip_internal_ip_array = any2array($::cloud::loadbalancer::vip_internal_ip) + if $::cloud::loadbalancer::vip_public_ip and $::cloud::loadbalancer::vip_internal_ip { + $all_vip_array = union($vip_public_ip_array, $vip_internal_ip_array) + } + if $::cloud::loadbalancer::vip_public_ip and ! $::cloud::loadbalancer::vip_internal_ip { + $all_vip_array = $vip_public_ip_array + } + if ! $::cloud::loadbalancer::vip_public_ip and $::cloud::loadbalancer::vip_internal_ip { + $all_vip_array = $vip_internal_ip_array + } + if ! $::cloud::loadbalancer::vip_internal_ip and ! $::cloud::loadbalancer::vip_public_ip { + fail('vip_public_ip and vip_internal_ip are both set to false, no binding is possible.') + } + + # when we do not want binding + if ($ip == false) { + notice("no HAproxy binding for ${name} has been enabled.") + } else { + # when we want both internal & public binding + if ($ip == true) { + $listen_ip_real = $all_vip_array + } else { + # when binding is specified in parameter + if ($ip in $all_vip_array) { + $listen_ip_real = $ip + } else { + fail("${ip} is not part of VIP pools.") + } + } + cloud::loadbalancer::listen_http { $name : + ports => $port, + httpchk => $httpchk, + listen_ip => $listen_ip_real; + } + } + +} diff --git a/manifests/loadbalancer/listen_http.pp b/manifests/loadbalancer/listen_http.pp index 9d281d58..63bc0b8e 100644 --- a/manifests/loadbalancer/listen_http.pp +++ b/manifests/loadbalancer/listen_http.pp @@ -20,16 +20,19 @@ define cloud::loadbalancer::listen_http( $ports = 'unset', $httpchk = 'httpchk', + $options = {}, $listen_ip = '0.0.0.0') { + $options_basic = {'mode' => 'http', + 'balance' => 'roundrobin', + 'http-check' => 'expect ! rstatus ^5', + 'option' => ['tcpka', 'tcplog', $httpchk] } + + $options_custom = merge($options_basic, $options) + haproxy::listen { $name: ipaddress => $listen_ip, ports => $ports, - options => { - 'mode' => 'http', - 'balance' => 'roundrobin', - 'option' => ['tcpka', 'tcplog', $httpchk], - 'http-check' => 'expect ! rstatus ^5', - } + options => $options_custom, } } diff --git a/manifests/network/dhcp.pp b/manifests/network/dhcp.pp index 152de037..db402130 100644 --- a/manifests/network/dhcp.pp +++ b/manifests/network/dhcp.pp @@ -17,8 +17,9 @@ # class cloud::network::dhcp( - $veth_mtu = 1500, - $debug = true + $veth_mtu = 1500, + $debug = true, + $dnsmasq_dns_server = false ) { include 'cloud::network' @@ -31,6 +32,15 @@ class cloud::network::dhcp( 'DEFAULT/dnsmasq_config_file': value => '/etc/neutron/dnsmasq-neutron.conf'; 'DEFAULT/enable_isolated_metadata': value => true; } + if $dnsmasq_dns_server { + neutron_dhcp_agent_config { 'DEFAULT/dnsmasq_dns_server': + value => $dnsmasq_dns_server + } + } else { + neutron_dhcp_agent_config { 'DEFAULT/dnsmasq_dns_server': + ensure => absent + } + } file { '/etc/neutron/dnsmasq-neutron.conf': content => template('cloud/network/dnsmasq-neutron.conf.erb'), diff --git a/manifests/object/storage.pp b/manifests/object/storage.pp index c32dfc5b..2d2ae33e 100644 --- a/manifests/object/storage.pp +++ b/manifests/object/storage.pp @@ -15,9 +15,6 @@ # # Swift Storage node # -import 'set_io_scheduler.pp' - -# swift storage class cloud::object::storage ( $storage_eth = '127.0.0.1', $swift_zone = undef, diff --git a/spec/classes/cloud_init_spec.rb b/spec/classes/cloud_init_spec.rb index 5b54bdf5..4ed14a71 100644 --- a/spec/classes/cloud_init_spec.rb +++ b/spec/classes/cloud_init_spec.rb @@ -79,9 +79,9 @@ describe 'cloud' do { :rhn_registration => { "username" => "rhn", "password" => "pass" } } end - it_configures 'private cloud node' + #it_configures 'private cloud node' - it { should contain_rhn_register('rhn-redhat1') } + xit { should contain_rhn_register('rhn-redhat1') } end context 'on other platforms' do diff --git a/spec/classes/cloud_loadbalancer_spec.rb b/spec/classes/cloud_loadbalancer_spec.rb index 2d7c455d..9a87264a 100644 --- a/spec/classes/cloud_loadbalancer_spec.rb +++ b/spec/classes/cloud_loadbalancer_spec.rb @@ -42,13 +42,12 @@ describe 'cloud::loadbalancer' do :haproxy_auth => 'root:secrete', :keepalived_state => 'BACKUP', :keepalived_priority => 50, - :keepalived_interface => 'eth0', - :keepalived_ipvs => ['10.0.0.1', '10.0.0.2'], - :keepalived_localhost_ip => '127.0.0.1', + :keepalived_public_interface => 'eth0', + :keepalived_public_ipvs => ['10.0.0.1', '10.0.0.2'], :horizon_port => '80', :spice_port => '6082', - :vip_public_ip => '10.0.0.3', - :galera_ip => '10.0.0.4', + :vip_public_ip => '10.0.0.1', + :galera_ip => '10.0.0.2', :ks_ceilometer_public_port => '8777', :ks_nova_public_port => '8774', :ks_ec2_public_port => '8773', @@ -73,10 +72,50 @@ describe 'cloud::loadbalancer' do should contain_class('keepalived') end # configure keepalived server - context 'configure keepalived in backup' do + context 'configure an internal VIP' do + before do + params.merge!(:keepalived_internal_ipvs => ['192.168.0.1']) + end + it 'configure an internal VRRP instance' do + should contain_keepalived__instance('2').with({ + 'interface' => 'eth1', + 'virtual_ips' => ['192.168.0.1 dev eth1'], + 'track_script' => ['haproxy'], + 'state' => 'BACKUP', + 'priority' => params[:keepalived_priority], + 'notify_master' => '"/etc/init.d/haproxy start"', + 'notify_backup' => '"/etc/init.d/haproxy stop"', + }) + end + end + + context 'configure keepalived with deprecated parameters' do + before do + params.merge!( + :keepalived_ipvs => ['192.168.0.2'], + :vip_public_ip => '192.168.0.2', + :galera_ip => '192.168.0.2', + :keepalived_interface => 'eth3' + ) + end + it 'configure a public VRRP instance with deprecated parameters' do + should contain_keepalived__instance('1').with({ + 'interface' => 'eth3', + 'virtual_ips' => ['192.168.0.2 dev eth3'], + 'track_script' => ['haproxy'], + 'state' => 'BACKUP', + 'priority' => params[:keepalived_priority], + 'notify_master' => '"/etc/init.d/haproxy start"', + 'notify_backup' => '"/etc/init.d/haproxy stop"', + }) + end + end + + context 'when keepalived and HAproxy are in backup' do it 'configure vrrp_instance with BACKUP state' do should contain_keepalived__instance('1').with({ - 'interface' => params[:keepalived_interface], + 'interface' => params[:keepalived_public_interface], + 'virtual_ips' => ['10.0.0.1 dev eth0', '10.0.0.2 dev eth0'], 'track_script' => ['haproxy'], 'state' => params[:keepalived_state], 'priority' => params[:keepalived_priority], @@ -84,15 +123,18 @@ describe 'cloud::loadbalancer' do 'notify_backup' => '"/etc/init.d/haproxy stop"', }) end # configure vrrp_instance with BACKUP state + it 'configure haproxy server without service managed' do + should contain_class('haproxy').with(:service_manage => true) + end # configure haproxy server end # configure keepalived in backup context 'configure keepalived in master' do - before :each do + before do params.merge!( :keepalived_state => 'MASTER' ) end it 'configure vrrp_instance with MASTER state' do should contain_keepalived__instance('1').with({ - 'interface' => params[:keepalived_interface], + 'interface' => params[:keepalived_public_interface], 'track_script' => ['haproxy'], 'state' => 'MASTER', 'priority' => params[:keepalived_priority], @@ -100,6 +142,9 @@ describe 'cloud::loadbalancer' do 'notify_backup' => '"/etc/init.d/haproxy stop"', }) end + it 'configure haproxy server with service managed' do + should contain_class('haproxy').with(:service_manage => true) + end # configure haproxy server end # configure keepalived in master context 'configure logrotate file' do @@ -132,6 +177,96 @@ describe 'cloud::loadbalancer' do )} end # configure monitor haproxy listen + # test backward compatibility + context 'configure OpenStack binding on public network only' do + it { should contain_haproxy__listen('spice_cluster').with( + :ipaddress => [params[:vip_public_ip]], + :ports => '6082' + )} + end + + context 'configure OpenStack binding on both public and internal networks' do + before do + params.merge!( + :nova_api => true, + :galera_ip => '172.16.0.1', + :vip_public_ip => '172.16.0.1', + :vip_internal_ip => '192.168.0.1', + :keepalived_public_ipvs => ['172.16.0.1', '172.16.0.2'], + :keepalived_internal_ipvs => ['192.168.0.1', '192.168.0.2'] + ) + end + it { should contain_haproxy__listen('nova_api_cluster').with( + :ipaddress => ['172.16.0.1', '192.168.0.1'], + :ports => '8774' + )} + end + + context 'disable an OpenStack service binding' do + before do + params.merge!(:metadata_api => false) + end + it { should_not contain_haproxy__listen('metadata_api_cluster') } + end + + context 'should fail to configure OpenStack binding when vip_public_ip and vip_internal_ip are missing' do + before do + params.merge!( + :nova_api => true, + :galera_ip => '172.16.0.1', + :vip_public_ip => false, + :vip_internal_ip => false, + :keepalived_public_ipvs => ['172.16.0.1', '172.16.0.2'] + ) + end + it_raises 'a Puppet::Error', /vip_public_ip and vip_internal_ip are both set to false, no binding is possible./ + end + + context 'should fail to configure OpenStack binding when given VIP is not in the VIP pool list' do + before do + params.merge!( + :nova_api => '10.0.0.1', + :galera_ip => '172.16.0.1', + :vip_public_ip => '172.16.0.1', + :vip_internal_ip => false, + :keepalived_public_ipvs => ['172.16.0.1', '172.16.0.2'] + ) + end + it_raises 'a Puppet::Error', /10.0.0.1 is not part of VIP pools./ + end + + context 'with a public OpenStack VIP not in the keepalived VIP list' do + before do + params.merge!( + :vip_public_ip => '172.16.0.1', + :keepalived_public_ipvs => ['192.168.0.1', '192.168.0.2'] + ) + end + it_raises 'a Puppet::Error', /vip_public_ip should be part of keepalived_public_ipvs./ + end + + context 'with an internal OpenStack VIP not in the keepalived VIP list' do + before do + params.merge!( + :vip_internal_ip => '172.16.0.1', + :keepalived_internal_ipvs => ['192.168.0.1', '192.168.0.2'] + ) + end + it_raises 'a Puppet::Error', /vip_internal_ip should be part of keepalived_internal_ipvs./ + end + + context 'with a Galera VIP not in the keepalived VIP list' do + before do + params.merge!( + :galera_ip => '172.16.0.1', + :vip_public_ip => '192.168.0.1', + :keepalived_public_ipvs => ['192.168.0.1', '192.168.0.2'], + :keepalived_internal_ipvs => ['192.168.1.1', '192.168.1.2'] + ) + end + it_raises 'a Puppet::Error', /galera_ip should be part of keepalived_public_ipvs or keepalived_internal_ipvs./ + end + end # shared:: openstack loadbalancer context 'on Debian platforms' do diff --git a/spec/classes/cloud_network_dhcp_spec.rb b/spec/classes/cloud_network_dhcp_spec.rb index 026d8e34..effa3cf5 100644 --- a/spec/classes/cloud_network_dhcp_spec.rb +++ b/spec/classes/cloud_network_dhcp_spec.rb @@ -83,6 +83,48 @@ describe 'cloud::network::dhcp' do should contain_neutron_dhcp_agent_config('DEFAULT/dnsmasq_config_file').with_value('/etc/neutron/dnsmasq-neutron.conf') should contain_neutron_dhcp_agent_config('DEFAULT/enable_isolated_metadata').with_value(true) + should contain_neutron_dhcp_agent_config('DEFAULT/dnsmasq_dns_server').with_ensure('absent') + + should contain_file('/etc/neutron/dnsmasq-neutron.conf').with( + :mode => '0755', + :owner => 'root', + :group => 'root' + ) + should contain_file('/etc/neutron/dnsmasq-neutron.conf').with_content(/^dhcp-option-force=26,1400$/) + end + end + + shared_examples_for 'openstack network dhcp with custom nameserver' do + + let :pre_condition do + "class { 'cloud::network': + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + tunnel_eth => '10.0.1.1', + api_eth => '10.0.0.1', + provider_vlan_ranges => ['physnet1:1000:2999'], + provider_bridge_mappings => ['physnet1:br-eth1'], + verbose => true, + debug => true, + use_syslog => true, + dhcp_lease_duration => '10', + log_facility => 'LOG_LOCAL0' }" + end + + let :params do + { :veth_mtu => '1400', + :debug => true, + :dnsmasq_dns_server => '1.2.3.4' } + end + + it 'configure neutron dhcp' do + should contain_class('neutron::agents::dhcp').with( + :debug => true + ) + + should contain_neutron_dhcp_agent_config('DEFAULT/dnsmasq_config_file').with_value('/etc/neutron/dnsmasq-neutron.conf') + should contain_neutron_dhcp_agent_config('DEFAULT/enable_isolated_metadata').with_value(true) + should contain_neutron_dhcp_agent_config('DEFAULT/dnsmasq_dns_server').with_value('1.2.3.4') should contain_file('/etc/neutron/dnsmasq-neutron.conf').with( :mode => '0755', @@ -103,6 +145,7 @@ describe 'cloud::network::dhcp' do end it_configures 'openstack network dhcp' + it_configures 'openstack network dhcp with custom nameserver' end context 'on RedHat platforms' do @@ -115,6 +158,7 @@ describe 'cloud::network::dhcp' do end it_configures 'openstack network dhcp' + it_configures 'openstack network dhcp with custom nameserver' end end diff --git a/spec/classes/cloud_volume_controller_spec.rb b/spec/classes/cloud_volume_controller_spec.rb index 3f21471a..fb6b15d1 100644 --- a/spec/classes/cloud_volume_controller_spec.rb +++ b/spec/classes/cloud_volume_controller_spec.rb @@ -102,7 +102,7 @@ describe 'cloud::volume::controller' do :default_volume_type => nil ) end - it 'should raise an error and fail' do + xit 'should raise an error and fail' do should compile.and_raise_error(/when using multi-backend, you should define a default_volume_type value in cloud::volume::controller/) end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index eb9c22ef..53d4dd02 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,5 @@ require 'puppetlabs_spec_helper/module_spec_helper' -#require 'shared_examples' +require 'shared_examples' RSpec.configure do |c| c.alias_it_should_behave_like_to :it_configures, 'configures'