Files
puppet-pacemaker/lib/puppet/provider/service/pacemaker_xml.rb
Dmitry Ilyin c1c2031c84 Fixes for race condition when adding primitives without OCF present
* Change the servie "status" to report "stopped" if the
  primitive has failures on the node.
* Enable "status" failure check.
* Support error detection for a missing OCF monitor operations.
* Add operations status debug method
* Add forgotten cib_reset to the wait_for_online

Change-Id: I34fbf8b4a7d2420fb568719f473bc9b40063cc82
2017-02-23 17:45:07 +00:00

369 lines
12 KiB
Ruby

require_relative '../pacemaker_xml'
Puppet::Type.type(:service).provide(:pacemaker_xml, parent: Puppet::Provider::PacemakerXML) do
has_feature :enableable
has_feature :refreshable
commands crm_node: 'crm_node'
commands crm_resource: 'crm_resource'
commands crm_attribute: 'crm_attribute'
commands cibadmin: 'cibadmin'
# original title of the service
# @return [String]
def service_title
@resource.title
end
# original name of the service
# in most cases will be equal to the title
# but can be different
# @return [String]
def service_name
resource[:name]
end
# check if the service name is the same as service title
# @return [true,false]
def name_equals_title?
service_title == service_name
end
# find a primitive name that is present in the CIB
# or nil if none is present
# @return [String,nil]
def pick_existing_name(*names)
names.flatten.find do |name|
primitive_exists? name
end
end
# generate a list of strings the service name could be written as
# perhaps, one of them could be found in the CIB
# @param name [String]
# @return [Array<String>]
def service_name_variations(name)
name = name.to_s
variations = []
variations << name
variations << if name.start_with? 'p_'
name.gsub(/^p_/, '')
else
"p_#{name}"
end
base_name = primitive_base_name name
unless base_name == name
variations << base_name
variations << if base_name.start_with? 'p_'
base_name.gsub(/^p_/, '')
else
"p_#{base_name}"
end
end
variations
end
# get the correct name of the service primitive
# @return [String]
def name
return @name if @name
@name = pick_existing_name service_name_variations(service_title), service_name_variations(service_name)
if @name
message = "Using CIB name '#{@name}' for primitive '#{service_title}'"
message += " with name '#{service_name}'" unless name_equals_title?
debug message
else
message = "Primitive '#{service_title}'"
message += " with name '#{service_name}'" unless name_equals_title?
message += ' was not found in CIB!'
raise message
end
@name
end
# full name of the primitive
# if resource is complex use group name
# @return [String]
def full_name
return @full_name if @full_name
if primitive_is_complex? name
full_name = primitive_full_name name
debug "Using full name '#{full_name}' for complex primitive '#{name}'"
@full_name = full_name
else
@full_name = name
end
end
# name of the basic service without 'p_' prefix
# used to disable the basic service.
# Uses "name" property if it's not the same as title
# because most likely it will be the real system service name
# @return [String]
def basic_service_name
return @basic_service_name if @basic_service_name
basic_service_name = name
basic_service_name = service_name unless name_equals_title?
if basic_service_name.start_with? 'p_'
basic_service_name = basic_service_name.gsub(/^p_/, '')
end
debug "Using '#{basic_service_name}' as the basic service name for the primitive '#{name}'"
@basic_service_name = basic_service_name
end
# cleanup a primitive and
# wait until cleanup finishes
def cleanup
cleanup_primitive full_name, hostname
wait_for_status name
end
# run the disable basic service action only
# if it's enabled fot this provider action
# and is globally enabled too
# @param [Symbol] action (:start/:stop/:status)
def disable_basic_service_on_action(action)
if action == :start
return unless pacemaker_options[:disable_basic_service_on_start]
elsif action == :stop
return unless pacemaker_options[:disable_basic_service_on_stop]
elsif action == :status
return unless pacemaker_options[:disable_basic_service_on_status]
else
fail "Action '#{action}' is incorrect!"
end
disable_basic_service
end
# called by Puppet to determine if the service
# is running on the local node
# @return [:running,:stopped]
def status
debug "Call: 'status' for Pacemaker service '#{name}' on node '#{hostname}'"
disable_basic_service_on_action :status
cib_reset 'service_status'
wait_for_online 'service_status'
out = if primitive_is_master? name
service_status_mode pacemaker_options[:status_mode_master]
elsif primitive_is_clone? name
service_status_mode pacemaker_options[:status_mode_clone]
else
service_status_mode pacemaker_options[:status_mode_simple]
end
if pacemaker_options[:add_location_constraint]
if out == :running && (!service_location_exists? full_name, hostname)
debug 'Location constraint is missing. Service status set to "stopped".'
out = :stopped
end
end
if pacemaker_options[:cleanup_on_status]
if out == :running and primitive_has_failures? name, hostname
debug "Primitive: '#{name}' has failures on the node: '#{hostname}' Service status set to 'stopped'."
out = :stopped
end
end
debug "Return: '#{out}' (#{out.class})"
debug cluster_debug_report "#{@resource} status"
out
end
# called by Puppet to start the service
def start
debug "Call 'start' for Pacemaker service '#{name}' on node '#{hostname}'"
disable_basic_service_on_action :start
enable unless primitive_is_managed? name
if pacemaker_options[:cleanup_on_start]
if !pacemaker_options[:cleanup_only_if_failures] || primitive_has_failures?(name, hostname)
cleanup
end
end
if pacemaker_options[:add_location_constraint]
service_location_add full_name, hostname unless service_location_exists? full_name, hostname
end
unban_primitive name, hostname
start_primitive name
start_primitive full_name
if primitive_is_master? name
debug "Choose master start for Pacemaker service '#{name}'"
wait_for_master name
else
service_start_mode pacemaker_options[:start_mode_simple]
end
debug cluster_debug_report "#{@resource} start"
end
# called by Puppet to stop the service
def stop
debug "Call 'stop' for Pacemaker service '#{name}' on node '#{hostname}'"
disable_basic_service_on_action :stop
enable unless primitive_is_managed? name
if pacemaker_options[:cleanup_on_stop]
if !pacemaker_options[:cleanup_only_if_failures] || primitive_has_failures?(name, hostname)
cleanup
end
end
if pacemaker_options[:add_location_constraint]
service_location_remove full_name, hostname if service_location_exists? full_name, hostname
end
if primitive_is_master? name
service_stop_mode pacemaker_options[:stop_mode_master]
elsif primitive_is_clone? name
service_stop_mode pacemaker_options[:stop_mode_clone]
else
service_stop_mode pacemaker_options[:stop_mode_simple]
end
debug cluster_debug_report "#{@resource} stop"
end
# called by Puppet to restart the service
def restart
debug "Call 'restart' for Pacemaker service '#{name}' on node '#{hostname}'"
if pacemaker_options[:restart_only_if_local] && (!primitive_is_running? name, hostname)
Puppet.info "Pacemaker service '#{name}' is not running on node '#{hostname}'. Skipping restart!"
return
end
begin
stop
rescue
nil
ensure
start
end
end
# wait for the service to start using
# the selected method.
# @param mode [:global, :master, :local]
def service_start_mode(mode = :global)
if mode == :master
debug "Choose master start for Pacemaker service '#{name}'"
wait_for_master name
elsif mode == :local
debug "Choose local start for Pacemaker service '#{name}' on node '#{hostname}'"
wait_for_start name, hostname
elsif mode == :global
debug "Choose global start for Pacemaker service '#{name}'"
wait_for_start name
else
raise "Unknown service start mode '#{mode}'"
end
end
# wait for the service to stop using
# the selected method.
# @param mode [:global, :master, :local]
def service_stop_mode(mode = :global)
if mode == :local
debug "Choose local stop for Pacemaker service '#{name}' on node '#{hostname}'"
ban_primitive name, hostname
wait_for_stop name, hostname
elsif mode == :global
debug "Choose global stop for Pacemaker service '#{name}'"
stop_primitive name
wait_for_stop name
else
raise "Unknown service stop mode '#{mode}'"
end
end
# determine the status of the service using
# the selected method.
# @param mode [:global, :master, :local]
# @return [:running,:stopped]
def service_status_mode(mode = :local)
if mode == :local
debug "Choose local status for Pacemaker service '#{name}' on node '#{hostname}'"
get_primitive_puppet_status name, hostname
elsif mode == :global
debug "Choose global status for Pacemaker service '#{name}'"
get_primitive_puppet_status name
else
raise "Unknown service status mode '#{mode}'"
end
end
# called by Puppet to enable the service
def enable
debug "Call 'enable' for Pacemaker service '#{name}' on node '#{hostname}'"
manage_primitive name
end
# called by Puppet to disable the service
def disable
debug "Call 'disable' for Pacemaker service '#{name}' on node '#{hostname}'"
unmanage_primitive name
end
alias_method :manual_start, :disable
# called by Puppet to determine if the service is enabled
# @return [:true,:false]
def enabled?
debug "Call 'enabled?' for Pacemaker service '#{name}' on node '#{hostname}'"
out = get_primitive_puppet_enable name
debug "Return: '#{out}' (#{out.class})"
out
end
# create an extra provider instance to deal with the basic service
# the provider will be chosen to match the current system
# @return [Puppet::Type::Service::Provider]
def extra_provider(provider_name = nil)
return @extra_provider if @extra_provider
begin
param_hash = {}
param_hash.store :name, basic_service_name
param_hash.store :provider, provider_name if provider_name
type = Puppet::Type::Service.new param_hash
@extra_provider = type.provider
rescue => e
Puppet.info "Could not get extra provider for Pacemaker primitive '#{name}': #{e.message}"
@extra_provider = nil
end
end
# disable and stop the basic service
def disable_basic_service
# skip native-based primitive classes
if pacemaker_options[:native_based_primitive_classes].include?(primitive_class name)
Puppet.info "Not stopping basic service '#{basic_service_name}', since its Pacemaker primitive is using primitive_class '#{primitive_class name}'"
return
end
return unless extra_provider
begin
if extra_provider.enableable? && extra_provider.enabled? == :true
Puppet.info "Disable basic service '#{extra_provider.name}' using provider '#{extra_provider.class.name}'"
extra_provider.disable
else
Puppet.info "Basic service '#{extra_provider.name}' is disabled as reported by '#{extra_provider.class.name}' provider"
end
if extra_provider.status == :running
Puppet.info "Stop basic service '#{extra_provider.name}' using provider '#{extra_provider.class.name}'"
extra_provider.stop
else
Puppet.info "Basic service '#{extra_provider.name}' is stopped as reported by '#{extra_provider.class.name}' provider"
end
rescue => e
Puppet.info "Could not disable basic service for Pacemaker primitive '#{name}' using '#{extra_provider.class.name}' provider: #{e.message}"
end
end
end