require File.join File.dirname(__FILE__), '../pacemaker_common.rb'

Puppet::Type.type(:service).provide :pacemaker, :parent => Puppet::Provider::Pacemaker_common do

  has_feature :enableable
  has_feature :refreshable

  commands :uname => 'uname'
  commands :pcs => 'pcs'
  commands :crm_resource => 'crm_resource'
  commands :crm_attribute => 'crm_attribute'
  commands :cibadmin => 'cibadmin'

  # hostname of the current node
  # @return [String]
  def hostname
    return @hostname if @hostname
    @hostname = (uname '-n').chomp.strip
  end

  # original name passed from the type
  # @return [String]
  def title
    @resource[:name]
  end

  # primitive name with 'p_' added if needed
  # @return [String]
  def name
    return @name if @name
    primitive_name = title
    if primitive_exists? primitive_name
      Puppet.debug "Primitive with title '#{primitive_name}' was found in CIB"
      @name = primitive_name
      return @name
    end
    primitive_name = "p_#{primitive_name}"
    if primitive_exists? primitive_name
      Puppet.debug "Using '#{primitive_name}' name instead of '#{title}'"
      @name = primitive_name
      return @name
    end
    fail "Primitive '#{title}' was not found in CIB!"
  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 = primitives[name]['name']
      Puppet.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
  # @return [String]
  def basic_service_name
    return @basic_service_name if @basic_service_name
    if name.start_with? 'p_'
      basic_service_name = name.gsub /^p_/, ''
      Puppet.debug "Using '#{basic_service_name}' as the basic service name for primitive '#{name}'"
      @basic_service_name = basic_service_name
    else
      @basic_service_name = name
    end
  end

  # cleanup a primitive and
  # wait for cleanup to finish
  def cleanup
    cleanup_primitive full_name, hostname
    wait_for_status name
  end

  # called by Puppet to determine if the service
  # is running on the local node
  # @return [:running,:stopped]
  def status
    wait_for_online
    Puppet.debug "Call: 'status' for Pacemaker service '#{name}' on node '#{hostname}'"
    cib_reset
    out = get_primitive_puppet_status name, hostname
    Puppet.debug get_cluster_debug_report
    Puppet.debug "Return: '#{out}' (#{out.class})"
    out
  end

  # called by Puppet to start the service
  def start
    Puppet.debug "Call 'start' for Pacemaker service '#{name}' on node '#{hostname}'"
    enable unless primitive_is_managed? name
    disable_basic_service
    constraint_location_add full_name, hostname
    cleanup
    unban_primitive name, hostname
    start_primitive full_name
    start_primitive name

    if primitive_is_multistate? name
      Puppet.debug "Choose master start for Pacemaker service '#{name}'"
      wait_for_master name
    else
      Puppet.debug "Choose global start for Pacemaker service '#{name}'"
      wait_for_start name
    end
  end

  # called by Puppet to stop the service
  def stop
    Puppet.debug "Call 'stop' for Pacemaker service '#{name}' on node '#{hostname}'"
    cleanup
    enable unless primitive_is_managed? name

    if primitive_is_complex? name
      Puppet.debug "Choose local stop for Pacemaker service '#{name}' on node '#{hostname}'"
      ban_primitive name, hostname
      wait_for_stop name, hostname
    else
      Puppet.debug "Choose global stop for Pacemaker service '#{name}'"
      stop_primitive name
      wait_for_stop name
    end
  end

  # called by Puppet to restart the service
  def restart
    Puppet.debug "Call 'restart' for Pacemaker service '#{name}' on node '#{hostname}'"
    unless 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

  # called by Puppet to enable the service
  def enable
    Puppet.debug "Call 'enable' for Pacemaker service '#{name}' on node '#{hostname}'"
    manage_primitive name
  end

  # called by Puppet to disable  the service
  def disable
    Puppet.debug "Call 'disable' for Pacemaker service '#{name}' on node '#{hostname}'"
    unmanage_primitive name
  end
  alias :manual_start :disable

  # called by Puppet to determine if the service is enabled
  # @return [:true,:false]
  def enabled?
    Puppet.debug "Call 'enabled?' for Pacemaker service '#{name}' on node '#{hostname}'"
    out = get_primitive_puppet_enable name
    Puppet.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.warning "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
    return unless extra_provider
    begin
      if extra_provider.enableable? and 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
       if not ['lsb','systemd','upstart'].include?(primitive_class name)
          Puppet.info "Stop basic service '#{extra_provider.name}' using provider '#{extra_provider.class.name}'"
          extra_provider.stop
        else
          Puppet.info "Not stopping basic service '#{extra_provider.name}', since its Pacemaker primitive is using primitive_class '#{extra_provider.class.name}'"
        end
      else
        Puppet.info "Basic service '#{extra_provider.name}' is stopped as reported by '#{extra_provider.class.name}' provider"
      end
    rescue => e
      Puppet.warning "Could not disable basic service for Pacemaker primitive '#{name}' using '#{extra_provider.class.name}' provider: #{e.message}"
    end
  end

end
