sensu-plugins/sensu-plugins-puppet

View on GitHub
bin/check-puppet-last-run.rb

Summary

Maintainability
C
1 day
Test Coverage
#! /usr/bin/env ruby
# frozen_string_literal: true

#
# check-puppet-last-run
#
# DESCRIPTION:
#   Check the last time puppet was last run
#
# OUTPUT:
#   plain-text
#
# PLATFORMS:
#   Linux
#
# DEPENDENCIES:
#   gem: sensu-plugin
#
# USAGE:
#   Critical if last run is greater than
#
#   check-puppet-last-run --summary-file /opt/puppetlabs/puppet/cache/state/last_run_summary.yaml --warn-age 3600 --crit-age 7200
#
# NOTES:
#
# LICENSE:
#   Copyright 2014 Sonian, Inc. and contributors. <support@sensuapp.org>
#   Released under the same terms as Sensu (the MIT license); see LICENSE
#   for details.
#

require 'sensu-plugins-puppet'
require 'sensu-plugin/check/cli'
require 'yaml'
require 'json'
require 'time'

class PuppetLastRun < Sensu::Plugin::Check::CLI
  option :summary_file,
         short: '-s PATH',
         long: '--summary-file PATH',
         default: SensuPluginsPuppet::SUMMARY_FILE,
         description: 'Location of last_run_summary.yaml file'

  option :warn_age,
         short: '-w N',
         long: '--warn-age SECONDS',
         default: 3600,
         proc: proc(&:to_i),
         description: 'Age in seconds to be a warning'

  option :crit_age,
         short: '-c N',
         long: '--crit-age SECONDS',
         default: 7200,
         proc: proc(&:to_i),
         description: 'Age in seconds to be a critical'

  option :agent_disabled_file,
         short: '-a PATH',
         long: '--agent-disabled-file PATH',
         default: SensuPluginsPuppet::AGENT_DISABLED_FILE,
         description: 'Path to agent disabled lock file'

  option :disabled_age_limits,
         short: '-d',
         long: '--disabled-age-limits',
         boolean: true,
         default: false,
         description: 'Consider disabled age limits, otherwise use main limits'

  option :warn_age_disabled,
         short: '-W N',
         long: '--warn-age-disabled SECONDS',
         default: 3600,
         proc: proc(&:to_i),
         description: 'Age in seconds to warn when agent is disabled'

  option :crit_age_disabled,
         short: '-C N',
         long: '--crit-age-disabled SECONDS',
         default: 7200,
         proc: proc(&:to_i),
         description: 'Age in seconds to crit when agent is disabled'

  option :report_restart_failures,
         short: '-r',
         long: '--report-restart-failures',
         boolean: true,
         default: false,
         description: 'Raise alerts if restart failures have happened'

  option :ignore_failures,
         short: '-i',
         long: '--ignore-failures',
         boolean: true,
         default: false,
         description: 'Ignore Puppet failures'

  def run
    unless File.exist?(config[:summary_file])
      unknown "File #{config[:summary_file]} not found"
    end

    @now = Time.now.to_i
    @failures = 0
    @restart_failures = 0

    begin
      summary = YAML.load_file(config[:summary_file])
      if summary['time']
        @last_run = summary['time']['last_run'].to_i
      else
        critical "#{config[:summary_file]} is missing information about the last run timestamp"
      end
      unless config[:ignore_failures]
        if summary['events']
          @failures = summary['events']['failure'].to_i
        else
          critical "#{config[:summary_file]} is missing information about the events"
        end
        @restart_failures = if config[:report_restart_failures] && summary['resources']
                              summary['resources']['failed_to_restart'].to_i
                            else
                              0
                            end
      end
    rescue StandardError
      unknown "Could not process #{config[:summary_file]}"
    end

    @message = "Puppet last run #{formatted_duration(@now - @last_run)} ago"

    if File.exist?(config[:agent_disabled_file])
      begin
        disabled_message = JSON.parse(File.read(config[:agent_disabled_file]))['disabled_message']
        @message += " (disabled reason: #{disabled_message})"
      rescue StandardError => e
        unknown "Could not get disabled message. Reason: #{e.message}"
      end
    end

    if @failures > 0 # rubocop:disable Style/NumericPredicate
      @message += " with #{@failures} failures"
    end

    if @restart_failures > 0 # rubocop:disable Style/NumericPredicate
      @message += " with #{@restart_failures} restart failures"
    end

    if config[:disabled_age_limits] && File.exist?(config[:agent_disabled_file])
      if @now - @last_run > config[:crit_age_disabled]
        critical @message
      elsif @now - @last_run > config[:warn_age_disabled]
        warning @message
      else
        ok @message
      end
    end

    if @now - @last_run > config[:crit_age] || @failures > 0 || @restart_failures > 0 # rubocop:disable Style/NumericPredicate
      critical @message
    elsif @now - @last_run > config[:warn_age]
      warning @message
    else
      ok @message
    end
  end

  def formatted_duration(total_seconds)
    hours = total_seconds / (60 * 60)
    minutes = (total_seconds / 60) % 60
    seconds = total_seconds % 60

    if hours <= 0 && minutes > 0 # rubocop:disable Style/NumericPredicate
      "#{minutes}m #{seconds}s"
    elsif minutes <= 0
      "#{seconds}s"
    else
      "#{hours}h #{minutes}m #{seconds}s"
    end
  end
end