Files
puppet-pacemaker/lib/puppet/provider/pacemaker_resource/pacemaker_xml.rb
Dmitry Ilyin 4d2e554f68 Merge with fuel-infra/puppet-pacemaker
* Import all providers, specs and tests to this module
* Use corosync module to actually install paceamker

Fuel-CI: disable
Change-Id: I9a16ad1453b694aa0a3e78d079f9c57365a5fcf1
2016-05-25 11:22:22 +02:00

351 lines
11 KiB
Ruby

require_relative '../pacemaker_xml'
require 'set'
Puppet::Type.type(:pacemaker_resource).provide(:xml, parent: Puppet::Provider::PacemakerXML) do
desc <<-eof
Specific provider for a rather specific type since I currently have no
plan to abstract corosync/pacemaker vs. keepalived. Primitives in
Corosync 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 Corosync calls
operations and parameters. A hash is used instead of constucting a
better model since these values can be almost anything.'
eof
commands cibadmin: 'cibadmin'
commands crm_attribute: 'crm_attribute'
commands crm_node: 'crm_node'
commands crm_resource: 'crm_resource'
commands crm_attribute: 'crm_attribute'
attr_accessor :property_hash
attr_accessor :resource
def self.instances
debug 'Call: self.instances'
proxy_instance = new
instances = []
proxy_instance.primitives.map do |title, data|
parameters = {}
debug "Prefetch resource: #{title}"
proxy_instance.retrieve_data data, parameters
instance = new(parameters)
instance.cib = proxy_instance.cib
instances << instance
end
instances
end
def self.prefetch(catalog_instances)
debug 'Call: self.prefetch'
return unless pacemaker_options[:prefetch]
discovered_instances = instances
discovered_instances.each do |instance|
next unless catalog_instances.key? instance.name
catalog_instances[instance.name].provider = instance
end
end
# retrieve data from library to the target_structure
# @param data [Hash] extracted primitive data
# will extract the current primitive data unless a value is provided
# @param target_structure [Hash] copy data to this structure
# defaults to the property_hash of this provider
def retrieve_data(data = nil, target_structure = property_hash)
debug 'Call: retrieve_data'
data = primitives.fetch resource[:name], {} unless data
target_structure[:ensure] = :present
target_structure[:complex_type] = :simple
copy_value data, 'id', target_structure, :name
copy_value data, 'class', target_structure, :primitive_class
copy_value data, 'provider', target_structure, :primitive_provider
copy_value data, 'type', target_structure, :primitive_type
if data['complex']
data_complex_type = data['complex']['type'].to_sym
target_structure[:complex_type] = data_complex_type if complex_types.include? data_complex_type
complex_metadata = import_attributes_structure data['complex']['meta_attributes']
target_structure[:complex_metadata] = complex_metadata if complex_metadata
end
if data['instance_attributes']
parameters_data = import_attributes_structure data['instance_attributes']
if parameters_data && parameters_data.is_a?(Hash)
target_structure[:parameters] = parameters_data if parameters_data
end
end
if data['meta_attributes']
metadata_data = import_attributes_structure data['meta_attributes']
if metadata_data && metadata_data.is_a?(Hash)
target_structure[:metadata] = metadata_data
end
end
if data['operations']
operations_set = Set.new
data['operations'].each do |_id, operation|
operation.delete 'id'
operations_set.add operation
end
target_structure[:operations] = operations_set
end
end
def exists?
debug 'Call: exists?'
out = primitive_exists? resource[:name]
retrieve_data
debug "Return: #{out}"
out
end
# check if the location ensure is set to present
# @return [TrueClass,FalseClass]
def present?
property_hash[:ensure] == :present
end
# check if the complex type of the resource is changing
# and we have to recreate it
# @return [true,false]
def complex_change?
current_complex_type = primitive_complex_type(name) || :simple
current_complex_type != complex_type
end
# is this primitive complex?
# @return [true,false]
def is_complex?
complex_types.include? complex_type
end
# list of the actually supported complex types
# @return [Array<Symbol>]
def complex_types
[:clone, :master]
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'
self.property_hash = {
ensure: :absent,
name: resource[:name],
}
parameters = [
:primitive_class,
:primitive_provider,
:primitive_type,
:parameters,
:operations,
:metadata,
:complex_type,
:complex_metadata,
]
parameters.each do |parameter|
send "#{parameter}=".to_sym, resource[parameter]
end
end
# use cibadmin to remove the XML section describing this primitive
def remove_primitive
return unless primitive_exists? resource[:name]
stop_service
primitive_tag = 'primitive'
primitive_tag = primitive_complex_type resource[:name] if primitive_is_complex? resource[:name]
wait_for_primitive_remove "<#{primitive_tag} id='#{primitive_full_name resource[:name]}'/>\n", resource[:name]
property_hash[:ensure] = :absent
end
# stop the primitive before its removal
def stop_service
stop_primitive primitive_full_name resource[:name]
cleanup_primitive primitive_full_name resource[:name]
wait_for_stop resource[:name]
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 'Call: destroy'
remove_primitive
property_hash.clear
cluster_debug_report "#{resource} destroy"
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 complex_metadata
property_hash[:complex_metadata]
end
def complex_type
if property_hash[:complex_type].respond_to? :to_sym
property_hash[:complex_type].to_sym
else
property_hash[:complex_type]
end
end
def primitive_class
property_hash[:primitive_class]
end
def primitive_provider
property_hash[:primitive_provider]
end
def primitive_type
property_hash[:primitive_type]
end
def full_name
if is_complex?
"#{name}-#{complex_type}"
else
name
end
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)
property_hash[:parameters] = should
end
def operations=(should)
should = should.first if should.is_a? Array
property_hash[:operations] = should
end
def metadata=(should)
property_hash[:metadata] = should
end
def complex_metadata=(should)
property_hash[:complex_metadata] = should
end
def complex_type=(should)
property_hash[:complex_type] = should
end
def primitive_class=(should)
property_hash[:primitive_class] = should
end
def primitive_provider=(should)
property_hash[:primitive_provider] = should
end
def primitive_type=(should)
property_hash[:primitive_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'
return unless property_hash && property_hash.any?
unless primitive_class && primitive_type
raise 'Primitive class and type should be present!'
end
# if the complex type is changing we have to remove the resource
# and create a new one with the correct complex type
if complex_change?
debug 'Changing the complex type of the primitive. First remove and then create it!'
remove_primitive
end
# basic primitive structure
primitive_structure = {}
primitive_structure['id'] = name
primitive_structure['name'] = full_name
primitive_structure['class'] = primitive_class
primitive_structure['provider'] = primitive_provider if primitive_provider
primitive_structure['type'] = primitive_type
# complex structure
if is_complex?
complex_structure = {}
complex_structure['type'] = complex_type
complex_structure['id'] = full_name
# complex meta_attributes structure
if complex_metadata && complex_metadata.any?
meta_attributes_structure = export_attributes_structure complex_metadata, 'meta_attributes'
complex_structure['meta_attributes'] = meta_attributes_structure if meta_attributes_structure
end
primitive_structure['complex'] = complex_structure
end
# operations structure
if operations && operations.any?
primitive_structure['operations'] = {}
operations.each do |operation|
if operation.is_a?(Array) && operation.length == 2
# operations were provided and Hash { name => { parameters } }, convert it
operation_name = operation[0]
operation = operation[1]
operation['name'] = operation_name unless operation['name']
end
unless operation['id']
# there is no id provided, generate it
id_components = [name, operation['name'], operation['interval']]
id_components.reject!(&:nil?)
operation['id'] = id_components.join '-'
end
primitive_structure['operations'].store operation['id'], operation
end
end
# instance_attributes structure
if parameters && parameters.any?
instance_attributes_structure = export_attributes_structure parameters, 'instance_attributes'
primitive_structure['instance_attributes'] = instance_attributes_structure if instance_attributes_structure
end
# meta_attributes structure
if metadata && metadata.any?
meta_attributes_structure = export_attributes_structure metadata, 'meta_attributes'
primitive_structure['meta_attributes'] = meta_attributes_structure
end
# create and apply XML patch
primitive_patch = xml_document
primitive_element = xml_primitive primitive_structure
raise "Could not create XML patch for '#{resource}'" unless primitive_element
primitive_patch.add_element primitive_element
if present?
wait_for_primitive_update xml_pretty_format(primitive_patch.root), primitive_structure['id']
else
wait_for_primitive_create xml_pretty_format(primitive_patch.root), primitive_structure['id']
end
cluster_debug_report "#{resource} flush"
end
end