From 32f1a697c4d25724415cf32db2397a53bd832dfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20M=C3=A1gr?= Date: Mon, 19 Jan 2015 14:03:14 +0100 Subject: [PATCH] Add common Pacemaker installer class Installs Pacemaker / Corosync cluster via puppetlabs-corosync module This path needs following puppetlabs-corosync PR to be functional: https://github.com/puppetlabs/puppetlabs-corosync/pull/106 Change-Id: Ib6e0f2f96a8563d19ac6c20e573d059a49e1853b --- manifests/clustering.pp | 151 ++++++++++++++++++++++++++ spec/classes/cloud_clustering_spec.rb | 149 +++++++++++++++++++++++++ 2 files changed, 300 insertions(+) create mode 100644 manifests/clustering.pp create mode 100644 spec/classes/cloud_clustering_spec.rb diff --git a/manifests/clustering.pp b/manifests/clustering.pp new file mode 100644 index 00000000..f5d6c3b9 --- /dev/null +++ b/manifests/clustering.pp @@ -0,0 +1,151 @@ +# +# Copyright (C) 2015 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: cloud::clustering +# +# Initialize Pacemaker / Corosync cluster +# +# === Parameters: +# +# [*cluster_members*] +# (required) Array of hostnames of cluster nodes +# +# [*cluster_ip*] +# (optional) IP address used by Corosync to send multicast traffic +# Defaults to '127.0.0.1' +# +# [*cluster_auth*] +# (optional) Controls corosync's ability to authenticate and encrypt +# multicast messages. +# Defaults to false +# +# [*cluster_authkey*] +# (optional) Specifies the path to the CA which is used to sign Corosync's +# certificate. +# Defaults to '/var/lib/puppet/ssl/certs/ca.pem' +# +# [*cluster_recheck_interval*] +# (optional) This tells the cluster to periodically recalculate the ideal +# state of the cluster. +# Defaults to 5min +# +# [*pe_warn_series_max*] +# (optional) The number of PE inputs resulting in WARNINGs to save. Used when +# reporting problems. +# Defaults to 1000 +# +# [*pe_input_series_max*] +# (optional) The number of "normal" PE inputs to save. Used when reporting +# problems. +# Defaults to 1000 +# +# [*pe_error_series_max*] +# (optional) The number of PE inputs resulting in ERRORs to save. Used when +# reporting problems. +# Defaults to 1000 +# +# [*multicast_address*] +# (optionnal) IP address used to send multicast traffic +# Defaults to '239.192.168.1' +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be a hash. +# Default to {} +# +class cloud::clustering ( + $cluster_members, + $cluster_ip = '127.0.0.1', + $cluster_auth = false, + $cluster_authkey = '/var/lib/puppet/ssl/certs/ca.pem', + $cluster_recheck_interval = '5min', + $pe_warn_series_max = 1000, + $pe_input_series_max = 1000, + $pe_error_series_max = 1000, + $multicast_address = '239.192.168.1', + $firewall_settings = {}, +) { + + if $::osfamily == 'RedHat' { + $packages = ['corosync', 'pacemaker', 'pcs'] + $set_votequorum = true + + Service['pcsd'] -> Cs_property<||> + Service['pacemaker'] -> Cs_property<||> + + service { 'pcsd': + ensure => 'running', + enable => true, + require => Class['corosync'], + } -> service { 'pacemaker': + ensure => 'running', + enable => true, + require => Class['corosync'], + } + } else { + $packages = ['corosync', 'pacemaker'] + $set_votequorum = false + } + + class { 'corosync': + enable_secauth => $cluster_auth, + authkey => $cluster_authkey, + bind_address => $cluster_ip, + multicast_address => $multicast_address, + packages => $packages, + set_votequorum => $set_votequorum, + quorum_members => $cluster_members, + } + + corosync::service { 'pacemaker': + version => '0', + } + + Package['corosync'] -> Cs_property<||> + cs_property { + # Doesn't work with pcs yet (Fedora20), but will work in future: + # -> https://github.com/feist/pcs/issues/20 + #'cluster-recheck-interval': value => $cluster_recheck_interval; + 'pe-warn-series-max': value => $pe_warn_series_max; + 'pe-input-series-max': value => $pe_input_series_max; + 'pe-error-series-max': value => $pe_error_series_max; + } + if count($cluster_members) < 3 { + # stonith is not required for less then 3 nodes, also quorum can be hold + # only with three or more nodes + cs_property { + 'no-quorum-policy': value => 'ignore'; + 'stonith-enabled': value => 'false'; + } + } + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow vrrp access': + port => undef, + proto => 'vrrp', + extras => $firewall_settings, + } + cloud::firewall::rule{ '100 allow corosync tcp access': + port => ['2224', '3121', '21064'], + extras => $firewall_settings, + } + cloud::firewall::rule{ '100 allow corosync udp access': + port => ['5404', '5405'], + proto => 'udp', + extras => $firewall_settings, + } + } +} diff --git a/spec/classes/cloud_clustering_spec.rb b/spec/classes/cloud_clustering_spec.rb new file mode 100644 index 00000000..871d764a --- /dev/null +++ b/spec/classes/cloud_clustering_spec.rb @@ -0,0 +1,149 @@ +# +# Copyright (C) 2015 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. +# +# Unit tests for cloud::clustering class +# + +require 'spec_helper' + +describe 'cloud::clustering' do + + let :pre_condition do + "class { 'cloud': + manage_firewall => true + }" + end + + let :params do + { :cluster_members => ['node1.test-example.org', + 'node2.test-example.org', + 'node3.test-example.org'], + :cluster_ip => '127.0.0.1', + :cluster_auth => false, + :cluster_authkey => '/var/lib/puppet/ssl/certs/ca.pem', + :cluster_recheck_interval => '5min', + :pe_warn_series_max => 1000, + :pe_input_series_max => 1000, + :pe_error_series_max => 1000, + :multicast_address => '239.192.168.1', + :firewall_settings => {} } + end + + shared_examples_for 'corosync and pacemaker' do + + context 'with default parameters' do + it 'configure corosync' do + is_expected.to contain_class('corosync').with( + :enable_secauth => params[:cluster_auth], + :authkey => params[:cluster_authkey], + :bind_address => params[:cluster_ip], + :multicast_address => params[:multicast_address], + :packages => platform_params[:packages], + #:set_votequorum => platform_params[:set_votequorum], + #:quorum_members => params[:cluster_members], + ) + + is_expected.to contain_cs_property('pe-warn-series-max').with( + :value => params[:pe_warn_series_max] + ) + is_expected.to contain_cs_property('pe-input-series-max').with( + :value => params[:pe_input_series_max] + ) + is_expected.to contain_cs_property('pe-error-series-max').with( + :value => params[:pe_error_series_max] + ) + + is_expected.to contain_corosync__service('pacemaker') + end + + it 'configure pacemaker firewall rules' do + is_expected.to contain_firewall('100 allow vrrp access').with( + :port => nil, + :proto => 'vrrp', + :action => 'accept', + ) + is_expected.to contain_firewall('100 allow corosync tcp access').with( + :port => ['2224', '3121', '21064'], + :action => 'accept', + ) + is_expected.to contain_firewall('100 allow corosync udp access').with( + :port => ['5404', '5405'], + :proto => 'udp', + :action => 'accept', + ) + end + end + + context 'with two nodes only' do + before :each do + params.merge!( + :cluster_members => ['node1', 'node2'] + ) + end + + it 'disables stonith and ignores votequorum errors' do + is_expected.to contain_cs_property('no-quorum-policy').with( + :value => 'ignore' + ) + is_expected.to contain_cs_property('stonith-enabled').with( + :value => 'false' + ) + end + end + end + + shared_examples_for 'specific resources for RH platforms' do + context 'with default parameters' do + it { should contain_service('pacemaker').with( + :ensure => 'running', + :enable => true, + :require => 'Class[Corosync]', + )} + + it { should contain_service('pcsd').with( + :ensure => 'running', + :enable => true, + :require => 'Class[Corosync]', + )} + end + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + let :platform_params do + { :set_votequorum => false, + :packages => ['corosync', 'pacemaker'] } + end + + it_configures 'corosync and pacemaker' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + let :platform_params do + { :set_votequorum => true, + :packages => ['corosync', 'pacemaker', 'pcs']} + end + + it_configures 'corosync and pacemaker' + it_configures 'specific resources for RH platforms' + end +end