fuel-library/deployment/puppet/pacemaker/lib/puppet/provider/cs_resource/crm.rb

239 lines
8.6 KiB
Ruby

require File.join File.dirname(__FILE__), '../pacemaker_base.rb'
require 'pp'
Puppet::Type.type(:cs_resource).provide(:crm, :parent => Puppet::Provider::Pacemaker) do
desc 'Specific provider for a rather specific type since I currently have no
plan to abstract pacemaker vs. keepalived. Primitives in
Pacemaker are the thing we desire to monitor; websites, ipaddresses,
databases, etc, etc. Here we manage the creation and deletion of
these primitives. We will accept a hash for what Pacemaker calls
operations and parameters. A hash is used instead of constucting a
better model since these values can be almost anything.'
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 = []
raw, status = dump_cib
doc = REXML::Document.new(raw)
REXML::XPath.each(doc, '//primitive') do |e|
items = e.attributes
primitive = {
:ensure => :present,
:name => items['id'].to_s,
:primitive_class => items['class'].to_s,
:primitive_type => items['type'].to_s,
:provided_by => items['provider'].to_s,
}
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
if o.elements['instance_attributes']
o.elements['instance_attributes'].each_element do |inst|
primitive[:operations][op_name].store inst.attributes['name'].to_s, inst.attributes['value'].to_s
end
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
# 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],
:complex_type => @resource[:complex_type],
}
@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. Pacemaker forces us
# to "stop" the primitive before we are able to remove it.
def destroy
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
# that have been populated by prefetch or instances (depends on if your using
# puppet resource or not).
def parameters
@property_hash[:parameters]
end
def operations
@property_hash[:operations]
end
def metadata
@property_hash[:metadata]
end
def ms_metadata
@property_hash[:ms_metadata]
end
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 complex_type=(should)
Puppet.debug "Set complex_type:\n#{should.pretty_inspect}"
# 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[:complex_type] = should
end
# Flush is triggered on anything that has been detected as being
# modified in the property_hash. It generates a temporary file with
# the updates that need to be made. The temporary file is then used
# as stdin for the crm command. We have to do a bit of munging of our
# operations and parameters hash to eventually flatten them into a string
# that can be used by the crm command.
def flush
debug "Call: flush on cs_resource '#{@resource[:name]}'"
return if @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}' " unless v.empty?
end
end
end
unless @property_hash[:parameters].empty?
parameters = @property_hash[:parameters].reject{|k,v| v.empty?}.map{|k,v| "#{k}='#{v}'"}.join(' ')
parameters = (parameters.empty? ? nil : "params #{parameters}")
end
unless @property_hash[:metadata].empty?
metadatas = @property_hash[:metadata].reject{|k,v| v.empty?}.map{|k,v| "#{k}='#{v}'"}.join(' ')
metadatas = (metadatas.empty? ? nil : "meta #{metadatas}")
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?
ms_metadatas = @property_hash[:ms_metadata].reject{|k,v| v.empty?}.map{|k,v| "#{k}='#{v}'"}.join(' ')
unless ms_metadatas.empty?
updated << "meta #{ms_metadatas}"
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