Fixes for pcmk_nodes and crm_node
* Use crm_node -n to get node's hostname * Check if node is in cluster in service provider * implement pacemaker nodes add in pcmk_nodes * compatibility between fqdn and hostname in pcmk_nodes * fix new node id and number generation in pcmk_nodes Change-Id: I2e40e25df975621506141dfe78efa884255f1642 Related-Bug: 1451795
This commit is contained in:
parent
9ba57d1728
commit
3b01fecf2e
|
@ -4,7 +4,7 @@
|
|||
#
|
||||
class cluster (
|
||||
$internal_address = '127.0.0.1',
|
||||
$unicast_addresses = undef,
|
||||
$corosync_nodes = undef,
|
||||
) {
|
||||
|
||||
#todo: move half of openstack::corosync
|
||||
|
@ -13,7 +13,7 @@ class cluster (
|
|||
if defined(Stage['corosync_setup']) {
|
||||
class { 'openstack::corosync':
|
||||
bind_address => $internal_address,
|
||||
unicast_addresses => $unicast_addresses,
|
||||
corosync_nodes => $corosync_nodes,
|
||||
stage => 'corosync_setup',
|
||||
corosync_version => '2',
|
||||
packages => ['corosync', 'pacemaker', 'crmsh', 'pcs'],
|
||||
|
@ -21,7 +21,7 @@ class cluster (
|
|||
} else {
|
||||
class { 'openstack::corosync':
|
||||
bind_address => $internal_address,
|
||||
unicast_addresses => $unicast_addresses,
|
||||
corosync_nodes => $corosync_nodes,
|
||||
corosync_version => '2',
|
||||
packages => ['corosync', 'pacemaker', 'crmsh', 'pcs'],
|
||||
}
|
||||
|
|
|
@ -38,11 +38,8 @@
|
|||
# Use 'broadcast' to have broadcast instead
|
||||
# Can be specified as an array to have multiple rings (multicast only).
|
||||
#
|
||||
# [*unicast_addresses*]
|
||||
# An array of IP addresses that make up the cluster's members. These are
|
||||
# use if you are able to use multicast on your network and instead opt for
|
||||
# the udpu transport. You need a relatively recent version of Corosync to
|
||||
# make this possible.
|
||||
# [*corosync_nodes*]
|
||||
# { node_name => { 'ip' => '...', 'id' => '...' }}
|
||||
#
|
||||
# [*force_online*]
|
||||
# True/false parameter specifying whether to force nodes that have been put
|
||||
|
@ -89,7 +86,7 @@ class corosync(
|
|||
$port = $::corosync::params::port,
|
||||
$bind_address = $::corosync::params::bind_address,
|
||||
$multicast_address = $::corosync::params::multicast_address,
|
||||
$unicast_addresses = $::corosync::params::unicast_addresses,
|
||||
$corosync_nodes = $::corosync::params::corosync_nodes,
|
||||
$force_online = $::corosync::params::force_online,
|
||||
$check_standby = $::corosync::params::check_standby,
|
||||
$debug = $::corosync::params::debug,
|
||||
|
@ -131,14 +128,14 @@ class corosync(
|
|||
default => $bind_address,
|
||||
}
|
||||
|
||||
if $unicast_addresses == 'UNSET' {
|
||||
if $corosync_nodes == 'UNSET' {
|
||||
$corosync_conf = "${module_name}/corosync.conf.erb"
|
||||
} else {
|
||||
$corosync_conf = "${module_name}/corosync.conf.udpu.erb"
|
||||
}
|
||||
|
||||
# $multicast_address is NOT required if $unicast_address is provided
|
||||
if $multicast_address == 'UNSET' and $unicast_addresses == 'UNSET' {
|
||||
if $multicast_address == 'UNSET' and $corosync_nodes == 'UNSET' {
|
||||
fail('You must provide a value for multicast_address')
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ class corosync::params {
|
|||
$port = '5405'
|
||||
$bind_address = $::ipaddress
|
||||
$multicast_address = 'UNSET'
|
||||
$unicast_addresses = 'UNSET'
|
||||
$corosync_nodes = 'UNSET'
|
||||
$force_online = false
|
||||
$check_standby = false
|
||||
$debug = false
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
<% if @corosync_version == '2' %>
|
||||
<% if @corosync_version == '2' -%>
|
||||
compatibility: whitetank
|
||||
|
||||
quorum {
|
||||
provider: corosync_votequorum
|
||||
<% if @unicast_addresses.length == 2 %>
|
||||
<% if @corosync_nodes.length == 2 -%>
|
||||
two_node: 1
|
||||
<% else %>
|
||||
<% else -%>
|
||||
two_node: 0
|
||||
<% end %>
|
||||
<% end -%>
|
||||
}
|
||||
|
||||
nodelist {
|
||||
<% id = 0 %>
|
||||
<% @unicast_addresses.each do |node| %>
|
||||
<% @corosync_nodes.each do |name, node| -%>
|
||||
node {
|
||||
ring0_addr: <%= node %>
|
||||
nodeid: <%= id+=1 %>
|
||||
# <%= name %>
|
||||
ring0_addr: <%= node['ip'] %>
|
||||
nodeid: <%= node['id'] %>
|
||||
}
|
||||
<% end %>
|
||||
<% end -%>
|
||||
}
|
||||
<% end %>
|
||||
<% end -%>
|
||||
|
||||
totem {
|
||||
version: 2
|
||||
|
@ -58,7 +58,7 @@ logging {
|
|||
to_syslog: yes
|
||||
syslog_facility: daemon
|
||||
syslog_priority: info
|
||||
debug: <%= scope.lookupvar('debug') ? 'on' : 'off' %>
|
||||
debug: <%= @debug ? 'on' : 'off' %>
|
||||
function_name: on
|
||||
timestamp: on
|
||||
logger_subsys {
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
<% if @corosync_version == '2' %>
|
||||
<% if @corosync_version == '2' -%>
|
||||
compatibility: whitetank
|
||||
|
||||
quorum {
|
||||
provider: corosync_votequorum
|
||||
<% if @unicast_addresses.length == 2 %>
|
||||
<% if @corosync_nodes.length == 2 -%>
|
||||
two_node: 1
|
||||
<% else %>
|
||||
<% else -%>
|
||||
two_node: 0
|
||||
<% end %>
|
||||
<% end -%>
|
||||
}
|
||||
|
||||
nodelist {
|
||||
<% id = 0 %>
|
||||
<% @unicast_addresses.each do |node| %>
|
||||
<% @corosync_nodes.each do |name, node| -%>
|
||||
node {
|
||||
ring0_addr: <%= node %>
|
||||
nodeid: <%= id+=1 %>
|
||||
# <%= name %>
|
||||
ring0_addr: <%= node['ip'] %>
|
||||
nodeid: <%= node['id'] %>
|
||||
}
|
||||
<% end %>
|
||||
<% end -%>
|
||||
}
|
||||
<% end %>
|
||||
<% end -%>
|
||||
|
||||
totem {
|
||||
version: 2
|
||||
|
@ -35,11 +35,6 @@ totem {
|
|||
threads: <%= @threads_real %>
|
||||
transport: udpu
|
||||
interface {
|
||||
<% @unicast_addresses.each do |addr| -%>
|
||||
member {
|
||||
memberaddr: <%= addr %>
|
||||
}
|
||||
<% end -%>
|
||||
ringnumber: 0
|
||||
bindnetaddr: <%= @bind_address_real %>
|
||||
mcastport: <%= @port_real %>
|
||||
|
@ -57,7 +52,7 @@ logging {
|
|||
to_syslog: yes
|
||||
syslog_facility: daemon
|
||||
syslog_priority: info
|
||||
debug: <%= scope.lookupvar('debug') ? 'on' : 'off' %>
|
||||
debug: <%= @debug ? 'on' : 'off' %>
|
||||
function_name: on
|
||||
timestamp: on
|
||||
logger_subsys {
|
||||
|
|
|
@ -5,7 +5,7 @@ class openstack::corosync (
|
|||
$stonith = false,
|
||||
$quorum_policy = 'ignore',
|
||||
$expected_quorum_votes = '2',
|
||||
$unicast_addresses = undef,
|
||||
$corosync_nodes = undef,
|
||||
$corosync_version = '1',
|
||||
$packages = ['corosync', 'pacemaker'],
|
||||
) {
|
||||
|
@ -52,7 +52,7 @@ class openstack::corosync (
|
|||
enable_secauth => $secauth,
|
||||
bind_address => $bind_address,
|
||||
multicast_address => $multicast_address,
|
||||
unicast_addresses => $unicast_addresses,
|
||||
corosync_nodes => $corosync_nodes,
|
||||
corosync_version => $corosync_version,
|
||||
packages => $packages,
|
||||
# NOTE(bogdando) debug is *too* verbose
|
||||
|
|
|
@ -10,10 +10,14 @@ to be used in pcmk_nodes resource.
|
|||
nodes.each do |node|
|
||||
fqdn = node['fqdn']
|
||||
ip = node['internal_address']
|
||||
uid = node['uid']
|
||||
role = node['role']
|
||||
next unless %w(primary-controller controller).include? role
|
||||
next unless ip and fqdn
|
||||
corosync_nodes[fqdn] = ip
|
||||
corosync_nodes[fqdn] = {
|
||||
'id' => uid,
|
||||
'ip' => ip,
|
||||
}
|
||||
end
|
||||
corosync_nodes
|
||||
end
|
||||
|
|
|
@ -2,14 +2,22 @@ notice('MODULAR: cluster.pp')
|
|||
|
||||
$nodes = hiera('nodes')
|
||||
$corosync_nodes = corosync_nodes($nodes)
|
||||
$internal_address = hiera('internal_address')
|
||||
|
||||
class { '::cluster':
|
||||
internal_address => hiera('internal_address'),
|
||||
unicast_addresses => ipsort(values($corosync_nodes)),
|
||||
class { 'cluster':
|
||||
internal_address => $internal_address,
|
||||
corosync_nodes => $corosync_nodes,
|
||||
}
|
||||
|
||||
pcmk_nodes { 'pacemaker' :
|
||||
nodes => $corosync_nodes,
|
||||
add_pacemaker_nodes => false,
|
||||
}
|
||||
|
||||
Service <| title == 'corosync' |> {
|
||||
subscribe => File['/etc/corosync/service.d'],
|
||||
require => File['/etc/corosync/corosync.conf'],
|
||||
}
|
||||
|
||||
Service['corosync'] -> Pcmk_nodes<||>
|
||||
Pcmk_nodes<||> -> Service<| provider == 'pacemaker' |>
|
||||
|
|
|
@ -38,8 +38,14 @@ describe 'the corosync_nodes function' do
|
|||
|
||||
let(:corosync_nodes_hash) do
|
||||
{
|
||||
"node-1.domain.tld" => "192.168.0.5",
|
||||
"node-2.domain.tld" => "192.168.0.6",
|
||||
"node-1.domain.tld" => {
|
||||
"ip" => "192.168.0.5",
|
||||
"id" => "1",
|
||||
},
|
||||
"node-2.domain.tld" => {
|
||||
"ip" => "192.168.0.6",
|
||||
"id" => "2",
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -276,7 +276,7 @@ class Puppet::Provider::Pacemaker_common < Puppet::Provider
|
|||
cib_section_node_ids.each do |node_block|
|
||||
node = attributes_to_hash node_block
|
||||
next unless node['id'] and node['uname']
|
||||
@node_ids.store node['uname'], node['id']
|
||||
@node_ids.store node['uname'], node
|
||||
end
|
||||
@node_ids
|
||||
end
|
||||
|
|
|
@ -4,6 +4,13 @@ Puppet::Type.type(:pcmk_nodes).provide(:ruby, :parent => Puppet::Provider::Pacem
|
|||
|
||||
commands 'cmapctl' => '/usr/sbin/corosync-cmapctl'
|
||||
commands 'cibadmin' => '/usr/sbin/cibadmin'
|
||||
commands 'crm_node' => '/usr/sbin/crm_node'
|
||||
commands 'crm_attribute' => '/usr/sbin/crm_attribute'
|
||||
|
||||
def node_name
|
||||
return @node_name if @node_name
|
||||
@node_name = crm_node('-n').chomp.strip
|
||||
end
|
||||
|
||||
def cmapctl_nodelist
|
||||
cmapctl '-b', 'nodelist.node'
|
||||
|
@ -14,7 +21,11 @@ Puppet::Type.type(:pcmk_nodes).provide(:ruby, :parent => Puppet::Provider::Pacem
|
|||
debug (['cmapctl'] + args).join ' '
|
||||
return
|
||||
end
|
||||
cmapctl *args
|
||||
begin
|
||||
cmapctl *args
|
||||
rescue => e
|
||||
info "Command failed: #{e.message}"
|
||||
end
|
||||
end
|
||||
|
||||
def cibadmin_safe(*args)
|
||||
|
@ -22,106 +33,201 @@ Puppet::Type.type(:pcmk_nodes).provide(:ruby, :parent => Puppet::Provider::Pacem
|
|||
debug (['cibadmin'] + args).join ' '
|
||||
return
|
||||
end
|
||||
cibadmin *args
|
||||
begin
|
||||
cibadmin *args
|
||||
rescue => e
|
||||
info "Command failed: #{e.message}"
|
||||
end
|
||||
end
|
||||
|
||||
###################################
|
||||
|
||||
def corosync_nodes_structure
|
||||
return @corosync_nodes_structure if @corosync_nodes_structure
|
||||
nodes = {}
|
||||
cmapctl_nodelist.split("\n").each do |line|
|
||||
if line =~ %r(^nodelist\.node\.(\d+)\.nodeid\s+\(u32\)\s+=\s+(\d+))
|
||||
node_number = $1
|
||||
node_nodeid = $2
|
||||
nodes[node_number] = {} unless nodes[node_number]
|
||||
nodes[node_number]['id'] = node_nodeid
|
||||
nodes[node_number]['number'] = node_number
|
||||
node_name = pacemaker_node_id_to_name node_nodeid
|
||||
nodes[node_number]['uname'] = node_name if node_name
|
||||
end
|
||||
if line =~ %r(^nodelist\.node\.(\d+)\.ring(\d+)_addr\s+\(str\)\s+=\s+(\S+))
|
||||
node_number = $1
|
||||
node_ring_number = $2
|
||||
node_ip_addr = $3
|
||||
nodes[node_number] = {} unless nodes[node_number]
|
||||
ring = "ring#{node_ring_number}_addr"
|
||||
nodes[node_number][ring] = node_ip_addr
|
||||
end
|
||||
end
|
||||
@corosync_nodes_structure = {}
|
||||
nodes.each do |number, node|
|
||||
name = node['uname']
|
||||
next unless name
|
||||
@corosync_nodes_structure.store name, node
|
||||
end
|
||||
@corosync_nodes_structure
|
||||
end
|
||||
|
||||
def pacemaker_nodes_structure
|
||||
node_ids
|
||||
end
|
||||
|
||||
def pacemaker_node_id_to_name(id)
|
||||
pacemaker_nodes_structure.invert[id]
|
||||
end
|
||||
|
||||
def highest_corosync_node_number
|
||||
corosync_nodes_structure.inject(0) do |max, node|
|
||||
number = node.last['number'].to_i
|
||||
max = number if number > max
|
||||
max
|
||||
end
|
||||
end
|
||||
|
||||
def highest_pacemaker_node_id
|
||||
pacemaker_nodes_structure.inject(0) do |max, node|
|
||||
number = node.last.to_i
|
||||
max = number if number > max
|
||||
max
|
||||
end
|
||||
end
|
||||
|
||||
def nodes_data
|
||||
@resource[:nodes]
|
||||
end
|
||||
|
||||
def corosync_nodes_data
|
||||
@resource[:corosync_nodes]
|
||||
end
|
||||
|
||||
def pacemaker_nodes_data
|
||||
@resource[:pacemaker_nodes]
|
||||
end
|
||||
|
||||
###################################
|
||||
|
||||
def corosync_nodes_state
|
||||
return @corosync_nodes_data if @corosync_nodes_data
|
||||
@corosync_nodes_data = {}
|
||||
cmapctl_nodelist.split("\n").each do |line|
|
||||
if line =~ %r(^nodelist\.node\.(\d+)\.nodeid\s+\(u32\)\s+=\s+(\d+))
|
||||
node_number = $1
|
||||
node_id = $2
|
||||
@corosync_nodes_data[node_number] = {} unless @corosync_nodes_data[node_number]
|
||||
@corosync_nodes_data[node_number]['id'] = node_id
|
||||
@corosync_nodes_data[node_number]['number'] = node_number
|
||||
end
|
||||
if line =~ %r(^nodelist\.node\.(\d+)\.ring(\d+)_addr\s+\(str\)\s+=\s+(\S+))
|
||||
node_number = $1
|
||||
node_ip_addr = $3
|
||||
@corosync_nodes_data[node_number] = {} unless @corosync_nodes_data[node_number]
|
||||
@corosync_nodes_data[node_number]['ip'] = node_ip_addr
|
||||
end
|
||||
end
|
||||
@corosync_nodes_data
|
||||
end
|
||||
|
||||
def corosync_nodes_structure
|
||||
return @corosync_nodes_structure if @corosync_nodes_structure
|
||||
@corosync_nodes_structure = {}
|
||||
corosync_nodes_state.each do |number, node|
|
||||
id = node['id']
|
||||
ip = node['ip']
|
||||
next unless id and ip
|
||||
@corosync_nodes_structure.store id, ip
|
||||
end
|
||||
@corosync_nodes_structure
|
||||
end
|
||||
|
||||
def pcmk_nodes_reset
|
||||
@corosync_nodes_structure = nil
|
||||
@corosync_nodes_data = nil
|
||||
@pacemaker_nodes_structure = nil
|
||||
@node_name = nil
|
||||
end
|
||||
|
||||
###
|
||||
|
||||
def change_fqdn_to_name?
|
||||
begin
|
||||
return false if nodes_data.keys.include? node_name
|
||||
return true if nodes_data.keys.map { |fqdn| fqdn.split('.').first }.include? node_name
|
||||
false
|
||||
rescue
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def change_fqdn_to_name
|
||||
debug 'Changing Pacemaker node names from FQDNs to Hostnames'
|
||||
nodes = {}
|
||||
@resource[:nodes].each do |fqdn, ip|
|
||||
name = fqdn.split('.').first
|
||||
nodes.store name, ip
|
||||
end
|
||||
@resource[:nodes] = nodes
|
||||
@resource.set_corosync_nodes
|
||||
@resource.set_pacemaker_nodes
|
||||
pcmk_nodes_reset
|
||||
end
|
||||
|
||||
###
|
||||
|
||||
def pacemaker_nodes_structure
|
||||
@pacemaker_nodes_structure = {}
|
||||
node_ids.each do |name, node|
|
||||
id = node['id']
|
||||
next unless name and id
|
||||
@pacemaker_nodes_structure.store name, id
|
||||
end
|
||||
@pacemaker_nodes_structure
|
||||
end
|
||||
|
||||
def next_corosync_node_number
|
||||
number = corosync_nodes_state.inject(0) do |max, node|
|
||||
number = node.last['number'].to_i
|
||||
max = number if number > max
|
||||
max
|
||||
end
|
||||
number += 1
|
||||
number.to_s
|
||||
end
|
||||
|
||||
def remove_pacemaker_node(node_name)
|
||||
debug "Remove pacemaker node: '#{node_name}'"
|
||||
remove_pacemaker_node_record node_name
|
||||
remove_pacemaker_node_state node_name
|
||||
purge_node_locations node_name
|
||||
end
|
||||
|
||||
def add_pacemaker_node(node_name)
|
||||
debug "Add pacemaker node: '#{node_name}'"
|
||||
node_id = nodes_data.fetch(node_name, {}).fetch 'id', nil
|
||||
fail "Could not get all the data for the new pacemaker node '#{node_name}'!" unless node_id
|
||||
add_pacemaker_node_record node_name, node_id
|
||||
add_pacemaker_node_state node_name, node_id
|
||||
end
|
||||
|
||||
def remove_pacemaker_node_record(node_name)
|
||||
cibadmin_safe '--delete', '--obj_type', 'nodes', '--crm_xml', "<node uname='#{node_name}'/>"
|
||||
cibadmin_safe '--delete', '--scope', 'nodes', '--xml-text', "<node uname='#{node_name}'/>"
|
||||
end
|
||||
|
||||
def remove_pacemaker_node_state(node_name)
|
||||
cibadmin_safe '--delete', '--obj_type', 'status', '--crm_xml', "<node_state uname='#{node_name}'/>"
|
||||
cibadmin_safe '--delete', '--scope', 'status', '--xml-text', "<node_state uname='#{node_name}'/>"
|
||||
end
|
||||
|
||||
def remove_location_constraint(constraint_id)
|
||||
cibadmin_safe '--delete', '--obj_type', 'constraints', '--crm_xml', "<rsc_location id='#{constraint_id}'/>"
|
||||
cibadmin_safe '--delete', '--scope', 'constraints', '--xml-text', "<rsc_location id='#{constraint_id}'/>"
|
||||
end
|
||||
|
||||
def remove_corosync_node(node_name)
|
||||
node_number = corosync_nodes_structure[node_name]['number']
|
||||
fail "Could not get node_number of '#{node_name}' node!" unless node_number
|
||||
def add_pacemaker_node_record(node_name, node_id)
|
||||
patch = <<-eos
|
||||
<diff>
|
||||
<diff-added>
|
||||
<cib>
|
||||
<configuration>
|
||||
<nodes>
|
||||
<node id="#{node_id}" uname="#{node_name}" __crm_diff_marker__="added:top"/>
|
||||
</nodes>
|
||||
</configuration>
|
||||
</cib>
|
||||
</diff-added>
|
||||
</diff>
|
||||
eos
|
||||
cibadmin_safe '--patch', '--sync-call', '--xml-text', patch
|
||||
end
|
||||
|
||||
def add_pacemaker_node_state(node_name, node_id)
|
||||
patch = <<-eos
|
||||
<diff>
|
||||
<diff-added>
|
||||
<cib>
|
||||
<status>
|
||||
<node_state id="#{node_id}" uname="#{node_name}" in_ccm="true" crmd="online" crm-debug-origin="do_update_resource" join="member" expected="member" __crm_diff_marker__="added:top"/>
|
||||
</status>
|
||||
</cib>
|
||||
</diff-added>
|
||||
</diff>
|
||||
eos
|
||||
cibadmin_safe '--patch', '--sync-call', '--xml-text', patch
|
||||
end
|
||||
|
||||
def remove_corosync_node(node_id)
|
||||
debug "Remove corosync node: '#{node_id}'"
|
||||
node_number = nil
|
||||
corosync_nodes_state.each do |number, node|
|
||||
node_number = number if node['id'] == node_id
|
||||
end
|
||||
fail "Could not get node_number of node id: '#{node_id}'!" unless node_number
|
||||
remove_corosync_node_record node_number
|
||||
pcmk_nodes_reset
|
||||
end
|
||||
|
||||
def remove_corosync_node_record(node_number)
|
||||
cmapctl_safe '-D', "nodelist.node.#{node_number}"
|
||||
begin
|
||||
cmapctl_safe '-D', "nodelist.node.#{node_number}"
|
||||
rescue => e
|
||||
debug "Failed: #{e.message}"
|
||||
end
|
||||
end
|
||||
|
||||
def add_corosync_node(node_name)
|
||||
node_number = highest_corosync_node_number + 1
|
||||
node_addr = nodes_data[node_name]
|
||||
node_id = highest_pacemaker_node_id + 1
|
||||
unless node_number and node_addr and node_id
|
||||
fail "Could not get all the data for the new node '#{node_name}' (#{node_number}, #{node_addr}, #{node_id})"
|
||||
end
|
||||
add_corosync_node_record node_number, node_addr, node_id
|
||||
def add_corosync_node(node_id)
|
||||
debug "Add corosync node: '#{node_id}'"
|
||||
node_ip = corosync_nodes_data.fetch node_id, nil
|
||||
node_number = next_corosync_node_number
|
||||
fail "Could not get all the data for the new corosync node '#{node_name}'!" unless node_id and node_ip and node_number
|
||||
add_corosync_node_record node_number, node_ip, node_id
|
||||
pcmk_nodes_reset
|
||||
end
|
||||
|
||||
def add_corosync_node_record(node_number=nil, node_addr=nil, node_id=nil, ring_number=0)
|
||||
|
@ -141,38 +247,55 @@ Puppet::Type.type(:pcmk_nodes).provide(:ruby, :parent => Puppet::Provider::Pacem
|
|||
|
||||
def corosync_nodes
|
||||
debug 'Call: corosync_nodes'
|
||||
corosync_nodes_structure.keys
|
||||
wait_for_online
|
||||
change_fqdn_to_name if change_fqdn_to_name?
|
||||
debug "Return: #{corosync_nodes_structure.inspect}"
|
||||
corosync_nodes_structure
|
||||
end
|
||||
|
||||
def corosync_nodes=(expected_nodes)
|
||||
debug "Call: corosync_nodes='#{expected_nodes.inspect}'"
|
||||
existing_nodes = corosync_nodes_structure.keys
|
||||
existing_nodes = corosync_nodes_structure
|
||||
|
||||
existing_nodes.each do |node|
|
||||
next if expected_nodes.include? node
|
||||
remove_corosync_node node
|
||||
if @resource[:remove_corosync_nodes]
|
||||
existing_nodes.each do |existing_node_id, existing_node_ip|
|
||||
next if expected_nodes[existing_node_id] == existing_node_ip
|
||||
remove_corosync_node existing_node_id
|
||||
end
|
||||
end
|
||||
|
||||
expected_nodes.each do |node|
|
||||
next if existing_nodes.include? node
|
||||
add_corosync_node node
|
||||
if @resource[:add_corosync_nodes]
|
||||
expected_nodes.each do |expected_node_id, expected_node_ip|
|
||||
next if existing_nodes[expected_node_id] == expected_node_ip
|
||||
add_corosync_node expected_node_id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
|
||||
def pacemaker_nodes
|
||||
debug 'Call: pacemaker_nodes'
|
||||
pacemaker_nodes_structure.keys
|
||||
wait_for_online
|
||||
change_fqdn_to_name if change_fqdn_to_name?
|
||||
debug "Return: #{pacemaker_nodes_structure.inspect}"
|
||||
pacemaker_nodes_structure
|
||||
end
|
||||
|
||||
def pacemaker_nodes=(expected_nodes)
|
||||
debug "Call: pacemaker_nodes='#{expected_nodes.inspect}'"
|
||||
existing_nodes = pacemaker_nodes_structure.keys
|
||||
existing_nodes = pacemaker_nodes_structure
|
||||
|
||||
existing_nodes.each do |node|
|
||||
next if expected_nodes.include? node
|
||||
remove_pacemaker_node node
|
||||
if @resource[:remove_pacemaker_nodes]
|
||||
existing_nodes.each do |existing_node_name, existing_node_id|
|
||||
next if expected_nodes[existing_node_name] == existing_node_id
|
||||
remove_pacemaker_node existing_node_name
|
||||
end
|
||||
end
|
||||
|
||||
if @resource[:add_pacemaker_nodes]
|
||||
expected_nodes.each do |expected_node_name, expected_node_id|
|
||||
next if existing_nodes[expected_node_name] == expected_node_id
|
||||
add_pacemaker_node expected_node_name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -11,21 +11,34 @@ module Puppet
|
|||
defaultto false
|
||||
end
|
||||
|
||||
newparam(:nodes, :array_matching => :all) do
|
||||
desc 'Nodes data structure. Hash { "name" => "ip" }'
|
||||
newparam(:nodes) do
|
||||
desc <<-eos
|
||||
Nodes data structure:
|
||||
{
|
||||
'node-1' => { 'id' => '1', 'ip' => '192.168.0.1'},
|
||||
'node-2' => { 'id' => '2', 'ip' => '192.168.0.2'},
|
||||
}
|
||||
eos
|
||||
validate do |value|
|
||||
unless value.is_a? Hash and value.any?
|
||||
fail 'Nodes should be a non-empty hash { "name" => "ip" }!'
|
||||
fail 'Nodes should be a non-empty hash!'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newproperty(:corosync_nodes, :array_matching => :all) do
|
||||
defaultto { @resource[:nodes].keys if @resource[:nodes] }
|
||||
newproperty(:corosync_nodes) do
|
||||
desc <<-eos
|
||||
Corosync_nodes data structure:
|
||||
{
|
||||
# 'id' => 'ip',
|
||||
'1' => '192.168.0.1',
|
||||
'2' => '192.168.0.2',
|
||||
}
|
||||
eos
|
||||
defaultto { @resource.set_corosync_nodes }
|
||||
|
||||
def insync?(is)
|
||||
return false unless is.is_a? Array and should.is_a? Array
|
||||
is.sort == should.sort
|
||||
is == should
|
||||
end
|
||||
|
||||
def is_to_s(is)
|
||||
|
@ -37,12 +50,19 @@ module Puppet
|
|||
end
|
||||
end
|
||||
|
||||
newproperty(:pacemaker_nodes, :array_matching => :all) do
|
||||
defaultto { @resource[:nodes].keys if @resource[:nodes] }
|
||||
newproperty(:pacemaker_nodes) do
|
||||
desc <<-eos
|
||||
Pacemaker_nodes data structure:
|
||||
{
|
||||
# 'name' => 'id',
|
||||
'node-1' => '1',
|
||||
'node-2' => '2',
|
||||
}
|
||||
eos
|
||||
defaultto { @resource.set_pacemaker_nodes }
|
||||
|
||||
def insync?(is)
|
||||
return false unless is.is_a? Array and should.is_a? Array
|
||||
is.sort == should.sort
|
||||
is == should
|
||||
end
|
||||
|
||||
def is_to_s(is)
|
||||
|
@ -54,9 +74,48 @@ module Puppet
|
|||
end
|
||||
end
|
||||
|
||||
newparam(:add_pacemaker_nodes) do
|
||||
defaultto true
|
||||
end
|
||||
|
||||
newparam(:remove_pacemaker_nodes) do
|
||||
defaultto true
|
||||
end
|
||||
|
||||
newparam(:add_corosync_nodes) do
|
||||
defaultto true
|
||||
end
|
||||
|
||||
newparam(:remove_corosync_nodes) do
|
||||
defaultto true
|
||||
end
|
||||
|
||||
def validate
|
||||
fail 'No corosync_nodes!' unless self[:corosync_nodes].is_a? Array and self[:corosync_nodes].any?
|
||||
fail 'No pacemaker_nodes!' unless self[:pacemaker_nodes].is_a? Array and self[:pacemaker_nodes].any?
|
||||
fail 'No corosync_nodes!' unless self[:corosync_nodes].is_a? Hash and self[:corosync_nodes].any?
|
||||
fail 'No pacemaker_nodes!' unless self[:pacemaker_nodes].is_a? Hash and self[:pacemaker_nodes].any?
|
||||
end
|
||||
|
||||
def set_corosync_nodes
|
||||
return unless self[:nodes].respond_to? :each
|
||||
corosync_nodes = {}
|
||||
self[:nodes].each do |name, node|
|
||||
id = node['id']
|
||||
ip = node['ip']
|
||||
next unless id and ip
|
||||
corosync_nodes.store id, ip
|
||||
end
|
||||
self[:corosync_nodes] = corosync_nodes
|
||||
end
|
||||
|
||||
def set_pacemaker_nodes
|
||||
return unless self[:nodes].respond_to? :each
|
||||
pacemaker_nodes = {}
|
||||
self[:nodes].each do |name, node|
|
||||
id = node['id']
|
||||
next unless id and name
|
||||
pacemaker_nodes.store name, id
|
||||
end
|
||||
self[:pacemaker_nodes] = pacemaker_nodes
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -226,8 +226,16 @@ describe Puppet::Provider::Pacemaker_common do
|
|||
end
|
||||
|
||||
context 'node id functions' do
|
||||
let(:node_ids) do
|
||||
{
|
||||
"node-1" => {"id"=>"1", "uname"=>"node-1"},
|
||||
"node-2" => {"id"=>"2", "uname"=>"node-2"},
|
||||
"node-3" => {"id"=>"3", "uname"=>"node-3"},
|
||||
}
|
||||
end
|
||||
|
||||
it 'can get the node ids structure' do
|
||||
expect(@class.node_ids).to eq({"node-1"=>"1", "node-2"=>"2", "node-3"=>"3"})
|
||||
expect(@class.node_ids).to eq node_ids
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ describe Puppet::Type.type(:pcmk_nodes).provider(:ruby) do
|
|||
|
||||
let(:provider) do
|
||||
provider = resource.provider
|
||||
if ENV['PUPPET_DEBUG']
|
||||
if ENV['SPEC_PUPPET_DEBUG']
|
||||
class << provider
|
||||
def debug(str)
|
||||
puts str
|
||||
|
@ -22,19 +22,46 @@ describe Puppet::Type.type(:pcmk_nodes).provider(:ruby) do
|
|||
provider
|
||||
end
|
||||
|
||||
# output of corosync_cmapctl -b nodelist
|
||||
let(:cmap_nodelist) do
|
||||
<<-eos
|
||||
####
|
||||
nodelist.node.0.nodeid (u32) = 1
|
||||
nodelist.node.0.ring0_addr (str) = 192.168.0.1
|
||||
nodelist.node.1.nodeid (u32) = 2
|
||||
nodelist.node.1.ring0_addr (str) = 192.168.0.2
|
||||
nodelist.node.2.nodeid (u32) = 3
|
||||
nodelist.node.2.ring0_addr (str) = 192.168.0.3
|
||||
#####
|
||||
eos
|
||||
end
|
||||
|
||||
# comes from 'nodes' library method excluing unrelated data
|
||||
let(:nodes_input) do
|
||||
{
|
||||
"node-1" => {'id' => "1", 'uname' => "node-1"},
|
||||
"node-2" => {'id' => "2", 'uname' => "node-2"},
|
||||
"node-3" => {'id' => "3", 'uname' => "node-3"},
|
||||
}
|
||||
end
|
||||
|
||||
# comes from 'node_ids' library method
|
||||
let(:node_ids_input) do
|
||||
{
|
||||
"node-1" => {'id' => "1", 'uname' => "node-1"},
|
||||
"node-2" => {'id' => "2", 'uname' => "node-2"},
|
||||
"node-3" => {'id' => "3", 'uname' => "node-3"},
|
||||
}
|
||||
end
|
||||
|
||||
# retreived corosync nodes state
|
||||
let(:corosync_nodes_state) do
|
||||
{
|
||||
"0"=>{ "id" => "1", "number" => "0", "ip" => "192.168.0.1" },
|
||||
"1"=>{ "id" => "2", "number" => "1", "ip" => "192.168.0.2" },
|
||||
"2"=>{ "id" => "3", "number" => "2", "ip" => "192.168.0.3" },
|
||||
}
|
||||
end
|
||||
|
||||
# generated existing paceamker nodes structure
|
||||
let(:pacemaker_nodes_structure) do
|
||||
{
|
||||
"node-1" => "1",
|
||||
|
@ -43,23 +70,17 @@ nodelist.node.2.ring0_addr (str) = 192.168.0.3
|
|||
}
|
||||
end
|
||||
|
||||
let(:nodes_states) do
|
||||
{
|
||||
"node-1" => {'id' => "1", 'uname' => "node-1"},
|
||||
"node-2" => {'id' => "2", 'uname' => "node-2"},
|
||||
"node-3" => {'id' => "3", 'uname' => "node-3"},
|
||||
}
|
||||
end
|
||||
|
||||
# generated existing corosync nodes structure
|
||||
let(:corosync_nodes_structure) do
|
||||
{
|
||||
"node-1" => {'id' => "1", 'number' => "0", 'uname' => "node-1", 'ring0_addr' => "192.168.0.1"},
|
||||
"node-2" => {'id' => "2", 'number' => "1", 'uname' => "node-2", 'ring0_addr' => "192.168.0.2"},
|
||||
"node-3" => {'id' => "3", 'number' => "2", 'uname' => "node-3", 'ring0_addr' => "192.168.0.3"},
|
||||
"1" => "192.168.0.1",
|
||||
"2" => "192.168.0.2",
|
||||
"3" => "192.168.0.3",
|
||||
}
|
||||
end
|
||||
|
||||
let(:constraint_locations) do
|
||||
# comes from 'constraint_locations' library method
|
||||
let(:constraint_locations_input) do
|
||||
{
|
||||
"p_neutron-dhcp-agent_on_node-1" => {"id" => "p_neutron-dhcp-agent_on_node-1", "node" => "node-1", "rsc" => "p_neutron-dhcp-agent", "score" => "100"},
|
||||
"p_neutron-dhcp-agent_on_node-2" => {"id" => "p_neutron-dhcp-agent_on_node-2", "node" => "node-2", "rsc" => "p_neutron-dhcp-agent", "score" => "100"},
|
||||
|
@ -70,32 +91,40 @@ nodelist.node.2.ring0_addr (str) = 192.168.0.3
|
|||
}
|
||||
end
|
||||
|
||||
# 'nodes' parameter when nodes should be added and removed
|
||||
let(:nodes_data) do
|
||||
{
|
||||
'node-1' => "192.168.0.1",
|
||||
'node-2' => "192.168.0.2",
|
||||
'node-3' => "192.168.0.3",
|
||||
'node-4' => "192.168.0.4",
|
||||
'node-2' => { "ip" => "192.168.0.2", "id" => "2" },
|
||||
'node-3' => { "ip" => "192.168.0.3", "id" => "3" },
|
||||
'node-4' => { "ip" => "192.168.0.4", "id" => "4" },
|
||||
}
|
||||
end
|
||||
|
||||
let(:existing_nodes) do
|
||||
%w(node-1 node-2 node-3)
|
||||
end
|
||||
|
||||
let(:expected_nodes) do
|
||||
%w(node-2 node-3 node-4)
|
||||
# 'nodes' parameter when fqdn should be switched to name
|
||||
let(:fqdn_nodes_data) do
|
||||
data = {}
|
||||
nodes_data.each do |name, node|
|
||||
name = "#{name}.example.com"
|
||||
data[name] = node
|
||||
end
|
||||
data
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
provider.stubs(:cmapctl_nodelist).returns cmap_nodelist
|
||||
provider.stubs(:node_ids).returns pacemaker_nodes_structure
|
||||
provider.stubs(:nodes).returns nodes_states
|
||||
provider.stubs(:constraint_locations).returns constraint_locations
|
||||
provider.stubs(:nodes_data).returns nodes_data
|
||||
provider.stubs(:node_ids).returns node_ids_input
|
||||
provider.stubs(:nodes).returns nodes_input
|
||||
provider.stubs(:constraint_locations).returns constraint_locations_input
|
||||
provider.stubs(:node_name).returns 'node-2'
|
||||
provider.stubs(:wait_for_online).returns true
|
||||
end
|
||||
|
||||
|
||||
context 'data structures' do
|
||||
it 'corosync_nodes_state' do
|
||||
expect(provider.corosync_nodes_state).to eq(corosync_nodes_state)
|
||||
end
|
||||
|
||||
it 'corosync_nodes_structure' do
|
||||
expect(provider.corosync_nodes_structure).to eq(corosync_nodes_structure)
|
||||
end
|
||||
|
@ -107,34 +136,60 @@ nodelist.node.2.ring0_addr (str) = 192.168.0.3
|
|||
end
|
||||
|
||||
context 'main actions' do
|
||||
before(:each) do
|
||||
provider.stubs(:add_corosync_node)
|
||||
provider.stubs(:remove_corosync_node)
|
||||
provider.stubs(:add_pacemaker_node)
|
||||
provider.stubs(:remove_pacemaker_node)
|
||||
end
|
||||
|
||||
it 'can get corosync_nodes' do
|
||||
expect(provider.corosync_nodes).to eq existing_nodes
|
||||
expect(provider.corosync_nodes).to eq corosync_nodes_structure
|
||||
end
|
||||
|
||||
it 'can get pacemaker_nodes' do
|
||||
expect(provider.pacemaker_nodes).to eq existing_nodes
|
||||
expect(provider.pacemaker_nodes).to eq pacemaker_nodes_structure
|
||||
end
|
||||
|
||||
it 'removes unexpected corosync_nodes' do
|
||||
provider.expects(:remove_corosync_node).with('node-1')
|
||||
provider.stubs(:add_corosync_node)
|
||||
provider.corosync_nodes = expected_nodes
|
||||
provider.expects(:remove_corosync_node).with('1')
|
||||
provider.corosync_nodes = resource[:corosync_nodes]
|
||||
end
|
||||
|
||||
it 'adds missing corosync_nodes' do
|
||||
provider.expects(:add_corosync_node).with('node-4')
|
||||
provider.stubs(:remove_corosync_node)
|
||||
provider.corosync_nodes = expected_nodes
|
||||
provider.expects(:add_corosync_node).with('4')
|
||||
provider.corosync_nodes = resource[:corosync_nodes]
|
||||
end
|
||||
|
||||
it 'removes unexpected pacemaker_nodes' do
|
||||
provider.expects(:remove_pacemaker_node).with('node-1')
|
||||
provider.pacemaker_nodes = expected_nodes
|
||||
provider.pacemaker_nodes = resource[:pacemaker_nodes]
|
||||
end
|
||||
|
||||
it 'adds missing pacemaker_nodes' do
|
||||
provider.expects(:add_pacemaker_node).with('node-4')
|
||||
provider.pacemaker_nodes = resource[:pacemaker_nodes]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context 'when a paceamker node is removed' do
|
||||
context 'when adding a new pacemaker_node' do
|
||||
|
||||
it 'it adds a node record' do
|
||||
provider.expects(:add_pacemaker_node_record).with('node-4', '4')
|
||||
provider.stubs(:add_pacemaker_node_state)
|
||||
provider.add_pacemaker_node 'node-4'
|
||||
end
|
||||
|
||||
it 'adds a node_state record' do
|
||||
provider.stubs(:add_pacemaker_node_record)
|
||||
provider.expects(:add_pacemaker_node_state).with('node-4', '4')
|
||||
provider.add_pacemaker_node 'node-4'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when removing a paceamker_node' do
|
||||
|
||||
before(:each) do
|
||||
provider.stubs(:remove_pacemaker_node_state)
|
||||
provider.stubs(:remove_pacemaker_node_record)
|
||||
|
@ -159,24 +214,42 @@ nodelist.node.2.ring0_addr (str) = 192.168.0.3
|
|||
end
|
||||
|
||||
context 'when adding a new corosync_node' do
|
||||
it 'can determine the highest corosync node number' do
|
||||
expect(provider.highest_corosync_node_number).to eq 2
|
||||
|
||||
it 'cat get a new free corosync nodes number' do
|
||||
expect(provider.next_corosync_node_number).to eq '3'
|
||||
end
|
||||
|
||||
it 'can determine the highest pacemaker node id' do
|
||||
expect(provider.highest_pacemaker_node_id).to eq 3
|
||||
end
|
||||
|
||||
it 'adds a new corosync node with correct parameters' do
|
||||
provider.expects(:add_corosync_node_record).with 3, "192.168.0.4", 4
|
||||
provider.add_corosync_node 'node-4'
|
||||
it 'adds a new corosync node with the correct parameters' do
|
||||
provider.expects(:add_corosync_node_record).with '3', '192.168.0.4', '4'
|
||||
provider.add_corosync_node '4'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when removing a corosync node' do
|
||||
context 'when removing a corosync_node' do
|
||||
|
||||
it 'removes a node with the correct number' do
|
||||
provider.expects(:remove_corosync_node_record).with '0'
|
||||
provider.remove_corosync_node 'node-1'
|
||||
provider.remove_corosync_node '1'
|
||||
end
|
||||
end
|
||||
|
||||
context 'FQDN and Hostname compatibility' do
|
||||
let(:resource) do
|
||||
Puppet::Type.type(:pcmk_nodes).new(
|
||||
:name => 'paceamker',
|
||||
:provider => :ruby,
|
||||
:nodes => fqdn_nodes_data,
|
||||
)
|
||||
end
|
||||
|
||||
it 'can determine when the switch is needed' do
|
||||
expect(provider.change_fqdn_to_name?).to eq true
|
||||
end
|
||||
|
||||
it 'can rewrite fqdns in the node input data to the hostnames' do
|
||||
provider.change_fqdn_to_name
|
||||
expect(provider.resource[:nodes]).to eq nodes_data
|
||||
expect(provider.corosync_nodes_structure).to eq(corosync_nodes_structure)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -7,13 +7,32 @@ describe Puppet::Type.type(:pcmk_nodes) do
|
|||
|
||||
let(:nodes_data) do
|
||||
{
|
||||
'node-1' => "192.168.0.1",
|
||||
'node-2' => "192.168.0.2",
|
||||
'node-3' => "192.168.0.3",
|
||||
'node-4' => "192.168.0.4",
|
||||
'node-1' => { "ip" => "192.168.0.1", "id" => "1" },
|
||||
'node-2' => { "ip" => "192.168.0.2", "id" => "2" },
|
||||
'node-3' => { "ip" => "192.168.0.3", "id" => "3" },
|
||||
'node-4' => { "ip" => "192.168.0.4", "id" => "4" },
|
||||
}
|
||||
end
|
||||
|
||||
let(:corosync_nodes_data) do
|
||||
{
|
||||
'1' => "192.168.0.1",
|
||||
'2' => "192.168.0.2",
|
||||
'3' => "192.168.0.3",
|
||||
'4' => "192.168.0.4",
|
||||
}
|
||||
end
|
||||
|
||||
let(:pacemaker_nodes_data) do
|
||||
{
|
||||
'node-1' => "1",
|
||||
'node-2' => "2",
|
||||
'node-3' => "3",
|
||||
'node-4' => "4",
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
it "should have a 'name' parameter" do
|
||||
expect(subject[:name]).to eq 'pacemaker'
|
||||
end
|
||||
|
@ -23,11 +42,11 @@ describe Puppet::Type.type(:pcmk_nodes) do
|
|||
end
|
||||
|
||||
it "should have a 'corosync_nodes' property that defaults to 'nodes' parameter" do
|
||||
expect(subject[:corosync_nodes]).to eq nodes_data.keys
|
||||
expect(subject[:corosync_nodes]).to eq corosync_nodes_data
|
||||
end
|
||||
|
||||
it "should have a 'pacemaker_nodes' property that defaults to 'nodes' parameter" do
|
||||
expect(subject[:pacemaker_nodes]).to eq nodes_data.keys
|
||||
expect(subject[:pacemaker_nodes]).to eq pacemaker_nodes_data
|
||||
end
|
||||
|
||||
it "should fail if nodes data is not provided or incorrect" do
|
||||
|
@ -45,7 +64,7 @@ describe Puppet::Type.type(:pcmk_nodes) do
|
|||
subject.validate
|
||||
}.to raise_error
|
||||
expect {
|
||||
subject[:corosync_nodes] = []
|
||||
subject[:corosync_nodes] = {}
|
||||
subject.validate
|
||||
}.to raise_error
|
||||
end
|
||||
|
@ -56,7 +75,7 @@ describe Puppet::Type.type(:pcmk_nodes) do
|
|||
subject.validate
|
||||
}.to raise_error
|
||||
expect {
|
||||
subject[:pacemaker_nodes] = []
|
||||
subject[:pacemaker_nodes] = {}
|
||||
subject.validate
|
||||
}.to raise_error
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue