Initial pcs 0.10 support

pcs 0.10 has changed a few things around cluster authentication
so we need to parametrize things depending on the pcs version on the
system.

We also make sure that if you use the cluster_members_addr parameter
it stays compatible with pcs 0.9 by only returning the node names
and not the nodenames + addr=... strings.

Tested this on a standalone env with pcs 0.10 (rhel8os + f28 containers
no pre-installed pcs rpm) and on an OSP14 using pcs 0.9

Change-Id: I8693cc0fdd51be155273e0dba8bc14b3db79ba8d
This commit is contained in:
Michele Baldessari 2018-12-18 10:39:55 +01:00
parent 584d478314
commit c7b21ee0cb
5 changed files with 112 additions and 47 deletions

View File

@ -1,3 +1,5 @@
require_relative '../../provider/pcmk_common'
module Puppet::Parser::Functions
newfunction(
:pcmk_cluster_setup,
@ -8,6 +10,7 @@ Input data cluster_members string separated by a space:
* String A space-separated string containing a list of node names
* String A list containing either a single string (single ip) or a list of strings
(multiple ipaddresses) associated to each cluster node
* String the version of pcs used
Output forms:
* string - Output A string to be used in the cluster setup call to pcs
@ -15,35 +18,45 @@ Output forms:
) do |args|
nodes = args[0]
addr_list = args[1]
pcs_version = args[2]
fail "pcmk_cluster_setup: Got unsupported nodes input data: #{nodes.inspect}" if not nodes.is_a? String
fail "pcmk_cluster_setup: Got unsupported addr_list input data: #{addr_list.inspect}" if not addr_list.is_a? Array
fail "pcmk_cluster_setup: Got unsupported version input data: #{pcs_version.inspect}" if not pcs_version.is_a? String
node_list = nodes.split()
fail "pcmk_cluster_setup: node list and addr list should be of the same size when defined and not empty" if addr_list.size > 0 and addr_list.size != node_list.size
# If the addr_list was specified we need to return a string in the form of
# node1 addr=1.2.3.4 node2 addr=1.2.3.5 addr=1.2.3.6 node3 addr=1.2.3.7
if addr_list.size > 0
ret = ''
node_list.zip(addr_list).each do |node, ip|
# addr can be '1.2.3.4' or ['1.2.3.4', '1.2.3.5'] or
if ip.is_a? String
addr = "addr=#{ip}"
elsif ip.is_a? Array
addr = ''
ip.each do |i|
addr += "addr=#{i}"
addr += " " if not i.equal?(ip.last)
# pcs 0.10 supports knet clusters which require addresses to be specified
if pcs_version =~ /0.10/
# If the addr_list was specified we need to return a string in the form of
# node1 addr=1.2.3.4 node2 addr=1.2.3.5 addr=1.2.3.6 node3 addr=1.2.3.7
if addr_list.size > 0
ret = ''
node_list.zip(addr_list).each do |node, ip|
# addr can be '1.2.3.4' or ['1.2.3.4', '1.2.3.5'] or
if ip.is_a? String
addr = "addr=#{ip}"
elsif ip.is_a? Array
addr = ''
ip.each do |i|
addr += "addr=#{i}"
addr += " " if not i.equal?(ip.last)
end
else
fail "pcmk_cluster_setup: One of the addresses in addr_list is neither a String nor an Array"
end
else
fail "pcmk_cluster_setup: One of the addresses in addr_list is neither a String nor an Array"
ret += "#{node} #{addr}"
ret += " " if not node.equal?(node_list.last)
end
ret += "#{node} #{addr}"
ret += " " if not node.equal?(node_list.last)
# only node_list is specified so we just return the original string
else
ret = nodes.strip()
end
# only node_list is specified so we just return the original string
ret
elsif pcs_version =~ /0.9/
# With pcs 0.9 only non-knet clusters are supported, aka only one address can be used
# so we take the node name as we always did
nodes.strip()
else
ret = nodes.strip()
fail("pcmk_cluster_setup: pcs #{pcs_version} is unsupported")
end
ret
end
end

View File

@ -26,6 +26,19 @@ else
TIMEOUT_BIN = '/usr/bin/timeout'
end
# Use pcs_cli_version() as opposed to a facter so that if the pcs
# package gets installed during the puppet run everything still works
# as expected. Returns empty string if pcs command does not exist
def pcs_cli_version()
begin
pcs_cli_version = `#{PCS_BIN} --version`
rescue Errno::ENOENT
pcs_cli_version = ''
end
return pcs_cli_version
end
# Ruby 2.5 has dropped Dir::Tmpname.make_tmpname
# https://github.com/ruby/ruby/commit/25d56ea7b7b52dc81af30c92a9a0e2d2dab6ff27
def pcmk_tmpname((prefix, suffix), n)

View File

@ -114,6 +114,16 @@ class pacemaker::corosync(
$pcsd_debug = false,
) inherits pacemaker {
include ::pacemaker::params
if ! $cluster_members_rrp {
if $::pacemaker::pcs_010 {
$cluster_members_rrp_real = pcmk_cluster_setup($cluster_members, $cluster_members_addr, '0.10')
} else {
$cluster_members_rrp_real = pcmk_cluster_setup($cluster_members, $cluster_members_addr, '0.9')
}
} else {
$cluster_members_rrp_real = $cluster_members_rrp
}
$cluster_setup_extras_real = inline_template('<%= @cluster_setup_extras.flatten.join(" ") %>')
if $manage_fw {
firewall { '001 corosync mcast':
@ -162,8 +172,22 @@ class pacemaker::corosync(
notify => Exec['reauthenticate-across-all-nodes'],
}
# pcs-0.10.x has different commands to set up the cluster
if $::pacemaker::pcs_010 {
$cluster_setup_cmd = "${::pacemaker::pcs_bin} cluster setup ${cluster_name} ${cluster_members_rrp_real}"
$cluster_reauthenticate_cmd = "${::pacemaker::pcs_bin} host auth ${cluster_members} -u hacluster -p ${::pacemaker::hacluster_pwd}"
$cluster_authenticate_cmd = "${::pacemaker::pcs_bin} host auth ${cluster_members} -u hacluster -p ${::pacemaker::hacluster_pwd}"
$cluster_authenticate_unless = "${::pacemaker::pcs_bin} host auth ${cluster_members} -u hacluster -p ${::pacemaker::hacluster_pwd} | grep 'Already authorized'"
} else {
$cluster_setup_cmd = "${::pacemaker::pcs_bin} cluster setup --wait --name ${cluster_name} ${cluster_members_rrp_real} ${cluster_setup_extras_real}"
$cluster_reauthenticate_cmd = "${::pacemaker::pcs_bin} cluster auth ${cluster_members} -u hacluster -p ${::pacemaker::hacluster_pwd} --force"
$cluster_authenticate_cmd = "${::pacemaker::pcs_bin} cluster auth ${cluster_members} -u hacluster -p ${::pacemaker::hacluster_pwd}"
$cluster_authenticate_unless = "${::pacemaker::pcs_bin} cluster auth ${cluster_members} -u hacluster -p ${::pacemaker::hacluster_pwd} | grep 'Already authorized'"
}
exec { 'reauthenticate-across-all-nodes':
command => "${::pacemaker::pcs_bin} cluster auth ${cluster_members} -u hacluster -p ${::pacemaker::hacluster_pwd} --force",
command => $cluster_reauthenticate_cmd,
refreshonly => true,
timeout => $settle_timeout,
tries => $settle_tries,
@ -173,13 +197,13 @@ class pacemaker::corosync(
}
exec { 'auth-successful-across-all-nodes':
command => "${::pacemaker::pcs_bin} cluster auth ${cluster_members} -u hacluster -p ${::pacemaker::hacluster_pwd}",
command => $cluster_authenticate_cmd,
refreshonly => true,
timeout => $settle_timeout,
tries => $settle_tries,
try_sleep => $settle_try_sleep,
require => [Service['pcsd'], User['hacluster']],
unless => "${::pacemaker::pcs_bin} cluster auth ${cluster_members} -u hacluster -p ${::pacemaker::hacluster_pwd} | grep 'Already authorized'",
unless => $cluster_authenticate_unless,
tag => 'pacemaker-auth',
}
@ -216,18 +240,11 @@ class pacemaker::corosync(
Exec <|tag == 'pacemaker-auth'|> -> Exec <|tag == 'pacemaker-scaleup'|>
}
if ! $cluster_members_rrp {
$cluster_members_rrp_real = pcmk_cluster_setup($cluster_members, $cluster_members_addr)
} else {
$cluster_members_rrp_real = $cluster_members_rrp
}
$cluster_setup_extras_real = inline_template('<%= @cluster_setup_extras.flatten.join(" ") %>')
Exec <|tag == 'pacemaker-auth'|>
->
exec {"Create Cluster ${cluster_name}":
creates => '/etc/cluster/cluster.conf',
command => "${::pacemaker::pcs_bin} cluster setup --wait --name ${cluster_name} ${cluster_members_rrp_real} ${cluster_setup_extras_real}",
command => $cluster_setup_cmd,
timeout => $cluster_start_timeout,
tries => $cluster_start_tries,
try_sleep => $cluster_start_try_sleep,

View File

@ -50,6 +50,12 @@ class pacemaker::params {
} else {
$pcmk_remote_package_list = ['pacemaker','pcs','fence-agents-all','pacemaker-libs', 'pacemaker-remote']
}
# Detect pcs 0.10.x versions and use different commands
if $::operatingsystemrelease =~ /8\..*$/ {
$pcs_010 = true
} else {
$pcs_010 = false
}
$service_name = 'pacemaker'
}
default: {

View File

@ -2,24 +2,40 @@ require 'spec_helper'
describe 'pcmk_cluster_setup' do
context 'interface' do
it { is_expected.to run.with_params(123,[]).and_raise_error(Puppet::Error, /pcmk_cluster_setup: Got unsupported nodes input data/) }
it { is_expected.to run.with_params('foo', 'bar').and_raise_error(Puppet::Error, /pcmk_cluster_setup: Got unsupported addr_list input data/) }
it { is_expected.to run.with_params('n1 n2 n3', ['1', '2']).and_raise_error(Puppet::Error, /pcmk_cluster_setup: node list and addr list should be of the same size when defined and not empty/) }
it { is_expected.to run.with_params('n1 n2 n3', ['1', ['2'], nil]).and_raise_error(Puppet::Error, /pcmk_cluster_setup: One of the addresses in addr_list is neither a String nor an Array/) }
it { is_expected.to run.with_params('n1', [], []).and_raise_error(Puppet::Error, /pcmk_cluster_setup: Got unsupported version input data/) }
it { is_expected.to run.with_params(123,[], '0.9').and_raise_error(Puppet::Error, /pcmk_cluster_setup: Got unsupported nodes input data/) }
it { is_expected.to run.with_params('foo', 'bar', '0.9').and_raise_error(Puppet::Error, /pcmk_cluster_setup: Got unsupported addr_list input data/) }
it { is_expected.to run.with_params('n1 n2 n3', ['1', '2'], '0.9').and_raise_error(Puppet::Error, /pcmk_cluster_setup: node list and addr list should be of the same size when defined and not empty/) }
it { is_expected.to run.with_params(123,[], '0.10').and_raise_error(Puppet::Error, /pcmk_cluster_setup: Got unsupported nodes input data/) }
it { is_expected.to run.with_params('foo', 'bar', '0.10').and_raise_error(Puppet::Error, /pcmk_cluster_setup: Got unsupported addr_list input data/) }
it { is_expected.to run.with_params('n1 n2 n3', ['1', '2'], '0.10').and_raise_error(Puppet::Error, /pcmk_cluster_setup: node list and addr list should be of the same size when defined and not empty/) }
it { is_expected.to run.with_params('n1 n2 n3', ['1', ['2'], nil], '0.10').and_raise_error(Puppet::Error, /pcmk_cluster_setup: One of the addresses in addr_list is neither a String nor an Array/) }
end
it 'returns the original node string when no addresses are specified' do
is_expected.to run.with_params('n1 n2 n3', []).and_return('n1 n2 n3')
is_expected.to run.with_params('n1 n2', []).and_return('n1 n2')
is_expected.to run.with_params('n1', []).and_return('n1')
is_expected.to run.with_params('n1 ', []).and_return('n1')
it 'returns the original node string when no addresses are specified with pcs 0.10' do
is_expected.to run.with_params('n1 n2 n3', [], '0.10').and_return('n1 n2 n3')
is_expected.to run.with_params('n1 n2', [], '0.10').and_return('n1 n2')
is_expected.to run.with_params('n1', [], '0.10').and_return('n1')
is_expected.to run.with_params('n1 ', [], '0.10').and_return('n1')
end
it 'returns the correct cluster setup cmd given both nodes and ip address' do
is_expected.to run.with_params('ctr-0 ctr-1 ctr-2', ['1.1.1.1', '2.2.2.2', '3.3.3.3']).and_return('ctr-0 addr=1.1.1.1 ctr-1 addr=2.2.2.2 ctr-2 addr=3.3.3.3')
is_expected.to run.with_params('ctr-0 ctr-1', ['1.1.1.1', ['2.2.2.2', '3.3.3.3']]).and_return('ctr-0 addr=1.1.1.1 ctr-1 addr=2.2.2.2 addr=3.3.3.3')
is_expected.to run.with_params('ctr-0', [['2.2.2.2', '3.3.3.3', '4.4.4.4']]).and_return('ctr-0 addr=2.2.2.2 addr=3.3.3.3 addr=4.4.4.4')
is_expected.to run.with_params('ctr-0 ctr-1 ctr-2', [['1.1.1.1'], ['2.2.2.2'], '3.3.3.3']).and_return('ctr-0 addr=1.1.1.1 ctr-1 addr=2.2.2.2 ctr-2 addr=3.3.3.3')
is_expected.to run.with_params('ctr-0 ctr-1 ctr-2', [['1fe80::7ed:a95d:ed26:f5b', 'fe80::7ed:a95d:ed26:f5c', 'fe80::7ed:a95d:ed26:f5d'], ['1.1.1.1', '2.2.2.2'], '3.3.3.3']).and_return('ctr-0 addr=1fe80::7ed:a95d:ed26:f5b addr=fe80::7ed:a95d:ed26:f5c addr=fe80::7ed:a95d:ed26:f5d ctr-1 addr=1.1.1.1 addr=2.2.2.2 ctr-2 addr=3.3.3.3')
it 'returns the original node string when no addresses are specified with pcs 0.9' do
is_expected.to run.with_params('n1 n2 n3', [], '0.9').and_return('n1 n2 n3')
is_expected.to run.with_params('n1 n2', [], '0.9').and_return('n1 n2')
is_expected.to run.with_params('n1', [], '0.9').and_return('n1')
is_expected.to run.with_params('n1 ', [], '0.9').and_return('n1')
end
it 'returns the correct cluster setup cmd given both nodes and ip address with pcs 0.10' do
is_expected.to run.with_params('ctr-0 ctr-1 ctr-2', ['1.1.1.1', '2.2.2.2', '3.3.3.3'], '0.10').and_return('ctr-0 addr=1.1.1.1 ctr-1 addr=2.2.2.2 ctr-2 addr=3.3.3.3')
is_expected.to run.with_params('ctr-0 ctr-1', ['1.1.1.1', ['2.2.2.2', '3.3.3.3']], '0.10').and_return('ctr-0 addr=1.1.1.1 ctr-1 addr=2.2.2.2 addr=3.3.3.3')
is_expected.to run.with_params('ctr-0', [['2.2.2.2', '3.3.3.3', '4.4.4.4']], '0.10').and_return('ctr-0 addr=2.2.2.2 addr=3.3.3.3 addr=4.4.4.4')
is_expected.to run.with_params('ctr-0 ctr-1 ctr-2', [['1.1.1.1'], ['2.2.2.2'], '3.3.3.3'], '0.10').and_return('ctr-0 addr=1.1.1.1 ctr-1 addr=2.2.2.2 ctr-2 addr=3.3.3.3')
is_expected.to run.with_params('ctr-0 ctr-1 ctr-2', [['1fe80::7ed:a95d:ed26:f5b', 'fe80::7ed:a95d:ed26:f5c', 'fe80::7ed:a95d:ed26:f5d'], ['1.1.1.1', '2.2.2.2'], '3.3.3.3'], '0.10').and_return('ctr-0 addr=1fe80::7ed:a95d:ed26:f5b addr=fe80::7ed:a95d:ed26:f5c addr=fe80::7ed:a95d:ed26:f5d ctr-1 addr=1.1.1.1 addr=2.2.2.2 ctr-2 addr=3.3.3.3')
end
it 'returns the correct cluster setup cmd given both nodes and ip address with pcs 0.9' do
is_expected.to run.with_params('ctr-0 ctr-1 ctr-2', ['1.1.1.1', '2.2.2.2', '3.3.3.3'], '0.9').and_return('ctr-0 ctr-1 ctr-2')
is_expected.to run.with_params('ctr-0 ctr-1 ctr-2', [['1fe80::7ed:a95d:ed26:f5b', 'fe80::7ed:a95d:ed26:f5c', 'fe80::7ed:a95d:ed26:f5d'], ['1.1.1.1', '2.2.2.2'], '3.3.3.3'], '0.9').and_return('ctr-0 ctr-1 ctr-2')
end
end