|
#!/usr/bin/env ruby |
|
|
|
require 'rubygems' |
|
require 'json' |
|
require 'net/http' |
|
require 'net/https' |
|
require 'optparse' |
|
|
|
class CheckPuppetRunHealth |
|
def self.run |
|
options = {} |
|
OptionParser.new do |opts| |
|
opts.banner = 'check_puppet_run_health' |
|
|
|
opts.on '-h', '--host HOST' do |val| |
|
options[:host] = val |
|
end |
|
|
|
opts.on '-d', '--puppetdb HOST' do |val| |
|
options[:puppetdb_host] = val |
|
end |
|
|
|
opts.on '-p', '--port PORT' do |val| |
|
options[:puppetdb_port] = val.to_i |
|
end |
|
|
|
opts.on '-s', '--[no-]ssl' do |val| |
|
options[:ssl] = val |
|
end |
|
end.parse! |
|
|
|
api = PuppetDB.new :host => options[:puppetdb_host], |
|
:port => options[:puppetdb_port], |
|
:ssl => options[:ssl] |
|
|
|
reports = api.reports(options[:host]).first(2) |
|
|
|
if reports.empty? |
|
puts "UNKNOWN - This host has no reports stored in PuppetDB" |
|
return 3 |
|
end |
|
|
|
current_report = api.events(reports.first['hash']) |
|
|
|
failing_events = current_report.select do |event| |
|
event['status'] != 'success' |
|
end |
|
|
|
if reports.count == 2 |
|
old_report = api.events(reports.last['hash']) |
|
else |
|
old_report = [] |
|
end |
|
|
|
recurring_events = (current_report & old_report) - failing_events |
|
|
|
if !failing_events.empty? |
|
status = 'CRITICAL' |
|
exit_code = 2 |
|
elsif !recurring_events.empty? |
|
status = 'WARNING' |
|
exit_code = 1 |
|
else |
|
status = 'OK' |
|
exit_code = 0 |
|
end |
|
|
|
puts "#{status}: #{failing_events.count} failing actions, #{recurring_events.count} recurring actions" |
|
|
|
unless failing_events.empty? |
|
puts "\nFAILING EVENTS" |
|
puts "--------------" |
|
failing_events.each do |event| |
|
puts [ |
|
"#{event['resource-type']}[#{event['resource-title']}]/#{event['property']}:", |
|
event['message'], |
|
].join(' ') |
|
end |
|
end |
|
|
|
unless recurring_events.empty? |
|
puts "\nRECURRING EVENTS" |
|
puts "----------------" |
|
recurring_events.each do |event| |
|
puts [ |
|
"#{event['resource-type']}[#{event['resource-title']}]/#{event['property']}:", |
|
event['message'], |
|
].join(' ') |
|
end |
|
end |
|
return exit_code |
|
end |
|
|
|
class PuppetDB |
|
def initialize(opts) |
|
@host = opts[:host] |
|
@port = opts[:port] |
|
@ssl = opts[:ssl] || false |
|
end |
|
|
|
def query(uri, *params) |
|
http = Net::HTTP.new(@host, @port) |
|
http.use_ssl = @ssl |
|
http.verify_mode = OpenSSL::SSL::VERIFY_NONE |
|
|
|
request = Net::HTTP::Get.new uri, 'Accept' => 'application/json' |
|
request.set_form_data 'query' => params.inspect |
|
|
|
response = http.request(request) |
|
JSON.parse(response.body) |
|
end |
|
|
|
def reports(host) |
|
query '/experimental/reports', '=', 'certname', host |
|
end |
|
|
|
def events(report) |
|
events = query '/experimental/events', 'and', |
|
['=', 'report', report], |
|
['not', |
|
['or', |
|
['=', 'status', 'skipped'], |
|
['=', 'status', 'noop'], |
|
], |
|
] |
|
|
|
# strip out the report id and timestamp so we can easily diff the array |
|
events.map do |event| |
|
event.delete 'report' |
|
event.delete 'timestamp' |
|
event |
|
end |
|
end |
|
end |
|
end |
|
|
|
exit CheckPuppetRunHealth.run |