Yanis Guenane c37c7b52eb loadbalancer: Allow user to bind multiple public/private ips
Currently, a deployer can only bind on the loadbalancer 1 ip per
network (be it public, internal, admin, etc...).
If a deployer wants to bind to the ipv6 and ipv4 at the same time he
couldn't. This patch aims to fix that.
2014-09-16 15:33:13 -04:00

510 lines
21 KiB
Puppet

#
# Copyright (C) 2014 eNovance SAS <licensing@enovance.com>
#
# 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: cloud::loadbalancer
#
# Install Load-Balancer node (HAproxy + Keepalived)
#
# === Parameters:
# [*keepalived_vrrp_interface*]
# (optional) Networking interface to bind the vrrp traffic.
# Defaults to false (disabled)
#
# [*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
#
# [*trove_api*]
# (optional) Enable or not Trove 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,
$cinder_api = true,
$glance_api = true,
$glance_registry = true,
$neutron_api = true,
$heat_api = true,
$heat_cfn_api = true,
$heat_cloudwatch_api = true,
$nova_api = true,
$ec2_api = true,
$metadata_api = true,
$keystone_api = true,
$keystone_api_admin = true,
$trove_api = true,
$horizon = true,
$horizon_ssl = false,
$spice = true,
$haproxy_auth = 'admin:changeme',
$keepalived_state = 'BACKUP',
$keepalived_priority = '50',
$keepalived_vrrp_interface = false,
$keepalived_public_interface = 'eth0',
$keepalived_public_ipvs = ['127.0.0.1'],
$keepalived_internal_interface = 'eth1',
$keepalived_internal_ipvs = [],
$ceilometer_bind_options = [],
$cinder_bind_options = [],
$ec2_bind_options = [],
$glance_api_bind_options = [],
$glance_registry_bind_options = [],
$heat_cfn_bind_options = [],
$heat_cloudwatch_bind_options = [],
$heat_api_bind_options = [],
$keystone_bind_options = [],
$keystone_admin_bind_options = [],
$metadata_bind_options = [],
$neutron_bind_options = [],
$nova_bind_options = [],
$trove_bind_options = [],
$swift_bind_options = [],
$spice_bind_options = [],
$horizon_bind_options = [],
$horizon_ssl_bind_options = [],
$galera_bind_options = [],
$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_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,
$ks_neutron_public_port = 9696,
$ks_nova_public_port = 8774,
$ks_swift_public_port = 8080,
$ks_trove_public_port = 8779,
$horizon_port = 80,
$horizon_ssl_port = 443,
$spice_port = 6082,
$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
}
if $keepalived_vrrp_interface {
$keepalived_vrrp_interface_real = $keepalived_vrrp_interface
} else {
$keepalived_vrrp_interface_real = $keepalived_public_interface_real
}
# end of deprecation support
# Fail if OpenStack and Galera VIP are not in the VIP list
if $vip_public_ip and !(member(any2array($keepalived_public_ipvs_real), $vip_public_ip)) {
fail('vip_public_ip should be part of keepalived_public_ipvs.')
}
if $vip_internal_ip and !(member(any2array($keepalived_internal_ipvs),$vip_internal_ip)) {
fail('vip_internal_ip should be part of keepalived_internal_ipvs.')
}
if $galera_ip and !((member(any2array($keepalived_public_ipvs_real),$galera_ip)) or (member(any2array($keepalived_internal_ipvs),$galera_ip))) {
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':
service_manage => true
}
keepalived::vrrp_script { 'haproxy':
name_is_process => true
}
keepalived::instance { '1':
interface => $keepalived_vrrp_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,
notify_master => '"/etc/init.d/haproxy start"',
notify_backup => '"/etc/init.d/haproxy stop"',
}
if !empty($keepalived_internal_ipvs) {
if ! $keepalived_vrrp_interface {
$keepalived_vrrp_interface_internal = $keepalived_internal_interface
} else {
$keepalived_vrrp_interface_internal = $keepalived_vrrp_interface
}
keepalived::instance { '2':
interface => $keepalived_vrrp_interface_internal,
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',
owner => root,
group => root,
mode => '0644';
}
haproxy::listen { 'monitor':
ipaddress => $vip_public_ip,
ports => '9300',
options => {
'mode' => 'http',
'monitor-uri' => '/status',
'stats' => ['enable','uri /admin','realm Haproxy\ Statistics',"auth ${haproxy_auth}", 'refresh 5s' ],
'' => template('cloud/loadbalancer/monitor.erb'),
}
}
# Instanciate HAproxy binding
cloud::loadbalancer::binding { 'keystone_api_cluster':
ip => $keystone_api,
port => $ks_keystone_public_port,
bind_options => $keystone_bind_options,
}
cloud::loadbalancer::binding { 'keystone_api_admin_cluster':
ip => $keystone_api_admin,
port => $ks_keystone_admin_port,
bind_options => $keystone_admin_bind_options,
}
cloud::loadbalancer::binding { 'swift_api_cluster':
ip => $swift_api,
port => $ks_swift_public_port,
bind_options => $swift_bind_options,
httpchk => 'httpchk /healthcheck',
}
cloud::loadbalancer::binding { 'nova_api_cluster':
ip => $nova_api,
port => $ks_nova_public_port,
bind_options => $nova_bind_options,
}
cloud::loadbalancer::binding { 'ec2_api_cluster':
ip => $ec2_api,
port => $ks_ec2_public_port,
bind_options => $ec2_bind_options,
}
cloud::loadbalancer::binding { 'metadata_api_cluster':
ip => $metadata_api,
port => $ks_metadata_public_port,
bind_options => $metadata_bind_options,
}
cloud::loadbalancer::binding { 'spice_cluster':
ip => $spice,
port => $spice_port,
options => {
'mode' => 'tcp',
'option' => ['tcpka', 'tcplog', 'forwardfor'],
'balance' => 'source',
'timeout server' => '120m',
'timeout client' => '120m',
},
bind_options => $spice_bind_options,
}
cloud::loadbalancer::binding { 'trove_api_cluster':
ip => $trove_api,
port => $ks_trove_public_port,
bind_options => $trove_bind_options,
}
cloud::loadbalancer::binding { 'glance_api_cluster':
ip => $glance_api,
options => {
'mode' => 'tcp',
'balance' => 'source',
'option' => ['tcpka', 'tcplog', 'forwardfor'],
'timeout server' => '120m',
'timeout client' => '120m',
},
port => $ks_glance_api_public_port,
bind_options => $glance_api_bind_options,
}
cloud::loadbalancer::binding { 'glance_registry_cluster':
ip => $glance_registry,
port => $ks_glance_registry_internal_port,
bind_options => $glance_registry_bind_options,
}
cloud::loadbalancer::binding { 'neutron_api_cluster':
ip => $neutron_api,
port => $ks_neutron_public_port,
bind_options => $neutron_bind_options,
}
cloud::loadbalancer::binding { 'cinder_api_cluster':
ip => $cinder_api,
port => $ks_cinder_public_port,
bind_options => $cinder_bind_options,
}
cloud::loadbalancer::binding { 'ceilometer_api_cluster':
ip => $ceilometer_api,
port => $ks_ceilometer_public_port,
bind_options => $ceilometer_bind_options,
}
if 'ssl' in $heat_api_bind_options {
$heat_api_options = {
'reqadd' => 'X-Forwarded-Proto:\ https if { ssl_fc }' }
} else {
$heat_api_options = {}
}
cloud::loadbalancer::binding { 'heat_api_cluster':
ip => $heat_api,
port => $ks_heat_public_port,
bind_options => $heat_api_bind_options,
options => $heat_api_options
}
if 'ssl' in $heat_cfn_bind_options {
$heat_cfn_options = {
'reqadd' => 'X-Forwarded-Proto:\ https if { ssl_fc }' }
} else {
$heat_cfn_options = { }
}
cloud::loadbalancer::binding { 'heat_cfn_api_cluster':
ip => $heat_cfn_api,
port => $ks_heat_cfn_public_port,
bind_options => $heat_cfn_bind_options,
options => $heat_cfn_options
}
if 'ssl' in $heat_cloudwatch_bind_options {
$heat_cloudwatch_options = {
'reqadd' => 'X-Forwarded-Proto:\ https if { ssl_fc }' }
} else {
$heat_cloudwatch_options = { }
}
cloud::loadbalancer::binding { 'heat_cloudwatch_api_cluster':
ip => $heat_cloudwatch_api,
port => $ks_heat_cloudwatch_public_port,
bind_options => $heat_cloudwatch_bind_options,
options => $heat_cloudwatch_options
}
if $::osfamily == 'RedHat' {
$horizon_auth_url = 'dashboard'
} else {
$horizon_auth_url = 'horizon'
}
$horizon_ssl_options = {
'mode' => 'tcp',
'cookie' => 'sessionid prefix',
'balance' => 'leastconn'
}
if 'ssl' in $horizon_bind_options {
$horizon_options = {
'cookie' => 'sessionid prefix',
'reqadd' => 'X-Forwarded-Proto:\ https if { ssl_fc }',
'balance' => 'leastconn'
}
} else {
$horizon_options = {
'cookie' => 'sessionid prefix',
'balance' => 'leastconn'
}
}
cloud::loadbalancer::binding { 'horizon_cluster':
ip => $horizon,
port => $horizon_port,
httpchk => "httpchk GET /${horizon_auth_url} \"HTTP/1.0\\r\\nUser-Agent: HAproxy-${::hostname}\"",
options => $horizon_options,
bind_options => $horizon_bind_options,
}
cloud::loadbalancer::binding { 'horizon_ssl_cluster':
ip => $horizon_ssl,
port => $horizon_ssl_port,
httpchk => 'ssl-hello-chk',
options => $horizon_ssl_options,
bind_options => $horizon_ssl_bind_options,
}
if (member(any2array($keepalived_public_ipvs_real), $galera_ip)) {
warning('Exposing Galera cluster to public network is a security issue.')
}
haproxy::listen { 'galera_cluster':
ipaddress => $galera_ip,
ports => 3306,
options => {
'mode' => 'tcp',
'balance' => 'roundrobin',
'option' => ['tcpka', 'tcplog', 'httpchk'], #httpchk mandatory expect 200 on port 9000
'timeout client' => '400s',
'timeout server' => '400s',
},
bind_options => $galera_bind_options,
}
# Allow HAProxy to bind to a non-local IP address
$haproxy_sysctl_settings = {
'net.ipv4.ip_nonlocal_bind' => { value => 1 }
}
create_resources(sysctl::value,$haproxy_sysctl_settings)
}