Fix idempotency of cs_resource
* insync? to drop status metadata from checks * code cleanup * fix rspec for cs_resource type * switch location add implementation from pcs to cibadmin --patch to solve problems with cib changes not being synced to other nodes related-blueprint: pacemaker-improvements Related-Bug: 1391599 Related-Bug: 1390480 Related-Bug: 1396481 Change-Id: I5410b91ea01fc8c6805de6becdf0800d0d486188 Signed-off-by: Sergii Golovatiuk <sgolovatiuk@mirantis.com>
This commit is contained in:
parent
27e4449ad4
commit
c045ce3078
|
@ -7,7 +7,7 @@ define cluster::corosync::cs_service (
|
|||
$service_name,
|
||||
$service_title = undef, # Title of Service, that been mangled for pacemakering
|
||||
$package_name = undef,
|
||||
$csr_multistate_hash = undef,
|
||||
$csr_complex_type = undef,
|
||||
$csr_ms_metadata = undef,
|
||||
$csr_parameters = undef,
|
||||
$csr_metadata = undef,
|
||||
|
@ -38,7 +38,7 @@ define cluster::corosync::cs_service (
|
|||
primitive_class => 'ocf',
|
||||
provided_by => 'mirantis',
|
||||
primitive_type => $ocf_script,
|
||||
multistate_hash => $csr_multistate_hash,
|
||||
complex_type => $csr_complex_type,
|
||||
ms_metadata => $csr_ms_metadata,
|
||||
parameters => $csr_parameters,
|
||||
metadata => $csr_metadata,
|
||||
|
|
|
@ -25,9 +25,7 @@ class cluster::haproxy_ocf (
|
|||
primitive_class => 'ocf',
|
||||
provided_by => 'mirantis',
|
||||
primitive_type => 'ns_haproxy',
|
||||
multistate_hash => {
|
||||
'type' => 'clone',
|
||||
},
|
||||
complex_type => 'clone',
|
||||
ms_metadata => {
|
||||
'interleave' => true,
|
||||
},
|
||||
|
|
|
@ -21,11 +21,11 @@ class cluster::neutron::dhcp (
|
|||
value => $agents_per_net
|
||||
}
|
||||
$csr_metadata = undef
|
||||
$csr_multistate_hash = { 'type' => 'clone' }
|
||||
$csr_complex_type = 'clone'
|
||||
$csr_ms_metadata = { 'interleave' => 'true' }
|
||||
} else {
|
||||
$csr_metadata = { 'resource-stickiness' => '1' }
|
||||
$csr_multistate_hash = undef
|
||||
$csr_complex_type = undef
|
||||
$csr_ms_metadata = undef
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ class cluster::neutron::dhcp (
|
|||
'amqp_server_port' => $amqp_server_port
|
||||
},
|
||||
csr_metadata => $csr_metadata,
|
||||
csr_multistate_hash => $csr_multistate_hash,
|
||||
csr_complex_type => $csr_complex_type,
|
||||
csr_ms_metadata => $csr_ms_metadata,
|
||||
csr_mon_intr => '20',
|
||||
csr_mon_timeout => '10',
|
||||
|
|
|
@ -23,11 +23,11 @@ define cluster::neutron::l3 (
|
|||
value => true
|
||||
}
|
||||
$csr_metadata = undef
|
||||
$csr_multistate_hash = { 'type' => 'clone' }
|
||||
$csr_complex_type = 'clone'
|
||||
$csr_ms_metadata = { 'interleave' => 'true' }
|
||||
} else {
|
||||
$csr_metadata = { 'resource-stickiness' => '1' }
|
||||
$csr_multistate_hash = undef
|
||||
$csr_complex_type = undef
|
||||
$csr_ms_metadata = undef
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ define cluster::neutron::l3 (
|
|||
'multiple_agents' => $multiple_agents
|
||||
},
|
||||
csr_metadata => $csr_metadata,
|
||||
csr_multistate_hash => $csr_multistate_hash,
|
||||
csr_complex_type => $csr_complex_type,
|
||||
csr_ms_metadata => $csr_ms_metadata,
|
||||
csr_mon_intr => '20',
|
||||
csr_mon_timeout => '10',
|
||||
|
|
|
@ -14,7 +14,7 @@ class cluster::neutron::metadata (
|
|||
#TODO (bogdando) move to extras ha wrappers
|
||||
cluster::corosync::cs_service {'neutron-metadata-agent':
|
||||
ocf_script => 'neutron-agent-metadata',
|
||||
csr_multistate_hash => { 'type' => 'clone' },
|
||||
csr_complex_type => 'clone',
|
||||
csr_ms_metadata => { 'interleave' => 'true' },
|
||||
csr_mon_intr => '60',
|
||||
csr_mon_timeout => '10',
|
||||
|
|
|
@ -14,7 +14,7 @@ class cluster::neutron::ovs (
|
|||
|
||||
cluster::corosync::cs_service {'ovs':
|
||||
ocf_script => 'neutron-agent-ovs',
|
||||
csr_multistate_hash => { 'type' => 'clone' },
|
||||
csr_complex_type => 'clone',
|
||||
csr_ms_metadata => { 'interleave' => 'true' },
|
||||
csr_parameters => { 'plugin_config' => $plugin_config },
|
||||
csr_mon_intr => '20',
|
||||
|
|
|
@ -109,12 +109,10 @@ define cluster::virtual_ip (
|
|||
'dampen' => '30s',
|
||||
'timeout' => '3s',
|
||||
},
|
||||
operations => {
|
||||
operations => {
|
||||
'monitor' => { 'interval' => '20', 'timeout' => '30' },
|
||||
},
|
||||
multistate_hash => {
|
||||
'type' => 'clone',
|
||||
},
|
||||
complex_type => 'clone',
|
||||
}
|
||||
service { "ping_${vip_name}":
|
||||
ensure => 'running',
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
class Puppet::Provider::Corosync < Puppet::Provider
|
||||
require "open3"
|
||||
# Yep, that's right we are parsing XML...FUN! (It really wasn't that bad)
|
||||
require 'rexml/document'
|
||||
#require 'system'
|
||||
require 'pp'
|
||||
require 'open3'
|
||||
require 'rexml/document'
|
||||
|
||||
class Puppet::Provider::Corosync < Puppet::Provider
|
||||
|
||||
def self.dump_cib
|
||||
self.block_until_ready
|
||||
|
@ -68,8 +67,11 @@ class Puppet::Provider::Corosync < Puppet::Provider
|
|||
|
||||
def exists?
|
||||
self.class.block_until_ready
|
||||
debug(@property_hash.inspect)
|
||||
!(@property_hash[:ensure] == :absent or @property_hash.empty?)
|
||||
Puppet.debug "Call exists? on cs_resource '#{@resource[:name]}'"
|
||||
out = !(@property_hash[:ensure] == :absent or @property_hash.empty?)
|
||||
Puppet.debug "Return: #{out}"
|
||||
Puppet.debug "Current state:\n#{@property_hash.pretty_inspect}" if @property_hash.any?
|
||||
out
|
||||
end
|
||||
|
||||
def get_scope(type)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
require 'pathname'
|
||||
require Pathname.new(__FILE__).dirname.dirname.expand_path + 'corosync'
|
||||
require File.join File.dirname(__FILE__), '../corosync.rb'
|
||||
require 'pp'
|
||||
|
||||
Puppet::Type.type(:cs_resource).provide(:crm, :parent => Puppet::Provider::Corosync) do
|
||||
desc 'Specific provider for a rather specific type since I currently have no
|
||||
|
@ -10,99 +10,72 @@ Puppet::Type.type(:cs_resource).provide(:crm, :parent => Puppet::Provider::Coros
|
|||
operations and parameters. A hash is used instead of constucting a
|
||||
better model since these values can be almost anything.'
|
||||
|
||||
# Path to the crm binary for interacting with the cluster configuration.
|
||||
|
||||
|
||||
commands :cibadmin => 'cibadmin'
|
||||
commands :crm_shadow => 'crm_shadow'
|
||||
commands :crm => 'crm'
|
||||
commands :pcs => 'pcs'
|
||||
commands :crm_diff => 'crm_diff'
|
||||
commands :crm_attribute => 'crm_attribute'
|
||||
|
||||
# parse CIB XML and create the array of found primitives
|
||||
# @return [Array<Puppet::Provider::Crm>]
|
||||
def self.instances
|
||||
|
||||
block_until_ready
|
||||
|
||||
instances = []
|
||||
|
||||
# cmd = [ command(:crm), 'configure', 'show', 'xml' ]
|
||||
raw, status = dump_cib
|
||||
doc = REXML::Document.new(raw)
|
||||
|
||||
# We are obtaining four different sets of data in this block. We obtain
|
||||
# key/value pairs for basic primitive information (which Corosync stores
|
||||
# in the configuration as "resources"). After getting that basic data we
|
||||
# descend into parameters, operations (which the config labels as
|
||||
# instance_attributes and operations), and metadata then generate embedded
|
||||
# hash structures of each entry.
|
||||
REXML::XPath.each(doc, '//primitive') do |e|
|
||||
|
||||
primitive = {}
|
||||
items = e.attributes
|
||||
primitive.merge!({
|
||||
items['id'].to_sym => {
|
||||
:class => items['class'],
|
||||
:type => items['type'],
|
||||
:provider => items['provider']
|
||||
}
|
||||
})
|
||||
|
||||
primitive[items['id'].to_sym][:parameters] = {}
|
||||
primitive[items['id'].to_sym][:operations] = {}
|
||||
primitive[items['id'].to_sym][:metadata] = {}
|
||||
primitive[items['id'].to_sym][:ms_metadata] = {}
|
||||
primitive[items['id'].to_sym][:multistate_hash] = {}
|
||||
|
||||
if ! e.elements['instance_attributes'].nil?
|
||||
e.elements['instance_attributes'].each_element do |i|
|
||||
primitive[items['id'].to_sym][:parameters][(i.attributes['name'])] = i.attributes['value']
|
||||
end
|
||||
end
|
||||
|
||||
if ! e.elements['meta_attributes'].nil?
|
||||
e.elements['meta_attributes'].each_element do |m|
|
||||
primitive[items['id'].to_sym][:metadata][(m.attributes['name'])] = m.attributes['value']
|
||||
end
|
||||
end
|
||||
|
||||
if ! e.elements['operations'].nil?
|
||||
e.elements['operations'].each_element do |o|
|
||||
valids = o.attributes.reject do |k,v| k == 'id' end
|
||||
if valids['role']
|
||||
op_name = "#{valids['name']}:#{valids['role']}" #.to_sym()
|
||||
else
|
||||
op_name = valids['name'] #.to_sym()
|
||||
end
|
||||
primitive[items['id'].to_sym][:operations][op_name] = {}
|
||||
valids.each do |k,v|
|
||||
primitive[items['id'].to_sym][:operations][op_name][k] = v if k != 'name'
|
||||
end
|
||||
end
|
||||
end
|
||||
if e.parent.name == 'master' or e.parent.name == 'clone'
|
||||
primitive[items['id'].to_sym][:multistate_hash][:name] = e.parent.attributes['id']
|
||||
primitive[items['id'].to_sym][:multistate_hash][:type] = e.parent.name
|
||||
if ! e.parent.elements['meta_attributes'].nil?
|
||||
e.parent.elements['meta_attributes'].each_element do |m|
|
||||
primitive[items['id'].to_sym][:ms_metadata][(m.attributes['name'])] = m.attributes['value']
|
||||
end
|
||||
end
|
||||
end
|
||||
primitive_instance = {
|
||||
:name => primitive.first[0],
|
||||
primitive = {
|
||||
:ensure => :present,
|
||||
:primitive_class => primitive.first[1][:class],
|
||||
:provided_by => primitive.first[1][:provider],
|
||||
:primitive_type => primitive.first[1][:type],
|
||||
:parameters => primitive.first[1][:parameters],
|
||||
:operations => primitive.first[1][:operations],
|
||||
:metadata => primitive.first[1][:metadata],
|
||||
:ms_metadata => primitive.first[1][:ms_metadata],
|
||||
:multistate_hash => primitive.first[1][:multistate_hash],
|
||||
:provider => self.name
|
||||
:name => items['id'].to_s,
|
||||
:primitive_class => items['class'].to_s,
|
||||
:primitive_type => items['type'].to_s,
|
||||
:provided_by => items['provider'].to_s,
|
||||
}
|
||||
|
||||
instances << new(primitive_instance)
|
||||
primitive[:parameters] = {}
|
||||
primitive[:operations] = {}
|
||||
primitive[:metadata] = {}
|
||||
primitive[:ms_metadata] = {}
|
||||
|
||||
if e.elements['instance_attributes']
|
||||
e.elements['instance_attributes'].each_element do |i|
|
||||
primitive[:parameters].store i.attributes['name'].to_s, i.attributes['value'].to_s
|
||||
end
|
||||
end
|
||||
|
||||
if e.elements['meta_attributes']
|
||||
e.elements['meta_attributes'].each_element do |m|
|
||||
primitive[:metadata].store m.attributes['name'].to_s, m.attributes['value'].to_s
|
||||
end
|
||||
end
|
||||
|
||||
if e.elements['operations']
|
||||
e.elements['operations'].each_element do |o|
|
||||
op_name = o.attributes['name'].to_s
|
||||
op_name += ":#{o.attributes['role']}" if o.attributes['role']
|
||||
primitive[:operations][op_name] = {}
|
||||
o.attributes.each do |k,v|
|
||||
next if k == 'name'
|
||||
next if k == 'id'
|
||||
primitive[:operations][op_name].store k.to_s, v.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if e.parent.name == 'master' or e.parent.name == 'clone'
|
||||
primitive[:complex_type] = e.parent.name
|
||||
if e.parent.elements['meta_attributes']
|
||||
e.parent.elements['meta_attributes'].each_element do |m|
|
||||
primitive[:ms_metadata].store m.attributes['name'].to_s, m.attributes['value'].to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
instances << new(primitive)
|
||||
end
|
||||
instances
|
||||
end
|
||||
|
@ -110,31 +83,29 @@ Puppet::Type.type(:cs_resource).provide(:crm, :parent => Puppet::Provider::Coros
|
|||
# Create just adds our resource to the property_hash and flush will take care
|
||||
# of actually doing the work.
|
||||
def create
|
||||
debug "Call: create on cs_resource '#{@resource[:name]}'"
|
||||
@property_hash = {
|
||||
:name => @resource[:name],
|
||||
:ensure => :present,
|
||||
:primitive_class => @resource[:primitive_class],
|
||||
:provided_by => @resource[:provided_by],
|
||||
:primitive_type => @resource[:primitive_type],
|
||||
:multistate_hash => @resource[:multistate_hash],
|
||||
:complex_type => @resource[:complex_type],
|
||||
}
|
||||
@property_hash[:parameters] = @resource[:parameters] if ! @resource[:parameters].nil?
|
||||
@property_hash[:operations] = @resource[:operations] if ! @resource[:operations].nil?
|
||||
@property_hash[:metadata] = @resource[:metadata] if ! @resource[:metadata].nil?
|
||||
@property_hash[:ms_metadata] = @resource[:ms_metadata] if ! @resource[:ms_metadata].nil?
|
||||
@property_hash[:cib] = @resource[:cib] if ! @resource[:cib].nil?
|
||||
@property_hash[:parameters] = @resource[:parameters] if @resource[:parameters]
|
||||
@property_hash[:operations] = @resource[:operations] if @resource[:operations]
|
||||
@property_hash[:metadata] = @resource[:metadata] if @resource[:metadata]
|
||||
@property_hash[:ms_metadata] = @resource[:ms_metadata] if @resource[:ms_metadata]
|
||||
@property_hash[:cib] = @resource[:cib] if @resource[:cib]
|
||||
end
|
||||
|
||||
# Unlike create we actually immediately delete the item. Corosync forces us
|
||||
# to "stop" the primitive before we are able to remove it.
|
||||
def destroy
|
||||
debug('Stopping primitive before removing it')
|
||||
crm('resource', 'stop', @resource[:name])
|
||||
debug('Removing primitive')
|
||||
## FIXME(aglarendil): may be we need to apply crm_diff related approach
|
||||
## FIXME(aglarendil): due to 1338594 bug and do this in flush section
|
||||
try_command("delete",@resource[:name])
|
||||
@property_hash.clear
|
||||
debug "Call: destroy on cs_resource '#{@resource[:name]}'"
|
||||
pcs 'resource', 'disable', @resource[:name]
|
||||
pcs 'resource', 'cleanup', @resource[:name]
|
||||
pcs 'resource', 'delete', @resource[:name]
|
||||
end
|
||||
|
||||
# Getters that obtains the parameters and operations defined in our primitive
|
||||
|
@ -156,56 +127,42 @@ Puppet::Type.type(:cs_resource).provide(:crm, :parent => Puppet::Provider::Coros
|
|||
@property_hash[:ms_metadata]
|
||||
end
|
||||
|
||||
def multistate_hash
|
||||
@property_hash[:multistate_hash]
|
||||
def complex_type
|
||||
@property_hash[:complex_type]
|
||||
end
|
||||
|
||||
# Our setters for parameters and operations. Setters are used when the
|
||||
# resource already exists so we just update the current value in the
|
||||
# property_hash and doing this marks it to be flushed.
|
||||
def parameters=(should)
|
||||
Puppet.debug "Set paramemter:\n#{should.pretty_inspect}"
|
||||
@property_hash[:parameters] = should
|
||||
end
|
||||
|
||||
def operations=(should)
|
||||
Puppet.debug "Set operations:\n#{should.pretty_inspect}"
|
||||
@property_hash[:operations] = should
|
||||
end
|
||||
|
||||
def metadata=(should)
|
||||
Puppet.debug "Set metadata:\n#{should.pretty_inspect}"
|
||||
@property_hash[:metadata] = should
|
||||
end
|
||||
|
||||
def ms_metadata=(should)
|
||||
Puppet.debug "Set ms_metadata:\n#{should.pretty_inspect}"
|
||||
@property_hash[:ms_metadata] = should
|
||||
end
|
||||
|
||||
def multistate_hash=(should)
|
||||
#Check if we use default multistate name
|
||||
#if it is empty
|
||||
if should[:type] and should[:name].to_s.empty?
|
||||
newname = "#{should[:type]}_#{@property_hash[:name]}"
|
||||
else
|
||||
newname = should[:name]
|
||||
end
|
||||
if (should[:type] != @property_hash[:multistate_hash][:type] and @property_hash[:multistate_hash][:type])
|
||||
#If the type of resource has changed
|
||||
#simply stop and delete it both in live
|
||||
#and shadow cib
|
||||
def complex_type=(should)
|
||||
Puppet.debug "Set complex_type:\n#{should.pretty_inspect}"
|
||||
|
||||
crm('resource', 'stop', "#{@property_hash[:multistate_hash][:name]}")
|
||||
try_command("delete",@property_hash[:multistate_hash][:name])
|
||||
try_command("delete",@property_hash[:multistate_hash][:name],nil,@resource[:cib])
|
||||
elsif
|
||||
#otherwise, stop it and rename it both
|
||||
#in shadow and live cib
|
||||
(should[:type] == @property_hash[:multistate_hash][:type] and @property_hash[:multistate_hash][:type] and
|
||||
newname != @property_hash[:multistate_hash][:name])
|
||||
crm('resource', 'stop', "#{@property_hash[:multistate_hash][:name]}")
|
||||
try_command("rename",@property_hash[:multistate_hash][:name],newname)
|
||||
try_command("rename",@property_hash[:multistate_hash][:name],newname,@resource[:cib])
|
||||
# try to change complex_type of the existing primitive by deleting and recreating it later
|
||||
if should != @property_hash[:complex_type]
|
||||
self.destroy
|
||||
end
|
||||
@property_hash[:multistate_hash][:name] = newname
|
||||
@property_hash[:multistate_hash][:type] = should[:type]
|
||||
|
||||
@property_hash[:complex_type] = should
|
||||
end
|
||||
|
||||
# Flush is triggered on anything that has been detected as being
|
||||
|
@ -215,61 +172,66 @@ Puppet::Type.type(:cs_resource).provide(:crm, :parent => Puppet::Provider::Coros
|
|||
# operations and parameters hash to eventually flatten them into a string
|
||||
# that can be used by the crm command.
|
||||
def flush
|
||||
unless @property_hash.empty?
|
||||
self.class.block_until_ready
|
||||
unless @property_hash[:operations].empty?
|
||||
operations = ''
|
||||
@property_hash[:operations].each do |o|
|
||||
op_namerole = o[0].to_s.split(':')
|
||||
if op_namerole[1]
|
||||
o[1]['role'] = o[1]['role'] || op_namerole[1] # Hash['role'] has more priority, than Name
|
||||
end
|
||||
operations << "op #{op_namerole[0]} "
|
||||
o[1].each_pair do |k,v|
|
||||
operations << "#{k}=#{v} "
|
||||
end
|
||||
end
|
||||
end
|
||||
unless @property_hash[:parameters].empty?
|
||||
parameters = 'params '
|
||||
@property_hash[:parameters].each_pair do |k,v|
|
||||
parameters << "#{k}=#{v} "
|
||||
end
|
||||
end
|
||||
unless @property_hash[:metadata].empty?
|
||||
metadatas = 'meta '
|
||||
@property_hash[:metadata].each_pair do |k,v|
|
||||
metadatas << "#{k}=#{v} "
|
||||
end
|
||||
end
|
||||
updated = "primitive "
|
||||
updated << "#{@property_hash[:name]} #{@property_hash[:primitive_class]}:"
|
||||
updated << "#{@property_hash[:provided_by]}:" if @property_hash[:provided_by]
|
||||
updated << "#{@property_hash[:primitive_type]} "
|
||||
updated << "#{operations} " unless operations.nil?
|
||||
updated << "#{parameters} " unless parameters.nil?
|
||||
updated << "#{metadatas} " unless metadatas.nil?
|
||||
debug "Call: flush on cs_resource '#{@resource[:name]}'"
|
||||
return if @property_hash.empty?
|
||||
self.class.block_until_ready
|
||||
|
||||
if ( @property_hash[:multistate_hash][:type] == "master" or @property_hash[:multistate_hash][:type] == "clone" )
|
||||
debug("creating multistate #{@property_hash[:multistate_hash][:type]} resource for #{@property_hash[:multistate_hash][:name]}")
|
||||
crm_name = @property_hash[:multistate_hash][:type] == "master" ? :ms : :clone
|
||||
updated << "\n"
|
||||
updated << " #{crm_name} #{@property_hash[:multistate_hash][:name]} #{@property_hash[:name]} "
|
||||
unless @property_hash[:ms_metadata].empty?
|
||||
updated << 'meta '
|
||||
@property_hash[:ms_metadata].each_pair do |k,v|
|
||||
updated << "#{k}=#{v} "
|
||||
end
|
||||
unless @property_hash[:operations].empty?
|
||||
operations = ''
|
||||
@property_hash[:operations].each do |o|
|
||||
op_namerole = o[0].to_s.split(':')
|
||||
if op_namerole[1]
|
||||
o[1]['role'] = o[1]['role'] || op_namerole[1] # Hash['role'] has more priority, than Name
|
||||
end
|
||||
operations << "op #{op_namerole[0]} "
|
||||
o[1].each_pair do |k,v|
|
||||
operations << "#{k}=#{v} "
|
||||
end
|
||||
end
|
||||
debug("will update tmp file with #{updated}")
|
||||
Tempfile.open('puppet_crm_update') do |tmpfile|
|
||||
tmpfile.write(updated)
|
||||
tmpfile.flush
|
||||
#env["CIB_shadow"] = @resource[:cib].to_s if !@resource[:cib].nil?
|
||||
##LP1338594 part: should be put into separate method, I guess
|
||||
apply_changes(@resource[:name],tmpfile,'resource')
|
||||
end
|
||||
end
|
||||
|
||||
unless @property_hash[:parameters].empty?
|
||||
parameters = 'params '
|
||||
@property_hash[:parameters].each_pair do |k,v|
|
||||
parameters << "#{k}=#{v} "
|
||||
end
|
||||
end
|
||||
|
||||
unless @property_hash[:metadata].empty?
|
||||
metadatas = 'meta '
|
||||
@property_hash[:metadata].each_pair do |k,v|
|
||||
metadatas << "#{k}=#{v} "
|
||||
end
|
||||
end
|
||||
|
||||
updated = 'primitive '
|
||||
updated << "#{@property_hash[:name]} #{@property_hash[:primitive_class]}:"
|
||||
updated << "#{@property_hash[:provided_by]}:" if @property_hash[:provided_by]
|
||||
updated << "#{@property_hash[:primitive_type]} "
|
||||
updated << "#{operations} " unless operations.nil?
|
||||
updated << "#{parameters} " unless parameters.nil?
|
||||
updated << "#{metadatas} " unless metadatas.nil?
|
||||
|
||||
if @property_hash[:complex_type]
|
||||
complex_name = "#{@property_hash[:complex_type]}_#{@property_hash[:name]}"
|
||||
crm_cmd_type = @property_hash[:complex_type].to_s == 'master' ? 'ms' : 'clone'
|
||||
debug "Creating '#{crm_cmd_type}' parent named '#{complex_name}' for #{@property_hash[:name]} resource"
|
||||
updated << "\n"
|
||||
updated << " #{crm_cmd_type} #{complex_name} #{@property_hash[:name]} "
|
||||
unless @property_hash[:ms_metadata].empty?
|
||||
updated << 'meta '
|
||||
@property_hash[:ms_metadata].each_pair do |k,v|
|
||||
updated << "#{k}=#{v} "
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
debug("Will update tmpfile with '#{updated}'")
|
||||
Tempfile.open('puppet_crm_update') do |tmpfile|
|
||||
tmpfile.write(updated)
|
||||
tmpfile.flush
|
||||
apply_changes(@resource[:name], tmpfile, 'resource')
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -387,9 +387,22 @@ class Puppet::Provider::Pacemaker_common < Puppet::Provider
|
|||
# @param node [String] the node's name
|
||||
# @param score [Numeric,String] score value
|
||||
def constraint_location_add(primitive, node, score = 100)
|
||||
id = "#{primitive}_on_#{node}"
|
||||
id = "#{primitive}-on-#{node}"
|
||||
xml = <<-EOF
|
||||
<diff>
|
||||
<diff-added>
|
||||
<cib>
|
||||
<configuration>
|
||||
<constraints>
|
||||
<rsc_location id="#{id}" node="#{node}" rsc="#{primitive}" score="#{score}"/>
|
||||
</constraints>
|
||||
</configuration>
|
||||
</cib>
|
||||
</diff-added>
|
||||
</diff>
|
||||
EOF
|
||||
retry_command {
|
||||
pcs 'constraint', 'location', 'add', id, primitive, node, score
|
||||
cibadmin '--patch', '--sync-call', '--xml-text', xml
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -74,10 +74,12 @@ module Puppet
|
|||
defining a model and just accept a hash."
|
||||
|
||||
validate do |value|
|
||||
raise Puppet::Error, "Puppet::Type::Cs_Primitive: parameters property must be a hash." unless value.is_a? Hash
|
||||
unless value.is_a? Hash
|
||||
raise Puppet::Error, "Puppet::Type::Cs_Primitive: parameters property must be a hash."
|
||||
end
|
||||
end
|
||||
munge do |parameters|
|
||||
convert_to_sym(parameters)
|
||||
munge do |value|
|
||||
stringify value
|
||||
end
|
||||
defaultto Hash.new
|
||||
end
|
||||
|
@ -91,10 +93,12 @@ module Puppet
|
|||
is valid."
|
||||
|
||||
validate do |value|
|
||||
raise Puppet::Error, "Puppet::Type::Cs_Primitive: operations property must be a hash." unless value.is_a? Hash
|
||||
unless value.is_a? Hash
|
||||
raise Puppet::Error, "Puppet::Type::Cs_Primitive: operations property must be a hash."
|
||||
end
|
||||
end
|
||||
munge do |operations|
|
||||
convert_to_sym(operations)
|
||||
munge do |value|
|
||||
stringify value
|
||||
end
|
||||
defaultto Hash.new
|
||||
end
|
||||
|
@ -107,60 +111,60 @@ module Puppet
|
|||
behavior but have no affect of the data that is synced or manipulated."
|
||||
|
||||
validate do |value|
|
||||
raise Puppet::Error, "Puppet::Type::Cs_Primitive: metadata property must be a hash." unless value.is_a? Hash
|
||||
unless value.is_a? Hash
|
||||
raise Puppet::Error, "Puppet::Type::Cs_Primitive: metadata property must be a hash."
|
||||
end
|
||||
end
|
||||
munge do |metadata|
|
||||
convert_to_sym(metadata)
|
||||
munge do |value|
|
||||
stringify value
|
||||
end
|
||||
defaultto Hash.new
|
||||
|
||||
def insync?(is)
|
||||
status_metadata = %w(target-role is-managed)
|
||||
is_without_state = is.reject do |k, v|
|
||||
status_metadata.include? k.to_s
|
||||
end
|
||||
should_without_state = should.reject do |k, v|
|
||||
status_metadata.include? k.to_s
|
||||
end
|
||||
is_without_state == should_without_state
|
||||
end
|
||||
end
|
||||
|
||||
newproperty(:ms_metadata) do
|
||||
desc "A hash of metadata for the multistate state."
|
||||
|
||||
munge do |value_hash|
|
||||
value_hash.inject({}) do |memo,(key,value)|
|
||||
memo[key] = String(value)
|
||||
memo
|
||||
validate do |value|
|
||||
unless value.is_a? Hash
|
||||
raise Puppet::Error, "Puppet::Type::Cs_Primitive: ms_metadata property must be a hash"
|
||||
end
|
||||
end
|
||||
|
||||
validate do |value|
|
||||
raise Puppet::Error, "Puppet::Type::Cs_Primitive: ms_metadata property must be a hash" unless value.is_a? Hash
|
||||
munge do |value|
|
||||
stringify value
|
||||
end
|
||||
|
||||
defaultto Hash.new
|
||||
|
||||
def insync?(is)
|
||||
status_metadata = %w(target-role is-managed)
|
||||
is_without_state = is.reject do |k, v|
|
||||
status_metadata.include? k.to_s
|
||||
end
|
||||
should_without_state = should.reject do |k, v|
|
||||
status_metadata.include? k.to_s
|
||||
end
|
||||
is_without_state == should_without_state
|
||||
end
|
||||
end
|
||||
|
||||
newproperty(:multistate_hash) do
|
||||
newproperty(:complex_type) do
|
||||
desc "Designates if the primitive is capable of being managed in a multistate
|
||||
state. This will create a new ms or clone resource in your Corosync config and add
|
||||
this primitive to it. Concequently Corosync will be helpful and update all
|
||||
your colocation and order resources too but Puppet won't. Hash contains
|
||||
two key-value pairs: type (master, clone) and its name (${type}_{$primitive_name})
|
||||
by default"
|
||||
|
||||
munge do |value_hash|
|
||||
munged_hash = value_hash.inject({}) do |memo,(key,value)|
|
||||
memo[key.to_sym] = String(value)
|
||||
memo
|
||||
end
|
||||
if munged_hash[:name].to_s.empty? and !munged_hash[:type].to_s.empty?
|
||||
munged_hash[:name] = "#{munged_hash[:type]}_#{@resource[:name]}"
|
||||
end
|
||||
munged_hash
|
||||
end
|
||||
|
||||
validate do |value|
|
||||
raise Puppet::Error, "Puppet::Type::Cs_Resource: multistate_hash property must be a hash" unless
|
||||
value.is_a? Hash
|
||||
|
||||
raise Puppet::Error, "Puppet::Type::Cs_Resource: multistate_hash type property #{value[:type]} must be in master|clone|'' if set" unless
|
||||
["master", "clone", ""].include?(value[:type]) or value[:type] == nil
|
||||
|
||||
end
|
||||
defaultto Hash.new
|
||||
|
||||
newvalues('clone', 'master')
|
||||
end
|
||||
|
||||
autorequire(:cs_shadow) do
|
||||
|
@ -169,29 +173,36 @@ module Puppet
|
|||
Puppet.debug("#{@parameters[:cib].value}")
|
||||
autos << @parameters[:cib].value
|
||||
end
|
||||
|
||||
autos
|
||||
end
|
||||
|
||||
autorequire(:service) do
|
||||
[ 'corosync' ]
|
||||
%w(corosync pacemaker)
|
||||
end
|
||||
|
||||
validate do
|
||||
unless self[:ms_metadata].empty? or self[:complex_type]
|
||||
raise Puppet::Error, 'You should not use ms_metadata if your resource is not clone or master!'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
def convert_to_sym(hash)
|
||||
if hash.is_a? Hash
|
||||
hash.inject({}) do |memo,(key,value)|
|
||||
value = convert_to_sym(value)
|
||||
if value.is_a?(Array)
|
||||
value.collect! do |arr_el|
|
||||
convert_to_sym(arr_el)
|
||||
end
|
||||
end
|
||||
memo[key.to_sym] = value
|
||||
memo
|
||||
# convert data structure to strings
|
||||
def stringify(data)
|
||||
if data.is_a? Hash
|
||||
new_data = {}
|
||||
data.each do |key, value|
|
||||
new_data.store stringify(key), stringify(value)
|
||||
end
|
||||
data.clear
|
||||
data.merge! new_data
|
||||
elsif data.is_a? Array
|
||||
data.map! do |element|
|
||||
stringify element
|
||||
end
|
||||
else
|
||||
hash
|
||||
data.to_s
|
||||
end
|
||||
end
|
||||
end
|
|
@ -46,7 +46,7 @@ cs_resource { 'bar':
|
|||
primitive_class => 'ocf',
|
||||
provided_by => 'pacemaker',
|
||||
primitive_type => 'Dummy',
|
||||
multistate_hash => { type => 'master' },
|
||||
complex_type => 'master',
|
||||
operations => {
|
||||
'monitor' => {
|
||||
'interval' => '20'
|
||||
|
@ -58,7 +58,7 @@ cs_resource { 'blort':
|
|||
primitive_class => 'ocf',
|
||||
provided_by => 'pacemaker',
|
||||
primitive_type => 'Dummy',
|
||||
multistate_hash => { 'type' => 'clone', 'name' => 'blort_clone' },
|
||||
complex_type => 'clone',
|
||||
operations => {
|
||||
'monitor' => {
|
||||
'interval' => '20'
|
||||
|
@ -73,7 +73,7 @@ cs_resource { 'foo':
|
|||
ensure => present,
|
||||
primitive_class => 'ocf',
|
||||
provided_by => 'pacemaker',
|
||||
multistate_hash => { 'type' => 'clone', 'name' => 'super_good_foo_clone' },
|
||||
complex_type => 'clone',
|
||||
primitive_type => 'Dummy',
|
||||
}
|
||||
cs_colocation { 'foo-with-bar':
|
||||
|
|
|
@ -6,7 +6,8 @@ describe Puppet::Type.type(:cs_resource) do
|
|||
end
|
||||
|
||||
it "should have a 'name' parameter" do
|
||||
subject.new(:name => "mock_resource")[:name].should == "mock_resource"
|
||||
type = subject.new(:name => "mock_resource")
|
||||
expect(type[:name]).to eq("mock_resource")
|
||||
end
|
||||
|
||||
describe "basic structure" do
|
||||
|
@ -14,26 +15,26 @@ describe Puppet::Type.type(:cs_resource) do
|
|||
provider_class = Puppet::Type::Cs_resource.provider(Puppet::Type::Cs_resource.providers[0])
|
||||
Puppet::Type::Cs_resource.expects(:defaultprovider).returns(provider_class)
|
||||
|
||||
subject.new(:name => "mock_resource").should_not be_nil
|
||||
expect(subject.new(:name => "mock_resource")).to_not be_nil
|
||||
end
|
||||
|
||||
[:name, :primitive_class, :primitive_type, :provided_by, :cib].each do |param|
|
||||
it "should have a #{param} parameter" do
|
||||
subject.validparameter?(param).should be_true
|
||||
expect(subject.validparameter?(param)).to be_truthy
|
||||
end
|
||||
|
||||
it "should have documentation for its #{param} parameter" do
|
||||
subject.paramclass(param).doc.should be_instance_of(String)
|
||||
expect(subject.paramclass(param).doc).to be_instance_of(String)
|
||||
end
|
||||
end
|
||||
|
||||
[:parameters, :operations, :ms_metadata, :multistate_hash].each do |property|
|
||||
[:parameters, :operations, :ms_metadata, :complex_type].each do |property|
|
||||
it "should have a #{property} property" do
|
||||
subject.validproperty?(property).should be_true
|
||||
expect(subject.validproperty?(property)).to be_truthy
|
||||
end
|
||||
|
||||
it "should have documentation for its #{property} property" do
|
||||
subject.propertybyname(property).doc.should be_instance_of(String)
|
||||
expect(subject.propertybyname(property).doc).to be_instance_of(String)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -41,7 +42,7 @@ describe Puppet::Type.type(:cs_resource) do
|
|||
describe "when validating attributes" do
|
||||
[:parameters, :operations, :metadata, :ms_metadata].each do |attribute|
|
||||
it "should validate that the #{attribute} attribute defaults to a hash" do
|
||||
subject.new(:name => "mock_resource")[:parameters].should == {}
|
||||
expect(subject.new(:name => "mock_resource")[:parameters]).to eq({})
|
||||
end
|
||||
|
||||
it "should validate that the #{attribute} attribute must be a hash" do
|
||||
|
@ -52,17 +53,16 @@ describe Puppet::Type.type(:cs_resource) do
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
it "should validate that the multistate_hash type attribute cannot be other values" do
|
||||
it "should validate that the complex_type type attribute cannot be other values" do
|
||||
["fail", 42].each do |value|
|
||||
expect { subject.new(
|
||||
:name => "mock_resource",
|
||||
:multistate_hash => { :type=> value }
|
||||
:name => "mock_resource",
|
||||
:complex_type => value,
|
||||
) }.to raise_error(Puppet::Error, /(master|clone|\'\')/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe "when autorequiring resources" do
|
||||
|
||||
before :each do
|
||||
|
@ -77,7 +77,7 @@ describe "when autorequiring resources" do
|
|||
|
||||
@catalog.add_resource @resource
|
||||
req = @resource.autorequire
|
||||
req.size.should == 1
|
||||
expect(req.size).to eq(1)
|
||||
#rewrite this f*cking should method of property type by the ancestor method
|
||||
[req[0].target,req[0].source].each do |instance|
|
||||
class << instance
|
||||
|
@ -86,10 +86,57 @@ describe "when autorequiring resources" do
|
|||
end
|
||||
end
|
||||
end
|
||||
req[0].target.should eql(@resource)
|
||||
req[0].source.should eql(@shadow)
|
||||
expect(req[0].target).to eql(@resource)
|
||||
expect(req[0].source).to eql(@shadow)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
describe 'special insync conditions' do
|
||||
before :each do
|
||||
@type = subject.new (
|
||||
{
|
||||
:name => 'my_resource',
|
||||
:ms_metadata => {
|
||||
'a' => 1,
|
||||
'is-managed' => 'true',
|
||||
},
|
||||
:metadata => {
|
||||
'a' => 2,
|
||||
'is-managed' => 'true',
|
||||
},
|
||||
:complex_type => 'master',
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
it 'should ignore status metadata from ms_metadata hash comparison' do
|
||||
ms_metadata = @type.property(:ms_metadata)
|
||||
expect(ms_metadata.insync?({"a" => "1", "is-managed" => "false"})).to be_truthy
|
||||
end
|
||||
|
||||
it 'should ignore status metadata from metadata hash comparison' do
|
||||
metadata = @type.property(:metadata)
|
||||
expect(metadata.insync?({"a" => "2", "is-managed" => "false"})).to be_truthy
|
||||
end
|
||||
|
||||
it 'should compare non-status ms_metadata' do
|
||||
ms_metadata = @type.property(:ms_metadata)
|
||||
expect(ms_metadata.insync?({'a' => 2})).to be_falsey
|
||||
end
|
||||
|
||||
it 'should compare non-status metadata' do
|
||||
metadata = @type.property(:metadata)
|
||||
expect(metadata.insync?({'a' => 1})).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
describe 'munging of input data' do
|
||||
it 'should convert hash keys and values to strings' do
|
||||
@type = subject.new({:name => 'myresource'})
|
||||
@type[:ms_metadata] = { :a => 1, 'b' => true, 'c' => { :a => true, 'b' => :s, 4 => 'd' } }
|
||||
expect(@type[:ms_metadata]).to eq({"a"=>"1", "b"=>"true", "c"=>{"a"=>"true", "b"=>"s", "4"=>"d"}})
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -30,7 +30,7 @@ cs_resource { 'blort':
|
|||
primitive_class => 'ocf',
|
||||
provided_by => 'pacemaker',
|
||||
primitive_type => 'Dummy',
|
||||
multistate_hash => { type => 'master' },
|
||||
complex_type => 'master',
|
||||
operations => {
|
||||
'monitor' => {
|
||||
'interval' => '20'
|
||||
|
|
|
@ -165,9 +165,7 @@ class galera (
|
|||
primitive_class => 'ocf',
|
||||
provided_by => 'mirantis',
|
||||
primitive_type => 'mysql-wss',
|
||||
multistate_hash => {
|
||||
'type' => 'clone',
|
||||
},
|
||||
complex_type => 'clone',
|
||||
parameters => {
|
||||
'test_user' => "${mysql_user}",
|
||||
'test_passwd' => "${mysql_password}",
|
||||
|
|
|
@ -24,10 +24,6 @@ class heat_ha::engine inherits heat::engine {
|
|||
},
|
||||
}
|
||||
|
||||
$multistate_hash = {
|
||||
'type' => 'clone',
|
||||
}
|
||||
|
||||
$ms_metadata = {
|
||||
'interleave' => true,
|
||||
}
|
||||
|
@ -35,10 +31,10 @@ class heat_ha::engine inherits heat::engine {
|
|||
pacemaker_wrappers::service { $service_name :
|
||||
primitive_type => $primitive_type,
|
||||
metadata => $metadata,
|
||||
multistate_hash => $multistate_hash,
|
||||
complex_type => 'clone',
|
||||
ms_metadata => $ms_metadata,
|
||||
operations => $operations,
|
||||
ocf_script_template => $ocf_script_template,
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -171,8 +171,9 @@ class mysql::server (
|
|||
primitive_class => 'ocf',
|
||||
provided_by => 'heartbeat',
|
||||
primitive_type => 'mysql',
|
||||
multistate_hash => {'type'=>'master'},
|
||||
ms_metadata => {'notify' => "true"},
|
||||
cib => 'mysql',
|
||||
complex_type => 'master',
|
||||
ms_metadata => {'notify' => "true"},
|
||||
parameters => {
|
||||
'binary' => "/usr/bin/mysqld_safe",
|
||||
'test_table' => 'mysql.user',
|
||||
|
|
|
@ -140,13 +140,11 @@ class nova::rabbitmq(
|
|||
'failure-timeout' => '60s'
|
||||
|
||||
},
|
||||
multistate_hash => {
|
||||
'type' => 'master',
|
||||
},
|
||||
complex_type => 'master',
|
||||
ms_metadata => {
|
||||
'notify' => 'true',
|
||||
'ordered' => 'false', # We shouldn't enable ordered start for parallel start of RA.
|
||||
'interleave' => 'true',
|
||||
'interleave' => 'true',
|
||||
'master-max' => '1',
|
||||
'master-node-max' => '1',
|
||||
'target-role' => 'Master'
|
||||
|
|
|
@ -9,7 +9,7 @@ define pacemaker_wrappers::service (
|
|||
$operations = undef,
|
||||
$metadata = undef,
|
||||
$ms_metadata = undef,
|
||||
$multistate_hash = undef,
|
||||
$complex_type = undef,
|
||||
|
||||
$use_handler = true,
|
||||
$handler_root_path = '/usr/local/bin',
|
||||
|
@ -48,7 +48,7 @@ define pacemaker_wrappers::service (
|
|||
operations => $operations,
|
||||
metadata => $metadata,
|
||||
ms_metadata => $ms_metadata,
|
||||
multistate_hash => $multistate_hash,
|
||||
complex_type => $complex_type,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue